diff --git a/apex/Android.bp b/apex/Android.bp index b9b9bde2e2784b45e6009dfa9f218a599b374acf..570ca0199242ea36a45f586f834146b808f6d601 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -56,6 +56,7 @@ apex_defaults { prebuilts: [ "code_coverage.policy", "com.android.media-mediatranscoding.rc", + "com.android.media-mediatranscoding.32rc", "crash_dump.policy", "mediaextractor.policy", "media-linker-config", @@ -177,6 +178,7 @@ apex_defaults { ], prebuilts: [ "com.android.media.swcodec-mediaswcodec.rc", + "com.android.media.swcodec-mediaswcodec.32rc", "com.android.media.swcodec-ld.config.txt", "mediaswcodec.policy", "code_coverage.policy", @@ -201,17 +203,34 @@ apex_defaults { compressible: true, } +// install as mediatranscoding.* and mediaswcodec.* instead of init.* +// so we are ready for day we have more than 1 *rc file within the apex. + prebuilt_etc { name: "com.android.media-mediatranscoding.rc", src: "mediatranscoding.rc", - filename: "init.rc", + filename: "mediatranscoding.rc", + installable: false, +} + +prebuilt_etc { + name: "com.android.media-mediatranscoding.32rc", + src: "mediatranscoding.32rc", + filename: "mediatranscoding.32rc", installable: false, } prebuilt_etc { name: "com.android.media.swcodec-mediaswcodec.rc", src: "mediaswcodec.rc", - filename: "init.rc", + filename: "mediaswcodec.rc", + installable: false, +} + +prebuilt_etc { + name: "com.android.media.swcodec-mediaswcodec.32rc", + src: "mediaswcodec.32rc", + filename: "mediaswcodec.32rc", installable: false, } diff --git a/apex/mediaswcodec.32rc b/apex/mediaswcodec.32rc index 79aef36d046291be8b935e19009be7fec036d372..f40d1728daf49e3a1963503826c1385a49654c0a 100644 --- a/apex/mediaswcodec.32rc +++ b/apex/mediaswcodec.32rc @@ -1,3 +1,5 @@ +## for SDK releases >= 32 +## service media.swcodec /apex/com.android.media.swcodec/bin/mediaswcodec class main user mediacodec diff --git a/apex/mediaswcodec.rc b/apex/mediaswcodec.rc index 0c9b8c8cfe219f75f0b47a61d9c5e79e3e6e5a34..46799c771fe179796c0a9028bfa0676d2bc98645 100644 --- a/apex/mediaswcodec.rc +++ b/apex/mediaswcodec.rc @@ -1,3 +1,6 @@ +## for SDK releases 29..31 +## where writepid has not yet been replaced by task_profiles +## service media.swcodec /apex/com.android.media.swcodec/bin/mediaswcodec class main user mediacodec diff --git a/apex/mediatranscoding.32rc b/apex/mediatranscoding.32rc index 5169462414abc817acd1e559a04cd040a76c9118..edba9b9f810131acc7c35960dbee897f1daf24fb 100644 --- a/apex/mediatranscoding.32rc +++ b/apex/mediatranscoding.32rc @@ -1,3 +1,6 @@ +## for SDK releases >= 32 +## +# # media.transcoding service is defined on com.android.media apex which goes back # to API29, but we only want it started on API31+ devices. So we declare it as # "disabled" and start it explicitly on boot. diff --git a/apex/mediatranscoding.rc b/apex/mediatranscoding.rc index ae9f8baac605fe6d9b15b6d1867fe43bfdbf1a60..6e453be364aa0f0aa685038a18cda027889e0426 100644 --- a/apex/mediatranscoding.rc +++ b/apex/mediatranscoding.rc @@ -1,3 +1,7 @@ +## for SDK releases 29..31 +## where writepid has not yet been replaced by task_profiles +## +# # media.transcoding service is defined on com.android.media apex which goes back # to API29, but we only want it started on API31+ devices. So we declare it as # "disabled" and start it explicitly on boot. diff --git a/camera/Android.bp b/camera/Android.bp index 4ed3269665cd52e2f77e983ceb8602a6c2467f89..e44202bfbf192e39f0de6cf840742e2355d64790 100644 --- a/camera/Android.bp +++ b/camera/Android.bp @@ -113,6 +113,30 @@ cc_library_shared { } +cc_library_host_static { + name: "libcamera_client_host", + + srcs: [ + "CameraMetadata.cpp", + "VendorTagDescriptor.cpp", + ], + + shared_libs: [ + "libbase", + "libcamera_metadata", + ], + + include_dirs: [ + "system/media/private/camera/include", + "frameworks/native/include/media/openmax", + ], + + export_include_dirs: [ + "include", + "include/camera" + ], +} + // AIDL interface between camera clients and the camera service. filegroup { name: "libcamera_client_aidl", diff --git a/camera/CameraBase.cpp b/camera/CameraBase.cpp index 03439fdf72543f99529d3a6c3a0055214a861c53..24c9108224aed8a42eac685d79da7d0ad3aa0bc8 100644 --- a/camera/CameraBase.cpp +++ b/camera/CameraBase.cpp @@ -68,6 +68,9 @@ status_t CameraStatus::writeToParcel(android::Parcel* parcel) const { unavailablePhysicalIds16.push_back(String16(id8)); } res = parcel->writeString16Vector(unavailablePhysicalIds16); + if (res != OK) return res; + + res = parcel->writeString16(String16(clientPackage)); return res; } @@ -86,6 +89,12 @@ status_t CameraStatus::readFromParcel(const android::Parcel* parcel) { for (auto& id16 : unavailablePhysicalIds16) { unavailablePhysicalIds.push_back(String8(id16)); } + + String16 tempClientPackage; + res = parcel->readString16(&tempClientPackage); + if (res != OK) return res; + clientPackage = String8(tempClientPackage); + return res; } diff --git a/camera/CameraSessionStats.cpp b/camera/CameraSessionStats.cpp index 28e037ff946a4f84290582969d7e5f7142289e50..d1aa36a5246b153a71e5486d5ea59f3c66e1bf09 100644 --- a/camera/CameraSessionStats.cpp +++ b/camera/CameraSessionStats.cpp @@ -52,6 +52,12 @@ status_t CameraStreamStats::readFromParcel(const android::Parcel* parcel) { return err; } + float maxPreviewFps = 0; + if ((err = parcel->readFloat(&maxPreviewFps)) != OK) { + ALOGE("%s: Failed to read maxPreviewFps from parcel", __FUNCTION__); + return err; + } + int dataSpace = 0; if ((err = parcel->readInt32(&dataSpace)) != OK) { ALOGE("%s: Failed to read dataSpace from parcel", __FUNCTION__); @@ -112,9 +118,22 @@ status_t CameraStreamStats::readFromParcel(const android::Parcel* parcel) { return err; } + int64_t dynamicRangeProfile = ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD; + if ((err = parcel->readInt64(&dynamicRangeProfile)) != OK) { + ALOGE("%s: Failed to read dynamic range profile type from parcel", __FUNCTION__); + return err; + } + + int64_t streamUseCase = ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT; + if ((err = parcel->readInt64(&streamUseCase)) != OK) { + ALOGE("%s: Failed to read stream use case from parcel", __FUNCTION__); + return err; + } + mWidth = width; mHeight = height; mFormat = format; + mMaxPreviewFps = maxPreviewFps; mDataSpace = dataSpace; mUsage = usage; mRequestCount = requestCount; @@ -125,6 +144,8 @@ status_t CameraStreamStats::readFromParcel(const android::Parcel* parcel) { mHistogramType = histogramType; mHistogramBins = std::move(histogramBins); mHistogramCounts = std::move(histogramCounts); + mDynamicRangeProfile = dynamicRangeProfile; + mStreamUseCase = streamUseCase; return OK; } @@ -152,6 +173,11 @@ status_t CameraStreamStats::writeToParcel(android::Parcel* parcel) const { return err; } + if ((err = parcel->writeFloat(mMaxPreviewFps)) != OK) { + ALOGE("%s: Failed to write stream maxPreviewFps!", __FUNCTION__); + return err; + } + if ((err = parcel->writeInt32(mDataSpace)) != OK) { ALOGE("%s: Failed to write stream dataSpace!", __FUNCTION__); return err; @@ -202,6 +228,16 @@ status_t CameraStreamStats::writeToParcel(android::Parcel* parcel) const { return err; } + if ((err = parcel->writeInt64(mDynamicRangeProfile)) != OK) { + ALOGE("%s: Failed to write dynamic range profile type", __FUNCTION__); + return err; + } + + if ((err = parcel->writeInt64(mStreamUseCase)) != OK) { + ALOGE("%s: Failed to write stream use case!", __FUNCTION__); + return err; + } + return OK; } @@ -223,11 +259,13 @@ CameraSessionStats::CameraSessionStats() : mApiLevel(0), mIsNdk(false), mLatencyMs(-1), + mMaxPreviewFps(0), mSessionType(0), mInternalReconfigure(0), mRequestCount(0), mResultErrorCount(0), - mDeviceError(false) {} + mDeviceError(false), + mVideoStabilizationMode(-1) {} CameraSessionStats::CameraSessionStats(const String16& cameraId, int facing, int newCameraState, const String16& clientName, @@ -239,11 +277,13 @@ CameraSessionStats::CameraSessionStats(const String16& cameraId, mApiLevel(apiLevel), mIsNdk(isNdk), mLatencyMs(latencyMs), + mMaxPreviewFps(0), mSessionType(0), mInternalReconfigure(0), mRequestCount(0), mResultErrorCount(0), - mDeviceError(0) {} + mDeviceError(0), + mVideoStabilizationMode(-1) {} status_t CameraSessionStats::readFromParcel(const android::Parcel* parcel) { if (parcel == NULL) { @@ -295,6 +335,12 @@ status_t CameraSessionStats::readFromParcel(const android::Parcel* parcel) { return err; } + float maxPreviewFps; + if ((err = parcel->readFloat(&maxPreviewFps)) != OK) { + ALOGE("%s: Failed to read maxPreviewFps from parcel", __FUNCTION__); + return err; + } + int32_t sessionType; if ((err = parcel->readInt32(&sessionType)) != OK) { ALOGE("%s: Failed to read session type from parcel", __FUNCTION__); @@ -331,6 +377,18 @@ status_t CameraSessionStats::readFromParcel(const android::Parcel* parcel) { return err; } + String16 userTag; + if ((err = parcel->readString16(&userTag)) != OK) { + ALOGE("%s: Failed to read user tag!", __FUNCTION__); + return BAD_VALUE; + } + + int32_t videoStabilizationMode; + if ((err = parcel->readInt32(&videoStabilizationMode)) != OK) { + ALOGE("%s: Failed to read video stabilization mode from parcel", __FUNCTION__); + return err; + } + mCameraId = id; mFacing = facing; mNewCameraState = newCameraState; @@ -338,12 +396,15 @@ status_t CameraSessionStats::readFromParcel(const android::Parcel* parcel) { mApiLevel = apiLevel; mIsNdk = isNdk; mLatencyMs = latencyMs; + mMaxPreviewFps = maxPreviewFps; mSessionType = sessionType; mInternalReconfigure = internalReconfigure; mRequestCount = requestCount; mResultErrorCount = resultErrorCount; mDeviceError = deviceError; mStreamStats = std::move(streamStats); + mUserTag = userTag; + mVideoStabilizationMode = videoStabilizationMode; return OK; } @@ -391,6 +452,11 @@ status_t CameraSessionStats::writeToParcel(android::Parcel* parcel) const { return err; } + if ((err = parcel->writeFloat(mMaxPreviewFps)) != OK) { + ALOGE("%s: Failed to write maxPreviewFps!", __FUNCTION__); + return err; + } + if ((err = parcel->writeInt32(mSessionType)) != OK) { ALOGE("%s: Failed to write session type!", __FUNCTION__); return err; @@ -421,6 +487,15 @@ status_t CameraSessionStats::writeToParcel(android::Parcel* parcel) const { return err; } + if ((err = parcel->writeString16(mUserTag)) != OK) { + ALOGE("%s: Failed to write user tag!", __FUNCTION__); + return err; + } + + if ((err = parcel->writeInt32(mVideoStabilizationMode)) != OK) { + ALOGE("%s: Failed to write video stabilization mode!", __FUNCTION__); + return err; + } return OK; } diff --git a/camera/CameraUtils.cpp b/camera/CameraUtils.cpp index af3c49276265d92036a13032dd34700d70417b2d..34737806eb34eaa8cf39f72ba3bbd4a014119141 100644 --- a/camera/CameraUtils.cpp +++ b/camera/CameraUtils.cpp @@ -18,6 +18,7 @@ //#define LOG_NDEBUG 0 #include +#include #include #include @@ -31,7 +32,7 @@ namespace android { const char *kCameraServiceDisabledProperty = "config.disable_cameraservice"; status_t CameraUtils::getRotationTransform(const CameraMetadata& staticInfo, - /*out*/int32_t* transform) { + int mirrorMode, /*out*/int32_t* transform) { ALOGV("%s", __FUNCTION__); if (transform == NULL) { @@ -55,9 +56,18 @@ status_t CameraUtils::getRotationTransform(const CameraMetadata& staticInfo, int32_t& flags = *transform; - bool mirror = (entryFacing.data.u8[0] == ANDROID_LENS_FACING_FRONT); + int32_t mirror = 0; + if (mirrorMode == OutputConfiguration::MIRROR_MODE_AUTO && + entryFacing.data.u8[0] == ANDROID_LENS_FACING_FRONT) { + mirror = NATIVE_WINDOW_TRANSFORM_FLIP_H; + } else if (mirrorMode == OutputConfiguration::MIRROR_MODE_H) { + mirror = NATIVE_WINDOW_TRANSFORM_FLIP_H; + } else if (mirrorMode == OutputConfiguration::MIRROR_MODE_V) { + mirror = NATIVE_WINDOW_TRANSFORM_FLIP_V; + } + int orientation = entry.data.i32[0]; - if (!mirror) { + if (mirror == 0) { switch (orientation) { case 0: flags = 0; @@ -77,25 +87,25 @@ status_t CameraUtils::getRotationTransform(const CameraMetadata& staticInfo, return INVALID_OPERATION; } } else { - // Front camera needs to be horizontally flipped for mirror-like behavior. + // - Front camera needs to be horizontally flipped for mirror-like behavior. + // - Application-specified mirroring needs to be applied. // Note: Flips are applied before rotates; using XOR here as some of these flags are // composed in terms of other flip/rotation flags, and are not bitwise-ORable. switch (orientation) { case 0: - flags = NATIVE_WINDOW_TRANSFORM_FLIP_H; + flags = mirror; break; case 90: - flags = NATIVE_WINDOW_TRANSFORM_FLIP_H ^ + flags = mirror ^ NATIVE_WINDOW_TRANSFORM_ROT_270; break; case 180: - flags = NATIVE_WINDOW_TRANSFORM_FLIP_H ^ + flags = mirror ^ NATIVE_WINDOW_TRANSFORM_ROT_180; break; case 270: - flags = NATIVE_WINDOW_TRANSFORM_FLIP_H ^ + flags = mirror ^ NATIVE_WINDOW_TRANSFORM_ROT_90; - break; default: ALOGE("%s: Invalid HAL android.sensor.orientation value: %d", diff --git a/camera/OWNERS b/camera/OWNERS index 2a1d5237e7cfeeb1db6eff897832f854e562e67b..385c163e5af0056518c96c03ba1921076cb3ccbf 100644 --- a/camera/OWNERS +++ b/camera/OWNERS @@ -1,4 +1,3 @@ - # Bug component: 41727 etalvala@google.com arakesh@google.com diff --git a/camera/aidl/android/hardware/ICameraService.aidl b/camera/aidl/android/hardware/ICameraService.aidl index 78a77d4ea1e0bc38109223949306beec23f6ea78..1e748c7540006d804e73fee7e5966238341e535e 100644 --- a/camera/aidl/android/hardware/ICameraService.aidl +++ b/camera/aidl/android/hardware/ICameraService.aidl @@ -173,6 +173,13 @@ interface ICameraService void setTorchMode(String cameraId, boolean enabled, IBinder clientBinder); + // Change the brightness level of the flash unit associated with cameraId to strengthLevel. + // If the torch is in OFF state and strengthLevel > 0 then the torch will also be turned ON. + void turnOnTorchWithStrengthLevel(String cameraId, int strengthLevel, IBinder clientBinder); + + // Get the brightness level of the flash unit associated with cameraId. + int getTorchStrengthLevel(String cameraId); + /** * Notify the camera service of a system event. Should only be called from system_server. * @@ -180,6 +187,8 @@ interface ICameraService */ const int EVENT_NONE = 0; const int EVENT_USER_SWITCHED = 1; // The argument is the set of new foreground user IDs. + const int EVENT_USB_DEVICE_ATTACHED = 2; // The argument is the deviceId and vendorId + const int EVENT_USB_DEVICE_DETACHED = 3; // The argument is the deviceId and vendorId oneway void notifySystemEvent(int eventId, in int[] args); /** diff --git a/camera/aidl/android/hardware/ICameraServiceListener.aidl b/camera/aidl/android/hardware/ICameraServiceListener.aidl index c54813c8b28164ce5a458282eda0630148c592f5..5f17f5be5919a64b3b22261173096f86b38c64a2 100644 --- a/camera/aidl/android/hardware/ICameraServiceListener.aidl +++ b/camera/aidl/android/hardware/ICameraServiceListener.aidl @@ -83,6 +83,8 @@ interface ICameraServiceListener oneway void onTorchStatusChanged(int status, String cameraId); + oneway void onTorchStrengthLevelChanged(String cameraId, int newTorchStrength); + /** * Notify registered clients about camera access priority changes. * Clients which were previously unable to open a certain camera device diff --git a/camera/aidl/android/hardware/ICameraServiceProxy.aidl b/camera/aidl/android/hardware/ICameraServiceProxy.aidl index f5d0120042c7edb892619b7a28bc6a45bb9f106e..88783fbdebe646aa97e3b7214acdc72b44a38da0 100644 --- a/camera/aidl/android/hardware/ICameraServiceProxy.aidl +++ b/camera/aidl/android/hardware/ICameraServiceProxy.aidl @@ -44,4 +44,9 @@ interface ICameraServiceProxy * {@link android.hardware.camera2.CameraMetadata#SCALER_ROTATE_AND_CROP_270}). */ int getRotateAndCropOverride(String packageName, int lensFacing, int userId); + + /** + * Checks if the camera has been disabled via device policy. + */ + boolean isCameraDisabled(); } diff --git a/camera/camera2/CaptureRequest.cpp b/camera/camera2/CaptureRequest.cpp index ebc09d74299049ac90086bc7e9b899e922845c80..7a8a4bae411287d19bca7880bb3b94fe21b59f89 100644 --- a/camera/camera2/CaptureRequest.cpp +++ b/camera/camera2/CaptureRequest.cpp @@ -146,6 +146,20 @@ status_t CaptureRequest::readFromParcel(const android::Parcel* parcel) { mSurfaceIdxList.push_back(surfaceIdx); } + int32_t hasUserTag; + if ((err = parcel->readInt32(&hasUserTag)) != OK) { + ALOGE("%s: Failed to read user tag availability flag", __FUNCTION__); + return BAD_VALUE; + } + if (hasUserTag) { + String16 userTag; + if ((err = parcel->readString16(&userTag)) != OK) { + ALOGE("%s: Failed to read user tag!", __FUNCTION__); + return BAD_VALUE; + } + mUserTag = String8(userTag).c_str(); + } + return OK; } @@ -213,6 +227,14 @@ status_t CaptureRequest::writeToParcel(android::Parcel* parcel) const { return err; } } + + if (mUserTag.empty()) { + parcel->writeInt32(0); + } else { + parcel->writeInt32(1); + parcel->writeString16(String16(mUserTag.c_str())); + } + return OK; } diff --git a/camera/camera2/OutputConfiguration.cpp b/camera/camera2/OutputConfiguration.cpp index 2bccd870b8a7cf9c28595748387f89cfab35e558..11d4960dbc3987c62a2a212cd214bb2bf41b9f27 100644 --- a/camera/camera2/OutputConfiguration.cpp +++ b/camera/camera2/OutputConfiguration.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include namespace android { @@ -76,6 +77,22 @@ const std::vector &OutputConfiguration::getSensorPixelModesUsed() const return mSensorPixelModesUsed; } +int64_t OutputConfiguration::getDynamicRangeProfile() const { + return mDynamicRangeProfile; +} + +int64_t OutputConfiguration::getStreamUseCase() const { + return mStreamUseCase; +} + +int OutputConfiguration::getTimestampBase() const { + return mTimestampBase; +} + +int OutputConfiguration::getMirrorMode() const { + return mMirrorMode; +} + OutputConfiguration::OutputConfiguration() : mRotation(INVALID_ROTATION), mSurfaceSetID(INVALID_SET_ID), @@ -84,7 +101,11 @@ OutputConfiguration::OutputConfiguration() : mHeight(0), mIsDeferred(false), mIsShared(false), - mIsMultiResolution(false) { + mIsMultiResolution(false), + mDynamicRangeProfile(ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD), + mStreamUseCase(ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT), + mTimestampBase(TIMESTAMP_BASE_DEFAULT), + mMirrorMode(MIRROR_MODE_AUTO) { } OutputConfiguration::OutputConfiguration(const android::Parcel& parcel) : @@ -165,6 +186,30 @@ status_t OutputConfiguration::readFromParcel(const android::Parcel* parcel) { ALOGE("%s: Failed to read sensor pixel mode(s) from parcel", __FUNCTION__); return err; } + int64_t dynamicProfile; + if ((err = parcel->readInt64(&dynamicProfile)) != OK) { + ALOGE("%s: Failed to read surface dynamic range profile flag from parcel", __FUNCTION__); + return err; + } + + int64_t streamUseCase = ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT; + if ((err = parcel->readInt64(&streamUseCase)) != OK) { + ALOGE("%s: Failed to read stream use case from parcel", __FUNCTION__); + return err; + } + + int timestampBase = TIMESTAMP_BASE_DEFAULT; + if ((err = parcel->readInt32(×tampBase)) != OK) { + ALOGE("%s: Failed to read timestamp base from parcel", __FUNCTION__); + return err; + } + + int mirrorMode = MIRROR_MODE_AUTO; + if ((err = parcel->readInt32(&mirrorMode)) != OK) { + ALOGE("%s: Failed to read mirroring mode from parcel", __FUNCTION__); + return err; + } + mRotation = rotation; mSurfaceSetID = setID; mSurfaceType = surfaceType; @@ -173,6 +218,9 @@ status_t OutputConfiguration::readFromParcel(const android::Parcel* parcel) { mIsDeferred = isDeferred != 0; mIsShared = isShared != 0; mIsMultiResolution = isMultiResolution != 0; + mStreamUseCase = streamUseCase; + mTimestampBase = timestampBase; + mMirrorMode = mirrorMode; for (auto& surface : surfaceShims) { ALOGV("%s: OutputConfiguration: %p, name %s", __FUNCTION__, surface.graphicBufferProducer.get(), @@ -181,10 +229,14 @@ status_t OutputConfiguration::readFromParcel(const android::Parcel* parcel) { } mSensorPixelModesUsed = std::move(sensorPixelModesUsed); + mDynamicRangeProfile = dynamicProfile; ALOGV("%s: OutputConfiguration: rotation = %d, setId = %d, surfaceType = %d," - " physicalCameraId = %s, isMultiResolution = %d", __FUNCTION__, mRotation, - mSurfaceSetID, mSurfaceType, String8(mPhysicalCameraId).string(), mIsMultiResolution); + " physicalCameraId = %s, isMultiResolution = %d, streamUseCase = %" PRId64 + ", timestampBase = %d, mirrorMode = %d", + __FUNCTION__, mRotation, mSurfaceSetID, mSurfaceType, + String8(mPhysicalCameraId).string(), mIsMultiResolution, mStreamUseCase, timestampBase, + mMirrorMode); return err; } @@ -199,6 +251,10 @@ OutputConfiguration::OutputConfiguration(sp& gbp, int ro mIsShared = isShared; mPhysicalCameraId = physicalId; mIsMultiResolution = false; + mDynamicRangeProfile = ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD; + mStreamUseCase = ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT; + mTimestampBase = TIMESTAMP_BASE_DEFAULT; + mMirrorMode = MIRROR_MODE_AUTO; } OutputConfiguration::OutputConfiguration( @@ -207,7 +263,11 @@ OutputConfiguration::OutputConfiguration( int width, int height, bool isShared) : mGbps(gbps), mRotation(rotation), mSurfaceSetID(surfaceSetID), mSurfaceType(surfaceType), mWidth(width), mHeight(height), mIsDeferred(false), mIsShared(isShared), - mPhysicalCameraId(physicalCameraId), mIsMultiResolution(false) { } + mPhysicalCameraId(physicalCameraId), mIsMultiResolution(false), + mDynamicRangeProfile(ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD), + mStreamUseCase(ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT), + mTimestampBase(TIMESTAMP_BASE_DEFAULT), + mMirrorMode(MIRROR_MODE_AUTO) { } status_t OutputConfiguration::writeToParcel(android::Parcel* parcel) const { @@ -254,6 +314,18 @@ status_t OutputConfiguration::writeToParcel(android::Parcel* parcel) const { err = parcel->writeParcelableVector(mSensorPixelModesUsed); if (err != OK) return err; + err = parcel->writeInt64(mDynamicRangeProfile); + if (err != OK) return err; + + err = parcel->writeInt64(mStreamUseCase); + if (err != OK) return err; + + err = parcel->writeInt32(mTimestampBase); + if (err != OK) return err; + + err = parcel->writeInt32(mMirrorMode); + if (err != OK) return err; + return OK; } diff --git a/camera/cameraserver/Android.bp b/camera/cameraserver/Android.bp index 8ca892001251fcd5ac52ea36df3f0fd3549e16a5..094a3c19854f94330ab703d806387440edad5563 100644 --- a/camera/cameraserver/Android.bp +++ b/camera/cameraserver/Android.bp @@ -43,6 +43,7 @@ cc_binary { "android.hardware.camera.provider@2.5", "android.hardware.camera.provider@2.6", "android.hardware.camera.provider@2.7", + "android.hardware.camera.provider-V1-ndk", "android.hardware.camera.device@1.0", "android.hardware.camera.device@3.2", "android.hardware.camera.device@3.4", diff --git a/camera/include/camera/CameraBase.h b/camera/include/camera/CameraBase.h index e156994798e10df296c37f5a5594f4f0030192f1..8e5396863355a3d95c553b2a95dc7987d51e27c2 100644 --- a/camera/include/camera/CameraBase.h +++ b/camera/include/camera/CameraBase.h @@ -85,11 +85,17 @@ struct CameraStatus : public android::Parcelable { */ std::vector unavailablePhysicalIds; + /** + * Client package name if camera is open, otherwise not applicable + */ + String8 clientPackage; + virtual status_t writeToParcel(android::Parcel* parcel) const; virtual status_t readFromParcel(const android::Parcel* parcel); - CameraStatus(String8 id, int32_t s, const std::vector& unavailSubIds) : - cameraId(id), status(s), unavailablePhysicalIds(unavailSubIds) {} + CameraStatus(String8 id, int32_t s, const std::vector& unavailSubIds, + const String8& clientPkg) : cameraId(id), status(s), + unavailablePhysicalIds(unavailSubIds), clientPackage(clientPkg) {} CameraStatus() : status(ICameraServiceListener::STATUS_PRESENT) {} }; diff --git a/camera/include/camera/CameraSessionStats.h b/camera/include/camera/CameraSessionStats.h index c398acaa02e4b65122b4537f1b7dba385293c58a..aaa88b2b08dfaca7f0a0cb70f17a75c57ea948f1 100644 --- a/camera/include/camera/CameraSessionStats.h +++ b/camera/include/camera/CameraSessionStats.h @@ -19,6 +19,8 @@ #include +#include + namespace android { namespace hardware { @@ -35,6 +37,7 @@ public: int mWidth; int mHeight; int mFormat; + float mMaxPreviewFps; int mDataSpace; int64_t mUsage; @@ -60,16 +63,26 @@ public: // size(mHistogramBins) + 1 = size(mHistogramCounts) std::vector mHistogramCounts; + // Dynamic range profile + int64_t mDynamicRangeProfile; + // Stream use case + int64_t mStreamUseCase; + CameraStreamStats() : - mWidth(0), mHeight(0), mFormat(0), mDataSpace(0), mUsage(0), + mWidth(0), mHeight(0), mFormat(0), mMaxPreviewFps(0), mDataSpace(0), mUsage(0), mRequestCount(0), mErrorCount(0), mStartLatencyMs(0), - mMaxHalBuffers(0), mMaxAppBuffers(0), mHistogramType(HISTOGRAM_TYPE_UNKNOWN) {} - CameraStreamStats(int width, int height, int format, int dataSpace, int64_t usage, - int maxHalBuffers, int maxAppBuffers) - : mWidth(width), mHeight(height), mFormat(format), mDataSpace(dataSpace), - mUsage(usage), mRequestCount(0), mErrorCount(0), mStartLatencyMs(0), - mMaxHalBuffers(maxHalBuffers), mMaxAppBuffers(maxAppBuffers), - mHistogramType(HISTOGRAM_TYPE_UNKNOWN) {} + mMaxHalBuffers(0), mMaxAppBuffers(0), mHistogramType(HISTOGRAM_TYPE_UNKNOWN), + mDynamicRangeProfile(ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD), + mStreamUseCase(ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT) {} + CameraStreamStats(int width, int height, int format, float maxPreviewFps, int dataSpace, + int64_t usage, int maxHalBuffers, int maxAppBuffers, int dynamicRangeProfile, + int streamUseCase) + : mWidth(width), mHeight(height), mFormat(format), mMaxPreviewFps(maxPreviewFps), + mDataSpace(dataSpace), mUsage(usage), mRequestCount(0), mErrorCount(0), + mStartLatencyMs(0), mMaxHalBuffers(maxHalBuffers), mMaxAppBuffers(maxAppBuffers), + mHistogramType(HISTOGRAM_TYPE_UNKNOWN), + mDynamicRangeProfile(dynamicRangeProfile), + mStreamUseCase(streamUseCase) {} virtual status_t readFromParcel(const android::Parcel* parcel) override; virtual status_t writeToParcel(android::Parcel* parcel) const override; @@ -111,6 +124,7 @@ public: bool mIsNdk; // latency in ms for camera open, close, or session creation. int mLatencyMs; + float mMaxPreviewFps; // Session info and statistics int mSessionType; @@ -122,6 +136,8 @@ public: // Whether the device runs into an error state bool mDeviceError; std::vector mStreamStats; + String16 mUserTag; + int mVideoStabilizationMode; // Constructors CameraSessionStats(); diff --git a/camera/include/camera/CameraUtils.h b/camera/include/camera/CameraUtils.h index a397ccdd5e2f83635872f6eaac590b7ad65e92a9..31d25e79ea8d538cce233b525e3e843881574c58 100644 --- a/camera/include/camera/CameraUtils.h +++ b/camera/include/camera/CameraUtils.h @@ -37,10 +37,13 @@ class CameraUtils { * metadata. This is based on the sensor orientation and lens facing * attributes of the camera device. * + * If mirrorMode is not AUTO, it will be used to override the lens + * facing based mirror. + * * Returns OK on success, or a negative error code. */ static status_t getRotationTransform(const CameraMetadata& staticInfo, - /*out*/int32_t* transform); + int mirrorMode, /*out*/int32_t* transform); /** * Check if the image data is VideoNativeHandleMetadata, that contains a native handle. diff --git a/camera/include/camera/camera2/CaptureRequest.h b/camera/include/camera/camera2/CaptureRequest.h index 506abab70474aec771059af0cb99117814b28475..28dbc7cb289be683208f0817fa6363ac01f4816c 100644 --- a/camera/include/camera/camera2/CaptureRequest.h +++ b/camera/include/camera/camera2/CaptureRequest.h @@ -63,6 +63,8 @@ struct CaptureRequest : public Parcelable { void* mContext; // arbitrary user context from NDK apps, null for java apps + std::string mUserTag; // The string representation of object passed into setTag. + /** * Keep impl up-to-date with CaptureRequest.java in frameworks/base */ diff --git a/camera/include/camera/camera2/OutputConfiguration.h b/camera/include/camera/camera2/OutputConfiguration.h index f80ed3a8e6787fc1b571db003bd49e025439681b..b842885a6b77a427807cadf44b5ff67d918f3837 100644 --- a/camera/include/camera/camera2/OutputConfiguration.h +++ b/camera/include/camera/camera2/OutputConfiguration.h @@ -38,16 +38,34 @@ public: SURFACE_TYPE_SURFACE_VIEW = 0, SURFACE_TYPE_SURFACE_TEXTURE = 1 }; + enum TimestampBaseType { + TIMESTAMP_BASE_DEFAULT = 0, + TIMESTAMP_BASE_SENSOR = 1, + TIMESTAMP_BASE_MONOTONIC = 2, + TIMESTAMP_BASE_REALTIME = 3, + TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED = 4 + }; + enum MirrorModeType { + MIRROR_MODE_AUTO = 0, + MIRROR_MODE_NONE = 1, + MIRROR_MODE_H = 2, + MIRROR_MODE_V = 3, + }; + const std::vector>& getGraphicBufferProducers() const; int getRotation() const; int getSurfaceSetID() const; int getSurfaceType() const; int getWidth() const; int getHeight() const; + int64_t getDynamicRangeProfile() const; bool isDeferred() const; bool isShared() const; String16 getPhysicalCameraId() const; bool isMultiResolution() const; + int64_t getStreamUseCase() const; + int getTimestampBase() const; + int getMirrorMode() const; // set of sensor pixel mode resolutions allowed {MAX_RESOLUTION, DEFAULT_MODE}; const std::vector& getSensorPixelModesUsed() const; @@ -89,7 +107,11 @@ public: gbpsEqual(other) && mPhysicalCameraId == other.mPhysicalCameraId && mIsMultiResolution == other.mIsMultiResolution && - sensorPixelModesUsedEqual(other)); + sensorPixelModesUsedEqual(other) && + mDynamicRangeProfile == other.mDynamicRangeProfile && + mStreamUseCase == other.mStreamUseCase && + mTimestampBase == other.mTimestampBase && + mMirrorMode == other.mMirrorMode); } bool operator != (const OutputConfiguration& other) const { return !(*this == other); @@ -126,6 +148,18 @@ public: if (!sensorPixelModesUsedEqual(other)) { return sensorPixelModesUsedLessThan(other); } + if (mDynamicRangeProfile != other.mDynamicRangeProfile) { + return mDynamicRangeProfile < other.mDynamicRangeProfile; + } + if (mStreamUseCase != other.mStreamUseCase) { + return mStreamUseCase < other.mStreamUseCase; + } + if (mTimestampBase != other.mTimestampBase) { + return mTimestampBase < other.mTimestampBase; + } + if (mMirrorMode != other.mMirrorMode) { + return mMirrorMode < other.mMirrorMode; + } return gbpsLessThan(other); } @@ -150,6 +184,10 @@ private: String16 mPhysicalCameraId; bool mIsMultiResolution; std::vector mSensorPixelModesUsed; + int64_t mDynamicRangeProfile; + int64_t mStreamUseCase; + int mTimestampBase; + int mMirrorMode; }; } // namespace params } // namespace camera2 diff --git a/camera/ndk/NdkCameraCaptureSession.cpp b/camera/ndk/NdkCameraCaptureSession.cpp index 1ac8482ac41c3599f4afeea36717b7f07611517e..9c98778fe45f8055e829f20771f3bf72a05754b6 100644 --- a/camera/ndk/NdkCameraCaptureSession.cpp +++ b/camera/ndk/NdkCameraCaptureSession.cpp @@ -29,6 +29,7 @@ #include "impl/ACameraCaptureSession.h" #include "impl/ACameraCaptureSession.inc" +#include "NdkCameraCaptureSession.inc" using namespace android; @@ -72,22 +73,16 @@ camera_status_t ACameraCaptureSession_capture( int numRequests, ACaptureRequest** requests, /*optional*/int* captureSequenceId) { ATRACE_CALL(); - if (session == nullptr || requests == nullptr || numRequests < 1) { - ALOGE("%s: Error: invalid input: session %p, numRequest %d, requests %p", - __FUNCTION__, session, numRequests, requests); - return ACAMERA_ERROR_INVALID_PARAMETER; - } - - if (session->isClosed()) { - ALOGE("%s: session %p is already closed", __FUNCTION__, session); - if (captureSequenceId != nullptr) { - *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE; - } - return ACAMERA_ERROR_SESSION_CLOSED; - } + return captureTemplate(session, cbs, numRequests, requests, captureSequenceId); +} - return session->capture( - cbs, numRequests, requests, captureSequenceId); +EXPORT +camera_status_t ACameraCaptureSession_captureV2( + ACameraCaptureSession* session, /*optional*/ACameraCaptureSession_captureCallbacksV2* cbs, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId) { + ATRACE_CALL(); + return captureTemplate(session, cbs, numRequests, requests, captureSequenceId); } EXPORT @@ -97,22 +92,26 @@ camera_status_t ACameraCaptureSession_logicalCamera_capture( int numRequests, ACaptureRequest** requests, /*optional*/int* captureSequenceId) { ATRACE_CALL(); - if (session == nullptr || requests == nullptr || numRequests < 1) { - ALOGE("%s: Error: invalid input: session %p, numRequest %d, requests %p", - __FUNCTION__, session, numRequests, requests); - return ACAMERA_ERROR_INVALID_PARAMETER; - } + return captureTemplate(session, lcbs, numRequests, requests, captureSequenceId); +} - if (session->isClosed()) { - ALOGE("%s: session %p is already closed", __FUNCTION__, session); - if (captureSequenceId) { - *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE; - } - return ACAMERA_ERROR_SESSION_CLOSED; - } +EXPORT +camera_status_t ACameraCaptureSession_logicalCamera_captureV2( + ACameraCaptureSession* session, + /*optional*/ACameraCaptureSession_logicalCamera_captureCallbacksV2* lcbs, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId) { + ATRACE_CALL(); + return captureTemplate(session, lcbs, numRequests, requests, captureSequenceId); +} - return session->capture( - lcbs, numRequests, requests, captureSequenceId); +EXPORT +camera_status_t ACameraCaptureSession_setRepeatingRequestV2( + ACameraCaptureSession* session, /*optional*/ACameraCaptureSession_captureCallbacksV2* cbs, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId) { + ATRACE_CALL(); + return setRepeatingRequestTemplate(session, cbs, numRequests, requests, captureSequenceId); } EXPORT @@ -121,23 +120,10 @@ camera_status_t ACameraCaptureSession_setRepeatingRequest( int numRequests, ACaptureRequest** requests, /*optional*/int* captureSequenceId) { ATRACE_CALL(); - if (session == nullptr || requests == nullptr || numRequests < 1) { - ALOGE("%s: Error: invalid input: session %p, numRequest %d, requests %p", - __FUNCTION__, session, numRequests, requests); - return ACAMERA_ERROR_INVALID_PARAMETER; - } - - if (session->isClosed()) { - ALOGE("%s: session %p is already closed", __FUNCTION__, session); - if (captureSequenceId) { - *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE; - } - return ACAMERA_ERROR_SESSION_CLOSED; - } - - return session->setRepeatingRequest(cbs, numRequests, requests, captureSequenceId); + return setRepeatingRequestTemplate(session, cbs, numRequests, requests, captureSequenceId); } + EXPORT camera_status_t ACameraCaptureSession_logicalCamera_setRepeatingRequest( ACameraCaptureSession* session, @@ -145,21 +131,18 @@ camera_status_t ACameraCaptureSession_logicalCamera_setRepeatingRequest( int numRequests, ACaptureRequest** requests, /*optional*/int* captureSequenceId) { ATRACE_CALL(); - if (session == nullptr || requests == nullptr || numRequests < 1) { - ALOGE("%s: Error: invalid input: session %p, numRequest %d, requests %p", - __FUNCTION__, session, numRequests, requests); - return ACAMERA_ERROR_INVALID_PARAMETER; - } + return setRepeatingRequestTemplate(session, lcbs, numRequests, requests, captureSequenceId); +} - if (session->isClosed()) { - ALOGE("%s: session %p is already closed", __FUNCTION__, session); - if (captureSequenceId) { - *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE; - } - return ACAMERA_ERROR_SESSION_CLOSED; - } - return session->setRepeatingRequest(lcbs, numRequests, requests, captureSequenceId); +EXPORT +camera_status_t ACameraCaptureSession_logicalCamera_setRepeatingRequestV2( + ACameraCaptureSession* session, + /*optional*/ACameraCaptureSession_logicalCamera_captureCallbacksV2* lcbs, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId) { + ATRACE_CALL(); + return setRepeatingRequestTemplate(session, lcbs, numRequests, requests, captureSequenceId); } EXPORT diff --git a/camera/ndk/NdkCameraCaptureSession.inc b/camera/ndk/NdkCameraCaptureSession.inc new file mode 100644 index 0000000000000000000000000000000000000000..258e20d34eb67eed2f765214f28699e36ac600e2 --- /dev/null +++ b/camera/ndk/NdkCameraCaptureSession.inc @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "impl/ACameraCaptureSession.h" + +#include + +using namespace android; + +template +camera_status_t captureTemplate( + ACameraCaptureSession* session, + /*optional*/CallbackType* cbs, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId) { + ATRACE_CALL(); + if (session == nullptr || requests == nullptr || numRequests < 1) { + ALOGE("%s: Error: invalid input: session %p, numRequest %d, requests %p", + __FUNCTION__, session, numRequests, requests); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + + if (session->isClosed()) { + ALOGE("%s: session %p is already closed", __FUNCTION__, session); + if (captureSequenceId) { + *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE; + } + return ACAMERA_ERROR_SESSION_CLOSED; + } + + return session->capture( + cbs, numRequests, requests, captureSequenceId); +} + +template +camera_status_t setRepeatingRequestTemplate( + ACameraCaptureSession* session, + /*optional*/CallbackType* cbs, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId) { + ATRACE_CALL(); + if (session == nullptr || requests == nullptr || numRequests < 1) { + ALOGE("%s: Error: invalid input: session %p, numRequest %d, requests %p", + __FUNCTION__, session, numRequests, requests); + return ACAMERA_ERROR_INVALID_PARAMETER; + } + + if (session->isClosed()) { + ALOGE("%s: session %p is already closed", __FUNCTION__, session); + if (captureSequenceId) { + *captureSequenceId = CAPTURE_SEQUENCE_ID_NONE; + } + return ACAMERA_ERROR_SESSION_CLOSED; + } + + return session->setRepeatingRequest(cbs, numRequests, requests, captureSequenceId); +} diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp index dd652c70c52ea4661690abafdc8af6337bd8834d..7997768e6e5809d7c06dbbf1295fdbb86705b5e2 100644 --- a/camera/ndk/impl/ACameraDevice.cpp +++ b/camera/ndk/impl/ACameraDevice.cpp @@ -26,8 +26,6 @@ #include "ACaptureRequest.h" #include "ACameraCaptureSession.h" -#include "ACameraCaptureSession.inc" - ACameraDevice::~ACameraDevice() { mDevice->stopLooperAndDisconnect(); } @@ -913,6 +911,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( case kWhatOnError: case kWhatSessionStateCb: case kWhatCaptureStart: + case kWhatCaptureStart2: case kWhatCaptureResult: case kWhatLogicalCaptureResult: case kWhatCaptureFail: @@ -985,6 +984,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( } case kWhatSessionStateCb: case kWhatCaptureStart: + case kWhatCaptureStart2: case kWhatCaptureResult: case kWhatLogicalCaptureResult: case kWhatCaptureFail: @@ -1004,6 +1004,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( sp requestSp = nullptr; switch (msg->what()) { case kWhatCaptureStart: + case kWhatCaptureStart2: case kWhatCaptureResult: case kWhatLogicalCaptureResult: case kWhatCaptureFail: @@ -1055,6 +1056,35 @@ void CameraDevice::CallbackHandler::onMessageReceived( freeACaptureRequest(request); break; } + case kWhatCaptureStart2: + { + ACameraCaptureSession_captureCallback_startV2 onStart2; + found = msg->findPointer(kCallbackFpKey, (void**) &onStart2); + if (!found) { + ALOGE("%s: Cannot find capture startV2 callback!", __FUNCTION__); + return; + } + if (onStart2 == nullptr) { + return; + } + int64_t timestamp; + found = msg->findInt64(kTimeStampKey, ×tamp); + if (!found) { + ALOGE("%s: Cannot find timestamp!", __FUNCTION__); + return; + } + int64_t frameNumber; + found = msg->findInt64(kFrameNumberKey, &frameNumber); + if (!found) { + ALOGE("%s: Cannot find frame number!", __FUNCTION__); + return; + } + + ACaptureRequest* request = allocateACaptureRequest(requestSp, mId); + (*onStart2)(context, session.get(), request, timestamp, frameNumber); + freeACaptureRequest(request); + break; + } case kWhatCaptureResult: { ACameraCaptureSession_captureCallback_result onResult; @@ -1285,7 +1315,8 @@ CameraDevice::CallbackHolder::CallbackHolder( ACameraCaptureSession_captureCallbacks* cbs) : mSession(session), mRequests(requests), mIsRepeating(isRepeating), - mIsLogicalCameraCallback(false) { + mIsLogicalCameraCallback(false), + mIs2Callback(false) { initCaptureCallbacks(cbs); if (cbs != nullptr) { @@ -1301,7 +1332,8 @@ CameraDevice::CallbackHolder::CallbackHolder( ACameraCaptureSession_logicalCamera_captureCallbacks* lcbs) : mSession(session), mRequests(requests), mIsRepeating(isRepeating), - mIsLogicalCameraCallback(true) { + mIsLogicalCameraCallback(true), + mIs2Callback(false) { initCaptureCallbacks(lcbs); if (lcbs != nullptr) { @@ -1310,6 +1342,40 @@ CameraDevice::CallbackHolder::CallbackHolder( } } +CameraDevice::CallbackHolder::CallbackHolder( + sp session, + const Vector >& requests, + bool isRepeating, + ACameraCaptureSession_captureCallbacksV2* cbs) : + mSession(session), mRequests(requests), + mIsRepeating(isRepeating), + mIsLogicalCameraCallback(false), + mIs2Callback(true) { + initCaptureCallbacksV2(cbs); + + if (cbs != nullptr) { + mOnCaptureCompleted = cbs->onCaptureCompleted; + mOnCaptureFailed = cbs->onCaptureFailed; + } +} + +CameraDevice::CallbackHolder::CallbackHolder( + sp session, + const Vector >& requests, + bool isRepeating, + ACameraCaptureSession_logicalCamera_captureCallbacksV2* lcbs) : + mSession(session), mRequests(requests), + mIsRepeating(isRepeating), + mIsLogicalCameraCallback(true), + mIs2Callback(true) { + initCaptureCallbacksV2(lcbs); + + if (lcbs != nullptr) { + mOnLogicalCameraCaptureCompleted = lcbs->onLogicalCameraCaptureCompleted; + mOnLogicalCameraCaptureFailed = lcbs->onLogicalCameraCaptureFailed; + } +} + void CameraDevice::checkRepeatingSequenceCompleteLocked( const int sequenceId, const int64_t lastFrameNumber) { @@ -1536,7 +1602,6 @@ CameraDevice::ServiceCallback::onCaptureStarted( const CaptureResultExtras& resultExtras, int64_t timestamp) { binder::Status ret = binder::Status::ok(); - sp dev = mDevice.promote(); if (dev == nullptr) { return ret; // device has been closed @@ -1551,11 +1616,14 @@ CameraDevice::ServiceCallback::onCaptureStarted( int sequenceId = resultExtras.requestId; int32_t burstId = resultExtras.burstId; + int64_t frameNumber = resultExtras.frameNumber; auto it = dev->mSequenceCallbackMap.find(sequenceId); if (it != dev->mSequenceCallbackMap.end()) { CallbackHolder cbh = (*it).second; + bool v2Callback = cbh.mIs2Callback; ACameraCaptureSession_captureCallback_start onStart = cbh.mOnCaptureStarted; + ACameraCaptureSession_captureCallback_startV2 onStart2 = cbh.mOnCaptureStarted2; sp session = cbh.mSession; if ((size_t) burstId >= cbh.mRequests.size()) { ALOGE("%s: Error: request index %d out of bound (size %zu)", @@ -1563,12 +1631,19 @@ CameraDevice::ServiceCallback::onCaptureStarted( dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE); } sp request = cbh.mRequests[burstId]; - sp msg = new AMessage(kWhatCaptureStart, dev->mHandler); + sp msg = nullptr; + if (v2Callback) { + msg = new AMessage(kWhatCaptureStart2, dev->mHandler); + msg->setPointer(kCallbackFpKey, (void*) onStart2); + } else { + msg = new AMessage(kWhatCaptureStart, dev->mHandler); + msg->setPointer(kCallbackFpKey, (void *)onStart); + } msg->setPointer(kContextKey, cbh.mContext); msg->setObject(kSessionSpKey, session); - msg->setPointer(kCallbackFpKey, (void*) onStart); msg->setObject(kCaptureRequestKey, request); msg->setInt64(kTimeStampKey, timestamp); + msg->setInt64(kFrameNumberKey, frameNumber); dev->postSessionMsgAndCleanup(msg); } return ret; diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h index 344d9644183349dbeb2b5357f26e8d86aae0d47f..17988fe8f1cde30df02a890452dd8837611337b5 100644 --- a/camera/ndk/impl/ACameraDevice.h +++ b/camera/ndk/impl/ACameraDevice.h @@ -215,6 +215,7 @@ class CameraDevice final : public RefBase { kWhatSessionStateCb, // onReady, onActive // Capture callbacks kWhatCaptureStart, // onCaptureStarted + kWhatCaptureStart2, // onCaptureStarted kWhatCaptureResult, // onCaptureProgressed, onCaptureCompleted kWhatLogicalCaptureResult, // onLogicalCameraCaptureCompleted kWhatCaptureFail, // onCaptureFailed @@ -294,11 +295,18 @@ class CameraDevice final : public RefBase { const Vector >& requests, bool isRepeating, ACameraCaptureSession_logicalCamera_captureCallbacks* lcbs); - - template - void initCaptureCallbacks(T* cbs) { + CallbackHolder(sp session, + const Vector >& requests, + bool isRepeating, + ACameraCaptureSession_captureCallbacksV2* cbs); + CallbackHolder(sp session, + const Vector >& requests, + bool isRepeating, + ACameraCaptureSession_logicalCamera_captureCallbacksV2* lcbs); + void clearCallbacks() { mContext = nullptr; mOnCaptureStarted = nullptr; + mOnCaptureStarted2 = nullptr; mOnCaptureProgressed = nullptr; mOnCaptureCompleted = nullptr; mOnLogicalCameraCaptureCompleted = nullptr; @@ -307,6 +315,24 @@ class CameraDevice final : public RefBase { mOnCaptureSequenceCompleted = nullptr; mOnCaptureSequenceAborted = nullptr; mOnCaptureBufferLost = nullptr; + } + + template + void initCaptureCallbacksV2(T* cbs) { + clearCallbacks(); + if (cbs != nullptr) { + mContext = cbs->context; + mOnCaptureStarted2 = cbs->onCaptureStarted; + mOnCaptureProgressed = cbs->onCaptureProgressed; + mOnCaptureSequenceCompleted = cbs->onCaptureSequenceCompleted; + mOnCaptureSequenceAborted = cbs->onCaptureSequenceAborted; + mOnCaptureBufferLost = cbs->onCaptureBufferLost; + } + } + + template + void initCaptureCallbacks(T* cbs) { + clearCallbacks(); if (cbs != nullptr) { mContext = cbs->context; mOnCaptureStarted = cbs->onCaptureStarted; @@ -320,9 +346,11 @@ class CameraDevice final : public RefBase { Vector > mRequests; const bool mIsRepeating; const bool mIsLogicalCameraCallback; + const bool mIs2Callback; void* mContext; ACameraCaptureSession_captureCallback_start mOnCaptureStarted; + ACameraCaptureSession_captureCallback_startV2 mOnCaptureStarted2; ACameraCaptureSession_captureCallback_result mOnCaptureProgressed; ACameraCaptureSession_captureCallback_result mOnCaptureCompleted; ACameraCaptureSession_logicalCamera_captureCallback_result mOnLogicalCameraCaptureCompleted; diff --git a/camera/ndk/impl/ACameraManager.cpp b/camera/ndk/impl/ACameraManager.cpp index 95ef2b2af1f828c9901bb42f9ff780adb87c4fee..5892f1ad0f19c9d2498983fea7291379b8ef2834 100644 --- a/camera/ndk/impl/ACameraManager.cpp +++ b/camera/ndk/impl/ACameraManager.cpp @@ -189,8 +189,12 @@ void CameraManagerGlobal::DeathNotifier::binderDied(const wp&) sp cm = mCameraManager.promote(); if (cm != nullptr) { AutoMutex lock(cm->mLock); + std::vector cameraIdList; for (auto& pair : cm->mDeviceStatusMap) { - const String8 &cameraId = pair.first; + cameraIdList.push_back(pair.first); + } + + for (String8 cameraId : cameraIdList) { cm->onStatusChangedLocked( CameraServiceListener::STATUS_NOT_PRESENT, cameraId); } diff --git a/camera/ndk/impl/ACameraManager.h b/camera/ndk/impl/ACameraManager.h index da887a26e09e692d26a2d79237bcabea57d16558..d53d8099ded18e701fbcc64caacccafb84b1ef5d 100644 --- a/camera/ndk/impl/ACameraManager.h +++ b/camera/ndk/impl/ACameraManager.h @@ -95,6 +95,9 @@ class CameraManagerGlobal final : public RefBase { virtual binder::Status onTorchStatusChanged(int32_t, const String16&) { return binder::Status::ok(); } + virtual binder::Status onTorchStrengthLevelChanged(const String16&, int32_t) { + return binder::Status::ok(); + } virtual binder::Status onCameraAccessPrioritiesChanged(); virtual binder::Status onCameraOpened(const String16&, const String16&) { diff --git a/camera/ndk/include/camera/NdkCameraCaptureSession.h b/camera/ndk/include/camera/NdkCameraCaptureSession.h index 2b7f040be449c3884795ed41b1aa6dc6d027c0ee..b0fd00ce20cc8b42bdcc24dac8f7f1ceb03810ba 100644 --- a/camera/ndk/include/camera/NdkCameraCaptureSession.h +++ b/camera/ndk/include/camera/NdkCameraCaptureSession.h @@ -811,6 +811,184 @@ camera_status_t ACameraCaptureSession_logicalCamera_setRepeatingRequest( int numRequests, ACaptureRequest** requests, /*optional*/int* captureSequenceId) __INTRODUCED_IN(29); +/** + * The definition of camera capture start callback. The same as + * {@link ACameraCaptureSession_captureCallbacks#onCaptureStarted}, except that + * it has the frame number of the capture as well. + * + * @param context The optional application context provided by user in + * {@link ACameraCaptureSession_captureCallbacks}. + * @param session The camera capture session of interest. + * @param request The capture request that is starting. Note that this pointer points to a copy of + * capture request sent by application, so the address is different to what + * application sent but the content will match. This request will be freed by + * framework immediately after this callback returns. + * @param timestamp The timestamp when the capture is started. This timestamp will match + * {@link ACAMERA_SENSOR_TIMESTAMP} of the {@link ACameraMetadata} in + * {@link ACameraCaptureSession_captureCallbacks#onCaptureCompleted} callback. + * @param frameNumber the frame number of the capture started + */ +typedef void (*ACameraCaptureSession_captureCallback_startV2)( + void* context, ACameraCaptureSession* session, + const ACaptureRequest* request, int64_t timestamp, int64_t frameNumber); +/** + * This has the same functionality as ACameraCaptureSession_captureCallbacks, + * with the exception that captureCallback_startV2 callback is + * used, instead of captureCallback_start, to support retrieving the frame number. + */ +typedef struct ACameraCaptureSession_captureCallbacksV2 { + /** + * Same as ACameraCaptureSession_captureCallbacks + */ + void* context; + + /** + * Same as {@link ACameraCaptureSession_captureCallbacks#onCaptureStarted}, + * except that it has the frame number of the capture added in the parameter + * list. + */ + ACameraCaptureSession_captureCallback_startV2 onCaptureStarted; + + /** + * Same as {@link ACameraCaptureSession_captureCallbacks#onCaptureProgressed}. + */ + ACameraCaptureSession_captureCallback_result onCaptureProgressed; + + /** + * Same as {@link ACameraCaptureSession_captureCallbacks#onCaptureCompleted}. + */ + ACameraCaptureSession_captureCallback_result onCaptureCompleted; + + /** + * Same as {@link ACameraCaptureSession_captureCallbacks#onCaptureFailed}. + */ + ACameraCaptureSession_captureCallback_failed onCaptureFailed; + + /** + * Same as {@link ACameraCaptureSession_captureCallbacks#onCaptureSequenceCompleted}. + */ + ACameraCaptureSession_captureCallback_sequenceEnd onCaptureSequenceCompleted; + + /** + * Same as {@link ACameraCaptureSession_captureCallbacks#onCaptureSequenceAborted}. + */ + ACameraCaptureSession_captureCallback_sequenceAbort onCaptureSequenceAborted; + + /** + * Same as {@link ACameraCaptureSession_captureCallbacks#onCaptureBufferLost}. + */ + ACameraCaptureSession_captureCallback_bufferLost onCaptureBufferLost; + + +} ACameraCaptureSession_captureCallbacksV2; + +/** + * This has the same functionality as ACameraCaptureSession_logicalCamera_captureCallbacks, + * with the exception that an captureCallback_startV2 callback is + * used, instead of captureCallback_start, to support retrieving frame number. + */ +typedef struct ACameraCaptureSession_logicalCamera_captureCallbacksV2 { + /** + * Same as ACameraCaptureSession_captureCallbacks + */ + void* context; + + /** + * Same as {@link ACameraCaptureSession_captureCallbacks#onCaptureStarted}, + * except that it has the frame number of the capture added in the parameter + * list. + */ + ACameraCaptureSession_captureCallback_startV2 onCaptureStarted; + + + /** + * Same as {@link ACameraCaptureSession_captureCallbacks#onCaptureProgressed}. + */ + ACameraCaptureSession_captureCallback_result onCaptureProgressed; + + /** + * Same as + * {@link ACameraCaptureSession_logicalCamera_captureCallbacks#onLogicalCaptureCompleted}. + */ + ACameraCaptureSession_logicalCamera_captureCallback_result onLogicalCameraCaptureCompleted; + + /** + * This callback is called instead of {@link onLogicalCameraCaptureCompleted} when the + * camera device failed to produce a capture result for the + * request. + * + *

Other requests are unaffected, and some or all image buffers from + * the capture may have been pushed to their respective output + * streams.

+ * + *

Note that the ACaptureRequest pointer in the callback will not match what application has + * submitted, but the contents the ACaptureRequest will match what application submitted.

+ * + * @see ALogicalCameraCaptureFailure + */ + ACameraCaptureSession_logicalCamera_captureCallback_failed onLogicalCameraCaptureFailed; + + /** + * Same as {@link ACameraCaptureSession_captureCallbacks#onCaptureSequenceCompleted}. + */ + ACameraCaptureSession_captureCallback_sequenceEnd onCaptureSequenceCompleted; + + /** + * Same as {@link ACameraCaptureSession_captureCallbacks#onCaptureSequenceAborted}. + */ + ACameraCaptureSession_captureCallback_sequenceAbort onCaptureSequenceAborted; + + /** + * Same as {@link ACameraCaptureSession_captureCallbacks#onCaptureBufferLost}. + */ + ACameraCaptureSession_captureCallback_bufferLost onCaptureBufferLost; + +} ACameraCaptureSession_logicalCamera_captureCallbacksV2; + +/** + * This has the same functionality as ACameraCaptureSession_capture, with added + * support for v2 of camera callbacks, where the onCaptureStarted callback + * adds frame number in its parameter list. + */ +camera_status_t ACameraCaptureSession_captureV2( + ACameraCaptureSession* session, + /*optional*/ACameraCaptureSession_captureCallbacksV2* callbacks, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId) __INTRODUCED_IN(33); + +/** + * This has the same functionality as ACameraCaptureSession_logical_setRepeatingRequest, with added + * support for v2 of logical multi-camera callbacks where the onCaptureStarted + * callback adds frame number in its parameter list. + */ +camera_status_t ACameraCaptureSession_setRepeatingRequestV2( + ACameraCaptureSession* session, + /*optional*/ACameraCaptureSession_captureCallbacksV2* callbacks, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId) __INTRODUCED_IN(33); + +/** + * This has the same functionality as ACameraCaptureSession_logical_capture, with added + * support for v2 of logical multi-camera callbacks where the onCaptureStarted callback + * adds frame number in its parameter list. + */ +camera_status_t ACameraCaptureSession_logicalCamera_captureV2( + ACameraCaptureSession* session, + /*optional*/ACameraCaptureSession_logicalCamera_captureCallbacksV2* callbacks, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId) __INTRODUCED_IN(33); + +/** + * This has the same functionality as ACameraCaptureSession_logical_setRepeatingRequest, with added + * support for v2 of logical multi-camera callbacks where the onCaptureStarted + * callback adds frame number in its parameter list. + */ +camera_status_t ACameraCaptureSession_logicalCamera_setRepeatingRequestV2( + ACameraCaptureSession* session, + /*optional*/ACameraCaptureSession_logicalCamera_captureCallbacksV2* callbacks, + int numRequests, ACaptureRequest** requests, + /*optional*/int* captureSequenceId) __INTRODUCED_IN(33); + __END_DECLS #endif /* _NDK_CAMERA_CAPTURE_SESSION_H */ diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h index 816303c4088c41ad65be8cf5128ae20610de861d..b6f8552ae1b5a5760e2ef4436a23f16a44a71727 100644 --- a/camera/ndk/include/camera/NdkCameraMetadataTags.h +++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h @@ -72,6 +72,8 @@ typedef enum acamera_metadata_section { ACAMERA_DISTORTION_CORRECTION, ACAMERA_HEIC, ACAMERA_HEIC_INFO, + ACAMERA_AUTOMOTIVE, + ACAMERA_AUTOMOTIVE_LENS, ACAMERA_SECTION_COUNT, ACAMERA_VENDOR = 0x8000 @@ -115,6 +117,8 @@ typedef enum acamera_metadata_section_start { << 16, ACAMERA_HEIC_START = ACAMERA_HEIC << 16, ACAMERA_HEIC_INFO_START = ACAMERA_HEIC_INFO << 16, + ACAMERA_AUTOMOTIVE_START = ACAMERA_AUTOMOTIVE << 16, + ACAMERA_AUTOMOTIVE_LENS_START = ACAMERA_AUTOMOTIVE_LENS << 16, ACAMERA_VENDOR_START = ACAMERA_VENDOR << 16 } acamera_metadata_section_start_t; @@ -517,6 +521,14 @@ typedef enum acamera_metadata_tag { * region and output only the intersection rectangle as the metering region in the result * metadata. If the region is entirely outside the crop region, it will be ignored and * not reported in the result metadata.

+ *

When setting the AE metering regions, the application must consider the additional + * crop resulted from the aspect ratio differences between the preview stream and + * ACAMERA_SCALER_CROP_REGION. For example, if the ACAMERA_SCALER_CROP_REGION is the full + * active array size with 4:3 aspect ratio, and the preview stream is 16:9, + * the boundary of AE regions will be [0, y_crop] and + * [active_width, active_height - 2 * y_crop] rather than [0, 0] and + * [active_width, active_height], where y_crop is the additional crop due to aspect ratio + * mismatch.

*

Starting from API level 30, the coordinate system of activeArraySize or * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not * pre-zoom field of view. This means that the same aeRegions values at different @@ -718,6 +730,14 @@ typedef enum acamera_metadata_tag { * region and output only the intersection rectangle as the metering region in the result * metadata. If the region is entirely outside the crop region, it will be ignored and * not reported in the result metadata.

+ *

When setting the AF metering regions, the application must consider the additional + * crop resulted from the aspect ratio differences between the preview stream and + * ACAMERA_SCALER_CROP_REGION. For example, if the ACAMERA_SCALER_CROP_REGION is the full + * active array size with 4:3 aspect ratio, and the preview stream is 16:9, + * the boundary of AF regions will be [0, y_crop] and + * [active_width, active_height - 2 * y_crop] rather than [0, 0] and + * [active_width, active_height], where y_crop is the additional crop due to aspect ratio + * mismatch.

*

Starting from API level 30, the coordinate system of activeArraySize or * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not * pre-zoom field of view. This means that the same afRegions values at different @@ -838,7 +858,7 @@ typedef enum acamera_metadata_tag { * routine is enabled, overriding the application's selected * ACAMERA_COLOR_CORRECTION_TRANSFORM, ACAMERA_COLOR_CORRECTION_GAINS and * ACAMERA_COLOR_CORRECTION_MODE. Note that when ACAMERA_CONTROL_AE_MODE - * is OFF, the behavior of AWB is device dependent. It is recommened to + * is OFF, the behavior of AWB is device dependent. It is recommended to * also set AWB mode to OFF or lock AWB by using ACAMERA_CONTROL_AWB_LOCK before * setting AE mode to OFF.

*

When set to the OFF mode, the camera device's auto-white balance @@ -913,6 +933,14 @@ typedef enum acamera_metadata_tag { * region and output only the intersection rectangle as the metering region in the result * metadata. If the region is entirely outside the crop region, it will be ignored and * not reported in the result metadata.

+ *

When setting the AWB metering regions, the application must consider the additional + * crop resulted from the aspect ratio differences between the preview stream and + * ACAMERA_SCALER_CROP_REGION. For example, if the ACAMERA_SCALER_CROP_REGION is the full + * active array size with 4:3 aspect ratio, and the preview stream is 16:9, + * the boundary of AWB regions will be [0, y_crop] and + * [active_width, active_height - 2 * y_crop] rather than [0, 0] and + * [active_width, active_height], where y_crop is the additional crop due to aspect ratio + * mismatch.

*

Starting from API level 30, the coordinate system of activeArraySize or * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not * pre-zoom field of view. This means that the same awbRegions values at different @@ -961,13 +989,15 @@ typedef enum acamera_metadata_tag { * *

This control (except for MANUAL) is only effective if * ACAMERA_CONTROL_MODE != OFF and any 3A routine is active.

- *

All intents are supported by all devices, except that: - * * ZERO_SHUTTER_LAG will be supported if ACAMERA_REQUEST_AVAILABLE_CAPABILITIES contains - * PRIVATE_REPROCESSING or YUV_REPROCESSING. - * * MANUAL will be supported if ACAMERA_REQUEST_AVAILABLE_CAPABILITIES contains - * MANUAL_SENSOR. - * * MOTION_TRACKING will be supported if ACAMERA_REQUEST_AVAILABLE_CAPABILITIES contains - * MOTION_TRACKING.

+ *

All intents are supported by all devices, except that:

+ *
    + *
  • ZERO_SHUTTER_LAG will be supported if ACAMERA_REQUEST_AVAILABLE_CAPABILITIES contains + * PRIVATE_REPROCESSING or YUV_REPROCESSING.
  • + *
  • MANUAL will be supported if ACAMERA_REQUEST_AVAILABLE_CAPABILITIES contains + * MANUAL_SENSOR.
  • + *
  • MOTION_TRACKING will be supported if ACAMERA_REQUEST_AVAILABLE_CAPABILITIES contains + * MOTION_TRACKING.
  • + *
* * @see ACAMERA_CONTROL_MODE * @see ACAMERA_REQUEST_AVAILABLE_CAPABILITIES @@ -1090,6 +1120,15 @@ typedef enum acamera_metadata_tag { * (ACAMERA_LENS_OPTICAL_STABILIZATION_MODE), turning both modes on may * produce undesirable interaction, so it is recommended not to enable * both at the same time.

+ *

If video stabilization is set to "PREVIEW_STABILIZATION", + * ACAMERA_LENS_OPTICAL_STABILIZATION_MODE is overridden. The camera sub-system may choose + * to turn on hardware based image stabilization in addition to software based stabilization + * if it deems that appropriate. + * This key may be a part of the available session keys, which camera clients may + * query via + * {@link ACameraManager_getCameraCharacteristics }. + * If this is the case, changing this key over the life-time of a capture session may + * cause delays / glitches.

* * @see ACAMERA_CONTROL_VIDEO_STABILIZATION_MODE * @see ACAMERA_LENS_OPTICAL_STABILIZATION_MODE @@ -1449,7 +1488,7 @@ typedef enum acamera_metadata_tag { * Any state (excluding LOCKED) | ACAMERA_CONTROL_AE_PRECAPTURE_TRIGGER is START, sequence done | FLASH_REQUIRED | Converged but too dark w/o flash after a precapture sequence, transient states are skipped by camera device. * Any state (excluding LOCKED) | ACAMERA_CONTROL_AE_PRECAPTURE_TRIGGER is START, sequence done | CONVERGED | Converged after a precapture sequence, transient states are skipped by camera device. * Any state (excluding LOCKED) | ACAMERA_CONTROL_AE_PRECAPTURE_TRIGGER is CANCEL, converged | FLASH_REQUIRED | Converged but too dark w/o flash after a precapture sequence is canceled, transient states are skipped by camera device. - * Any state (excluding LOCKED) | ACAMERA_CONTROL_AE_PRECAPTURE_TRIGGER is CANCEL, converged | CONVERGED | Converged after a precapture sequenceis canceled, transient states are skipped by camera device. + * Any state (excluding LOCKED) | ACAMERA_CONTROL_AE_PRECAPTURE_TRIGGER is CANCEL, converged | CONVERGED | Converged after a precapture sequences canceled, transient states are skipped by camera device. * CONVERGED | Camera device finished AE scan | FLASH_REQUIRED | Converged but too dark w/o flash after a new scan, transient states are skipped by camera device. * FLASH_REQUIRED | Camera device finished AE scan | CONVERGED | Converged after a new scan, transient states are skipped by camera device.

* @@ -1685,7 +1724,7 @@ typedef enum acamera_metadata_tag { *

* *

Devices support post RAW sensitivity boost will advertise - * ACAMERA_CONTROL_POST_RAW_SENSITIVITY_BOOST key for controling + * ACAMERA_CONTROL_POST_RAW_SENSITIVITY_BOOST key for controlling * post RAW sensitivity boost.

*

This key will be null for devices that do not support any RAW format * outputs. For devices that do support RAW format outputs, this key will always @@ -2144,6 +2183,51 @@ typedef enum acamera_metadata_tag { */ ACAMERA_FLASH_INFO_AVAILABLE = // byte (acamera_metadata_enum_android_flash_info_available_t) ACAMERA_FLASH_INFO_START, + /** + *

Maximum flashlight brightness level.

+ * + *

Type: int32

+ * + *

This tag may appear in: + *

    + *
  • ACameraMetadata from ACameraManager_getCameraCharacteristics
  • + *

+ * + *

If this value is greater than 1, then the device supports controlling the + * flashlight brightness level via + * CameraManager#turnOnTorchWithStrengthLevel. + * If this value is equal to 1, flashlight brightness control is not supported. + * The value for this key will be null for devices with no flash unit.

+ */ + ACAMERA_FLASH_INFO_STRENGTH_MAXIMUM_LEVEL = // int32 + ACAMERA_FLASH_INFO_START + 2, + /** + *

Default flashlight brightness level to be set via + * CameraManager#turnOnTorchWithStrengthLevel.

+ * + *

Type: int32

+ * + *

This tag may appear in: + *

    + *
  • ACameraMetadata from ACameraManager_getCameraCharacteristics
  • + *

+ * + *

If flash unit is available this will be greater than or equal to 1 and less + * or equal to ACAMERA_FLASH_INFO_STRENGTH_MAXIMUM_LEVEL.

+ *

Setting flashlight brightness above the default level + * (i.e.ACAMERA_FLASH_INFO_STRENGTH_DEFAULT_LEVEL) may make the device more + * likely to reach thermal throttling conditions and slow down, or drain the + * battery quicker than normal. To minimize such issues, it is recommended to + * start the flashlight at this default brightness until a user explicitly requests + * a brighter level. + * Note that the value for this key will be null for devices with no flash unit. + * The default level should always be > 0.

+ * + * @see ACAMERA_FLASH_INFO_STRENGTH_DEFAULT_LEVEL + * @see ACAMERA_FLASH_INFO_STRENGTH_MAXIMUM_LEVEL + */ + ACAMERA_FLASH_INFO_STRENGTH_DEFAULT_LEVEL = // int32 + ACAMERA_FLASH_INFO_START + 3, ACAMERA_FLASH_INFO_END, /** @@ -2341,7 +2425,7 @@ typedef enum acamera_metadata_tag { * and keep jpeg and thumbnail image data unrotated. *
  • Rotate the jpeg and thumbnail image data and not set * EXIF orientation flag. In this - * case, LIMITED or FULL hardware level devices will report rotated thumnail size in + * case, LIMITED or FULL hardware level devices will report rotated thumbnail size in * capture result, so the width and height will be interchanged if 90 or 270 degree * orientation is requested. LEGACY device will always report unrotated thumbnail * size.
  • @@ -2370,7 +2454,7 @@ typedef enum acamera_metadata_tag { * *

    This list will include at least one non-zero resolution, plus (0,0) for indicating no * thumbnail should be generated.

    - *

    Below condiditions will be satisfied for this size list:

    + *

    Below conditions will be satisfied for this size list:

    *
      *
    • The sizes will be sorted by increasing pixel area (width x height). * If several resolutions have the same area, they will be sorted by increasing width.
    • @@ -2526,12 +2610,18 @@ typedef enum acamera_metadata_tag { *

      If a camera device supports both OIS and digital image stabilization * (ACAMERA_CONTROL_VIDEO_STABILIZATION_MODE), turning both modes on may produce undesirable * interaction, so it is recommended not to enable both at the same time.

      + *

      If ACAMERA_CONTROL_VIDEO_STABILIZATION_MODE is set to "PREVIEW_STABILIZATION", + * ACAMERA_LENS_OPTICAL_STABILIZATION_MODE is overridden. The camera sub-system may choose + * to turn on hardware based image stabilization in addition to software based stabilization + * if it deems that appropriate. This key's value in the capture result will reflect which + * OIS mode was chosen.

      *

      Not all devices will support OIS; see * ACAMERA_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION for * available controls.

      * * @see ACAMERA_CONTROL_VIDEO_STABILIZATION_MODE * @see ACAMERA_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION + * @see ACAMERA_LENS_OPTICAL_STABILIZATION_MODE */ ACAMERA_LENS_OPTICAL_STABILIZATION_MODE = // byte (acamera_metadata_enum_android_lens_optical_stabilization_mode_t) ACAMERA_LENS_START + 4, @@ -2634,6 +2724,9 @@ typedef enum acamera_metadata_tag { * with PRIMARY_CAMERA.

      *

      When ACAMERA_LENS_POSE_REFERENCE is UNDEFINED, this position cannot be accurately * represented by the camera device, and will be represented as (0, 0, 0).

      + *

      When ACAMERA_LENS_POSE_REFERENCE is AUTOMOTIVE, then this position is relative to the + * origin of the automotive sensor coordinate system, which is at the center of the rear + * axle.

      * * @see ACAMERA_LENS_DISTORTION * @see ACAMERA_LENS_INTRINSIC_CALIBRATION @@ -2675,7 +2768,7 @@ typedef enum acamera_metadata_tag { *

      When the state is STATIONARY, the lens parameters are not changing. This could be * either because the parameters are all fixed, or because the lens has had enough * time to reach the most recently-requested values. - * If all these lens parameters are not changable for a camera device, as listed below:

      + * If all these lens parameters are not changeable for a camera device, as listed below:

      *
        *
      • Fixed focus (ACAMERA_LENS_INFO_MINIMUM_FOCUS_DISTANCE == 0), which means * ACAMERA_LENS_FOCUS_DISTANCE parameter will always be 0.
      • @@ -3127,7 +3220,7 @@ typedef enum acamera_metadata_tag { * the camera device. Using more streams simultaneously may require more hardware and * CPU resources that will consume more power. The image format for an output stream can * be any supported format provided by ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS. - * The formats defined in ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS can be catergorized + * The formats defined in ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS can be categorized * into the 3 stream types as below:

        *
          *
        • Processed (but stalling): any non-RAW format with a stallDurations > 0. @@ -3354,7 +3447,7 @@ typedef enum acamera_metadata_tag { * but clients should be aware and expect delays during their application. * An example usage scenario could look like this:

          *
            - *
          • The camera client starts by quering the session parameter key list via + *
          • The camera client starts by querying the session parameter key list via * {@link ACameraManager_getCameraCharacteristics }.
          • *
          • Before triggering the capture session create sequence, a capture request * must be built via @@ -3393,16 +3486,36 @@ typedef enum acamera_metadata_tag { *

          * *

          This is a subset of ACAMERA_REQUEST_AVAILABLE_REQUEST_KEYS which contains a list - * of keys that can be overridden using Builder#setPhysicalCameraKey. + * of keys that can be overridden using + * Builder#setPhysicalCameraKey. * The respective value of such request key can be obtained by calling - * Builder#getPhysicalCameraKey. Capture requests that contain - * individual physical device requests must be built via + * Builder#getPhysicalCameraKey. + * Capture requests that contain individual physical device requests must be built via * Set).

          * * @see ACAMERA_REQUEST_AVAILABLE_REQUEST_KEYS */ ACAMERA_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS = // int32[n] ACAMERA_REQUEST_START + 17, + /** + *

          A map of all available 10-bit dynamic range profiles along with their + * capture request constraints.

          + * + *

          Type: int64[n*3] (acamera_metadata_enum_android_request_available_dynamic_range_profiles_map_t)

          + * + *

          This tag may appear in: + *

            + *
          • ACameraMetadata from ACameraManager_getCameraCharacteristics
          • + *

          + * + *

          Devices supporting the 10-bit output capability + * CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT + * must list their supported dynamic range profiles. In case the camera is not able to + * support every possible profile combination within a single capture request, then the + * constraints must be listed here as well.

          + */ + ACAMERA_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP = // int64[n*3] (acamera_metadata_enum_android_request_available_dynamic_range_profiles_map_t) + ACAMERA_REQUEST_START + 19, ACAMERA_REQUEST_END, /** @@ -3601,7 +3714,7 @@ typedef enum acamera_metadata_tag { * IMPLEMENTATION_DEFINED | same as YUV_420_888 | Any |

          *

          For applications targeting SDK version 31 or newer, if the mobile device declares to be * media performance class 12 or higher by setting - * MEDIA_PERFORMANCE_CLASS to be 31 or larger, + * VERSION#MEDIA_PERFORMANCE_CLASS to be 31 or larger, * the primary camera devices (first rear/front camera in the camera ID list) will not * support JPEG sizes smaller than 1080p. If the application configures a JPEG stream * smaller than 1080p, the camera device will round up the JPEG image size to at least @@ -3620,7 +3733,7 @@ typedef enum acamera_metadata_tag { * IMPLEMENTATION_DEFINED | same as YUV_420_888 | Any |

          *

          For applications targeting SDK version 31 or newer, if the mobile device doesn't declare * to be media performance class 12 or better by setting - * MEDIA_PERFORMANCE_CLASS to be 31 or larger, + * VERSION#MEDIA_PERFORMANCE_CLASS to be 31 or larger, * or if the camera device isn't a primary rear/front camera, the minimum required output * stream configurations are the same as for applications targeting SDK version older than * 31.

          @@ -4114,19 +4227,70 @@ typedef enum acamera_metadata_tag { * to output different resolution images depending on the current active physical camera or * pixel mode. With multi-resolution input streams, the camera device can reprocess images * of different resolutions from different physical cameras or sensor pixel modes.

          - *

          When set to TRUE: - * * For a logical multi-camera, the camera framework derives + *

          When set to TRUE:

          + *
            + *
          • For a logical multi-camera, the camera framework derives * android.scaler.multiResolutionStreamConfigurationMap by combining the * ACAMERA_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS from its physical - * cameras. - * * For an ultra-high resolution sensor camera, the camera framework directly copies + * cameras.
          • + *
          • For an ultra-high resolution sensor camera, the camera framework directly copies * the value of ACAMERA_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS to - * android.scaler.multiResolutionStreamConfigurationMap.

            + * android.scaler.multiResolutionStreamConfigurationMap.
          • + *
          * * @see ACAMERA_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS */ ACAMERA_SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED = // byte (acamera_metadata_enum_android_scaler_multi_resolution_stream_supported_t) ACAMERA_SCALER_START + 24, + /** + *

          The stream use cases supported by this camera device.

          + * + *

          Type: int64[n] (acamera_metadata_enum_android_scaler_available_stream_use_cases_t)

          + * + *

          This tag may appear in: + *

            + *
          • ACameraMetadata from ACameraManager_getCameraCharacteristics
          • + *

          + * + *

          The stream use case indicates the purpose of a particular camera stream from + * the end-user perspective. Some examples of camera use cases are: preview stream for + * live viewfinder shown to the user, still capture for generating high quality photo + * capture, video record for encoding the camera output for the purpose of future playback, + * and video call for live realtime video conferencing.

          + *

          With this flag, the camera device can optimize the image processing pipeline + * parameters, such as tuning, sensor mode, and ISP settings, independent of + * the properties of the immediate camera output surface. For example, if the output + * surface is a SurfaceTexture, the stream use case flag can be used to indicate whether + * the camera frames eventually go to display, video encoder, + * still image capture, or all of them combined.

          + *

          The application sets the use case of a camera stream by calling + * OutputConfiguration#setStreamUseCase.

          + *

          A camera device with + * CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE + * capability must support the following stream use cases:

          + *
            + *
          • DEFAULT
          • + *
          • PREVIEW
          • + *
          • STILL_CAPTURE
          • + *
          • VIDEO_RECORD
          • + *
          • PREVIEW_VIDEO_STILL
          • + *
          • VIDEO_CALL
          • + *
          + *

          The guaranteed stream combinations related to stream use case for a camera device with + * CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE + * capability is documented in the camera device + * guideline. The + * application is strongly recommended to use one of the guaranteed stream combinations. + * If the application creates a session with a stream combination not in the guaranteed + * list, or with mixed DEFAULT and non-DEFAULT use cases within the same session, + * the camera device may ignore some stream use cases due to hardware constraints + * and implementation details.

          + *

          For stream combinations not covered by the stream use case mandatory lists, such as + * reprocessable session, constrained high speed session, or RAW stream combinations, the + * application should leave stream use cases within the session as DEFAULT.

          + */ + ACAMERA_SCALER_AVAILABLE_STREAM_USE_CASES = // int64[n] (acamera_metadata_enum_android_scaler_available_stream_use_cases_t) + ACAMERA_SCALER_START + 25, ACAMERA_SCALER_END, /** @@ -4680,7 +4844,7 @@ typedef enum acamera_metadata_tag { * noise model used here is:

          *

          N(x) = sqrt(Sx + O)

          *

          Where x represents the recorded signal of a CFA channel normalized to - * the range [0, 1], and S and O are the noise model coeffiecients for + * the range [0, 1], and S and O are the noise model coefficients for * that channel.

          *

          A more detailed description of the noise model can be found in the * Adobe DNG specification for the NoiseProfile tag.

          @@ -4729,7 +4893,7 @@ typedef enum acamera_metadata_tag { *
        • 1.20 <= R >= 1.03 will require some software * correction to avoid demosaic errors (3-20% divergence).
        • *
        • R > 1.20 will require strong software correction to produce - * a usuable image (>20% divergence).
        • + * a usable image (>20% divergence). *
        *

        Starting from Android Q, this key will not be present for a MONOCHROME camera, even if * the camera device has RAW capability.

        @@ -4986,7 +5150,7 @@ typedef enum acamera_metadata_tag { *
      • ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks
      • *

      * - *

      This key will only be present in devices advertisting the + *

      This key will only be present in devices advertising the * CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR * capability which also advertise REMOSAIC_REPROCESSING capability. On all other devices * RAW targets will have a regular bayer pattern.

      @@ -6171,9 +6335,11 @@ typedef enum acamera_metadata_tag { *
    • ACaptureRequest
    • *

    * - *

    The tonemap curve will be defined the following formula: - * * OUT = pow(IN, 1.0 / gamma) - * where IN and OUT is the input pixel value scaled to range [0.0, 1.0], + *

    The tonemap curve will be defined the following formula:

    + *
      + *
    • OUT = pow(IN, 1.0 / gamma)
    • + *
    + *

    where IN and OUT is the input pixel value scaled to range [0.0, 1.0], * pow is the power function and gamma is the gamma value specified by this * key.

    *

    The same curve will be applied to all color channels. The camera device @@ -7071,6 +7237,87 @@ typedef enum acamera_metadata_tag { ACAMERA_HEIC_START + 5, ACAMERA_HEIC_END, + /** + *

    Location of the cameras on the automotive devices.

    + * + *

    Type: byte (acamera_metadata_enum_android_automotive_location_t)

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraManager_getCameraCharacteristics
    • + *

    + * + *

    This enum defines the locations of the cameras relative to the vehicle body frame on + * the automotive sensor coordinate system. + * If the system has FEATURE_AUTOMOTIVE, the camera will have this entry in its static + * metadata.

    + *
      + *
    • INTERIOR is the inside of the vehicle body frame (or the passenger cabin).
    • + *
    • EXTERIOR is the outside of the vehicle body frame.
    • + *
    • EXTRA is the extra vehicle such as a trailer.
    • + *
    + *

    Each side of the vehicle body frame on this coordinate system is defined as below:

    + *
      + *
    • FRONT is where the Y-axis increases toward.
    • + *
    • REAR is where the Y-axis decreases toward.
    • + *
    • LEFT is where the X-axis decreases toward.
    • + *
    • RIGHT is where the X-axis increases toward.
    • + *
    + *

    If the camera has either EXTERIOR_OTHER or EXTRA_OTHER, its static metadata will list + * the following entries, so that applications can determine the camera's exact location:

    + *
      + *
    • ACAMERA_LENS_POSE_REFERENCE
    • + *
    • ACAMERA_LENS_POSE_ROTATION
    • + *
    • ACAMERA_LENS_POSE_TRANSLATION
    • + *
    + * + * @see ACAMERA_LENS_POSE_REFERENCE + * @see ACAMERA_LENS_POSE_ROTATION + * @see ACAMERA_LENS_POSE_TRANSLATION + */ + ACAMERA_AUTOMOTIVE_LOCATION = // byte (acamera_metadata_enum_android_automotive_location_t) + ACAMERA_AUTOMOTIVE_START, + ACAMERA_AUTOMOTIVE_END, + + /** + *

    The direction of the camera faces relative to the vehicle body frame and the + * passenger seats.

    + * + *

    Type: byte[n] (acamera_metadata_enum_android_automotive_lens_facing_t)

    + * + *

    This tag may appear in: + *

      + *
    • ACameraMetadata from ACameraManager_getCameraCharacteristics
    • + *

    + * + *

    This enum defines the lens facing characteristic of the cameras on the automotive + * devices with locations ACAMERA_AUTOMOTIVE_LOCATION defines. If the system has + * FEATURE_AUTOMOTIVE, the camera will have this entry in its static metadata.

    + *

    When ACAMERA_AUTOMOTIVE_LOCATION is INTERIOR, this has one or more INTERIOR_* + * values or a single EXTERIOR_* value. When this has more than one INTERIOR_*, + * the first value must be the one for the seat closest to the optical axis. If this + * contains INTERIOR_OTHER, all other values will be ineffective.

    + *

    When ACAMERA_AUTOMOTIVE_LOCATION is EXTERIOR_* or EXTRA, this has a single + * EXTERIOR_* value.

    + *

    If a camera has INTERIOR_OTHER or EXTERIOR_OTHER, or more than one camera is at the + * same location and facing the same direction, their static metadata will list the + * following entries, so that applications can determine their lenses' exact facing + * directions:

    + *
      + *
    • ACAMERA_LENS_POSE_REFERENCE
    • + *
    • ACAMERA_LENS_POSE_ROTATION
    • + *
    • ACAMERA_LENS_POSE_TRANSLATION
    • + *
    + * + * @see ACAMERA_AUTOMOTIVE_LOCATION + * @see ACAMERA_LENS_POSE_REFERENCE + * @see ACAMERA_LENS_POSE_ROTATION + * @see ACAMERA_LENS_POSE_TRANSLATION + */ + ACAMERA_AUTOMOTIVE_LENS_FACING = // byte[n] (acamera_metadata_enum_android_automotive_lens_facing_t) + ACAMERA_AUTOMOTIVE_LENS_START, + ACAMERA_AUTOMOTIVE_LENS_END, + } acamera_metadata_tag_t; /** @@ -7972,6 +8219,17 @@ typedef enum acamera_metadata_enum_acamera_control_video_stabilization_mode { */ ACAMERA_CONTROL_VIDEO_STABILIZATION_MODE_ON = 1, + /** + *

    Preview stabilization, where the preview in addition to all other non-RAW streams are + * stabilized with the same quality of stabilization, is enabled. This mode aims to give + * clients a 'what you see is what you get' effect. In this mode, the FoV reduction will + * be a maximum of 20 % both horizontally and vertically + * (10% from left, right, top, bottom) for the given zoom ratio / crop region. + * The resultant FoV will also be the same across all processed streams + * (that have the same aspect ratio).

    + */ + ACAMERA_CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION = 2, + } acamera_metadata_enum_android_control_video_stabilization_mode_t; // ACAMERA_CONTROL_AE_STATE @@ -8452,6 +8710,14 @@ typedef enum acamera_metadata_enum_acamera_lens_pose_reference { */ ACAMERA_LENS_POSE_REFERENCE_UNDEFINED = 2, + /** + *

    The value of ACAMERA_LENS_POSE_TRANSLATION is relative to the origin of the + * automotive sensor coordinate system, which is at the center of the rear axle.

    + * + * @see ACAMERA_LENS_POSE_TRANSLATION + */ + ACAMERA_LENS_POSE_REFERENCE_AUTOMOTIVE = 3, + } acamera_metadata_enum_android_lens_pose_reference_t; @@ -8765,7 +9031,7 @@ typedef enum acamera_metadata_enum_acamera_request_available_capabilities { * for the largest YUV_420_888 size.

    *

    If the device supports the {@link AIMAGE_FORMAT_RAW10 }, {@link AIMAGE_FORMAT_RAW12 }, {@link AIMAGE_FORMAT_Y8 }, then those can also be * captured at the same rate as the maximum-size YUV_420_888 resolution is.

    - *

    In addition, the ACAMERA_SYNC_MAX_LATENCY field is guaranted to have a value between 0 + *

    In addition, the ACAMERA_SYNC_MAX_LATENCY field is guaranteed to have a value between 0 * and 4, inclusive. ACAMERA_CONTROL_AE_LOCK_AVAILABLE and ACAMERA_CONTROL_AWB_LOCK_AVAILABLE * are also guaranteed to be true so burst capture with these two locks ON yields * consistent image output.

    @@ -8931,7 +9197,7 @@ typedef enum acamera_metadata_enum_acamera_request_available_capabilities { * non-active physical cameras. For example, if the logical camera has a wide-ultrawide * configuration where the wide lens is the default, when the crop region is set to the * logical camera's active array size, (and the zoom ratio set to 1.0 starting from - * Android 11), a physical stream for the ultrawide camera may prefer outputing images + * Android 11), a physical stream for the ultrawide camera may prefer outputting images * with larger field-of-view than that of the wide camera for better stereo matching * margin or more robust motion tracking. At the same time, the physical non-RAW streams' * field of view must not be smaller than the requested crop region and zoom ratio, as @@ -9052,8 +9318,132 @@ typedef enum acamera_metadata_enum_acamera_request_available_capabilities { ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR = 16, + /** + *

    The camera device supports selecting a per-stream use case via + * OutputConfiguration#setStreamUseCase + * so that the device can optimize camera pipeline parameters such as tuning, sensor + * mode, or ISP settings for a specific user scenario. + * Some sample usages of this capability are:

    + *
      + *
    • Distinguish high quality YUV captures from a regular YUV stream where + * the image quality may not be as good as the JPEG stream, or
    • + *
    • Use one stream to serve multiple purposes: viewfinder, video recording and + * still capture. This is common with applications that wish to apply edits equally + * to preview, saved images, and saved videos.
    • + *
    + *

    This capability requires the camera device to support the following + * stream use cases:

    + *
      + *
    • DEFAULT for backward compatibility where the application doesn't set + * a stream use case
    • + *
    • PREVIEW for live viewfinder and in-app image analysis
    • + *
    • STILL_CAPTURE for still photo capture
    • + *
    • VIDEO_RECORD for recording video clips
    • + *
    • PREVIEW_VIDEO_STILL for one single stream used for viewfinder, video + * recording, and still capture.
    • + *
    • VIDEO_CALL for long running video calls
    • + *
    + *

    CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES + * lists all of the supported stream use cases.

    + *

    Refer to CameraDevice#createCaptureSession for the + * mandatory stream combinations involving stream use cases, which can also be queried + * via MandatoryStreamCombination.

    + */ + ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE = 19, + } acamera_metadata_enum_android_request_available_capabilities_t; +// ACAMERA_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP +typedef enum acamera_metadata_enum_acamera_request_available_dynamic_range_profiles_map { + /** + *

    8-bit SDR profile which is the default for all non 10-bit output capable devices.

    + */ + ACAMERA_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD = 0x1, + + /** + *

    10-bit pixel samples encoded using the Hybrid log-gamma transfer function.

    + */ + ACAMERA_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HLG10 = 0x2, + + /** + *

    10-bit pixel samples encoded using the SMPTE ST 2084 transfer function. + * This profile utilizes internal static metadata to increase the quality + * of the capture.

    + */ + ACAMERA_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10 = 0x4, + + /** + *

    10-bit pixel samples encoded using the SMPTE ST 2084 transfer function. + * In contrast to HDR10, this profile uses internal per-frame metadata + * to further enhance the quality of the capture.

    + */ + ACAMERA_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10_PLUS = 0x8, + + /** + *

    This is a camera mode for Dolby Vision capture optimized for a more scene + * accurate capture. This would typically differ from what a specific device + * might want to tune for a consumer optimized Dolby Vision general capture.

    + */ + ACAMERA_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF + = 0x10, + + /** + *

    This is the power optimized mode for 10-bit Dolby Vision HDR Reference Mode.

    + */ + ACAMERA_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF_PO + = 0x20, + + /** + *

    This is the camera mode for the default Dolby Vision capture mode for the + * specific device. This would be tuned by each specific device for consumer + * pleasing results that resonate with their particular audience. We expect + * that each specific device would have a different look for their default + * Dolby Vision capture.

    + */ + ACAMERA_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM + = 0x40, + + /** + *

    This is the power optimized mode for 10-bit Dolby Vision HDR device specific + * capture Mode.

    + */ + ACAMERA_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM_PO + = 0x80, + + /** + *

    This is the 8-bit version of the Dolby Vision reference capture mode optimized + * for scene accuracy.

    + */ + ACAMERA_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF + = 0x100, + + /** + *

    This is the power optimized mode for 8-bit Dolby Vision HDR Reference Mode.

    + */ + ACAMERA_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF_PO + = 0x200, + + /** + *

    This is the 8-bit version of device specific tuned and optimized Dolby Vision + * capture mode.

    + */ + ACAMERA_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM + = 0x400, + + /** + *

    This is the power optimized mode for 8-bit Dolby Vision HDR device specific + * capture Mode.

    + */ + ACAMERA_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM_PO + = 0x800, + + /** + * + */ + ACAMERA_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_MAX = 0x1000, + +} acamera_metadata_enum_android_request_available_dynamic_range_profiles_map_t; + // ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS typedef enum acamera_metadata_enum_acamera_scaler_available_stream_configurations { @@ -9144,6 +9534,20 @@ typedef enum acamera_metadata_enum_acamera_scaler_available_recommended_stream_c ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_PUBLIC_END = 0x7, + /** + *

    If supported, the recommended 10-bit output stream configurations must include + * a subset of the advertised ImageFormat#YCBCR_P010 and + * ImageFormat#PRIVATE outputs that are optimized for power + * and performance when registered along with a supported 10-bit dynamic range profile. + * see android.hardware.camera2.params.OutputConfiguration#setDynamicRangeProfile for + * details.

    + */ + ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_10BIT_OUTPUT + = 0x8, + + ACAMERA_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_PUBLIC_END_3_8 + = 0x9, + /** *

    Vendor defined use cases. These depend on the vendor implementation.

    */ @@ -9225,6 +9629,76 @@ typedef enum acamera_metadata_enum_acamera_scaler_multi_resolution_stream_suppor } acamera_metadata_enum_android_scaler_multi_resolution_stream_supported_t; +// ACAMERA_SCALER_AVAILABLE_STREAM_USE_CASES +typedef enum acamera_metadata_enum_acamera_scaler_available_stream_use_cases { + /** + *

    Default stream use case.

    + *

    This use case is the same as when the application doesn't set any use case for + * the stream. The camera device uses the properties of the output target, such as + * format, dataSpace, or surface class type, to optimize the image processing pipeline.

    + */ + ACAMERA_SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT = 0x0, + + /** + *

    Live stream shown to the user.

    + *

    Optimized for performance and usability as a viewfinder, but not necessarily for + * image quality. The output is not meant to be persisted as saved images or video.

    + *

    No stall if ACAMERA_CONTROL_* are set to FAST. There may be stall if + * they are set to HIGH_QUALITY. This use case has the same behavior as the + * default SurfaceView and SurfaceTexture targets. Additionally, this use case can be + * used for in-app image analysis.

    + */ + ACAMERA_SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW = 0x1, + + /** + *

    Still photo capture.

    + *

    Optimized for high-quality high-resolution capture, and not expected to maintain + * preview-like frame rates.

    + *

    The stream may have stalls regardless of whether ACAMERA_CONTROL_* is HIGH_QUALITY. + * This use case has the same behavior as the default JPEG and RAW related formats.

    + */ + ACAMERA_SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE = 0x2, + + /** + *

    Recording video clips.

    + *

    Optimized for high-quality video capture, including high-quality image stabilization + * if supported by the device and enabled by the application. As a result, may produce + * output frames with a substantial lag from real time, to allow for highest-quality + * stabilization or other processing. As such, such an output is not suitable for drawing + * to screen directly, and is expected to be persisted to disk or similar for later + * playback or processing. Only streams that set the VIDEO_RECORD use case are guaranteed + * to have video stabilization applied when the video stabilization control is set + * to ON, as opposed to PREVIEW_STABILIZATION.

    + *

    This use case has the same behavior as the default MediaRecorder and MediaCodec + * targets.

    + */ + ACAMERA_SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD = 0x3, + + /** + *

    One single stream used for combined purposes of preview, video, and still capture.

    + *

    For such multi-purpose streams, the camera device aims to make the best tradeoff + * between the individual use cases. For example, the STILL_CAPTURE use case by itself + * may have stalls for achieving best image quality. But if combined with PREVIEW and + * VIDEO_RECORD, the camera device needs to trade off the additional image processing + * for speed so that preview and video recording aren't slowed down.

    + *

    Similarly, VIDEO_RECORD may produce frames with a substantial lag, but + * PREVIEW_VIDEO_STILL must have minimal output delay. This means that to enable video + * stabilization with this use case, the device must support and the app must select the + * PREVIEW_STABILIZATION mode for video stabilization.

    + */ + ACAMERA_SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL = 0x4, + + /** + *

    Long-running video call optimized for both power efficiency and video quality.

    + *

    The camera sensor may run in a lower-resolution mode to reduce power consumption + * at the cost of some image and digital zoom quality. Unlike VIDEO_RECORD, VIDEO_CALL + * outputs are expected to work in dark conditions, so are usually accompanied with + * variable frame rate settings to allow sufficient exposure time in low light.

    + */ + ACAMERA_SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL = 0x5, + +} acamera_metadata_enum_android_scaler_available_stream_use_cases_t; + // ACAMERA_SENSOR_REFERENCE_ILLUMINANT1 typedef enum acamera_metadata_enum_acamera_sensor_reference_illuminant1 { @@ -9642,7 +10116,7 @@ typedef enum acamera_metadata_enum_acamera_tonemap_mode { ACAMERA_TONEMAP_MODE_HIGH_QUALITY = 2, /** - *

    Use the gamma value specified in ACAMERA_TONEMAP_GAMMA to peform + *

    Use the gamma value specified in ACAMERA_TONEMAP_GAMMA to perform * tonemapping.

    *

    All color enhancement and tonemapping must be disabled, except * for applying the tonemapping curve specified by ACAMERA_TONEMAP_GAMMA.

    @@ -9654,7 +10128,7 @@ typedef enum acamera_metadata_enum_acamera_tonemap_mode { /** *

    Use the preset tonemapping curve specified in - * ACAMERA_TONEMAP_PRESET_CURVE to peform tonemapping.

    + * ACAMERA_TONEMAP_PRESET_CURVE to perform tonemapping.

    *

    All color enhancement and tonemapping must be disabled, except * for applying the tonemapping curve specified by * ACAMERA_TONEMAP_PRESET_CURVE.

    @@ -9752,7 +10226,7 @@ typedef enum acamera_metadata_enum_acamera_info_supported_hardware_level { * fire the flash for flash power metering during precapture, and then fire the flash * for the final capture, if a flash is available on the device and the AE mode is set to * enable the flash.

    - *

    Devices that initially shipped with Android version Q or newer will not include any LEGACY-level devices.

    + *

    Devices that initially shipped with Android version Q or newer will not include any LEGACY-level devices.

    * * @see ACAMERA_CONTROL_AE_PRECAPTURE_TRIGGER * @see ACAMERA_REQUEST_AVAILABLE_CAPABILITIES @@ -9986,6 +10460,167 @@ typedef enum acamera_metadata_enum_acamera_heic_available_heic_stream_configurat +// ACAMERA_AUTOMOTIVE_LOCATION +typedef enum acamera_metadata_enum_acamera_automotive_location { + /** + *

    The camera device exists inside of the vehicle cabin.

    + */ + ACAMERA_AUTOMOTIVE_LOCATION_INTERIOR = 0, + + /** + *

    The camera exists outside of the vehicle body frame but not exactly on one of the + * exterior locations this enum defines. The applications should determine the exact + * location from ACAMERA_LENS_POSE_TRANSLATION.

    + * + * @see ACAMERA_LENS_POSE_TRANSLATION + */ + ACAMERA_AUTOMOTIVE_LOCATION_EXTERIOR_OTHER = 1, + + /** + *

    The camera device exists outside of the vehicle body frame and on its front side.

    + */ + ACAMERA_AUTOMOTIVE_LOCATION_EXTERIOR_FRONT = 2, + + /** + *

    The camera device exists outside of the vehicle body frame and on its rear side.

    + */ + ACAMERA_AUTOMOTIVE_LOCATION_EXTERIOR_REAR = 3, + + /** + *

    The camera device exists outside and on left side of the vehicle body frame.

    + */ + ACAMERA_AUTOMOTIVE_LOCATION_EXTERIOR_LEFT = 4, + + /** + *

    The camera device exists outside and on right side of the vehicle body frame.

    + */ + ACAMERA_AUTOMOTIVE_LOCATION_EXTERIOR_RIGHT = 5, + + /** + *

    The camera device exists on an extra vehicle, such as the trailer, but not exactly + * on one of front, rear, left, or right side. Applications should determine the exact + * location from ACAMERA_LENS_POSE_TRANSLATION.

    + * + * @see ACAMERA_LENS_POSE_TRANSLATION + */ + ACAMERA_AUTOMOTIVE_LOCATION_EXTRA_OTHER = 6, + + /** + *

    The camera device exists outside of the extra vehicle's body frame and on its front + * side.

    + */ + ACAMERA_AUTOMOTIVE_LOCATION_EXTRA_FRONT = 7, + + /** + *

    The camera device exists outside of the extra vehicle's body frame and on its rear + * side.

    + */ + ACAMERA_AUTOMOTIVE_LOCATION_EXTRA_REAR = 8, + + /** + *

    The camera device exists outside and on left side of the extra vehicle body.

    + */ + ACAMERA_AUTOMOTIVE_LOCATION_EXTRA_LEFT = 9, + + /** + *

    The camera device exists outside and on right side of the extra vehicle body.

    + */ + ACAMERA_AUTOMOTIVE_LOCATION_EXTRA_RIGHT = 10, + +} acamera_metadata_enum_android_automotive_location_t; + + +// ACAMERA_AUTOMOTIVE_LENS_FACING +typedef enum acamera_metadata_enum_acamera_automotive_lens_facing { + /** + *

    The camera device faces the outside of the vehicle body frame but not exactly + * one of the exterior sides defined by this enum. Applications should determine + * the exact facing direction from ACAMERA_LENS_POSE_ROTATION and + * ACAMERA_LENS_POSE_TRANSLATION.

    + * + * @see ACAMERA_LENS_POSE_ROTATION + * @see ACAMERA_LENS_POSE_TRANSLATION + */ + ACAMERA_AUTOMOTIVE_LENS_FACING_EXTERIOR_OTHER = 0, + + /** + *

    The camera device faces the front of the vehicle body frame.

    + */ + ACAMERA_AUTOMOTIVE_LENS_FACING_EXTERIOR_FRONT = 1, + + /** + *

    The camera device faces the rear of the vehicle body frame.

    + */ + ACAMERA_AUTOMOTIVE_LENS_FACING_EXTERIOR_REAR = 2, + + /** + *

    The camera device faces the left side of the vehicle body frame.

    + */ + ACAMERA_AUTOMOTIVE_LENS_FACING_EXTERIOR_LEFT = 3, + + /** + *

    The camera device faces the right side of the vehicle body frame.

    + */ + ACAMERA_AUTOMOTIVE_LENS_FACING_EXTERIOR_RIGHT = 4, + + /** + *

    The camera device faces the inside of the vehicle body frame but not exactly + * one of seats described by this enum. Applications should determine the exact + * facing direction from ACAMERA_LENS_POSE_ROTATION and ACAMERA_LENS_POSE_TRANSLATION.

    + * + * @see ACAMERA_LENS_POSE_ROTATION + * @see ACAMERA_LENS_POSE_TRANSLATION + */ + ACAMERA_AUTOMOTIVE_LENS_FACING_INTERIOR_OTHER = 5, + + /** + *

    The camera device faces the left side seat of the first row.

    + */ + ACAMERA_AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_1_LEFT = 6, + + /** + *

    The camera device faces the center seat of the first row.

    + */ + ACAMERA_AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_1_CENTER = 7, + + /** + *

    The camera device faces the right seat of the first row.

    + */ + ACAMERA_AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_1_RIGHT = 8, + + /** + *

    The camera device faces the left side seat of the second row.

    + */ + ACAMERA_AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_2_LEFT = 9, + + /** + *

    The camera device faces the center seat of the second row.

    + */ + ACAMERA_AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_2_CENTER = 10, + + /** + *

    The camera device faces the right side seat of the second row.

    + */ + ACAMERA_AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_2_RIGHT = 11, + + /** + *

    The camera device faces the left side seat of the third row.

    + */ + ACAMERA_AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_3_LEFT = 12, + + /** + *

    The camera device faces the center seat of the third row.

    + */ + ACAMERA_AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_3_CENTER = 13, + + /** + *

    The camera device faces the right seat of the third row.

    + */ + ACAMERA_AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_3_RIGHT = 14, + +} acamera_metadata_enum_android_automotive_lens_facing_t; + + __END_DECLS diff --git a/camera/ndk/libcamera2ndk.map.txt b/camera/ndk/libcamera2ndk.map.txt index 2b630db3edfc782a09279d92d856215d8f4822e3..b3977ff4183a3682a35be5bbe029adcf586d67d0 100644 --- a/camera/ndk/libcamera2ndk.map.txt +++ b/camera/ndk/libcamera2ndk.map.txt @@ -2,11 +2,15 @@ LIBCAMERA2NDK { global: ACameraCaptureSession_abortCaptures; ACameraCaptureSession_capture; + ACameraCaptureSession_captureV2; # introduced=33 ACameraCaptureSession_logicalCamera_capture; # introduced=29 + ACameraCaptureSession_logicalCamera_captureV2; # introduced=33 ACameraCaptureSession_close; ACameraCaptureSession_getDevice; ACameraCaptureSession_setRepeatingRequest; + ACameraCaptureSession_setRepeatingRequestV2; # introduced=33 ACameraCaptureSession_logicalCamera_setRepeatingRequest; # introduced=29 + ACameraCaptureSession_logicalCamera_setRepeatingRequestV2; # introduced=33 ACameraCaptureSession_stopRepeating; ACameraCaptureSession_updateSharedOutput; # introduced=28 ACameraDevice_close; diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp index 9f63099152d97e3ab6028e1b816e80a478fb8280..0a57590f8d219330a55476df2cb77e35bb452032 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp @@ -29,8 +29,6 @@ #include "ACaptureRequest.h" #include "utils.h" -#include "ACameraCaptureSession.inc" - #define CHECK_TRANSACTION_AND_RET(remoteRet, status, callName) \ if (!remoteRet.isOk()) { \ ALOGE("%s: Transaction error during %s call %s", __FUNCTION__, callName, \ @@ -332,7 +330,8 @@ camera_status_t CameraDevice::updateOutputConfigurationLocked(ACaptureSessionOut return ACAMERA_ERROR_UNKNOWN; } - mConfiguredOutputs[streamId] = std::make_pair(output->mWindow, outConfigW); + mConfiguredOutputs[streamId] = + std::move(std::make_pair(std::move(output->mWindow), std::move(outConfigW))); return ACAMERA_OK; } @@ -492,6 +491,7 @@ CameraDevice::disconnectLocked(sp& session) { } if (mRemote != nullptr) { + ALOGD("%s: binder disconnect reached", __FUNCTION__); auto ret = mRemote->disconnect(); if (!ret.isOk()) { ALOGE("%s: Transaction error while disconnecting device %s", __FUNCTION__, @@ -625,7 +625,8 @@ CameraDevice::configureStreamsLocked(const ACaptureSessionOutputContainer* outpu outConfigInsert.windowHandles[0] = anw; outConfigInsert.physicalCameraId = outConfig.mPhysicalCameraId; native_handle_ptr_wrapper wrap(anw); - outputSet.insert(std::make_pair(anw, outConfigInsertW)); + + outputSet.emplace(std::make_pair(std::move(anw), std::move(outConfigInsertW))); } std::set> addSet = outputSet; std::vector deleteList; @@ -682,7 +683,7 @@ CameraDevice::configureStreamsLocked(const ACaptureSessionOutputContainer* outpu } // add new streams - for (auto outputPair : addSet) { + for (const auto &outputPair : addSet) { int streamId; Status status = Status::UNKNOWN_ERROR; auto ret = mRemote->createStream(outputPair.second, @@ -847,12 +848,32 @@ CameraDevice::onCaptureErrorLocked( return; } - const auto& windowHandles = outputPairIt->second.second.mOutputConfiguration.windowHandles; - for (const auto& outHandle : windowHandles) { - for (auto streamAndWindowId : request->mCaptureRequest.streamAndWindowIds) { - int32_t windowId = streamAndWindowId.windowId; - if (utils::isWindowNativeHandleEqual(windowHandles[windowId],outHandle)) { - const native_handle_t* anw = windowHandles[windowId].getNativeHandle(); + // Get the surfaces corresponding to the error stream id, go through + // them and try to match the surfaces in the corresponding + // CaptureRequest. + const auto& errorWindowHandles = + outputPairIt->second.second.mOutputConfiguration.windowHandles; + for (const auto& errorWindowHandle : errorWindowHandles) { + for (const auto &requestStreamAndWindowId : + request->mCaptureRequest.streamAndWindowIds) { + // Go through the surfaces in the capture request and see which + // ones match the surfaces in the error stream. + int32_t requestWindowId = requestStreamAndWindowId.windowId; + auto requestSurfacePairIt = + mConfiguredOutputs.find(requestStreamAndWindowId.streamId); + if (requestSurfacePairIt == mConfiguredOutputs.end()) { + ALOGE("%s: Error: request stream id %d does not exist", __FUNCTION__, + requestStreamAndWindowId.streamId); + setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE); + return; + } + + const auto &requestWindowHandles = + requestSurfacePairIt->second.second.mOutputConfiguration.windowHandles; + if (utils::isWindowNativeHandleEqual( + requestWindowHandles[requestWindowId], errorWindowHandle)) { + const native_handle_t* anw = + requestWindowHandles[requestWindowId].getNativeHandle(); ALOGV("Camera %s Lost output buffer for ANW %p frame %" PRId64, getId(), anw, frameNumber); @@ -910,6 +931,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( case kWhatOnError: case kWhatSessionStateCb: case kWhatCaptureStart: + case kWhatCaptureStart2: case kWhatCaptureResult: case kWhatLogicalCaptureResult: case kWhatCaptureFail: @@ -982,6 +1004,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( } case kWhatSessionStateCb: case kWhatCaptureStart: + case kWhatCaptureStart2: case kWhatCaptureResult: case kWhatLogicalCaptureResult: case kWhatCaptureFail: @@ -1002,6 +1025,7 @@ void CameraDevice::CallbackHandler::onMessageReceived( const char *id_cstr = mId.c_str(); switch (msg->what()) { case kWhatCaptureStart: + case kWhatCaptureStart2: case kWhatCaptureResult: case kWhatLogicalCaptureResult: case kWhatCaptureFail: @@ -1053,6 +1077,35 @@ void CameraDevice::CallbackHandler::onMessageReceived( freeACaptureRequest(request); break; } + case kWhatCaptureStart2: + { + ACameraCaptureSession_captureCallback_startV2 onStart2; + found = msg->findPointer(kCallbackFpKey, (void**) &onStart2); + if (!found) { + ALOGE("%s: Cannot find capture startV2 callback!", __FUNCTION__); + return; + } + if (onStart2 == nullptr) { + return; + } + int64_t timestamp; + found = msg->findInt64(kTimeStampKey, ×tamp); + if (!found) { + ALOGE("%s: Cannot find timestamp!", __FUNCTION__); + return; + } + int64_t frameNumber; + found = msg->findInt64(kFrameNumberKey, &frameNumber); + if (!found) { + ALOGE("%s: Cannot find frame number!", __FUNCTION__); + return; + } + + ACaptureRequest* request = allocateACaptureRequest(requestSp, id_cstr); + (*onStart2)(context, session.get(), request, timestamp, frameNumber); + freeACaptureRequest(request); + break; + } case kWhatCaptureResult: { ACameraCaptureSession_captureCallback_result onResult; @@ -1281,6 +1334,7 @@ CameraDevice::CallbackHolder::CallbackHolder( ACameraCaptureSession_captureCallbacks* cbs) : mSession(session), mRequests(requests), mIsRepeating(isRepeating), + mIs2Callback(false), mIsLogicalCameraCallback(false) { initCaptureCallbacks(cbs); @@ -1297,6 +1351,7 @@ CameraDevice::CallbackHolder::CallbackHolder( ACameraCaptureSession_logicalCamera_captureCallbacks* lcbs) : mSession(session), mRequests(requests), mIsRepeating(isRepeating), + mIs2Callback(false), mIsLogicalCameraCallback(true) { initCaptureCallbacks(lcbs); @@ -1306,6 +1361,40 @@ CameraDevice::CallbackHolder::CallbackHolder( } } +CameraDevice::CallbackHolder::CallbackHolder( + sp session, + const Vector >& requests, + bool isRepeating, + ACameraCaptureSession_captureCallbacksV2* cbs) : + mSession(session), mRequests(requests), + mIsRepeating(isRepeating), + mIs2Callback(true), + mIsLogicalCameraCallback(false) { + initCaptureCallbacksV2(cbs); + + if (cbs != nullptr) { + mOnCaptureCompleted = cbs->onCaptureCompleted; + mOnCaptureFailed = cbs->onCaptureFailed; + } +} + +CameraDevice::CallbackHolder::CallbackHolder( + sp session, + const Vector >& requests, + bool isRepeating, + ACameraCaptureSession_logicalCamera_captureCallbacksV2* lcbs) : + mSession(session), mRequests(requests), + mIsRepeating(isRepeating), + mIs2Callback(true), + mIsLogicalCameraCallback(true) { + initCaptureCallbacksV2(lcbs); + + if (lcbs != nullptr) { + mOnLogicalCameraCaptureCompleted = lcbs->onLogicalCameraCaptureCompleted; + mOnLogicalCameraCaptureFailed = lcbs->onLogicalCameraCaptureFailed; + } +} + void CameraDevice::checkRepeatingSequenceCompleteLocked( const int sequenceId, const int64_t lastFrameNumber) { @@ -1542,11 +1631,14 @@ CameraDevice::ServiceCallback::onCaptureStarted( int32_t sequenceId = resultExtras.requestId; int32_t burstId = resultExtras.burstId; + int64_t frameNumber = resultExtras.frameNumber; auto it = dev->mSequenceCallbackMap.find(sequenceId); if (it != dev->mSequenceCallbackMap.end()) { CallbackHolder cbh = (*it).second; ACameraCaptureSession_captureCallback_start onStart = cbh.mOnCaptureStarted; + ACameraCaptureSession_captureCallback_startV2 onStart2 = cbh.mOnCaptureStarted2; + bool v2Callback = cbh.mIs2Callback; sp session = cbh.mSession; if ((size_t) burstId >= cbh.mRequests.size()) { ALOGE("%s: Error: request index %d out of bound (size %zu)", @@ -1554,12 +1646,19 @@ CameraDevice::ServiceCallback::onCaptureStarted( dev->setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE); } sp request = cbh.mRequests[burstId]; - sp msg = new AMessage(kWhatCaptureStart, dev->mHandler); + sp msg = nullptr; + if (v2Callback) { + msg = new AMessage(kWhatCaptureStart2, dev->mHandler); + msg->setPointer(kCallbackFpKey, (void*) onStart2); + } else { + msg = new AMessage(kWhatCaptureStart, dev->mHandler); + msg->setPointer(kCallbackFpKey, (void*) onStart); + } msg->setPointer(kContextKey, cbh.mContext); msg->setObject(kSessionSpKey, session); - msg->setPointer(kCallbackFpKey, (void*) onStart); msg->setObject(kCaptureRequestKey, request); msg->setInt64(kTimeStampKey, timestamp); + msg->setInt64(kFrameNumberKey, frameNumber); dev->postSessionMsgAndCleanup(msg); } return ret; diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.h b/camera/ndk/ndk_vendor/impl/ACameraDevice.h index 0b6c7c83af00fa7d000a2059d2fffa032d4d5a6b..c306206bc3685f03f01309cc45d16990c9038b0f 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraDevice.h +++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.h @@ -245,6 +245,7 @@ class CameraDevice final : public RefBase { kWhatSessionStateCb, // onReady, onActive // Capture callbacks kWhatCaptureStart, // onCaptureStarted + kWhatCaptureStart2, // onCaptureStarted2 kWhatCaptureResult, // onCaptureProgressed, onCaptureCompleted kWhatLogicalCaptureResult, // onLogicalCameraCaptureCompleted kWhatCaptureFail, // onCaptureFailed @@ -309,11 +310,18 @@ class CameraDevice final : public RefBase { const Vector>& requests, bool isRepeating, ACameraCaptureSession_logicalCamera_captureCallbacks* lcbs); - - template - void initCaptureCallbacks(T* cbs) { + CallbackHolder(sp session, + const Vector >& requests, + bool isRepeating, + ACameraCaptureSession_captureCallbacksV2* cbs); + CallbackHolder(sp session, + const Vector >& requests, + bool isRepeating, + ACameraCaptureSession_logicalCamera_captureCallbacksV2* lcbs); + void clearCallbacks() { mContext = nullptr; mOnCaptureStarted = nullptr; + mOnCaptureStarted2 = nullptr; mOnCaptureProgressed = nullptr; mOnCaptureCompleted = nullptr; mOnLogicalCameraCaptureCompleted = nullptr; @@ -322,6 +330,24 @@ class CameraDevice final : public RefBase { mOnCaptureSequenceCompleted = nullptr; mOnCaptureSequenceAborted = nullptr; mOnCaptureBufferLost = nullptr; + } + + template + void initCaptureCallbacksV2(T* cbs) { + clearCallbacks(); + if (cbs != nullptr) { + mContext = cbs->context; + mOnCaptureStarted2 = cbs->onCaptureStarted; + mOnCaptureProgressed = cbs->onCaptureProgressed; + mOnCaptureSequenceCompleted = cbs->onCaptureSequenceCompleted; + mOnCaptureSequenceAborted = cbs->onCaptureSequenceAborted; + mOnCaptureBufferLost = cbs->onCaptureBufferLost; + } + } + + template + void initCaptureCallbacks(T* cbs) { + clearCallbacks(); if (cbs != nullptr) { mContext = cbs->context; mOnCaptureStarted = cbs->onCaptureStarted; @@ -335,10 +361,12 @@ class CameraDevice final : public RefBase { sp mSession; Vector> mRequests; const bool mIsRepeating; + const bool mIs2Callback; const bool mIsLogicalCameraCallback; void* mContext; ACameraCaptureSession_captureCallback_start mOnCaptureStarted; + ACameraCaptureSession_captureCallback_startV2 mOnCaptureStarted2; ACameraCaptureSession_captureCallback_result mOnCaptureProgressed; ACameraCaptureSession_captureCallback_result mOnCaptureCompleted; ACameraCaptureSession_logicalCamera_captureCallback_result mOnLogicalCameraCaptureCompleted; diff --git a/camera/ndk/ndk_vendor/impl/utils.h b/camera/ndk/ndk_vendor/impl/utils.h index 6f5820ecee9cc67a8c364862adadf02db04f079c..62779a4b02992a067f1a92cb2061076dfac36579 100644 --- a/camera/ndk/ndk_vendor/impl/utils.h +++ b/camera/ndk/ndk_vendor/impl/utils.h @@ -109,8 +109,30 @@ struct OutputConfigurationWrapper { mOutputConfiguration.windowGroupId = -1; }; - OutputConfigurationWrapper(OutputConfiguration &outputConfiguration) - : mOutputConfiguration((outputConfiguration)) { } + OutputConfigurationWrapper(const OutputConfigurationWrapper &other) { + *this = other; + } + + // Needed to make sure that OutputConfiguration in + // OutputConfigurationWrapper, when copied doesn't call hidl_handle's + // assignment operator / copy constructor, which will lead to native handle + // cloning, which is not what we want for app callbacks which have the native + // handle as parameter. + OutputConfigurationWrapper &operator=(const OutputConfigurationWrapper &other) { + const OutputConfiguration &outputConfiguration = other.mOutputConfiguration; + mOutputConfiguration.rotation = outputConfiguration.rotation; + mOutputConfiguration.isDeferred = outputConfiguration.isDeferred; + mOutputConfiguration.width = outputConfiguration.width; + mOutputConfiguration.height = outputConfiguration.height; + mOutputConfiguration.windowGroupId = outputConfiguration.windowGroupId; + mOutputConfiguration.windowHandles.resize(outputConfiguration.windowHandles.size()); + mOutputConfiguration.physicalCameraId = outputConfiguration.physicalCameraId; + size_t i = 0; + for (const auto &handle : outputConfiguration.windowHandles) { + mOutputConfiguration.windowHandles[i++] = handle.getNativeHandle(); + } + return *this; + } bool operator ==(const OutputConfiguration &other) const { const OutputConfiguration &self = mOutputConfiguration; diff --git a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp index ba14c5c4fd69fd029016e6870b7d55a1d35022e2..63cdb76ae5b0de4204d3c555d69e3e2159e7ff9d 100644 --- a/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp +++ b/camera/ndk/ndk_vendor/tests/AImageReaderVendorTest.cpp @@ -236,6 +236,11 @@ class CameraHelper { return ACameraCaptureSession_capture(mSession, &mCaptureCallbacks, 1, &mStillRequest, &seqId); } + int takePicture2() { + int seqId; + return ACameraCaptureSession_captureV2(mSession, &mCaptureCallbacksV2, 1, + &mStillRequest, &seqId); + } int takeLogicalCameraPicture() { int seqId; @@ -243,15 +248,31 @@ class CameraHelper { 1, &mStillRequest, &seqId); } + int takeLogicalCameraPicture2() { + int seqId; + return ACameraCaptureSession_logicalCamera_captureV2(mSession, + &mLogicalCaptureCallbacksV2, 1, &mStillRequest, &seqId); + } + bool checkCallbacks(int pictureCount) { std::lock_guard lock(mMutex); if (mCompletedCaptureCallbackCount != pictureCount) { - ALOGE("Completed capture callaback count not as expected. expected %d actual %d", + ALOGE("Completed capture callback count not as expected. expected %d actual %d", pictureCount, mCompletedCaptureCallbackCount); return false; } return true; } + bool checkCallbacksV2(int pictureCount) { + std::lock_guard lock(mMutex); + if (mCaptureStartedCallbackCount != pictureCount) { + ALOGE("Capture started callback count not as expected. expected %d actual %d", + pictureCount, mCaptureStartedCallbackCount); + return false; + } + return true; + } + private: ACameraDevice_StateCallbacks mDeviceCb{this, nullptr, nullptr}; @@ -276,6 +297,7 @@ class CameraHelper { const char* mCameraId; ACameraManager* mCameraManager; int mCompletedCaptureCallbackCount = 0; + int mCaptureStartedCallbackCount = 0; std::mutex mMutex; ACameraCaptureSession_captureCallbacks mCaptureCallbacks = { // TODO: Add tests for other callbacks @@ -293,8 +315,25 @@ class CameraHelper { nullptr, // onCaptureSequenceAborted nullptr, // onCaptureBufferLost }; + ACameraCaptureSession_captureCallbacksV2 mCaptureCallbacksV2 = { + this, // context + [](void* ctx , ACameraCaptureSession *,const ACaptureRequest *, int64_t, + int64_t frameNumber ) { + CameraHelper *ch = static_cast(ctx); + ASSERT_TRUE(frameNumber >= 0); + std::lock_guard lock(ch->mMutex); + ch->mCaptureStartedCallbackCount++; + }, + nullptr, // onCaptureProgressed + nullptr, // onCaptureCompleted + nullptr, // onCaptureFailed + nullptr, // onCaptureSequenceCompleted + nullptr, // onCaptureSequenceAborted + nullptr, // onCaptureBufferLost + }; std::vector mPhysicalCameraIds; + ACameraCaptureSession_logicalCamera_captureCallbacks mLogicalCaptureCallbacks = { // TODO: Add tests for other callbacks this, // context @@ -336,6 +375,23 @@ class CameraHelper { nullptr, // onCaptureSequenceAborted nullptr, // onCaptureBufferLost }; + ACameraCaptureSession_logicalCamera_captureCallbacksV2 mLogicalCaptureCallbacksV2 = { + this, // context + [](void* ctx , ACameraCaptureSession *,const ACaptureRequest *, int64_t, + int64_t frameNumber) { + CameraHelper *ch = static_cast(ctx); + ASSERT_TRUE(frameNumber >= 0); + std::lock_guard lock(ch->mMutex); + ch->mCaptureStartedCallbackCount++; + }, + nullptr, // onCaptureProgressed + nullptr, //onLogicalCaptureCompleted + nullptr, //onLogicalCpatureFailed + nullptr, // onCaptureSequenceCompleted + nullptr, // onCaptureSequenceAborted + nullptr, // onCaptureBufferLost + }; + }; class ImageReaderTestCase { @@ -570,7 +626,7 @@ class AImageReaderVendorTest : public ::testing::Test { } bool takePictures(const char* id, uint64_t readerUsage, int readerMaxImages, - bool readerAsync, int pictureCount) { + bool readerAsync, int pictureCount, bool v2 = false) { int ret = 0; ImageReaderTestCase testCase( @@ -600,7 +656,11 @@ class AImageReaderVendorTest : public ::testing::Test { } for (int i = 0; i < pictureCount; i++) { - ret = cameraHelper.takePicture(); + if (v2) { + ret = cameraHelper.takePicture2(); + } else { + ret = cameraHelper.takePicture(); + } if (ret < 0) { ALOGE("Unable to take picture"); return false; @@ -617,7 +677,8 @@ class AImageReaderVendorTest : public ::testing::Test { } } return testCase.getAcquiredImageCount() == pictureCount && - cameraHelper.checkCallbacks(pictureCount); + v2 ? cameraHelper.checkCallbacksV2(pictureCount) : + cameraHelper.checkCallbacks(pictureCount); } bool testTakePicturesNative(const char* id) { @@ -626,12 +687,14 @@ class AImageReaderVendorTest : public ::testing::Test { for (auto& readerMaxImages : {1, 4, 8}) { for (auto& readerAsync : {true, false}) { for (auto& pictureCount : {1, 4, 8}) { - if (!takePictures(id, readerUsage, readerMaxImages, - readerAsync, pictureCount)) { - ALOGE("Test takePictures failed for test case usage=%" PRIu64 - ", maxImages=%d, async=%d, pictureCount=%d", - readerUsage, readerMaxImages, readerAsync, pictureCount); - return false; + for ( auto & v2 : {true, false}) { + if (!takePictures(id, readerUsage, readerMaxImages, + readerAsync, pictureCount, v2)) { + ALOGE("Test takePictures failed for test case usage=%" PRIu64 + ", maxImages=%d, async=%d, pictureCount=%d", + readerUsage, readerMaxImages, readerAsync, pictureCount); + return false; + } } } } @@ -725,7 +788,7 @@ class AImageReaderVendorTest : public ::testing::Test { return; } - void testLogicalCameraPhysicalStream(bool usePhysicalSettings) { + void testLogicalCameraPhysicalStream(bool usePhysicalSettings, bool v2) { const char* cameraId = nullptr; ACameraMetadata* staticMetadata = nullptr; std::vector physicalCameraIds; @@ -772,7 +835,12 @@ class AImageReaderVendorTest : public ::testing::Test { } for (int i = 0; i < pictureCount; i++) { - ret = cameraHelper.takeLogicalCameraPicture(); + if (v2) { + ret = cameraHelper.takeLogicalCameraPicture2(); + } + else { + ret = cameraHelper.takeLogicalCameraPicture(); + } ASSERT_EQ(ret, 0); } @@ -793,8 +861,11 @@ class AImageReaderVendorTest : public ::testing::Test { ALOGI("Testing window %p", testCase->getNativeWindow()); ASSERT_EQ(testCase->getAcquiredImageCount(), pictureCount); } - - ASSERT_TRUE(cameraHelper.checkCallbacks(pictureCount)); + if (v2) { + ASSERT_TRUE(cameraHelper.checkCallbacksV2(pictureCount)); + } else { + ASSERT_TRUE(cameraHelper.checkCallbacks(pictureCount)); + } ACameraMetadata_free(staticMetadata); } @@ -834,8 +905,10 @@ TEST_F(AImageReaderVendorTest, CreateWindowNativeHandle) { } TEST_F(AImageReaderVendorTest, LogicalCameraPhysicalStream) { - testLogicalCameraPhysicalStream(false/*usePhysicalSettings*/); - testLogicalCameraPhysicalStream(true/*usePhysicalSettings*/); + for (auto & v2 : {true, false}) { + testLogicalCameraPhysicalStream(false/*usePhysicalSettings*/, v2); + testLogicalCameraPhysicalStream(true/*usePhysicalSettings*/, v2); + } } } // namespace diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp index 9f2f430f6246c3a3f8390117e4e4017796b6effd..17ea51270c590e2859fc92b8adc296fd17148060 100644 --- a/camera/tests/CameraBinderTests.cpp +++ b/camera/tests/CameraBinderTests.cpp @@ -96,6 +96,12 @@ public: return binder::Status::ok(); }; + virtual binder::Status onTorchStrengthLevelChanged(const String16& /*cameraId*/, + int32_t /*torchStrength*/) { + // No op + return binder::Status::ok(); + } + virtual binder::Status onCameraAccessPrioritiesChanged() { // No op return binder::Status::ok(); diff --git a/cmds/screenrecord/Android.bp b/cmds/screenrecord/Android.bp index 359a8350da1dc673f90d3f2c24de0b68882c5e32..d0b3ce074f08df4a6d52be10602f9876f90b0f69 100644 --- a/cmds/screenrecord/Android.bp +++ b/cmds/screenrecord/Android.bp @@ -55,12 +55,6 @@ cc_binary { "libGLESv2", ], - include_dirs: [ - "frameworks/av/media/libstagefright", - "frameworks/av/media/libstagefright/include", - "frameworks/native/include/media/openmax", - ], - cflags: [ "-Werror", "-Wall", diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp index e6e347321d6769b70b9f0c577ffd8862f41b6e74..2e0b678e7cdd15310c74a641cbe06a6af3fcb854 100644 --- a/cmds/screenrecord/screenrecord.cpp +++ b/cmds/screenrecord/screenrecord.cpp @@ -701,7 +701,7 @@ static status_t recordScreen(const char* fileName) { printf("Display is %dx%d @%.2ffps (orientation=%s), layerStack=%u\n", layerStackSpaceRect.getWidth(), layerStackSpaceRect.getHeight(), displayMode.refreshRate, toCString(displayState.orientation), - displayState.layerStack); + displayState.layerStack.id); fflush(stdout); } @@ -1067,7 +1067,7 @@ int main(int argc, char* const argv[]) { std::optional displayId = SurfaceComposerClient::getInternalDisplayId(); if (!displayId) { - fprintf(stderr, "Failed to get token for internal display\n"); + fprintf(stderr, "Failed to get ID for internal display\n"); return 1; } @@ -1168,17 +1168,14 @@ int main(int argc, char* const argv[]) { } break; case 'd': - gPhysicalDisplayId = PhysicalDisplayId(atoll(optarg)); - if (gPhysicalDisplayId.value == 0) { - fprintf(stderr, "Please specify a valid physical display id\n"); - return 2; - } else if (SurfaceComposerClient:: - getPhysicalDisplayToken(gPhysicalDisplayId) == nullptr) { - fprintf(stderr, "Invalid physical display id: %s\n", - to_string(gPhysicalDisplayId).c_str()); - return 2; + if (const auto id = android::DisplayId::fromValue(atoll(optarg)); + id && SurfaceComposerClient::getPhysicalDisplayToken(*id)) { + gPhysicalDisplayId = *id; + break; } - break; + + fprintf(stderr, "Invalid physical display ID\n"); + return 2; default: if (ic != '?') { fprintf(stderr, "getopt_long returned unexpected value 0x%x\n", ic); diff --git a/cmds/stagefright/Android.bp b/cmds/stagefright/Android.bp index c4783d36dd452dca80b3e4f0dd961c203f50c645..e1fe07edcbe88624277d2caec8ce830ca2e1ac96 100644 --- a/cmds/stagefright/Android.bp +++ b/cmds/stagefright/Android.bp @@ -227,8 +227,6 @@ cc_binary { "rs-headers", ], - include_dirs: ["frameworks/av/media/libstagefright"], - shared_libs: [ "libstagefright", "liblog", diff --git a/cmds/stagefright/AudioPlayer.cpp b/cmds/stagefright/AudioPlayer.cpp index 55427cafac9ce59169b5bba133c59fe34182bc2c..6cddf475040471b7647fd4f1043e77b1f996f875 100644 --- a/cmds/stagefright/AudioPlayer.cpp +++ b/cmds/stagefright/AudioPlayer.cpp @@ -249,7 +249,8 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) { mAudioTrack = new AudioTrack( AUDIO_STREAM_MUSIC, mSampleRate, AUDIO_FORMAT_PCM_16_BIT, audioMask, - 0 /*frameCount*/, AUDIO_OUTPUT_FLAG_NONE, &AudioCallback, this, + 0 /*frameCount*/, AUDIO_OUTPUT_FLAG_NONE, + wp::fromExisting(this), 0 /*notificationFrames*/); if ((err = mAudioTrack->initCheck()) != OK) { @@ -397,10 +398,6 @@ void AudioPlayer::reset() { mStartPosUs = 0; } -// static -void AudioPlayer::AudioCallback(int event, void *user, void *info) { - static_cast(user)->AudioCallback(event, info); -} bool AudioPlayer::reachedEOS(status_t *finalStatus) { *finalStatus = OK; @@ -455,20 +452,12 @@ size_t AudioPlayer::AudioSinkCallback( return 0; } -void AudioPlayer::AudioCallback(int event, void *info) { - switch (event) { - case AudioTrack::EVENT_MORE_DATA: - { - AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info; - size_t numBytesWritten = fillBuffer(buffer->raw, buffer->size); - buffer->size = numBytesWritten; - } - break; +size_t AudioPlayer::onMoreData(const AudioTrack::Buffer& buffer) { + return fillBuffer(buffer.data(), buffer.size()); +} - case AudioTrack::EVENT_STREAM_END: - mReachedEOS = true; - break; - } +void AudioPlayer::onStreamEnd() { + mReachedEOS = true; } size_t AudioPlayer::fillBuffer(void *data, size_t size) { diff --git a/cmds/stagefright/AudioPlayer.h b/cmds/stagefright/AudioPlayer.h index 43550ea23dafe3bfc8324a247108107c4d25b2cf..608f54bb36dc3fbd2dce058171095b5e0d5fb733 100644 --- a/cmds/stagefright/AudioPlayer.h +++ b/cmds/stagefright/AudioPlayer.h @@ -19,6 +19,7 @@ #define AUDIO_PLAYER_H_ #include +#include #include #include #include @@ -26,10 +27,9 @@ namespace android { -class AudioTrack; struct AwesomePlayer; -class AudioPlayer { +class AudioPlayer : AudioTrack::IAudioTrackCallback { public: enum { REACHED_EOS, @@ -66,6 +66,9 @@ public: status_t getPlaybackRate(AudioPlaybackRate *rate /* nonnull */); private: + friend sp; + size_t onMoreData(const AudioTrack::Buffer& buffer) override; + void onStreamEnd() override; sp mSource; sp mAudioTrack; @@ -99,9 +102,6 @@ private: int64_t mStartPosUs; const uint32_t mCreateFlags; - static void AudioCallback(int event, void *user, void *info); - void AudioCallback(int event, void *info); - static size_t AudioSinkCallback( MediaPlayerBase::AudioSink *audioSink, void *data, size_t size, void *me, diff --git a/cmds/stagefright/audioloop.cpp b/cmds/stagefright/audioloop.cpp index 4b41ff8095574c06e99340b22e4c9e0c4297015e..83f8fe95cbd7d169e6be90931ae8e4f0f383db00 100644 --- a/cmds/stagefright/audioloop.cpp +++ b/cmds/stagefright/audioloop.cpp @@ -166,9 +166,9 @@ int main(int argc, char* argv[]) sp decoder = SimpleDecodingSource::Create(encoder); if (playToSpeaker) { - AudioPlayer player(NULL); - player.setSource(decoder); - player.start(); + sp player = sp::make(nullptr); + player->setSource(decoder); + player->start(); sleep(duration); ALOGI("Line: %d", __LINE__); diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp index 098c27865a930c4ac02a6ec04d01ecb3327fbaf6..5743ad6fed5c3bd9c0e997ec677b83af83d0c6d0 100644 --- a/cmds/stagefright/record.cpp +++ b/cmds/stagefright/record.cpp @@ -32,7 +32,6 @@ #include #include -#include "AudioPlayer.h" using namespace android; @@ -274,17 +273,6 @@ int main(int /* argc */, char ** /* argv */) { const int32_t kNumChannels = 2; sp audioSource = new SineSource(kSampleRate, kNumChannels); -#if 0 - sp audioSink; - AudioPlayer *player = new AudioPlayer(audioSink); - player->setSource(audioSource); - player->start(); - - sleep(10); - - player->stop(); -#endif - sp encMeta = new AMessage; encMeta->setString("mime", 0 ? MEDIA_MIMETYPE_AUDIO_AMR_WB : MEDIA_MIMETYPE_AUDIO_AAC); diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index 6d1263efbb983eb4733fe8ef1772dd523a453db0..9783855b2a7e0b2ca92235b24b87e4cfad8b15df 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -224,7 +224,7 @@ static void playSource(sp &source) { } if (gPlaybackAudio) { - AudioPlayer *player = new AudioPlayer(NULL); + sp player = sp::make(nullptr); player->setSource(rawSource); rawSource.clear(); @@ -239,9 +239,6 @@ static void playSource(sp &source) { fprintf(stderr, "unable to start playback err=%d (0x%08x)\n", err, err); } - delete player; - player = NULL; - return; } else if (gReproduceBug >= 3 && gReproduceBug <= 5) { int64_t durationUs; diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp index 74e3223bce017ec1a2a64270a58c7523057a4970..f7989bd3661460c5b203e90dc07e493df860338e 100644 --- a/drm/drmserver/DrmManager.cpp +++ b/drm/drmserver/DrmManager.cpp @@ -208,7 +208,11 @@ void DrmManager::removeUniqueId(int uniqueId) { } status_t DrmManager::loadPlugIns() { +#if __LP64__ + String8 pluginDirPath("/system/lib64/drm"); +#else String8 pluginDirPath("/system/lib/drm"); +#endif loadPlugIns(pluginDirPath); return DRM_NO_ERROR; } diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp index 71df58cae8c8b8220eda58dc70db52f70e781bb6..408d21664ad1fee8941ae9d7cc770cad987180d2 100644 --- a/drm/libmediadrm/Android.bp +++ b/drm/libmediadrm/Android.bp @@ -28,8 +28,13 @@ cc_library { "DrmSessionManager.cpp", "SharedLibrary.cpp", "DrmHal.cpp", + "DrmHalHidl.cpp", + "DrmHalAidl.cpp", "CryptoHal.cpp", + "CryptoHalHidl.cpp", + "CryptoHalAidl.cpp", "DrmUtils.cpp", + "DrmHalListener.cpp", ], local_include_dirs: [ @@ -63,10 +68,12 @@ cc_library { "android.hardware.drm@1.4", "libhidlallocatorutils", "libhidlbase", + "android.hardware.drm-V1-ndk", ], static_libs: [ "resourcemanager_aidl_interface-ndk", + "libaidlcommonsupport", ], export_shared_lib_headers: [ @@ -162,10 +169,6 @@ cc_library_shared { "DrmMetricsConsumer.cpp", ], - include_dirs: [ - "frameworks/av/media/libmedia/include" - ], - shared_libs: [ "android.hardware.drm@1.0", "android.hardware.drm@1.1", @@ -181,5 +184,6 @@ cc_library_shared { header_libs: [ "libmediametrics_headers", "libstagefright_foundation_headers", + "libmedia_headers", ], } diff --git a/drm/libmediadrm/CryptoHal.cpp b/drm/libmediadrm/CryptoHal.cpp index e0db1c4378cf84831d56684cab5077103b80d47e..f95d5272ae9a5816cfd4c3f2f88329605e39194d 100644 --- a/drm/libmediadrm/CryptoHal.cpp +++ b/drm/libmediadrm/CryptoHal.cpp @@ -16,389 +16,100 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "CryptoHal" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include #include -using drm::V1_0::BufferType; -using drm::V1_0::DestinationBuffer; -using drm::V1_0::ICryptoFactory; -using drm::V1_0::ICryptoPlugin; -using drm::V1_0::Mode; -using drm::V1_0::Pattern; -using drm::V1_0::SharedBuffer; -using drm::V1_0::Status; -using drm::V1_0::SubSample; - -using ::android::DrmUtils::toStatusT; -using ::android::hardware::hidl_array; -using ::android::hardware::hidl_handle; -using ::android::hardware::hidl_memory; -using ::android::hardware::hidl_string; -using ::android::hardware::hidl_vec; -using ::android::hardware::HidlMemory; -using ::android::hardware::Return; -using ::android::hardware::Void; -using ::android::sp; - -typedef drm::V1_2::Status Status_V1_2; - namespace android { -static hidl_vec toHidlVec(const Vector &vector) { - hidl_vec vec; - vec.setToExternal(const_cast(vector.array()), vector.size()); - return vec; -} - -static hidl_vec toHidlVec(const void *ptr, size_t size) { - hidl_vec vec; - vec.resize(size); - memcpy(vec.data(), ptr, size); - return vec; -} - -static hidl_array toHidlArray16(const uint8_t *ptr) { - if (!ptr) { - return hidl_array(); - } - return hidl_array(ptr); -} - - -static String8 toString8(hidl_string hString) { - return String8(hString.c_str()); -} - - -CryptoHal::CryptoHal() - : mFactories(makeCryptoFactories()), - mInitCheck((mFactories.size() == 0) ? ERROR_UNSUPPORTED : NO_INIT), - mHeapSeqNum(0) { -} - -CryptoHal::~CryptoHal() { -} - -Vector> CryptoHal::makeCryptoFactories() { - Vector> factories; - - auto manager = hardware::defaultServiceManager1_2(); - if (manager != NULL) { - manager->listManifestByInterface(drm::V1_0::ICryptoFactory::descriptor, - [&factories](const hidl_vec ®istered) { - for (const auto &instance : registered) { - auto factory = drm::V1_0::ICryptoFactory::getService(instance); - if (factory != NULL) { - ALOGD("found drm@1.0 ICryptoFactory %s", instance.c_str()); - factories.push_back(factory); - } - } - } - ); - manager->listManifestByInterface(drm::V1_1::ICryptoFactory::descriptor, - [&factories](const hidl_vec ®istered) { - for (const auto &instance : registered) { - auto factory = drm::V1_1::ICryptoFactory::getService(instance); - if (factory != NULL) { - ALOGD("found drm@1.1 ICryptoFactory %s", instance.c_str()); - factories.push_back(factory); - } - } - } - ); - } - - if (factories.size() == 0) { - // must be in passthrough mode, load the default passthrough service - auto passthrough = ICryptoFactory::getService(); - if (passthrough != NULL) { - ALOGI("makeCryptoFactories: using default passthrough crypto instance"); - factories.push_back(passthrough); - } else { - ALOGE("Failed to find any crypto factories"); - } - } - return factories; -} - -sp CryptoHal::makeCryptoPlugin(const sp& factory, - const uint8_t uuid[16], const void *initData, size_t initDataSize) { - - sp plugin; - Return hResult = factory->createPlugin(toHidlArray16(uuid), - toHidlVec(initData, initDataSize), - [&](Status status, const sp& hPlugin) { - if (status != Status::OK) { - ALOGE("Failed to make crypto plugin"); - return; - } - plugin = hPlugin; - } - ); - if (!hResult.isOk()) { - mInitCheck = DEAD_OBJECT; - } - return plugin; +CryptoHal::CryptoHal() { + mCryptoHalAidl = sp::make(); + mCryptoHalHidl = sp::make(); } +CryptoHal::~CryptoHal() {} status_t CryptoHal::initCheck() const { - return mInitCheck; + if (mCryptoHalAidl->initCheck() == OK || mCryptoHalHidl->initCheck() == OK) return OK; + if (mCryptoHalAidl->initCheck() == NO_INIT || mCryptoHalHidl->initCheck() == NO_INIT) + return NO_INIT; + return mCryptoHalHidl->initCheck(); } - bool CryptoHal::isCryptoSchemeSupported(const uint8_t uuid[16]) { - Mutex::Autolock autoLock(mLock); - - for (size_t i = 0; i < mFactories.size(); i++) { - if (mFactories[i]->isCryptoSchemeSupported(uuid)) { - return true; - } - } - return false; + return mCryptoHalAidl->isCryptoSchemeSupported(uuid) || + mCryptoHalHidl->isCryptoSchemeSupported(uuid); } -status_t CryptoHal::createPlugin(const uint8_t uuid[16], const void *data, - size_t size) { - Mutex::Autolock autoLock(mLock); - - for (size_t i = 0; i < mFactories.size(); i++) { - if (mFactories[i]->isCryptoSchemeSupported(uuid)) { - mPlugin = makeCryptoPlugin(mFactories[i], uuid, data, size); - if (mPlugin != NULL) { - mPluginV1_2 = drm::V1_2::ICryptoPlugin::castFrom(mPlugin); - } - } - } - - if (mInitCheck == NO_INIT) { - mInitCheck = mPlugin == NULL ? ERROR_UNSUPPORTED : OK; - } - - return mInitCheck; +status_t CryptoHal::createPlugin(const uint8_t uuid[16], const void* data, size_t size) { + if (mCryptoHalAidl->createPlugin(uuid, data, size) != OK) + return mCryptoHalHidl->createPlugin(uuid, data, size); + return OK; } status_t CryptoHal::destroyPlugin() { - Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return mInitCheck; - } - - mPlugin.clear(); - mPluginV1_2.clear(); - return OK; + // This requires plugin to be created. + if (mCryptoHalAidl->initCheck() == OK) return mCryptoHalAidl->destroyPlugin(); + return mCryptoHalHidl->destroyPlugin(); } -bool CryptoHal::requiresSecureDecoderComponent(const char *mime) const { - Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return false; - } - - Return hResult = mPlugin->requiresSecureDecoderComponent(hidl_string(mime)); - if (!hResult.isOk()) { - return false; - } - return hResult; +bool CryptoHal::requiresSecureDecoderComponent(const char* mime) const { + // This requires plugin to be created. + if (mCryptoHalAidl->initCheck() == OK) + return mCryptoHalAidl->requiresSecureDecoderComponent(mime); + return mCryptoHalHidl->requiresSecureDecoderComponent(mime); } - -/** - * If the heap base isn't set, get the heap base from the HidlMemory - * and send it to the HAL so it can map a remote heap of the same - * size. Once the heap base is established, shared memory buffers - * are sent by providing an offset into the heap and a buffer size. - */ -int32_t CryptoHal::setHeapBase(const sp& heap) { - if (heap == NULL || mHeapSeqNum < 0) { - ALOGE("setHeapBase(): heap %p mHeapSeqNum %d", heap.get(), mHeapSeqNum); - return -1; +void CryptoHal::notifyResolution(uint32_t width, uint32_t height) { + // This requires plugin to be created. + if (mCryptoHalAidl->initCheck() == OK) { + mCryptoHalAidl->notifyResolution(width, height); + return; } - Mutex::Autolock autoLock(mLock); - - int32_t seqNum = mHeapSeqNum++; - uint32_t bufferId = static_cast(seqNum); - mHeapSizes.add(seqNum, heap->size()); - Return hResult = mPlugin->setSharedBufferBase(*heap, bufferId); - ALOGE_IF(!hResult.isOk(), "setSharedBufferBase(): remote call failed"); - return seqNum; + mCryptoHalHidl->notifyResolution(width, height); } -void CryptoHal::clearHeapBase(int32_t seqNum) { - Mutex::Autolock autoLock(mLock); - - /* - * Clear the remote shared memory mapping by setting the shared - * buffer base to a null hidl_memory. - * - * TODO: Add a releaseSharedBuffer method in a future DRM HAL - * API version to make this explicit. - */ - ssize_t index = mHeapSizes.indexOfKey(seqNum); - if (index >= 0) { - if (mPlugin != NULL) { - uint32_t bufferId = static_cast(seqNum); - Return hResult = mPlugin->setSharedBufferBase(hidl_memory(), bufferId); - ALOGE_IF(!hResult.isOk(), "setSharedBufferBase(): remote call failed"); - } - mHeapSizes.removeItem(seqNum); - } +status_t CryptoHal::setMediaDrmSession(const Vector& sessionId) { + // This requires plugin to be created. + if (mCryptoHalAidl->initCheck() == OK) return mCryptoHalAidl->setMediaDrmSession(sessionId); + return mCryptoHalHidl->setMediaDrmSession(sessionId); } -status_t CryptoHal::checkSharedBuffer(const ::SharedBuffer &buffer) { - int32_t seqNum = static_cast(buffer.bufferId); - // memory must be in one of the heaps that have been set - if (mHeapSizes.indexOfKey(seqNum) < 0) { - return UNKNOWN_ERROR; - } - - // memory must be within the address space of the heap - size_t heapSize = mHeapSizes.valueFor(seqNum); - if (heapSize < buffer.offset + buffer.size || - SIZE_MAX - buffer.offset < buffer.size) { - android_errorWriteLog(0x534e4554, "76221123"); - return UNKNOWN_ERROR; - } - - return OK; +ssize_t CryptoHal::decrypt(const uint8_t key[16], const uint8_t iv[16], CryptoPlugin::Mode mode, + const CryptoPlugin::Pattern& pattern, const ::SharedBuffer& source, + size_t offset, const CryptoPlugin::SubSample* subSamples, + size_t numSubSamples, const ::DestinationBuffer& destination, + AString* errorDetailMsg) { + // This requires plugin to be created. + if (mCryptoHalAidl->initCheck() == OK) + return mCryptoHalAidl->decrypt(key, iv, mode, pattern, source, offset, subSamples, + numSubSamples, destination, errorDetailMsg); + return mCryptoHalHidl->decrypt(key, iv, mode, pattern, source, offset, subSamples, + numSubSamples, destination, errorDetailMsg); } -ssize_t CryptoHal::decrypt(const uint8_t keyId[16], const uint8_t iv[16], - CryptoPlugin::Mode mode, const CryptoPlugin::Pattern &pattern, - const ::SharedBuffer &hSource, size_t offset, - const CryptoPlugin::SubSample *subSamples, size_t numSubSamples, - const ::DestinationBuffer &hDestination, AString *errorDetailMsg) { - Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return mInitCheck; - } - - Mode hMode; - switch(mode) { - case CryptoPlugin::kMode_Unencrypted: - hMode = Mode::UNENCRYPTED ; - break; - case CryptoPlugin::kMode_AES_CTR: - hMode = Mode::AES_CTR; - break; - case CryptoPlugin::kMode_AES_WV: - hMode = Mode::AES_CBC_CTS; - break; - case CryptoPlugin::kMode_AES_CBC: - hMode = Mode::AES_CBC; - break; - default: - return UNKNOWN_ERROR; - } - - Pattern hPattern; - hPattern.encryptBlocks = pattern.mEncryptBlocks; - hPattern.skipBlocks = pattern.mSkipBlocks; - - std::vector stdSubSamples; - for (size_t i = 0; i < numSubSamples; i++) { - SubSample subSample; - subSample.numBytesOfClearData = subSamples[i].mNumBytesOfClearData; - subSample.numBytesOfEncryptedData = subSamples[i].mNumBytesOfEncryptedData; - stdSubSamples.push_back(subSample); - } - auto hSubSamples = hidl_vec(stdSubSamples); - - bool secure; - if (hDestination.type == BufferType::SHARED_MEMORY) { - status_t status = checkSharedBuffer(hDestination.nonsecureMemory); - if (status != OK) { - return status; - } - secure = false; - } else if (hDestination.type == BufferType::NATIVE_HANDLE) { - secure = true; - } else { - android_errorWriteLog(0x534e4554, "70526702"); - return UNKNOWN_ERROR; - } - - status_t status = checkSharedBuffer(hSource); - if (status != OK) { - return status; - } - - status_t err = UNKNOWN_ERROR; - uint32_t bytesWritten = 0; - - Return hResult; - - mLock.unlock(); - if (mPluginV1_2 != NULL) { - hResult = mPluginV1_2->decrypt_1_2(secure, toHidlArray16(keyId), toHidlArray16(iv), - hMode, hPattern, hSubSamples, hSource, offset, hDestination, - [&](Status_V1_2 status, uint32_t hBytesWritten, hidl_string hDetailedError) { - if (status == Status_V1_2::OK) { - bytesWritten = hBytesWritten; - *errorDetailMsg = toString8(hDetailedError); - } - err = toStatusT(status); - } - ); - } else { - hResult = mPlugin->decrypt(secure, toHidlArray16(keyId), toHidlArray16(iv), - hMode, hPattern, hSubSamples, hSource, offset, hDestination, - [&](Status status, uint32_t hBytesWritten, hidl_string hDetailedError) { - if (status == Status::OK) { - bytesWritten = hBytesWritten; - *errorDetailMsg = toString8(hDetailedError); - } - err = toStatusT(status); - } - ); - } - - err = hResult.isOk() ? err : DEAD_OBJECT; - if (err == OK) { - return bytesWritten; - } - return err; +int32_t CryptoHal::setHeap(const sp& heap) { + // This requires plugin to be created. + if (mCryptoHalAidl->initCheck() == OK) return mCryptoHalAidl->setHeap(heap); + return mCryptoHalHidl->setHeap(heap); } -void CryptoHal::notifyResolution(uint32_t width, uint32_t height) { - Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { +void CryptoHal::unsetHeap(int32_t seqNum) { + // This requires plugin to be created. + if (mCryptoHalAidl->initCheck() == OK) { + mCryptoHalAidl->unsetHeap(seqNum); return; } - auto hResult = mPlugin->notifyResolution(width, height); - ALOGE_IF(!hResult.isOk(), "notifyResolution txn failed %s", hResult.description().c_str()); + mCryptoHalHidl->unsetHeap(seqNum); } -status_t CryptoHal::setMediaDrmSession(const Vector &sessionId) { - Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return mInitCheck; - } - - auto err = mPlugin->setMediaDrmSession(toHidlVec(sessionId)); - return err.isOk() ? toStatusT(err) : DEAD_OBJECT; +status_t CryptoHal::getLogMessages(Vector& logs) const { + // This requires plugin to be created. + if (mCryptoHalAidl->initCheck() == OK) return mCryptoHalAidl->getLogMessages(logs); + return mCryptoHalHidl->getLogMessages(logs); } -status_t CryptoHal::getLogMessages(Vector &logs) const { - Mutex::Autolock autoLock(mLock); - return DrmUtils::GetLogMessages(mPlugin, logs); -} -} // namespace android +} // namespace android \ No newline at end of file diff --git a/drm/libmediadrm/CryptoHalAidl.cpp b/drm/libmediadrm/CryptoHalAidl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8b9d1de93adc82228e6cfa6b526aaf1f878ff287 --- /dev/null +++ b/drm/libmediadrm/CryptoHalAidl.cpp @@ -0,0 +1,425 @@ +/* + * Copyright (C) 2021 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 "CryptoHalAidl" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using ::aidl::android::hardware::drm::CryptoSchemes; +using DestinationBufferAidl = ::aidl::android::hardware::drm::DestinationBuffer; +using ::aidl::android::hardware::drm::Mode; +using ::aidl::android::hardware::drm::Pattern; +using SharedBufferAidl = ::aidl::android::hardware::drm::SharedBuffer; +using ::aidl::android::hardware::drm::Status; +using ::aidl::android::hardware::drm::SubSample; +using ::aidl::android::hardware::drm::Uuid; +using ::aidl::android::hardware::drm::SecurityLevel; +using NativeHandleAidlCommon = ::aidl::android::hardware::common::NativeHandle; +using ::aidl::android::hardware::drm::DecryptArgs; + +using ::android::sp; +using ::android::DrmUtils::statusAidlToStatusT; +using ::android::hardware::hidl_array; +using ::android::hardware::hidl_handle; +using ::android::hardware::hidl_memory; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::HidlMemory; +using ::android::hardware::Return; +using ::android::hardware::Void; + +using ::aidl::android::hardware::drm::Uuid; +// -------Hidl interface related----------------- +// TODO: replace before removing hidl interface + +using BufferTypeHidl = ::android::hardware::drm::V1_0::BufferType; +using SharedBufferHidl = ::android::hardware::drm::V1_0::SharedBuffer; +using DestinationBufferHidl = ::android::hardware::drm::V1_0::DestinationBuffer; + +// -------Hidl interface related end------------- + +namespace android { + +template +static std::vector toStdVec(const Vector& vector) { + auto v = reinterpret_cast(vector.array()); + std::vector vec(v, v + vector.size()); + return vec; +} + +// -------Hidl interface related----------------- +// TODO: replace before removing hidl interface +status_t CryptoHalAidl::checkSharedBuffer(const SharedBufferHidl& buffer) { + int32_t seqNum = static_cast(buffer.bufferId); + // memory must be in one of the heaps that have been set + if (mHeapSizes.indexOfKey(seqNum) < 0) { + return UNKNOWN_ERROR; + } + + // memory must be within the address space of the heap + size_t heapSize = mHeapSizes.valueFor(seqNum); + if (heapSize < buffer.offset + buffer.size || SIZE_MAX - buffer.offset < buffer.size) { + android_errorWriteLog(0x534e4554, "76221123"); + return UNKNOWN_ERROR; + } + + return OK; +} + +static SharedBufferAidl hidlSharedBufferToAidlSharedBuffer(const SharedBufferHidl& buffer) { + SharedBufferAidl aidlsb; + aidlsb.bufferId = buffer.bufferId; + aidlsb.offset = buffer.offset; + aidlsb.size = buffer.size; + return aidlsb; +} + +static DestinationBufferAidl hidlDestinationBufferToAidlDestinationBuffer( + const DestinationBufferHidl& buffer) { + DestinationBufferAidl aidldb; + // skip negative convert check as count of enum elements are 2 + switch(buffer.type) { + case BufferTypeHidl::SHARED_MEMORY: + aidldb.set( + hidlSharedBufferToAidlSharedBuffer(buffer.nonsecureMemory)); + break; + default: + auto handle = buffer.secureMemory.getNativeHandle(); + if (handle) { + aidldb.set( + ::android::dupToAidl(handle)); + } else { + NativeHandleAidlCommon emptyhandle; + aidldb.set( + std::move(emptyhandle)); + } + break; + } + + return aidldb; +} + +static hidl_vec toHidlVec(const void* ptr, size_t size) { + hidl_vec vec; + vec.resize(size); + memcpy(vec.data(), ptr, size); + return vec; +} + +static const Vector toVector(const std::vector& vec) { + Vector vector; + vector.appendArray(vec.data(), vec.size()); + return *const_cast*>(&vector); +} + +static String8 toString8(const std::string& string) { + return String8(string.c_str()); +} + +static std::vector toStdVec(const uint8_t* ptr, size_t n) { + if (!ptr) { + return std::vector(); + } + return std::vector(ptr, ptr + n); +} + +// -------Hidl interface related end-------------- + +bool CryptoHalAidl::isCryptoSchemeSupportedInternal(const uint8_t uuid[16], int* factoryIdx) { + Uuid uuidAidl = DrmUtils::toAidlUuid(uuid); + for (size_t i = 0; i < mFactories.size(); i++) { + CryptoSchemes schemes{}; + if (mFactories[i]->getSupportedCryptoSchemes(&schemes).isOk()) { + if (std::count(schemes.uuids.begin(), schemes.uuids.end(), uuidAidl)) { + if (factoryIdx != NULL) *factoryIdx = i; + return true; + } + } + } + + return false; +} + +CryptoHalAidl::CryptoHalAidl() + : mFactories(DrmUtils::makeDrmFactoriesAidl()), + mInitCheck((mFactories.size() == 0) ? ERROR_UNSUPPORTED : NO_INIT), + mHeapSeqNum(0) {} + +CryptoHalAidl::~CryptoHalAidl() {} + +status_t CryptoHalAidl::initCheck() const { + return mInitCheck; +} + +bool CryptoHalAidl::isCryptoSchemeSupported(const uint8_t uuid[16]) { + Mutex::Autolock autoLock(mLock); + + return isCryptoSchemeSupportedInternal(uuid, NULL); +} + +status_t CryptoHalAidl::createPlugin(const uint8_t uuid[16], const void* data, size_t size) { + Mutex::Autolock autoLock(mLock); + + Uuid uuidAidl = DrmUtils::toAidlUuid(uuid); + std::vector dataAidl = toStdVec(toVector(toHidlVec(data, size))); + int i = 0; + if (isCryptoSchemeSupportedInternal(uuid, &i)) { + mPlugin = makeCryptoPlugin(mFactories[i], uuidAidl, dataAidl); + } + + if (mInitCheck == NO_INIT) { + mInitCheck = mPlugin == NULL ? ERROR_UNSUPPORTED : OK; + } + + return mInitCheck; +} + +std::shared_ptr CryptoHalAidl::makeCryptoPlugin( + const std::shared_ptr& factory, const Uuid& uuidAidl, + const std::vector initData) { + std::shared_ptr pluginAidl; + if (factory->createCryptoPlugin(uuidAidl, initData, &pluginAidl).isOk()) { + ALOGI("Create ICryptoPluginAidl. UUID:[%s]", uuidAidl.toString().c_str()); + } else { + mInitCheck = DEAD_OBJECT; + ALOGE("Failed to create ICryptoPluginAidl. UUID:[%s]", uuidAidl.toString().c_str()); + } + + return pluginAidl; +} + +status_t CryptoHalAidl::destroyPlugin() { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + mPlugin.reset(); + mInitCheck = NO_INIT; + return OK; +} + +bool CryptoHalAidl::requiresSecureDecoderComponent(const char* mime) const { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return false; + } + + std::string mimeStr = std::string(mime); + bool result; + if (!mPlugin->requiresSecureDecoderComponent(mimeStr, &result).isOk()) { + ALOGE("Failed to requiresSecureDecoderComponent. mime:[%s]", mime); + return false; + } + + return result; +} + +void CryptoHalAidl::notifyResolution(uint32_t width, uint32_t height) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return; + } + + // Check negative width and height after type conversion + // Log error and return if any is negative + if ((int32_t)width < 0 || (int32_t)height < 0) { + ALOGE("Negative width: %d or height %d in notifyResolution", width, height); + return; + } + + ::ndk::ScopedAStatus status = mPlugin->notifyResolution(width, height); + if (!status.isOk()) { + ALOGE("notifyResolution txn failed status code: %d", status.getServiceSpecificError()); + } +} + +status_t CryptoHalAidl::setMediaDrmSession(const Vector& sessionId) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + auto err = mPlugin->setMediaDrmSession(toStdVec(sessionId)); + return statusAidlToStatusT(err); +} + +ssize_t CryptoHalAidl::decrypt(const uint8_t keyId[16], const uint8_t iv[16], + CryptoPlugin::Mode mode, const CryptoPlugin::Pattern& pattern, + const SharedBufferHidl& hSource, size_t offset, + const CryptoPlugin::SubSample* subSamples, size_t numSubSamples, + const DestinationBufferHidl& hDestination, AString* errorDetailMsg) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + Mode aMode; + switch (mode) { + case CryptoPlugin::kMode_Unencrypted: + aMode = Mode::UNENCRYPTED; + break; + case CryptoPlugin::kMode_AES_CTR: + aMode = Mode::AES_CTR; + break; + case CryptoPlugin::kMode_AES_WV: + aMode = Mode::AES_CBC_CTS; + break; + case CryptoPlugin::kMode_AES_CBC: + aMode = Mode::AES_CBC; + break; + default: + return UNKNOWN_ERROR; + } + + Pattern aPattern; + aPattern.encryptBlocks = pattern.mEncryptBlocks; + aPattern.skipBlocks = pattern.mSkipBlocks; + + std::vector stdSubSamples; + for (size_t i = 0; i < numSubSamples; i++) { + SubSample subSample; + subSample.numBytesOfClearData = subSamples[i].mNumBytesOfClearData; + subSample.numBytesOfEncryptedData = subSamples[i].mNumBytesOfEncryptedData; + stdSubSamples.push_back(subSample); + } + + bool secure; + if (hDestination.type == BufferTypeHidl::SHARED_MEMORY) { + status_t status = checkSharedBuffer(hDestination.nonsecureMemory); + if (status != OK) { + return status; + } + secure = false; + } else if (hDestination.type == BufferTypeHidl::NATIVE_HANDLE) { + secure = true; + } else { + android_errorWriteLog(0x534e4554, "70526702"); + return UNKNOWN_ERROR; + } + + status_t status = checkSharedBuffer(hSource); + if (status != OK) { + return status; + } + + status_t err = UNKNOWN_ERROR; + mLock.unlock(); + + std::vector keyIdAidl(toStdVec(keyId, 16)); + std::vector ivAidl(toStdVec(iv, 16)); + + DecryptArgs args; + args.secure = secure; + args.keyId = keyIdAidl; + args.iv = ivAidl; + args.mode = aMode; + args.pattern = aPattern; + args.subSamples = std::move(stdSubSamples); + args.source = hidlSharedBufferToAidlSharedBuffer(hSource); + args.offset = offset; + args.destination = hidlDestinationBufferToAidlDestinationBuffer(hDestination); + + + int32_t result = 0; + ::ndk::ScopedAStatus statusAidl = mPlugin->decrypt(args, &result); + + err = statusAidlToStatusT(statusAidl); + std::string msgStr(statusAidl.getMessage()); + if (errorDetailMsg != nullptr) { + *errorDetailMsg = toString8(msgStr); + } + if (err != OK) { + ALOGE("Failed on decrypt, error description:%s", statusAidl.getDescription().c_str()); + return err; + } + + return result; +} + +int32_t CryptoHalAidl::setHeap(const sp& heap) { + if (heap == NULL || mHeapSeqNum < 0) { + ALOGE("setHeap(): heap %p mHeapSeqNum %d", heap.get(), mHeapSeqNum); + return -1; + } + + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return -1; + } + + int32_t seqNum = mHeapSeqNum++; + uint32_t bufferId = static_cast(seqNum); + mHeapSizes.add(seqNum, heap->size()); + + SharedBufferAidl memAidl; + memAidl.handle = ::android::dupToAidl(heap->handle()); + memAidl.size = heap->size(); + memAidl.bufferId = bufferId; + + auto status = mPlugin->setSharedBufferBase(memAidl); + ALOGE_IF(!status.isOk(), + "setSharedBufferBase(): remote call failed"); + return seqNum; +} + +void CryptoHalAidl::unsetHeap(int32_t seqNum) { + Mutex::Autolock autoLock(mLock); + + /* + * Clear the remote shared memory mapping by setting the shared + * buffer base to a null hidl_memory. + * + * TODO: Add a releaseSharedBuffer method in a future DRM HAL + * API version to make this explicit. + */ + ssize_t index = mHeapSizes.indexOfKey(seqNum); + if (index >= 0) { + if (mPlugin != NULL) { + uint32_t bufferId = static_cast(seqNum); + SharedBufferAidl memAidl{}; + memAidl.bufferId = bufferId; + auto status = mPlugin->setSharedBufferBase(memAidl); + ALOGE_IF(!status.isOk(), + "setSharedBufferBase(): remote call failed"); + } + mHeapSizes.removeItem(seqNum); + } +} + +status_t CryptoHalAidl::getLogMessages(Vector& logs) const { + Mutex::Autolock autoLock(mLock); + // Need to convert logmessage + + return DrmUtils::GetLogMessagesAidl(mPlugin, logs); +} +} // namespace android diff --git a/drm/libmediadrm/CryptoHalHidl.cpp b/drm/libmediadrm/CryptoHalHidl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..55364b5168f4578378ca45933d32a231b627dff0 --- /dev/null +++ b/drm/libmediadrm/CryptoHalHidl.cpp @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2017 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 "CryptoHalHidl" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using drm::V1_0::BufferType; +using drm::V1_0::DestinationBuffer; +using drm::V1_0::ICryptoFactory; +using drm::V1_0::ICryptoPlugin; +using drm::V1_0::Mode; +using drm::V1_0::Pattern; +using drm::V1_0::SharedBuffer; +using drm::V1_0::Status; +using drm::V1_0::SubSample; + +using ::android::sp; +using ::android::DrmUtils::toStatusT; +using ::android::hardware::hidl_array; +using ::android::hardware::hidl_handle; +using ::android::hardware::hidl_memory; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::HidlMemory; +using ::android::hardware::Return; +using ::android::hardware::Void; + +typedef drm::V1_2::Status Status_V1_2; + +namespace android { + +static hidl_vec toHidlVec(const Vector& vector) { + hidl_vec vec; + vec.setToExternal(const_cast(vector.array()), vector.size()); + return vec; +} + +static hidl_vec toHidlVec(const void* ptr, size_t size) { + hidl_vec vec; + vec.resize(size); + memcpy(vec.data(), ptr, size); + return vec; +} + +static hidl_array toHidlArray16(const uint8_t* ptr) { + if (!ptr) { + return hidl_array(); + } + return hidl_array(ptr); +} + +static String8 toString8(hidl_string hString) { + return String8(hString.c_str()); +} + +CryptoHalHidl::CryptoHalHidl() + : mFactories(makeCryptoFactories()), + mInitCheck((mFactories.size() == 0) ? ERROR_UNSUPPORTED : NO_INIT), + mHeapSeqNum(0) {} + +CryptoHalHidl::~CryptoHalHidl() {} + +Vector> CryptoHalHidl::makeCryptoFactories() { + Vector> factories; + + auto manager = hardware::defaultServiceManager1_2(); + if (manager != NULL) { + manager->listManifestByInterface( + drm::V1_0::ICryptoFactory::descriptor, + [&factories](const hidl_vec& registered) { + for (const auto& instance : registered) { + auto factory = drm::V1_0::ICryptoFactory::getService(instance); + if (factory != NULL) { + ALOGD("found drm@1.0 ICryptoFactory %s", instance.c_str()); + factories.push_back(factory); + } + } + }); + manager->listManifestByInterface( + drm::V1_1::ICryptoFactory::descriptor, + [&factories](const hidl_vec& registered) { + for (const auto& instance : registered) { + auto factory = drm::V1_1::ICryptoFactory::getService(instance); + if (factory != NULL) { + ALOGD("found drm@1.1 ICryptoFactory %s", instance.c_str()); + factories.push_back(factory); + } + } + }); + } + + if (factories.size() == 0) { + // must be in passthrough mode, load the default passthrough service + auto passthrough = ICryptoFactory::getService(); + if (passthrough != NULL) { + ALOGI("makeCryptoFactories: using default passthrough crypto instance"); + factories.push_back(passthrough); + } else { + ALOGE("Failed to find any crypto factories"); + } + } + return factories; +} + +sp CryptoHalHidl::makeCryptoPlugin(const sp& factory, + const uint8_t uuid[16], const void* initData, + size_t initDataSize) { + sp plugin; + Return hResult = + factory->createPlugin(toHidlArray16(uuid), toHidlVec(initData, initDataSize), + [&](Status status, const sp& hPlugin) { + if (status != Status::OK) { + ALOGE("Failed to make crypto plugin"); + return; + } + plugin = hPlugin; + }); + if (!hResult.isOk()) { + mInitCheck = DEAD_OBJECT; + } + return plugin; +} + +status_t CryptoHalHidl::initCheck() const { + return mInitCheck; +} + +bool CryptoHalHidl::isCryptoSchemeSupported(const uint8_t uuid[16]) { + Mutex::Autolock autoLock(mLock); + + for (size_t i = 0; i < mFactories.size(); i++) { + if (mFactories[i]->isCryptoSchemeSupported(uuid)) { + return true; + } + } + return false; +} + +status_t CryptoHalHidl::createPlugin(const uint8_t uuid[16], const void* data, size_t size) { + Mutex::Autolock autoLock(mLock); + + for (size_t i = 0; i < mFactories.size(); i++) { + if (mFactories[i]->isCryptoSchemeSupported(uuid)) { + mPlugin = makeCryptoPlugin(mFactories[i], uuid, data, size); + if (mPlugin != NULL) { + mPluginV1_2 = drm::V1_2::ICryptoPlugin::castFrom(mPlugin); + } + } + } + + if (mInitCheck == NO_INIT) { + mInitCheck = mPlugin == NULL ? ERROR_UNSUPPORTED : OK; + } + + return mInitCheck; +} + +status_t CryptoHalHidl::destroyPlugin() { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + mPlugin.clear(); + mPluginV1_2.clear(); + mInitCheck = NO_INIT; + return OK; +} + +bool CryptoHalHidl::requiresSecureDecoderComponent(const char* mime) const { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return false; + } + + Return hResult = mPlugin->requiresSecureDecoderComponent(hidl_string(mime)); + if (!hResult.isOk()) { + return false; + } + return hResult; +} + +/** + * If the heap base isn't set, get the heap base from the HidlMemory + * and send it to the HAL so it can map a remote heap of the same + * size. Once the heap base is established, shared memory buffers + * are sent by providing an offset into the heap and a buffer size. + */ +int32_t CryptoHalHidl::setHeapBase(const sp& heap) { + if (heap == NULL || mHeapSeqNum < 0) { + ALOGE("setHeapBase(): heap %p mHeapSeqNum %d", heap.get(), mHeapSeqNum); + return -1; + } + + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return -1; + } + + int32_t seqNum = mHeapSeqNum++; + uint32_t bufferId = static_cast(seqNum); + mHeapSizes.add(seqNum, heap->size()); + Return hResult = mPlugin->setSharedBufferBase(*heap, bufferId); + ALOGE_IF(!hResult.isOk(), "setSharedBufferBase(): remote call failed"); + return seqNum; +} + +void CryptoHalHidl::clearHeapBase(int32_t seqNum) { + Mutex::Autolock autoLock(mLock); + + /* + * Clear the remote shared memory mapping by setting the shared + * buffer base to a null hidl_memory. + * + * TODO: Add a releaseSharedBuffer method in a future DRM HAL + * API version to make this explicit. + */ + ssize_t index = mHeapSizes.indexOfKey(seqNum); + if (index >= 0) { + if (mPlugin != NULL) { + uint32_t bufferId = static_cast(seqNum); + Return hResult = mPlugin->setSharedBufferBase(hidl_memory(), bufferId); + ALOGE_IF(!hResult.isOk(), "setSharedBufferBase(): remote call failed"); + } + mHeapSizes.removeItem(seqNum); + } +} + +status_t CryptoHalHidl::checkSharedBuffer(const ::SharedBuffer& buffer) { + int32_t seqNum = static_cast(buffer.bufferId); + // memory must be in one of the heaps that have been set + if (mHeapSizes.indexOfKey(seqNum) < 0) { + return UNKNOWN_ERROR; + } + + // memory must be within the address space of the heap + size_t heapSize = mHeapSizes.valueFor(seqNum); + if (heapSize < buffer.offset + buffer.size || SIZE_MAX - buffer.offset < buffer.size) { + android_errorWriteLog(0x534e4554, "76221123"); + return UNKNOWN_ERROR; + } + + return OK; +} + +ssize_t CryptoHalHidl::decrypt(const uint8_t keyId[16], const uint8_t iv[16], + CryptoPlugin::Mode mode, const CryptoPlugin::Pattern& pattern, + const drm::V1_0::SharedBuffer& hSource, size_t offset, + const CryptoPlugin::SubSample* subSamples, size_t numSubSamples, + const drm::V1_0::DestinationBuffer& hDestination, + AString* errorDetailMsg) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + Mode hMode; + switch (mode) { + case CryptoPlugin::kMode_Unencrypted: + hMode = Mode::UNENCRYPTED; + break; + case CryptoPlugin::kMode_AES_CTR: + hMode = Mode::AES_CTR; + break; + case CryptoPlugin::kMode_AES_WV: + hMode = Mode::AES_CBC_CTS; + break; + case CryptoPlugin::kMode_AES_CBC: + hMode = Mode::AES_CBC; + break; + default: + return UNKNOWN_ERROR; + } + + Pattern hPattern; + hPattern.encryptBlocks = pattern.mEncryptBlocks; + hPattern.skipBlocks = pattern.mSkipBlocks; + + std::vector stdSubSamples; + for (size_t i = 0; i < numSubSamples; i++) { + SubSample subSample; + subSample.numBytesOfClearData = subSamples[i].mNumBytesOfClearData; + subSample.numBytesOfEncryptedData = subSamples[i].mNumBytesOfEncryptedData; + stdSubSamples.push_back(subSample); + } + auto hSubSamples = hidl_vec(stdSubSamples); + + bool secure; + if (hDestination.type == BufferType::SHARED_MEMORY) { + status_t status = checkSharedBuffer(hDestination.nonsecureMemory); + if (status != OK) { + return status; + } + secure = false; + } else if (hDestination.type == BufferType::NATIVE_HANDLE) { + secure = true; + } else { + android_errorWriteLog(0x534e4554, "70526702"); + return UNKNOWN_ERROR; + } + + status_t status = checkSharedBuffer(hSource); + if (status != OK) { + return status; + } + + status_t err = UNKNOWN_ERROR; + uint32_t bytesWritten = 0; + + Return hResult; + + mLock.unlock(); + if (mPluginV1_2 != NULL) { + hResult = mPluginV1_2->decrypt_1_2( + secure, toHidlArray16(keyId), toHidlArray16(iv), hMode, hPattern, hSubSamples, + hSource, offset, hDestination, + [&](Status_V1_2 status, uint32_t hBytesWritten, hidl_string hDetailedError) { + if (status == Status_V1_2::OK) { + bytesWritten = hBytesWritten; + if (errorDetailMsg != nullptr) { + *errorDetailMsg = toString8(hDetailedError); + } + } + err = toStatusT(status); + }); + } else { + hResult = mPlugin->decrypt( + secure, toHidlArray16(keyId), toHidlArray16(iv), hMode, hPattern, hSubSamples, + hSource, offset, hDestination, + [&](Status status, uint32_t hBytesWritten, hidl_string hDetailedError) { + if (status == Status::OK) { + bytesWritten = hBytesWritten; + if (errorDetailMsg != nullptr) { + *errorDetailMsg = toString8(hDetailedError); + } + } + err = toStatusT(status); + }); + } + + err = hResult.isOk() ? err : DEAD_OBJECT; + if (err == OK) { + return bytesWritten; + } + return err; +} + +void CryptoHalHidl::notifyResolution(uint32_t width, uint32_t height) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return; + } + + auto hResult = mPlugin->notifyResolution(width, height); + ALOGE_IF(!hResult.isOk(), "notifyResolution txn failed %s", hResult.description().c_str()); +} + +status_t CryptoHalHidl::setMediaDrmSession(const Vector& sessionId) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + auto err = mPlugin->setMediaDrmSession(toHidlVec(sessionId)); + return err.isOk() ? toStatusT(err) : DEAD_OBJECT; +} + +status_t CryptoHalHidl::getLogMessages(Vector& logs) const { + Mutex::Autolock autoLock(mLock); + return DrmUtils::GetLogMessages(mPlugin, logs); +} +} // namespace android diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp index 40d1e0c4af4ae38722928e5b076a0c1c4e10177a..c394d5aaf03a3459188b993147f23124a62d7978 100644 --- a/drm/libmediadrm/DrmHal.cpp +++ b/drm/libmediadrm/DrmHal.cpp @@ -17,1557 +17,280 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "DrmHal" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include +#include +#include #include -#include - -#include -#include - -using drm::V1_0::KeyedVector; -using drm::V1_0::KeyRequestType; -using drm::V1_0::KeyType; -using drm::V1_0::KeyValue; -using drm::V1_0::SecureStop; -using drm::V1_0::SecureStopId; -using drm::V1_0::Status; -using drm::V1_1::HdcpLevel; -using drm::V1_1::SecureStopRelease; -using drm::V1_1::SecurityLevel; -using drm::V1_2::KeySetId; -using drm::V1_2::KeyStatusType; -using ::android::DrmUtils::toStatusT; -using ::android::hardware::drm::V1_1::DrmMetricGroup; -using ::android::hardware::hidl_array; -using ::android::hardware::hidl_string; -using ::android::hardware::hidl_vec; -using ::android::hardware::Return; -using ::android::hardware::Void; -using ::android::os::PersistableBundle; -using ::android::sp; - -typedef drm::V1_1::KeyRequestType KeyRequestType_V1_1; -typedef drm::V1_2::Status Status_V1_2; -typedef drm::V1_2::HdcpLevel HdcpLevel_V1_2; - -namespace { - -// This constant corresponds to the PROPERTY_DEVICE_UNIQUE_ID constant -// in the MediaDrm API. -constexpr char kPropertyDeviceUniqueId[] = "deviceUniqueId"; -constexpr char kEqualsSign[] = "="; - -template -std::string toBase64StringNoPad(const T* data, size_t size) { - // Note that the base 64 conversion only works with arrays of single-byte - // values. If the source is empty or is not an array of single-byte values, - // return empty string. - if (size == 0 || sizeof(data[0]) != 1) { - return ""; - } - - android::AString outputString; - encodeBase64(data, size, &outputString); - // Remove trailing equals padding if it exists. - while (outputString.size() > 0 && outputString.endsWith(kEqualsSign)) { - outputString.erase(outputString.size() - 1, 1); - } - - return std::string(outputString.c_str(), outputString.size()); -} - -} // anonymous namespace namespace android { -#define INIT_CHECK() {if (mInitCheck != OK) return mInitCheck;} - -static const Vector toVector(const hidl_vec &vec) { - Vector vector; - vector.appendArray(vec.data(), vec.size()); - return *const_cast *>(&vector); -} - -static hidl_vec toHidlVec(const Vector &vector) { - hidl_vec vec; - vec.setToExternal(const_cast(vector.array()), vector.size()); - return vec; -} - -static String8 toString8(const hidl_string &string) { - return String8(string.c_str()); -} - -static hidl_string toHidlString(const String8& string) { - return hidl_string(string.string()); -} - -static DrmPlugin::SecurityLevel toSecurityLevel(SecurityLevel level) { - switch(level) { - case SecurityLevel::SW_SECURE_CRYPTO: - return DrmPlugin::kSecurityLevelSwSecureCrypto; - case SecurityLevel::SW_SECURE_DECODE: - return DrmPlugin::kSecurityLevelSwSecureDecode; - case SecurityLevel::HW_SECURE_CRYPTO: - return DrmPlugin::kSecurityLevelHwSecureCrypto; - case SecurityLevel::HW_SECURE_DECODE: - return DrmPlugin::kSecurityLevelHwSecureDecode; - case SecurityLevel::HW_SECURE_ALL: - return DrmPlugin::kSecurityLevelHwSecureAll; - default: - return DrmPlugin::kSecurityLevelUnknown; - } -} - -static SecurityLevel toHidlSecurityLevel(DrmPlugin::SecurityLevel level) { - switch(level) { - case DrmPlugin::kSecurityLevelSwSecureCrypto: - return SecurityLevel::SW_SECURE_CRYPTO; - case DrmPlugin::kSecurityLevelSwSecureDecode: - return SecurityLevel::SW_SECURE_DECODE; - case DrmPlugin::kSecurityLevelHwSecureCrypto: - return SecurityLevel::HW_SECURE_CRYPTO; - case DrmPlugin::kSecurityLevelHwSecureDecode: - return SecurityLevel::HW_SECURE_DECODE; - case DrmPlugin::kSecurityLevelHwSecureAll: - return SecurityLevel::HW_SECURE_ALL; - default: - return SecurityLevel::UNKNOWN; - } -} - -static DrmPlugin::OfflineLicenseState toOfflineLicenseState( - OfflineLicenseState licenseState) { - switch(licenseState) { - case OfflineLicenseState::USABLE: - return DrmPlugin::kOfflineLicenseStateUsable; - case OfflineLicenseState::INACTIVE: - return DrmPlugin::kOfflineLicenseStateReleased; - default: - return DrmPlugin::kOfflineLicenseStateUnknown; - } -} - -static DrmPlugin::HdcpLevel toHdcpLevel(HdcpLevel_V1_2 level) { - switch(level) { - case HdcpLevel_V1_2::HDCP_NONE: - return DrmPlugin::kHdcpNone; - case HdcpLevel_V1_2::HDCP_V1: - return DrmPlugin::kHdcpV1; - case HdcpLevel_V1_2::HDCP_V2: - return DrmPlugin::kHdcpV2; - case HdcpLevel_V1_2::HDCP_V2_1: - return DrmPlugin::kHdcpV2_1; - case HdcpLevel_V1_2::HDCP_V2_2: - return DrmPlugin::kHdcpV2_2; - case HdcpLevel_V1_2::HDCP_V2_3: - return DrmPlugin::kHdcpV2_3; - case HdcpLevel_V1_2::HDCP_NO_OUTPUT: - return DrmPlugin::kHdcpNoOutput; - default: - return DrmPlugin::kHdcpLevelUnknown; - } -} -static ::KeyedVector toHidlKeyedVector(const KeyedVector& - keyedVector) { - std::vector stdKeyedVector; - for (size_t i = 0; i < keyedVector.size(); i++) { - KeyValue keyValue; - keyValue.key = toHidlString(keyedVector.keyAt(i)); - keyValue.value = toHidlString(keyedVector.valueAt(i)); - stdKeyedVector.push_back(keyValue); - } - return ::KeyedVector(stdKeyedVector); -} - -static KeyedVector toKeyedVector(const ::KeyedVector& - hKeyedVector) { - KeyedVector keyedVector; - for (size_t i = 0; i < hKeyedVector.size(); i++) { - keyedVector.add(toString8(hKeyedVector[i].key), - toString8(hKeyedVector[i].value)); - } - return keyedVector; -} - -static List> toSecureStops(const hidl_vec& - hSecureStops) { - List> secureStops; - for (size_t i = 0; i < hSecureStops.size(); i++) { - secureStops.push_back(toVector(hSecureStops[i].opaqueData)); - } - return secureStops; -} - -static List> toSecureStopIds(const hidl_vec& - hSecureStopIds) { - List> secureStopIds; - for (size_t i = 0; i < hSecureStopIds.size(); i++) { - secureStopIds.push_back(toVector(hSecureStopIds[i])); - } - return secureStopIds; -} - -static List> toKeySetIds(const hidl_vec& - hKeySetIds) { - List> keySetIds; - for (size_t i = 0; i < hKeySetIds.size(); i++) { - keySetIds.push_back(toVector(hKeySetIds[i])); - } - return keySetIds; -} - -Mutex DrmHal::mLock; - -struct DrmHal::DrmSessionClient : public aidl::android::media::BnResourceManagerClient { - explicit DrmSessionClient(DrmHal* drm, const Vector& sessionId) - : mSessionId(sessionId), - mDrm(drm) {} - - ::ndk::ScopedAStatus reclaimResource(bool* _aidl_return) override; - ::ndk::ScopedAStatus getName(::std::string* _aidl_return) override; - - const Vector mSessionId; - - virtual ~DrmSessionClient(); - -private: - wp mDrm; - - DISALLOW_EVIL_CONSTRUCTORS(DrmSessionClient); -}; - -::ndk::ScopedAStatus DrmHal::DrmSessionClient::reclaimResource(bool* _aidl_return) { - auto sessionId = mSessionId; - sp drm = mDrm.promote(); - if (drm == NULL) { - *_aidl_return = true; - return ::ndk::ScopedAStatus::ok(); - } - status_t err = drm->closeSession(sessionId); - if (err != OK) { - *_aidl_return = false; - return ::ndk::ScopedAStatus::ok(); - } - drm->sendEvent(EventType::SESSION_RECLAIMED, - toHidlVec(sessionId), hidl_vec()); - *_aidl_return = true; - return ::ndk::ScopedAStatus::ok(); -} - -::ndk::ScopedAStatus DrmHal::DrmSessionClient::getName(::std::string* _aidl_return) { - String8 name; - sp drm = mDrm.promote(); - if (drm == NULL) { - name.append(""); - } else if (drm->getPropertyStringInternal(String8("vendor"), name) != OK - || name.isEmpty()) { - name.append(""); - } - name.append("["); - for (size_t i = 0; i < mSessionId.size(); ++i) { - name.appendFormat("%02x", mSessionId[i]); - } - name.append("]"); - *_aidl_return = name; - return ::ndk::ScopedAStatus::ok(); -} - -DrmHal::DrmSessionClient::~DrmSessionClient() { - DrmSessionManager::Instance()->removeSession(mSessionId); -} - -DrmHal::DrmHal() - : mFactories(makeDrmFactories()), - mInitCheck((mFactories.size() == 0) ? ERROR_UNSUPPORTED : NO_INIT) { -} - -void DrmHal::closeOpenSessions() { - Mutex::Autolock autoLock(mLock); - auto openSessions = mOpenSessions; - for (size_t i = 0; i < openSessions.size(); i++) { - mLock.unlock(); - closeSession(openSessions[i]->mSessionId); - mLock.lock(); - } - mOpenSessions.clear(); -} - -DrmHal::~DrmHal() { -} - -void DrmHal::cleanup() { - closeOpenSessions(); - - Mutex::Autolock autoLock(mLock); - reportFrameworkMetrics(reportPluginMetrics()); - - setListener(NULL); - mInitCheck = NO_INIT; - if (mPluginV1_2 != NULL) { - if (!mPluginV1_2->setListener(NULL).isOk()) { - mInitCheck = DEAD_OBJECT; - } - } else if (mPlugin != NULL) { - if (!mPlugin->setListener(NULL).isOk()) { - mInitCheck = DEAD_OBJECT; - } - } - mPlugin.clear(); - mPluginV1_1.clear(); - mPluginV1_2.clear(); - mPluginV1_4.clear(); +DrmHal::DrmHal() { + mDrmHalHidl = sp::make(); + mDrmHalAidl = sp::make(); } -std::vector> DrmHal::makeDrmFactories() { - static std::vector> factories(DrmUtils::MakeDrmFactories()); - if (factories.size() == 0) { - // must be in passthrough mode, load the default passthrough service - auto passthrough = IDrmFactory::getService(); - if (passthrough != NULL) { - DrmUtils::LOG2BI("makeDrmFactories: using default passthrough drm instance"); - factories.push_back(passthrough); - } else { - DrmUtils::LOG2BE("Failed to find any drm factories"); - } - } - return factories; -} - -sp DrmHal::makeDrmPlugin(const sp& factory, - const uint8_t uuid[16], const String8& appPackageName) { - mAppPackageName = appPackageName; - mMetrics.SetAppPackageName(appPackageName); - mMetrics.SetAppUid(AIBinder_getCallingUid()); - - sp plugin; - Return hResult = factory->createPlugin(uuid, appPackageName.string(), - [&](Status status, const sp& hPlugin) { - if (status != Status::OK) { - DrmUtils::LOG2BE(uuid, "Failed to make drm plugin: %d", status); - return; - } - plugin = hPlugin; - } - ); - - if (!hResult.isOk()) { - DrmUtils::LOG2BE(uuid, "createPlugin remote call failed: %s", - hResult.description().c_str()); - } - - return plugin; -} +DrmHal::~DrmHal() {} status_t DrmHal::initCheck() const { - return mInitCheck; -} - -status_t DrmHal::setListener(const sp& listener) -{ - Mutex::Autolock lock(mEventLock); - mListener = listener; - return NO_ERROR; -} - -Return DrmHal::sendEvent(EventType hEventType, - const hidl_vec& sessionId, const hidl_vec& data) { - mMetrics.mEventCounter.Increment(hEventType); - - mEventLock.lock(); - sp listener = mListener; - mEventLock.unlock(); - - if (listener != NULL) { - Mutex::Autolock lock(mNotifyLock); - DrmPlugin::EventType eventType; - switch(hEventType) { - case EventType::PROVISION_REQUIRED: - eventType = DrmPlugin::kDrmPluginEventProvisionRequired; - break; - case EventType::KEY_NEEDED: - eventType = DrmPlugin::kDrmPluginEventKeyNeeded; - break; - case EventType::KEY_EXPIRED: - eventType = DrmPlugin::kDrmPluginEventKeyExpired; - break; - case EventType::VENDOR_DEFINED: - eventType = DrmPlugin::kDrmPluginEventVendorDefined; - break; - case EventType::SESSION_RECLAIMED: - eventType = DrmPlugin::kDrmPluginEventSessionReclaimed; - break; - default: - return Void(); - } - listener->sendEvent(eventType, sessionId, data); - } - return Void(); -} - -Return DrmHal::sendExpirationUpdate(const hidl_vec& sessionId, - int64_t expiryTimeInMS) { - - mEventLock.lock(); - sp listener = mListener; - mEventLock.unlock(); - - if (listener != NULL) { - Mutex::Autolock lock(mNotifyLock); - listener->sendExpirationUpdate(sessionId, expiryTimeInMS); - } - return Void(); -} - -Return DrmHal::sendKeysChange(const hidl_vec& sessionId, - const hidl_vec& keyStatusList_V1_0, bool hasNewUsableKey) { - std::vector keyStatusVec; - for (const auto &keyStatus_V1_0 : keyStatusList_V1_0) { - keyStatusVec.push_back({keyStatus_V1_0.keyId, - static_cast(keyStatus_V1_0.type)}); - } - hidl_vec keyStatusList_V1_2(keyStatusVec); - return sendKeysChange_1_2(sessionId, keyStatusList_V1_2, hasNewUsableKey); -} - -Return DrmHal::sendKeysChange_1_2(const hidl_vec& sessionId, - const hidl_vec& hKeyStatusList, bool hasNewUsableKey) { - - mEventLock.lock(); - sp listener = mListener; - mEventLock.unlock(); - - if (listener != NULL) { - std::vector keyStatusList; - size_t nKeys = hKeyStatusList.size(); - for (size_t i = 0; i < nKeys; ++i) { - const KeyStatus &keyStatus = hKeyStatusList[i]; - uint32_t type; - switch(keyStatus.type) { - case KeyStatusType::USABLE: - type = DrmPlugin::kKeyStatusType_Usable; - break; - case KeyStatusType::EXPIRED: - type = DrmPlugin::kKeyStatusType_Expired; - break; - case KeyStatusType::OUTPUTNOTALLOWED: - type = DrmPlugin::kKeyStatusType_OutputNotAllowed; - break; - case KeyStatusType::STATUSPENDING: - type = DrmPlugin::kKeyStatusType_StatusPending; - break; - case KeyStatusType::USABLEINFUTURE: - type = DrmPlugin::kKeyStatusType_UsableInFuture; - break; - case KeyStatusType::INTERNALERROR: - default: - type = DrmPlugin::kKeyStatusType_InternalError; - break; - } - keyStatusList.push_back({type, keyStatus.keyId}); - mMetrics.mKeyStatusChangeCounter.Increment(keyStatus.type); - } - - Mutex::Autolock lock(mNotifyLock); - listener->sendKeysChange(sessionId, keyStatusList, hasNewUsableKey); - } else { - // There's no listener. But we still want to count the key change - // events. - size_t nKeys = hKeyStatusList.size(); - for (size_t i = 0; i < nKeys; i++) { - mMetrics.mKeyStatusChangeCounter.Increment(hKeyStatusList[i].type); - } - } - - return Void(); + if (mDrmHalAidl->initCheck() == OK || mDrmHalHidl->initCheck() == OK) return OK; + if (mDrmHalAidl->initCheck() == NO_INIT || mDrmHalHidl->initCheck() == NO_INIT) return NO_INIT; + return mDrmHalHidl->initCheck(); } -Return DrmHal::sendSessionLostState( - const hidl_vec& sessionId) { - - mEventLock.lock(); - sp listener = mListener; - mEventLock.unlock(); - - if (listener != NULL) { - Mutex::Autolock lock(mNotifyLock); - listener->sendSessionLostState(sessionId); - } - return Void(); -} - -status_t DrmHal::matchMimeTypeAndSecurityLevel(const sp &factory, - const uint8_t uuid[16], - const String8 &mimeType, - DrmPlugin::SecurityLevel level, - bool *isSupported) { - *isSupported = false; - - // handle default value cases - if (level == DrmPlugin::kSecurityLevelUnknown) { - if (mimeType == "") { - // isCryptoSchemeSupported(uuid) - *isSupported = true; - } else { - // isCryptoSchemeSupported(uuid, mimeType) - *isSupported = factory->isContentTypeSupported(mimeType.string()); - } - return OK; - } else if (mimeType == "") { - return BAD_VALUE; - } - - sp factoryV1_2 = drm::V1_2::IDrmFactory::castFrom(factory); - if (factoryV1_2 == NULL) { - return ERROR_UNSUPPORTED; - } else { - *isSupported = factoryV1_2->isCryptoSchemeSupported_1_2(uuid, - mimeType.string(), toHidlSecurityLevel(level)); - return OK; - } -} - -status_t DrmHal::isCryptoSchemeSupported(const uint8_t uuid[16], - const String8 &mimeType, - DrmPlugin::SecurityLevel level, - bool *isSupported) { - Mutex::Autolock autoLock(mLock); - *isSupported = false; - for (ssize_t i = mFactories.size() - 1; i >= 0; i--) { - if (mFactories[i]->isCryptoSchemeSupported(uuid)) { - return matchMimeTypeAndSecurityLevel(mFactories[i], - uuid, mimeType, level, isSupported); - } - } - return OK; +status_t DrmHal::isCryptoSchemeSupported(const uint8_t uuid[16], const String8& mimeType, + DrmPlugin::SecurityLevel securityLevel, bool* result) { + status_t statusResult; + statusResult = mDrmHalAidl->isCryptoSchemeSupported(uuid, mimeType, securityLevel, result); + if (*result) return statusResult; + return mDrmHalHidl->isCryptoSchemeSupported(uuid, mimeType, securityLevel, result); } -status_t DrmHal::createPlugin(const uint8_t uuid[16], - const String8& appPackageName) { - Mutex::Autolock autoLock(mLock); - - for (ssize_t i = mFactories.size() - 1; i >= 0; i--) { - auto hResult = mFactories[i]->isCryptoSchemeSupported(uuid); - if (hResult.isOk() && hResult) { - auto plugin = makeDrmPlugin(mFactories[i], uuid, appPackageName); - if (plugin != NULL) { - mPlugin = plugin; - mPluginV1_1 = drm::V1_1::IDrmPlugin::castFrom(mPlugin); - mPluginV1_2 = drm::V1_2::IDrmPlugin::castFrom(mPlugin); - mPluginV1_4 = drm::V1_4::IDrmPlugin::castFrom(mPlugin); - break; - } - } - } - - if (mPlugin == NULL) { - DrmUtils::LOG2BE(uuid, "No supported hal instance found"); - mInitCheck = ERROR_UNSUPPORTED; - } else { - mInitCheck = OK; - if (mPluginV1_2 != NULL) { - if (!mPluginV1_2->setListener(this).isOk()) { - mInitCheck = DEAD_OBJECT; - } - } else if (!mPlugin->setListener(this).isOk()) { - mInitCheck = DEAD_OBJECT; - } - if (mInitCheck != OK) { - mPlugin.clear(); - mPluginV1_1.clear(); - mPluginV1_2.clear(); - mPluginV1_4.clear(); - } - } - - - return mInitCheck; +status_t DrmHal::createPlugin(const uint8_t uuid[16], const String8& appPackageName) { + status_t statusResult; + statusResult = mDrmHalAidl->createPlugin(uuid, appPackageName); + if (statusResult != OK) return mDrmHalHidl->createPlugin(uuid, appPackageName); + return statusResult; } status_t DrmHal::destroyPlugin() { - cleanup(); - return OK; + status_t statusResult = mDrmHalAidl->destroyPlugin(); + status_t statusResultHidl = mDrmHalHidl->destroyPlugin(); + if (statusResult != OK) return statusResult; + return statusResultHidl; } -status_t DrmHal::openSession(DrmPlugin::SecurityLevel level, - Vector &sessionId) { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - SecurityLevel hSecurityLevel = toHidlSecurityLevel(level); - bool setSecurityLevel = true; - - if (level == DrmPlugin::kSecurityLevelMax) { - setSecurityLevel = false; - } else { - if (hSecurityLevel == SecurityLevel::UNKNOWN) { - return ERROR_DRM_CANNOT_HANDLE; - } - } - - status_t err = UNKNOWN_ERROR; - bool retry = true; - do { - hidl_vec hSessionId; - - Return hResult; - if (mPluginV1_1 == NULL || !setSecurityLevel) { - hResult = mPlugin->openSession( - [&](Status status,const hidl_vec& id) { - if (status == Status::OK) { - sessionId = toVector(id); - } - err = toStatusT(status); - } - ); - } else { - hResult = mPluginV1_1->openSession_1_1(hSecurityLevel, - [&](Status status, const hidl_vec& id) { - if (status == Status::OK) { - sessionId = toVector(id); - } - err = toStatusT(status); - } - ); - } - - if (!hResult.isOk()) { - err = DEAD_OBJECT; - } - - if (err == ERROR_DRM_RESOURCE_BUSY && retry) { - mLock.unlock(); - // reclaimSession may call back to closeSession, since mLock is - // shared between Drm instances, we should unlock here to avoid - // deadlock. - retry = DrmSessionManager::Instance()->reclaimSession(AIBinder_getCallingPid()); - mLock.lock(); - } else { - retry = false; - } - } while (retry); - - if (err == OK) { - std::shared_ptr client = - ndk::SharedRefBase::make(this, sessionId); - DrmSessionManager::Instance()->addSession(AIBinder_getCallingPid(), - std::static_pointer_cast(client), sessionId); - mOpenSessions.push_back(client); - mMetrics.SetSessionStart(sessionId); - } - - mMetrics.mOpenSessionCounter.Increment(err); - return err; +status_t DrmHal::openSession(DrmPlugin::SecurityLevel securityLevel, Vector& sessionId) { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->openSession(securityLevel, sessionId); + return mDrmHalHidl->openSession(securityLevel, sessionId); } -status_t DrmHal::closeSession(Vector const &sessionId) { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - Return status = mPlugin->closeSession(toHidlVec(sessionId)); - if (status.isOk()) { - if (status == Status::OK) { - DrmSessionManager::Instance()->removeSession(sessionId); - for (auto i = mOpenSessions.begin(); i != mOpenSessions.end(); i++) { - if (isEqualSessionId((*i)->mSessionId, sessionId)) { - mOpenSessions.erase(i); - break; - } - } - } - status_t response = toStatusT(status); - mMetrics.SetSessionEnd(sessionId); - mMetrics.mCloseSessionCounter.Increment(response); - return response; - } - mMetrics.mCloseSessionCounter.Increment(DEAD_OBJECT); - return DEAD_OBJECT; +status_t DrmHal::closeSession(Vector const& sessionId) { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->closeSession(sessionId); + return mDrmHalHidl->closeSession(sessionId); } -static DrmPlugin::KeyRequestType toKeyRequestType( - KeyRequestType keyRequestType) { - switch (keyRequestType) { - case KeyRequestType::INITIAL: - return DrmPlugin::kKeyRequestType_Initial; - break; - case KeyRequestType::RENEWAL: - return DrmPlugin::kKeyRequestType_Renewal; - break; - case KeyRequestType::RELEASE: - return DrmPlugin::kKeyRequestType_Release; - break; - default: - return DrmPlugin::kKeyRequestType_Unknown; - break; - } +status_t DrmHal::getKeyRequest(Vector const& sessionId, Vector const& initData, + String8 const& mimeType, DrmPlugin::KeyType keyType, + KeyedVector const& optionalParameters, + Vector& request, String8& defaultUrl, + DrmPlugin::KeyRequestType* keyRequestType) { + if (mDrmHalAidl->initCheck() == OK) + return mDrmHalAidl->getKeyRequest(sessionId, initData, mimeType, keyType, + optionalParameters, request, defaultUrl, keyRequestType); + return mDrmHalHidl->getKeyRequest(sessionId, initData, mimeType, keyType, optionalParameters, + request, defaultUrl, keyRequestType); } -static DrmPlugin::KeyRequestType toKeyRequestType_1_1( - KeyRequestType_V1_1 keyRequestType) { - switch (keyRequestType) { - case KeyRequestType_V1_1::NONE: - return DrmPlugin::kKeyRequestType_None; - break; - case KeyRequestType_V1_1::UPDATE: - return DrmPlugin::kKeyRequestType_Update; - break; - default: - return toKeyRequestType(static_cast(keyRequestType)); - break; - } +status_t DrmHal::provideKeyResponse(Vector const& sessionId, + Vector const& response, Vector& keySetId) { + if (mDrmHalAidl->initCheck() == OK) + return mDrmHalAidl->provideKeyResponse(sessionId, response, keySetId); + return mDrmHalHidl->provideKeyResponse(sessionId, response, keySetId); } -status_t DrmHal::getKeyRequest(Vector const &sessionId, - Vector const &initData, String8 const &mimeType, - DrmPlugin::KeyType keyType, KeyedVector const &optionalParameters, Vector &request, - String8 &defaultUrl, DrmPlugin::KeyRequestType *keyRequestType) { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - EventTimer keyRequestTimer(&mMetrics.mGetKeyRequestTimeUs); - - DrmSessionManager::Instance()->useSession(sessionId); - - KeyType hKeyType; - if (keyType == DrmPlugin::kKeyType_Streaming) { - hKeyType = KeyType::STREAMING; - } else if (keyType == DrmPlugin::kKeyType_Offline) { - hKeyType = KeyType::OFFLINE; - } else if (keyType == DrmPlugin::kKeyType_Release) { - hKeyType = KeyType::RELEASE; - } else { - keyRequestTimer.SetAttribute(BAD_VALUE); - return BAD_VALUE; - } - - ::KeyedVector hOptionalParameters = toHidlKeyedVector(optionalParameters); - - status_t err = UNKNOWN_ERROR; - Return hResult; - - if (mPluginV1_2 != NULL) { - hResult = mPluginV1_2->getKeyRequest_1_2( - toHidlVec(sessionId), toHidlVec(initData), - toHidlString(mimeType), hKeyType, hOptionalParameters, - [&](Status_V1_2 status, const hidl_vec& hRequest, - KeyRequestType_V1_1 hKeyRequestType, - const hidl_string& hDefaultUrl) { - if (status == Status_V1_2::OK) { - request = toVector(hRequest); - defaultUrl = toString8(hDefaultUrl); - *keyRequestType = toKeyRequestType_1_1(hKeyRequestType); - } - err = toStatusT(status); - }); - } else if (mPluginV1_1 != NULL) { - hResult = mPluginV1_1->getKeyRequest_1_1( - toHidlVec(sessionId), toHidlVec(initData), - toHidlString(mimeType), hKeyType, hOptionalParameters, - [&](Status status, const hidl_vec& hRequest, - KeyRequestType_V1_1 hKeyRequestType, - const hidl_string& hDefaultUrl) { - if (status == Status::OK) { - request = toVector(hRequest); - defaultUrl = toString8(hDefaultUrl); - *keyRequestType = toKeyRequestType_1_1(hKeyRequestType); - } - err = toStatusT(status); - }); - } else { - hResult = mPlugin->getKeyRequest( - toHidlVec(sessionId), toHidlVec(initData), - toHidlString(mimeType), hKeyType, hOptionalParameters, - [&](Status status, const hidl_vec& hRequest, - KeyRequestType hKeyRequestType, - const hidl_string& hDefaultUrl) { - if (status == Status::OK) { - request = toVector(hRequest); - defaultUrl = toString8(hDefaultUrl); - *keyRequestType = toKeyRequestType(hKeyRequestType); - } - err = toStatusT(status); - }); - } - - err = hResult.isOk() ? err : DEAD_OBJECT; - keyRequestTimer.SetAttribute(err); - return err; +status_t DrmHal::removeKeys(Vector const& keySetId) { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->removeKeys(keySetId); + return mDrmHalHidl->removeKeys(keySetId); } -status_t DrmHal::provideKeyResponse(Vector const &sessionId, - Vector const &response, Vector &keySetId) { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - EventTimer keyResponseTimer(&mMetrics.mProvideKeyResponseTimeUs); - - DrmSessionManager::Instance()->useSession(sessionId); - - status_t err = UNKNOWN_ERROR; - - Return hResult = mPlugin->provideKeyResponse(toHidlVec(sessionId), - toHidlVec(response), - [&](Status status, const hidl_vec& hKeySetId) { - if (status == Status::OK) { - keySetId = toVector(hKeySetId); - } - err = toStatusT(status); - } - ); - err = hResult.isOk() ? err : DEAD_OBJECT; - keyResponseTimer.SetAttribute(err); - return err; +status_t DrmHal::restoreKeys(Vector const& sessionId, Vector const& keySetId) { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->restoreKeys(sessionId, keySetId); + return mDrmHalHidl->restoreKeys(sessionId, keySetId); } -status_t DrmHal::removeKeys(Vector const &keySetId) { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - Return status = mPlugin->removeKeys(toHidlVec(keySetId)); - return status.isOk() ? toStatusT(status) : DEAD_OBJECT; +status_t DrmHal::queryKeyStatus(Vector const& sessionId, + KeyedVector& infoMap) const { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->queryKeyStatus(sessionId, infoMap); + return mDrmHalHidl->queryKeyStatus(sessionId, infoMap); } -status_t DrmHal::restoreKeys(Vector const &sessionId, - Vector const &keySetId) { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - DrmSessionManager::Instance()->useSession(sessionId); - - Return status = mPlugin->restoreKeys(toHidlVec(sessionId), - toHidlVec(keySetId)); - return status.isOk() ? toStatusT(status) : DEAD_OBJECT; +status_t DrmHal::getProvisionRequest(String8 const& certType, String8 const& certAuthority, + Vector& request, String8& defaultUrl) { + if (mDrmHalAidl->initCheck() == OK) + return mDrmHalAidl->getProvisionRequest(certType, certAuthority, request, defaultUrl); + return mDrmHalHidl->getProvisionRequest(certType, certAuthority, request, defaultUrl); } -status_t DrmHal::queryKeyStatus(Vector const &sessionId, - KeyedVector &infoMap) const { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - DrmSessionManager::Instance()->useSession(sessionId); - - ::KeyedVector hInfoMap; - - status_t err = UNKNOWN_ERROR; - - Return hResult = mPlugin->queryKeyStatus(toHidlVec(sessionId), - [&](Status status, const hidl_vec& map) { - if (status == Status::OK) { - infoMap = toKeyedVector(map); - } - err = toStatusT(status); - } - ); - - return hResult.isOk() ? err : DEAD_OBJECT; +status_t DrmHal::provideProvisionResponse(Vector const& response, + Vector& certificate, + Vector& wrappedKey) { + if (mDrmHalAidl->initCheck() == OK) + return mDrmHalAidl->provideProvisionResponse(response, certificate, wrappedKey); + return mDrmHalHidl->provideProvisionResponse(response, certificate, wrappedKey); } -status_t DrmHal::getProvisionRequest(String8 const &certType, - String8 const &certAuthority, Vector &request, - String8 &defaultUrl) { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - status_t err = UNKNOWN_ERROR; - Return hResult; - - if (mPluginV1_2 != NULL) { - hResult = mPluginV1_2->getProvisionRequest_1_2( - toHidlString(certType), toHidlString(certAuthority), - [&](Status_V1_2 status, const hidl_vec& hRequest, - const hidl_string& hDefaultUrl) { - if (status == Status_V1_2::OK) { - request = toVector(hRequest); - defaultUrl = toString8(hDefaultUrl); - } - err = toStatusT(status); - } - ); - } else { - hResult = mPlugin->getProvisionRequest( - toHidlString(certType), toHidlString(certAuthority), - [&](Status status, const hidl_vec& hRequest, - const hidl_string& hDefaultUrl) { - if (status == Status::OK) { - request = toVector(hRequest); - defaultUrl = toString8(hDefaultUrl); - } - err = toStatusT(status); - } - ); - } - - err = hResult.isOk() ? err : DEAD_OBJECT; - mMetrics.mGetProvisionRequestCounter.Increment(err); - return err; +status_t DrmHal::getSecureStops(List>& secureStops) { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->getSecureStops(secureStops); + return mDrmHalHidl->getSecureStops(secureStops); } -status_t DrmHal::provideProvisionResponse(Vector const &response, - Vector &certificate, Vector &wrappedKey) { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - status_t err = UNKNOWN_ERROR; - - Return hResult = mPlugin->provideProvisionResponse(toHidlVec(response), - [&](Status status, const hidl_vec& hCertificate, - const hidl_vec& hWrappedKey) { - if (status == Status::OK) { - certificate = toVector(hCertificate); - wrappedKey = toVector(hWrappedKey); - } - err = toStatusT(status); - } - ); - - err = hResult.isOk() ? err : DEAD_OBJECT; - mMetrics.mProvideProvisionResponseCounter.Increment(err); - return err; -} - -status_t DrmHal::getSecureStops(List> &secureStops) { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - status_t err = UNKNOWN_ERROR; - - Return hResult = mPlugin->getSecureStops( - [&](Status status, const hidl_vec& hSecureStops) { - if (status == Status::OK) { - secureStops = toSecureStops(hSecureStops); - } - err = toStatusT(status); - } - ); - - return hResult.isOk() ? err : DEAD_OBJECT; -} - - -status_t DrmHal::getSecureStopIds(List> &secureStopIds) { - Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return mInitCheck; - } - - if (mPluginV1_1 == NULL) { - return ERROR_DRM_CANNOT_HANDLE; - } - - status_t err = UNKNOWN_ERROR; - - Return hResult = mPluginV1_1->getSecureStopIds( - [&](Status status, const hidl_vec& hSecureStopIds) { - if (status == Status::OK) { - secureStopIds = toSecureStopIds(hSecureStopIds); - } - err = toStatusT(status); - } - ); - - return hResult.isOk() ? err : DEAD_OBJECT; +status_t DrmHal::getSecureStopIds(List>& secureStopIds) { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->getSecureStopIds(secureStopIds); + return mDrmHalHidl->getSecureStopIds(secureStopIds); } - -status_t DrmHal::getSecureStop(Vector const &ssid, Vector &secureStop) { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - status_t err = UNKNOWN_ERROR; - - Return hResult = mPlugin->getSecureStop(toHidlVec(ssid), - [&](Status status, const SecureStop& hSecureStop) { - if (status == Status::OK) { - secureStop = toVector(hSecureStop.opaqueData); - } - err = toStatusT(status); - } - ); - - return hResult.isOk() ? err : DEAD_OBJECT; +status_t DrmHal::getSecureStop(Vector const& ssid, Vector& secureStop) { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->getSecureStop(ssid, secureStop); + return mDrmHalHidl->getSecureStop(ssid, secureStop); } -status_t DrmHal::releaseSecureStops(Vector const &ssRelease) { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - Return status(Status::ERROR_DRM_UNKNOWN); - if (mPluginV1_1 != NULL) { - SecureStopRelease secureStopRelease; - secureStopRelease.opaqueData = toHidlVec(ssRelease); - status = mPluginV1_1->releaseSecureStops(secureStopRelease); - } else { - status = mPlugin->releaseSecureStop(toHidlVec(ssRelease)); - } - return status.isOk() ? toStatusT(status) : DEAD_OBJECT; +status_t DrmHal::releaseSecureStops(Vector const& ssRelease) { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->releaseSecureStops(ssRelease); + return mDrmHalHidl->releaseSecureStops(ssRelease); } -status_t DrmHal::removeSecureStop(Vector const &ssid) { - Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return mInitCheck; - } - - if (mPluginV1_1 == NULL) { - return ERROR_DRM_CANNOT_HANDLE; - } - - Return status = mPluginV1_1->removeSecureStop(toHidlVec(ssid)); - return status.isOk() ? toStatusT(status) : DEAD_OBJECT; +status_t DrmHal::removeSecureStop(Vector const& ssid) { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->removeSecureStop(ssid); + return mDrmHalHidl->removeSecureStop(ssid); } status_t DrmHal::removeAllSecureStops() { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - Return status(Status::ERROR_DRM_UNKNOWN); - if (mPluginV1_1 != NULL) { - status = mPluginV1_1->removeAllSecureStops(); - } else { - status = mPlugin->releaseAllSecureStops(); - } - return status.isOk() ? toStatusT(status) : DEAD_OBJECT; + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->removeAllSecureStops(); + return mDrmHalHidl->removeAllSecureStops(); } -status_t DrmHal::getHdcpLevels(DrmPlugin::HdcpLevel *connected, - DrmPlugin::HdcpLevel *max) const { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - if (connected == NULL || max == NULL) { - return BAD_VALUE; - } - status_t err = UNKNOWN_ERROR; - - *connected = DrmPlugin::kHdcpLevelUnknown; - *max = DrmPlugin::kHdcpLevelUnknown; - - Return hResult; - if (mPluginV1_2 != NULL) { - hResult = mPluginV1_2->getHdcpLevels_1_2( - [&](Status_V1_2 status, const HdcpLevel_V1_2& hConnected, const HdcpLevel_V1_2& hMax) { - if (status == Status_V1_2::OK) { - *connected = toHdcpLevel(hConnected); - *max = toHdcpLevel(hMax); - } - err = toStatusT(status); - }); - } else if (mPluginV1_1 != NULL) { - hResult = mPluginV1_1->getHdcpLevels( - [&](Status status, const HdcpLevel& hConnected, const HdcpLevel& hMax) { - if (status == Status::OK) { - *connected = toHdcpLevel(static_cast(hConnected)); - *max = toHdcpLevel(static_cast(hMax)); - } - err = toStatusT(status); - }); - } else { - return ERROR_DRM_CANNOT_HANDLE; - } - - return hResult.isOk() ? err : DEAD_OBJECT; +status_t DrmHal::getHdcpLevels(DrmPlugin::HdcpLevel* connectedLevel, + DrmPlugin::HdcpLevel* maxLevel) const { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->getHdcpLevels(connectedLevel, maxLevel); + return mDrmHalHidl->getHdcpLevels(connectedLevel, maxLevel); } -status_t DrmHal::getNumberOfSessions(uint32_t *open, uint32_t *max) const { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - if (open == NULL || max == NULL) { - return BAD_VALUE; - } - status_t err = UNKNOWN_ERROR; - - *open = 0; - *max = 0; - - if (mPluginV1_1 == NULL) { - return ERROR_DRM_CANNOT_HANDLE; - } - - Return hResult = mPluginV1_1->getNumberOfSessions( - [&](Status status, uint32_t hOpen, uint32_t hMax) { - if (status == Status::OK) { - *open = hOpen; - *max = hMax; - } - err = toStatusT(status); - } - ); - - return hResult.isOk() ? err : DEAD_OBJECT; +status_t DrmHal::getNumberOfSessions(uint32_t* currentSessions, uint32_t* maxSessions) const { + if (mDrmHalAidl->initCheck() == OK) + return mDrmHalAidl->getNumberOfSessions(currentSessions, maxSessions); + return mDrmHalHidl->getNumberOfSessions(currentSessions, maxSessions); } -status_t DrmHal::getSecurityLevel(Vector const &sessionId, - DrmPlugin::SecurityLevel *level) const { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - if (level == NULL) { - return BAD_VALUE; - } - status_t err = UNKNOWN_ERROR; - - if (mPluginV1_1 == NULL) { - return ERROR_DRM_CANNOT_HANDLE; - } - - *level = DrmPlugin::kSecurityLevelUnknown; - - Return hResult = mPluginV1_1->getSecurityLevel(toHidlVec(sessionId), - [&](Status status, SecurityLevel hLevel) { - if (status == Status::OK) { - *level = toSecurityLevel(hLevel); - } - err = toStatusT(status); - } - ); - - return hResult.isOk() ? err : DEAD_OBJECT; +status_t DrmHal::getSecurityLevel(Vector const& sessionId, + DrmPlugin::SecurityLevel* level) const { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->getSecurityLevel(sessionId, level); + return mDrmHalHidl->getSecurityLevel(sessionId, level); } -status_t DrmHal::getOfflineLicenseKeySetIds(List> &keySetIds) const { - Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return mInitCheck; - } - - if (mPluginV1_2 == NULL) { - return ERROR_UNSUPPORTED; - } - - status_t err = UNKNOWN_ERROR; - - Return hResult = mPluginV1_2->getOfflineLicenseKeySetIds( - [&](Status status, const hidl_vec& hKeySetIds) { - if (status == Status::OK) { - keySetIds = toKeySetIds(hKeySetIds); - } - err = toStatusT(status); - } - ); - - return hResult.isOk() ? err : DEAD_OBJECT; +status_t DrmHal::getOfflineLicenseKeySetIds(List>& keySetIds) const { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->getOfflineLicenseKeySetIds(keySetIds); + return mDrmHalHidl->getOfflineLicenseKeySetIds(keySetIds); } -status_t DrmHal::removeOfflineLicense(Vector const &keySetId) { - Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return mInitCheck; - } - - if (mPluginV1_2 == NULL) { - return ERROR_UNSUPPORTED; - } - - Return status = mPluginV1_2->removeOfflineLicense(toHidlVec(keySetId)); - return status.isOk() ? toStatusT(status) : DEAD_OBJECT; +status_t DrmHal::removeOfflineLicense(Vector const& keySetId) { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->removeOfflineLicense(keySetId); + return mDrmHalHidl->removeOfflineLicense(keySetId); } -status_t DrmHal::getOfflineLicenseState(Vector const &keySetId, - DrmPlugin::OfflineLicenseState *licenseState) const { - Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return mInitCheck; - } - - if (mPluginV1_2 == NULL) { - return ERROR_UNSUPPORTED; - } - *licenseState = DrmPlugin::kOfflineLicenseStateUnknown; - - status_t err = UNKNOWN_ERROR; - - Return hResult = mPluginV1_2->getOfflineLicenseState(toHidlVec(keySetId), - [&](Status status, OfflineLicenseState hLicenseState) { - if (status == Status::OK) { - *licenseState = toOfflineLicenseState(hLicenseState); - } - err = toStatusT(status); - } - ); - - return hResult.isOk() ? err : DEAD_OBJECT; +status_t DrmHal::getOfflineLicenseState(Vector const& keySetId, + DrmPlugin::OfflineLicenseState* licenseState) const { + if (mDrmHalAidl->initCheck() == OK) + return mDrmHalAidl->getOfflineLicenseState(keySetId, licenseState); + return mDrmHalHidl->getOfflineLicenseState(keySetId, licenseState); } -status_t DrmHal::getPropertyString(String8 const &name, String8 &value ) const { - Mutex::Autolock autoLock(mLock); - return getPropertyStringInternal(name, value); +status_t DrmHal::getPropertyString(String8 const& name, String8& value) const { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->getPropertyString(name, value); + return mDrmHalHidl->getPropertyString(name, value); } -status_t DrmHal::getPropertyStringInternal(String8 const &name, String8 &value) const { - // This function is internal to the class and should only be called while - // mLock is already held. - INIT_CHECK(); - - status_t err = UNKNOWN_ERROR; - - Return hResult = mPlugin->getPropertyString(toHidlString(name), - [&](Status status, const hidl_string& hValue) { - if (status == Status::OK) { - value = toString8(hValue); - } - err = toStatusT(status); - } - ); - - return hResult.isOk() ? err : DEAD_OBJECT; +status_t DrmHal::getPropertyByteArray(String8 const& name, Vector& value) const { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->getPropertyByteArray(name, value); + return mDrmHalHidl->getPropertyByteArray(name, value); } -status_t DrmHal::getPropertyByteArray(String8 const &name, Vector &value ) const { - Mutex::Autolock autoLock(mLock); - return getPropertyByteArrayInternal(name, value); +status_t DrmHal::setPropertyString(String8 const& name, String8 const& value) const { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->setPropertyString(name, value); + return mDrmHalHidl->setPropertyString(name, value); } -status_t DrmHal::getPropertyByteArrayInternal(String8 const &name, Vector &value ) const { - // This function is internal to the class and should only be called while - // mLock is already held. - INIT_CHECK(); - - status_t err = UNKNOWN_ERROR; - - Return hResult = mPlugin->getPropertyByteArray(toHidlString(name), - [&](Status status, const hidl_vec& hValue) { - if (status == Status::OK) { - value = toVector(hValue); - } - err = toStatusT(status); - } - ); - - err = hResult.isOk() ? err : DEAD_OBJECT; - if (name == kPropertyDeviceUniqueId) { - mMetrics.mGetDeviceUniqueIdCounter.Increment(err); - } - return err; +status_t DrmHal::setPropertyByteArray(String8 const& name, Vector const& value) const { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->setPropertyByteArray(name, value); + return mDrmHalHidl->setPropertyByteArray(name, value); } -status_t DrmHal::setPropertyString(String8 const &name, String8 const &value ) const { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - Return status = mPlugin->setPropertyString(toHidlString(name), - toHidlString(value)); - return status.isOk() ? toStatusT(status) : DEAD_OBJECT; +status_t DrmHal::getMetrics(const sp& consumer) { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->getMetrics(consumer); + return mDrmHalHidl->getMetrics(consumer); } -status_t DrmHal::setPropertyByteArray(String8 const &name, - Vector const &value ) const { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - Return status = mPlugin->setPropertyByteArray(toHidlString(name), - toHidlVec(value)); - return status.isOk() ? toStatusT(status) : DEAD_OBJECT; +status_t DrmHal::setCipherAlgorithm(Vector const& sessionId, String8 const& algorithm) { + if (mDrmHalAidl->initCheck() == OK) + return mDrmHalAidl->setCipherAlgorithm(sessionId, algorithm); + return mDrmHalHidl->setCipherAlgorithm(sessionId, algorithm); } -status_t DrmHal::getMetrics(const sp &consumer) { - if (consumer == nullptr) { - return UNEXPECTED_NULL; - } - consumer->consumeFrameworkMetrics(mMetrics); - - // Append vendor metrics if they are supported. - if (mPluginV1_1 != NULL) { - String8 vendor; - String8 description; - if (getPropertyStringInternal(String8("vendor"), vendor) != OK - || vendor.isEmpty()) { - ALOGE("Get vendor failed or is empty"); - vendor = "NONE"; - } - if (getPropertyStringInternal(String8("description"), description) != OK - || description.isEmpty()) { - ALOGE("Get description failed or is empty."); - description = "NONE"; - } - vendor += "."; - vendor += description; - - hidl_vec pluginMetrics; - status_t err = UNKNOWN_ERROR; - - Return status = mPluginV1_1->getMetrics( - [&](Status status, hidl_vec pluginMetrics) { - if (status != Status::OK) { - ALOGV("Error getting plugin metrics: %d", status); - } else { - consumer->consumeHidlMetrics(vendor, pluginMetrics); - } - err = toStatusT(status); - }); - return status.isOk() ? err : DEAD_OBJECT; - } - - return OK; +status_t DrmHal::setMacAlgorithm(Vector const& sessionId, String8 const& algorithm) { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->setMacAlgorithm(sessionId, algorithm); + return mDrmHalHidl->setMacAlgorithm(sessionId, algorithm); } -status_t DrmHal::setCipherAlgorithm(Vector const &sessionId, - String8 const &algorithm) { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - DrmSessionManager::Instance()->useSession(sessionId); - - Return status = mPlugin->setCipherAlgorithm(toHidlVec(sessionId), - toHidlString(algorithm)); - return status.isOk() ? toStatusT(status) : DEAD_OBJECT; +status_t DrmHal::encrypt(Vector const& sessionId, Vector const& keyId, + Vector const& input, Vector const& iv, + Vector& output) { + if (mDrmHalAidl->initCheck() == OK) + return mDrmHalAidl->encrypt(sessionId, keyId, input, iv, output); + return mDrmHalHidl->encrypt(sessionId, keyId, input, iv, output); } -status_t DrmHal::setMacAlgorithm(Vector const &sessionId, - String8 const &algorithm) { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - DrmSessionManager::Instance()->useSession(sessionId); - - Return status = mPlugin->setMacAlgorithm(toHidlVec(sessionId), - toHidlString(algorithm)); - return status.isOk() ? toStatusT(status) : DEAD_OBJECT; -} - -status_t DrmHal::encrypt(Vector const &sessionId, - Vector const &keyId, Vector const &input, - Vector const &iv, Vector &output) { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - DrmSessionManager::Instance()->useSession(sessionId); - - status_t err = UNKNOWN_ERROR; - - Return hResult = mPlugin->encrypt(toHidlVec(sessionId), - toHidlVec(keyId), toHidlVec(input), toHidlVec(iv), - [&](Status status, const hidl_vec& hOutput) { - if (status == Status::OK) { - output = toVector(hOutput); - } - err = toStatusT(status); - } - ); - - return hResult.isOk() ? err : DEAD_OBJECT; -} - -status_t DrmHal::decrypt(Vector const &sessionId, - Vector const &keyId, Vector const &input, - Vector const &iv, Vector &output) { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - DrmSessionManager::Instance()->useSession(sessionId); - - status_t err = UNKNOWN_ERROR; - - Return hResult = mPlugin->decrypt(toHidlVec(sessionId), - toHidlVec(keyId), toHidlVec(input), toHidlVec(iv), - [&](Status status, const hidl_vec& hOutput) { - if (status == Status::OK) { - output = toVector(hOutput); - } - err = toStatusT(status); - } - ); - - return hResult.isOk() ? err : DEAD_OBJECT; +status_t DrmHal::decrypt(Vector const& sessionId, Vector const& keyId, + Vector const& input, Vector const& iv, + Vector& output) { + if (mDrmHalAidl->initCheck() == OK) + return mDrmHalAidl->decrypt(sessionId, keyId, input, iv, output); + return mDrmHalHidl->decrypt(sessionId, keyId, input, iv, output); } -status_t DrmHal::sign(Vector const &sessionId, - Vector const &keyId, Vector const &message, - Vector &signature) { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - DrmSessionManager::Instance()->useSession(sessionId); - - status_t err = UNKNOWN_ERROR; - - Return hResult = mPlugin->sign(toHidlVec(sessionId), - toHidlVec(keyId), toHidlVec(message), - [&](Status status, const hidl_vec& hSignature) { - if (status == Status::OK) { - signature = toVector(hSignature); - } - err = toStatusT(status); - } - ); - - return hResult.isOk() ? err : DEAD_OBJECT; +status_t DrmHal::sign(Vector const& sessionId, Vector const& keyId, + Vector const& message, Vector& signature) { + if (mDrmHalAidl->initCheck() == OK) + return mDrmHalAidl->sign(sessionId, keyId, message, signature); + return mDrmHalHidl->sign(sessionId, keyId, message, signature); } -status_t DrmHal::verify(Vector const &sessionId, - Vector const &keyId, Vector const &message, - Vector const &signature, bool &match) { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - DrmSessionManager::Instance()->useSession(sessionId); - - status_t err = UNKNOWN_ERROR; - - Return hResult = mPlugin->verify(toHidlVec(sessionId),toHidlVec(keyId), - toHidlVec(message), toHidlVec(signature), - [&](Status status, bool hMatch) { - if (status == Status::OK) { - match = hMatch; - } else { - match = false; - } - err = toStatusT(status); - } - ); - - return hResult.isOk() ? err : DEAD_OBJECT; +status_t DrmHal::verify(Vector const& sessionId, Vector const& keyId, + Vector const& message, Vector const& signature, + bool& match) { + if (mDrmHalAidl->initCheck() == OK) + return mDrmHalAidl->verify(sessionId, keyId, message, signature, match); + return mDrmHalHidl->verify(sessionId, keyId, message, signature, match); } -status_t DrmHal::signRSA(Vector const &sessionId, - String8 const &algorithm, Vector const &message, - Vector const &wrappedKey, Vector &signature) { - Mutex::Autolock autoLock(mLock); - INIT_CHECK(); - - DrmSessionManager::Instance()->useSession(sessionId); - - status_t err = UNKNOWN_ERROR; - - Return hResult = mPlugin->signRSA(toHidlVec(sessionId), - toHidlString(algorithm), toHidlVec(message), toHidlVec(wrappedKey), - [&](Status status, const hidl_vec& hSignature) { - if (status == Status::OK) { - signature = toVector(hSignature); - } - err = toStatusT(status); - } - ); - - return hResult.isOk() ? err : DEAD_OBJECT; +status_t DrmHal::signRSA(Vector const& sessionId, String8 const& algorithm, + Vector const& message, Vector const& wrappedKey, + Vector& signature) { + if (mDrmHalAidl->initCheck() == OK) + return mDrmHalAidl->signRSA(sessionId, algorithm, message, wrappedKey, signature); + return mDrmHalHidl->signRSA(sessionId, algorithm, message, wrappedKey, signature); } -std::string DrmHal::reportFrameworkMetrics(const std::string& pluginMetrics) const -{ - mediametrics_handle_t item(mediametrics_create("mediadrm")); - mediametrics_setUid(item, mMetrics.GetAppUid()); - String8 vendor; - String8 description; - status_t result = getPropertyStringInternal(String8("vendor"), vendor); - if (result != OK) { - ALOGE("Failed to get vendor from drm plugin: %d", result); - } else { - mediametrics_setCString(item, "vendor", vendor.c_str()); - } - result = getPropertyStringInternal(String8("description"), description); - if (result != OK) { - ALOGE("Failed to get description from drm plugin: %d", result); - } else { - mediametrics_setCString(item, "description", description.c_str()); - } - - std::string serializedMetrics; - result = mMetrics.GetSerializedMetrics(&serializedMetrics); - if (result != OK) { - ALOGE("Failed to serialize framework metrics: %d", result); - } - std::string b64EncodedMetrics = toBase64StringNoPad(serializedMetrics.data(), - serializedMetrics.size()); - if (!b64EncodedMetrics.empty()) { - mediametrics_setCString(item, "serialized_metrics", b64EncodedMetrics.c_str()); - } - if (!pluginMetrics.empty()) { - mediametrics_setCString(item, "plugin_metrics", pluginMetrics.c_str()); - } - if (!mediametrics_selfRecord(item)) { - ALOGE("Failed to self record framework metrics"); - } - mediametrics_delete(item); - return serializedMetrics; +status_t DrmHal::setListener(const sp& listener) { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->setListener(listener); + return mDrmHalHidl->setListener(listener); } -std::string DrmHal::reportPluginMetrics() const -{ - Vector metricsVector; - String8 vendor; - String8 description; - std::string metricsString; - if (getPropertyStringInternal(String8("vendor"), vendor) == OK && - getPropertyStringInternal(String8("description"), description) == OK && - getPropertyByteArrayInternal(String8("metrics"), metricsVector) == OK) { - metricsString = toBase64StringNoPad(metricsVector.array(), - metricsVector.size()); - status_t res = android::reportDrmPluginMetrics(metricsString, vendor, - description, mMetrics.GetAppUid()); - if (res != OK) { - ALOGE("Metrics were retrieved but could not be reported: %d", res); - } - } - return metricsString; +status_t DrmHal::requiresSecureDecoder(const char* mime, bool* required) const { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->requiresSecureDecoder(mime, required); + return mDrmHalHidl->requiresSecureDecoder(mime, required); } -status_t DrmHal::requiresSecureDecoder(const char *mime, bool *required) const { - Mutex::Autolock autoLock(mLock); - if (mPluginV1_4 == NULL) { - return false; - } - auto hResult = mPluginV1_4->requiresSecureDecoderDefault(hidl_string(mime)); - if (!hResult.isOk()) { - DrmUtils::LOG2BE("requiresSecureDecoder txn failed: %s", hResult.description().c_str()); - return DEAD_OBJECT; - } - if (required) { - *required = hResult; - } - return OK; +status_t DrmHal::requiresSecureDecoder(const char* mime, DrmPlugin::SecurityLevel securityLevel, + bool* required) const { + if (mDrmHalAidl->initCheck() == OK) + return mDrmHalAidl->requiresSecureDecoder(mime, securityLevel, required); + return mDrmHalHidl->requiresSecureDecoder(mime, securityLevel, required); } -status_t DrmHal::requiresSecureDecoder(const char *mime, DrmPlugin::SecurityLevel securityLevel, - bool *required) const { - Mutex::Autolock autoLock(mLock); - if (mPluginV1_4 == NULL) { - return false; - } - auto hLevel = toHidlSecurityLevel(securityLevel); - auto hResult = mPluginV1_4->requiresSecureDecoder(hidl_string(mime), hLevel); - if (!hResult.isOk()) { - DrmUtils::LOG2BE("requiresSecureDecoder txn failed: %s", hResult.description().c_str()); - return DEAD_OBJECT; - } - if (required) { - *required = hResult; - } - return OK; +status_t DrmHal::setPlaybackId(Vector const& sessionId, const char* playbackId) { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->setPlaybackId(sessionId, playbackId); + return mDrmHalHidl->setPlaybackId(sessionId, playbackId); } -status_t DrmHal::setPlaybackId(Vector const &sessionId, const char *playbackId) { - Mutex::Autolock autoLock(mLock); - if (mPluginV1_4 == NULL) { - return ERROR_UNSUPPORTED; - } - auto err = mPluginV1_4->setPlaybackId(toHidlVec(sessionId), hidl_string(playbackId)); - return err.isOk() ? toStatusT(err) : DEAD_OBJECT; +status_t DrmHal::getLogMessages(Vector& logs) const { + if (mDrmHalAidl->initCheck() == OK) return mDrmHalAidl->getLogMessages(logs); + return mDrmHalHidl->getLogMessages(logs); } -status_t DrmHal::getLogMessages(Vector &logs) const { - Mutex::Autolock autoLock(mLock); - return DrmUtils::GetLogMessages(mPlugin, logs); +status_t DrmHal::getSupportedSchemes(std::vector &schemes) const { + status_t statusResult; + statusResult = mDrmHalAidl->getSupportedSchemes(schemes); + if (statusResult == OK) return statusResult; + return mDrmHalHidl->getSupportedSchemes(schemes); } } // namespace android diff --git a/drm/libmediadrm/DrmHalAidl.cpp b/drm/libmediadrm/DrmHalAidl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bdd83e94ac4e45fb3c75230fdd09d3c8ecef732f --- /dev/null +++ b/drm/libmediadrm/DrmHalAidl.cpp @@ -0,0 +1,1254 @@ +/* + * Copyright (C) 2021 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 "DrmHalAidl" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using ::android::DrmUtils::statusAidlToStatusT; +using ::aidl::android::hardware::drm::CryptoSchemes; +using ::aidl::android::hardware::drm::DrmMetricNamedValue; +using ::aidl::android::hardware::drm::DrmMetricValue; +using ::aidl::android::hardware::drm::HdcpLevel; +using ::aidl::android::hardware::drm::HdcpLevels; +using ::aidl::android::hardware::drm::KeyRequest; +using ::aidl::android::hardware::drm::KeyRequestType; +using ::aidl::android::hardware::drm::KeySetId; +using ::aidl::android::hardware::drm::KeyStatus; +using ::aidl::android::hardware::drm::KeyStatusType; +using ::aidl::android::hardware::drm::KeyType; +using ::aidl::android::hardware::drm::KeyValue; +using ::aidl::android::hardware::drm::NumberOfSessions; +using ::aidl::android::hardware::drm::OfflineLicenseState; +using ::aidl::android::hardware::drm::OpaqueData; +using ::aidl::android::hardware::drm::ProvideProvisionResponseResult; +using ::aidl::android::hardware::drm::ProvisionRequest; +using ::aidl::android::hardware::drm::SecureStop; +using ::aidl::android::hardware::drm::SecureStopId; +using ::aidl::android::hardware::drm::SecurityLevel; +using ::aidl::android::hardware::drm::Status; +using ::aidl::android::hardware::drm::SupportedContentType; +using ::aidl::android::hardware::drm::Uuid; +using DrmMetricGroupAidl = ::aidl::android::hardware::drm::DrmMetricGroup; +using DrmMetricGroupHidl = ::android::hardware::drm::V1_1::DrmMetricGroup; +using DrmMetricAidl = ::aidl::android::hardware::drm::DrmMetric; +using DrmMetricHidl = ::android::hardware::drm::V1_1::DrmMetricGroup::Metric; +using ValueHidl = ::android::hardware::drm::V1_1::DrmMetricGroup::Value; +using AttributeHidl = ::android::hardware::drm::V1_1::DrmMetricGroup::Attribute; +using IDrmPluginAidl = ::aidl::android::hardware::drm::IDrmPlugin; +using EventTypeAidl = ::aidl::android::hardware::drm::EventType; +using ::android::hardware::hidl_vec; + +namespace { + +constexpr char kPropertyDeviceUniqueId[] = "deviceUniqueId"; +constexpr char kEqualsSign[] = "="; + +template +std::string toBase64StringNoPad(const T* data, size_t size) { + // Note that the base 64 conversion only works with arrays of single-byte + // values. If the source is empty or is not an array of single-byte values, + // return empty string. + if (size == 0 || sizeof(data[0]) != 1) { + return ""; + } + + android::AString outputString; + encodeBase64(data, size, &outputString); + // Remove trailing equals padding if it exists. + while (outputString.size() > 0 && outputString.endsWith(kEqualsSign)) { + outputString.erase(outputString.size() - 1, 1); + } + + return std::string(outputString.c_str(), outputString.size()); +} + +} // anonymous namespace + +namespace android { + +#define INIT_CHECK() \ + { \ + if (mInitCheck != OK) return mInitCheck; \ + } + +template +static std::vector toStdVec(const Vector& vector) { + auto v = reinterpret_cast(vector.array()); + std::vector vec(v, v + vector.size()); + return vec; +} + +static const Vector toVector(const std::vector& vec) { + Vector vector; + vector.appendArray(vec.data(), vec.size()); + return *const_cast*>(&vector); +} + +static String8 toString8(const std::string& string) { + return String8(string.c_str()); +} + +static std::string toStdString(const String8& string8) { + return std::string(string8.string()); +} + +static std::vector toKeyValueVector(const KeyedVector& keyedVector) { + std::vector stdKeyedVector; + for (size_t i = 0; i < keyedVector.size(); i++) { + KeyValue keyValue; + keyValue.key = toStdString(keyedVector.keyAt(i)); + keyValue.value = toStdString(keyedVector.valueAt(i)); + stdKeyedVector.push_back(keyValue); + } + return stdKeyedVector; +} + +static KeyedVector toKeyedVector(const std::vector& keyValueVec) { + KeyedVector keyedVector; + for (size_t i = 0; i < keyValueVec.size(); i++) { + keyedVector.add(toString8(keyValueVec[i].key), toString8(keyValueVec[i].value)); + } + return keyedVector; +} + +static DrmPlugin::KeyRequestType toKeyRequestType(KeyRequestType keyRequestType) { + switch (keyRequestType) { + case KeyRequestType::INITIAL: + return DrmPlugin::kKeyRequestType_Initial; + break; + case KeyRequestType::RENEWAL: + return DrmPlugin::kKeyRequestType_Renewal; + break; + case KeyRequestType::RELEASE: + return DrmPlugin::kKeyRequestType_Release; + break; + case KeyRequestType::NONE: + return DrmPlugin::kKeyRequestType_None; + break; + case KeyRequestType::UPDATE: + return DrmPlugin::kKeyRequestType_Update; + break; + default: + return DrmPlugin::kKeyRequestType_Unknown; + break; + } +} + +static List> toSecureStops(const std::vector& aSecureStops) { + List> secureStops; + for (size_t i = 0; i < aSecureStops.size(); i++) { + secureStops.push_back(toVector(aSecureStops[i].opaqueData)); + } + return secureStops; +} + +static List> toSecureStopIds(const std::vector& aSecureStopIds) { + List> secureStopIds; + for (size_t i = 0; i < aSecureStopIds.size(); i++) { + secureStopIds.push_back(toVector(aSecureStopIds[i].secureStopId)); + } + return secureStopIds; +} + +static DrmPlugin::HdcpLevel toHdcpLevel(HdcpLevel level) { + switch (level) { + case HdcpLevel::HDCP_NONE: + return DrmPlugin::kHdcpNone; + case HdcpLevel::HDCP_V1: + return DrmPlugin::kHdcpV1; + case HdcpLevel::HDCP_V2: + return DrmPlugin::kHdcpV2; + case HdcpLevel::HDCP_V2_1: + return DrmPlugin::kHdcpV2_1; + case HdcpLevel::HDCP_V2_2: + return DrmPlugin::kHdcpV2_2; + case HdcpLevel::HDCP_V2_3: + return DrmPlugin::kHdcpV2_3; + case HdcpLevel::HDCP_NO_OUTPUT: + return DrmPlugin::kHdcpNoOutput; + default: + return DrmPlugin::kHdcpLevelUnknown; + } +} + +static DrmPlugin::SecurityLevel toSecurityLevel(SecurityLevel level) { + switch (level) { + case SecurityLevel::SW_SECURE_CRYPTO: + return DrmPlugin::kSecurityLevelSwSecureCrypto; + case SecurityLevel::SW_SECURE_DECODE: + return DrmPlugin::kSecurityLevelSwSecureDecode; + case SecurityLevel::HW_SECURE_CRYPTO: + return DrmPlugin::kSecurityLevelHwSecureCrypto; + case SecurityLevel::HW_SECURE_DECODE: + return DrmPlugin::kSecurityLevelHwSecureDecode; + case SecurityLevel::HW_SECURE_ALL: + return DrmPlugin::kSecurityLevelHwSecureAll; + case SecurityLevel::DEFAULT: + return DrmPlugin::kSecurityLevelMax; + default: + return DrmPlugin::kSecurityLevelUnknown; + } +} + +static SecurityLevel toAidlSecurityLevel(DrmPlugin::SecurityLevel level) { + switch (level) { + case DrmPlugin::kSecurityLevelSwSecureCrypto: + return SecurityLevel::SW_SECURE_CRYPTO; + case DrmPlugin::kSecurityLevelSwSecureDecode: + return SecurityLevel::SW_SECURE_DECODE; + case DrmPlugin::kSecurityLevelHwSecureCrypto: + return SecurityLevel::HW_SECURE_CRYPTO; + case DrmPlugin::kSecurityLevelHwSecureDecode: + return SecurityLevel::HW_SECURE_DECODE; + case DrmPlugin::kSecurityLevelHwSecureAll: + return SecurityLevel::HW_SECURE_ALL; + case DrmPlugin::kSecurityLevelMax: + return SecurityLevel::DEFAULT; + default: + return SecurityLevel::UNKNOWN; + } +} + +static List> toKeySetIds(const std::vector& hKeySetIds) { + List> keySetIds; + for (size_t i = 0; i < hKeySetIds.size(); i++) { + keySetIds.push_back(toVector(hKeySetIds[i].keySetId)); + } + return keySetIds; +} + +static hidl_vec toHidlVec(const Vector& vector) { + hidl_vec vec; + vec.setToExternal(const_cast(vector.array()), vector.size()); + return vec; +} + +static DrmPlugin::OfflineLicenseState toOfflineLicenseState(OfflineLicenseState licenseState) { + switch (licenseState) { + case OfflineLicenseState::USABLE: + return DrmPlugin::kOfflineLicenseStateUsable; + case OfflineLicenseState::INACTIVE: + return DrmPlugin::kOfflineLicenseStateReleased; + default: + return DrmPlugin::kOfflineLicenseStateUnknown; + } +} + +Mutex DrmHalAidl::mLock; + +static hidl_vec toDrmMetricGroupHidl(std::vector result) { + std::vector resultHidl; + for (auto r : result) { + DrmMetricGroupHidl re; + std::vector tmpMetric; + for (auto m : r.metrics) { + DrmMetricHidl me; + me.name = m.name; + std::vector aTmp; + for (auto attr : m.attributes) { + AttributeHidl attrHidl; + attrHidl.name = attr.name; + + switch (attr.value.getTag()) { + case DrmMetricValue::Tag::int64Value: + attrHidl.type = DrmMetricGroupHidl::ValueType::INT64_TYPE; + attrHidl.int64Value = attr.value.get(); + break; + case DrmMetricValue::Tag::doubleValue: + attrHidl.type = DrmMetricGroupHidl::ValueType::DOUBLE_TYPE; + attrHidl.doubleValue = attr.value.get(); + break; + case DrmMetricValue::Tag::stringValue: + attrHidl.type = DrmMetricGroupHidl::ValueType::STRING_TYPE; + attrHidl.stringValue = attr.value.get(); + break; + default: + break; + } + + aTmp.push_back(attrHidl); + } + + me.attributes = aTmp; + + std::vector vTmp; + for (auto value : m.values) { + ValueHidl valueHidl; + valueHidl.componentName = value.name; + switch (value.value.getTag()) { + case DrmMetricValue::Tag::int64Value: + valueHidl.type = DrmMetricGroupHidl::ValueType::INT64_TYPE; + valueHidl.int64Value = value.value.get(); + break; + case DrmMetricValue::Tag::doubleValue: + valueHidl.type = DrmMetricGroupHidl::ValueType::DOUBLE_TYPE; + valueHidl.doubleValue = value.value.get(); + break; + case DrmMetricValue::Tag::stringValue: + valueHidl.type = DrmMetricGroupHidl::ValueType::STRING_TYPE; + valueHidl.stringValue = value.value.get(); + break; + default: + break; + } + + vTmp.push_back(valueHidl); + } + + me.values = vTmp; + tmpMetric.push_back(me); + } + + re.metrics = tmpMetric; + resultHidl.push_back(re); + } + + return resultHidl; +} + +// DrmSessionClient Definition + +struct DrmHalAidl::DrmSessionClient : public aidl::android::media::BnResourceManagerClient { + explicit DrmSessionClient(DrmHalAidl* drm, const Vector& sessionId) + : mSessionId(sessionId), mDrm(drm) {} + + ::ndk::ScopedAStatus reclaimResource(bool* _aidl_return) override; + ::ndk::ScopedAStatus getName(::std::string* _aidl_return) override; + + const Vector mSessionId; + + virtual ~DrmSessionClient(); + + private: + wp mDrm; + + DISALLOW_EVIL_CONSTRUCTORS(DrmSessionClient); +}; + +::ndk::ScopedAStatus DrmHalAidl::DrmSessionClient::reclaimResource(bool* _aidl_return) { + auto sessionId = mSessionId; + sp drm = mDrm.promote(); + if (drm == NULL) { + *_aidl_return = true; + return ::ndk::ScopedAStatus::ok(); + } + status_t err = drm->closeSession(sessionId); + if (err != OK) { + *_aidl_return = false; + return ::ndk::ScopedAStatus::ok(); + } + drm->onEvent(EventTypeAidl::SESSION_RECLAIMED, toHidlVec(sessionId), hidl_vec()); + *_aidl_return = true; + return ::ndk::ScopedAStatus::ok(); +} + +::ndk::ScopedAStatus DrmHalAidl::DrmSessionClient::getName(::std::string* _aidl_return) { + String8 name; + sp drm = mDrm.promote(); + if (drm == NULL) { + name.append(""); + } else if (drm->getPropertyStringInternal(String8("vendor"), name) != OK || name.isEmpty()) { + name.append(""); + } + name.append("["); + for (size_t i = 0; i < mSessionId.size(); ++i) { + name.appendFormat("%02x", mSessionId[i]); + } + name.append("]"); + *_aidl_return = name; + return ::ndk::ScopedAStatus::ok(); +} + +DrmHalAidl::DrmSessionClient::~DrmSessionClient() { + DrmSessionManager::Instance()->removeSession(mSessionId); +} + +// DrmHalAidl methods +DrmHalAidl::DrmHalAidl() + : mListener(::ndk::SharedRefBase::make(&mMetrics)), + mFactories(DrmUtils::makeDrmFactoriesAidl()), + mInitCheck((mFactories.size() == 0) ? ERROR_UNSUPPORTED : NO_INIT) {} + +status_t DrmHalAidl::initCheck() const { + return mInitCheck; +} + +DrmHalAidl::~DrmHalAidl() {} + +status_t DrmHalAidl::setListener(const sp& listener) { + mListener->setListener(listener); + return NO_ERROR; +} + +status_t DrmHalAidl::isCryptoSchemeSupported(const uint8_t uuid[16], const String8& mimeType, + DrmPlugin::SecurityLevel level, bool* isSupported) { + Mutex::Autolock autoLock(mLock); + *isSupported = false; + Uuid uuidAidl = DrmUtils::toAidlUuid(uuid); + SecurityLevel levelAidl = toAidlSecurityLevel(level); + std::string mimeTypeStr = mimeType.string(); + + for (ssize_t i = mFactories.size() - 1; i >= 0; i--) { + CryptoSchemes schemes{}; + auto err = mFactories[i]->getSupportedCryptoSchemes(&schemes); + if (!err.isOk() || !std::count(schemes.uuids.begin(), schemes.uuids.end(), uuidAidl)) { + continue; + } + + ALOGV("supported schemes: %s; query: level %d mime %s", + schemes.toString().c_str(), levelAidl, mimeType.c_str()); + std::map contentTypes; + for (auto ct : schemes.mimeTypes) { + contentTypes[ct.mime] = ct; + } + + // handle default value cases + if (levelAidl == SecurityLevel::DEFAULT || levelAidl == SecurityLevel::UNKNOWN) { + if (mimeType == "") { + // isCryptoSchemeSupported(uuid) + *isSupported = true; + } else { + // isCryptoSchemeSupported(uuid, mimeType) + *isSupported = contentTypes.count(mimeTypeStr); + } + return OK; + } else if (mimeType == "") { + return BAD_VALUE; + } + + auto ct = contentTypes[mimeTypeStr]; + if (levelAidl > ct.maxLevel || levelAidl < ct.minLevel) { + continue; + } + + *isSupported = true; + break; + } + + return OK; +} + +status_t DrmHalAidl::createPlugin(const uint8_t uuid[16], const String8& appPackageName) { + Mutex::Autolock autoLock(mLock); + + Uuid uuidAidl = DrmUtils::toAidlUuid(uuid); + std::string appPackageNameAidl = toStdString(appPackageName); + std::shared_ptr pluginAidl; + mMetrics.SetAppPackageName(appPackageName); + mMetrics.SetAppUid(AIBinder_getCallingUid()); + for (ssize_t i = mFactories.size() - 1; i >= 0; i--) { + ::ndk::ScopedAStatus status = + mFactories[i]->createDrmPlugin(uuidAidl, appPackageNameAidl, &pluginAidl); + if (status.isOk()) { + if (pluginAidl != NULL) { + mPlugin = pluginAidl; + break; + } + } else { + DrmUtils::LOG2BE(uuid, "Failed to make drm plugin: %d", + status.getServiceSpecificError()); + } + } + + if (mPlugin == NULL) { + DrmUtils::LOG2BE(uuid, "No supported hal instance found"); + mInitCheck = ERROR_UNSUPPORTED; + } else { + mInitCheck = OK; + // Stored pointer mListener upcast to base BnDrmPluginListener + ::ndk::ScopedAStatus status = mPlugin + ->setListener(std::static_pointer_cast(mListener)); + if (!status.isOk()) { + mInitCheck = DEAD_OBJECT; + ALOGE("setListener failed: ex %d svc err %d", + status.getExceptionCode(), + status.getServiceSpecificError()); + } + + if (mInitCheck != OK) { + mPlugin.reset(); + } + } + + return mInitCheck; +} + +status_t DrmHalAidl::openSession(DrmPlugin::SecurityLevel level, Vector& sessionId) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + SecurityLevel aSecurityLevel = toAidlSecurityLevel(level); + + if (aSecurityLevel == SecurityLevel::UNKNOWN) { + return ERROR_DRM_CANNOT_HANDLE; + } + + status_t err = UNKNOWN_ERROR; + bool retry = true; + do { + std::vector aSessionId; + + ::ndk::ScopedAStatus status = mPlugin->openSession(aSecurityLevel, &aSessionId); + if (status.isOk()) sessionId = toVector(aSessionId); + err = statusAidlToStatusT(status); + + if (err == ERROR_DRM_RESOURCE_BUSY && retry) { + mLock.unlock(); + // reclaimSession may call back to closeSession, since mLock is + // shared between Drm instances, we should unlock here to avoid + // deadlock. + retry = DrmSessionManager::Instance()->reclaimSession(AIBinder_getCallingPid()); + mLock.lock(); + } else { + retry = false; + } + } while (retry); + + if (err == OK) { + std::shared_ptr client = + ndk::SharedRefBase::make(this, sessionId); + DrmSessionManager::Instance()->addSession( + AIBinder_getCallingPid(), std::static_pointer_cast(client), + sessionId); + mOpenSessions.push_back(client); + mMetrics.SetSessionStart(sessionId); + } + + mMetrics.mOpenSessionCounter.Increment(err); + return err; +} + +status_t DrmHalAidl::closeSession(Vector const& sessionId) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + std::vector sessionIdAidl = toStdVec(sessionId); + ::ndk::ScopedAStatus status = mPlugin->closeSession(sessionIdAidl); + status_t response = statusAidlToStatusT(status); + if (status.isOk()) { + DrmSessionManager::Instance()->removeSession(sessionId); + for (auto i = mOpenSessions.begin(); i != mOpenSessions.end(); i++) { + if (isEqualSessionId((*i)->mSessionId, sessionId)) { + mOpenSessions.erase(i); + break; + } + } + + mMetrics.SetSessionEnd(sessionId); + } + + mMetrics.mCloseSessionCounter.Increment(response); + return response; +} + +status_t DrmHalAidl::getKeyRequest(Vector const& sessionId, + Vector const& initData, String8 const& mimeType, + DrmPlugin::KeyType keyType, + KeyedVector const& optionalParameters, + Vector& request, String8& defaultUrl, + DrmPlugin::KeyRequestType* keyRequestType) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + EventTimer keyRequestTimer(&mMetrics.mGetKeyRequestTimeUs); + + DrmSessionManager::Instance()->useSession(sessionId); + + KeyType aKeyType; + if (keyType == DrmPlugin::kKeyType_Streaming) { + aKeyType = KeyType::STREAMING; + } else if (keyType == DrmPlugin::kKeyType_Offline) { + aKeyType = KeyType::OFFLINE; + } else if (keyType == DrmPlugin::kKeyType_Release) { + aKeyType = KeyType::RELEASE; + } else { + keyRequestTimer.SetAttribute(BAD_VALUE); + return BAD_VALUE; + } + + status_t err = UNKNOWN_ERROR; + + std::vector sessionIdAidl = toStdVec(sessionId); + std::vector initDataAidl = toStdVec(initData); + KeyRequest keyRequest; + + ::ndk::ScopedAStatus status = + mPlugin->getKeyRequest(sessionIdAidl, initDataAidl, toStdString(mimeType), aKeyType, + toKeyValueVector(optionalParameters), &keyRequest); + if (status.isOk()) { + request = toVector(keyRequest.request); + defaultUrl = toString8(keyRequest.defaultUrl); + *keyRequestType = toKeyRequestType(keyRequest.requestType); + } + + err = statusAidlToStatusT(status); + keyRequestTimer.SetAttribute(err); + return err; +} + +status_t DrmHalAidl::provideKeyResponse(Vector const& sessionId, + Vector const& response, + Vector& keySetId) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + EventTimer keyResponseTimer(&mMetrics.mProvideKeyResponseTimeUs); + + DrmSessionManager::Instance()->useSession(sessionId); + + status_t err = UNKNOWN_ERROR; + + std::vector sessionIdAidl = toStdVec(sessionId); + std::vector responseAidl = toStdVec(response); + KeySetId keySetIdsAidl; + ::ndk::ScopedAStatus status = + mPlugin->provideKeyResponse(sessionIdAidl, responseAidl, &keySetIdsAidl); + + if (status.isOk()) keySetId = toVector(keySetIdsAidl.keySetId); + err = statusAidlToStatusT(status); + keyResponseTimer.SetAttribute(err); + return err; +} + +status_t DrmHalAidl::removeKeys(Vector const& keySetId) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + ::ndk::ScopedAStatus status = mPlugin->removeKeys(toStdVec(keySetId)); + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::restoreKeys(Vector const& sessionId, + Vector const& keySetId) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + DrmSessionManager::Instance()->useSession(sessionId); + + KeySetId keySetIdsAidl; + keySetIdsAidl.keySetId = toStdVec(keySetId); + ::ndk::ScopedAStatus status = mPlugin->restoreKeys(toStdVec(sessionId), keySetIdsAidl); + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::queryKeyStatus(Vector const& sessionId, + KeyedVector& infoMap) const { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + DrmSessionManager::Instance()->useSession(sessionId); + + std::vector infoMapAidl; + ::ndk::ScopedAStatus status = mPlugin->queryKeyStatus(toStdVec(sessionId), &infoMapAidl); + + infoMap = toKeyedVector(infoMapAidl); + + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::getProvisionRequest(String8 const& certType, String8 const& certAuthority, + Vector& request, String8& defaultUrl) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + status_t err = UNKNOWN_ERROR; + + ProvisionRequest requestAidl; + ::ndk::ScopedAStatus status = mPlugin->getProvisionRequest( + toStdString(certType), toStdString(certAuthority), &requestAidl); + + request = toVector(requestAidl.request); + defaultUrl = toString8(requestAidl.defaultUrl); + + err = statusAidlToStatusT(status); + mMetrics.mGetProvisionRequestCounter.Increment(err); + return err; +} + +status_t DrmHalAidl::provideProvisionResponse(Vector const& response, + Vector& certificate, + Vector& wrappedKey) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + status_t err = UNKNOWN_ERROR; + ProvideProvisionResponseResult result; + ::ndk::ScopedAStatus status = mPlugin->provideProvisionResponse(toStdVec(response), &result); + + certificate = toVector(result.certificate); + wrappedKey = toVector(result.wrappedKey); + err = statusAidlToStatusT(status); + mMetrics.mProvideProvisionResponseCounter.Increment(err); + return err; +} + +status_t DrmHalAidl::getSecureStops(List>& secureStops) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + std::vector result; + ::ndk::ScopedAStatus status = mPlugin->getSecureStops(&result); + + secureStops = toSecureStops(result); + + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::getSecureStopIds(List>& secureStopIds) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + std::vector result; + ::ndk::ScopedAStatus status = mPlugin->getSecureStopIds(&result); + + secureStopIds = toSecureStopIds(result); + + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::getSecureStop(Vector const& ssid, Vector& secureStop) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + SecureStopId ssidAidl; + ssidAidl.secureStopId = toStdVec(ssid); + + SecureStop result; + ::ndk::ScopedAStatus status = mPlugin->getSecureStop(ssidAidl, &result); + + secureStop = toVector(result.opaqueData); + + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::releaseSecureStops(Vector const& ssRelease) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + OpaqueData ssId; + ssId.opaqueData = toStdVec(ssRelease); + ::ndk::ScopedAStatus status = mPlugin->releaseSecureStops(ssId); + + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::removeSecureStop(Vector const& ssid) { + Mutex::Autolock autoLock(mLock); + + INIT_CHECK(); + + SecureStopId ssidAidl; + ssidAidl.secureStopId = toStdVec(ssid); + ::ndk::ScopedAStatus status = mPlugin->removeSecureStop(ssidAidl); + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::removeAllSecureStops() { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + ::ndk::ScopedAStatus status = mPlugin->releaseAllSecureStops(); + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::getHdcpLevels(DrmPlugin::HdcpLevel* connected, + DrmPlugin::HdcpLevel* max) const { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + if (connected == NULL || max == NULL) { + return BAD_VALUE; + } + + *connected = DrmPlugin::kHdcpLevelUnknown; + *max = DrmPlugin::kHdcpLevelUnknown; + + HdcpLevels lvlsAidl; + ::ndk::ScopedAStatus status = mPlugin->getHdcpLevels(&lvlsAidl); + + *connected = toHdcpLevel(lvlsAidl.connectedLevel); + *max = toHdcpLevel(lvlsAidl.maxLevel); + + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::getNumberOfSessions(uint32_t* open, uint32_t* max) const { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + if (open == NULL || max == NULL) { + return BAD_VALUE; + } + + *open = 0; + *max = 0; + + NumberOfSessions result; + ::ndk::ScopedAStatus status = mPlugin->getNumberOfSessions(&result); + + *open = result.currentSessions; + *max = result.maxSessions; + + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::getSecurityLevel(Vector const& sessionId, + DrmPlugin::SecurityLevel* level) const { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + if (level == NULL) { + return BAD_VALUE; + } + + *level = DrmPlugin::kSecurityLevelUnknown; + + SecurityLevel result; + ::ndk::ScopedAStatus status = mPlugin->getSecurityLevel(toStdVec(sessionId), &result); + + *level = toSecurityLevel(result); + + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::getOfflineLicenseKeySetIds(List>& keySetIds) const { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + std::vector result; + ::ndk::ScopedAStatus status = mPlugin->getOfflineLicenseKeySetIds(&result); + + keySetIds = toKeySetIds(result); + + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::removeOfflineLicense(Vector const& keySetId) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + KeySetId keySetIdAidl; + keySetIdAidl.keySetId = toStdVec(keySetId); + ::ndk::ScopedAStatus status = mPlugin->removeOfflineLicense(keySetIdAidl); + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::getOfflineLicenseState(Vector const& keySetId, + DrmPlugin::OfflineLicenseState* licenseState) const { + Mutex::Autolock autoLock(mLock); + + INIT_CHECK(); + *licenseState = DrmPlugin::kOfflineLicenseStateUnknown; + + KeySetId keySetIdAidl; + keySetIdAidl.keySetId = toStdVec(keySetId); + + OfflineLicenseState result; + ::ndk::ScopedAStatus status = mPlugin->getOfflineLicenseState(keySetIdAidl, &result); + + *licenseState = toOfflineLicenseState(result); + + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::getPropertyString(String8 const& name, String8& value) const { + Mutex::Autolock autoLock(mLock); + return getPropertyStringInternal(name, value); +} + +status_t DrmHalAidl::getPropertyStringInternal(String8 const& name, String8& value) const { + // This function is internal to the class and should only be called while + // mLock is already held. + INIT_CHECK(); + + std::string result; + ::ndk::ScopedAStatus status = mPlugin->getPropertyString(toStdString(name), &result); + + value = toString8(result); + + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::getPropertyByteArray(String8 const& name, Vector& value) const { + Mutex::Autolock autoLock(mLock); + return getPropertyByteArrayInternal(name, value); +} + +status_t DrmHalAidl::getPropertyByteArrayInternal(String8 const& name, + Vector& value) const { + // This function is internal to the class and should only be called while + // mLock is already held. + INIT_CHECK(); + + status_t err = UNKNOWN_ERROR; + + std::vector result; + ::ndk::ScopedAStatus status = mPlugin->getPropertyByteArray(toStdString(name), &result); + + value = toVector(result); + err = statusAidlToStatusT(status); + if (name == kPropertyDeviceUniqueId) { + mMetrics.mGetDeviceUniqueIdCounter.Increment(err); + } + return err; +} + +status_t DrmHalAidl::setPropertyString(String8 const& name, String8 const& value) const { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + ::ndk::ScopedAStatus status = mPlugin->setPropertyString(toStdString(name), toStdString(value)); + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::setPropertyByteArray(String8 const& name, Vector const& value) const { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + ::ndk::ScopedAStatus status = mPlugin->setPropertyByteArray(toStdString(name), toStdVec(value)); + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::getMetrics(const sp& consumer) { + if (consumer == nullptr) { + return UNEXPECTED_NULL; + } + consumer->consumeFrameworkMetrics(mMetrics); + + // Append vendor metrics if they are supported. + + String8 vendor; + String8 description; + if (getPropertyStringInternal(String8("vendor"), vendor) != OK || vendor.isEmpty()) { + ALOGE("Get vendor failed or is empty"); + vendor = "NONE"; + } + if (getPropertyStringInternal(String8("description"), description) != OK || + description.isEmpty()) { + ALOGE("Get description failed or is empty."); + description = "NONE"; + } + vendor += "."; + vendor += description; + + hidl_vec pluginMetrics; + status_t err = UNKNOWN_ERROR; + + std::vector result; + ::ndk::ScopedAStatus status = mPlugin->getMetrics(&result); + + if (status.isOk()) { + pluginMetrics = toDrmMetricGroupHidl(result); + consumer->consumeHidlMetrics(vendor, pluginMetrics); + } + + err = statusAidlToStatusT(status); + + return err; +} + +status_t DrmHalAidl::setCipherAlgorithm(Vector const& sessionId, + String8 const& algorithm) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + DrmSessionManager::Instance()->useSession(sessionId); + + ::ndk::ScopedAStatus status = + mPlugin->setCipherAlgorithm(toStdVec(sessionId), toStdString(algorithm)); + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::setMacAlgorithm(Vector const& sessionId, String8 const& algorithm) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + DrmSessionManager::Instance()->useSession(sessionId); + + ::ndk::ScopedAStatus status = + mPlugin->setMacAlgorithm(toStdVec(sessionId), toStdString(algorithm)); + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::encrypt(Vector const& sessionId, Vector const& keyId, + Vector const& input, Vector const& iv, + Vector& output) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + DrmSessionManager::Instance()->useSession(sessionId); + + std::vector result; + ::ndk::ScopedAStatus status = mPlugin->encrypt(toStdVec(sessionId), toStdVec(keyId), + toStdVec(input), toStdVec(iv), &result); + + output = toVector(result); + + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::decrypt(Vector const& sessionId, Vector const& keyId, + Vector const& input, Vector const& iv, + Vector& output) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + DrmSessionManager::Instance()->useSession(sessionId); + + std::vector result; + ::ndk::ScopedAStatus status = mPlugin->decrypt(toStdVec(sessionId), toStdVec(keyId), + toStdVec(input), toStdVec(iv), &result); + + output = toVector(result); + + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::sign(Vector const& sessionId, Vector const& keyId, + Vector const& message, Vector& signature) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + DrmSessionManager::Instance()->useSession(sessionId); + + std::vector result; + ::ndk::ScopedAStatus status = + mPlugin->sign(toStdVec(sessionId), toStdVec(keyId), toStdVec(message), &result); + + signature = toVector(result); + + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::verify(Vector const& sessionId, Vector const& keyId, + Vector const& message, Vector const& signature, + bool& match) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + DrmSessionManager::Instance()->useSession(sessionId); + + ::ndk::ScopedAStatus status = mPlugin->verify(toStdVec(sessionId), toStdVec(keyId), + toStdVec(message), toStdVec(signature), &match); + + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::signRSA(Vector const& sessionId, String8 const& algorithm, + Vector const& message, Vector const& wrappedKey, + Vector& signature) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + DrmSessionManager::Instance()->useSession(sessionId); + + std::vector result; + ::ndk::ScopedAStatus status = + mPlugin->signRSA(toStdVec(sessionId), toStdString(algorithm), toStdVec(message), + toStdVec(wrappedKey), &result); + + signature = toVector(result); + + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::requiresSecureDecoder(const char* mime, bool* required) const { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + std::string mimeAidl(mime); + ::ndk::ScopedAStatus status = + mPlugin->requiresSecureDecoder(mimeAidl, SecurityLevel::DEFAULT, required); + if (!status.isOk()) { + DrmUtils::LOG2BE("requiresSecureDecoder txn failed: %d", status.getServiceSpecificError()); + return DEAD_OBJECT; + } + + return OK; +} + +status_t DrmHalAidl::requiresSecureDecoder(const char* mime, DrmPlugin::SecurityLevel securityLevel, + bool* required) const { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + auto aLevel = toAidlSecurityLevel(securityLevel); + std::string mimeAidl(mime); + ::ndk::ScopedAStatus status = mPlugin->requiresSecureDecoder(mimeAidl, aLevel, required); + + status_t err = statusAidlToStatusT(status); + if (!status.isOk()) { + DrmUtils::LOG2BE("requiresSecureDecoder txn failed: %d", status.getServiceSpecificError()); + } + + return err; +} + +status_t DrmHalAidl::setPlaybackId(Vector const& sessionId, const char* playbackId) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + std::string playbackIdAidl(playbackId); + ::ndk::ScopedAStatus status = mPlugin->setPlaybackId(toStdVec(sessionId), playbackIdAidl); + return statusAidlToStatusT(status); +} + +status_t DrmHalAidl::getLogMessages(Vector& logs) const { + Mutex::Autolock autoLock(mLock); + return DrmUtils::GetLogMessagesAidl(mPlugin, logs); +} + +void DrmHalAidl::closeOpenSessions() { + Mutex::Autolock autoLock(mLock); + auto openSessions = mOpenSessions; + for (size_t i = 0; i < openSessions.size(); i++) { + mLock.unlock(); + closeSession(openSessions[i]->mSessionId); + mLock.lock(); + } + mOpenSessions.clear(); +} + +std::string DrmHalAidl::reportPluginMetrics() const { + Vector metricsVector; + String8 vendor; + String8 description; + std::string metricsString; + if (getPropertyStringInternal(String8("vendor"), vendor) == OK && + getPropertyStringInternal(String8("description"), description) == OK && + getPropertyByteArrayInternal(String8("metrics"), metricsVector) == OK) { + metricsString = toBase64StringNoPad(metricsVector.array(), metricsVector.size()); + status_t res = android::reportDrmPluginMetrics(metricsString, vendor, description, + mMetrics.GetAppUid()); + if (res != OK) { + ALOGE("Metrics were retrieved but could not be reported: %d", res); + } + } + return metricsString; +} + +std::string DrmHalAidl::reportFrameworkMetrics(const std::string& pluginMetrics) const { + mediametrics_handle_t item(mediametrics_create("mediadrm")); + mediametrics_setUid(item, mMetrics.GetAppUid()); + String8 vendor; + String8 description; + status_t result = getPropertyStringInternal(String8("vendor"), vendor); + if (result != OK) { + ALOGE("Failed to get vendor from drm plugin: %d", result); + } else { + mediametrics_setCString(item, "vendor", vendor.c_str()); + } + result = getPropertyStringInternal(String8("description"), description); + if (result != OK) { + ALOGE("Failed to get description from drm plugin: %d", result); + } else { + mediametrics_setCString(item, "description", description.c_str()); + } + + std::string serializedMetrics; + result = mMetrics.GetSerializedMetrics(&serializedMetrics); + if (result != OK) { + ALOGE("Failed to serialize framework metrics: %d", result); + } + std::string b64EncodedMetrics = + toBase64StringNoPad(serializedMetrics.data(), serializedMetrics.size()); + if (!b64EncodedMetrics.empty()) { + mediametrics_setCString(item, "serialized_metrics", b64EncodedMetrics.c_str()); + } + if (!pluginMetrics.empty()) { + mediametrics_setCString(item, "plugin_metrics", pluginMetrics.c_str()); + } + if (!mediametrics_selfRecord(item)) { + ALOGE("Failed to self record framework metrics"); + } + mediametrics_delete(item); + return serializedMetrics; +} + +status_t DrmHalAidl::getSupportedSchemes(std::vector &schemes) const { + Mutex::Autolock autoLock(mLock); + + if (mFactories.empty()) return UNKNOWN_ERROR; + for (ssize_t i = mFactories.size() - 1; i >= 0; i--) { + CryptoSchemes curSchemes{}; + auto err = mFactories[i]->getSupportedCryptoSchemes(&curSchemes); + if (!err.isOk()) { + continue; + } + + for (auto uuidObj : curSchemes.uuids) { + schemes.insert(schemes.end(), uuidObj.uuid.begin(), uuidObj.uuid.end()); + } + } + + return OK; +} + +void DrmHalAidl::cleanup() { + closeOpenSessions(); + + Mutex::Autolock autoLock(mLock); + reportFrameworkMetrics(reportPluginMetrics()); + + setListener(NULL); + mInitCheck = NO_INIT; + if (mPlugin != NULL) { + if (!mPlugin->setListener(NULL).isOk()) { + mInitCheck = DEAD_OBJECT; + } + } + + mPlugin.reset(); +} + +status_t DrmHalAidl::destroyPlugin() { + cleanup(); + return OK; +} + +::ndk::ScopedAStatus DrmHalAidl::onEvent(EventTypeAidl eventTypeAidl, + const std::vector& sessionId, + const std::vector& data) { + return mListener->onEvent(eventTypeAidl, sessionId, data); +} + +::ndk::ScopedAStatus DrmHalAidl::onExpirationUpdate(const std::vector& sessionId, + int64_t expiryTimeInMS) { + return mListener->onExpirationUpdate(sessionId, expiryTimeInMS); +} + +::ndk::ScopedAStatus DrmHalAidl::onKeysChange(const std::vector& sessionId, + const std::vector& keyStatusListAidl, + bool hasNewUsableKey) { + return mListener->onKeysChange(sessionId, keyStatusListAidl, hasNewUsableKey); +} + +::ndk::ScopedAStatus DrmHalAidl::onSessionLostState(const std::vector& sessionId) { + return mListener->onSessionLostState(sessionId); +} + +} // namespace android \ No newline at end of file diff --git a/drm/libmediadrm/DrmHalHidl.cpp b/drm/libmediadrm/DrmHalHidl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c38dbef7cb21e4792a20890d73dc5fb7cffe33b6 --- /dev/null +++ b/drm/libmediadrm/DrmHalHidl.cpp @@ -0,0 +1,1537 @@ +/* + * Copyright (C) 2021 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 "DrmHalHidl" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using ::android::sp; +using ::android::DrmUtils::toStatusT; +using ::android::hardware::hidl_array; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::drm::V1_1::DrmMetricGroup; +using ::android::os::PersistableBundle; +using drm::V1_0::KeyedVector; +using drm::V1_0::KeyRequestType; +using drm::V1_0::KeyType; +using drm::V1_0::KeyValue; +using drm::V1_0::SecureStop; +using drm::V1_0::SecureStopId; +using drm::V1_0::Status; +using drm::V1_1::HdcpLevel; +using drm::V1_1::SecureStopRelease; +using drm::V1_1::SecurityLevel; +using drm::V1_2::KeySetId; +using drm::V1_2::KeyStatusType; + +typedef drm::V1_1::KeyRequestType KeyRequestType_V1_1; +typedef drm::V1_2::Status Status_V1_2; +typedef drm::V1_2::HdcpLevel HdcpLevel_V1_2; + +namespace { + +// This constant corresponds to the PROPERTY_DEVICE_UNIQUE_ID constant +// in the MediaDrm API. +constexpr char kPropertyDeviceUniqueId[] = "deviceUniqueId"; +constexpr char kEqualsSign[] = "="; + +template +std::string toBase64StringNoPad(const T* data, size_t size) { + // Note that the base 64 conversion only works with arrays of single-byte + // values. If the source is empty or is not an array of single-byte values, + // return empty string. + if (size == 0 || sizeof(data[0]) != 1) { + return ""; + } + + android::AString outputString; + encodeBase64(data, size, &outputString); + // Remove trailing equals padding if it exists. + while (outputString.size() > 0 && outputString.endsWith(kEqualsSign)) { + outputString.erase(outputString.size() - 1, 1); + } + + return std::string(outputString.c_str(), outputString.size()); +} + +} // anonymous namespace + +namespace android { + +#define INIT_CHECK() \ + { \ + if (mInitCheck != OK) return mInitCheck; \ + } + +static const Vector toVector(const hidl_vec& vec) { + Vector vector; + vector.appendArray(vec.data(), vec.size()); + return *const_cast*>(&vector); +} + +static hidl_vec toHidlVec(const Vector& vector) { + hidl_vec vec; + vec.setToExternal(const_cast(vector.array()), vector.size()); + return vec; +} + +static String8 toString8(const hidl_string& string) { + return String8(string.c_str()); +} + +static hidl_string toHidlString(const String8& string) { + return hidl_string(string.string()); +} + +static DrmPlugin::SecurityLevel toSecurityLevel(SecurityLevel level) { + switch (level) { + case SecurityLevel::SW_SECURE_CRYPTO: + return DrmPlugin::kSecurityLevelSwSecureCrypto; + case SecurityLevel::SW_SECURE_DECODE: + return DrmPlugin::kSecurityLevelSwSecureDecode; + case SecurityLevel::HW_SECURE_CRYPTO: + return DrmPlugin::kSecurityLevelHwSecureCrypto; + case SecurityLevel::HW_SECURE_DECODE: + return DrmPlugin::kSecurityLevelHwSecureDecode; + case SecurityLevel::HW_SECURE_ALL: + return DrmPlugin::kSecurityLevelHwSecureAll; + default: + return DrmPlugin::kSecurityLevelUnknown; + } +} + +static SecurityLevel toHidlSecurityLevel(DrmPlugin::SecurityLevel level) { + switch (level) { + case DrmPlugin::kSecurityLevelSwSecureCrypto: + return SecurityLevel::SW_SECURE_CRYPTO; + case DrmPlugin::kSecurityLevelSwSecureDecode: + return SecurityLevel::SW_SECURE_DECODE; + case DrmPlugin::kSecurityLevelHwSecureCrypto: + return SecurityLevel::HW_SECURE_CRYPTO; + case DrmPlugin::kSecurityLevelHwSecureDecode: + return SecurityLevel::HW_SECURE_DECODE; + case DrmPlugin::kSecurityLevelHwSecureAll: + return SecurityLevel::HW_SECURE_ALL; + default: + return SecurityLevel::UNKNOWN; + } +} + +static DrmPlugin::OfflineLicenseState toOfflineLicenseState(OfflineLicenseState licenseState) { + switch (licenseState) { + case OfflineLicenseState::USABLE: + return DrmPlugin::kOfflineLicenseStateUsable; + case OfflineLicenseState::INACTIVE: + return DrmPlugin::kOfflineLicenseStateReleased; + default: + return DrmPlugin::kOfflineLicenseStateUnknown; + } +} + +static DrmPlugin::HdcpLevel toHdcpLevel(HdcpLevel_V1_2 level) { + switch (level) { + case HdcpLevel_V1_2::HDCP_NONE: + return DrmPlugin::kHdcpNone; + case HdcpLevel_V1_2::HDCP_V1: + return DrmPlugin::kHdcpV1; + case HdcpLevel_V1_2::HDCP_V2: + return DrmPlugin::kHdcpV2; + case HdcpLevel_V1_2::HDCP_V2_1: + return DrmPlugin::kHdcpV2_1; + case HdcpLevel_V1_2::HDCP_V2_2: + return DrmPlugin::kHdcpV2_2; + case HdcpLevel_V1_2::HDCP_V2_3: + return DrmPlugin::kHdcpV2_3; + case HdcpLevel_V1_2::HDCP_NO_OUTPUT: + return DrmPlugin::kHdcpNoOutput; + default: + return DrmPlugin::kHdcpLevelUnknown; + } +} +static ::KeyedVector toHidlKeyedVector(const KeyedVector& keyedVector) { + std::vector stdKeyedVector; + for (size_t i = 0; i < keyedVector.size(); i++) { + KeyValue keyValue; + keyValue.key = toHidlString(keyedVector.keyAt(i)); + keyValue.value = toHidlString(keyedVector.valueAt(i)); + stdKeyedVector.push_back(keyValue); + } + return ::KeyedVector(stdKeyedVector); +} + +static KeyedVector toKeyedVector(const ::KeyedVector& hKeyedVector) { + KeyedVector keyedVector; + for (size_t i = 0; i < hKeyedVector.size(); i++) { + keyedVector.add(toString8(hKeyedVector[i].key), toString8(hKeyedVector[i].value)); + } + return keyedVector; +} + +static List> toSecureStops(const hidl_vec& hSecureStops) { + List> secureStops; + for (size_t i = 0; i < hSecureStops.size(); i++) { + secureStops.push_back(toVector(hSecureStops[i].opaqueData)); + } + return secureStops; +} + +static List> toSecureStopIds(const hidl_vec& hSecureStopIds) { + List> secureStopIds; + for (size_t i = 0; i < hSecureStopIds.size(); i++) { + secureStopIds.push_back(toVector(hSecureStopIds[i])); + } + return secureStopIds; +} + +static List> toKeySetIds(const hidl_vec& hKeySetIds) { + List> keySetIds; + for (size_t i = 0; i < hKeySetIds.size(); i++) { + keySetIds.push_back(toVector(hKeySetIds[i])); + } + return keySetIds; +} + +Mutex DrmHalHidl::mLock; + +struct DrmHalHidl::DrmSessionClient : public aidl::android::media::BnResourceManagerClient { + explicit DrmSessionClient(DrmHalHidl* drm, const Vector& sessionId) + : mSessionId(sessionId), mDrm(drm) {} + + ::ndk::ScopedAStatus reclaimResource(bool* _aidl_return) override; + ::ndk::ScopedAStatus getName(::std::string* _aidl_return) override; + + const Vector mSessionId; + + virtual ~DrmSessionClient(); + + private: + wp mDrm; + + DISALLOW_EVIL_CONSTRUCTORS(DrmSessionClient); +}; + +::ndk::ScopedAStatus DrmHalHidl::DrmSessionClient::reclaimResource(bool* _aidl_return) { + auto sessionId = mSessionId; + sp drm = mDrm.promote(); + if (drm == NULL) { + *_aidl_return = true; + return ::ndk::ScopedAStatus::ok(); + } + status_t err = drm->closeSession(sessionId); + if (err != OK) { + *_aidl_return = false; + return ::ndk::ScopedAStatus::ok(); + } + drm->sendEvent(EventType::SESSION_RECLAIMED, toHidlVec(sessionId), hidl_vec()); + *_aidl_return = true; + return ::ndk::ScopedAStatus::ok(); +} + +::ndk::ScopedAStatus DrmHalHidl::DrmSessionClient::getName(::std::string* _aidl_return) { + String8 name; + sp drm = mDrm.promote(); + if (drm == NULL) { + name.append(""); + } else if (drm->getPropertyStringInternal(String8("vendor"), name) != OK || name.isEmpty()) { + name.append(""); + } + name.append("["); + for (size_t i = 0; i < mSessionId.size(); ++i) { + name.appendFormat("%02x", mSessionId[i]); + } + name.append("]"); + *_aidl_return = name; + return ::ndk::ScopedAStatus::ok(); +} + +DrmHalHidl::DrmSessionClient::~DrmSessionClient() { + DrmSessionManager::Instance()->removeSession(mSessionId); +} + +DrmHalHidl::DrmHalHidl() + : mFactories(makeDrmFactories()), + mInitCheck((mFactories.size() == 0) ? ERROR_UNSUPPORTED : NO_INIT) {} + +void DrmHalHidl::closeOpenSessions() { + Mutex::Autolock autoLock(mLock); + auto openSessions = mOpenSessions; + for (size_t i = 0; i < openSessions.size(); i++) { + mLock.unlock(); + closeSession(openSessions[i]->mSessionId); + mLock.lock(); + } + mOpenSessions.clear(); +} + +DrmHalHidl::~DrmHalHidl() {} + +void DrmHalHidl::cleanup() { + closeOpenSessions(); + + Mutex::Autolock autoLock(mLock); + reportFrameworkMetrics(reportPluginMetrics()); + + setListener(NULL); + mInitCheck = NO_INIT; + if (mPluginV1_2 != NULL) { + if (!mPluginV1_2->setListener(NULL).isOk()) { + mInitCheck = DEAD_OBJECT; + } + } else if (mPlugin != NULL) { + if (!mPlugin->setListener(NULL).isOk()) { + mInitCheck = DEAD_OBJECT; + } + } + mPlugin.clear(); + mPluginV1_1.clear(); + mPluginV1_2.clear(); + mPluginV1_4.clear(); +} + +std::vector> DrmHalHidl::makeDrmFactories() { + static std::vector> factories(DrmUtils::MakeDrmFactories()); + if (factories.size() == 0) { + DrmUtils::LOG2BI("No hidl drm factories found"); + // could be in passthrough mode, load the default passthrough service + auto passthrough = IDrmFactory::getService(); + if (passthrough != NULL) { + DrmUtils::LOG2BI("makeDrmFactories: using default passthrough drm instance"); + factories.push_back(passthrough); + } else { + DrmUtils::LOG2BE("Failed to find passthrough drm factories"); + } + } + return factories; +} + +sp DrmHalHidl::makeDrmPlugin(const sp& factory, const uint8_t uuid[16], + const String8& appPackageName) { + mAppPackageName = appPackageName; + mMetrics.SetAppPackageName(appPackageName); + mMetrics.SetAppUid(AIBinder_getCallingUid()); + + sp plugin; + Return hResult = factory->createPlugin( + uuid, appPackageName.string(), [&](Status status, const sp& hPlugin) { + if (status != Status::OK) { + DrmUtils::LOG2BE(uuid, "Failed to make drm plugin: %d", status); + return; + } + plugin = hPlugin; + }); + + if (!hResult.isOk()) { + DrmUtils::LOG2BE(uuid, "createPlugin remote call failed: %s", + hResult.description().c_str()); + } + + return plugin; +} + +status_t DrmHalHidl::initCheck() const { + return mInitCheck; +} + +status_t DrmHalHidl::setListener(const sp& listener) { + Mutex::Autolock lock(mEventLock); + mListener = listener; + return NO_ERROR; +} + +Return DrmHalHidl::sendEvent(EventType hEventType, const hidl_vec& sessionId, + const hidl_vec& data) { + mMetrics.mEventCounter.Increment((uint32_t)hEventType); + + mEventLock.lock(); + sp listener = mListener; + mEventLock.unlock(); + + if (listener != NULL) { + Mutex::Autolock lock(mNotifyLock); + DrmPlugin::EventType eventType; + switch (hEventType) { + case EventType::PROVISION_REQUIRED: + eventType = DrmPlugin::kDrmPluginEventProvisionRequired; + break; + case EventType::KEY_NEEDED: + eventType = DrmPlugin::kDrmPluginEventKeyNeeded; + break; + case EventType::KEY_EXPIRED: + eventType = DrmPlugin::kDrmPluginEventKeyExpired; + break; + case EventType::VENDOR_DEFINED: + eventType = DrmPlugin::kDrmPluginEventVendorDefined; + break; + case EventType::SESSION_RECLAIMED: + eventType = DrmPlugin::kDrmPluginEventSessionReclaimed; + break; + default: + return Void(); + } + listener->sendEvent(eventType, sessionId, data); + } + return Void(); +} + +Return DrmHalHidl::sendExpirationUpdate(const hidl_vec& sessionId, + int64_t expiryTimeInMS) { + mEventLock.lock(); + sp listener = mListener; + mEventLock.unlock(); + + if (listener != NULL) { + Mutex::Autolock lock(mNotifyLock); + listener->sendExpirationUpdate(sessionId, expiryTimeInMS); + } + return Void(); +} + +Return DrmHalHidl::sendKeysChange(const hidl_vec& sessionId, + const hidl_vec& keyStatusList_V1_0, + bool hasNewUsableKey) { + std::vector keyStatusVec; + for (const auto& keyStatus_V1_0 : keyStatusList_V1_0) { + keyStatusVec.push_back( + {keyStatus_V1_0.keyId, static_cast(keyStatus_V1_0.type)}); + } + hidl_vec keyStatusList_V1_2(keyStatusVec); + return sendKeysChange_1_2(sessionId, keyStatusList_V1_2, hasNewUsableKey); +} + +Return DrmHalHidl::sendKeysChange_1_2(const hidl_vec& sessionId, + const hidl_vec& hKeyStatusList, + bool hasNewUsableKey) { + mEventLock.lock(); + sp listener = mListener; + mEventLock.unlock(); + + if (listener != NULL) { + std::vector keyStatusList; + size_t nKeys = hKeyStatusList.size(); + for (size_t i = 0; i < nKeys; ++i) { + const KeyStatus& keyStatus = hKeyStatusList[i]; + uint32_t type; + switch (keyStatus.type) { + case KeyStatusType::USABLE: + type = DrmPlugin::kKeyStatusType_Usable; + break; + case KeyStatusType::EXPIRED: + type = DrmPlugin::kKeyStatusType_Expired; + break; + case KeyStatusType::OUTPUTNOTALLOWED: + type = DrmPlugin::kKeyStatusType_OutputNotAllowed; + break; + case KeyStatusType::STATUSPENDING: + type = DrmPlugin::kKeyStatusType_StatusPending; + break; + case KeyStatusType::USABLEINFUTURE: + type = DrmPlugin::kKeyStatusType_UsableInFuture; + break; + case KeyStatusType::INTERNALERROR: + default: + type = DrmPlugin::kKeyStatusType_InternalError; + break; + } + keyStatusList.push_back({type, keyStatus.keyId}); + mMetrics.mKeyStatusChangeCounter.Increment((uint32_t)keyStatus.type); + } + + Mutex::Autolock lock(mNotifyLock); + listener->sendKeysChange(sessionId, keyStatusList, hasNewUsableKey); + } else { + // There's no listener. But we still want to count the key change + // events. + size_t nKeys = hKeyStatusList.size(); + for (size_t i = 0; i < nKeys; i++) { + mMetrics.mKeyStatusChangeCounter.Increment((uint32_t)hKeyStatusList[i].type); + } + } + + return Void(); +} + +Return DrmHalHidl::sendSessionLostState(const hidl_vec& sessionId) { + mEventLock.lock(); + sp listener = mListener; + mEventLock.unlock(); + + if (listener != NULL) { + Mutex::Autolock lock(mNotifyLock); + listener->sendSessionLostState(sessionId); + } + return Void(); +} + +status_t DrmHalHidl::matchMimeTypeAndSecurityLevel(const sp& factory, + const uint8_t uuid[16], const String8& mimeType, + DrmPlugin::SecurityLevel level, + bool* isSupported) { + *isSupported = false; + + // handle default value cases + if (level == DrmPlugin::kSecurityLevelUnknown) { + if (mimeType == "") { + // isCryptoSchemeSupported(uuid) + *isSupported = true; + } else { + // isCryptoSchemeSupported(uuid, mimeType) + *isSupported = factory->isContentTypeSupported(mimeType.string()); + } + return OK; + } else if (mimeType == "") { + return BAD_VALUE; + } + + sp factoryV1_2 = drm::V1_2::IDrmFactory::castFrom(factory); + if (factoryV1_2 == NULL) { + return ERROR_UNSUPPORTED; + } else { + *isSupported = factoryV1_2->isCryptoSchemeSupported_1_2(uuid, mimeType.string(), + toHidlSecurityLevel(level)); + return OK; + } +} + +status_t DrmHalHidl::isCryptoSchemeSupported(const uint8_t uuid[16], const String8& mimeType, + DrmPlugin::SecurityLevel level, bool* isSupported) { + Mutex::Autolock autoLock(mLock); + *isSupported = false; + for (ssize_t i = mFactories.size() - 1; i >= 0; i--) { + if (mFactories[i]->isCryptoSchemeSupported(uuid)) { + return matchMimeTypeAndSecurityLevel(mFactories[i], uuid, mimeType, level, isSupported); + } + } + return OK; +} + +status_t DrmHalHidl::createPlugin(const uint8_t uuid[16], const String8& appPackageName) { + Mutex::Autolock autoLock(mLock); + + for (ssize_t i = mFactories.size() - 1; i >= 0; i--) { + auto hResult = mFactories[i]->isCryptoSchemeSupported(uuid); + if (hResult.isOk() && hResult) { + auto plugin = makeDrmPlugin(mFactories[i], uuid, appPackageName); + if (plugin != NULL) { + mPlugin = plugin; + mPluginV1_1 = drm::V1_1::IDrmPlugin::castFrom(mPlugin); + mPluginV1_2 = drm::V1_2::IDrmPlugin::castFrom(mPlugin); + mPluginV1_4 = drm::V1_4::IDrmPlugin::castFrom(mPlugin); + break; + } + } + } + + if (mPlugin == NULL) { + DrmUtils::LOG2BE(uuid, "No supported hal instance found"); + mInitCheck = ERROR_UNSUPPORTED; + } else { + mInitCheck = OK; + if (mPluginV1_2 != NULL) { + if (!mPluginV1_2->setListener(this).isOk()) { + mInitCheck = DEAD_OBJECT; + } + } else if (!mPlugin->setListener(this).isOk()) { + mInitCheck = DEAD_OBJECT; + } + if (mInitCheck != OK) { + mPlugin.clear(); + mPluginV1_1.clear(); + mPluginV1_2.clear(); + mPluginV1_4.clear(); + } + } + + return mInitCheck; +} + +status_t DrmHalHidl::destroyPlugin() { + cleanup(); + return OK; +} + +status_t DrmHalHidl::openSession(DrmPlugin::SecurityLevel level, Vector& sessionId) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + SecurityLevel hSecurityLevel = toHidlSecurityLevel(level); + bool setSecurityLevel = true; + + if (level == DrmPlugin::kSecurityLevelMax) { + setSecurityLevel = false; + } else { + if (hSecurityLevel == SecurityLevel::UNKNOWN) { + return ERROR_DRM_CANNOT_HANDLE; + } + } + + status_t err = UNKNOWN_ERROR; + bool retry = true; + do { + hidl_vec hSessionId; + + Return hResult; + if (mPluginV1_1 == NULL || !setSecurityLevel) { + hResult = mPlugin->openSession([&](Status status, const hidl_vec& id) { + if (status == Status::OK) { + sessionId = toVector(id); + } + err = toStatusT(status); + }); + } else { + hResult = mPluginV1_1->openSession_1_1(hSecurityLevel, + [&](Status status, const hidl_vec& id) { + if (status == Status::OK) { + sessionId = toVector(id); + } + err = toStatusT(status); + }); + } + + if (!hResult.isOk()) { + err = DEAD_OBJECT; + } + + if (err == ERROR_DRM_RESOURCE_BUSY && retry) { + mLock.unlock(); + // reclaimSession may call back to closeSession, since mLock is + // shared between Drm instances, we should unlock here to avoid + // deadlock. + retry = DrmSessionManager::Instance()->reclaimSession(AIBinder_getCallingPid()); + mLock.lock(); + } else { + retry = false; + } + } while (retry); + + if (err == OK) { + std::shared_ptr client = + ndk::SharedRefBase::make(this, sessionId); + DrmSessionManager::Instance()->addSession( + AIBinder_getCallingPid(), std::static_pointer_cast(client), + sessionId); + mOpenSessions.push_back(client); + mMetrics.SetSessionStart(sessionId); + } + + mMetrics.mOpenSessionCounter.Increment(err); + return err; +} + +status_t DrmHalHidl::closeSession(Vector const& sessionId) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + Return status = mPlugin->closeSession(toHidlVec(sessionId)); + if (status.isOk()) { + if (status == Status::OK) { + DrmSessionManager::Instance()->removeSession(sessionId); + for (auto i = mOpenSessions.begin(); i != mOpenSessions.end(); i++) { + if (isEqualSessionId((*i)->mSessionId, sessionId)) { + mOpenSessions.erase(i); + break; + } + } + } + status_t response = toStatusT(status); + mMetrics.SetSessionEnd(sessionId); + mMetrics.mCloseSessionCounter.Increment(response); + return response; + } + mMetrics.mCloseSessionCounter.Increment(DEAD_OBJECT); + return DEAD_OBJECT; +} + +static DrmPlugin::KeyRequestType toKeyRequestType(KeyRequestType keyRequestType) { + switch (keyRequestType) { + case KeyRequestType::INITIAL: + return DrmPlugin::kKeyRequestType_Initial; + break; + case KeyRequestType::RENEWAL: + return DrmPlugin::kKeyRequestType_Renewal; + break; + case KeyRequestType::RELEASE: + return DrmPlugin::kKeyRequestType_Release; + break; + default: + return DrmPlugin::kKeyRequestType_Unknown; + break; + } +} + +static DrmPlugin::KeyRequestType toKeyRequestType_1_1(KeyRequestType_V1_1 keyRequestType) { + switch (keyRequestType) { + case KeyRequestType_V1_1::NONE: + return DrmPlugin::kKeyRequestType_None; + break; + case KeyRequestType_V1_1::UPDATE: + return DrmPlugin::kKeyRequestType_Update; + break; + default: + return toKeyRequestType(static_cast(keyRequestType)); + break; + } +} + +status_t DrmHalHidl::getKeyRequest(Vector const& sessionId, + Vector const& initData, String8 const& mimeType, + DrmPlugin::KeyType keyType, + KeyedVector const& optionalParameters, + Vector& request, String8& defaultUrl, + DrmPlugin::KeyRequestType* keyRequestType) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + EventTimer keyRequestTimer(&mMetrics.mGetKeyRequestTimeUs); + + DrmSessionManager::Instance()->useSession(sessionId); + + KeyType hKeyType; + if (keyType == DrmPlugin::kKeyType_Streaming) { + hKeyType = KeyType::STREAMING; + } else if (keyType == DrmPlugin::kKeyType_Offline) { + hKeyType = KeyType::OFFLINE; + } else if (keyType == DrmPlugin::kKeyType_Release) { + hKeyType = KeyType::RELEASE; + } else { + keyRequestTimer.SetAttribute(BAD_VALUE); + return BAD_VALUE; + } + + ::KeyedVector hOptionalParameters = toHidlKeyedVector(optionalParameters); + + status_t err = UNKNOWN_ERROR; + Return hResult; + + if (mPluginV1_2 != NULL) { + hResult = mPluginV1_2->getKeyRequest_1_2( + toHidlVec(sessionId), toHidlVec(initData), toHidlString(mimeType), hKeyType, + hOptionalParameters, + [&](Status_V1_2 status, const hidl_vec& hRequest, + KeyRequestType_V1_1 hKeyRequestType, const hidl_string& hDefaultUrl) { + if (status == Status_V1_2::OK) { + request = toVector(hRequest); + defaultUrl = toString8(hDefaultUrl); + *keyRequestType = toKeyRequestType_1_1(hKeyRequestType); + } + err = toStatusT(status); + }); + } else if (mPluginV1_1 != NULL) { + hResult = mPluginV1_1->getKeyRequest_1_1( + toHidlVec(sessionId), toHidlVec(initData), toHidlString(mimeType), hKeyType, + hOptionalParameters, + [&](Status status, const hidl_vec& hRequest, + KeyRequestType_V1_1 hKeyRequestType, const hidl_string& hDefaultUrl) { + if (status == Status::OK) { + request = toVector(hRequest); + defaultUrl = toString8(hDefaultUrl); + *keyRequestType = toKeyRequestType_1_1(hKeyRequestType); + } + err = toStatusT(status); + }); + } else { + hResult = mPlugin->getKeyRequest( + toHidlVec(sessionId), toHidlVec(initData), toHidlString(mimeType), hKeyType, + hOptionalParameters, + [&](Status status, const hidl_vec& hRequest, + KeyRequestType hKeyRequestType, const hidl_string& hDefaultUrl) { + if (status == Status::OK) { + request = toVector(hRequest); + defaultUrl = toString8(hDefaultUrl); + *keyRequestType = toKeyRequestType(hKeyRequestType); + } + err = toStatusT(status); + }); + } + + err = hResult.isOk() ? err : DEAD_OBJECT; + keyRequestTimer.SetAttribute(err); + return err; +} + +status_t DrmHalHidl::provideKeyResponse(Vector const& sessionId, + Vector const& response, + Vector& keySetId) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + EventTimer keyResponseTimer(&mMetrics.mProvideKeyResponseTimeUs); + + DrmSessionManager::Instance()->useSession(sessionId); + + status_t err = UNKNOWN_ERROR; + + Return hResult = + mPlugin->provideKeyResponse(toHidlVec(sessionId), toHidlVec(response), + [&](Status status, const hidl_vec& hKeySetId) { + if (status == Status::OK) { + keySetId = toVector(hKeySetId); + } + err = toStatusT(status); + }); + err = hResult.isOk() ? err : DEAD_OBJECT; + keyResponseTimer.SetAttribute(err); + return err; +} + +status_t DrmHalHidl::removeKeys(Vector const& keySetId) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + Return status = mPlugin->removeKeys(toHidlVec(keySetId)); + return status.isOk() ? toStatusT(status) : DEAD_OBJECT; +} + +status_t DrmHalHidl::restoreKeys(Vector const& sessionId, + Vector const& keySetId) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + DrmSessionManager::Instance()->useSession(sessionId); + + Return status = mPlugin->restoreKeys(toHidlVec(sessionId), toHidlVec(keySetId)); + return status.isOk() ? toStatusT(status) : DEAD_OBJECT; +} + +status_t DrmHalHidl::queryKeyStatus(Vector const& sessionId, + KeyedVector& infoMap) const { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + DrmSessionManager::Instance()->useSession(sessionId); + + ::KeyedVector hInfoMap; + + status_t err = UNKNOWN_ERROR; + + Return hResult = mPlugin->queryKeyStatus( + toHidlVec(sessionId), [&](Status status, const hidl_vec& map) { + if (status == Status::OK) { + infoMap = toKeyedVector(map); + } + err = toStatusT(status); + }); + + return hResult.isOk() ? err : DEAD_OBJECT; +} + +status_t DrmHalHidl::getProvisionRequest(String8 const& certType, String8 const& certAuthority, + Vector& request, String8& defaultUrl) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + status_t err = UNKNOWN_ERROR; + Return hResult; + + if (mPluginV1_2 != NULL) { + hResult = mPluginV1_2->getProvisionRequest_1_2( + toHidlString(certType), toHidlString(certAuthority), + [&](Status_V1_2 status, const hidl_vec& hRequest, + const hidl_string& hDefaultUrl) { + if (status == Status_V1_2::OK) { + request = toVector(hRequest); + defaultUrl = toString8(hDefaultUrl); + } + err = toStatusT(status); + }); + } else { + hResult = mPlugin->getProvisionRequest(toHidlString(certType), toHidlString(certAuthority), + [&](Status status, const hidl_vec& hRequest, + const hidl_string& hDefaultUrl) { + if (status == Status::OK) { + request = toVector(hRequest); + defaultUrl = toString8(hDefaultUrl); + } + err = toStatusT(status); + }); + } + + err = hResult.isOk() ? err : DEAD_OBJECT; + mMetrics.mGetProvisionRequestCounter.Increment(err); + return err; +} + +status_t DrmHalHidl::provideProvisionResponse(Vector const& response, + Vector& certificate, + Vector& wrappedKey) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + status_t err = UNKNOWN_ERROR; + + Return hResult = mPlugin->provideProvisionResponse( + toHidlVec(response), [&](Status status, const hidl_vec& hCertificate, + const hidl_vec& hWrappedKey) { + if (status == Status::OK) { + certificate = toVector(hCertificate); + wrappedKey = toVector(hWrappedKey); + } + err = toStatusT(status); + }); + + err = hResult.isOk() ? err : DEAD_OBJECT; + mMetrics.mProvideProvisionResponseCounter.Increment(err); + return err; +} + +status_t DrmHalHidl::getSecureStops(List>& secureStops) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + status_t err = UNKNOWN_ERROR; + + Return hResult = + mPlugin->getSecureStops([&](Status status, const hidl_vec& hSecureStops) { + if (status == Status::OK) { + secureStops = toSecureStops(hSecureStops); + } + err = toStatusT(status); + }); + + return hResult.isOk() ? err : DEAD_OBJECT; +} + +status_t DrmHalHidl::getSecureStopIds(List>& secureStopIds) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPluginV1_1 == NULL) { + return ERROR_DRM_CANNOT_HANDLE; + } + + status_t err = UNKNOWN_ERROR; + + Return hResult = mPluginV1_1->getSecureStopIds( + [&](Status status, const hidl_vec& hSecureStopIds) { + if (status == Status::OK) { + secureStopIds = toSecureStopIds(hSecureStopIds); + } + err = toStatusT(status); + }); + + return hResult.isOk() ? err : DEAD_OBJECT; +} + +status_t DrmHalHidl::getSecureStop(Vector const& ssid, Vector& secureStop) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + status_t err = UNKNOWN_ERROR; + + Return hResult = mPlugin->getSecureStop( + toHidlVec(ssid), [&](Status status, const SecureStop& hSecureStop) { + if (status == Status::OK) { + secureStop = toVector(hSecureStop.opaqueData); + } + err = toStatusT(status); + }); + + return hResult.isOk() ? err : DEAD_OBJECT; +} + +status_t DrmHalHidl::releaseSecureStops(Vector const& ssRelease) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + Return status(Status::ERROR_DRM_UNKNOWN); + if (mPluginV1_1 != NULL) { + SecureStopRelease secureStopRelease; + secureStopRelease.opaqueData = toHidlVec(ssRelease); + status = mPluginV1_1->releaseSecureStops(secureStopRelease); + } else { + status = mPlugin->releaseSecureStop(toHidlVec(ssRelease)); + } + return status.isOk() ? toStatusT(status) : DEAD_OBJECT; +} + +status_t DrmHalHidl::removeSecureStop(Vector const& ssid) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPluginV1_1 == NULL) { + return ERROR_DRM_CANNOT_HANDLE; + } + + Return status = mPluginV1_1->removeSecureStop(toHidlVec(ssid)); + return status.isOk() ? toStatusT(status) : DEAD_OBJECT; +} + +status_t DrmHalHidl::removeAllSecureStops() { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + Return status(Status::ERROR_DRM_UNKNOWN); + if (mPluginV1_1 != NULL) { + status = mPluginV1_1->removeAllSecureStops(); + } else { + status = mPlugin->releaseAllSecureStops(); + } + return status.isOk() ? toStatusT(status) : DEAD_OBJECT; +} + +status_t DrmHalHidl::getHdcpLevels(DrmPlugin::HdcpLevel* connected, + DrmPlugin::HdcpLevel* max) const { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + if (connected == NULL || max == NULL) { + return BAD_VALUE; + } + status_t err = UNKNOWN_ERROR; + + *connected = DrmPlugin::kHdcpLevelUnknown; + *max = DrmPlugin::kHdcpLevelUnknown; + + Return hResult; + if (mPluginV1_2 != NULL) { + hResult = mPluginV1_2->getHdcpLevels_1_2([&](Status_V1_2 status, + const HdcpLevel_V1_2& hConnected, + const HdcpLevel_V1_2& hMax) { + if (status == Status_V1_2::OK) { + *connected = toHdcpLevel(hConnected); + *max = toHdcpLevel(hMax); + } + err = toStatusT(status); + }); + } else if (mPluginV1_1 != NULL) { + hResult = mPluginV1_1->getHdcpLevels( + [&](Status status, const HdcpLevel& hConnected, const HdcpLevel& hMax) { + if (status == Status::OK) { + *connected = toHdcpLevel(static_cast(hConnected)); + *max = toHdcpLevel(static_cast(hMax)); + } + err = toStatusT(status); + }); + } else { + return ERROR_DRM_CANNOT_HANDLE; + } + + return hResult.isOk() ? err : DEAD_OBJECT; +} + +status_t DrmHalHidl::getNumberOfSessions(uint32_t* open, uint32_t* max) const { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + if (open == NULL || max == NULL) { + return BAD_VALUE; + } + status_t err = UNKNOWN_ERROR; + + *open = 0; + *max = 0; + + if (mPluginV1_1 == NULL) { + return ERROR_DRM_CANNOT_HANDLE; + } + + Return hResult = + mPluginV1_1->getNumberOfSessions([&](Status status, uint32_t hOpen, uint32_t hMax) { + if (status == Status::OK) { + *open = hOpen; + *max = hMax; + } + err = toStatusT(status); + }); + + return hResult.isOk() ? err : DEAD_OBJECT; +} + +status_t DrmHalHidl::getSecurityLevel(Vector const& sessionId, + DrmPlugin::SecurityLevel* level) const { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + if (level == NULL) { + return BAD_VALUE; + } + status_t err = UNKNOWN_ERROR; + + if (mPluginV1_1 == NULL) { + return ERROR_DRM_CANNOT_HANDLE; + } + + *level = DrmPlugin::kSecurityLevelUnknown; + + Return hResult = mPluginV1_1->getSecurityLevel(toHidlVec(sessionId), + [&](Status status, SecurityLevel hLevel) { + if (status == Status::OK) { + *level = toSecurityLevel(hLevel); + } + err = toStatusT(status); + }); + + return hResult.isOk() ? err : DEAD_OBJECT; +} + +status_t DrmHalHidl::getOfflineLicenseKeySetIds(List>& keySetIds) const { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPluginV1_2 == NULL) { + return ERROR_UNSUPPORTED; + } + + status_t err = UNKNOWN_ERROR; + + Return hResult = mPluginV1_2->getOfflineLicenseKeySetIds( + [&](Status status, const hidl_vec& hKeySetIds) { + if (status == Status::OK) { + keySetIds = toKeySetIds(hKeySetIds); + } + err = toStatusT(status); + }); + + return hResult.isOk() ? err : DEAD_OBJECT; +} + +status_t DrmHalHidl::removeOfflineLicense(Vector const& keySetId) { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPluginV1_2 == NULL) { + return ERROR_UNSUPPORTED; + } + + Return status = mPluginV1_2->removeOfflineLicense(toHidlVec(keySetId)); + return status.isOk() ? toStatusT(status) : DEAD_OBJECT; +} + +status_t DrmHalHidl::getOfflineLicenseState(Vector const& keySetId, + DrmPlugin::OfflineLicenseState* licenseState) const { + Mutex::Autolock autoLock(mLock); + + if (mInitCheck != OK) { + return mInitCheck; + } + + if (mPluginV1_2 == NULL) { + return ERROR_UNSUPPORTED; + } + *licenseState = DrmPlugin::kOfflineLicenseStateUnknown; + + status_t err = UNKNOWN_ERROR; + + Return hResult = mPluginV1_2->getOfflineLicenseState( + toHidlVec(keySetId), [&](Status status, OfflineLicenseState hLicenseState) { + if (status == Status::OK) { + *licenseState = toOfflineLicenseState(hLicenseState); + } + err = toStatusT(status); + }); + + return hResult.isOk() ? err : DEAD_OBJECT; +} + +status_t DrmHalHidl::getPropertyString(String8 const& name, String8& value) const { + Mutex::Autolock autoLock(mLock); + return getPropertyStringInternal(name, value); +} + +status_t DrmHalHidl::getPropertyStringInternal(String8 const& name, String8& value) const { + // This function is internal to the class and should only be called while + // mLock is already held. + INIT_CHECK(); + + status_t err = UNKNOWN_ERROR; + + Return hResult = mPlugin->getPropertyString( + toHidlString(name), [&](Status status, const hidl_string& hValue) { + if (status == Status::OK) { + value = toString8(hValue); + } + err = toStatusT(status); + }); + + return hResult.isOk() ? err : DEAD_OBJECT; +} + +status_t DrmHalHidl::getPropertyByteArray(String8 const& name, Vector& value) const { + Mutex::Autolock autoLock(mLock); + return getPropertyByteArrayInternal(name, value); +} + +status_t DrmHalHidl::getPropertyByteArrayInternal(String8 const& name, + Vector& value) const { + // This function is internal to the class and should only be called while + // mLock is already held. + INIT_CHECK(); + + status_t err = UNKNOWN_ERROR; + + Return hResult = mPlugin->getPropertyByteArray( + toHidlString(name), [&](Status status, const hidl_vec& hValue) { + if (status == Status::OK) { + value = toVector(hValue); + } + err = toStatusT(status); + }); + + err = hResult.isOk() ? err : DEAD_OBJECT; + if (name == kPropertyDeviceUniqueId) { + mMetrics.mGetDeviceUniqueIdCounter.Increment(err); + } + return err; +} + +status_t DrmHalHidl::setPropertyString(String8 const& name, String8 const& value) const { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + Return status = mPlugin->setPropertyString(toHidlString(name), toHidlString(value)); + return status.isOk() ? toStatusT(status) : DEAD_OBJECT; +} + +status_t DrmHalHidl::setPropertyByteArray(String8 const& name, Vector const& value) const { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + Return status = mPlugin->setPropertyByteArray(toHidlString(name), toHidlVec(value)); + return status.isOk() ? toStatusT(status) : DEAD_OBJECT; +} + +status_t DrmHalHidl::getMetrics(const sp& consumer) { + if (consumer == nullptr) { + return UNEXPECTED_NULL; + } + consumer->consumeFrameworkMetrics(mMetrics); + + // Append vendor metrics if they are supported. + if (mPluginV1_1 != NULL) { + String8 vendor; + String8 description; + if (getPropertyStringInternal(String8("vendor"), vendor) != OK || vendor.isEmpty()) { + ALOGE("Get vendor failed or is empty"); + vendor = "NONE"; + } + if (getPropertyStringInternal(String8("description"), description) != OK || + description.isEmpty()) { + ALOGE("Get description failed or is empty."); + description = "NONE"; + } + vendor += "."; + vendor += description; + + hidl_vec pluginMetrics; + status_t err = UNKNOWN_ERROR; + + Return status = + mPluginV1_1->getMetrics([&](Status status, hidl_vec pluginMetrics) { + if (status != Status::OK) { + ALOGV("Error getting plugin metrics: %d", status); + } else { + consumer->consumeHidlMetrics(vendor, pluginMetrics); + } + err = toStatusT(status); + }); + return status.isOk() ? err : DEAD_OBJECT; + } + + return OK; +} + +status_t DrmHalHidl::setCipherAlgorithm(Vector const& sessionId, + String8 const& algorithm) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + DrmSessionManager::Instance()->useSession(sessionId); + + Return status = + mPlugin->setCipherAlgorithm(toHidlVec(sessionId), toHidlString(algorithm)); + return status.isOk() ? toStatusT(status) : DEAD_OBJECT; +} + +status_t DrmHalHidl::setMacAlgorithm(Vector const& sessionId, String8 const& algorithm) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + DrmSessionManager::Instance()->useSession(sessionId); + + Return status = mPlugin->setMacAlgorithm(toHidlVec(sessionId), toHidlString(algorithm)); + return status.isOk() ? toStatusT(status) : DEAD_OBJECT; +} + +status_t DrmHalHidl::encrypt(Vector const& sessionId, Vector const& keyId, + Vector const& input, Vector const& iv, + Vector& output) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + DrmSessionManager::Instance()->useSession(sessionId); + + status_t err = UNKNOWN_ERROR; + + Return hResult = + mPlugin->encrypt(toHidlVec(sessionId), toHidlVec(keyId), toHidlVec(input), + toHidlVec(iv), [&](Status status, const hidl_vec& hOutput) { + if (status == Status::OK) { + output = toVector(hOutput); + } + err = toStatusT(status); + }); + + return hResult.isOk() ? err : DEAD_OBJECT; +} + +status_t DrmHalHidl::decrypt(Vector const& sessionId, Vector const& keyId, + Vector const& input, Vector const& iv, + Vector& output) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + DrmSessionManager::Instance()->useSession(sessionId); + + status_t err = UNKNOWN_ERROR; + + Return hResult = + mPlugin->decrypt(toHidlVec(sessionId), toHidlVec(keyId), toHidlVec(input), + toHidlVec(iv), [&](Status status, const hidl_vec& hOutput) { + if (status == Status::OK) { + output = toVector(hOutput); + } + err = toStatusT(status); + }); + + return hResult.isOk() ? err : DEAD_OBJECT; +} + +status_t DrmHalHidl::sign(Vector const& sessionId, Vector const& keyId, + Vector const& message, Vector& signature) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + DrmSessionManager::Instance()->useSession(sessionId); + + status_t err = UNKNOWN_ERROR; + + Return hResult = mPlugin->sign(toHidlVec(sessionId), toHidlVec(keyId), toHidlVec(message), + [&](Status status, const hidl_vec& hSignature) { + if (status == Status::OK) { + signature = toVector(hSignature); + } + err = toStatusT(status); + }); + + return hResult.isOk() ? err : DEAD_OBJECT; +} + +status_t DrmHalHidl::verify(Vector const& sessionId, Vector const& keyId, + Vector const& message, Vector const& signature, + bool& match) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + DrmSessionManager::Instance()->useSession(sessionId); + + status_t err = UNKNOWN_ERROR; + + Return hResult = + mPlugin->verify(toHidlVec(sessionId), toHidlVec(keyId), toHidlVec(message), + toHidlVec(signature), [&](Status status, bool hMatch) { + if (status == Status::OK) { + match = hMatch; + } else { + match = false; + } + err = toStatusT(status); + }); + + return hResult.isOk() ? err : DEAD_OBJECT; +} + +status_t DrmHalHidl::signRSA(Vector const& sessionId, String8 const& algorithm, + Vector const& message, Vector const& wrappedKey, + Vector& signature) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + DrmSessionManager::Instance()->useSession(sessionId); + + status_t err = UNKNOWN_ERROR; + + Return hResult = mPlugin->signRSA( + toHidlVec(sessionId), toHidlString(algorithm), toHidlVec(message), + toHidlVec(wrappedKey), [&](Status status, const hidl_vec& hSignature) { + if (status == Status::OK) { + signature = toVector(hSignature); + } + err = toStatusT(status); + }); + + return hResult.isOk() ? err : DEAD_OBJECT; +} + +std::string DrmHalHidl::reportFrameworkMetrics(const std::string& pluginMetrics) const { + mediametrics_handle_t item(mediametrics_create("mediadrm")); + mediametrics_setUid(item, mMetrics.GetAppUid()); + String8 vendor; + String8 description; + status_t result = getPropertyStringInternal(String8("vendor"), vendor); + if (result != OK) { + ALOGE("Failed to get vendor from drm plugin: %d", result); + } else { + mediametrics_setCString(item, "vendor", vendor.c_str()); + } + result = getPropertyStringInternal(String8("description"), description); + if (result != OK) { + ALOGE("Failed to get description from drm plugin: %d", result); + } else { + mediametrics_setCString(item, "description", description.c_str()); + } + + std::string serializedMetrics; + result = mMetrics.GetSerializedMetrics(&serializedMetrics); + if (result != OK) { + ALOGE("Failed to serialize framework metrics: %d", result); + } + std::string b64EncodedMetrics = + toBase64StringNoPad(serializedMetrics.data(), serializedMetrics.size()); + if (!b64EncodedMetrics.empty()) { + mediametrics_setCString(item, "serialized_metrics", b64EncodedMetrics.c_str()); + } + if (!pluginMetrics.empty()) { + mediametrics_setCString(item, "plugin_metrics", pluginMetrics.c_str()); + } + if (!mediametrics_selfRecord(item)) { + ALOGE("Failed to self record framework metrics"); + } + mediametrics_delete(item); + return serializedMetrics; +} + +std::string DrmHalHidl::reportPluginMetrics() const { + Vector metricsVector; + String8 vendor; + String8 description; + std::string metricsString; + if (getPropertyStringInternal(String8("vendor"), vendor) == OK && + getPropertyStringInternal(String8("description"), description) == OK && + getPropertyByteArrayInternal(String8("metrics"), metricsVector) == OK) { + metricsString = toBase64StringNoPad(metricsVector.array(), metricsVector.size()); + status_t res = android::reportDrmPluginMetrics(metricsString, vendor, description, + mMetrics.GetAppUid()); + if (res != OK) { + ALOGE("Metrics were retrieved but could not be reported: %d", res); + } + } + return metricsString; +} + +status_t DrmHalHidl::requiresSecureDecoder(const char* mime, bool* required) const { + Mutex::Autolock autoLock(mLock); + if (mPluginV1_4 == NULL) { + return false; + } + auto hResult = mPluginV1_4->requiresSecureDecoderDefault(hidl_string(mime)); + if (!hResult.isOk()) { + DrmUtils::LOG2BE("requiresSecureDecoder txn failed: %s", hResult.description().c_str()); + return DEAD_OBJECT; + } + if (required) { + *required = hResult; + } + return OK; +} + +status_t DrmHalHidl::requiresSecureDecoder(const char* mime, DrmPlugin::SecurityLevel securityLevel, + bool* required) const { + Mutex::Autolock autoLock(mLock); + if (mPluginV1_4 == NULL) { + return false; + } + auto hLevel = toHidlSecurityLevel(securityLevel); + auto hResult = mPluginV1_4->requiresSecureDecoder(hidl_string(mime), hLevel); + if (!hResult.isOk()) { + DrmUtils::LOG2BE("requiresSecureDecoder txn failed: %s", hResult.description().c_str()); + return DEAD_OBJECT; + } + if (required) { + *required = hResult; + } + return OK; +} + +status_t DrmHalHidl::setPlaybackId(Vector const& sessionId, const char* playbackId) { + Mutex::Autolock autoLock(mLock); + if (mPluginV1_4 == NULL) { + return ERROR_UNSUPPORTED; + } + auto err = mPluginV1_4->setPlaybackId(toHidlVec(sessionId), hidl_string(playbackId)); + return err.isOk() ? toStatusT(err) : DEAD_OBJECT; +} + +status_t DrmHalHidl::getLogMessages(Vector& logs) const { + Mutex::Autolock autoLock(mLock); + return DrmUtils::GetLogMessages(mPlugin, logs); +} + +status_t DrmHalHidl::getSupportedSchemes(std::vector &schemes) const { + Mutex::Autolock autoLock(mLock); + for (auto &factory : mFactories) { + sp factoryV1_3 = drm::V1_3::IDrmFactory::castFrom(factory); + if (factoryV1_3 == nullptr) { + continue; + } + + factoryV1_3->getSupportedCryptoSchemes( + [&](const hardware::hidl_vec>& schemes_hidl) { + for (const auto &scheme : schemes_hidl) { + schemes.insert(schemes.end(), scheme.data(), scheme.data() + scheme.size()); + } + }); + } + + return OK; +} + +} // namespace android diff --git a/drm/libmediadrm/DrmHalListener.cpp b/drm/libmediadrm/DrmHalListener.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cfcf4750e25c52744d0d45940c176ea354f96a2e --- /dev/null +++ b/drm/libmediadrm/DrmHalListener.cpp @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2022 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 "DrmHalListener" + +#include + +using ::aidl::android::hardware::drm::KeyStatusType; +using ::android::hardware::hidl_vec; + +namespace android { + +static const Vector toVector(const std::vector& vec) { + Vector vector; + vector.appendArray(vec.data(), vec.size()); + return *const_cast*>(&vector); +} + +template +static hidl_vec toHidlVec(const Vector& vector) { + hidl_vec vec; + vec.setToExternal(const_cast(vector.array()), vector.size()); + return vec; +} + +DrmHalListener::DrmHalListener(MediaDrmMetrics* metrics) + : mMetrics(metrics) {} + +DrmHalListener::~DrmHalListener() {} + +void DrmHalListener::setListener(sp listener) { + Mutex::Autolock lock(mEventLock); + mListener = listener; +} + +::ndk::ScopedAStatus DrmHalListener::onEvent(EventTypeAidl eventTypeAidl, + const std::vector& sessionId, + const std::vector& data) { + mMetrics->mEventCounter.Increment((uint32_t)eventTypeAidl); + + mEventLock.lock(); + sp listener = mListener; + mEventLock.unlock(); + + if (listener != NULL) { + Mutex::Autolock lock(mNotifyLock); + DrmPlugin::EventType eventType; + switch (eventTypeAidl) { + case EventTypeAidl::PROVISION_REQUIRED: + eventType = DrmPlugin::kDrmPluginEventProvisionRequired; + break; + case EventTypeAidl::KEY_NEEDED: + eventType = DrmPlugin::kDrmPluginEventKeyNeeded; + break; + case EventTypeAidl::KEY_EXPIRED: + eventType = DrmPlugin::kDrmPluginEventKeyExpired; + break; + case EventTypeAidl::VENDOR_DEFINED: + eventType = DrmPlugin::kDrmPluginEventVendorDefined; + break; + case EventTypeAidl::SESSION_RECLAIMED: + eventType = DrmPlugin::kDrmPluginEventSessionReclaimed; + break; + default: + return ::ndk::ScopedAStatus::ok(); + } + + listener->sendEvent(eventType, toHidlVec(toVector(sessionId)), toHidlVec(toVector(data))); + } + + return ::ndk::ScopedAStatus::ok(); +} + +::ndk::ScopedAStatus DrmHalListener::onExpirationUpdate(const std::vector& sessionId, + int64_t expiryTimeInMS) { + mEventLock.lock(); + sp listener = mListener; + mEventLock.unlock(); + + if (listener != NULL) { + Mutex::Autolock lock(mNotifyLock); + listener->sendExpirationUpdate(toHidlVec(toVector(sessionId)), expiryTimeInMS); + } + + return ::ndk::ScopedAStatus::ok(); +} + +::ndk::ScopedAStatus DrmHalListener::onKeysChange(const std::vector& sessionId, + const std::vector& keyStatusListAidl, + bool hasNewUsableKey) { + mEventLock.lock(); + sp listener = mListener; + mEventLock.unlock(); + + if (listener != NULL) { + std::vector keyStatusList; + size_t nKeys = keyStatusListAidl.size(); + for (size_t i = 0; i < nKeys; ++i) { + const KeyStatusAidl keyStatus = keyStatusListAidl[i]; + uint32_t type; + switch (keyStatus.type) { + case KeyStatusType::USABLE: + type = DrmPlugin::kKeyStatusType_Usable; + break; + case KeyStatusType::EXPIRED: + type = DrmPlugin::kKeyStatusType_Expired; + break; + case KeyStatusType::OUTPUT_NOT_ALLOWED: + type = DrmPlugin::kKeyStatusType_OutputNotAllowed; + break; + case KeyStatusType::STATUS_PENDING: + type = DrmPlugin::kKeyStatusType_StatusPending; + break; + case KeyStatusType::USABLE_IN_FUTURE: + type = DrmPlugin::kKeyStatusType_UsableInFuture; + break; + case KeyStatusType::INTERNAL_ERROR: + default: + type = DrmPlugin::kKeyStatusType_InternalError; + break; + } + keyStatusList.push_back({type, toHidlVec(toVector(keyStatus.keyId))}); + mMetrics->mKeyStatusChangeCounter.Increment((uint32_t)keyStatus.type); + } + + Mutex::Autolock lock(mNotifyLock); + listener->sendKeysChange(toHidlVec(toVector(sessionId)), keyStatusList, hasNewUsableKey); + } + else { + // There's no listener. But we still want to count the key change + // events. + size_t nKeys = keyStatusListAidl.size(); + + for (size_t i = 0; i < nKeys; i++) { + mMetrics->mKeyStatusChangeCounter.Increment((uint32_t)keyStatusListAidl[i].type); + } + } + + return ::ndk::ScopedAStatus::ok(); +} + +::ndk::ScopedAStatus DrmHalListener::onSessionLostState(const std::vector& sessionId) { + ::ndk::ScopedAStatus _aidl_status; + mEventLock.lock(); + sp listener = mListener; + mEventLock.unlock(); + + if (listener != NULL) { + Mutex::Autolock lock(mNotifyLock); + listener->sendSessionLostState(toHidlVec(toVector(sessionId))); + } + + return ::ndk::ScopedAStatus::ok(); +} + +} // namespace android \ No newline at end of file diff --git a/drm/libmediadrm/DrmMetrics.cpp b/drm/libmediadrm/DrmMetrics.cpp index 996fd192a1b3f86dee3548fa34ee589e0938ad6b..77b5343fdf517930bed592a07e970198fe4e20da 100644 --- a/drm/libmediadrm/DrmMetrics.cpp +++ b/drm/libmediadrm/DrmMetrics.cpp @@ -123,20 +123,19 @@ status_t MediaDrmMetrics::GetSerializedMetrics(std::string *serializedMetrics) { }); mKeyStatusChangeCounter.ExportValues( - [&](const KeyStatusType key_status_type, const int64_t value) { + [&](const uint32_t key_status_type, const int64_t value) { DrmFrameworkMetrics::Counter *counter = metrics.add_key_status_change_counter(); counter->set_count(value); - counter->mutable_attributes()->set_key_status_type( - (uint32_t)key_status_type); + counter->mutable_attributes()->set_key_status_type(key_status_type); }); mEventCounter.ExportValues( - [&](const EventType event_type, const int64_t value) { + [&](const uint32_t event_type, const int64_t value) { DrmFrameworkMetrics::Counter *counter = metrics.add_event_callback_counter(); counter->set_count(value); - counter->mutable_attributes()->set_event_type((uint32_t)event_type); + counter->mutable_attributes()->set_event_type(event_type); }); mGetDeviceUniqueIdCounter.ExportValues( diff --git a/drm/libmediadrm/DrmMetricsConsumer.cpp b/drm/libmediadrm/DrmMetricsConsumer.cpp index 5f0b26ec3e4c035824cdc78265dc309814d1f32d..c06f09b7e22a4705fa285a7932aa474a13c2bfa6 100644 --- a/drm/libmediadrm/DrmMetricsConsumer.cpp +++ b/drm/libmediadrm/DrmMetricsConsumer.cpp @@ -32,26 +32,24 @@ using ::android::os::PersistableBundle; namespace { -template std::string GetAttributeName(T type); - -template <> std::string GetAttributeName(KeyStatusType type) { - static const char *type_names[] = {"USABLE", "EXPIRED", +std::string GetAttributeName(const std::string &typeName, uint32_t attribute) { + if (typeName == "KeyStatusChange") { + static const char *type_names[] = {"USABLE", "EXPIRED", "OUTPUT_NOT_ALLOWED", "STATUS_PENDING", "INTERNAL_ERROR", "USABLE_IN_FUTURE"}; - if (((size_t)type) >= arraysize(type_names)) { - return "UNKNOWN_TYPE"; + if (attribute >= arraysize(type_names)) { + return "UNKNOWN_TYPE"; + } + return type_names[attribute]; } - return type_names[(size_t)type]; -} - -template <> std::string GetAttributeName(EventType type) { + static const char *type_names[] = {"PROVISION_REQUIRED", "KEY_NEEDED", "KEY_EXPIRED", "VENDOR_DEFINED", "SESSION_RECLAIMED"}; - if (((size_t)type) >= arraysize(type_names)) { + if (attribute >= arraysize(type_names)) { return "UNKNOWN_TYPE"; } - return type_names[(size_t)type]; + return type_names[attribute]; } template @@ -87,14 +85,14 @@ void ExportCounterMetric(const android::CounterMetric &counter, template void ExportCounterMetricWithAttributeNames( - const android::CounterMetric &counter, PersistableBundle *metrics) { + const android::CounterMetric &counter, const std::string &typeName, PersistableBundle *metrics) { if (!metrics) { ALOGE("metrics was unexpectedly null."); return; } - counter.ExportValues([&](const T &attribute, const int64_t value) { + counter.ExportValues([&](const uint32_t attribute, const int64_t value) { std::string name = counter.metric_name() + "." + - GetAttributeName(attribute) + ".count"; + GetAttributeName(typeName, attribute) + ".count"; metrics->putLong(android::String16(name.c_str()), value); }); } @@ -196,8 +194,8 @@ status_t DrmMetricsConsumer::consumeFrameworkMetrics(const MediaDrmMetrics &metr ExportEventMetric(metrics.mProvideKeyResponseTimeUs, mBundle); ExportCounterMetric(metrics.mGetProvisionRequestCounter, mBundle); ExportCounterMetric(metrics.mProvideProvisionResponseCounter, mBundle); - ExportCounterMetricWithAttributeNames(metrics.mKeyStatusChangeCounter, mBundle); - ExportCounterMetricWithAttributeNames(metrics.mEventCounter, mBundle); + ExportCounterMetricWithAttributeNames(metrics.mKeyStatusChangeCounter, "KeyStatusChange", mBundle); + ExportCounterMetricWithAttributeNames(metrics.mEventCounter, "Event", mBundle); ExportCounterMetric(metrics.mGetDeviceUniqueIdCounter, mBundle); ExportSessionLifespans(metrics.GetSessionLifespans(), mBundle); return android::OK; diff --git a/drm/libmediadrm/DrmUtils.cpp b/drm/libmediadrm/DrmUtils.cpp index 0b117a3cd21f828fa7282989cbdc6c38c3bd35cd..be0cd4b05dad594851e975901f78875b46aa6ed2 100644 --- a/drm/libmediadrm/DrmUtils.cpp +++ b/drm/libmediadrm/DrmUtils.cpp @@ -17,6 +17,7 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "DrmUtils" +#include #include #include #include @@ -32,10 +33,10 @@ #include #include +#include #include #include #include -#include #include #include @@ -57,10 +58,10 @@ namespace DrmUtils { namespace { -template -Hal *MakeObject(status_t *pstatus) { +template +Hal* MakeObject(status_t* pstatus) { status_t err = OK; - status_t &status = pstatus ? *pstatus : err; + status_t& status = pstatus ? *pstatus : err; auto obj = new Hal(); status = obj->initCheck(); if (status != OK && status != NO_INIT) { @@ -70,43 +71,44 @@ Hal *MakeObject(status_t *pstatus) { } template -void MakeHidlFactories(const uint8_t uuid[16], V &factories, M& instances) { +void MakeHidlFactories(const uint8_t uuid[16], V& factories, M& instances) { sp serviceManager = HServiceManager::getService(); if (serviceManager == nullptr) { LOG2BE("Failed to get service manager"); return; } - serviceManager->listManifestByInterface(Hal::descriptor, [&](const hidl_vec ®istered) { - for (const auto &instance : registered) { - auto factory = Hal::getService(instance); - if (factory != nullptr) { - instances[instance.c_str()] = Hal::descriptor; - if (!uuid) { - factories.push_back(factory); - continue; - } - auto supported = factory->isCryptoSchemeSupported(uuid); - if (!supported.isOk()) { - LOG2BE(uuid, "isCryptoSchemeSupported txn failed: %s", - supported.description().c_str()); - continue; - } - if (supported) { - factories.push_back(factory); + serviceManager->listManifestByInterface( + Hal::descriptor, [&](const hidl_vec& registered) { + for (const auto& instance : registered) { + auto factory = Hal::getService(instance); + if (factory != nullptr) { + instances[instance.c_str()] = Hal::descriptor; + if (!uuid) { + factories.push_back(factory); + continue; + } + auto supported = factory->isCryptoSchemeSupported(uuid); + if (!supported.isOk()) { + LOG2BE(uuid, "isCryptoSchemeSupported txn failed: %s", + supported.description().c_str()); + continue; + } + if (supported) { + factories.push_back(factory); + } + } } - } - } - }); + }); } template -void MakeHidlFactories(const uint8_t uuid[16], V &factories) { +void MakeHidlFactories(const uint8_t uuid[16], V& factories) { std::map instances; MakeHidlFactories(uuid, factories, instances); } -hidl_vec toHidlVec(const void *ptr, size_t size) { +hidl_vec toHidlVec(const void* ptr, size_t size) { hidl_vec vec(size); if (ptr != nullptr) { memcpy(vec.data(), ptr, size); @@ -114,19 +116,19 @@ hidl_vec toHidlVec(const void *ptr, size_t size) { return vec; } -hidl_array toHidlArray16(const uint8_t *ptr) { +hidl_array toHidlArray16(const uint8_t* ptr) { if (ptr == nullptr) { return hidl_array(); } return hidl_array(ptr); } -sp<::V1_0::IDrmPlugin> MakeDrmPlugin(const sp<::V1_0::IDrmFactory> &factory, - const uint8_t uuid[16], const char *appPackageName) { +sp<::V1_0::IDrmPlugin> MakeDrmPlugin(const sp<::V1_0::IDrmFactory>& factory, const uint8_t uuid[16], + const char* appPackageName) { sp<::V1_0::IDrmPlugin> plugin; auto err = factory->createPlugin( toHidlArray16(uuid), hidl_string(appPackageName), - [&](::V1_0::Status status, const sp<::V1_0::IDrmPlugin> &hPlugin) { + [&](::V1_0::Status status, const sp<::V1_0::IDrmPlugin>& hPlugin) { if (status != ::V1_0::Status::OK) { LOG2BE(uuid, "MakeDrmPlugin failed: %d", status); return; @@ -141,13 +143,13 @@ sp<::V1_0::IDrmPlugin> MakeDrmPlugin(const sp<::V1_0::IDrmFactory> &factory, } } -sp<::V1_0::ICryptoPlugin> MakeCryptoPlugin(const sp<::V1_0::ICryptoFactory> &factory, - const uint8_t uuid[16], const void *initData, +sp<::V1_0::ICryptoPlugin> MakeCryptoPlugin(const sp<::V1_0::ICryptoFactory>& factory, + const uint8_t uuid[16], const void* initData, size_t initDataSize) { sp<::V1_0::ICryptoPlugin> plugin; auto err = factory->createPlugin( toHidlArray16(uuid), toHidlVec(initData, initDataSize), - [&](::V1_0::Status status, const sp<::V1_0::ICryptoPlugin> &hPlugin) { + [&](::V1_0::Status status, const sp<::V1_0::ICryptoPlugin>& hPlugin) { if (status != ::V1_0::Status::OK) { LOG2BE(uuid, "MakeCryptoPlugin failed: %d", status); return; @@ -162,17 +164,38 @@ sp<::V1_0::ICryptoPlugin> MakeCryptoPlugin(const sp<::V1_0::ICryptoFactory> &fac } } -} // namespace +} // namespace bool UseDrmService() { return property_get_bool("mediadrm.use_mediadrmserver", true); } -sp MakeDrm(status_t *pstatus) { +std::vector> makeDrmFactoriesAidl() { + std::vector> factories; + AServiceManager_forEachDeclaredInstance( + IDrmFactoryAidl::descriptor, static_cast(&factories), + [](const char* instance, void* context) { + auto fullName = std::string(IDrmFactoryAidl::descriptor) + "/" + std::string(instance); + auto factory = IDrmFactoryAidl::fromBinder( + ::ndk::SpAIBinder(AServiceManager_waitForService(fullName.c_str()))); + if (factory == nullptr) { + ALOGE("not found IDrmFactory. Instance name:[%s]", fullName.c_str()); + return; + } + + ALOGI("found IDrmFactory. Instance name:[%s]", fullName.c_str()); + static_cast>*>(context)->emplace_back( + factory); + }); + + return factories; +} + +sp MakeDrm(status_t* pstatus) { return MakeObject(pstatus); } -sp MakeCrypto(status_t *pstatus) { +sp MakeCrypto(status_t* pstatus) { return MakeObject(pstatus); } @@ -191,9 +214,9 @@ std::vector> MakeDrmFactories(const uint8_t uuid[16]) { } std::vector> MakeDrmPlugins(const uint8_t uuid[16], - const char *appPackageName) { + const char* appPackageName) { std::vector> plugins; - for (const auto &factory : MakeDrmFactories(uuid)) { + for (const auto& factory : MakeDrmFactories(uuid)) { plugins.push_back(MakeDrmPlugin(factory, uuid, appPackageName)); } return plugins; @@ -209,10 +232,11 @@ std::vector> MakeCryptoFactories(const uint8_t uuid[1 return cryptoFactories; } -std::vector> MakeCryptoPlugins(const uint8_t uuid[16], const void *initData, - size_t initDataSize) { - std::vector> plugins; - for (const auto &factory : MakeCryptoFactories(uuid)) { +std::vector> MakeCryptoPlugins(const uint8_t uuid[16], + const void* initData, + size_t initDataSize) { + std::vector> plugins; + for (const auto& factory : MakeCryptoFactories(uuid)) { plugins.push_back(MakeCryptoPlugin(factory, uuid, initData, initDataSize)); } return plugins; @@ -220,90 +244,90 @@ std::vector> MakeCryptoPlugins(const uint8_t uuid[16], const v status_t toStatusT_1_4(::V1_4::Status status) { switch (status) { - case ::V1_4::Status::OK: - return OK; - case ::V1_4::Status::BAD_VALUE: - return BAD_VALUE; - case ::V1_4::Status::ERROR_DRM_CANNOT_HANDLE: - return ERROR_DRM_CANNOT_HANDLE; - case ::V1_4::Status::ERROR_DRM_DECRYPT: - return ERROR_DRM_DECRYPT; - case ::V1_4::Status::ERROR_DRM_DEVICE_REVOKED: - return ERROR_DRM_DEVICE_REVOKED; - case ::V1_4::Status::ERROR_DRM_FRAME_TOO_LARGE: - return ERROR_DRM_FRAME_TOO_LARGE; - case ::V1_4::Status::ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION: - return ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION; - case ::V1_4::Status::ERROR_DRM_INSUFFICIENT_SECURITY: - return ERROR_DRM_INSUFFICIENT_SECURITY; - case ::V1_4::Status::ERROR_DRM_INVALID_STATE: - return ERROR_DRM_INVALID_STATE; - case ::V1_4::Status::ERROR_DRM_LICENSE_EXPIRED: - return ERROR_DRM_LICENSE_EXPIRED; - case ::V1_4::Status::ERROR_DRM_NO_LICENSE: - return ERROR_DRM_NO_LICENSE; - case ::V1_4::Status::ERROR_DRM_NOT_PROVISIONED: - return ERROR_DRM_NOT_PROVISIONED; - case ::V1_4::Status::ERROR_DRM_RESOURCE_BUSY: - return ERROR_DRM_RESOURCE_BUSY; - case ::V1_4::Status::ERROR_DRM_RESOURCE_CONTENTION: - return ERROR_DRM_RESOURCE_CONTENTION; - case ::V1_4::Status::ERROR_DRM_SESSION_LOST_STATE: - return ERROR_DRM_SESSION_LOST_STATE; - case ::V1_4::Status::ERROR_DRM_SESSION_NOT_OPENED: - return ERROR_DRM_SESSION_NOT_OPENED; - - // New in S / drm@1.4: - case ::V1_4::Status::CANNOT_DECRYPT_ZERO_SUBSAMPLES: - return ERROR_DRM_ZERO_SUBSAMPLES; - case ::V1_4::Status::CRYPTO_LIBRARY_ERROR: - return ERROR_DRM_CRYPTO_LIBRARY; - case ::V1_4::Status::GENERAL_OEM_ERROR: - return ERROR_DRM_GENERIC_OEM; - case ::V1_4::Status::GENERAL_PLUGIN_ERROR: - return ERROR_DRM_GENERIC_PLUGIN; - case ::V1_4::Status::INIT_DATA_INVALID: - return ERROR_DRM_INIT_DATA; - case ::V1_4::Status::KEY_NOT_LOADED: - return ERROR_DRM_KEY_NOT_LOADED; - case ::V1_4::Status::LICENSE_PARSE_ERROR: - return ERROR_DRM_LICENSE_PARSE; - case ::V1_4::Status::LICENSE_POLICY_ERROR: - return ERROR_DRM_LICENSE_POLICY; - case ::V1_4::Status::LICENSE_RELEASE_ERROR: - return ERROR_DRM_LICENSE_RELEASE; - case ::V1_4::Status::LICENSE_REQUEST_REJECTED: - return ERROR_DRM_LICENSE_REQUEST_REJECTED; - case ::V1_4::Status::LICENSE_RESTORE_ERROR: - return ERROR_DRM_LICENSE_RESTORE; - case ::V1_4::Status::LICENSE_STATE_ERROR: - return ERROR_DRM_LICENSE_STATE; - case ::V1_4::Status::MALFORMED_CERTIFICATE: - return ERROR_DRM_CERTIFICATE_MALFORMED; - case ::V1_4::Status::MEDIA_FRAMEWORK_ERROR: - return ERROR_DRM_MEDIA_FRAMEWORK; - case ::V1_4::Status::MISSING_CERTIFICATE: - return ERROR_DRM_CERTIFICATE_MISSING; - case ::V1_4::Status::PROVISIONING_CERTIFICATE_ERROR: - return ERROR_DRM_PROVISIONING_CERTIFICATE; - case ::V1_4::Status::PROVISIONING_CONFIGURATION_ERROR: - return ERROR_DRM_PROVISIONING_CONFIG; - case ::V1_4::Status::PROVISIONING_PARSE_ERROR: - return ERROR_DRM_PROVISIONING_PARSE; - case ::V1_4::Status::PROVISIONING_REQUEST_REJECTED: - return ERROR_DRM_PROVISIONING_REQUEST_REJECTED; - case ::V1_4::Status::RETRYABLE_PROVISIONING_ERROR: - return ERROR_DRM_PROVISIONING_RETRY; - case ::V1_4::Status::SECURE_STOP_RELEASE_ERROR: - return ERROR_DRM_SECURE_STOP_RELEASE; - case ::V1_4::Status::STORAGE_READ_FAILURE: - return ERROR_DRM_STORAGE_READ; - case ::V1_4::Status::STORAGE_WRITE_FAILURE: - return ERROR_DRM_STORAGE_WRITE; - - case ::V1_4::Status::ERROR_DRM_UNKNOWN: - default: - return ERROR_DRM_UNKNOWN; + case ::V1_4::Status::OK: + return OK; + case ::V1_4::Status::BAD_VALUE: + return BAD_VALUE; + case ::V1_4::Status::ERROR_DRM_CANNOT_HANDLE: + return ERROR_DRM_CANNOT_HANDLE; + case ::V1_4::Status::ERROR_DRM_DECRYPT: + return ERROR_DRM_DECRYPT; + case ::V1_4::Status::ERROR_DRM_DEVICE_REVOKED: + return ERROR_DRM_DEVICE_REVOKED; + case ::V1_4::Status::ERROR_DRM_FRAME_TOO_LARGE: + return ERROR_DRM_FRAME_TOO_LARGE; + case ::V1_4::Status::ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION: + return ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION; + case ::V1_4::Status::ERROR_DRM_INSUFFICIENT_SECURITY: + return ERROR_DRM_INSUFFICIENT_SECURITY; + case ::V1_4::Status::ERROR_DRM_INVALID_STATE: + return ERROR_DRM_INVALID_STATE; + case ::V1_4::Status::ERROR_DRM_LICENSE_EXPIRED: + return ERROR_DRM_LICENSE_EXPIRED; + case ::V1_4::Status::ERROR_DRM_NO_LICENSE: + return ERROR_DRM_NO_LICENSE; + case ::V1_4::Status::ERROR_DRM_NOT_PROVISIONED: + return ERROR_DRM_NOT_PROVISIONED; + case ::V1_4::Status::ERROR_DRM_RESOURCE_BUSY: + return ERROR_DRM_RESOURCE_BUSY; + case ::V1_4::Status::ERROR_DRM_RESOURCE_CONTENTION: + return ERROR_DRM_RESOURCE_CONTENTION; + case ::V1_4::Status::ERROR_DRM_SESSION_LOST_STATE: + return ERROR_DRM_SESSION_LOST_STATE; + case ::V1_4::Status::ERROR_DRM_SESSION_NOT_OPENED: + return ERROR_DRM_SESSION_NOT_OPENED; + + // New in S / drm@1.4: + case ::V1_4::Status::CANNOT_DECRYPT_ZERO_SUBSAMPLES: + return ERROR_DRM_ZERO_SUBSAMPLES; + case ::V1_4::Status::CRYPTO_LIBRARY_ERROR: + return ERROR_DRM_CRYPTO_LIBRARY; + case ::V1_4::Status::GENERAL_OEM_ERROR: + return ERROR_DRM_GENERIC_OEM; + case ::V1_4::Status::GENERAL_PLUGIN_ERROR: + return ERROR_DRM_GENERIC_PLUGIN; + case ::V1_4::Status::INIT_DATA_INVALID: + return ERROR_DRM_INIT_DATA; + case ::V1_4::Status::KEY_NOT_LOADED: + return ERROR_DRM_KEY_NOT_LOADED; + case ::V1_4::Status::LICENSE_PARSE_ERROR: + return ERROR_DRM_LICENSE_PARSE; + case ::V1_4::Status::LICENSE_POLICY_ERROR: + return ERROR_DRM_LICENSE_POLICY; + case ::V1_4::Status::LICENSE_RELEASE_ERROR: + return ERROR_DRM_LICENSE_RELEASE; + case ::V1_4::Status::LICENSE_REQUEST_REJECTED: + return ERROR_DRM_LICENSE_REQUEST_REJECTED; + case ::V1_4::Status::LICENSE_RESTORE_ERROR: + return ERROR_DRM_LICENSE_RESTORE; + case ::V1_4::Status::LICENSE_STATE_ERROR: + return ERROR_DRM_LICENSE_STATE; + case ::V1_4::Status::MALFORMED_CERTIFICATE: + return ERROR_DRM_CERTIFICATE_MALFORMED; + case ::V1_4::Status::MEDIA_FRAMEWORK_ERROR: + return ERROR_DRM_MEDIA_FRAMEWORK; + case ::V1_4::Status::MISSING_CERTIFICATE: + return ERROR_DRM_CERTIFICATE_MISSING; + case ::V1_4::Status::PROVISIONING_CERTIFICATE_ERROR: + return ERROR_DRM_PROVISIONING_CERTIFICATE; + case ::V1_4::Status::PROVISIONING_CONFIGURATION_ERROR: + return ERROR_DRM_PROVISIONING_CONFIG; + case ::V1_4::Status::PROVISIONING_PARSE_ERROR: + return ERROR_DRM_PROVISIONING_PARSE; + case ::V1_4::Status::PROVISIONING_REQUEST_REJECTED: + return ERROR_DRM_PROVISIONING_REQUEST_REJECTED; + case ::V1_4::Status::RETRYABLE_PROVISIONING_ERROR: + return ERROR_DRM_PROVISIONING_RETRY; + case ::V1_4::Status::SECURE_STOP_RELEASE_ERROR: + return ERROR_DRM_SECURE_STOP_RELEASE; + case ::V1_4::Status::STORAGE_READ_FAILURE: + return ERROR_DRM_STORAGE_READ; + case ::V1_4::Status::STORAGE_WRITE_FAILURE: + return ERROR_DRM_STORAGE_WRITE; + + case ::V1_4::Status::ERROR_DRM_UNKNOWN: + default: + return ERROR_DRM_UNKNOWN; } return ERROR_DRM_UNKNOWN; } @@ -312,20 +336,34 @@ namespace { char logPriorityToChar(::V1_4::LogPriority priority) { char p = 'U'; switch (priority) { - case ::V1_4::LogPriority::VERBOSE: p = 'V'; break; - case ::V1_4::LogPriority::DEBUG: p = 'D'; break; - case ::V1_4::LogPriority::INFO: p = 'I'; break; - case ::V1_4::LogPriority::WARN: p = 'W'; break; - case ::V1_4::LogPriority::ERROR: p = 'E'; break; - case ::V1_4::LogPriority::FATAL: p = 'F'; break; - default: p = 'U'; break; + case ::V1_4::LogPriority::VERBOSE: + p = 'V'; + break; + case ::V1_4::LogPriority::DEBUG: + p = 'D'; + break; + case ::V1_4::LogPriority::INFO: + p = 'I'; + break; + case ::V1_4::LogPriority::WARN: + p = 'W'; + break; + case ::V1_4::LogPriority::ERROR: + p = 'E'; + break; + case ::V1_4::LogPriority::FATAL: + p = 'F'; + break; + default: + p = 'U'; + break; } return p; } } // namespace -std::string GetExceptionMessage(status_t err, const char *msg, - const Vector<::V1_4::LogMessage> &logs) { +std::string GetExceptionMessage(status_t err, const char* msg, + const Vector<::V1_4::LogMessage>& logs) { std::string ruler("=============================="); std::string header("Beginning of DRM Plugin Log"); std::string footer("End of DRM Plugin Log"); @@ -355,7 +393,7 @@ std::string GetExceptionMessage(status_t err, const char *msg, return msg8.c_str(); } -void LogBuffer::addLog(const ::V1_4::LogMessage &log) { +void LogBuffer::addLog(const ::V1_4::LogMessage& log) { std::unique_lock lock(mMutex); mBuffer.push_back(log); while (mBuffer.size() > MAX_CAPACITY) { diff --git a/drm/libmediadrm/fuzzer/Android.bp b/drm/libmediadrm/fuzzer/Android.bp index 49bbad4a8a2d346fac9cc9249d3832d2962058ea..a85e3cf1ed2a23580c05b121e16a272cb2a6308a 100644 --- a/drm/libmediadrm/fuzzer/Android.bp +++ b/drm/libmediadrm/fuzzer/Android.bp @@ -36,6 +36,7 @@ cc_fuzz { "libmediadrm", "liblog", "resourcemanager_aidl_interface-ndk", + "libaidlcommonsupport", ], header_libs: [ "libmedia_headers", @@ -59,6 +60,7 @@ cc_fuzz { "android.hardware.drm@1.4", "libhidlallocatorutils", "libhidlbase", + "android.hardware.drm-V1-ndk", ], fuzz_config: { cc: [ diff --git a/drm/libmediadrm/fuzzer/mediadrm_fuzzer.cpp b/drm/libmediadrm/fuzzer/mediadrm_fuzzer.cpp index 8df0477581542fd5aa54192cdc797aa05e22f5bc..597b72db59c4048961b5e2edff20625a0ff7974b 100644 --- a/drm/libmediadrm/fuzzer/mediadrm_fuzzer.cpp +++ b/drm/libmediadrm/fuzzer/mediadrm_fuzzer.cpp @@ -20,10 +20,13 @@ #include #include +#include #include #include #include #include "fuzzer/FuzzedDataProvider.h" +#include +#include #define AES_BLOCK_SIZE 16 #define UNUSED_PARAM __attribute__((unused)) @@ -33,6 +36,7 @@ using namespace android; using android::hardware::fromHeap; using ::android::os::PersistableBundle; using drm::V1_0::BufferType; +using ::android::hardware::drm::V1_0::DestinationBuffer; enum { INVALID_UUID = 0, @@ -398,7 +402,7 @@ void DrmFuzzer::invokeCryptoDecrypt(const uint8_t *data) { .secureMemory = nullptr}; const uint64_t offset = 0; - AString *errorDetailMsg = nullptr; + AString errorDetailMsg; CryptoPlugin::Mode mode; bool shouldPassRandomCryptoMode = mFuzzedDataProvider->ConsumeBool(); if (shouldPassRandomCryptoMode) { @@ -408,7 +412,7 @@ void DrmFuzzer::invokeCryptoDecrypt(const uint8_t *data) { kCryptoMode[mFuzzedDataProvider->ConsumeIntegralInRange(0, kNumCryptoMode - 1)]; } mCrypto->decrypt(keyId, iv, mode, pattern, sourceBuffer, offset, subSamples, numSubSamples, - destBuffer, errorDetailMsg); + destBuffer, &errorDetailMsg); if (heapSeqNum >= 0) { mCrypto->unsetHeap(heapSeqNum); diff --git a/drm/libmediadrm/include/mediadrm/CryptoHal.h b/drm/libmediadrm/include/mediadrm/CryptoHal.h index 5fd39e64f2c1c574d4c9a4028f74be02bd127484..32a6741e2a417de83e2a61f7ddd60f41e1fc757b 100644 --- a/drm/libmediadrm/include/mediadrm/CryptoHal.h +++ b/drm/libmediadrm/include/mediadrm/CryptoHal.h @@ -15,25 +15,12 @@ */ #ifndef CRYPTO_HAL_H_ - #define CRYPTO_HAL_H_ -#include -#include -#include -#include -#include - #include #include #include -namespace drm = ::android::hardware::drm; -using drm::V1_0::ICryptoFactory; -using drm::V1_0::ICryptoPlugin; -using drm::V1_0::SharedBuffer; -using drm::V1_0::DestinationBuffer; - using ::android::hardware::HidlMemory; class IMemoryHeap; @@ -43,67 +30,30 @@ namespace android { struct CryptoHal : public ICrypto { CryptoHal(); virtual ~CryptoHal(); - virtual status_t initCheck() const; - virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]); - virtual status_t createPlugin( const uint8_t uuid[16], const void *data, size_t size); - virtual status_t destroyPlugin(); - virtual bool requiresSecureDecoderComponent( const char *mime) const; - virtual void notifyResolution(uint32_t width, uint32_t height); - virtual status_t setMediaDrmSession(const Vector &sessionId); - virtual ssize_t decrypt(const uint8_t key[16], const uint8_t iv[16], CryptoPlugin::Mode mode, const CryptoPlugin::Pattern &pattern, - const ::SharedBuffer &source, size_t offset, + const drm::V1_0::SharedBuffer &source, size_t offset, const CryptoPlugin::SubSample *subSamples, size_t numSubSamples, - const ::DestinationBuffer &destination, + const drm::V1_0::DestinationBuffer &destination, AString *errorDetailMsg); - - virtual int32_t setHeap(const sp& heap) { - return setHeapBase(heap); - } - virtual void unsetHeap(int32_t seqNum) { clearHeapBase(seqNum); } - + virtual int32_t setHeap(const sp& heap); + virtual void unsetHeap(int32_t seqNum); virtual status_t getLogMessages(Vector &logs) const; - private: - mutable Mutex mLock; - - const Vector> mFactories; - sp mPlugin; - sp mPluginV1_2; - - /** - * mInitCheck is: - * NO_INIT if a plugin hasn't been created yet - * ERROR_UNSUPPORTED if a plugin can't be created for the uuid - * OK after a plugin has been created and mPlugin is valid - */ - status_t mInitCheck; - - KeyedVector mHeapSizes; - int32_t mHeapSeqNum; - - Vector> makeCryptoFactories(); - sp makeCryptoPlugin(const sp& factory, - const uint8_t uuid[16], const void *initData, size_t size); - - int32_t setHeapBase(const sp& heap); - void clearHeapBase(int32_t seqNum); - - status_t checkSharedBuffer(const ::SharedBuffer& buffer); - + sp mCryptoHalHidl; + sp mCryptoHalAidl; DISALLOW_EVIL_CONSTRUCTORS(CryptoHal); }; } // namespace android -#endif // CRYPTO_HAL_H_ +#endif // CRYPTO_HAL_H_ \ No newline at end of file diff --git a/drm/libmediadrm/include/mediadrm/CryptoHalAidl.h b/drm/libmediadrm/include/mediadrm/CryptoHalAidl.h new file mode 100644 index 0000000000000000000000000000000000000000..50878a6f8925620d22cf53212e5158fce91b640a --- /dev/null +++ b/drm/libmediadrm/include/mediadrm/CryptoHalAidl.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2021 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 CRYPTO_HAL_AIDL_H_ +#define CRYPTO_HAL_AIDL_H_ + +#include +#include +#include +#include +#include + +using IDrmFactoryAidl = ::aidl::android::hardware::drm::IDrmFactory; +using ICryptoPluginAidl = ::aidl::android::hardware::drm::ICryptoPlugin; +using ::aidl::android::hardware::drm::Uuid; + +// -------Hidl interface related----------------- +// TODO: replace before removing hidl interface +using ::android::hardware::drm::V1_0::DestinationBuffer; +using ::android::hardware::drm::V1_0::SharedBuffer; + +using ::android::hardware::HidlMemory; + +// -------Hidl interface related end------------- + +class IMemoryHeap; + +namespace android { + +struct CryptoHalAidl : public ICrypto { + CryptoHalAidl(); + virtual ~CryptoHalAidl(); + virtual status_t initCheck() const; + virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]); + virtual status_t createPlugin(const uint8_t uuid[16], const void* data, size_t size); + virtual status_t destroyPlugin(); + virtual bool requiresSecureDecoderComponent(const char* mime) const; + virtual void notifyResolution(uint32_t width, uint32_t height); + virtual status_t setMediaDrmSession(const Vector& sessionId); + virtual ssize_t decrypt(const uint8_t key[16], const uint8_t iv[16], CryptoPlugin::Mode mode, + const CryptoPlugin::Pattern& pattern, const ::SharedBuffer& source, + size_t offset, const CryptoPlugin::SubSample* subSamples, + size_t numSubSamples, const ::DestinationBuffer& destination, + AString* errorDetailMsg); + virtual int32_t setHeap(const sp& heap); + virtual void unsetHeap(int32_t seqNum); + virtual status_t getLogMessages(Vector& logs) const; + + private: + mutable Mutex mLock; + + const std::vector> mFactories; + std::shared_ptr mPlugin; + + /** + * mInitCheck is: + * NO_INIT if a plugin hasn't been created yet + * ERROR_UNSUPPORTED if a plugin can't be created for the uuid + * OK after a plugin has been created and mPlugin is valid + */ + status_t mInitCheck; + + KeyedVector mHeapSizes; + int32_t mHeapSeqNum; + + std::shared_ptr makeCryptoPlugin( + const std::shared_ptr& factory, const Uuid& uuidAidl, + const std::vector initData); + + status_t checkSharedBuffer(const ::SharedBuffer& buffer); + bool isCryptoSchemeSupportedInternal(const uint8_t uuid[16], int* factoryIdx); + + DISALLOW_EVIL_CONSTRUCTORS(CryptoHalAidl); +}; + +} // namespace android + +#endif // CRYPTO_HAL_AIDL_H_ diff --git a/drm/libmediadrm/include/mediadrm/CryptoHalHidl.h b/drm/libmediadrm/include/mediadrm/CryptoHalHidl.h new file mode 100644 index 0000000000000000000000000000000000000000..6db1e891089a5e28e7203e0b980911f4a0b535c0 --- /dev/null +++ b/drm/libmediadrm/include/mediadrm/CryptoHalHidl.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2021 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 CRYPTO_HAL_HIDL_H_ +#define CRYPTO_HAL_HIDL_H_ + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace drm = ::android::hardware::drm; +using drm::V1_0::ICryptoFactory; +using drm::V1_0::ICryptoPlugin; +using drm::V1_0::SharedBuffer; +using drm::V1_0::DestinationBuffer; + +using ::android::hardware::HidlMemory; + +class IMemoryHeap; + +namespace android { + +struct CryptoHalHidl : public ICrypto { + CryptoHalHidl(); + virtual ~CryptoHalHidl(); + + virtual status_t initCheck() const; + + virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]); + + virtual status_t createPlugin( + const uint8_t uuid[16], const void *data, size_t size); + + virtual status_t destroyPlugin(); + + virtual bool requiresSecureDecoderComponent( + const char *mime) const; + + virtual void notifyResolution(uint32_t width, uint32_t height); + + virtual status_t setMediaDrmSession(const Vector &sessionId); + + virtual ssize_t decrypt(const uint8_t key[16], const uint8_t iv[16], + CryptoPlugin::Mode mode, const CryptoPlugin::Pattern &pattern, + const ::SharedBuffer &source, size_t offset, + const CryptoPlugin::SubSample *subSamples, size_t numSubSamples, + const ::DestinationBuffer &destination, + AString *errorDetailMsg); + + virtual int32_t setHeap(const sp& heap) { + return setHeapBase(heap); + } + virtual void unsetHeap(int32_t seqNum) { clearHeapBase(seqNum); } + + virtual status_t getLogMessages(Vector &logs) const; + +private: + mutable Mutex mLock; + + const Vector> mFactories; + sp mPlugin; + sp mPluginV1_2; + + /** + * mInitCheck is: + * NO_INIT if a plugin hasn't been created yet + * ERROR_UNSUPPORTED if a plugin can't be created for the uuid + * OK after a plugin has been created and mPlugin is valid + */ + status_t mInitCheck; + + KeyedVector mHeapSizes; + int32_t mHeapSeqNum; + + Vector> makeCryptoFactories(); + sp makeCryptoPlugin(const sp& factory, + const uint8_t uuid[16], const void *initData, size_t size); + + int32_t setHeapBase(const sp& heap); + void clearHeapBase(int32_t seqNum); + + status_t checkSharedBuffer(const ::SharedBuffer& buffer); + + DISALLOW_EVIL_CONSTRUCTORS(CryptoHalHidl); +}; + +} // namespace android + +#endif // CRYPTO_HAL_H_ diff --git a/drm/libmediadrm/include/mediadrm/DrmHal.h b/drm/libmediadrm/include/mediadrm/DrmHal.h index 7eb1decbc8f57ffbfca835d69b92076d6c4d29db..eab597b005c69b2faf8784ce940b5388b47eeb7d 100644 --- a/drm/libmediadrm/include/mediadrm/DrmHal.h +++ b/drm/libmediadrm/include/mediadrm/DrmHal.h @@ -14,77 +14,27 @@ * limitations under the License. */ -#ifndef DRM_HAL_H_ - -#define DRM_HAL_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include #include -#include -#include -#include - -namespace drm = ::android::hardware::drm; -using drm::V1_0::EventType; -using drm::V1_0::IDrmFactory; -using drm::V1_0::IDrmPlugin; -using drm::V1_0::IDrmPluginListener; -using drm::V1_1::SecurityLevel; -using drm::V1_2::KeyStatus; -using drm::V1_2::OfflineLicenseState; -using ::android::hardware::hidl_vec; -using ::android::hardware::Return; -using ::android::hardware::Void; -typedef drm::V1_2::IDrmPluginListener IDrmPluginListener_V1_2; -typedef drm::V1_0::KeyStatus KeyStatus_V1_0; +#ifndef DRM_HAL_H_ +#define DRM_HAL_H_ namespace android { -struct DrmSessionClientInterface; - -inline bool operator==(const Vector &l, const Vector &r) { - if (l.size() != r.size()) return false; - return memcmp(l.array(), r.array(), l.size()) == 0; -} - -struct DrmHal : public IDrm, - public IDrmPluginListener_V1_2 { - - struct DrmSessionClient; - +struct DrmHal : public IDrm { DrmHal(); virtual ~DrmHal(); - virtual status_t initCheck() const; - virtual status_t isCryptoSchemeSupported(const uint8_t uuid[16], - const String8& mimeType, - DrmPlugin::SecurityLevel level, - bool *isSupported); - + const String8 &mimeType, + DrmPlugin::SecurityLevel securityLevel, + bool *result); virtual status_t createPlugin(const uint8_t uuid[16], const String8 &appPackageName); - virtual status_t destroyPlugin(); - - virtual status_t openSession(DrmPlugin::SecurityLevel level, + virtual status_t openSession(DrmPlugin::SecurityLevel securityLevel, Vector &sessionId); - virtual status_t closeSession(Vector const &sessionId); - virtual status_t getKeyRequest(Vector const &sessionId, Vector const &initData, @@ -92,168 +42,89 @@ struct DrmHal : public IDrm, KeyedVector const &optionalParameters, Vector &request, String8 &defaultUrl, DrmPlugin::KeyRequestType *keyRequestType); - virtual status_t provideKeyResponse(Vector const &sessionId, Vector const &response, Vector &keySetId); - virtual status_t removeKeys(Vector const &keySetId); - virtual status_t restoreKeys(Vector const &sessionId, Vector const &keySetId); - virtual status_t queryKeyStatus(Vector const &sessionId, KeyedVector &infoMap) const; - virtual status_t getProvisionRequest(String8 const &certType, String8 const &certAuthority, Vector &request, - String8 &defaulUrl); - + String8 &defaultUrl); virtual status_t provideProvisionResponse(Vector const &response, Vector &certificate, Vector &wrappedKey); - virtual status_t getSecureStops(List> &secureStops); virtual status_t getSecureStopIds(List> &secureStopIds); virtual status_t getSecureStop(Vector const &ssid, Vector &secureStop); - virtual status_t releaseSecureStops(Vector const &ssRelease); virtual status_t removeSecureStop(Vector const &ssid); virtual status_t removeAllSecureStops(); - virtual status_t getHdcpLevels(DrmPlugin::HdcpLevel *connectedLevel, DrmPlugin::HdcpLevel *maxLevel) const; virtual status_t getNumberOfSessions(uint32_t *currentSessions, uint32_t *maxSessions) const; virtual status_t getSecurityLevel(Vector const &sessionId, DrmPlugin::SecurityLevel *level) const; - virtual status_t getOfflineLicenseKeySetIds(List> &keySetIds) const; virtual status_t removeOfflineLicense(Vector const &keySetId); virtual status_t getOfflineLicenseState(Vector const &keySetId, DrmPlugin::OfflineLicenseState *licenseState) const; - - virtual status_t getPropertyString(String8 const &name, String8 &value ) const; + virtual status_t getPropertyString(String8 const &name, String8 &value) const; virtual status_t getPropertyByteArray(String8 const &name, - Vector &value ) const; - virtual status_t setPropertyString(String8 const &name, String8 const &value ) const; + Vector &value) const; + virtual status_t setPropertyString(String8 const &name, + String8 const &value ) const; virtual status_t setPropertyByteArray(String8 const &name, - Vector const &value ) const; + Vector const &value) const; virtual status_t getMetrics(const sp &consumer); - virtual status_t setCipherAlgorithm(Vector const &sessionId, String8 const &algorithm); - virtual status_t setMacAlgorithm(Vector const &sessionId, String8 const &algorithm); - virtual status_t encrypt(Vector const &sessionId, Vector const &keyId, Vector const &input, Vector const &iv, Vector &output); - virtual status_t decrypt(Vector const &sessionId, Vector const &keyId, Vector const &input, Vector const &iv, Vector &output); - virtual status_t sign(Vector const &sessionId, Vector const &keyId, Vector const &message, Vector &signature); - virtual status_t verify(Vector const &sessionId, Vector const &keyId, Vector const &message, Vector const &signature, bool &match); - virtual status_t signRSA(Vector const &sessionId, String8 const &algorithm, Vector const &message, Vector const &wrappedKey, Vector &signature); - virtual status_t setListener(const sp& listener); - virtual status_t requiresSecureDecoder(const char *mime, bool *required) const; - virtual status_t requiresSecureDecoder(const char *mime, DrmPlugin::SecurityLevel securityLevel, bool *required) const; - virtual status_t setPlaybackId( Vector const &sessionId, const char *playbackId); - virtual status_t getLogMessages(Vector &logs) const; - - // Methods of IDrmPluginListener - Return sendEvent(EventType eventType, - const hidl_vec& sessionId, const hidl_vec& data); - - Return sendExpirationUpdate(const hidl_vec& sessionId, - int64_t expiryTimeInMS); - - Return sendKeysChange(const hidl_vec& sessionId, - const hidl_vec& keyStatusList, bool hasNewUsableKey); - - Return sendKeysChange_1_2(const hidl_vec& sessionId, - const hidl_vec& keyStatusList, bool hasNewUsableKey); - - Return sendSessionLostState(const hidl_vec& sessionId); + virtual status_t getSupportedSchemes(std::vector &schemes) const; private: - static Mutex mLock; - - sp mListener; - mutable Mutex mEventLock; - mutable Mutex mNotifyLock; - - const std::vector> mFactories; - sp mPlugin; - sp mPluginV1_1; - sp mPluginV1_2; - sp mPluginV1_4; - String8 mAppPackageName; - - // Mutable to allow modification within GetPropertyByteArray. - mutable MediaDrmMetrics mMetrics; - - std::vector> mOpenSessions; - void closeOpenSessions(); - void cleanup(); - - /** - * mInitCheck is: - * NO_INIT if a plugin hasn't been created yet - * ERROR_UNSUPPORTED if a plugin can't be created for the uuid - * OK after a plugin has been created and mPlugin is valid - */ - status_t mInitCheck; - - std::vector> makeDrmFactories(); - sp makeDrmPlugin(const sp& factory, - const uint8_t uuid[16], const String8& appPackageName); - - void writeByteArray(Parcel &obj, const hidl_vec& array); - - std::string reportPluginMetrics() const; - std::string reportFrameworkMetrics(const std::string& pluginMetrics) const; - status_t getPropertyStringInternal(String8 const &name, String8 &value) const; - status_t getPropertyByteArrayInternal(String8 const &name, - Vector &value) const; - status_t matchMimeTypeAndSecurityLevel(const sp &factory, - const uint8_t uuid[16], - const String8 &mimeType, - DrmPlugin::SecurityLevel level, - bool *isSupported); - + sp mDrmHalHidl; + sp mDrmHalAidl; DISALLOW_EVIL_CONSTRUCTORS(DrmHal); }; -} // namespace android +} // namespace android -#endif // DRM_HAL_H_ +#endif // DRM_HAL_H_ \ No newline at end of file diff --git a/drm/libmediadrm/include/mediadrm/DrmHalAidl.h b/drm/libmediadrm/include/mediadrm/DrmHalAidl.h new file mode 100644 index 0000000000000000000000000000000000000000..0f51ce9e7c824a0d23c0221f1157895ae82b6887 --- /dev/null +++ b/drm/libmediadrm/include/mediadrm/DrmHalAidl.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2021 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 DRM_HAL_AIDL_H_ +#define DRM_HAL_AIDL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using IDrmPluginAidl = ::aidl::android::hardware::drm::IDrmPlugin; +using IDrmFactoryAidl = ::aidl::android::hardware::drm::IDrmFactory; +using EventTypeAidl = ::aidl::android::hardware::drm::EventType; +using KeyStatusAidl = ::aidl::android::hardware::drm::KeyStatus; +using ::aidl::android::hardware::drm::Uuid; + +namespace android { +struct DrmHalAidl : public IDrm{ + struct DrmSessionClient; + DrmHalAidl(); + virtual ~DrmHalAidl(); + virtual status_t initCheck() const; + virtual status_t isCryptoSchemeSupported(const uint8_t uuid[16], const String8& mimeType, + DrmPlugin::SecurityLevel securityLevel, bool* result); + virtual status_t createPlugin(const uint8_t uuid[16], const String8& appPackageName); + virtual status_t destroyPlugin(); + virtual status_t openSession(DrmPlugin::SecurityLevel securityLevel, + Vector& sessionId); + virtual status_t closeSession(Vector const& sessionId); + virtual status_t getKeyRequest(Vector const& sessionId, + Vector const& initData, String8 const& mimeType, + DrmPlugin::KeyType keyType, + KeyedVector const& optionalParameters, + Vector& request, String8& defaultUrl, + DrmPlugin::KeyRequestType* keyRequestType); + virtual status_t provideKeyResponse(Vector const& sessionId, + Vector const& response, Vector& keySetId); + virtual status_t removeKeys(Vector const& keySetId); + virtual status_t restoreKeys(Vector const& sessionId, Vector const& keySetId); + virtual status_t queryKeyStatus(Vector const& sessionId, + KeyedVector& infoMap) const; + virtual status_t getProvisionRequest(String8 const& certType, String8 const& certAuthority, + Vector& request, String8& defaultUrl); + virtual status_t provideProvisionResponse(Vector const& response, + Vector& certificate, + Vector& wrappedKey); + virtual status_t getSecureStops(List>& secureStops); + virtual status_t getSecureStopIds(List>& secureStopIds); + virtual status_t getSecureStop(Vector const& ssid, Vector& secureStop); + virtual status_t releaseSecureStops(Vector const& ssRelease); + virtual status_t removeSecureStop(Vector const& ssid); + virtual status_t removeAllSecureStops(); + virtual status_t getHdcpLevels(DrmPlugin::HdcpLevel* connectedLevel, + DrmPlugin::HdcpLevel* maxLevel) const; + virtual status_t getNumberOfSessions(uint32_t* currentSessions, uint32_t* maxSessions) const; + virtual status_t getSecurityLevel(Vector const& sessionId, + DrmPlugin::SecurityLevel* level) const; + virtual status_t getOfflineLicenseKeySetIds(List>& keySetIds) const; + virtual status_t removeOfflineLicense(Vector const& keySetId); + virtual status_t getOfflineLicenseState(Vector const& keySetId, + DrmPlugin::OfflineLicenseState* licenseState) const; + virtual status_t getPropertyString(String8 const& name, String8& value) const; + virtual status_t getPropertyByteArray(String8 const& name, Vector& value) const; + virtual status_t setPropertyString(String8 const& name, String8 const& value) const; + virtual status_t setPropertyByteArray(String8 const& name, Vector const& value) const; + virtual status_t getMetrics(const sp& consumer); + virtual status_t setCipherAlgorithm(Vector const& sessionId, String8 const& algorithm); + virtual status_t setMacAlgorithm(Vector const& sessionId, String8 const& algorithm); + virtual status_t encrypt(Vector const& sessionId, Vector const& keyId, + Vector const& input, Vector const& iv, + Vector& output); + virtual status_t decrypt(Vector const& sessionId, Vector const& keyId, + Vector const& input, Vector const& iv, + Vector& output); + virtual status_t sign(Vector const& sessionId, Vector const& keyId, + Vector const& message, Vector& signature); + virtual status_t verify(Vector const& sessionId, Vector const& keyId, + Vector const& message, Vector const& signature, + bool& match); + virtual status_t signRSA(Vector const& sessionId, String8 const& algorithm, + Vector const& message, Vector const& wrappedKey, + Vector& signature); + virtual status_t setListener(const sp& listener); + virtual status_t requiresSecureDecoder(const char* mime, bool* required) const; + virtual status_t requiresSecureDecoder(const char* mime, DrmPlugin::SecurityLevel securityLevel, + bool* required) const; + virtual status_t setPlaybackId(Vector const& sessionId, const char* playbackId); + virtual status_t getLogMessages(Vector& logs) const; + virtual status_t getSupportedSchemes(std::vector &schemes) const; + + ::ndk::ScopedAStatus onEvent(EventTypeAidl in_eventType, + const std::vector& in_sessionId, + const std::vector& in_data); + ::ndk::ScopedAStatus onExpirationUpdate(const std::vector& in_sessionId, + int64_t in_expiryTimeInMS); + ::ndk::ScopedAStatus onKeysChange(const std::vector& in_sessionId, + const std::vector& in_keyStatusList, + bool in_hasNewUsableKey); + ::ndk::ScopedAStatus onSessionLostState(const std::vector& in_sessionId); + private: + static Mutex mLock; + mutable MediaDrmMetrics mMetrics; + std::shared_ptr mListener; + const std::vector> mFactories; + std::shared_ptr mPlugin; + status_t mInitCheck; + std::vector> mOpenSessions; + void cleanup(); + void closeOpenSessions(); + std::string reportPluginMetrics() const; + std::string reportFrameworkMetrics(const std::string& pluginMetrics) const; + status_t getPropertyStringInternal(String8 const& name, String8& value) const; + status_t getPropertyByteArrayInternal(String8 const& name, Vector& value) const; + DISALLOW_EVIL_CONSTRUCTORS(DrmHalAidl); +}; + +} // namespace android + +#endif // DRM_HAL_AIDL_H_ \ No newline at end of file diff --git a/drm/libmediadrm/include/mediadrm/DrmHalHidl.h b/drm/libmediadrm/include/mediadrm/DrmHalHidl.h new file mode 100644 index 0000000000000000000000000000000000000000..11f06081d5f3e3425ab9c97d022e532c785a7603 --- /dev/null +++ b/drm/libmediadrm/include/mediadrm/DrmHalHidl.h @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2021 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 DRM_HAL_HIDL_H_ +#define DRM_HAL_HIDL_H_ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace drm = ::android::hardware::drm; +using drm::V1_0::EventType; +using drm::V1_0::IDrmFactory; +using drm::V1_0::IDrmPlugin; +using drm::V1_0::IDrmPluginListener; +using drm::V1_1::SecurityLevel; +using drm::V1_2::KeyStatus; +using drm::V1_2::OfflineLicenseState; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; + +typedef drm::V1_2::IDrmPluginListener IDrmPluginListener_V1_2; +typedef drm::V1_0::KeyStatus KeyStatus_V1_0; + +namespace android { + +struct DrmSessionClientInterface; + +inline bool operator==(const Vector &l, const Vector &r) { + if (l.size() != r.size()) return false; + return memcmp(l.array(), r.array(), l.size()) == 0; +} + +struct DrmHalHidl : public IDrm, + public IDrmPluginListener_V1_2 { + + struct DrmSessionClient; + + DrmHalHidl(); + virtual ~DrmHalHidl(); + + virtual status_t initCheck() const; + + virtual status_t isCryptoSchemeSupported(const uint8_t uuid[16], + const String8& mimeType, + DrmPlugin::SecurityLevel level, + bool *isSupported); + + virtual status_t createPlugin(const uint8_t uuid[16], + const String8 &appPackageName); + + virtual status_t destroyPlugin(); + + virtual status_t openSession(DrmPlugin::SecurityLevel level, + Vector &sessionId); + + virtual status_t closeSession(Vector const &sessionId); + + virtual status_t + getKeyRequest(Vector const &sessionId, + Vector const &initData, + String8 const &mimeType, DrmPlugin::KeyType keyType, + KeyedVector const &optionalParameters, + Vector &request, String8 &defaultUrl, + DrmPlugin::KeyRequestType *keyRequestType); + + virtual status_t provideKeyResponse(Vector const &sessionId, + Vector const &response, + Vector &keySetId); + + virtual status_t removeKeys(Vector const &keySetId); + + virtual status_t restoreKeys(Vector const &sessionId, + Vector const &keySetId); + + virtual status_t queryKeyStatus(Vector const &sessionId, + KeyedVector &infoMap) const; + + virtual status_t getProvisionRequest(String8 const &certType, + String8 const &certAuthority, + Vector &request, + String8 &defaultUrl); + + virtual status_t provideProvisionResponse(Vector const &response, + Vector &certificate, + Vector &wrappedKey); + + virtual status_t getSecureStops(List> &secureStops); + virtual status_t getSecureStopIds(List> &secureStopIds); + virtual status_t getSecureStop(Vector const &ssid, Vector &secureStop); + + virtual status_t releaseSecureStops(Vector const &ssRelease); + virtual status_t removeSecureStop(Vector const &ssid); + virtual status_t removeAllSecureStops(); + + virtual status_t getHdcpLevels(DrmPlugin::HdcpLevel *connectedLevel, + DrmPlugin::HdcpLevel *maxLevel) const; + virtual status_t getNumberOfSessions(uint32_t *currentSessions, + uint32_t *maxSessions) const; + virtual status_t getSecurityLevel(Vector const &sessionId, + DrmPlugin::SecurityLevel *level) const; + + virtual status_t getOfflineLicenseKeySetIds(List> &keySetIds) const; + virtual status_t removeOfflineLicense(Vector const &keySetId); + virtual status_t getOfflineLicenseState(Vector const &keySetId, + DrmPlugin::OfflineLicenseState *licenseState) const; + + virtual status_t getPropertyString(String8 const &name, String8 &value ) const; + virtual status_t getPropertyByteArray(String8 const &name, + Vector &value ) const; + virtual status_t setPropertyString(String8 const &name, String8 const &value ) const; + virtual status_t setPropertyByteArray(String8 const &name, + Vector const &value ) const; + virtual status_t getMetrics(const sp &consumer); + + virtual status_t setCipherAlgorithm(Vector const &sessionId, + String8 const &algorithm); + + virtual status_t setMacAlgorithm(Vector const &sessionId, + String8 const &algorithm); + + virtual status_t encrypt(Vector const &sessionId, + Vector const &keyId, + Vector const &input, + Vector const &iv, + Vector &output); + + virtual status_t decrypt(Vector const &sessionId, + Vector const &keyId, + Vector const &input, + Vector const &iv, + Vector &output); + + virtual status_t sign(Vector const &sessionId, + Vector const &keyId, + Vector const &message, + Vector &signature); + + virtual status_t verify(Vector const &sessionId, + Vector const &keyId, + Vector const &message, + Vector const &signature, + bool &match); + + virtual status_t signRSA(Vector const &sessionId, + String8 const &algorithm, + Vector const &message, + Vector const &wrappedKey, + Vector &signature); + + virtual status_t setListener(const sp& listener); + + virtual status_t requiresSecureDecoder(const char *mime, bool *required) const; + + virtual status_t requiresSecureDecoder(const char *mime, DrmPlugin::SecurityLevel securityLevel, + bool *required) const; + + virtual status_t setPlaybackId( + Vector const &sessionId, + const char *playbackId); + + virtual status_t getLogMessages(Vector &logs) const; + virtual status_t getSupportedSchemes(std::vector &schemes) const; + + // Methods of IDrmPluginListener + Return sendEvent(EventType eventType, + const hidl_vec& sessionId, const hidl_vec& data); + + Return sendExpirationUpdate(const hidl_vec& sessionId, + int64_t expiryTimeInMS); + + Return sendKeysChange(const hidl_vec& sessionId, + const hidl_vec& keyStatusList, bool hasNewUsableKey); + + Return sendKeysChange_1_2(const hidl_vec& sessionId, + const hidl_vec& keyStatusList, bool hasNewUsableKey); + + Return sendSessionLostState(const hidl_vec& sessionId); + +private: + static Mutex mLock; + + sp mListener; + mutable Mutex mEventLock; + mutable Mutex mNotifyLock; + + const std::vector> mFactories; + sp mPlugin; + sp mPluginV1_1; + sp mPluginV1_2; + sp mPluginV1_4; + String8 mAppPackageName; + + // Mutable to allow modification within GetPropertyByteArray. + mutable MediaDrmMetrics mMetrics; + + std::vector> mOpenSessions; + void closeOpenSessions(); + void cleanup(); + + /** + * mInitCheck is: + * NO_INIT if a plugin hasn't been created yet + * ERROR_UNSUPPORTED if a plugin can't be created for the uuid + * OK after a plugin has been created and mPlugin is valid + */ + status_t mInitCheck; + + std::vector> makeDrmFactories(); + sp makeDrmPlugin(const sp& factory, + const uint8_t uuid[16], const String8& appPackageName); + + void writeByteArray(Parcel &obj, const hidl_vec& array); + + std::string reportPluginMetrics() const; + std::string reportFrameworkMetrics(const std::string& pluginMetrics) const; + status_t getPropertyStringInternal(String8 const &name, String8 &value) const; + status_t getPropertyByteArrayInternal(String8 const &name, + Vector &value) const; + status_t matchMimeTypeAndSecurityLevel(const sp &factory, + const uint8_t uuid[16], + const String8 &mimeType, + DrmPlugin::SecurityLevel level, + bool *isSupported); + + DISALLOW_EVIL_CONSTRUCTORS(DrmHalHidl); +}; + +} // namespace android + +#endif // DRM_HAL_HIDL_H_ diff --git a/drm/libmediadrm/include/mediadrm/DrmHalListener.h b/drm/libmediadrm/include/mediadrm/DrmHalListener.h new file mode 100644 index 0000000000000000000000000000000000000000..22361adc05e28bc71172f573037a55236f5c4a22 --- /dev/null +++ b/drm/libmediadrm/include/mediadrm/DrmHalListener.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2022 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 DRM_HAL_LISTENER_H_ +#define DRM_HAL_LISTENER_H_ + +#include +#include +#include + +using EventTypeAidl = ::aidl::android::hardware::drm::EventType; +using KeyStatusAidl = ::aidl::android::hardware::drm::KeyStatus; +using aidl::android::hardware::drm::BnDrmPluginListener; + +namespace android { +struct DrmHalListener : public BnDrmPluginListener { + explicit DrmHalListener(MediaDrmMetrics* mMetrics); + ~DrmHalListener(); + ::ndk::ScopedAStatus onEvent(EventTypeAidl in_eventType, + const std::vector& in_sessionId, + const std::vector& in_data); + ::ndk::ScopedAStatus onExpirationUpdate(const std::vector& in_sessionId, + int64_t in_expiryTimeInMS); + ::ndk::ScopedAStatus onKeysChange(const std::vector& in_sessionId, + const std::vector& in_keyStatusList, + bool in_hasNewUsableKey); + ::ndk::ScopedAStatus onSessionLostState(const std::vector& in_sessionId); + void setListener(sp listener); +private: + mutable MediaDrmMetrics* mMetrics; + sp mListener; + mutable Mutex mEventLock; + mutable Mutex mNotifyLock; +}; +} // namespace android + +#endif // DRM_HAL_LISTENER_H_ \ No newline at end of file diff --git a/drm/libmediadrm/include/mediadrm/DrmMetrics.h b/drm/libmediadrm/include/mediadrm/DrmMetrics.h index 100b8f75176309a2756f76892203f243042da8e1..e1775c78579530be058a3f146f34ac7ca4d42bdf 100644 --- a/drm/libmediadrm/include/mediadrm/DrmMetrics.h +++ b/drm/libmediadrm/include/mediadrm/DrmMetrics.h @@ -50,12 +50,10 @@ class MediaDrmMetrics { CounterMetric mGetProvisionRequestCounter; // Count of provideProvisionResponse calls. CounterMetric mProvideProvisionResponseCounter; - // Count of key status events broken out by status type. - CounterMetric<::android::hardware::drm::V1_2::KeyStatusType> - mKeyStatusChangeCounter; + CounterMetric mKeyStatusChangeCounter; // Count of events broken out by event type - CounterMetric<::android::hardware::drm::V1_0::EventType> mEventCounter; + CounterMetric mEventCounter; // Count getPropertyByteArray calls to retrieve the device unique id. CounterMetric mGetDeviceUniqueIdCounter; diff --git a/drm/libmediadrm/include/mediadrm/IDrm.h b/drm/libmediadrm/include/mediadrm/IDrm.h index a88784d9cc2bdc141f96929a54475726eec708fb..ee2be6a303a855dba608302f8931a775cb288e60 100644 --- a/drm/libmediadrm/include/mediadrm/IDrm.h +++ b/drm/libmediadrm/include/mediadrm/IDrm.h @@ -165,6 +165,8 @@ struct IDrm : public virtual RefBase { virtual status_t getLogMessages(Vector &logs) const = 0; + virtual status_t getSupportedSchemes(std::vector &schemes) const = 0; + protected: IDrm() {} diff --git a/drm/libmediadrm/interface/mediadrm/DrmUtils.h b/drm/libmediadrm/interface/mediadrm/DrmUtils.h index ec0b8781dddaf40a9c8953136516e98bafcf99e5..980ce553927c0381be2eb7123cf8c5bf03077ee3 100644 --- a/drm/libmediadrm/interface/mediadrm/DrmUtils.h +++ b/drm/libmediadrm/interface/mediadrm/DrmUtils.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -38,12 +39,20 @@ #include #include #include - +#include +#include +#include using namespace ::android::hardware::drm; using ::android::hardware::hidl_vec; using ::android::hardware::Return; +using ::aidl::android::hardware::drm::LogPriority; +using ::aidl::android::hardware::drm::LogMessage; +using ::aidl::android::hardware::drm::Uuid; +using StatusAidl = ::aidl::android::hardware::drm::Status; +using IDrmFactoryAidl = ::aidl::android::hardware::drm::IDrmFactory; + namespace android { struct ICrypto; @@ -92,7 +101,8 @@ void LogToBuffer(android_LogPriority level, const char *fmt, Args... args) { template void LogToBuffer(android_LogPriority level, const uint8_t uuid[16], const char *fmt, Args... args) { - const uint64_t* uuid2 = reinterpret_cast(uuid); + uint64_t uuid2[2] = {}; + std::memcpy(uuid2, uuid, sizeof(uuid2)); std::string uuidFmt("uuid=[%lx %lx] "); uuidFmt += fmt; LogToBuffer(level, uuidFmt.c_str(), htobe64(uuid2[0]), htobe64(uuid2[1]), args...); @@ -155,6 +165,14 @@ void WriteKeysChange( obj.writeInt32(hasNewUsableKey); } +inline Uuid toAidlUuid(const uint8_t uuid[16]) { + Uuid uuidAidl; + for (int i = 0; i < 16; ++i) uuidAidl.uuid[i] = uuid[i]; + return uuidAidl; +} + +std::vector> makeDrmFactoriesAidl(); + std::vector> MakeDrmFactories(const uint8_t uuid[16] = nullptr); std::vector> MakeDrmPlugins(const uint8_t uuid[16], @@ -180,6 +198,138 @@ inline status_t toStatusT(const android::hardware::Return &status) { return toStatusT_1_4(err); } +inline status_t statusAidlToStatusT(::ndk::ScopedAStatus &statusAidl) { + if (statusAidl.isOk()) return OK; + if (statusAidl.getExceptionCode() != EX_SERVICE_SPECIFIC) return DEAD_OBJECT; + auto status = static_cast(statusAidl.getServiceSpecificError()); + switch (status) { + case StatusAidl::OK: + return OK; + case StatusAidl::BAD_VALUE: + return BAD_VALUE; + case StatusAidl::ERROR_DRM_CANNOT_HANDLE: + return ERROR_DRM_CANNOT_HANDLE; + case StatusAidl::ERROR_DRM_DECRYPT: + return ERROR_DRM_DECRYPT; + case StatusAidl::ERROR_DRM_DEVICE_REVOKED: + return ERROR_DRM_DEVICE_REVOKED; + case StatusAidl::ERROR_DRM_FRAME_TOO_LARGE: + return ERROR_DRM_FRAME_TOO_LARGE; + case StatusAidl::ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION: + return ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION; + case StatusAidl::ERROR_DRM_INSUFFICIENT_SECURITY: + return ERROR_DRM_INSUFFICIENT_SECURITY; + case StatusAidl::ERROR_DRM_INVALID_STATE: + return ERROR_DRM_INVALID_STATE; + case StatusAidl::ERROR_DRM_LICENSE_EXPIRED: + return ERROR_DRM_LICENSE_EXPIRED; + case StatusAidl::ERROR_DRM_NO_LICENSE: + return ERROR_DRM_NO_LICENSE; + case StatusAidl::ERROR_DRM_NOT_PROVISIONED: + return ERROR_DRM_NOT_PROVISIONED; + case StatusAidl::ERROR_DRM_RESOURCE_BUSY: + return ERROR_DRM_RESOURCE_BUSY; + case StatusAidl::ERROR_DRM_RESOURCE_CONTENTION: + return ERROR_DRM_RESOURCE_CONTENTION; + case StatusAidl::ERROR_DRM_SESSION_LOST_STATE: + return ERROR_DRM_SESSION_LOST_STATE; + case StatusAidl::ERROR_DRM_SESSION_NOT_OPENED: + return ERROR_DRM_SESSION_NOT_OPENED; + + // New in S / drm@1.4: + case StatusAidl::CANNOT_DECRYPT_ZERO_SUBSAMPLES: + return ERROR_DRM_ZERO_SUBSAMPLES; + case StatusAidl::CRYPTO_LIBRARY_ERROR: + return ERROR_DRM_CRYPTO_LIBRARY; + case StatusAidl::GENERAL_OEM_ERROR: + return ERROR_DRM_GENERIC_OEM; + case StatusAidl::GENERAL_PLUGIN_ERROR: + return ERROR_DRM_GENERIC_PLUGIN; + case StatusAidl::INIT_DATA_INVALID: + return ERROR_DRM_INIT_DATA; + case StatusAidl::KEY_NOT_LOADED: + return ERROR_DRM_KEY_NOT_LOADED; + case StatusAidl::LICENSE_PARSE_ERROR: + return ERROR_DRM_LICENSE_PARSE; + case StatusAidl::LICENSE_POLICY_ERROR: + return ERROR_DRM_LICENSE_POLICY; + case StatusAidl::LICENSE_RELEASE_ERROR: + return ERROR_DRM_LICENSE_RELEASE; + case StatusAidl::LICENSE_REQUEST_REJECTED: + return ERROR_DRM_LICENSE_REQUEST_REJECTED; + case StatusAidl::LICENSE_RESTORE_ERROR: + return ERROR_DRM_LICENSE_RESTORE; + case StatusAidl::LICENSE_STATE_ERROR: + return ERROR_DRM_LICENSE_STATE; + case StatusAidl::MALFORMED_CERTIFICATE: + return ERROR_DRM_CERTIFICATE_MALFORMED; + case StatusAidl::MEDIA_FRAMEWORK_ERROR: + return ERROR_DRM_MEDIA_FRAMEWORK; + case StatusAidl::MISSING_CERTIFICATE: + return ERROR_DRM_CERTIFICATE_MISSING; + case StatusAidl::PROVISIONING_CERTIFICATE_ERROR: + return ERROR_DRM_PROVISIONING_CERTIFICATE; + case StatusAidl::PROVISIONING_CONFIGURATION_ERROR: + return ERROR_DRM_PROVISIONING_CONFIG; + case StatusAidl::PROVISIONING_PARSE_ERROR: + return ERROR_DRM_PROVISIONING_PARSE; + case StatusAidl::PROVISIONING_REQUEST_REJECTED: + return ERROR_DRM_PROVISIONING_REQUEST_REJECTED; + case StatusAidl::RETRYABLE_PROVISIONING_ERROR: + return ERROR_DRM_PROVISIONING_RETRY; + case StatusAidl::SECURE_STOP_RELEASE_ERROR: + return ERROR_DRM_SECURE_STOP_RELEASE; + case StatusAidl::STORAGE_READ_FAILURE: + return ERROR_DRM_STORAGE_READ; + case StatusAidl::STORAGE_WRITE_FAILURE: + return ERROR_DRM_STORAGE_WRITE; + + case StatusAidl::ERROR_DRM_UNKNOWN: + default: + return ERROR_DRM_UNKNOWN; + } + return ERROR_DRM_UNKNOWN; +} + +template +status_t GetLogMessagesAidl(const std::shared_ptr &obj, Vector<::V1_4::LogMessage> &logs) { + std::shared_ptr plugin = obj; + if (obj == NULL) { + LOG2BW("%s obj is null", U::descriptor); + } else if (plugin == NULL) { + LOG2BW("Cannot cast %s obj to %s plugin", U::descriptor, T::descriptor); + } + + std::vector pluginLogsAidl; + if (plugin != NULL) { + if(!plugin->getLogMessages(&pluginLogsAidl).isOk()) { + LOG2BW("%s::getLogMessages remote call failed", T::descriptor); + } + } + + std::vector<::V1_4::LogMessage> pluginLogs; + for (LogMessage log : pluginLogsAidl) { + ::V1_4::LogMessage logHidl; + logHidl.timeMs = log.timeMs; + // skip negative convert check as count of enum elements is 7 + logHidl.priority = static_cast<::V1_4::LogPriority>((int32_t)log.priority); + logHidl.message = log.message; + pluginLogs.push_back(logHidl); + } + + auto allLogs(gLogBuf.getLogs()); + LOG2BD("framework logs size %zu; plugin logs size %zu", + allLogs.size(), pluginLogs.size()); + std::copy(pluginLogs.begin(), pluginLogs.end(), std::back_inserter(allLogs)); + std::sort(allLogs.begin(), allLogs.end(), + [](const ::V1_4::LogMessage &a, const ::V1_4::LogMessage &b) { + return a.timeMs < b.timeMs; + }); + + logs.appendVector(allLogs); + return OK; +} + template status_t GetLogMessages(const sp &obj, Vector<::V1_4::LogMessage> &logs) { sp plugin = T::castFrom(obj); diff --git a/drm/libmediadrm/tests/DrmMetrics_test.cpp b/drm/libmediadrm/tests/DrmMetrics_test.cpp index f362d6051847649e2a851c67e0ae27b3be674a69..237a88bd3885202806762b632a1a2f4eef21c5f3 100644 --- a/drm/libmediadrm/tests/DrmMetrics_test.cpp +++ b/drm/libmediadrm/tests/DrmMetrics_test.cpp @@ -83,8 +83,8 @@ TEST_F(MediaDrmMetricsTest, AllValuesSuccessCounts) { metrics.mProvideProvisionResponseCounter.Increment(OK); metrics.mGetDeviceUniqueIdCounter.Increment(OK); - metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::USABLE); - metrics.mEventCounter.Increment(EventType::PROVISION_REQUIRED); + metrics.mKeyStatusChangeCounter.Increment((uint32_t)KeyStatusType::USABLE); + metrics.mEventCounter.Increment((uint32_t)EventType::PROVISION_REQUIRED); PersistableBundle bundle; DrmMetricsConsumer consumer(&bundle); @@ -151,16 +151,16 @@ TEST_F(MediaDrmMetricsTest, AllValuesFull) { metrics.mGetDeviceUniqueIdCounter.Increment(OK); metrics.mGetDeviceUniqueIdCounter.Increment(UNEXPECTED_NULL); - metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::USABLE); - metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::EXPIRED); - metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::OUTPUTNOTALLOWED); - metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::STATUSPENDING); - metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::INTERNALERROR); - metrics.mEventCounter.Increment(EventType::PROVISION_REQUIRED); - metrics.mEventCounter.Increment(EventType::KEY_NEEDED); - metrics.mEventCounter.Increment(EventType::KEY_EXPIRED); - metrics.mEventCounter.Increment(EventType::VENDOR_DEFINED); - metrics.mEventCounter.Increment(EventType::SESSION_RECLAIMED); + metrics.mKeyStatusChangeCounter.Increment((uint32_t)KeyStatusType::USABLE); + metrics.mKeyStatusChangeCounter.Increment((uint32_t)KeyStatusType::EXPIRED); + metrics.mKeyStatusChangeCounter.Increment((uint32_t)KeyStatusType::OUTPUTNOTALLOWED); + metrics.mKeyStatusChangeCounter.Increment((uint32_t)KeyStatusType::STATUSPENDING); + metrics.mKeyStatusChangeCounter.Increment((uint32_t)KeyStatusType::INTERNALERROR); + metrics.mEventCounter.Increment((uint32_t)EventType::PROVISION_REQUIRED); + metrics.mEventCounter.Increment((uint32_t)EventType::KEY_NEEDED); + metrics.mEventCounter.Increment((uint32_t)EventType::KEY_EXPIRED); + metrics.mEventCounter.Increment((uint32_t)EventType::VENDOR_DEFINED); + metrics.mEventCounter.Increment((uint32_t)EventType::SESSION_RECLAIMED); android::Vector sessionId1; sessionId1.push_back(1); @@ -284,16 +284,16 @@ TEST_F(MediaDrmMetricsTest, CounterValuesProtoSerialization) { metrics.mGetDeviceUniqueIdCounter.Increment(OK); metrics.mGetDeviceUniqueIdCounter.Increment(UNEXPECTED_NULL); - metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::USABLE); - metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::EXPIRED); - metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::OUTPUTNOTALLOWED); - metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::STATUSPENDING); - metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::INTERNALERROR); - metrics.mEventCounter.Increment(EventType::PROVISION_REQUIRED); - metrics.mEventCounter.Increment(EventType::KEY_NEEDED); - metrics.mEventCounter.Increment(EventType::KEY_EXPIRED); - metrics.mEventCounter.Increment(EventType::VENDOR_DEFINED); - metrics.mEventCounter.Increment(EventType::SESSION_RECLAIMED); + metrics.mKeyStatusChangeCounter.Increment((uint32_t)KeyStatusType::USABLE); + metrics.mKeyStatusChangeCounter.Increment((uint32_t)KeyStatusType::EXPIRED); + metrics.mKeyStatusChangeCounter.Increment((uint32_t)KeyStatusType::OUTPUTNOTALLOWED); + metrics.mKeyStatusChangeCounter.Increment((uint32_t)KeyStatusType::STATUSPENDING); + metrics.mKeyStatusChangeCounter.Increment((uint32_t)KeyStatusType::INTERNALERROR); + metrics.mEventCounter.Increment((uint32_t)EventType::PROVISION_REQUIRED); + metrics.mEventCounter.Increment((uint32_t)EventType::KEY_NEEDED); + metrics.mEventCounter.Increment((uint32_t)EventType::KEY_EXPIRED); + metrics.mEventCounter.Increment((uint32_t)EventType::VENDOR_DEFINED); + metrics.mEventCounter.Increment((uint32_t)EventType::SESSION_RECLAIMED); std::string serializedMetrics; ASSERT_EQ(OK, metrics.GetSerializedMetrics(&serializedMetrics)); diff --git a/drm/mediadrm/plugins/clearkey/aidl/Android.bp b/drm/mediadrm/plugins/clearkey/aidl/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..2d1f7418c2636ca7bcc54bd2c5e79c1e26131071 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/aidl/Android.bp @@ -0,0 +1,71 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "hardware_interfaces_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_av_license"], +} + +cc_defaults { + name: "aidl_clearkey_service_defaults", + vendor: true, + + srcs: [ + "CreatePluginFactories.cpp", + "CryptoPlugin.cpp", + "DrmFactory.cpp", + "DrmPlugin.cpp", + ], + + relative_install_path: "hw", + + cflags: ["-Wall", "-Werror", "-Wthread-safety"], + + include_dirs: ["frameworks/av/include"], + + shared_libs: [ + "libbase", + "libbinder_ndk", + "libcrypto", + "liblog", + "libprotobuf-cpp-lite", + "libutils", + "android.hardware.drm-V1-ndk", + ], + + static_libs: [ + "android.hardware.common-V2-ndk", + "libclearkeybase", + ], + + local_include_dirs: ["include"], + + sanitize: { + integer_overflow: true, + }, +} + +cc_binary { + name: "android.hardware.drm-service.clearkey", + defaults: ["aidl_clearkey_service_defaults"], + srcs: ["Service.cpp"], + init_rc: ["android.hardware.drm-service.clearkey.rc"], + vintf_fragments: ["android.hardware.drm-service.clearkey.xml"], +} + +cc_binary { + name: "android.hardware.drm-service-lazy.clearkey", + defaults: ["aidl_clearkey_service_defaults"], + overrides: ["android.hardware.drm-service.clearkey"], + srcs: ["ServiceLazy.cpp"], + init_rc: ["android.hardware.drm-service-lazy.clearkey.rc"], + vintf_fragments: ["android.hardware.drm-service.clearkey.xml"], +} + +phony { + name: "android.hardware.drm@latest-service.clearkey", + required: [ + "android.hardware.drm-service.clearkey", + ], +} diff --git a/drm/mediadrm/plugins/clearkey/aidl/CreatePluginFactories.cpp b/drm/mediadrm/plugins/clearkey/aidl/CreatePluginFactories.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4e6fe3a36585489ea45b1eda78231313e11a50c1 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/aidl/CreatePluginFactories.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "CreatePluginFactories.h" + +namespace aidl { +namespace android { +namespace hardware { +namespace drm { +namespace clearkey { + +std::shared_ptr createDrmFactory() { + return ::ndk::SharedRefBase::make(); +} + +} // namespace clearkey +} // namespace drm +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/drm/mediadrm/plugins/clearkey/aidl/CryptoPlugin.cpp b/drm/mediadrm/plugins/clearkey/aidl/CryptoPlugin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..201cf022f48bb3463346145e76f7af1717be07e5 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/aidl/CryptoPlugin.cpp @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2021 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 "clearkey-CryptoPlugin" + +#include +#include +#include + +#include "CryptoPlugin.h" +#include "SessionLibrary.h" +#include "AidlUtils.h" + +namespace aidl { +namespace android { +namespace hardware { +namespace drm { +namespace clearkey { + +using ::aidl::android::hardware::drm::Status; + +::ndk::ScopedAStatus CryptoPlugin::decrypt(const DecryptArgs& in_args, int32_t* _aidl_return) { + const char* detailedError = ""; + + *_aidl_return = 0; + if (in_args.secure) { + detailedError = "secure decryption is not supported with ClearKey"; + return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE, detailedError); + } + + std::lock_guard shared_buffer_lock(mSharedBufferLock); + if (mSharedBufferMap.find(in_args.source.bufferId) == mSharedBufferMap.end()) { + detailedError = "source decrypt buffer base not set"; + return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE, detailedError); + } + + const auto NON_SECURE = DestinationBuffer::Tag::nonsecureMemory; + if (in_args.destination.getTag() != NON_SECURE) { + detailedError = "destination type not supported"; + return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE, detailedError); + } + + const SharedBuffer& destBuffer = in_args.destination.get(); + if (mSharedBufferMap.find(destBuffer.bufferId) == mSharedBufferMap.end()) { + detailedError = "destination decrypt buffer base not set"; + return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE, detailedError); + } + + auto src = mSharedBufferMap[in_args.source.bufferId]; + if (src->mBase == nullptr) { + detailedError = "source is a nullptr"; + return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE, detailedError); + } + + size_t totalSize = 0; + if (__builtin_add_overflow(in_args.source.offset, in_args.offset, &totalSize) || + __builtin_add_overflow(totalSize, in_args.source.size, &totalSize) || + totalSize > src->mSize) { + android_errorWriteLog(0x534e4554, "176496160"); + detailedError = "invalid buffer size"; + return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE, detailedError); + } + + // destination type must be non-secure shared memory + auto dest = mSharedBufferMap[destBuffer.bufferId]; + if (dest->mBase == nullptr) { + detailedError = "destination is a nullptr"; + return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE, detailedError); + } + + totalSize = 0; + if (__builtin_add_overflow(destBuffer.offset, destBuffer.size, &totalSize) || + totalSize > dest->mSize) { + android_errorWriteLog(0x534e4554, "176444622"); + detailedError = "invalid buffer size"; + return toNdkScopedAStatus(Status::ERROR_DRM_FRAME_TOO_LARGE, detailedError); + } + + // Calculate the output buffer size and determine if any subsamples are + // encrypted. + uint8_t* srcPtr = src->mBase + in_args.source.offset + in_args.offset; + uint8_t* destPtr = dest->mBase + destBuffer.offset; + size_t destSize = 0; + size_t srcSize = 0; + bool haveEncryptedSubsamples = false; + for (size_t i = 0; i < in_args.subSamples.size(); i++) { + const SubSample& subSample = in_args.subSamples[i]; + if (__builtin_add_overflow(destSize, subSample.numBytesOfClearData, &destSize) || + __builtin_add_overflow(srcSize, subSample.numBytesOfClearData, &srcSize)) { + detailedError = "subsample clear size overflow"; + return toNdkScopedAStatus(Status::ERROR_DRM_FRAME_TOO_LARGE, detailedError); + } + if (__builtin_add_overflow(destSize, subSample.numBytesOfEncryptedData, &destSize) || + __builtin_add_overflow(srcSize, subSample.numBytesOfEncryptedData, &srcSize)) { + detailedError = "subsample encrypted size overflow"; + return toNdkScopedAStatus(Status::ERROR_DRM_FRAME_TOO_LARGE, detailedError); + } + if (subSample.numBytesOfEncryptedData > 0) { + haveEncryptedSubsamples = true; + } + } + + if (destSize > destBuffer.size || srcSize > in_args.source.size) { + detailedError = "subsample sum too large"; + return toNdkScopedAStatus(Status::ERROR_DRM_FRAME_TOO_LARGE, detailedError); + } + + if (in_args.mode == Mode::UNENCRYPTED) { + if (haveEncryptedSubsamples) { + detailedError = "Encrypted subsamples found in allegedly unencrypted data."; + return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE, detailedError); + } + + size_t offset = 0; + for (size_t i = 0; i < in_args.subSamples.size(); ++i) { + const SubSample& subSample = in_args.subSamples[i]; + if (subSample.numBytesOfClearData != 0) { + memcpy(reinterpret_cast(destPtr) + offset, + reinterpret_cast(srcPtr) + offset, + subSample.numBytesOfClearData); + offset += subSample.numBytesOfClearData; + } + } + + *_aidl_return = static_cast(offset); + return toNdkScopedAStatus(Status::OK); + } else if (in_args.mode == Mode::AES_CTR) { + size_t bytesDecrypted{}; + std::vector clearDataLengths; + std::vector encryptedDataLengths; + for (auto ss : in_args.subSamples) { + clearDataLengths.push_back(ss.numBytesOfClearData); + encryptedDataLengths.push_back(ss.numBytesOfEncryptedData); + } + auto res = + mSession->decrypt(in_args.keyId.data(), in_args.iv.data(), + srcPtr, static_cast(destPtr), + clearDataLengths, encryptedDataLengths, + &bytesDecrypted); + if (res == clearkeydrm::OK) { + *_aidl_return = static_cast(bytesDecrypted); + return toNdkScopedAStatus(Status::OK); + } else { + *_aidl_return = 0; + detailedError = "Decryption Error"; + return toNdkScopedAStatus(static_cast(res), detailedError); + } + } else { + *_aidl_return = 0; + detailedError = "selected encryption mode is not supported by the ClearKey DRM Plugin"; + return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE, detailedError); + } +} + +::ndk::ScopedAStatus CryptoPlugin::getLogMessages( + std::vector<::aidl::android::hardware::drm::LogMessage>* _aidl_return) { + using std::chrono::duration_cast; + using std::chrono::milliseconds; + using std::chrono::system_clock; + + auto timeMillis = duration_cast(system_clock::now().time_since_epoch()).count(); + + std::vector<::aidl::android::hardware::drm::LogMessage> logs = { + {timeMillis, ::aidl::android::hardware::drm::LogPriority::ERROR, + std::string("Not implemented")}}; + *_aidl_return = logs; + return toNdkScopedAStatus(Status::OK); +} + +::ndk::ScopedAStatus CryptoPlugin::notifyResolution(int32_t in_width, int32_t in_height) { + UNUSED(in_width); + UNUSED(in_height); + return ::ndk::ScopedAStatus::ok(); +} + +::ndk::ScopedAStatus CryptoPlugin::requiresSecureDecoderComponent(const std::string& in_mime, + bool* _aidl_return) { + UNUSED(in_mime); + *_aidl_return = false; + return ::ndk::ScopedAStatus::ok(); +} + +::ndk::ScopedAStatus CryptoPlugin::setMediaDrmSession(const std::vector& in_sessionId) { + Status status = Status::OK; + if (!in_sessionId.size()) { + mSession = nullptr; + } else { + mSession = SessionLibrary::get()->findSession(in_sessionId); + if (!mSession.get()) { + status = Status::ERROR_DRM_SESSION_NOT_OPENED; + } + } + return toNdkScopedAStatus(status); +} + +::ndk::ScopedAStatus CryptoPlugin::setSharedBufferBase(const SharedBuffer& in_base) { + std::lock_guard shared_buffer_lock(mSharedBufferLock); + mSharedBufferMap[in_base.bufferId] = std::make_shared(in_base); + return ::ndk::ScopedAStatus::ok(); +} + +SharedBufferBase::SharedBufferBase(const SharedBuffer& mem) + : mBase(nullptr), + mSize(mem.size) { + if (mem.handle.fds.empty()) { + return; + } + auto fd = mem.handle.fds[0].get(); + auto addr = mmap(nullptr, mem.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) { + ALOGE("mmap err: fd %d; errno %s", fd, strerror(errno)); + } else { + mBase = static_cast(addr); + } +} + +SharedBufferBase::~SharedBufferBase() { + if (mBase && munmap(mBase, mSize)) { + ALOGE("munmap err: base %p; errno %s", + mBase, strerror(errno)); + } +} +} // namespace clearkey +} // namespace drm +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/drm/mediadrm/plugins/clearkey/aidl/DrmFactory.cpp b/drm/mediadrm/plugins/clearkey/aidl/DrmFactory.cpp new file mode 100644 index 0000000000000000000000000000000000000000..30db4c0e290c6dc52ebca0b3c6d8768267b14364 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/aidl/DrmFactory.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2021 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 "clearkey-DrmFactory" + +#include + +#include "DrmFactory.h" + +#include "ClearKeyUUID.h" +#include "CryptoPlugin.h" +#include "DrmPlugin.h" +#include "MimeTypeStdStr.h" +#include "SessionLibrary.h" +#include "AidlUtils.h" + +namespace aidl { +namespace android { +namespace hardware { +namespace drm { +namespace clearkey { + +using std::string; +using std::vector; + +using ::aidl::android::hardware::drm::SecurityLevel; +using ::aidl::android::hardware::drm::Status; +using ::aidl::android::hardware::drm::Uuid; + +::ndk::ScopedAStatus DrmFactory::createDrmPlugin( + const Uuid& in_uuid, const string& in_appPackageName, + std::shared_ptr<::aidl::android::hardware::drm::IDrmPlugin>* _aidl_return) { + UNUSED(in_appPackageName); + + if (!isClearKeyUUID(in_uuid.uuid.data())) { + ALOGE("Clearkey Drm HAL: failed to create drm plugin, " + "invalid crypto scheme"); + *_aidl_return = nullptr; + return toNdkScopedAStatus(Status::BAD_VALUE); + } + + std::shared_ptr plugin = + ::ndk::SharedRefBase::make(SessionLibrary::get()); + *_aidl_return = plugin; + return toNdkScopedAStatus(Status::OK); +} + +::ndk::ScopedAStatus DrmFactory::createCryptoPlugin( + const Uuid& in_uuid, const std::vector& in_initData, + std::shared_ptr<::aidl::android::hardware::drm::ICryptoPlugin>* _aidl_return) { + if (!isClearKeyUUID(in_uuid.uuid.data())) { + ALOGE("Clearkey Drm HAL: failed to create crypto plugin, " + "invalid crypto scheme"); + *_aidl_return = nullptr; + return toNdkScopedAStatus(Status::BAD_VALUE); + } + + std::shared_ptr plugin = ::ndk::SharedRefBase::make(in_initData); + Status status = plugin->getInitStatus(); + if (status != Status::OK) { + plugin.reset(); + plugin = nullptr; + } + *_aidl_return = plugin; + return toNdkScopedAStatus(status); +} + +::ndk::ScopedAStatus DrmFactory::getSupportedCryptoSchemes(CryptoSchemes* _aidl_return) { + CryptoSchemes schemes{}; + for (const auto& uuid : ::aidl::android::hardware::drm::clearkey::getSupportedCryptoSchemes()) { + schemes.uuids.push_back({uuid}); + } + for (auto mime : {kIsoBmffVideoMimeType, kIsoBmffAudioMimeType, kCencInitDataFormat, + kWebmVideoMimeType, kWebmAudioMimeType, kWebmInitDataFormat}) { + const auto minLevel = SecurityLevel::SW_SECURE_CRYPTO; + const auto maxLevel = SecurityLevel::SW_SECURE_CRYPTO; + schemes.mimeTypes.push_back({mime, minLevel, maxLevel}); + } + *_aidl_return = schemes; + return ndk::ScopedAStatus::ok(); +} + +binder_status_t DrmFactory::dump(int fd, const char** args, uint32_t numArgs) { + UNUSED(args); + UNUSED(numArgs); + + if (fd < 0) { + ALOGE("%s: negative fd", __FUNCTION__); + return STATUS_BAD_VALUE; + } + + uint32_t currentSessions = SessionLibrary::get()->numOpenSessions(); + dprintf(fd, "current open sessions: %u\n", currentSessions); + + return STATUS_OK; +} + +} // namespace clearkey +} // namespace drm +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/drm/mediadrm/plugins/clearkey/aidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/aidl/DrmPlugin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ea51e9d2799142aad6a3a1e09cc95355a504e090 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/aidl/DrmPlugin.cpp @@ -0,0 +1,1029 @@ +/* + * Copyright (C) 2021 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 "clearkey-DrmPlugin" + +#include +#include + +#include +#include +#include +#include + +#include "AidlUtils.h" +#include "ClearKeyDrmProperties.h" +#include "DrmPlugin.h" +#include "Session.h" +#include "Utils.h" +#include "AidlClearKeryProperties.h" + +namespace { +const std::string kKeySetIdPrefix("ckid"); +const int kKeySetIdLength = 16; +const int kSecureStopIdStart = 100; +const std::string kOfflineLicense("\"type\":\"persistent-license\""); +const std::string kStreaming("Streaming"); +const std::string kTemporaryLicense("\"type\":\"temporary\""); +const std::string kTrue("True"); + +const std::string kQueryKeyLicenseType("LicenseType"); +// Value: "Streaming" or "Offline" +const std::string kQueryKeyPlayAllowed("PlayAllowed"); +// Value: "True" or "False" +const std::string kQueryKeyRenewAllowed("RenewAllowed"); +// Value: "True" or "False" + +const int kSecureStopIdSize = 10; + +std::vector uint32ToVector(uint32_t value) { + // 10 bytes to display max value 4294967295 + one byte null terminator + char buffer[kSecureStopIdSize]; + memset(buffer, 0, kSecureStopIdSize); + snprintf(buffer, kSecureStopIdSize, "%" PRIu32, value); + return std::vector(buffer, buffer + sizeof(buffer)); +} + +}; // unnamed namespace + +namespace aidl { +namespace android { +namespace hardware { +namespace drm { +namespace clearkey { + +using ::android::Mutex; + +DrmPlugin::DrmPlugin(SessionLibrary* sessionLibrary) + : mSessionLibrary(sessionLibrary), + mOpenSessionOkCount(0), + mCloseSessionOkCount(0), + mCloseSessionNotOpenedCount(0), + mNextSecureStopId(kSecureStopIdStart), + mMockError(Status::OK) { + mPlayPolicy.clear(); + initProperties(); + mSecureStops.clear(); + mReleaseKeysMap.clear(); + std::srand(std::time(nullptr)); +} + +void DrmPlugin::initProperties() { + mStringProperties.clear(); + mStringProperties[kVendorKey] = kAidlVendorValue; + mStringProperties[kVersionKey] = kAidlVersionValue; + mStringProperties[kPluginDescriptionKey] = kAidlPluginDescriptionValue; + mStringProperties[kAlgorithmsKey] = kAidlAlgorithmsValue; + mStringProperties[kListenerTestSupportKey] = kAidlListenerTestSupportValue; + mStringProperties[kDrmErrorTestKey] = kAidlDrmErrorTestValue; + mStringProperties[kAidlVersionKey] = kAidlVersionValue; + + std::vector valueVector; + valueVector.clear(); + valueVector.insert(valueVector.end(), kTestDeviceIdData, + kTestDeviceIdData + sizeof(kTestDeviceIdData) / sizeof(uint8_t)); + mByteArrayProperties[kDeviceIdKey] = valueVector; + + valueVector.clear(); + valueVector.insert(valueVector.end(), kMetricsData, + kMetricsData + sizeof(kMetricsData) / sizeof(uint8_t)); + mByteArrayProperties[kMetricsKey] = valueVector; +} + +// The secure stop in ClearKey implementation is not installed securely. +// This function merely creates a test environment for testing secure stops APIs. +// The content in this secure stop is implementation dependent, the clearkey +// secureStop does not serve as a reference implementation. +void DrmPlugin::installSecureStop(const std::vector& sessionId) { + ::android::Mutex::Autolock lock(mSecureStopLock); + + ClearkeySecureStop clearkeySecureStop; + clearkeySecureStop.id = uint32ToVector(++mNextSecureStopId); + clearkeySecureStop.data.assign(sessionId.begin(), sessionId.end()); + + mSecureStops.insert(std::pair, ClearkeySecureStop>(clearkeySecureStop.id, + clearkeySecureStop)); +} + +::ndk::ScopedAStatus DrmPlugin::closeSession(const std::vector& in_sessionId) { + if (in_sessionId.size() == 0) { + return toNdkScopedAStatus(Status::BAD_VALUE); + } + + ::android::sp session = mSessionLibrary->findSession(in_sessionId); + if (session.get()) { + mSessionLibrary->destroySession(session); + if (session->getMockError() != clearkeydrm::OK) { + sendSessionLostState(in_sessionId); + return toNdkScopedAStatus(Status::ERROR_DRM_INVALID_STATE); + } + mCloseSessionOkCount++; + return toNdkScopedAStatus(Status::OK); + } + mCloseSessionNotOpenedCount++; + return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); +} + +::ndk::ScopedAStatus DrmPlugin::decrypt(const std::vector& in_sessionId, + const std::vector& in_keyId, + const std::vector& in_input, + const std::vector& in_iv, + std::vector* _aidl_return) { + *_aidl_return = {}; + if (in_sessionId.size() == 0 || in_keyId.size() == 0 || in_input.size() == 0 || + in_iv.size() == 0) { + return toNdkScopedAStatus(Status::BAD_VALUE); + } + return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); +} + +::ndk::ScopedAStatus DrmPlugin::encrypt(const std::vector& in_sessionId, + const std::vector& in_keyId, + const std::vector& in_input, + const std::vector& in_iv, + std::vector* _aidl_return) { + *_aidl_return = {}; + if (in_sessionId.size() == 0 || in_keyId.size() == 0 || in_input.size() == 0 || + in_iv.size() == 0) { + return toNdkScopedAStatus(Status::BAD_VALUE); + } + return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); +} + +::ndk::ScopedAStatus DrmPlugin::getHdcpLevels( + ::aidl::android::hardware::drm::HdcpLevels* _aidl_return) { + _aidl_return->connectedLevel = HdcpLevel::HDCP_NONE; + _aidl_return->maxLevel = HdcpLevel::HDCP_NO_OUTPUT; + return toNdkScopedAStatus(Status::OK); +} + +::ndk::ScopedAStatus DrmPlugin::getKeyRequest( + const std::vector& in_scope, const std::vector& in_initData, + const std::string& in_mimeType, ::aidl::android::hardware::drm::KeyType in_keyType, + const std::vector<::aidl::android::hardware::drm::KeyValue>& in_optionalParameters, + ::aidl::android::hardware::drm::KeyRequest* _aidl_return) { + UNUSED(in_optionalParameters); + + KeyRequestType keyRequestType = KeyRequestType::UNKNOWN; + std::string defaultUrl("https://default.url"); + + _aidl_return->request = {}; + _aidl_return->requestType = keyRequestType; + _aidl_return->defaultUrl = defaultUrl; + + if (in_scope.size() == 0 || + (in_keyType != KeyType::STREAMING && in_keyType != KeyType::OFFLINE && + in_keyType != KeyType::RELEASE)) { + return toNdkScopedAStatus(Status::BAD_VALUE); + } + + const std::vector scopeId = in_scope; + ::android::sp session; + std::set init_types{KeyType::STREAMING, KeyType::OFFLINE}; + if (init_types.count(in_keyType)) { + std::vector sessionId(scopeId.begin(), scopeId.end()); + session = mSessionLibrary->findSession(sessionId); + if (!session.get()) { + return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); + } else if (session->getMockError() != clearkeydrm::OK) { + return toNdkScopedAStatus(session->getMockError()); + } + keyRequestType = KeyRequestType::INITIAL; + } + + std::vector request = {}; + auto keyType = static_cast(in_keyType); + auto status = session->getKeyRequest(in_initData, in_mimeType, keyType, &request); + + if (in_keyType == KeyType::RELEASE) { + std::vector keySetId(scopeId.begin(), scopeId.end()); + std::string requestString(request.begin(), request.end()); + if (requestString.find(kOfflineLicense) != std::string::npos) { + std::string emptyResponse; + std::string keySetIdString(keySetId.begin(), keySetId.end()); + if (!mFileHandle.StoreLicense(keySetIdString, DeviceFiles::kLicenseStateReleasing, + emptyResponse)) { + ALOGE("Problem releasing offline license"); + return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); + } + if (mReleaseKeysMap.find(keySetIdString) == mReleaseKeysMap.end()) { + ::android::sp session = mSessionLibrary->createSession(); + mReleaseKeysMap[keySetIdString] = session->sessionId(); + } else { + ALOGI("key is in use, ignore release request"); + } + } else { + ALOGE("Offline license not found, nothing to release"); + } + keyRequestType = KeyRequestType::RELEASE; + } + _aidl_return->request = request; + _aidl_return->requestType = keyRequestType; + _aidl_return->defaultUrl = defaultUrl; + return toNdkScopedAStatus(status); +} + +::ndk::ScopedAStatus DrmPlugin::getLogMessages( + std::vector<::aidl::android::hardware::drm::LogMessage>* _aidl_return) { + using std::chrono::duration_cast; + using std::chrono::milliseconds; + using std::chrono::system_clock; + + auto timeMillis = duration_cast(system_clock::now().time_since_epoch()).count(); + + std::vector<::aidl::android::hardware::drm::LogMessage> logs = { + {timeMillis, ::aidl::android::hardware::drm::LogPriority::ERROR, + std::string("Not implemented")}}; + *_aidl_return = logs; + return toNdkScopedAStatus(Status::OK); +} + +::ndk::ScopedAStatus DrmPlugin::getMetrics( + std::vector<::aidl::android::hardware::drm::DrmMetricGroup>* _aidl_return) { + // Set the open session count metric. + DrmMetricNamedValue openSessionOkAttribute = {"status", static_cast(Status::OK)}; + DrmMetricNamedValue openSessionMetricValue = {"count", mOpenSessionOkCount}; + DrmMetric openSessionMetric = { + "open_session", {openSessionOkAttribute}, {openSessionMetricValue}}; + + // Set the close session count metric. + DrmMetricNamedValue closeSessionOkAttribute = {"status", static_cast(Status::OK)}; + DrmMetricNamedValue closeSessionMetricValue = {"count", mCloseSessionOkCount}; + DrmMetric closeSessionMetric = { + "close_session", {closeSessionOkAttribute}, {closeSessionMetricValue}}; + + // Set the close session, not opened metric. + DrmMetricNamedValue closeSessionNotOpenedAttribute = {"status", + static_cast(Status::ERROR_DRM_SESSION_NOT_OPENED)}; + DrmMetricNamedValue closeSessionNotOpenedMetricValue = {"count", mCloseSessionNotOpenedCount}; + DrmMetric closeSessionNotOpenedMetric = { + "close_session", {closeSessionNotOpenedAttribute}, {closeSessionNotOpenedMetricValue}}; + + // Set the setPlaybackId metric. + std::vector sids = {}; + std::vector playbackIds = {}; + for (const auto& [key, value] : mPlaybackId) { + std::string sid(key.begin(), key.end()); + DrmMetricNamedValue sessionIdAttribute = {"sid", sid}; + sids.push_back(sessionIdAttribute); + + DrmMetricNamedValue playbackIdMetricValue = {"playbackId", value}; + playbackIds.push_back(playbackIdMetricValue); + } + DrmMetric setPlaybackIdMetric = {"set_playback_id", sids, playbackIds}; + + DrmMetricGroup metrics = {{openSessionMetric, closeSessionMetric, closeSessionNotOpenedMetric, + setPlaybackIdMetric}}; + + *_aidl_return = {metrics}; + return toNdkScopedAStatus(Status::OK); +} + +::ndk::ScopedAStatus DrmPlugin::getNumberOfSessions( + ::aidl::android::hardware::drm::NumberOfSessions* _aidl_return) { + _aidl_return->currentSessions = mSessionLibrary->numOpenSessions(); + _aidl_return->maxSessions = 10; + return toNdkScopedAStatus(Status::OK); +} + +::ndk::ScopedAStatus DrmPlugin::getOfflineLicenseKeySetIds( + std::vector<::aidl::android::hardware::drm::KeySetId>* _aidl_return) { + std::vector licenseNames = mFileHandle.ListLicenses(); + std::vector keySetIds = {}; + if (mMockError != Status::OK) { + *_aidl_return = keySetIds; + return toNdkScopedAStatus(toMockStatus(mMockError)); + } + for (const auto& name : licenseNames) { + std::vector keySetId(name.begin(), name.end()); + KeySetId id = {}; + id.keySetId = keySetId; + keySetIds.push_back(id); + } + *_aidl_return = keySetIds; + return toNdkScopedAStatus(Status::OK); +} + +::ndk::ScopedAStatus DrmPlugin::getOfflineLicenseState( + const ::aidl::android::hardware::drm::KeySetId& in_keySetId, + ::aidl::android::hardware::drm::OfflineLicenseState* _aidl_return) { + std::string licenseName(in_keySetId.keySetId.begin(), in_keySetId.keySetId.end()); + DeviceFiles::LicenseState state; + std::string license; + OfflineLicenseState licenseState = OfflineLicenseState::UNKNOWN; + Status status = Status::OK; + if (mMockError != Status::OK) { + *_aidl_return = licenseState; + return toNdkScopedAStatus(toMockStatus(mMockError)); + } else if (mFileHandle.RetrieveLicense(licenseName, &state, &license)) { + switch (state) { + case DeviceFiles::kLicenseStateActive: + licenseState = OfflineLicenseState::USABLE; + break; + case DeviceFiles::kLicenseStateReleasing: + licenseState = OfflineLicenseState::INACTIVE; + break; + case DeviceFiles::kLicenseStateUnknown: + licenseState = OfflineLicenseState::UNKNOWN; + break; + } + } else { + status = Status::BAD_VALUE; + } + *_aidl_return = licenseState; + return toNdkScopedAStatus(status); +} + +::ndk::ScopedAStatus DrmPlugin::getPropertyByteArray(const std::string& in_propertyName, + std::vector* _aidl_return) { + std::map>::iterator itr = + mByteArrayProperties.find(std::string(in_propertyName.c_str())); + Status status = Status::OK; + if (itr != mByteArrayProperties.end()) { + *_aidl_return = itr->second; + } else { + ALOGE("App requested unknown property: %s", in_propertyName.c_str()); + status = Status::BAD_VALUE; + *_aidl_return = {}; + } + return toNdkScopedAStatus(status); +} + +::ndk::ScopedAStatus DrmPlugin::getPropertyString(const std::string& in_propertyName, + std::string* _aidl_return) { + std::string name(in_propertyName.c_str()); + std::string value; + Status status = Status::OK; + + if (name == kVendorKey) { + value = mStringProperties[kVendorKey]; + } else if (name == kVersionKey) { + value = mStringProperties[kVersionKey]; + } else if (name == kPluginDescriptionKey) { + value = mStringProperties[kPluginDescriptionKey]; + } else if (name == kAlgorithmsKey) { + value = mStringProperties[kAlgorithmsKey]; + } else if (name == kListenerTestSupportKey) { + value = mStringProperties[kListenerTestSupportKey]; + } else if (name == kDrmErrorTestKey) { + value = mStringProperties[kDrmErrorTestKey]; + } else if (name == kAidlVersionKey) { + value = mStringProperties[kAidlVersionKey]; + } else { + ALOGE("App requested unknown string property %s", name.c_str()); + status = Status::ERROR_DRM_CANNOT_HANDLE; + } + *_aidl_return = value; + return toNdkScopedAStatus(status); +} + +::ndk::ScopedAStatus DrmPlugin::getProvisionRequest( + const std::string& in_certificateType, const std::string& in_certificateAuthority, + ::aidl::android::hardware::drm::ProvisionRequest* _aidl_return) { + UNUSED(in_certificateType); + UNUSED(in_certificateAuthority); + _aidl_return->request = {}; + _aidl_return->defaultUrl = {}; + return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); +} + +::ndk::ScopedAStatus DrmPlugin::getSecureStop( + const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId, + ::aidl::android::hardware::drm::SecureStop* _aidl_return) { + std::vector stop = {}; + + mSecureStopLock.lock(); + auto itr = mSecureStops.find(in_secureStopId.secureStopId); + if (itr != mSecureStops.end()) { + ClearkeySecureStop clearkeyStop = itr->second; + stop.insert(stop.end(), clearkeyStop.id.begin(), clearkeyStop.id.end()); + stop.insert(stop.end(), clearkeyStop.data.begin(), clearkeyStop.data.end()); + } + mSecureStopLock.unlock(); + + SecureStop secureStop = {}; + Status status = Status::OK; + if (!stop.empty()) { + secureStop.opaqueData = stop; + } else { + status = Status::BAD_VALUE; + } + *_aidl_return = secureStop; + return toNdkScopedAStatus(status); +} + +::ndk::ScopedAStatus DrmPlugin::getSecureStopIds( + std::vector<::aidl::android::hardware::drm::SecureStopId>* _aidl_return) { + mSecureStopLock.lock(); + std::vector<::aidl::android::hardware::drm::SecureStopId> ids; + for (auto itr = mSecureStops.begin(); itr != mSecureStops.end(); ++itr) { + SecureStopId id; + id.secureStopId = itr->first; + ids.push_back(id); + } + mSecureStopLock.unlock(); + + *_aidl_return = ids; + return toNdkScopedAStatus(Status::OK); +} + +::ndk::ScopedAStatus DrmPlugin::getSecureStops( + std::vector<::aidl::android::hardware::drm::SecureStop>* _aidl_return) { + mSecureStopLock.lock(); + std::vector<::aidl::android::hardware::drm::SecureStop> stops; + for (auto itr = mSecureStops.begin(); itr != mSecureStops.end(); ++itr) { + ClearkeySecureStop clearkeyStop = itr->second; + std::vector stop{}; + stop.insert(stop.end(), clearkeyStop.id.begin(), clearkeyStop.id.end()); + stop.insert(stop.end(), clearkeyStop.data.begin(), clearkeyStop.data.end()); + + SecureStop secureStop; + secureStop.opaqueData = stop; + stops.push_back(secureStop); + } + mSecureStopLock.unlock(); + + *_aidl_return = stops; + return toNdkScopedAStatus(Status::OK); +} + +::ndk::ScopedAStatus DrmPlugin::getSecurityLevel( + const std::vector& in_sessionId, + ::aidl::android::hardware::drm::SecurityLevel* _aidl_return) { + if (in_sessionId.size() == 0) { + *_aidl_return = ::aidl::android::hardware::drm::SecurityLevel::UNKNOWN; + return toNdkScopedAStatus(Status::BAD_VALUE); + } + + std::vector sid = in_sessionId; + ::android::sp session = mSessionLibrary->findSession(sid); + if (!session.get()) { + *_aidl_return = SecurityLevel::UNKNOWN; + return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); + } + + std::map, ::aidl::android::hardware::drm::SecurityLevel>::iterator itr = + mSecurityLevel.find(sid); + if (itr == mSecurityLevel.end()) { + ALOGE("Session id not found"); + *_aidl_return = SecurityLevel::UNKNOWN; + return toNdkScopedAStatus(Status::ERROR_DRM_INVALID_STATE); + } + + *_aidl_return = SecurityLevel::SW_SECURE_CRYPTO; + return toNdkScopedAStatus(Status::OK); +} + +::ndk::ScopedAStatus DrmPlugin::openSession( + ::aidl::android::hardware::drm::SecurityLevel in_securityLevel, + std::vector* _aidl_return) { + ::android::sp session = mSessionLibrary->createSession(); + processMockError(session); + std::vector sessionId = session->sessionId(); + + Status status = setSecurityLevel(sessionId, in_securityLevel); + if (status == Status::OK) { + mOpenSessionOkCount++; + } else { + mSessionLibrary->destroySession(session); + sessionId.clear(); + } + *_aidl_return = sessionId; + return toNdkScopedAStatus(status); +} + +::ndk::ScopedAStatus DrmPlugin::provideKeyResponse( + const std::vector& in_scope, const std::vector& in_response, + ::aidl::android::hardware::drm::KeySetId* _aidl_return) { + if (in_scope.size() == 0 || in_response.size() == 0) { + // Returns empty keySetId + *_aidl_return = {}; + return toNdkScopedAStatus(Status::BAD_VALUE); + } + + std::string responseString(reinterpret_cast(in_response.data()), + in_response.size()); + const std::vector scopeId = in_scope; + std::vector sessionId = {}; + std::string keySetId; + + bool isOfflineLicense = responseString.find(kOfflineLicense) != std::string::npos; + if (scopeId.size() < kKeySetIdPrefix.size()) { + android_errorWriteLog(0x534e4554, "144507096"); + *_aidl_return = {}; + return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); + } + bool isRelease = (memcmp(scopeId.data(), kKeySetIdPrefix.data(), kKeySetIdPrefix.size()) == 0); + if (isRelease) { + keySetId.assign(scopeId.begin(), scopeId.end()); + + auto iter = mReleaseKeysMap.find(std::string(keySetId.begin(), keySetId.end())); + if (iter != mReleaseKeysMap.end()) { + sessionId.assign(iter->second.begin(), iter->second.end()); + } + } else { + sessionId.assign(scopeId.begin(), scopeId.end()); + // non offline license returns empty keySetId + keySetId.clear(); + } + + ::android::sp session = mSessionLibrary->findSession(sessionId); + if (!session.get()) { + *_aidl_return = {}; + return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); + } + setPlayPolicy(); + + auto res = session->provideKeyResponse(in_response); + if (res == clearkeydrm::OK) { + if (isOfflineLicense) { + if (isRelease) { + mFileHandle.DeleteLicense(keySetId); + mSessionLibrary->destroySession(session); + } else { + if (!makeKeySetId(&keySetId)) { + *_aidl_return = {}; + return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); + } + + bool ok = mFileHandle.StoreLicense( + keySetId, DeviceFiles::kLicenseStateActive, + std::string(in_response.begin(), in_response.end())); + if (!ok) { + ALOGE("Failed to store offline license"); + } + } + } + + // Test calling AMediaDrm listeners. + sendEvent(EventType::VENDOR_DEFINED, sessionId, sessionId); + + sendExpirationUpdate(sessionId, 100); + + std::vector keysStatus = {}; + KeyStatus keyStatus; + + std::vector keyId1 = {0xA, 0xB, 0xC}; + keyStatus.keyId = keyId1; + keyStatus.type = KeyStatusType::USABLE; + keysStatus.push_back(keyStatus); + + std::vector keyId2 = {0xD, 0xE, 0xF}; + keyStatus.keyId = keyId2; + keyStatus.type = KeyStatusType::EXPIRED; + keysStatus.push_back(keyStatus); + + std::vector keyId3 = {0x0, 0x1, 0x2}; + keyStatus.keyId = keyId3; + keyStatus.type = KeyStatusType::USABLE_IN_FUTURE; + keysStatus.push_back(keyStatus); + + sendKeysChange(sessionId, keysStatus, true); + + installSecureStop(sessionId); + } else { + ALOGE("provideKeyResponse returns error=%d", res); + } + + std::vector keySetIdVec(keySetId.begin(), keySetId.end()); + _aidl_return->keySetId = keySetIdVec; + return toNdkScopedAStatus(res); +} + +::ndk::ScopedAStatus DrmPlugin::provideProvisionResponse( + const std::vector& in_response, + ::aidl::android::hardware::drm::ProvideProvisionResponseResult* _aidl_return) { + Status status = Status::ERROR_DRM_CANNOT_HANDLE; + _aidl_return->certificate = {}; + _aidl_return->wrappedKey = {}; + if (in_response.size() == 0) { + status = Status::BAD_VALUE; + } + return toNdkScopedAStatus(status); +} + +::ndk::ScopedAStatus DrmPlugin::queryKeyStatus( + const std::vector& in_sessionId, + std::vector<::aidl::android::hardware::drm::KeyValue>* _aidl_return) { + if (in_sessionId.size() == 0) { + // Returns empty key status KeyValue pair + *_aidl_return = {}; + return toNdkScopedAStatus(Status::BAD_VALUE); + } + + std::vector infoMap = {}; + mPlayPolicyLock.lock(); + KeyValue keyValuePair; + for (size_t i = 0; i < mPlayPolicy.size(); ++i) { + keyValuePair.key = mPlayPolicy[i].key; + keyValuePair.value = mPlayPolicy[i].value; + infoMap.push_back(keyValuePair); + } + mPlayPolicyLock.unlock(); + *_aidl_return = infoMap; + return toNdkScopedAStatus(Status::OK); +} + +::ndk::ScopedAStatus DrmPlugin::releaseAllSecureStops() { + Status status = Status::OK; + const auto res = removeAllSecureStops(); + if (!res.isOk() && res.getExceptionCode() == EX_SERVICE_SPECIFIC) { + status = static_cast(res.getServiceSpecificError()); + } + return toNdkScopedAStatus(status); +} + +::ndk::ScopedAStatus DrmPlugin::releaseSecureStop( + const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId) { + Status status = Status::OK; + const auto res = removeSecureStop(in_secureStopId); + if (!res.isOk() && res.getExceptionCode() == EX_SERVICE_SPECIFIC) { + status = static_cast(res.getServiceSpecificError()); + } + return toNdkScopedAStatus(status); +} + +::ndk::ScopedAStatus DrmPlugin::releaseSecureStops( + const ::aidl::android::hardware::drm::OpaqueData& in_ssRelease) { + // OpaqueData starts with 4 byte decimal integer string + const size_t kFourBytesOffset = 4; + if (in_ssRelease.opaqueData.size() < kFourBytesOffset) { + ALOGE("Invalid secureStopRelease length"); + return toNdkScopedAStatus(Status::BAD_VALUE); + } + + Status status = Status::OK; + std::vector input = in_ssRelease.opaqueData; + + if (input.size() < kSecureStopIdSize + kFourBytesOffset) { + // The minimum size of secure stop has to contain + // a 4 bytes count and one secureStop id + ALOGE("Total size of secureStops is too short"); + return toNdkScopedAStatus(Status::BAD_VALUE); + } + + // The format of opaqueData is shared between the server + // and the drm service. The clearkey implementation consists of: + // count - number of secure stops + // list of fixed length secure stops + size_t countBufferSize = sizeof(uint32_t); + if (input.size() < countBufferSize) { + // SafetyNet logging + android_errorWriteLog(0x534e4554, "144766455"); + return toNdkScopedAStatus(Status::BAD_VALUE); + } + uint32_t count = 0; + sscanf(reinterpret_cast(input.data()), "%04" PRIu32, &count); + + // Avoid divide by 0 below. + if (count == 0) { + ALOGE("Invalid 0 secureStop count"); + return toNdkScopedAStatus(Status::BAD_VALUE); + } + + // Computes the fixed length secureStop size + size_t secureStopSize = (input.size() - kFourBytesOffset) / count; + if (secureStopSize < kSecureStopIdSize) { + // A valid secureStop contains the id plus data + ALOGE("Invalid secureStop size"); + return toNdkScopedAStatus(Status::BAD_VALUE); + } + uint8_t* buffer = new uint8_t[secureStopSize]; + size_t offset = kFourBytesOffset; // skip the count + for (size_t i = 0; i < count; ++i, offset += secureStopSize) { + memcpy(buffer, input.data() + offset, secureStopSize); + + // A secureStop contains id+data, we only use the id for removal + std::vector id(buffer, buffer + kSecureStopIdSize); + ::aidl::android::hardware::drm::SecureStopId secureStopId{id}; + const auto res = removeSecureStop(secureStopId); + if (!res.isOk() && res.getExceptionCode() == EX_SERVICE_SPECIFIC) { + status = static_cast(res.getServiceSpecificError()); + } + if (Status::OK != status) break; + } + + delete[] buffer; + return toNdkScopedAStatus(status); +} + +::ndk::ScopedAStatus DrmPlugin::removeAllSecureStops() { + Mutex::Autolock lock(mSecureStopLock); + + mSecureStops.clear(); + mNextSecureStopId = kSecureStopIdStart; + return toNdkScopedAStatus(Status::OK); +} + +::ndk::ScopedAStatus DrmPlugin::removeKeys(const std::vector& in_sessionId) { + Status status = Status::ERROR_DRM_CANNOT_HANDLE; + if (in_sessionId.size() == 0) { + status = Status::BAD_VALUE; + } + return toNdkScopedAStatus(status); +} + +::ndk::ScopedAStatus DrmPlugin::removeOfflineLicense( + const ::aidl::android::hardware::drm::KeySetId& in_keySetId) { + if (mMockError != Status::OK) { + return toNdkScopedAStatus(toMockStatus(mMockError)); + } + Status status = Status::BAD_VALUE; + std::string licenseName(in_keySetId.keySetId.begin(), in_keySetId.keySetId.end()); + if (mFileHandle.DeleteLicense(licenseName)) { + status = Status::OK; + } + return toNdkScopedAStatus(status); +} + +::ndk::ScopedAStatus DrmPlugin::removeSecureStop( + const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId) { + Mutex::Autolock lock(mSecureStopLock); + + Status status = Status::OK; + if (1 != mSecureStops.erase(in_secureStopId.secureStopId)) { + status = Status::BAD_VALUE; + } + return toNdkScopedAStatus(status); +} + +::ndk::ScopedAStatus DrmPlugin::requiresSecureDecoder( + const std::string& in_mime, ::aidl::android::hardware::drm::SecurityLevel in_level, + bool* _aidl_return) { + UNUSED(in_mime); + UNUSED(in_level); + *_aidl_return = false; + return ::ndk::ScopedAStatus::ok(); +} + +::ndk::ScopedAStatus DrmPlugin::restoreKeys( + const std::vector& in_sessionId, + const ::aidl::android::hardware::drm::KeySetId& in_keySetId) { + if (in_sessionId.size() == 0 || in_keySetId.keySetId.size() == 0) { + return toNdkScopedAStatus(Status::BAD_VALUE); + } + + DeviceFiles::LicenseState licenseState; + std::string offlineLicense; + if (!mFileHandle.RetrieveLicense( + std::string(in_keySetId.keySetId.begin(), in_keySetId.keySetId.end()), + &licenseState, &offlineLicense)) { + ALOGE("Failed to restore offline license"); + return toNdkScopedAStatus(Status::ERROR_DRM_NO_LICENSE); + } + + if (DeviceFiles::kLicenseStateUnknown == licenseState || + DeviceFiles::kLicenseStateReleasing == licenseState) { + ALOGE("Invalid license state=%d", licenseState); + return toNdkScopedAStatus(Status::ERROR_DRM_NO_LICENSE); + } + + ::android::sp session = mSessionLibrary->findSession(in_sessionId); + if (!session.get()) { + return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); + } + auto res = session->provideKeyResponse( + std::vector(offlineLicense.begin(), offlineLicense.end())); + if (res != clearkeydrm::OK) { + ALOGE("Failed to restore keys"); + } + return toNdkScopedAStatus(res); +} + +void DrmPlugin::sendEvent(::aidl::android::hardware::drm::EventType in_eventType, + const std::vector& in_sessionId, + const std::vector& in_data) { + if (mListener != nullptr) { + mListener->onEvent(in_eventType, in_sessionId, in_data); + } else { + ALOGE("Null event listener, event not sent"); + } + return; +} + +void DrmPlugin::sendExpirationUpdate(const std::vector& in_sessionId, + int64_t in_expiryTimeInMS) { + if (mListener != nullptr) { + mListener->onExpirationUpdate(in_sessionId, in_expiryTimeInMS); + } else { + ALOGE("Null event listener, event not sent"); + } + return; +} + +void DrmPlugin::sendKeysChange( + const std::vector& in_sessionId, + const std::vector<::aidl::android::hardware::drm::KeyStatus>& in_keyStatusList, + bool in_hasNewUsableKey) { + if (mListener != nullptr) { + mListener->onKeysChange(in_sessionId, in_keyStatusList, in_hasNewUsableKey); + } else { + ALOGE("Null event listener, event not sent"); + } + return; +} + +void DrmPlugin::sendSessionLostState(const std::vector& in_sessionId) { + if (mListener != nullptr) { + mListener->onSessionLostState(in_sessionId); + } + return; +} + +::ndk::ScopedAStatus DrmPlugin::setCipherAlgorithm(const std::vector& /*in_sessionId*/, + const std::string& /*in_algorithm*/) { + return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); +} + +::ndk::ScopedAStatus DrmPlugin::setListener( + const std::shared_ptr< + ::aidl::android::hardware::drm::IDrmPluginListener>& in_listener) { + mListener = in_listener; + return toNdkScopedAStatus(Status::OK); +} + +::ndk::ScopedAStatus DrmPlugin::setMacAlgorithm(const std::vector& /*in_sessionId*/, + const std::string& /*in_algorithm*/) { + return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); +} + +::ndk::ScopedAStatus DrmPlugin::setPlaybackId(const std::vector& in_sessionId, + const std::string& in_playbackId) { + if (in_sessionId.size() == 0) { + ALOGE("Invalid empty session id"); + return toNdkScopedAStatus(Status::BAD_VALUE); + } + + std::vector sid = in_sessionId; + mPlaybackId[sid] = in_playbackId; + return toNdkScopedAStatus(Status::OK); +} + +::ndk::ScopedAStatus DrmPlugin::setPropertyByteArray(const std::string& in_propertyName, + const std::vector& in_value) { + if (in_propertyName == kDeviceIdKey) { + ALOGD("Cannot set immutable property: %s", in_propertyName.c_str()); + return toNdkScopedAStatus(Status::BAD_VALUE); + } else if (in_propertyName == kClientIdKey) { + mByteArrayProperties[kClientIdKey] = in_value; + return toNdkScopedAStatus(Status::OK); + } + + // Setting of undefined properties is not supported + ALOGE("Failed to set property byte array, key=%s", in_propertyName.c_str()); + return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); +} + +::ndk::ScopedAStatus DrmPlugin::setPropertyString(const std::string& in_propertyName, + const std::string& in_value) { + std::string immutableKeys; + immutableKeys.append(kAlgorithmsKey + ","); + immutableKeys.append(kPluginDescriptionKey + ","); + immutableKeys.append(kVendorKey + ","); + immutableKeys.append(kVersionKey + ","); + + std::string key = std::string(in_propertyName.c_str()); + if (immutableKeys.find(key) != std::string::npos) { + ALOGD("Cannot set immutable property: %s", key.c_str()); + return toNdkScopedAStatus(Status::BAD_VALUE); + } + + std::map::iterator itr = mStringProperties.find(key); + if (itr == mStringProperties.end()) { + ALOGE("Cannot set undefined property string, key=%s", key.c_str()); + return toNdkScopedAStatus(Status::BAD_VALUE); + } + + if (in_propertyName == kDrmErrorTestKey) { + if (in_value == kResourceContentionValue) { + mMockError = Status::ERROR_DRM_RESOURCE_CONTENTION; + } else if (in_value == kLostStateValue) { + mMockError = Status::ERROR_DRM_SESSION_LOST_STATE; + } else if (in_value == kFrameTooLargeValue) { + mMockError = Status::ERROR_DRM_FRAME_TOO_LARGE; + } else if (in_value == kInvalidStateValue) { + mMockError = Status::ERROR_DRM_INVALID_STATE; + } else { + mMockError = Status::ERROR_DRM_UNKNOWN; + } + } + + mStringProperties[key] = std::string(in_value.c_str()); + return toNdkScopedAStatus(Status::OK); +} + +::ndk::ScopedAStatus DrmPlugin::sign(const std::vector& /*in_sessionId*/, + const std::vector& /*in_keyId*/, + const std::vector& /*in_message*/, + std::vector* _aidl_return) { + *_aidl_return = {}; + return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); +} + +::ndk::ScopedAStatus DrmPlugin::signRSA(const std::vector& /*in_sessionId*/, + const std::string& /*in_algorithm*/, + const std::vector& /*in_message*/, + const std::vector& /*in_wrappedkey*/, + std::vector* _aidl_return) { + *_aidl_return = {}; + return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); +} + +::ndk::ScopedAStatus DrmPlugin::verify(const std::vector& /*in_sessionId*/, + const std::vector& /*in_keyId*/, + const std::vector& /*in_message*/, + const std::vector& /*in_signature*/, + bool* _aidl_return) { + *_aidl_return = false; + return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); +} + +// Private methods below. +void DrmPlugin::setPlayPolicy() { + ::android::Mutex::Autolock lock(mPlayPolicyLock); + mPlayPolicy.clear(); + + KeyValue policy; + policy.key = kQueryKeyLicenseType; + policy.value = kStreaming; + mPlayPolicy.push_back(policy); + + policy.key = kQueryKeyPlayAllowed; + policy.value = kTrue; + mPlayPolicy.push_back(policy); + + policy.key = kQueryKeyRenewAllowed; + mPlayPolicy.push_back(policy); +} + +bool DrmPlugin::makeKeySetId(std::string* keySetId) { + if (!keySetId) { + ALOGE("keySetId destination not provided"); + return false; + } + std::vector ksid(kKeySetIdPrefix.begin(), kKeySetIdPrefix.end()); + ksid.resize(kKeySetIdLength); + std::vector randomData((kKeySetIdLength - kKeySetIdPrefix.size()) / 2, 0); + + while (keySetId->empty()) { + for (auto itr = randomData.begin(); itr != randomData.end(); ++itr) { + *itr = std::rand() % 0xff; + } + auto id = reinterpret_cast(randomData.data()); + *keySetId = kKeySetIdPrefix + ::android::ByteArrayToHexString(id, randomData.size()); + if (mFileHandle.LicenseExists(*keySetId)) { + // collision, regenerate + ALOGV("Retry generating KeySetId"); + keySetId->clear(); + } + } + return true; +} + +Status DrmPlugin::setSecurityLevel(const std::vector& sessionId, SecurityLevel level) { + if (sessionId.size() == 0) { + ALOGE("Invalid empty session id"); + return Status::BAD_VALUE; + } + + if (level != SecurityLevel::DEFAULT && level != SecurityLevel::SW_SECURE_CRYPTO) { + ALOGE("Cannot set security level > max"); + return Status::ERROR_DRM_CANNOT_HANDLE; + } + + std::vector sid = sessionId; + ::android::sp session = mSessionLibrary->findSession(sid); + if (!session.get()) { + return Status::ERROR_DRM_SESSION_NOT_OPENED; + } + + std::map, SecurityLevel>::iterator itr = mSecurityLevel.find(sid); + if (itr != mSecurityLevel.end()) { + mSecurityLevel[sid] = level; + } else { + if (!mSecurityLevel.insert(std::pair, SecurityLevel>(sid, level)) + .second) { + ALOGE("Failed to set security level"); + return Status::ERROR_DRM_INVALID_STATE; + } + } + return Status::OK; +} + +} // namespace clearkey +} // namespace drm +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/drm/mediadrm/plugins/clearkey/aidl/Service.cpp b/drm/mediadrm/plugins/clearkey/aidl/Service.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0b0786419cd2d41b898f1a8373ce0ccd0e5d203f --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/aidl/Service.cpp @@ -0,0 +1,45 @@ +/* + * Copyright 2021 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 1 +#define LOG_TAG "clearkey-main" + +#include "CreatePluginFactories.h" + +#include + +#include +#include + +using ::android::base::InitLogging; +using ::android::base::LogdLogger; + +using ::aidl::android::hardware::drm::clearkey::createDrmFactory; +using ::aidl::android::hardware::drm::clearkey::DrmFactory; + +int main(int /*argc*/, char* argv[]) { + InitLogging(argv, LogdLogger()); + ::android::base::SetMinimumLogSeverity(::android::base::VERBOSE); + ABinderProcess_setThreadPoolMaxThreadCount(8); + + std::shared_ptr drmFactory = createDrmFactory(); + const std::string drmInstance = std::string() + DrmFactory::descriptor + "/clearkey"; + binder_status_t status = + AServiceManager_addService(drmFactory->asBinder().get(), drmInstance.c_str()); + CHECK(status == STATUS_OK); + + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reached +} diff --git a/drm/mediadrm/plugins/clearkey/aidl/ServiceLazy.cpp b/drm/mediadrm/plugins/clearkey/aidl/ServiceLazy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c3a33bb3aaad9ca456eecaebb28912b10a3bbbf4 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/aidl/ServiceLazy.cpp @@ -0,0 +1,45 @@ +/* + * Copyright 2021 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 1 +#define LOG_TAG "clearkey-main" + +#include "CreatePluginFactories.h" + +#include + +#include +#include + +using ::android::base::InitLogging; +using ::android::base::LogdLogger; + +using ::aidl::android::hardware::drm::clearkey::createDrmFactory; +using ::aidl::android::hardware::drm::clearkey::DrmFactory; + +int main(int /*argc*/, char* argv[]) { + InitLogging(argv, LogdLogger()); + ::android::base::SetMinimumLogSeverity(::android::base::VERBOSE); + ABinderProcess_setThreadPoolMaxThreadCount(8); + + std::shared_ptr drmFactory = createDrmFactory(); + const std::string drmInstance = std::string() + DrmFactory::descriptor + "/clearkey"; + binder_status_t status = + AServiceManager_registerLazyService(drmFactory->asBinder().get(), drmInstance.c_str()); + CHECK(status == STATUS_OK); + + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reached +} diff --git a/drm/mediadrm/plugins/clearkey/aidl/android.hardware.drm-service-lazy.clearkey.rc b/drm/mediadrm/plugins/clearkey/aidl/android.hardware.drm-service-lazy.clearkey.rc new file mode 100644 index 0000000000000000000000000000000000000000..c87aabc6bfa76d6a07681135cbb1ed3fbfd4576e --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/aidl/android.hardware.drm-service-lazy.clearkey.rc @@ -0,0 +1,9 @@ +service vendor.drm-clearkey-service /vendor/bin/hw/android.hardware.drm-service-lazy.clearkey + oneshot + disabled + class hal + user media + group mediadrm drmrpc + ioprio rt 4 + task_profiles ProcessCapacityHigh + interface aidl android.hardware.drm.IDrmFactory/clearkey \ No newline at end of file diff --git a/drm/mediadrm/plugins/clearkey/aidl/android.hardware.drm-service.clearkey.rc b/drm/mediadrm/plugins/clearkey/aidl/android.hardware.drm-service.clearkey.rc new file mode 100644 index 0000000000000000000000000000000000000000..e9252cd6006216b3f126dd81b94ecd9d38b828f0 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/aidl/android.hardware.drm-service.clearkey.rc @@ -0,0 +1,7 @@ +service vendor.drm-clearkey-service /vendor/bin/hw/android.hardware.drm-service.clearkey + class hal + user media + group mediadrm drmrpc + ioprio rt 4 + task_profiles ProcessCapacityHigh + interface aidl android.hardware.drm.IDrmFactory/clearkey diff --git a/drm/mediadrm/plugins/clearkey/aidl/android.hardware.drm-service.clearkey.xml b/drm/mediadrm/plugins/clearkey/aidl/android.hardware.drm-service.clearkey.xml new file mode 100644 index 0000000000000000000000000000000000000000..ac3e922c158158fcacaeea08c7caa0313ae070cd --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/aidl/android.hardware.drm-service.clearkey.xml @@ -0,0 +1,7 @@ + + + android.hardware.drm + 1 + IDrmFactory/clearkey + + diff --git a/drm/mediadrm/plugins/clearkey/aidl/include/AidlClearKeryProperties.h b/drm/mediadrm/plugins/clearkey/aidl/include/AidlClearKeryProperties.h new file mode 100644 index 0000000000000000000000000000000000000000..fb2cceb7f51029ccaa90d00a0434ae0f45178eba --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/aidl/include/AidlClearKeryProperties.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2022 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 AIDL_CLEARKEY_PROPERTIES_H +#define AIDL_CLEARKEY_PROPERTIES_H +#include + +namespace clearkeydrm { +static const std::string kAidlVendorValue("Google"); +static const std::string kAidlVersionValue("aidl-1"); +static const std::string kAidlPluginDescriptionValue("ClearKey CDM"); +static const std::string kAidlAlgorithmsValue(""); +static const std::string kAidlListenerTestSupportValue("true"); + +static const std::string kAidlDrmErrorTestValue(""); +static const std::string kAidlResourceContentionValue("resourceContention"); +static const std::string kAidlLostStateValue("lostState"); +static const std::string kAidlFrameTooLargeValue("frameTooLarge"); +static const std::string kAidlInvalidStateValue("invalidState"); +} // namespace clearkeydrm + +#endif \ No newline at end of file diff --git a/drm/mediadrm/plugins/clearkey/aidl/include/AidlUtils.h b/drm/mediadrm/plugins/clearkey/aidl/include/AidlUtils.h new file mode 100644 index 0000000000000000000000000000000000000000..9257b17075a714a8c5f277100e30599db4af3242 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/aidl/include/AidlUtils.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2021 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. + */ +#pragma once + +#include +#include + +#include +#include "aidl/android/hardware/drm/Status.h" +#include "ClearKeyTypes.h" + +namespace aidl { +namespace android { +namespace hardware { +namespace drm { +namespace clearkey { + +inline ::aidl::android::hardware::drm::Status toMockStatus( + ::aidl::android::hardware::drm::Status status) { + switch (status) { + case ::aidl::android::hardware::drm::Status::ERROR_DRM_INSUFFICIENT_SECURITY: + case ::aidl::android::hardware::drm::Status::ERROR_DRM_FRAME_TOO_LARGE: + case ::aidl::android::hardware::drm::Status::ERROR_DRM_SESSION_LOST_STATE: + return ::aidl::android::hardware::drm::Status::ERROR_DRM_UNKNOWN; + default: + return status; + } +} + +inline ::ndk::ScopedAStatus toNdkScopedAStatus(::aidl::android::hardware::drm::Status status, + const char* msg = nullptr) { + if (Status::OK == status) { + return ::ndk::ScopedAStatus::ok(); + } else { + auto err = static_cast(status); + if (msg) { + return ::ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(err, msg); + } else { + return ::ndk::ScopedAStatus::fromServiceSpecificError(err); + } + } +} + +inline ::ndk::ScopedAStatus toNdkScopedAStatus(clearkeydrm::CdmResponseType res) { + return toNdkScopedAStatus(static_cast<::aidl::android::hardware::drm::Status>(res)); +} + +#define UNUSED(x) (void)(x); + +} // namespace clearkey +} // namespace drm +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/media/libaudioclient/aidl/android/media/ExtraAudioDescriptor.aidl b/drm/mediadrm/plugins/clearkey/aidl/include/CreatePluginFactories.h similarity index 62% rename from media/libaudioclient/aidl/android/media/ExtraAudioDescriptor.aidl rename to drm/mediadrm/plugins/clearkey/aidl/include/CreatePluginFactories.h index ec5b67a0ceda3477978337e130a5f3c335301ee3..e2afcf53b0ac0642c7f309a4b56cec4445e6fb93 100644 --- a/media/libaudioclient/aidl/android/media/ExtraAudioDescriptor.aidl +++ b/drm/mediadrm/plugins/clearkey/aidl/include/CreatePluginFactories.h @@ -13,20 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once -package android.media; +#include "DrmFactory.h" -import android.media.AudioEncapsulationType; -import android.media.AudioStandard; +namespace aidl { +namespace android { +namespace hardware { +namespace drm { +namespace clearkey { -/** - * The audio descriptor that descibes playback/capture capabilities according to - * a particular standard. - * - * {@hide} - */ -parcelable ExtraAudioDescriptor { - AudioStandard standard; - byte[] audioDescriptor; - AudioEncapsulationType encapsulationType; -} +std::shared_ptr createDrmFactory(); + +} // namespace clearkey +} // namespace drm +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/drm/mediadrm/plugins/clearkey/aidl/include/CryptoPlugin.h b/drm/mediadrm/plugins/clearkey/aidl/include/CryptoPlugin.h new file mode 100644 index 0000000000000000000000000000000000000000..60dbf77c461ca0c8dc1c1690b0fe74a055cd7219 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/aidl/include/CryptoPlugin.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2021 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. + */ +#pragma once + +#include +#include + +#include + +#include + +#include +#include + +#include "ClearKeyTypes.h" +#include "Session.h" + +namespace { +static const size_t KEY_ID_SIZE = 16; +static const size_t KEY_IV_SIZE = 16; +} // namespace + +namespace aidl { +namespace android { +namespace hardware { +namespace drm { +namespace clearkey { + +using namespace clearkeydrm; +using ::aidl::android::hardware::drm::DecryptArgs; +using ::aidl::android::hardware::drm::Status; + +struct SharedBufferBase { + uint8_t* mBase; + int64_t mSize; + SharedBufferBase(const ::aidl::android::hardware::drm::SharedBuffer& mem); + ~SharedBufferBase(); +}; + +struct CryptoPlugin : public BnCryptoPlugin { + explicit CryptoPlugin(const std::vector& sessionId) { + const auto res = setMediaDrmSession(sessionId); + mInitStatus = Status::OK; + if (!res.isOk() && res.getExceptionCode() == EX_SERVICE_SPECIFIC) { + mInitStatus = static_cast(res.getServiceSpecificError()); + } + } + virtual ~CryptoPlugin() {} + + ::ndk::ScopedAStatus decrypt(const DecryptArgs& in_args, int32_t* _aidl_return) override; + + ::ndk::ScopedAStatus getLogMessages( + std::vector<::aidl::android::hardware::drm::LogMessage>* _aidl_return) override; + + ::ndk::ScopedAStatus notifyResolution(int32_t in_width, int32_t in_height) override; + + ::ndk::ScopedAStatus requiresSecureDecoderComponent(const std::string& in_mime, + bool* _aidl_return) override; + + ::ndk::ScopedAStatus setMediaDrmSession(const std::vector& in_sessionId) override; + + ::ndk::ScopedAStatus setSharedBufferBase( + const ::aidl::android::hardware::drm::SharedBuffer& in_base) override; + + ::aidl::android::hardware::drm::Status getInitStatus() const { return mInitStatus; } + + private: + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(CryptoPlugin); + + std::mutex mSharedBufferLock; + std::map> mSharedBufferMap + GUARDED_BY(mSharedBufferLock); + ::android::sp mSession; + ::aidl::android::hardware::drm::Status mInitStatus; +}; + +} // namespace clearkey +} // namespace drm +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/drm/mediadrm/plugins/clearkey/aidl/include/DrmFactory.h b/drm/mediadrm/plugins/clearkey/aidl/include/DrmFactory.h new file mode 100644 index 0000000000000000000000000000000000000000..1239aa49ab8c864747c1f3e21d3889364f92525d --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/aidl/include/DrmFactory.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2021 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. + */ +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include "ClearKeyTypes.h" + +namespace aidl { +namespace android { +namespace hardware { +namespace drm { +namespace clearkey { + +struct DrmFactory : public BnDrmFactory { + DrmFactory() {} + virtual ~DrmFactory() {} + + ::ndk::ScopedAStatus createDrmPlugin( + const ::aidl::android::hardware::drm::Uuid& in_uuid, + const std::string& in_appPackageName, + std::shared_ptr<::aidl::android::hardware::drm::IDrmPlugin>* _aidl_return) override; + + ::ndk::ScopedAStatus createCryptoPlugin( + const ::aidl::android::hardware::drm::Uuid& in_uuid, + const std::vector& in_initData, + std::shared_ptr<::aidl::android::hardware::drm::ICryptoPlugin>* _aidl_return) override; + + ::ndk::ScopedAStatus getSupportedCryptoSchemes( + ::aidl::android::hardware::drm::CryptoSchemes* _aidl_return) override; + + binder_status_t dump(int fd, const char** args, uint32_t numArgs) override; + + + private: + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(DrmFactory); +}; + +} // namespace clearkey +} // namespace drm +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/drm/mediadrm/plugins/clearkey/aidl/include/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/aidl/include/DrmPlugin.h new file mode 100644 index 0000000000000000000000000000000000000000..25c05f0ca639e10c6c9c2b17f4fe08b63a501029 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/aidl/include/DrmPlugin.h @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2021 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. + */ +#pragma once + +#include +#include +#include + +#include +#include + +#include + +#include "DeviceFiles.h" +#include "SessionLibrary.h" + +namespace aidl { +namespace android { +namespace hardware { +namespace drm { +namespace clearkey { + +using namespace clearkeydrm; +using ::aidl::android::hardware::drm::KeyType; +using ::aidl::android::hardware::drm::Status; + +struct DrmPlugin : public BnDrmPlugin { + public: + explicit DrmPlugin(SessionLibrary* sessionLibrary); + virtual ~DrmPlugin() { mFileHandle.DeleteAllLicenses(); } + + ::ndk::ScopedAStatus closeSession(const std::vector& in_sessionId) override; + ::ndk::ScopedAStatus decrypt(const std::vector& in_sessionId, + const std::vector& in_keyId, + const std::vector& in_input, + const std::vector& in_iv, + std::vector* _aidl_return) override; + ::ndk::ScopedAStatus encrypt(const std::vector& in_sessionId, + const std::vector& in_keyId, + const std::vector& in_input, + const std::vector& in_iv, + std::vector* _aidl_return) override; + ::ndk::ScopedAStatus getHdcpLevels( + ::aidl::android::hardware::drm::HdcpLevels* _aidl_return) override; + ::ndk::ScopedAStatus getKeyRequest( + const std::vector& in_scope, const std::vector& in_initData, + const std::string& in_mimeType, ::aidl::android::hardware::drm::KeyType in_keyType, + const std::vector<::aidl::android::hardware::drm::KeyValue>& in_optionalParameters, + ::aidl::android::hardware::drm::KeyRequest* _aidl_return) override; + ::ndk::ScopedAStatus getLogMessages( + std::vector<::aidl::android::hardware::drm::LogMessage>* _aidl_return) override; + + ::ndk::ScopedAStatus getMetrics( + std::vector<::aidl::android::hardware::drm::DrmMetricGroup>* _aidl_return) override; + ::ndk::ScopedAStatus getNumberOfSessions( + ::aidl::android::hardware::drm::NumberOfSessions* _aidl_return) override; + ::ndk::ScopedAStatus getOfflineLicenseKeySetIds( + std::vector<::aidl::android::hardware::drm::KeySetId>* _aidl_return) override; + ::ndk::ScopedAStatus getOfflineLicenseState( + const ::aidl::android::hardware::drm::KeySetId& in_keySetId, + ::aidl::android::hardware::drm::OfflineLicenseState* _aidl_return) override; + ::ndk::ScopedAStatus getPropertyByteArray(const std::string& in_propertyName, + std::vector* _aidl_return) override; + ::ndk::ScopedAStatus getPropertyString(const std::string& in_propertyName, + std::string* _aidl_return) override; + ::ndk::ScopedAStatus getProvisionRequest( + const std::string& in_certificateType, const std::string& in_certificateAuthority, + ::aidl::android::hardware::drm::ProvisionRequest* _aidl_return) override; + ::ndk::ScopedAStatus getSecureStop( + const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId, + ::aidl::android::hardware::drm::SecureStop* _aidl_return) override; + ::ndk::ScopedAStatus getSecureStopIds( + std::vector<::aidl::android::hardware::drm::SecureStopId>* _aidl_return) override; + ::ndk::ScopedAStatus getSecureStops( + std::vector<::aidl::android::hardware::drm::SecureStop>* _aidl_return) override; + ::ndk::ScopedAStatus getSecurityLevel( + const std::vector& in_sessionId, + ::aidl::android::hardware::drm::SecurityLevel* _aidl_return) override; + ::ndk::ScopedAStatus openSession(::aidl::android::hardware::drm::SecurityLevel in_securityLevel, + std::vector* _aidl_return) override; + ::ndk::ScopedAStatus provideKeyResponse( + const std::vector& in_scope, const std::vector& in_response, + ::aidl::android::hardware::drm::KeySetId* _aidl_return) override; + ::ndk::ScopedAStatus provideProvisionResponse( + const std::vector& in_response, + ::aidl::android::hardware::drm::ProvideProvisionResponseResult* _aidl_return) override; + ::ndk::ScopedAStatus queryKeyStatus( + const std::vector& in_sessionId, + std::vector<::aidl::android::hardware::drm::KeyValue>* _aidl_return) override; + ::ndk::ScopedAStatus releaseAllSecureStops() override; + ::ndk::ScopedAStatus releaseSecureStop( + const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId) override; + ::ndk::ScopedAStatus releaseSecureStops( + const ::aidl::android::hardware::drm::OpaqueData& in_ssRelease) override; + ::ndk::ScopedAStatus removeAllSecureStops() override; + ::ndk::ScopedAStatus removeKeys(const std::vector& in_sessionId) override; + ::ndk::ScopedAStatus removeOfflineLicense( + const ::aidl::android::hardware::drm::KeySetId& in_keySetId) override; + ::ndk::ScopedAStatus removeSecureStop( + const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId) override; + ::ndk::ScopedAStatus requiresSecureDecoder( + const std::string& in_mime, ::aidl::android::hardware::drm::SecurityLevel in_level, + bool* _aidl_return) override; + ::ndk::ScopedAStatus restoreKeys( + const std::vector& in_sessionId, + const ::aidl::android::hardware::drm::KeySetId& in_keySetId) override; + ::ndk::ScopedAStatus setCipherAlgorithm(const std::vector& in_sessionId, + const std::string& in_algorithm) override; + ::ndk::ScopedAStatus setListener( + // const ::android::sp<::aidl::android::hardware::drm::IDrmPluginListener>& + // in_listener) + const std::shared_ptr& in_listener) override; + ::ndk::ScopedAStatus setMacAlgorithm(const std::vector& in_sessionId, + const std::string& in_algorithm) override; + ::ndk::ScopedAStatus setPlaybackId(const std::vector& in_sessionId, + const std::string& in_playbackId) override; + ::ndk::ScopedAStatus setPropertyByteArray(const std::string& in_propertyName, + const std::vector& in_value) override; + ::ndk::ScopedAStatus setPropertyString(const std::string& in_propertyName, + const std::string& in_value) override; + ::ndk::ScopedAStatus sign(const std::vector& in_sessionId, + const std::vector& in_keyId, + const std::vector& in_message, + std::vector* _aidl_return) override; + ::ndk::ScopedAStatus signRSA(const std::vector& in_sessionId, + const std::string& in_algorithm, + const std::vector& in_message, + const std::vector& in_wrappedkey, + std::vector* _aidl_return) override; + ::ndk::ScopedAStatus verify(const std::vector& in_sessionId, + const std::vector& in_keyId, + const std::vector& in_message, + const std::vector& in_signature, + bool* _aidl_return) override; + + private: + void initProperties(); + void installSecureStop(const std::vector& sessionId); + bool makeKeySetId(std::string* keySetId); + void setPlayPolicy(); + + void sendEvent(::aidl::android::hardware::drm::EventType in_eventType, + const std::vector& in_sessionId, + const std::vector& in_data); + void sendExpirationUpdate(const std::vector& in_sessionId, + int64_t in_expiryTimeInMS); + void sendKeysChange( + const std::vector& in_sessionId, + const std::vector<::aidl::android::hardware::drm::KeyStatus>& in_keyStatusList, + bool in_hasNewUsableKey); + void sendSessionLostState(const std::vector& in_sessionId); + + Status setSecurityLevel(const std::vector& sessionId, SecurityLevel level); + + Status getKeyRequestCommon(const std::vector& scope, + const std::vector& initData, const std::string& mimeType, + KeyType keyType, const std::vector& optionalParameters, + std::vector* request, KeyRequestType* getKeyRequestType, + std::string* defaultUrl); + + struct ClearkeySecureStop { + std::vector id; + std::vector data; + }; + + std::map, ClearkeySecureStop> mSecureStops; + std::vector mPlayPolicy; + std::map mStringProperties; + std::map> mByteArrayProperties; + std::map> mReleaseKeysMap; + std::map, std::string> mPlaybackId; + std::map, SecurityLevel> mSecurityLevel; + ::std::shared_ptr mListener; + SessionLibrary* mSessionLibrary; + int64_t mOpenSessionOkCount; + int64_t mCloseSessionOkCount; + int64_t mCloseSessionNotOpenedCount; + uint32_t mNextSecureStopId; + ::android::Mutex mPlayPolicyLock; + + // set by property to mock error scenarios + Status mMockError; + + void processMockError(const ::android::sp& session) { + session->setMockError(static_cast(mMockError)); + mMockError = Status::OK; + } + + DeviceFiles mFileHandle; + ::android::Mutex mSecureStopLock; + + CLEARKEY_DISALLOW_COPY_AND_ASSIGN_AND_NEW(DrmPlugin); +}; + +} // namespace clearkey +} // namespace drm +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/drm/mediadrm/plugins/clearkey/common/AesCtrDecryptor.cpp b/drm/mediadrm/plugins/clearkey/common/AesCtrDecryptor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0b9782089faba64419b483529a62128d6186dec9 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/AesCtrDecryptor.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2021 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 "clearkey-AesDecryptor" + +#include + +#include + +#include "AesCtrDecryptor.h" +#include "ClearKeyTypes.h" + +namespace clearkeydrm { + +static const size_t kBlockBitCount = kBlockSize * 8; + +CdmResponseType AesCtrDecryptor::decrypt(const std::vector& key, const Iv iv, + const uint8_t* source, uint8_t* destination, + const std::vector& clearDataLengths, + const std::vector& encryptedDataLengths, + size_t* bytesDecryptedOut) { + + if (key.size() != kBlockSize || clearDataLengths.size() != encryptedDataLengths.size()) { + android_errorWriteLog(0x534e4554, "63982768"); + return clearkeydrm::ERROR_DECRYPT; + } + + uint32_t blockOffset = 0; + uint8_t previousEncryptedCounter[kBlockSize]; + memset(previousEncryptedCounter, 0, kBlockSize); + + size_t offset = 0; + AES_KEY opensslKey; + AES_set_encrypt_key(key.data(), kBlockBitCount, &opensslKey); + Iv opensslIv; + memcpy(opensslIv, iv, sizeof(opensslIv)); + + for (size_t i = 0; i < clearDataLengths.size(); ++i) { + int32_t numBytesOfClearData = clearDataLengths[i]; + if (numBytesOfClearData > 0) { + memcpy(destination + offset, source + offset, numBytesOfClearData); + offset += numBytesOfClearData; + } + + int32_t numBytesOfEncryptedData = encryptedDataLengths[i]; + if (numBytesOfEncryptedData > 0) { + AES_ctr128_encrypt(source + offset, destination + offset, + numBytesOfEncryptedData, &opensslKey, opensslIv, + previousEncryptedCounter, &blockOffset); + offset += numBytesOfEncryptedData; + } + } + + *bytesDecryptedOut = offset; + return clearkeydrm::OK; +} + +} // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/common/Android.bp b/drm/mediadrm/plugins/clearkey/common/Android.bp index 7ed8b88bf8974f86e933904d3f1ff6c069815597..a6a5b2883d69261ea1858ccd623c56c103b046cc 100644 --- a/drm/mediadrm/plugins/clearkey/common/Android.bp +++ b/drm/mediadrm/plugins/clearkey/common/Android.bp @@ -44,3 +44,56 @@ cc_library_static { integer_overflow: true, }, } + +cc_library_static { + name: "libclearkeydevicefiles-protos.common", + vendor: true, + + proto: { + export_proto_headers: true, + type: "lite", + }, + srcs: ["protos/DeviceFiles.proto"], +} + +cc_library_static { + name: "libclearkeybase", + vendor: true, + + srcs: [ + "AesCtrDecryptor.cpp", + "Base64.cpp", + "Buffer.cpp", + "ClearKeyUUID.cpp", + "DeviceFiles.cpp", + "InitDataParser.cpp", + "JsonWebKey.cpp", + "MemoryFileSystem.cpp", + "Session.cpp", + "SessionLibrary.cpp", + "Utils.cpp", + ], + + cflags: ["-Wall", "-Werror"], + + include_dirs: ["frameworks/av/include"], + + shared_libs: [ + "libutils", + "libcrypto", + ], + + whole_static_libs: [ + "libjsmn", + "libclearkeydevicefiles-protos.common", + ], + + export_include_dirs: [ + "include", + "include/clearkeydrm", + ], + + sanitize: { + integer_overflow: true, + }, +} diff --git a/drm/mediadrm/plugins/clearkey/common/Base64.cpp b/drm/mediadrm/plugins/clearkey/common/Base64.cpp new file mode 100644 index 0000000000000000000000000000000000000000..64997933969c301688ff50e5b082770cb7c76045 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/Base64.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2021 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 "clearkey-Base64" + +#include "Base64.h" + +#include + +namespace clearkeydrm { + +::android::sp decodeBase64(const std::string& s) { + size_t n = s.size(); + + if ((n % 4) != 0) { + return nullptr; + } + + size_t padding = 0; + if (n >= 1 && s.c_str()[n - 1] == '=') { + padding = 1; + + if (n >= 2 && s.c_str()[n - 2] == '=') { + padding = 2; + + if (n >= 3 && s.c_str()[n - 3] == '=') { + padding = 3; + } + } + } + + // We divide first to avoid overflow. It's OK to do this because we + // already made sure that n % 4 == 0. + size_t outLen = (n / 4) * 3 - padding; + + ::android::sp buffer = new Buffer(outLen); + uint8_t* out = buffer->data(); + if (out == nullptr || buffer->size() < outLen) { + return nullptr; + } + + size_t j = 0; + uint32_t accum = 0; + for (size_t i = 0; i < n; ++i) { + char c = s.c_str()[i]; + unsigned value; + if (c >= 'A' && c <= 'Z') { + value = c - 'A'; + } else if (c >= 'a' && c <= 'z') { + value = 26 + c - 'a'; + } else if (c >= '0' && c <= '9') { + value = 52 + c - '0'; + } else if (c == '+' || c == '-') { + value = 62; + } else if (c == '/' || c == '_') { + value = 63; + } else if (c != '=') { + return nullptr; + } else { + if (i < n - padding) { + return nullptr; + } + + value = 0; + } + + accum = (accum << 6) | value; + + if (((i + 1) % 4) == 0) { + if (j < outLen) { + out[j++] = (accum >> 16); + } + if (j < outLen) { + out[j++] = (accum >> 8) & 0xff; + } + if (j < outLen) { + out[j++] = accum & 0xff; + } + + accum = 0; + } + } + + return buffer; +} + +static char encode6Bit(unsigned x) { + if (x <= 25) { + return 'A' + x; + } else if (x <= 51) { + return 'a' + x - 26; + } else if (x <= 61) { + return '0' + x - 52; + } else if (x == 62) { + return '+'; + } else { + return '/'; + } +} + +void encodeBase64(const void* _data, size_t size, std::string* out) { + out->clear(); + + const uint8_t* data = (const uint8_t*)_data; + + size_t i; + for (i = 0; i < (size / 3) * 3; i += 3) { + uint8_t x1 = data[i]; + uint8_t x2 = data[i + 1]; + uint8_t x3 = data[i + 2]; + + out->push_back(encode6Bit(x1 >> 2)); + out->push_back(encode6Bit((x1 << 4 | x2 >> 4) & 0x3f)); + out->push_back(encode6Bit((x2 << 2 | x3 >> 6) & 0x3f)); + out->push_back(encode6Bit(x3 & 0x3f)); + } + switch (size % 3) { + case 0: + break; + case 2: { + uint8_t x1 = data[i]; + uint8_t x2 = data[i + 1]; + out->push_back(encode6Bit(x1 >> 2)); + out->push_back(encode6Bit((x1 << 4 | x2 >> 4) & 0x3f)); + out->push_back(encode6Bit((x2 << 2) & 0x3f)); + out->push_back('='); + break; + } + default: { + uint8_t x1 = data[i]; + out->push_back(encode6Bit(x1 >> 2)); + out->push_back(encode6Bit((x1 << 4) & 0x3f)); + out->append("=="); + break; + } + } +} + +void encodeBase64Url(const void* _data, size_t size, std::string* out) { + encodeBase64(_data, size, out); + + if ((std::string::npos != out->find("+")) || (std::string::npos != out->find("/"))) { + size_t outLen = out->size(); + char* base64url = new char[outLen]; + for (size_t i = 0; i < outLen; ++i) { + if (out->c_str()[i] == '+') + base64url[i] = '-'; + else if (out->c_str()[i] == '/') + base64url[i] = '_'; + else + base64url[i] = out->c_str()[i]; + } + + out->assign(base64url, outLen); + delete[] base64url; + } +} + +} // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/common/Buffer.cpp b/drm/mediadrm/plugins/clearkey/common/Buffer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1671598fad1e6dc804a7d95178aceef8c3343a35 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/Buffer.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "Buffer.h" + +namespace clearkeydrm { + +Buffer::Buffer(size_t capacity) : mRangeOffset(0), mOwnsData(true) { + mData = malloc(capacity); + if (mData == nullptr) { + mCapacity = 0; + mRangeLength = 0; + } else { + mCapacity = capacity; + mRangeLength = capacity; + } +} + +Buffer::~Buffer() { + if (mOwnsData) { + if (mData != nullptr) { + free(mData); + mData = nullptr; + } + } +} + +} // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/common/DeviceFiles.cpp b/drm/mediadrm/plugins/clearkey/common/DeviceFiles.cpp new file mode 100644 index 0000000000000000000000000000000000000000..229924911393fd3636665573970dbc49259355d1 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/DeviceFiles.cpp @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include + +#include +#include + +#include "DeviceFiles.h" +#include "protos/DeviceFiles.pb.h" + +#include + +// Protobuf generated classes. +using clearkeydrm::HashedFile; +using clearkeydrm::License; +using clearkeydrm::License_LicenseState_ACTIVE; +using clearkeydrm::License_LicenseState_RELEASING; +using clearkeydrm::OfflineFile; + +namespace { +const char kLicenseFileNameExt[] = ".lic"; + +bool Hash(const std::string& data, std::string* hash) { + if (!hash) return false; + + hash->resize(SHA256_DIGEST_LENGTH); + + const unsigned char* input = reinterpret_cast(data.data()); + unsigned char* output = reinterpret_cast(&(*hash)[0]); + SHA256(input, data.size(), output); + return true; +} + +} // namespace + +namespace clearkeydrm { + +bool DeviceFiles::StoreLicense(const std::string& keySetId, LicenseState state, + const std::string& licenseResponse) { + OfflineFile file; + file.set_type(OfflineFile::LICENSE); + file.set_version(OfflineFile::VERSION_1); + + License* license = file.mutable_license(); + switch (state) { + case kLicenseStateActive: + license->set_state(License_LicenseState_ACTIVE); + license->set_license(licenseResponse); + break; + case kLicenseStateReleasing: + license->set_state(License_LicenseState_RELEASING); + license->set_license(licenseResponse); + break; + default: + ALOGW("StoreLicense: Unknown license state: %u", state); + return false; + } + + std::string serializedFile; + file.SerializeToString(&serializedFile); + + return StoreFileWithHash(keySetId + kLicenseFileNameExt, serializedFile); +} + +bool DeviceFiles::StoreFileWithHash(const std::string& fileName, + const std::string& serializedFile) { + std::string hash; + if (!Hash(serializedFile, &hash)) { + ALOGE("StoreFileWithHash: Failed to compute hash"); + return false; + } + + HashedFile hashFile; + hashFile.set_file(serializedFile); + hashFile.set_hash(hash); + + std::string serializedHashFile; + hashFile.SerializeToString(&serializedHashFile); + + return StoreFileRaw(fileName, serializedHashFile); +} + +bool DeviceFiles::StoreFileRaw(const std::string& fileName, const std::string& serializedHashFile) { + MemoryFileSystem::MemoryFile memFile; + memFile.setFileName(fileName); + memFile.setContent(serializedHashFile); + memFile.setFileSize(serializedHashFile.size()); + size_t len = mFileHandle.Write(fileName, memFile); + + if (len != static_cast(serializedHashFile.size())) { + ALOGE("StoreFileRaw: Failed to write %s", fileName.c_str()); + ALOGD("StoreFileRaw: expected=%zd, actual=%zu", serializedHashFile.size(), len); + return false; + } + + ALOGD("StoreFileRaw: wrote %zu bytes to %s", serializedHashFile.size(), fileName.c_str()); + return true; +} + +bool DeviceFiles::RetrieveLicense(const std::string& keySetId, LicenseState* state, + std::string* offlineLicense) { + OfflineFile file; + if (!RetrieveHashedFile(keySetId + kLicenseFileNameExt, &file)) { + return false; + } + + if (file.type() != OfflineFile::LICENSE) { + ALOGE("RetrieveLicense: Invalid file type"); + return false; + } + + if (file.version() != OfflineFile::VERSION_1) { + ALOGE("RetrieveLicense: Invalid file version"); + return false; + } + + if (!file.has_license()) { + ALOGE("RetrieveLicense: License not present"); + return false; + } + + License license = file.license(); + switch (license.state()) { + case License_LicenseState_ACTIVE: + *state = kLicenseStateActive; + break; + case License_LicenseState_RELEASING: + *state = kLicenseStateReleasing; + break; + default: + ALOGW("RetrieveLicense: Unrecognized license state: %u", kLicenseStateUnknown); + *state = kLicenseStateUnknown; + break; + } + *offlineLicense = license.license(); + return true; +} + +bool DeviceFiles::DeleteLicense(const std::string& keySetId) { + return mFileHandle.RemoveFile(keySetId + kLicenseFileNameExt); +} + +bool DeviceFiles::DeleteAllLicenses() { + return mFileHandle.RemoveAllFiles(); +} + +bool DeviceFiles::LicenseExists(const std::string& keySetId) { + return mFileHandle.FileExists(keySetId + kLicenseFileNameExt); +} + +std::vector DeviceFiles::ListLicenses() const { + std::vector licenses = mFileHandle.ListFiles(); + for (size_t i = 0; i < licenses.size(); i++) { + std::string& license = licenses[i]; + license = license.substr(0, license.size() - strlen(kLicenseFileNameExt)); + } + return licenses; +} + +bool DeviceFiles::RetrieveHashedFile(const std::string& fileName, OfflineFile* deSerializedFile) { + if (!deSerializedFile) { + ALOGE("RetrieveHashedFile: invalid file parameter"); + return false; + } + + if (!FileExists(fileName)) { + ALOGE("RetrieveHashedFile: %s does not exist", fileName.c_str()); + return false; + } + + ssize_t bytes = GetFileSize(fileName); + if (bytes <= 0) { + ALOGE("RetrieveHashedFile: invalid file size: %s", fileName.c_str()); + // Remove the corrupted file so the caller will not get the same error + // when trying to access the file repeatedly, causing the system to stall. + RemoveFile(fileName); + return false; + } + + std::string serializedHashFile; + serializedHashFile.resize(bytes); + bytes = mFileHandle.Read(fileName, &serializedHashFile); + + if (bytes != static_cast(serializedHashFile.size())) { + ALOGE("RetrieveHashedFile: Failed to read from %s", fileName.c_str()); + ALOGV("RetrieveHashedFile: expected: %zd, actual: %zd", serializedHashFile.size(), bytes); + // Remove the corrupted file so the caller will not get the same error + // when trying to access the file repeatedly, causing the system to stall. + RemoveFile(fileName); + return false; + } + + ALOGV("RetrieveHashedFile: read %zd from %s", bytes, fileName.c_str()); + + HashedFile hashFile; + if (!hashFile.ParseFromString(serializedHashFile)) { + ALOGE("RetrieveHashedFile: Unable to parse hash file"); + // Remove corrupt file. + RemoveFile(fileName); + return false; + } + + std::string hash; + if (!Hash(hashFile.file(), &hash)) { + ALOGE("RetrieveHashedFile: Hash computation failed"); + return false; + } + + if (hash != hashFile.hash()) { + ALOGE("RetrieveHashedFile: Hash mismatch"); + // Remove corrupt file. + RemoveFile(fileName); + return false; + } + + if (!deSerializedFile->ParseFromString(hashFile.file())) { + ALOGE("RetrieveHashedFile: Unable to parse file"); + // Remove corrupt file. + RemoveFile(fileName); + return false; + } + + return true; +} + +bool DeviceFiles::FileExists(const std::string& fileName) const { + return mFileHandle.FileExists(fileName); +} + +bool DeviceFiles::RemoveFile(const std::string& fileName) { + return mFileHandle.RemoveFile(fileName); +} + +ssize_t DeviceFiles::GetFileSize(const std::string& fileName) const { + return mFileHandle.GetFileSize(fileName); +} + +} // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/common/InitDataParser.cpp b/drm/mediadrm/plugins/clearkey/common/InitDataParser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fc839e9a004720a40a1dfa826204267a45d153e3 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/InitDataParser.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2021 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 "clearkey-InitDataParser" + +#include +#include +#include + +#include "InitDataParser.h" + +#include "Base64.h" + +#include "ClearKeyUUID.h" +#include "MimeType.h" + +namespace { +const size_t kKeyIdSize = 16; +const size_t kSystemIdSize = 16; +} // namespace + +namespace clearkeydrm { + +std::vector StrToVector(const std::string& str) { + std::vector vec(str.begin(), str.end()); + return vec; +} + +CdmResponseType InitDataParser::parse(const std::vector& initData, + const std::string& mimeType, + CdmKeyType keyType, + std::vector* licenseRequest) { + // Build a list of the key IDs + std::vector keyIds; + + if (mimeType == kIsoBmffVideoMimeType.c_str() || mimeType == kIsoBmffAudioMimeType.c_str() || + mimeType == kCencInitDataFormat.c_str()) { + auto res = parsePssh(initData, &keyIds); + if (res != clearkeydrm::OK) { + return res; + } + } else if (mimeType == kWebmVideoMimeType.c_str() || mimeType == kWebmAudioMimeType.c_str() || + mimeType == kWebmInitDataFormat.c_str()) { + // WebM "init data" is just a single key ID + if (initData.size() != kKeyIdSize) { + return clearkeydrm::ERROR_CANNOT_HANDLE; + } + keyIds.push_back(initData.data()); + } else { + return clearkeydrm::ERROR_CANNOT_HANDLE; + } + + if (keyType == clearkeydrm::KEY_TYPE_RELEASE) { + // restore key + } + + // Build the request + std::string requestJson = generateRequest(keyType, keyIds); + std::vector requestJsonVec = StrToVector(requestJson); + + licenseRequest->clear(); + licenseRequest->insert(licenseRequest->end(), requestJsonVec.begin(), requestJsonVec.end()); + return clearkeydrm::OK; +} + +CdmResponseType InitDataParser::parsePssh(const std::vector& initData, + std::vector* keyIds) { + // Description of PSSH format: + // https://w3c.github.io/encrypted-media/format-registry/initdata/cenc.html + size_t readPosition = 0; + + uint32_t expectedSize = initData.size(); + const char psshIdentifier[4] = {'p', 's', 's', 'h'}; + const uint8_t psshVersion1[4] = {1, 0, 0, 0}; + uint32_t keyIdCount = 0; + size_t headerSize = sizeof(expectedSize) + sizeof(psshIdentifier) + sizeof(psshVersion1) + + kSystemIdSize + sizeof(keyIdCount); + if (initData.size() < headerSize) { + return clearkeydrm::ERROR_CANNOT_HANDLE; + } + + // Validate size field + expectedSize = htonl(expectedSize); + if (memcmp(&initData[readPosition], &expectedSize, sizeof(expectedSize)) != 0) { + return clearkeydrm::ERROR_CANNOT_HANDLE; + } + readPosition += sizeof(expectedSize); + + // Validate PSSH box identifier + if (memcmp(&initData[readPosition], psshIdentifier, sizeof(psshIdentifier)) != 0) { + return clearkeydrm::ERROR_CANNOT_HANDLE; + } + readPosition += sizeof(psshIdentifier); + + // Validate EME version number + if (memcmp(&initData[readPosition], psshVersion1, sizeof(psshVersion1)) != 0) { + return clearkeydrm::ERROR_CANNOT_HANDLE; + } + readPosition += sizeof(psshVersion1); + + // Validate system ID + if (!isClearKeyUUID(&initData[readPosition])) { + return clearkeydrm::ERROR_CANNOT_HANDLE; + } + readPosition += kSystemIdSize; + + // Read key ID count + memcpy(&keyIdCount, &initData[readPosition], sizeof(keyIdCount)); + keyIdCount = ntohl(keyIdCount); + readPosition += sizeof(keyIdCount); + + uint64_t psshSize = 0; + if (__builtin_mul_overflow(keyIdCount, kKeyIdSize, &psshSize) || + __builtin_add_overflow(readPosition, psshSize, &psshSize) || + psshSize != initData.size() - sizeof(uint32_t) /* DataSize(0) */) { + return clearkeydrm::ERROR_CANNOT_HANDLE; + } + + // Calculate the key ID offsets + for (uint32_t i = 0; i < keyIdCount; ++i) { + size_t keyIdPosition = readPosition + (i * kKeyIdSize); + keyIds->push_back(&initData[keyIdPosition]); + } + return clearkeydrm::OK; +} + +std::string InitDataParser::generateRequest(CdmKeyType keyType, + const std::vector& keyIds) { + const std::string kRequestPrefix("{\"kids\":["); + const std::string kTemporarySession("],\"type\":\"temporary\"}"); + const std::string kPersistentSession("],\"type\":\"persistent-license\"}"); + + std::string request(kRequestPrefix); + std::string encodedId; + for (size_t i = 0; i < keyIds.size(); ++i) { + encodedId.clear(); + encodeBase64Url(keyIds[i], kKeyIdSize, &encodedId); + if (i != 0) { + request.append(","); + } + request.push_back('\"'); + request.append(encodedId); + request.push_back('\"'); + } + if (keyType == clearkeydrm::KEY_TYPE_STREAMING) { + request.append(kTemporarySession); + } else if (keyType == clearkeydrm::KEY_TYPE_OFFLINE || + keyType == clearkeydrm::KEY_TYPE_RELEASE) { + request.append(kPersistentSession); + } + + // Android's Base64 encoder produces padding. EME forbids padding. + const char kBase64Padding = '='; + request.erase(std::remove(request.begin(), request.end(), kBase64Padding), request.end()); + + return request; +} + +} // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/common/JsonWebKey.cpp b/drm/mediadrm/plugins/clearkey/common/JsonWebKey.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ddbc594276db3135934da15688c014546ca1675d --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/JsonWebKey.cpp @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2021 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 "clearkey-JsonWebKey" + +#include + +#include "JsonWebKey.h" + +#include "Base64.h" + +namespace { +const std::string kBase64Padding("="); +const std::string kKeysTag("keys"); +const std::string kKeyTypeTag("kty"); +const std::string kKeyTag("k"); +const std::string kKeyIdTag("kid"); +const std::string kMediaSessionType("type"); +const std::string kPersistentLicenseSession("persistent-license"); +const std::string kSymmetricKeyValue("oct"); +const std::string kTemporaryLicenseSession("temporary"); +} // namespace + +namespace clearkeydrm { + +JsonWebKey::JsonWebKey() {} + +JsonWebKey::~JsonWebKey() {} + +/* + * Parses a JSON Web Key Set string, initializes a KeyMap with key id:key + * pairs from the JSON Web Key Set. Both key ids and keys are base64url + * encoded. The KeyMap contains base64url decoded key id:key pairs. + * + * @return Returns false for errors, true for success. + */ +bool JsonWebKey::extractKeysFromJsonWebKeySet(const std::string& jsonWebKeySet, KeyMap* keys) { + keys->clear(); + + if (!parseJsonWebKeySet(jsonWebKeySet, &mJsonObjects)) { + return false; + } + + // mJsonObjects[0] contains the entire JSON Web Key Set, including + // all the base64 encoded keys. Each key is also stored separately as + // a JSON object in mJsonObjects[1..n] where n is the total + // number of keys in the set. + if (mJsonObjects.size() == 0 || !isJsonWebKeySet(mJsonObjects[0])) { + return false; + } + + std::string encodedKey, encodedKeyId; + std::vector decodedKey, decodedKeyId; + + // mJsonObjects[1] contains the first JSON Web Key in the set + for (size_t i = 1; i < mJsonObjects.size(); ++i) { + encodedKeyId.clear(); + encodedKey.clear(); + + if (!parseJsonObject(mJsonObjects[i], &mTokens)) return false; + + if (findKey(mJsonObjects[i], &encodedKeyId, &encodedKey)) { + if (encodedKeyId.empty() || encodedKey.empty()) { + ALOGE("Must have both key id and key in the JsonWebKey set."); + continue; + } + + if (!decodeBase64String(encodedKeyId, &decodedKeyId)) { + ALOGE("Failed to decode key id(%s)", encodedKeyId.c_str()); + continue; + } + + if (!decodeBase64String(encodedKey, &decodedKey)) { + ALOGE("Failed to decode key(%s)", encodedKey.c_str()); + continue; + } + + keys->insert(std::pair, std::vector>(decodedKeyId, + decodedKey)); + } + } + return true; +} + +bool JsonWebKey::decodeBase64String(const std::string& encodedText, + std::vector* decodedText) { + decodedText->clear(); + + // encodedText should not contain padding characters as per EME spec. + if (encodedText.find(kBase64Padding) != std::string::npos) { + return false; + } + + // Since decodeBase64() requires padding characters, + // add them so length of encodedText is exactly a multiple of 4. + int remainder = encodedText.length() % 4; + std::string paddedText(encodedText); + if (remainder > 0) { + for (int i = 0; i < 4 - remainder; ++i) { + paddedText.append(kBase64Padding); + } + } + + ::android::sp buffer = decodeBase64(paddedText); + if (buffer == nullptr) { + ALOGE("Malformed base64 encoded content found."); + return false; + } + + decodedText->insert(decodedText->end(), buffer->base(), buffer->base() + buffer->size()); + return true; +} + +bool JsonWebKey::findKey(const std::string& jsonObject, std::string* keyId, + std::string* encodedKey) { + std::string key, value; + + // Only allow symmetric key, i.e. "kty":"oct" pair. + if (jsonObject.find(kKeyTypeTag) != std::string::npos) { + findValue(kKeyTypeTag, &value); + if (0 != value.compare(kSymmetricKeyValue)) return false; + } + + if (jsonObject.find(kKeyIdTag) != std::string::npos) { + findValue(kKeyIdTag, keyId); + } + + if (jsonObject.find(kKeyTag) != std::string::npos) { + findValue(kKeyTag, encodedKey); + } + return true; +} + +void JsonWebKey::findValue(const std::string& key, std::string* value) { + value->clear(); + const char* valueToken; + for (std::vector::const_iterator nextToken = mTokens.begin(); + nextToken != mTokens.end(); ++nextToken) { + if (0 == (*nextToken).compare(key)) { + if (nextToken + 1 == mTokens.end()) break; + valueToken = (*(nextToken + 1)).c_str(); + value->assign(valueToken); + nextToken++; + break; + } + } +} + +bool JsonWebKey::isJsonWebKeySet(const std::string& jsonObject) const { + if (jsonObject.find(kKeysTag) == std::string::npos) { + ALOGE("JSON Web Key does not contain keys."); + return false; + } + return true; +} + +/* + * Parses a JSON objects string and initializes a vector of tokens. + * + * @return Returns false for errors, true for success. + */ +bool JsonWebKey::parseJsonObject(const std::string& jsonObject, std::vector* tokens) { + jsmn_parser parser; + + jsmn_init(&parser); + int numTokens = jsmn_parse(&parser, jsonObject.c_str(), jsonObject.size(), nullptr, 0); + if (numTokens < 0) { + ALOGE("Parser returns error code=%d", numTokens); + return false; + } + + unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t); + mJsmnTokens.clear(); + mJsmnTokens.resize(jsmnTokensSize); + + jsmn_init(&parser); + int status = jsmn_parse(&parser, jsonObject.c_str(), jsonObject.size(), mJsmnTokens.data(), + numTokens); + if (status < 0) { + ALOGE("Parser returns error code=%d", status); + return false; + } + + tokens->clear(); + std::string token; + const char* pjs; + for (int j = 0; j < numTokens; ++j) { + pjs = jsonObject.c_str() + mJsmnTokens[j].start; + if (mJsmnTokens[j].type == JSMN_STRING || mJsmnTokens[j].type == JSMN_PRIMITIVE) { + token.assign(pjs, mJsmnTokens[j].end - mJsmnTokens[j].start); + tokens->push_back(token); + } + } + return true; +} + +/* + * Parses JSON Web Key Set string and initializes a vector of JSON objects. + * + * @return Returns false for errors, true for success. + */ +bool JsonWebKey::parseJsonWebKeySet(const std::string& jsonWebKeySet, + std::vector* jsonObjects) { + if (jsonWebKeySet.empty()) { + ALOGE("Empty JSON Web Key"); + return false; + } + + // The jsmn parser only supports unicode encoding. + jsmn_parser parser; + + // Computes number of tokens. A token marks the type, offset in + // the original string. + jsmn_init(&parser); + int numTokens = jsmn_parse(&parser, jsonWebKeySet.c_str(), jsonWebKeySet.size(), nullptr, 0); + if (numTokens < 0) { + ALOGE("Parser returns error code=%d", numTokens); + return false; + } + + unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t); + mJsmnTokens.resize(jsmnTokensSize); + + jsmn_init(&parser); + int status = jsmn_parse(&parser, jsonWebKeySet.c_str(), jsonWebKeySet.size(), + mJsmnTokens.data(), numTokens); + if (status < 0) { + ALOGE("Parser returns error code=%d", status); + return false; + } + + std::string token; + const char* pjs; + for (int i = 0; i < numTokens; ++i) { + pjs = jsonWebKeySet.c_str() + mJsmnTokens[i].start; + if (mJsmnTokens[i].type == JSMN_OBJECT) { + token.assign(pjs, mJsmnTokens[i].end - mJsmnTokens[i].start); + jsonObjects->push_back(token); + } + } + return true; +} + +} // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/common/MemoryFileSystem.cpp b/drm/mediadrm/plugins/clearkey/common/MemoryFileSystem.cpp new file mode 100644 index 0000000000000000000000000000000000000000..10454588089f8a33b810c7302cd5e263d050caa8 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/MemoryFileSystem.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include + +#include "MemoryFileSystem.h" + +namespace clearkeydrm { + +std::string MemoryFileSystem::GetFileName(const std::string& path) { + size_t index = path.find_last_of('/'); + if (index != std::string::npos) { + return path.substr(index + 1); + } else { + return path; + } +} + +bool MemoryFileSystem::FileExists(const std::string& fileName) const { + auto result = mMemoryFileSystem.find(fileName); + return result != mMemoryFileSystem.end(); +} + +ssize_t MemoryFileSystem::GetFileSize(const std::string& fileName) const { + auto result = mMemoryFileSystem.find(fileName); + if (result != mMemoryFileSystem.end()) { + return static_cast(result->second.getFileSize()); + } else { + ALOGE("Failed to get size for %s", fileName.c_str()); + return -1; + } +} + +std::vector MemoryFileSystem::ListFiles() const { + std::vector list; + for (const auto& filename : mMemoryFileSystem) { + list.push_back(filename.first); + } + return list; +} + +size_t MemoryFileSystem::Read(const std::string& path, std::string* buffer) { + std::string key = GetFileName(path); + auto result = mMemoryFileSystem.find(key); + if (result != mMemoryFileSystem.end()) { + std::string serializedHashFile = result->second.getContent(); + buffer->assign(serializedHashFile); + return buffer->size(); + } else { + ALOGE("Failed to read from %s", path.c_str()); + return -1; + } +} + +size_t MemoryFileSystem::Write(const std::string& path, const MemoryFile& memoryFile) { + std::string key = GetFileName(path); + auto result = mMemoryFileSystem.find(key); + if (result != mMemoryFileSystem.end()) { + mMemoryFileSystem.erase(key); + } + mMemoryFileSystem.insert(std::pair(key, memoryFile)); + return memoryFile.getFileSize(); +} + +bool MemoryFileSystem::RemoveFile(const std::string& fileName) { + auto result = mMemoryFileSystem.find(fileName); + if (result != mMemoryFileSystem.end()) { + mMemoryFileSystem.erase(result); + return true; + } else { + ALOGE("Cannot find license to remove: %s", fileName.c_str()); + return false; + } +} + +bool MemoryFileSystem::RemoveAllFiles() { + mMemoryFileSystem.clear(); + return mMemoryFileSystem.empty(); +} + +} // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/common/Session.cpp b/drm/mediadrm/plugins/clearkey/common/Session.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d7fd13a8b1074ba495c9b046edb5958b7dbbaaef --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/Session.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2021 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 "clearkey-Session" + +#include + +#include "Session.h" + +#include "AesCtrDecryptor.h" +#include "InitDataParser.h" +#include "JsonWebKey.h" + +namespace clearkeydrm { + +using ::android::Mutex; +using ::android::sp; + +CdmResponseType Session::getKeyRequest(const std::vector& initData, + const std::string& mimeType, + CdmKeyType keyType, + std::vector* keyRequest) const { + InitDataParser parser; + return parser.parse(initData, mimeType, keyType, keyRequest); +} + +CdmResponseType Session::provideKeyResponse(const std::vector& response) { + std::string responseString(reinterpret_cast(response.data()), response.size()); + KeyMap keys; + + Mutex::Autolock lock(mMapLock); + JsonWebKey parser; + if (parser.extractKeysFromJsonWebKeySet(responseString, &keys)) { + for (auto& key : keys) { + std::string first(key.first.begin(), key.first.end()); + std::string second(key.second.begin(), key.second.end()); + mKeyMap.insert( + std::pair, std::vector>(key.first, key.second)); + } + return clearkeydrm::OK; + } else { + return clearkeydrm::ERROR_UNKNOWN; + } +} + +CdmResponseType Session::decrypt(const KeyId keyId, const Iv iv, + const uint8_t* srcPtr, uint8_t* destPtr, + const std::vector& clearDataLengths, + const std::vector& encryptedDataLengths, + size_t* bytesDecryptedOut) { + Mutex::Autolock lock(mMapLock); + + if (getMockError() != clearkeydrm::OK) { + return getMockError(); + } + + std::vector keyIdVector; + keyIdVector.clear(); + keyIdVector.insert(keyIdVector.end(), keyId, keyId + kBlockSize); + std::map, std::vector>::iterator itr; + itr = mKeyMap.find(keyIdVector); + if (itr == mKeyMap.end()) { + return clearkeydrm::ERROR_NO_LICENSE; + } + + clearkeydrm::AesCtrDecryptor decryptor; + auto status = decryptor.decrypt(itr->second /*key*/, iv, srcPtr, destPtr, + clearDataLengths, + encryptedDataLengths, + bytesDecryptedOut); + return status; +} + +} // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/common/SessionLibrary.cpp b/drm/mediadrm/plugins/clearkey/common/SessionLibrary.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6b2ff387b4ffa0f9335b63efc8be9d381a6a77d7 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/SessionLibrary.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2021 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 "clearkey-SessionLibrary" + +#include + +#include "SessionLibrary.h" + +namespace clearkeydrm { + +using ::android::Mutex; +using ::android::sp; + +Mutex SessionLibrary::sSingletonLock; +SessionLibrary* SessionLibrary::sSingleton = NULL; + +SessionLibrary* SessionLibrary::get() { + Mutex::Autolock lock(sSingletonLock); + + if (sSingleton == NULL) { + ALOGD("Instantiating Session Library Singleton."); + sSingleton = new SessionLibrary(); + } + + return sSingleton; +} + +sp SessionLibrary::createSession() { + Mutex::Autolock lock(mSessionsLock); + + char sessionIdRaw[16]; + snprintf(sessionIdRaw, sizeof(sessionIdRaw), "%u", mNextSessionId); + + mNextSessionId += 1; + + std::vector sessionId; + sessionId.insert(sessionId.end(), sessionIdRaw, + sessionIdRaw + sizeof(sessionIdRaw) / sizeof(uint8_t)); + + mSessions.insert( + std::pair, sp>(sessionId, new Session(sessionId))); + std::map, sp>::iterator itr = mSessions.find(sessionId); + if (itr != mSessions.end()) { + return itr->second; + } else { + return nullptr; + } +} + +sp SessionLibrary::findSession(const std::vector& sessionId) { + Mutex::Autolock lock(mSessionsLock); + std::map, sp>::iterator itr = mSessions.find(sessionId); + if (itr != mSessions.end()) { + return itr->second; + } else { + return nullptr; + } +} + +void SessionLibrary::destroySession(const sp& session) { + Mutex::Autolock lock(mSessionsLock); + mSessions.erase(session->sessionId()); +} + +} // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/common/include/ClearKeyUUID.h b/drm/mediadrm/plugins/clearkey/common/include/ClearKeyUUID.h index fe10fba755921eb053f96a9edd078b32cb8f6419..8911024f9878007c06c79b551543d1a9042b5984 100644 --- a/drm/mediadrm/plugins/clearkey/common/include/ClearKeyUUID.h +++ b/drm/mediadrm/plugins/clearkey/common/include/ClearKeyUUID.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * Copyright (C) 2021 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. @@ -13,9 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#ifndef CLEARKEY_UUID_H_ -#define CLEARKEY_UUID_H_ +#pragma once #include #include @@ -27,6 +25,4 @@ bool isClearKeyUUID(const uint8_t uuid[16]); std::vector> getSupportedCryptoSchemes(); -} // namespace clearkeydrm - -#endif // CLEARKEY_UUID_H_ +} // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/AesCtrDecryptor.h b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/AesCtrDecryptor.h new file mode 100644 index 0000000000000000000000000000000000000000..dbf30987f9c450e018b5ef8fa5cb2bfe358a7a9b --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/AesCtrDecryptor.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 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. + */ +#pragma once + +#include + +#include "ClearKeyTypes.h" + +namespace clearkeydrm { + +class AesCtrDecryptor { + public: + AesCtrDecryptor() {} + + CdmResponseType decrypt(const std::vector& key, const Iv iv, const uint8_t* source, + uint8_t* destination, + const std::vector& clearDataLengths, + const std::vector& encryptedDataLengths, + size_t* bytesDecryptedOut); + + private: + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(AesCtrDecryptor); +}; + +} // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/Base64.h b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/Base64.h new file mode 100644 index 0000000000000000000000000000000000000000..075d247afa732794d1526f2b5069585bf8fc0490 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/Base64.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2021 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. + */ +#pragma once + +#include "Buffer.h" + +namespace clearkeydrm { + +struct Buffer; + +::android::sp decodeBase64(const std::string& s); + +void encodeBase64(const void* data, size_t size, std::string* out); + +void encodeBase64Url(const void* data, size_t size, std::string* out); + +} // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/Buffer.h b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/Buffer.h new file mode 100644 index 0000000000000000000000000000000000000000..d41c4f34fd753368bdb94295e1ca15087cf03a8b --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/Buffer.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2021 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. + */ +#pragma once + +#include + +#include "ClearKeyTypes.h" + +namespace clearkeydrm { +struct Buffer : public ::android::RefBase { + explicit Buffer(size_t capacity); + + uint8_t* base() { return reinterpret_cast(mData); } + uint8_t* data() { return reinterpret_cast(mData) + mRangeOffset; } + size_t capacity() const { return mCapacity; } + size_t size() const { return mRangeLength; } + size_t offset() const { return mRangeOffset; } + + protected: + virtual ~Buffer(); + + private: + void* mData; + size_t mCapacity; + size_t mRangeOffset; + size_t mRangeLength; + + bool mOwnsData; + + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(Buffer); +}; + +} // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/ClearKeyDrmProperties.h b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/ClearKeyDrmProperties.h new file mode 100644 index 0000000000000000000000000000000000000000..bfda3883b8eb86b039c2e003bd79a6797b1038c4 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/ClearKeyDrmProperties.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2021 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. + */ +#pragma once + +#include + +namespace clearkeydrm { +static const std::string kVendorKey("vendor"); +static const std::string kVendorValue("Google"); +static const std::string kVersionKey("version"); +static const std::string kVersionValue("1.2"); +static const std::string kPluginDescriptionKey("description"); +static const std::string kPluginDescriptionValue("ClearKey CDM"); +static const std::string kAlgorithmsKey("algorithms"); +static const std::string kAlgorithmsValue(""); +static const std::string kListenerTestSupportKey("listenerTestSupport"); +static const std::string kListenerTestSupportValue("true"); +static const std::string kDrmErrorTestKey("drmErrorTest"); +static const std::string kDrmErrorTestValue(""); +static const std::string kResourceContentionValue("resourceContention"); +static const std::string kLostStateValue("lostState"); +static const std::string kFrameTooLargeValue("frameTooLarge"); +static const std::string kInvalidStateValue("invalidState"); +static const std::string kAidlVersionKey("aidlVersion"); + +static const std::string kDeviceIdKey("deviceId"); +static const uint8_t kTestDeviceIdData[] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; + +// settable byte array property +static const std::string kClientIdKey("clientId"); + +// TODO stub out metrics for nw +static const std::string kMetricsKey("metrics"); +static const uint8_t kMetricsData[] = {0}; + +} // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/ClearKeyTypes.h b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/ClearKeyTypes.h new file mode 100644 index 0000000000000000000000000000000000000000..0cc9511fa70ae33b24af760e08f2c2b20e3b5b46 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/ClearKeyTypes.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2021 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. + */ + +#pragma once + +#include +#include +#include + +namespace clearkeydrm { + +const uint8_t kBlockSize = 16; // AES_BLOCK_SIZE; +typedef uint8_t KeyId[kBlockSize]; +typedef uint8_t Iv[kBlockSize]; + +typedef std::map, std::vector> KeyMap; + +#define CLEARKEY_DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete; + +#define CLEARKEY_DISALLOW_COPY_AND_ASSIGN_AND_NEW(TypeName) \ + TypeName() = delete; \ + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete; + +enum CdmResponseType : int32_t { + OK = 0, + ERROR_NO_LICENSE = 1, + ERROR_SESSION_NOT_OPENED = 3, + ERROR_CANNOT_HANDLE = 4, + ERROR_INVALID_STATE = 5, + BAD_VALUE = 6, + ERROR_DECRYPT = 11, + ERROR_UNKNOWN = 12, + ERROR_INSUFFICIENT_SECURITY = 13, + ERROR_FRAME_TOO_LARGE = 14, + ERROR_SESSION_LOST_STATE = 15, + ERROR_RESOURCE_CONTENTION = 16, +}; + +enum CdmKeyType : int32_t { + KEY_TYPE_OFFLINE = 0, + KEY_TYPE_STREAMING = 1, + KEY_TYPE_RELEASE = 2, +}; + +} // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/DeviceFiles.h b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/DeviceFiles.h new file mode 100644 index 0000000000000000000000000000000000000000..56984419945d4f6bf67ec74961015a2f6a327e1a --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/DeviceFiles.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2021 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. + */ +#pragma once + +#include +#include +#include + +#include +#include +#include + +#include "ClearKeyTypes.h" +#include "MemoryFileSystem.h" + +namespace clearkeydrm { +class OfflineFile; +class DeviceFiles { + public: + typedef enum { + kLicenseStateUnknown, + kLicenseStateActive, + kLicenseStateReleasing, + } LicenseState; + + DeviceFiles(){}; + virtual ~DeviceFiles(){}; + + virtual bool StoreLicense(const std::string& keySetId, LicenseState state, + const std::string& keyResponse); + + virtual bool RetrieveLicense(const std::string& key_set_id, LicenseState* state, + std::string* offlineLicense); + + virtual bool LicenseExists(const std::string& keySetId); + + virtual std::vector ListLicenses() const; + + virtual bool DeleteLicense(const std::string& keySetId); + + virtual bool DeleteAllLicenses(); + + private: + bool FileExists(const std::string& path) const; + ssize_t GetFileSize(const std::string& fileName) const; + bool RemoveFile(const std::string& fileName); + + bool RetrieveHashedFile( + const std::string& fileName, + OfflineFile* deSerializedFile); + bool StoreFileRaw(const std::string& fileName, const std::string& serializedFile); + bool StoreFileWithHash(const std::string& fileName, const std::string& serializedFile); + + MemoryFileSystem mFileHandle; + + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(DeviceFiles); +}; + +} // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/InitDataParser.h b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/InitDataParser.h new file mode 100644 index 0000000000000000000000000000000000000000..8ecc8e3fe97a1f22c5d54d976cf5f09ad36901e3 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/InitDataParser.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 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. + */ +#pragma once + +#include "ClearKeyTypes.h" + +namespace clearkeydrm { + +class InitDataParser { + public: + InitDataParser() {} + + CdmResponseType parse(const std::vector& initData, const std::string& mimeType, + CdmKeyType keyType, std::vector* licenseRequest); + + private: + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(InitDataParser); + + CdmResponseType parsePssh(const std::vector& initData, + std::vector* keyIds); + + std::string generateRequest(CdmKeyType keyType, const std::vector& keyIds); +}; + +} // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/JsonWebKey.h b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/JsonWebKey.h new file mode 100644 index 0000000000000000000000000000000000000000..668155327237f1880b01a3c16150914b24dabcb6 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/JsonWebKey.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2021 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. + */ +#pragma once + +#include +#include + +#include "jsmn.h" +#include "ClearKeyTypes.h" + +namespace clearkeydrm { + +class JsonWebKey { + public: + JsonWebKey(); + virtual ~JsonWebKey(); + + bool extractKeysFromJsonWebKeySet(const std::string& jsonWebKeySet, KeyMap* keys); + + private: + std::vector mJsmnTokens; + std::vector mJsonObjects; + std::vector mTokens; + + bool decodeBase64String(const std::string& encodedText, std::vector* decodedText); + bool findKey(const std::string& jsonObject, std::string* keyId, std::string* encodedKey); + void findValue(const std::string& key, std::string* value); + bool isJsonWebKeySet(const std::string& jsonObject) const; + bool parseJsonObject(const std::string& jsonObject, std::vector* tokens); + bool parseJsonWebKeySet(const std::string& jsonWebKeySet, + std::vector* jsonObjects); + + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(JsonWebKey); +}; + +} // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/MemoryFileSystem.h b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/MemoryFileSystem.h new file mode 100644 index 0000000000000000000000000000000000000000..5642a0f80724337aa41f78ad716cdecddb64fe34 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/MemoryFileSystem.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2021 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. + */ +#pragma once + +#include +#include + +#include "ClearKeyTypes.h" + +namespace clearkeydrm { + +// Using android file system requires clearkey plugin to update +// its sepolicy. However, we are unable to update sepolicy for +// older vendor partitions. To provide backward compatibility, +// clearkey plugin implements a very simple file system in memory. +// This memory file system does not support directory structure. +class MemoryFileSystem { + public: + struct MemoryFile { + std::string fileName; // excludes path + std::string content; + size_t fileSize; + + std::string getContent() const { return content; } + size_t getFileSize() const { return fileSize; } + void setContent(const std::string& file) { content = file; } + void setFileName(const std::string& name) { fileName = name; } + void setFileSize(size_t size) { + content.resize(size); + fileSize = size; + } + }; + + MemoryFileSystem(){}; + virtual ~MemoryFileSystem(){}; + + bool FileExists(const std::string& fileName) const; + ssize_t GetFileSize(const std::string& fileName) const; + std::vector ListFiles() const; + size_t Read(const std::string& pathName, std::string* buffer); + bool RemoveAllFiles(); + bool RemoveFile(const std::string& fileName); + size_t Write(const std::string& pathName, const MemoryFile& memoryFile); + + private: + // License file name is made up of a unique keySetId, therefore, + // the filename can be used as the key to locate licenses in the + // memory file system. + std::map mMemoryFileSystem; + + std::string GetFileName(const std::string& path); + + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(MemoryFileSystem); +}; + +} // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/MimeTypeStdStr.h b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/MimeTypeStdStr.h new file mode 100644 index 0000000000000000000000000000000000000000..dea29749762153fbff1b0afd4c5efd2f05c5bb76 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/MimeTypeStdStr.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2021 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. + */ +#pragma once + +#include + +namespace { +const std::string kCencInitDataFormat("cenc"); +const std::string kIsoBmffAudioMimeType("audio/mp4"); +const std::string kIsoBmffVideoMimeType("video/mp4"); +const std::string kWebmInitDataFormat("webm"); +const std::string kWebmAudioMimeType("audio/webm"); +const std::string kWebmVideoMimeType("video/webm"); +} // namespace diff --git a/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/Session.h b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/Session.h new file mode 100644 index 0000000000000000000000000000000000000000..e2d4e32537fa9273869966debc28a0f0d0f2732c --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/Session.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2021 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. + */ +#pragma once + +#include +#include + +#include +#include + +#include "ClearKeyTypes.h" + +namespace clearkeydrm { + +class Session : public ::android::RefBase { + public: + explicit Session(const std::vector& sessionId) + : mSessionId(sessionId), mMockError(clearkeydrm::OK) {} + virtual ~Session() {} + + const std::vector& sessionId() const { return mSessionId; } + + CdmResponseType getKeyRequest(const std::vector& initDataType, + const std::string& mimeType, + CdmKeyType keyType, + std::vector* keyRequest) const; + + CdmResponseType provideKeyResponse(const std::vector& response); + + CdmResponseType decrypt(const KeyId keyId, const Iv iv, const uint8_t* srcPtr, uint8_t* dstPtr, + const std::vector& clearDataLengths, + const std::vector& encryptedDataLengths, + size_t* bytesDecryptedOut); + + void setMockError(CdmResponseType error) { mMockError = error; } + CdmResponseType getMockError() const { return mMockError; } + + private: + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(Session); + + const std::vector mSessionId; + KeyMap mKeyMap; + ::android::Mutex mMapLock; + + // For mocking error return scenarios + CdmResponseType mMockError; +}; + +} // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/SessionLibrary.h b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/SessionLibrary.h new file mode 100644 index 0000000000000000000000000000000000000000..17d4a226a1c9d88fe4ceca2f6e9368c8d017726b --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/include/clearkeydrm/SessionLibrary.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2021 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. + */ +#pragma once + +#include +#include + +#include "ClearKeyTypes.h" +#include "Session.h" + +namespace clearkeydrm { + +class SessionLibrary { + public: + static SessionLibrary* get(); + + ::android::sp createSession(); + + ::android::sp findSession(const std::vector& sessionId); + + void destroySession(const ::android::sp& session); + + size_t numOpenSessions() const { return mSessions.size(); } + + private: + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(SessionLibrary); + + SessionLibrary() : mNextSessionId(1) {} + + static ::android::Mutex sSingletonLock; + static SessionLibrary* sSingleton; + + ::android::Mutex mSessionsLock; + uint32_t mNextSessionId; + std::map, ::android::sp> mSessions; +}; + +} // namespace clearkeydrm diff --git a/drm/mediadrm/plugins/clearkey/common/protos/DeviceFiles.proto b/drm/mediadrm/plugins/clearkey/common/protos/DeviceFiles.proto new file mode 100644 index 0000000000000000000000000000000000000000..2d98656e23967947aecc0a366ad69f6417d2ce5e --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/protos/DeviceFiles.proto @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2021 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. + */ +syntax = "proto2"; + +package clearkeydrm; + +// need this if we are using libprotobuf-cpp-2.3.0-lite +option optimize_for = LITE_RUNTIME; + +message License { + enum LicenseState { + ACTIVE = 1; + RELEASING = 2; + } + + optional LicenseState state = 1; + optional bytes license = 2; +} + +message OfflineFile { + enum FileType { + LICENSE = 1; + } + + enum FileVersion { + VERSION_1 = 1; + } + + optional FileType type = 1; + optional FileVersion version = 2 [default = VERSION_1]; + optional License license = 3; + +} + +message HashedFile { + optional bytes file = 1; + // A raw (not hex-encoded) SHA256, taken over the bytes of 'file'. + optional bytes hash = 2; +} diff --git a/drm/mediadrm/plugins/clearkey/hidl/Android.bp b/drm/mediadrm/plugins/clearkey/hidl/Android.bp index 6c68532f2d089629eccb6e352fb6364db5ccd2bc..b82d99688d4ed905502d7a585780d525b98ad0f4 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/Android.bp +++ b/drm/mediadrm/plugins/clearkey/hidl/Android.bp @@ -93,6 +93,11 @@ cc_library_static { srcs: ["protos/DeviceFiles.proto"], } +cc_library { + name: "libclearkeyhidl", + defaults: ["clearkey_service_defaults"], +} + cc_binary { name: "android.hardware.drm@1.2-service.clearkey", defaults: ["clearkey_service_defaults"], @@ -126,3 +131,37 @@ cc_binary { init_rc: ["android.hardware.drm@1.4-service-lazy.clearkey.rc"], vintf_fragments: ["manifest_android.hardware.drm@1.4-service.clearkey.xml"], } + +cc_fuzz { + name: "clearkeyV1.4_fuzzer", + vendor: true, + srcs: [ + "fuzzer/clearkeyV1.4_fuzzer.cpp", + ], + static_libs: [ + "libclearkeyhidl", + "libclearkeycommon", + "libclearkeydevicefiles-protos", + "libjsmn", + "libprotobuf-cpp-lite", + ], + shared_libs: [ + "android.hidl.allocator@1.0", + "android.hardware.drm@1.0", + "android.hardware.drm@1.1", + "android.hardware.drm@1.2", + "android.hardware.drm@1.3", + "android.hardware.drm@1.4", + "libcrypto", + "libhidlbase", + "libhidlmemory", + "liblog", + "libutils", + ], + fuzz_config: { + cc: [ + "android-media-fuzzing-reports@google.com", + ], + componentid: 155276, + }, +} diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp index 6a374f91509eac9cb2cd34931aaf1291069df133..32d77237155ca55ce6c9da06722f44a6b9d70d9c 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp @@ -187,7 +187,7 @@ Status_V1_2 DrmPlugin::getKeyRequestCommon(const hidl_vec& scope, return Status_V1_2::ERROR_DRM_CANNOT_HANDLE; } - *defaultUrl = ""; + *defaultUrl = "https://default.url"; *keyRequestType = KeyRequestType_V1_1::UNKNOWN; *request = std::vector(); @@ -221,7 +221,6 @@ Status_V1_2 DrmPlugin::getKeyRequestCommon(const hidl_vec& scope, if (requestString.find(kOfflineLicense) != std::string::npos) { std::string emptyResponse; std::string keySetIdString(keySetId.begin(), keySetId.end()); - Mutex::Autolock lock(mFileHandleLock); if (!mFileHandle.StoreLicense(keySetIdString, DeviceFiles::kLicenseStateReleasing, emptyResponse)) { @@ -337,7 +336,6 @@ bool DrmPlugin::makeKeySetId(std::string* keySetId) { } *keySetId = kKeySetIdPrefix + ByteArrayToHexString( reinterpret_cast(randomData.data()), randomData.size()); - Mutex::Autolock lock(mFileHandleLock); if (mFileHandle.LicenseExists(*keySetId)) { // collision, regenerate ALOGV("Retry generating KeySetId"); @@ -395,7 +393,6 @@ Return DrmPlugin::provideKeyResponse( if (status == Status::OK) { if (isOfflineLicense) { if (isRelease) { - Mutex::Autolock lock(mFileHandleLock); mFileHandle.DeleteLicense(keySetId); mSessionLibrary->destroySession(session); } else { @@ -404,7 +401,6 @@ Return DrmPlugin::provideKeyResponse( return Void(); } - Mutex::Autolock lock(mFileHandleLock); bool ok = mFileHandle.StoreLicense( keySetId, DeviceFiles::kLicenseStateActive, @@ -459,7 +455,6 @@ Return DrmPlugin::restoreKeys( DeviceFiles::LicenseState licenseState; std::string offlineLicense; Status status = Status::OK; - Mutex::Autolock lock(mFileHandleLock); if (!mFileHandle.RetrieveLicense(std::string(keySetId.begin(), keySetId.end()), &licenseState, &offlineLicense)) { ALOGE("Failed to restore offline license"); @@ -769,8 +764,6 @@ Return DrmPlugin::getMetrics(getMetrics_cb _hidl_cb) { } Return DrmPlugin::getOfflineLicenseKeySetIds(getOfflineLicenseKeySetIds_cb _hidl_cb) { - Mutex::Autolock lock(mFileHandleLock); - std::vector licenseNames = mFileHandle.ListLicenses(); std::vector keySetIds; if (mMockError != Status_V1_2::OK) { @@ -791,7 +784,6 @@ Return DrmPlugin::removeOfflineLicense(const KeySetId& keySetId) { return toStatus_1_0(mMockError); } std::string licenseName(keySetId.begin(), keySetId.end()); - Mutex::Autolock lock(mFileHandleLock); if (mFileHandle.DeleteLicense(licenseName)) { return Status::OK; } @@ -800,8 +792,6 @@ Return DrmPlugin::removeOfflineLicense(const KeySetId& keySetId) { Return DrmPlugin::getOfflineLicenseState(const KeySetId& keySetId, getOfflineLicenseState_cb _hidl_cb) { - Mutex::Autolock lock(mFileHandleLock); - std::string licenseName(keySetId.begin(), keySetId.end()); DeviceFiles::LicenseState state; std::string license; diff --git a/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp b/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp index e61db3f71857c10e9e618d3a482c12386d7e61e8..56910be80c702bfc45ff0f5d1b701ffe63296ed3 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp +++ b/drm/mediadrm/plugins/clearkey/hidl/MemoryFileSystem.cpp @@ -24,13 +24,11 @@ std::string MemoryFileSystem::GetFileName(const std::string& path) { } bool MemoryFileSystem::FileExists(const std::string& fileName) const { - std::lock_guard lock(mMemoryFileSystemLock); auto result = mMemoryFileSystem.find(fileName); return result != mMemoryFileSystem.end(); } ssize_t MemoryFileSystem::GetFileSize(const std::string& fileName) const { - std::lock_guard lock(mMemoryFileSystemLock); auto result = mMemoryFileSystem.find(fileName); if (result != mMemoryFileSystem.end()) { return static_cast(result->second.getFileSize()); @@ -42,7 +40,6 @@ ssize_t MemoryFileSystem::GetFileSize(const std::string& fileName) const { std::vector MemoryFileSystem::ListFiles() const { std::vector list; - std::lock_guard lock(mMemoryFileSystemLock); for (const auto& filename : mMemoryFileSystem) { list.push_back(filename.first); } @@ -51,7 +48,6 @@ std::vector MemoryFileSystem::ListFiles() const { size_t MemoryFileSystem::Read(const std::string& path, std::string* buffer) { std::string key = GetFileName(path); - std::lock_guard lock(mMemoryFileSystemLock); auto result = mMemoryFileSystem.find(key); if (result != mMemoryFileSystem.end()) { std::string serializedHashFile = result->second.getContent(); @@ -65,7 +61,6 @@ size_t MemoryFileSystem::Read(const std::string& path, std::string* buffer) { size_t MemoryFileSystem::Write(const std::string& path, const MemoryFile& memoryFile) { std::string key = GetFileName(path); - std::lock_guard lock(mMemoryFileSystemLock); auto result = mMemoryFileSystem.find(key); if (result != mMemoryFileSystem.end()) { mMemoryFileSystem.erase(key); @@ -75,7 +70,6 @@ size_t MemoryFileSystem::Write(const std::string& path, const MemoryFile& memory } bool MemoryFileSystem::RemoveFile(const std::string& fileName) { - std::lock_guard lock(mMemoryFileSystemLock); auto result = mMemoryFileSystem.find(fileName); if (result != mMemoryFileSystem.end()) { mMemoryFileSystem.erase(result); @@ -87,7 +81,6 @@ bool MemoryFileSystem::RemoveFile(const std::string& fileName) { } bool MemoryFileSystem::RemoveAllFiles() { - std::lock_guard lock(mMemoryFileSystemLock); mMemoryFileSystem.clear(); return mMemoryFileSystem.empty(); } diff --git a/drm/mediadrm/plugins/clearkey/hidl/fuzzer/README.md b/drm/mediadrm/plugins/clearkey/hidl/fuzzer/README.md new file mode 100644 index 0000000000000000000000000000000000000000..cb45460047f5ec52e1fbce799ad463dfac2074e9 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/fuzzer/README.md @@ -0,0 +1,52 @@ +# Fuzzer for android.hardware.drm@1.4-service.clearkey + +## Plugin Design Considerations +The fuzzer plugin for android.hardware.drm@1.4-service.clearkey is designed based on the understanding of the +source code and tries to achieve the following: + +##### Maximize code coverage +The configuration parameters are not hardcoded, but instead selected based on +incoming data. This ensures more code paths are reached by the fuzzer. + +android.hardware.drm@1.4-service.clearkey supports the following parameters: +1. Security Level (parameter name: `securityLevel`) +2. Mime Type (parameter name: `mimeType`) +3. Key Type (parameter name: `keyType`) +4. Crypto Mode (parameter name: `cryptoMode`) + +| Parameter| Valid Values| Configured Value| +|------------- |-------------| ----- | +| `securityLevel` | 0.`SecurityLevel::UNKNOWN` 1.`SecurityLevel::SW_SECURE_CRYPTO` 2.`SecurityLevel::SW_SECURE_DECODE` 3.`SecurityLevel::HW_SECURE_CRYPTO` 4.`SecurityLevel::HW_SECURE_DECODE` 5.`SecurityLevel::HW_SECURE_ALL`| Value obtained from FuzzedDataProvider in the range 0 to 5| +| `mimeType` | 0.`video/mp4` 1.`video/mpeg` 2.`video/x-flv` 3.`video/mj2` 4.`video/3gp2` 5.`video/3gpp` 6.`video/3gpp2` 7.`audio/mp4` 8.`audio/mpeg` 9.`audio/aac` 10.`audio/3gp2` 11.`audio/3gpp` 12.`audio/3gpp2` 13.`audio/webm` 14.`video/webm` 15.`webm` 16.`cenc` 17.`video/unknown` 18.`audio/unknown`| Value obtained from FuzzedDataProvider in the range 0 to 18| +| `keyType` | 0.`KeyType::OFFLINE` 1.`KeyType::STREAMING` 2.`KeyType::RELEASE` | Value obtained from FuzzedDataProvider in the range 0 to 2| +| `cryptoMode` | 0.`Mode::UNENCRYPTED` 1.`Mode::AES_CTR` 2.`Mode::AES_CBC_CTS` 3.`Mode::AES_CBC` | Value obtained from FuzzedDataProvider in the range 0 to 3| + +This also ensures that the plugin is always deterministic for any given input. + +##### Maximize utilization of input data +The plugin feeds the entire input data to the module. +This ensures that the plugin tolerates any kind of input (empty, huge, +malformed, etc) and doesnt `exit()` on any input and thereby increasing the +chance of identifying vulnerabilities. + +## Build + +This describes steps to build clearkeyV1.4_fuzzer binary. + +### Android + +#### Steps to build +Build the fuzzer +``` + $ mm -j$(nproc) clearkeyV1.4_fuzzer +``` +#### Steps to run +To run on device +``` + $ adb sync data + $ adb shell /data/fuzz/${TARGET_ARCH}/clearkeyV1.4_fuzzer/vendor/hw/clearkeyV1.4_fuzzer +``` + +## References: + * http://llvm.org/docs/LibFuzzer.html + * https://github.com/google/oss-fuzz diff --git a/drm/mediadrm/plugins/clearkey/hidl/fuzzer/clearkeyV1.4_fuzzer.cpp b/drm/mediadrm/plugins/clearkey/hidl/fuzzer/clearkeyV1.4_fuzzer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..afe0e6c93d8906b5137e5840947c21541e0b765a --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/fuzzer/clearkeyV1.4_fuzzer.cpp @@ -0,0 +1,719 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace drm = ::android::hardware::drm; +using namespace std; +using namespace android; +using ::android::sp; +using ::android::hardware::hidl_array; +using ::android::hardware::hidl_memory; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hidl::allocator::V1_0::IAllocator; +using ::android::hidl::memory::V1_0::IMemory; +using drm::V1_0::BufferType; +using drm::V1_0::DestinationBuffer; +using drm::V1_0::EventType; +using drm::V1_0::ICryptoPlugin; +using drm::V1_0::IDrmPlugin; +using drm::V1_0::IDrmPluginListener; +using drm::V1_0::KeyedVector; +using drm::V1_0::KeyStatus; +using drm::V1_0::KeyStatusType; +using drm::V1_0::KeyType; +using drm::V1_0::Mode; +using drm::V1_0::Pattern; +using drm::V1_0::SecureStop; +using drm::V1_0::SharedBuffer; +using drm::V1_0::Status; +using drm::V1_0::SubSample; +using drm::V1_1::DrmMetricGroup; +using drm::V1_1::HdcpLevel; +using drm::V1_1::SecureStopRelease; +using drm::V1_1::SecurityLevel; +using drm::V1_2::KeySetId; +using drm::V1_2::OfflineLicenseState; +using drm::V1_4::clearkey::ICryptoFactory; +using drm::V1_4::clearkey::IDrmFactory; +using drm::V1_4::clearkey::kAlgorithmsKey; +using drm::V1_4::clearkey::kClientIdKey; +using drm::V1_4::clearkey::kDeviceIdKey; +using drm::V1_4::clearkey::kDrmErrorTestKey; +using drm::V1_4::clearkey::kListenerTestSupportKey; +using drm::V1_4::clearkey::kMetricsKey; +using drm::V1_4::clearkey::kPluginDescriptionKey; +using drm::V1_4::clearkey::kVendorKey; +using drm::V1_4::clearkey::kVersionKey; + +typedef ::android::hardware::hidl_vec SessionId; +typedef ::android::hardware::hidl_vec SecureStopId; + +static const uint8_t kInvalidUUID[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, + 0x70, 0x80, 0x10, 0x20, 0x30, 0x40, + 0x50, 0x60, 0x70, 0x80}; + +static const uint8_t kClearKeyUUID[] = {0xE2, 0x71, 0x9D, 0x58, 0xA9, 0x85, + 0xB3, 0xC9, 0x78, 0x1A, 0xB0, 0x30, + 0xAF, 0x78, 0xD3, 0x0E}; + +const SecurityLevel kSecurityLevel[] = { + SecurityLevel::UNKNOWN, SecurityLevel::SW_SECURE_CRYPTO, + SecurityLevel::SW_SECURE_DECODE, SecurityLevel::HW_SECURE_CRYPTO, + SecurityLevel::HW_SECURE_DECODE, SecurityLevel::HW_SECURE_ALL}; + +const char *kMimeType[] = { + "video/mp4", "video/mpeg", "video/x-flv", "video/mj2", "video/3gp2", + "video/3gpp", "video/3gpp2", "audio/mp4", "audio/mpeg", "audio/aac", + "audio/3gp2", "audio/3gpp", "audio/3gpp2", "audio/webm", "video/webm", + "webm", "cenc", "video/unknown", "audio/unknown"}; + +const char *kCipherAlgorithm[] = {"AES/CBC/NoPadding", ""}; + +const char *kMacAlgorithm[] = {"HmacSHA256", ""}; + +const char *kRSAAlgorithm[] = {"RSASSA-PSS-SHA1", ""}; + +const std::string kProperty[] = {kVendorKey, + kVersionKey, + kPluginDescriptionKey, + kAlgorithmsKey, + kListenerTestSupportKey, + kDrmErrorTestKey, + kDeviceIdKey, + kClientIdKey, + kMetricsKey, + "placeholder"}; + +const KeyType kKeyType[] = {KeyType::OFFLINE, KeyType::STREAMING, + KeyType::RELEASE}; + +const Mode kCryptoMode[] = {Mode::UNENCRYPTED, Mode::AES_CTR, Mode::AES_CBC_CTS, + Mode::AES_CBC}; + +const hidl_vec validInitData = { + // BMFF box header (4 bytes size + 'pssh') + 0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68, + // full box header (version = 1 flags = 0) + 0x01, 0x00, 0x00, 0x00, + // system id + 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, + 0x52, 0xe2, 0xfb, 0x4b, + // number of key ids + 0x00, 0x00, 0x00, 0x01, + // key id + 0x60, 0x06, 0x1e, 0x01, 0x7e, 0x47, 0x7e, 0x87, 0x7e, 0x57, 0xd0, 0x0d, + 0x1e, 0xd0, 0x0d, 0x1e, + // size of data, must be zero + 0x00, 0x00, 0x00, 0x00}; + +const hidl_vec validKeyResponse = { + 0x7b, 0x22, 0x6b, 0x65, 0x79, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, + 0x6b, 0x74, 0x79, 0x22, 0x3a, 0x22, 0x6f, 0x63, 0x74, 0x22, 0x2c, + 0x22, 0x6b, 0x69, 0x64, 0x22, 0x3a, 0x22, 0x59, 0x41, 0x59, 0x65, + 0x41, 0x58, 0x35, 0x48, 0x66, 0x6f, 0x64, 0x2d, 0x56, 0x39, 0x41, + 0x4e, 0x48, 0x74, 0x41, 0x4e, 0x48, 0x67, 0x22, 0x2c, 0x22, 0x6b, + 0x22, 0x3a, 0x22, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x54, 0x65, + 0x73, 0x74, 0x4b, 0x65, 0x79, 0x42, 0x61, 0x73, 0x65, 0x36, 0x34, + 0x67, 0x67, 0x67, 0x22, 0x7d, 0x5d, 0x7d, 0x0a}; + +const size_t kAESBlockSize = 16; +const size_t kMaxStringLength = 100; +const size_t kMaxSubSamples = 10; +const size_t kMaxNumBytes = 1000; +const size_t kSegmentIndex = 0; + +template +T getValueFromArray(FuzzedDataProvider *fdp, const T (&arr)[size]) { + return arr[fdp->ConsumeIntegralInRange(0, size - 1)]; +} + +class TestDrmPluginListener : public IDrmPluginListener { +public: + TestDrmPluginListener() {} + virtual ~TestDrmPluginListener() {} + + virtual Return sendEvent(EventType /*eventType*/, + const hidl_vec & /*sessionId*/, + const hidl_vec & /*data*/) override { + return Return(); + } + + virtual Return + sendExpirationUpdate(const hidl_vec & /*sessionId*/, + int64_t /*expiryTimeInMS*/) override { + return Return(); + } + + virtual Return + sendKeysChange(const hidl_vec & /*sessionId*/, + const hidl_vec & /*keyStatusList*/, + bool /*hasNewUsableKey*/) override { + return Return(); + } +}; + +class ClearKeyFuzzer { +public: + ~ClearKeyFuzzer() { deInit(); } + bool init(); + void process(const uint8_t *data, size_t size); + +private: + void deInit(); + void invokeDrmPlugin(const uint8_t *data, size_t size); + void invokeCryptoPlugin(const uint8_t *data); + void invokeDrm(const uint8_t *data, size_t size); + void invokeCrypto(const uint8_t *data); + void invokeDrmDecryptEncryptAPI(const uint8_t *data, size_t size); + bool invokeDrmFactory(); + bool invokeCryptoFactory(); + void invokeDrmV1_4API(); + void invokeDrmSetAlgorithmAPI(); + void invokeDrmPropertyAPI(); + void invokeDrmSecureStopAPI(); + void invokeDrmOfflineLicenseAPI(const uint8_t *data, size_t size); + SessionId getSessionId(); + SecureStopRelease makeSecureRelease(const SecureStop &stop); + sp mDrmFactory = nullptr; + sp mCryptoFactory = nullptr; + sp mDrmPlugin = nullptr; + sp mDrmPluginV1_1 = nullptr; + sp mDrmPluginV1_2 = nullptr; + sp mDrmPluginV1_4 = nullptr; + sp mCryptoPluginV1_4 = nullptr; + sp mCryptoPlugin = nullptr; + FuzzedDataProvider *mFDP = nullptr; + SessionId mSessionId = {}; + SessionId mSessionIdV1 = {}; +}; + +void ClearKeyFuzzer::deInit() { + if (mDrmPluginV1_1) { + mDrmPluginV1_1->closeSession(mSessionIdV1); + } + if (mDrmPluginV1_2) { + mDrmPluginV1_2->closeSession(mSessionId); + } + mDrmFactory.clear(); + mCryptoFactory.clear(); + mDrmPlugin.clear(); + mDrmPluginV1_1.clear(); + mDrmPluginV1_2.clear(); + mDrmPluginV1_4.clear(); + mCryptoPlugin.clear(); + mCryptoPluginV1_4.clear(); + mSessionId = {}; + mSessionIdV1 = {}; +} + +void ClearKeyFuzzer::invokeDrmV1_4API() { + mDrmPluginV1_4->requiresSecureDecoderDefault( + getValueFromArray(mFDP, kMimeType)); + mDrmPluginV1_4->requiresSecureDecoder( + getValueFromArray(mFDP, kMimeType), + getValueFromArray(mFDP, kSecurityLevel)); + mDrmPluginV1_4->setPlaybackId( + mSessionId, mFDP->ConsumeRandomLengthString(kMaxStringLength).c_str()); + drm::V1_4::IDrmPlugin::getLogMessages_cb cb = + [&]([[maybe_unused]] drm::V1_4::Status status, + [[maybe_unused]] hidl_vec logs) {}; + mDrmPluginV1_4->getLogMessages(cb); +} + +void ClearKeyFuzzer::invokeDrmSetAlgorithmAPI() { + const hidl_string cipherAlgo = + mFDP->ConsumeBool() + ? mFDP->ConsumeRandomLengthString(kMaxStringLength).c_str() + : hidl_string(kCipherAlgorithm[mFDP->ConsumeBool()]); + mDrmPluginV1_2->setCipherAlgorithm(mSessionId, cipherAlgo); + + const hidl_string macAlgo = + mFDP->ConsumeBool() + ? mFDP->ConsumeRandomLengthString(kMaxStringLength).c_str() + : hidl_string(kMacAlgorithm[mFDP->ConsumeBool()]); + mDrmPluginV1_2->setMacAlgorithm(mSessionId, macAlgo); +} + +void ClearKeyFuzzer::invokeDrmPropertyAPI() { + mDrmPluginV1_2->setPropertyString( + hidl_string(getValueFromArray(mFDP, kProperty)), hidl_string("value")); + + hidl_string stringValue; + mDrmPluginV1_2->getPropertyString( + getValueFromArray(mFDP, kProperty), + [&](Status status, const hidl_string &hValue) { + if (status == Status::OK) { + stringValue = hValue; + } + }); + + hidl_vec value = {}; + mDrmPluginV1_2->setPropertyByteArray( + hidl_string(getValueFromArray(mFDP, kProperty)), value); + + hidl_vec byteValue; + mDrmPluginV1_2->getPropertyByteArray( + getValueFromArray(mFDP, kProperty), + [&](Status status, const hidl_vec &hValue) { + if (status == Status::OK) { + byteValue = hValue; + } + }); +} + +SessionId ClearKeyFuzzer::getSessionId() { + SessionId emptySessionId = {}; + return mFDP->ConsumeBool() ? mSessionId : emptySessionId; +} + +void ClearKeyFuzzer::invokeDrmDecryptEncryptAPI(const uint8_t *data, + size_t size) { + uint32_t currSessions, maximumSessions; + mDrmPluginV1_2->getNumberOfSessions( + [&](Status status, uint32_t hCurrentSessions, uint32_t hMaxSessions) { + if (status == Status::OK) { + currSessions = hCurrentSessions; + maximumSessions = hMaxSessions; + } + }); + + HdcpLevel connected, maximum; + mDrmPluginV1_2->getHdcpLevels([&](Status status, + const HdcpLevel &hConnectedLevel, + const HdcpLevel &hMaxLevel) { + if (status == Status::OK) { + connected = hConnectedLevel; + maximum = hMaxLevel; + } + }); + + drm::V1_2::HdcpLevel connectedV1_2, maximumV1_2; + mDrmPluginV1_2->getHdcpLevels_1_2( + [&](drm::V1_2::Status status, const drm::V1_2::HdcpLevel &connectedLevel, + const drm::V1_2::HdcpLevel &maxLevel) { + if (status == drm::V1_2::Status::OK) { + connectedV1_2 = connectedLevel; + maximumV1_2 = maxLevel; + } + }); + + SecurityLevel securityLevel; + mDrmPluginV1_2->getSecurityLevel(mSessionId, + [&](Status status, SecurityLevel hLevel) { + if (status == Status::OK) { + securityLevel = hLevel; + } + }); + + hidl_vec metrics; + mDrmPluginV1_2->getMetrics( + [&](Status status, hidl_vec hMetricGroups) { + if (status == Status::OK) { + metrics = hMetricGroups; + } + }); + + hidl_string certificateType; + hidl_string certificateAuthority; + mDrmPluginV1_2->getProvisionRequest(certificateType, certificateAuthority, + [&]([[maybe_unused]] Status status, + const hidl_vec &, + const hidl_string &) {}); + + mDrmPluginV1_2->getProvisionRequest_1_2( + certificateType, certificateAuthority, + [&]([[maybe_unused]] drm::V1_2::Status status, const hidl_vec &, + const hidl_string &) {}); + + hidl_vec response; + mDrmPluginV1_2->provideProvisionResponse( + response, [&]([[maybe_unused]] Status status, const hidl_vec &, + const hidl_vec &) {}); + + hidl_vec initData = {}; + if (mFDP->ConsumeBool()) { + initData = validInitData; + } else { + initData.setToExternal(const_cast(data), kAESBlockSize); + } + hidl_string mimeType = getValueFromArray(mFDP, kMimeType); + KeyType keyType = mFDP->ConsumeBool() + ? static_cast(mFDP->ConsumeIntegral()) + : getValueFromArray(mFDP, kKeyType); + KeyedVector optionalParameters; + mDrmPluginV1_2->getKeyRequest_1_2( + mSessionId, initData, mimeType, keyType, optionalParameters, + [&]([[maybe_unused]] drm::V1_2::Status status, const hidl_vec &, + drm::V1_1::KeyRequestType, const hidl_string &) {}); + mDrmPluginV1_1->getKeyRequest_1_1( + mSessionIdV1, initData, mimeType, keyType, optionalParameters, + [&]([[maybe_unused]] drm::V1_0::Status status, const hidl_vec &, + drm::V1_1::KeyRequestType, const hidl_string &) {}); + hidl_vec emptyInitData = {}; + mDrmPlugin->getKeyRequest( + mSessionId, mFDP->ConsumeBool() ? initData : emptyInitData, mimeType, + keyType, optionalParameters, + [&]([[maybe_unused]] drm::V1_0::Status status, const hidl_vec &, + drm::V1_0::KeyRequestType, const hidl_string &) {}); + + hidl_vec keyResponse = {}; + if (mFDP->ConsumeBool()) { + keyResponse = validKeyResponse; + } else { + keyResponse.setToExternal(const_cast(data), size); + } + hidl_vec keySetId; + hidl_vec emptyKeyResponse = {}; + mDrmPluginV1_2->provideKeyResponse( + getSessionId(), mFDP->ConsumeBool() ? keyResponse : emptyKeyResponse, + [&](Status status, const hidl_vec &hKeySetId) { + if (status == Status::OK) { + keySetId = hKeySetId; + } + }); + + mDrmPluginV1_2->restoreKeys(getSessionId(), keySetId); + + mDrmPluginV1_2->queryKeyStatus( + getSessionId(), + [&]([[maybe_unused]] Status status, KeyedVector /* info */) {}); + + hidl_vec keyId, input, iv; + keyId.setToExternal(const_cast(data), size); + input.setToExternal(const_cast(data), size); + iv.setToExternal(const_cast(data), size); + mDrmPluginV1_2->encrypt( + getSessionId(), keyId, input, iv, + [&]([[maybe_unused]] Status status, const hidl_vec &) {}); + + mDrmPluginV1_2->decrypt( + getSessionId(), keyId, input, iv, + [&]([[maybe_unused]] Status status, const hidl_vec &) {}); + + hidl_vec message; + message.setToExternal(const_cast(data), size); + mDrmPluginV1_2->sign( + getSessionId(), keyId, message, + [&]([[maybe_unused]] Status status, const hidl_vec &) {}); + + hidl_vec signature; + signature.setToExternal(const_cast(data), size); + mDrmPluginV1_2->verify(getSessionId(), keyId, message, signature, + [&]([[maybe_unused]] Status status, bool) {}); + + hidl_vec wrappedKey; + signature.setToExternal(const_cast(data), size); + mDrmPluginV1_2->signRSA( + getSessionId(), kRSAAlgorithm[mFDP->ConsumeBool()], message, wrappedKey, + [&]([[maybe_unused]] Status status, const hidl_vec &) {}); + + mDrmPluginV1_2->removeKeys(getSessionId()); +} + +/** + * Helper function to create a secure release message for + * a secure stop. The clearkey secure stop release format + * is just a count followed by the secure stop opaque data. + */ +SecureStopRelease ClearKeyFuzzer::makeSecureRelease(const SecureStop &stop) { + std::vector stopData = stop.opaqueData; + std::vector buffer; + std::string count = "0001"; + + auto it = buffer.insert(buffer.begin(), count.begin(), count.end()); + buffer.insert(it + count.size(), stopData.begin(), stopData.end()); + SecureStopRelease release = {.opaqueData = hidl_vec(buffer)}; + return release; +} + +void ClearKeyFuzzer::invokeDrmSecureStopAPI() { + SecureStopId ssid; + mDrmPluginV1_2->getSecureStop( + ssid, [&]([[maybe_unused]] Status status, const SecureStop &) {}); + + mDrmPluginV1_2->getSecureStopIds( + [&]([[maybe_unused]] Status status, + [[maybe_unused]] const hidl_vec &secureStopIds) {}); + + SecureStopRelease release; + mDrmPluginV1_2->getSecureStops( + [&]([[maybe_unused]] Status status, const hidl_vec &stops) { + if (stops.size() > 0) { + release = makeSecureRelease( + stops[mFDP->ConsumeIntegralInRange(0, stops.size() - 1)]); + } + }); + + mDrmPluginV1_2->releaseSecureStops(release); + + mDrmPluginV1_2->removeSecureStop(ssid); + + mDrmPluginV1_2->removeAllSecureStops(); + + mDrmPluginV1_2->releaseSecureStop(ssid); + + mDrmPluginV1_2->releaseAllSecureStops(); +} + +void ClearKeyFuzzer::invokeDrmOfflineLicenseAPI(const uint8_t *data, + size_t size) { + hidl_vec keySetIds = {}; + mDrmPluginV1_2->getOfflineLicenseKeySetIds( + [&](Status status, const hidl_vec &hKeySetIds) { + if (status == Status::OK) { + keySetIds = hKeySetIds; + } + }); + + OfflineLicenseState licenseState; + KeySetId keySetId = {}; + if (keySetIds.size() > 0) { + keySetId = keySetIds[mFDP->ConsumeIntegralInRange( + 0, keySetIds.size() - 1)]; + } else { + keySetId.setToExternal(const_cast(data), size); + } + mDrmPluginV1_2->getOfflineLicenseState( + keySetId, [&](Status status, OfflineLicenseState hLicenseState) { + if (status == Status::OK) { + licenseState = hLicenseState; + } + }); + + mDrmPluginV1_2->removeOfflineLicense(keySetId); +} + +void ClearKeyFuzzer::invokeDrmPlugin(const uint8_t *data, size_t size) { + SecurityLevel secLevel = + mFDP->ConsumeBool() + ? getValueFromArray(mFDP, kSecurityLevel) + : static_cast(mFDP->ConsumeIntegral()); + mDrmPluginV1_1->openSession_1_1( + secLevel, [&]([[maybe_unused]] Status status, const SessionId &id) { + mSessionIdV1 = id; + }); + mDrmPluginV1_2->openSession([&]([[maybe_unused]] Status status, + const SessionId &id) { mSessionId = id; }); + + sp listener = new TestDrmPluginListener(); + mDrmPluginV1_2->setListener(listener); + const hidl_vec keyStatusList = { + {{1}, KeyStatusType::USABLE}, + {{2}, KeyStatusType::EXPIRED}, + {{3}, KeyStatusType::OUTPUTNOTALLOWED}, + {{4}, KeyStatusType::STATUSPENDING}, + {{5}, KeyStatusType::INTERNALERROR}, + }; + mDrmPluginV1_2->sendKeysChange(mSessionId, keyStatusList, true); + + invokeDrmV1_4API(); + invokeDrmSetAlgorithmAPI(); + invokeDrmPropertyAPI(); + invokeDrmDecryptEncryptAPI(data, size); + invokeDrmSecureStopAPI(); + invokeDrmOfflineLicenseAPI(data, size); +} + +void ClearKeyFuzzer::invokeCryptoPlugin(const uint8_t *data) { + mCryptoPlugin->requiresSecureDecoderComponent( + getValueFromArray(mFDP, kMimeType)); + + const uint32_t width = mFDP->ConsumeIntegral(); + const uint32_t height = mFDP->ConsumeIntegral(); + mCryptoPlugin->notifyResolution(width, height); + + mCryptoPlugin->setMediaDrmSession(mSessionId); + + size_t totalSize = 0; + const size_t numSubSamples = + mFDP->ConsumeIntegralInRange(1, kMaxSubSamples); + + const Pattern pattern = {0, 0}; + hidl_vec subSamples; + subSamples.resize(numSubSamples); + + for (size_t i = 0; i < numSubSamples; ++i) { + const uint32_t clearBytes = + mFDP->ConsumeIntegralInRange(0, kMaxNumBytes); + const uint32_t encryptedBytes = + mFDP->ConsumeIntegralInRange(0, kMaxNumBytes); + subSamples[i].numBytesOfClearData = clearBytes; + subSamples[i].numBytesOfEncryptedData = encryptedBytes; + totalSize += subSamples[i].numBytesOfClearData; + totalSize += subSamples[i].numBytesOfEncryptedData; + } + + // The first totalSize bytes of shared memory is the encrypted + // input, the second totalSize bytes is the decrypted output. + size_t memoryBytes = totalSize * 2; + + sp ashmemAllocator = IAllocator::getService("ashmem"); + if (!ashmemAllocator.get()) { + return; + } + + hidl_memory hidlMemory; + ashmemAllocator->allocate(memoryBytes, [&]([[maybe_unused]] bool success, + const hidl_memory &memory) { + mCryptoPlugin->setSharedBufferBase(memory, kSegmentIndex); + hidlMemory = memory; + }); + + sp mappedMemory = mapMemory(hidlMemory); + if (!mappedMemory.get()) { + return; + } + mCryptoPlugin->setSharedBufferBase(hidlMemory, kSegmentIndex); + + uint32_t srcBufferId = + mFDP->ConsumeBool() ? kSegmentIndex : mFDP->ConsumeIntegral(); + const SharedBuffer sourceBuffer = { + .bufferId = srcBufferId, .offset = 0, .size = totalSize}; + + BufferType type = mFDP->ConsumeBool() ? BufferType::SHARED_MEMORY + : BufferType::NATIVE_HANDLE; + uint32_t destBufferId = + mFDP->ConsumeBool() ? kSegmentIndex : mFDP->ConsumeIntegral(); + const DestinationBuffer destBuffer = { + .type = type, + {.bufferId = destBufferId, .offset = totalSize, .size = totalSize}, + .secureMemory = nullptr}; + + const uint64_t offset = 0; + uint32_t bytesWritten = 0; + hidl_array keyId = + hidl_array(data); + hidl_array iv = + hidl_array(data); + Mode mode = getValueFromArray(mFDP, kCryptoMode); + mCryptoPlugin->decrypt( + mFDP->ConsumeBool(), keyId, iv, mode, pattern, subSamples, sourceBuffer, + offset, destBuffer, + [&]([[maybe_unused]] Status status, uint32_t count, + [[maybe_unused]] string detailedError) { bytesWritten = count; }); + drm::V1_4::IDrmPlugin::getLogMessages_cb cb = + [&]([[maybe_unused]] drm::V1_4::Status status, + [[maybe_unused]] hidl_vec logs) {}; + mCryptoPluginV1_4->getLogMessages(cb); +} + +bool ClearKeyFuzzer::invokeDrmFactory() { + hidl_string packageName( + mFDP->ConsumeRandomLengthString(kMaxStringLength).c_str()); + hidl_string mimeType(getValueFromArray(mFDP, kMimeType)); + SecurityLevel securityLevel = + mFDP->ConsumeBool() + ? getValueFromArray(mFDP, kSecurityLevel) + : static_cast(mFDP->ConsumeIntegral()); + const hidl_array uuid = + mFDP->ConsumeBool() ? kClearKeyUUID : kInvalidUUID; + mDrmFactory->isCryptoSchemeSupported_1_2(uuid, mimeType, securityLevel); + mDrmFactory->createPlugin( + uuid, packageName, [&](Status status, const sp &plugin) { + if (status == Status::OK) { + mDrmPlugin = plugin.get(); + mDrmPluginV1_1 = drm::V1_1::IDrmPlugin::castFrom(mDrmPlugin); + mDrmPluginV1_2 = drm::V1_2::IDrmPlugin::castFrom(mDrmPlugin); + mDrmPluginV1_4 = drm::V1_4::IDrmPlugin::castFrom(mDrmPlugin); + } + }); + + std::vector> supportedSchemes; + mDrmFactory->getSupportedCryptoSchemes( + [&](const hidl_vec> &schemes) { + for (const auto &scheme : schemes) { + supportedSchemes.push_back(scheme); + } + }); + + if (!(mDrmPlugin && mDrmPluginV1_1 && mDrmPluginV1_2 && mDrmPluginV1_4)) { + return false; + } + return true; +} + +bool ClearKeyFuzzer::invokeCryptoFactory() { + const hidl_array uuid = + mFDP->ConsumeBool() ? kClearKeyUUID : kInvalidUUID; + mCryptoFactory->createPlugin( + uuid, mSessionId, [this](Status status, const sp &plugin) { + if (status == Status::OK) { + mCryptoPlugin = plugin; + mCryptoPluginV1_4 = drm::V1_4::ICryptoPlugin::castFrom(mCryptoPlugin); + } + }); + + if (!mCryptoPlugin && !mCryptoPluginV1_4) { + return false; + } + return true; +} + +void ClearKeyFuzzer::invokeDrm(const uint8_t *data, size_t size) { + if (!invokeDrmFactory()) { + return; + } + invokeDrmPlugin(data, size); +} + +void ClearKeyFuzzer::invokeCrypto(const uint8_t *data) { + if (!invokeCryptoFactory()) { + return; + } + invokeCryptoPlugin(data); +} + +void ClearKeyFuzzer::process(const uint8_t *data, size_t size) { + mFDP = new FuzzedDataProvider(data, size); + invokeDrm(data, size); + invokeCrypto(data); + delete mFDP; +} + +bool ClearKeyFuzzer::init() { + mCryptoFactory = + android::hardware::drm::V1_4::clearkey::createCryptoFactory(); + mDrmFactory = android::hardware::drm::V1_4::clearkey::createDrmFactory(); + if (!mDrmFactory && !mCryptoFactory) { + return false; + } + return true; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size < kAESBlockSize) { + return 0; + } + ClearKeyFuzzer clearKeyFuzzer; + if (clearKeyFuzzer.init()) { + clearKeyFuzzer.process(data, size); + } + return 0; +} diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h index 5d6e3daca178a6114cde26e00b9961b9fa702c6f..cb5c9fe5c10b1a0a5095801751aa86611d280bcd 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h @@ -432,8 +432,7 @@ private: mMockError = Status_V1_2::OK; } - DeviceFiles mFileHandle GUARDED_BY(mFileHandleLock); - Mutex mFileHandleLock; + DeviceFiles mFileHandle; Mutex mSecureStopLock; CLEARKEY_DISALLOW_COPY_AND_ASSIGN_AND_NEW(DrmPlugin); diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h b/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h index a90d818b0f7f51d2ace642ee2552bcdeaff3b150..1d98860af1365621db4839d8fe9869c46fdc3259 100644 --- a/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h +++ b/drm/mediadrm/plugins/clearkey/hidl/include/MemoryFileSystem.h @@ -5,9 +5,7 @@ #ifndef CLEARKEY_MEMORY_FILE_SYSTEM_H_ #define CLEARKEY_MEMORY_FILE_SYSTEM_H_ -#include #include -#include #include #include "ClearKeyTypes.h" @@ -51,12 +49,10 @@ class MemoryFileSystem { size_t Write(const std::string& pathName, const MemoryFile& memoryFile); private: - mutable std::mutex mMemoryFileSystemLock; - // License file name is made up of a unique keySetId, therefore, // the filename can be used as the key to locate licenses in the // memory file system. - std::map mMemoryFileSystem GUARDED_BY(mMemoryFileSystemLock); + std::map mMemoryFileSystem; std::string GetFileName(const std::string& path); diff --git a/services/camera/libcameraservice/Android.mk b/drm/mediadrm/plugins/clearkey/service-lazy.mk similarity index 77% rename from services/camera/libcameraservice/Android.mk rename to drm/mediadrm/plugins/clearkey/service-lazy.mk index 4cfecfdfa1a72b6b1d476be6943a8531996f6aba..0d16f4c8e2631e83e305dff487273f840af15ea4 100644 --- a/services/camera/libcameraservice/Android.mk +++ b/drm/mediadrm/plugins/clearkey/service-lazy.mk @@ -1,4 +1,5 @@ -# Copyright 2010 The Android Open Source Project +# +# Copyright (C) 2022 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. @@ -11,12 +12,5 @@ # 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. - -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -# Build tests - -include $(LOCAL_PATH)/tests/Android.mk - +# +PRODUCT_PACKAGES += android.hardware.drm-service-lazy.clearkey diff --git a/drm/mediadrm/plugins/clearkey/service.mk b/drm/mediadrm/plugins/clearkey/service.mk new file mode 100644 index 0000000000000000000000000000000000000000..15988fb218bf5a3bf668b70f297b45308d7ffc49 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/service.mk @@ -0,0 +1,16 @@ +# +# Copyright (C) 2022 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. +# +PRODUCT_PACKAGES += android.hardware.drm-service.clearkey diff --git a/include/private/media/VideoFrame.h b/include/private/media/VideoFrame.h index 16e794af7861e69abf9d1c9a151762fe45e96e8d..d4025e5cbcad5735ae54a47a8322630dc6044941 100644 --- a/include/private/media/VideoFrame.h +++ b/include/private/media/VideoFrame.h @@ -38,13 +38,13 @@ public: VideoFrame(uint32_t width, uint32_t height, uint32_t displayWidth, uint32_t displayHeight, uint32_t tileWidth, uint32_t tileHeight, - uint32_t angle, uint32_t bpp, bool hasData, size_t iccSize): + uint32_t angle, uint32_t bpp, uint32_t bitDepth, bool hasData, size_t iccSize): mWidth(width), mHeight(height), mDisplayWidth(displayWidth), mDisplayHeight(displayHeight), mTileWidth(tileWidth), mTileHeight(tileHeight), mDurationUs(0), mRotationAngle(angle), mBytesPerPixel(bpp), mRowBytes(bpp * width), mSize(hasData ? (bpp * width * height) : 0), - mIccSize(iccSize), mReserved(0) { + mIccSize(iccSize), mBitDepth(bitDepth) { } void init(const VideoFrame& copy, const void* iccData, size_t iccSize) { @@ -84,7 +84,9 @@ public: uint32_t mRowBytes; // Number of bytes per row before rotation uint32_t mSize; // Number of bytes of frame data uint32_t mIccSize; // Number of bytes of ICC data - uint32_t mReserved; // (padding to make mData 64-bit aligned) + uint32_t mBitDepth; // number of bits per R / G / B channel + + // Adding new items must be 64-bit aligned. }; }; // namespace android diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING index 2793e7640f5b7ff62f3d3927e5282d2a8d4a9e44..afd1d343e6dac4c5e45ce0e41a6f6711ef6ef71d 100644 --- a/media/TEST_MAPPING +++ b/media/TEST_MAPPING @@ -24,26 +24,8 @@ { "path": "frameworks/av/drm/mediadrm/plugins" } - ], - - "platinum-postsubmit": [ - // runs regularly, independent of changes in this tree. - // signals if changes elsewhere break media functionality - { - "name": "CtsMediaCodecTestCases", - "options": [ - { - "include-filter": "android.media.codec.cts.EncodeDecodeTest" - } - ] - }, - { - "name": "CtsMediaCodecTestCases", - "options": [ - { - "include-filter": "android.media.codec.cts.DecodeEditEncodeTest" - } - ] - } ] + + // TODO (b/229286407) Add EncodeDecodeTest and DecodeEditEncodeTest to + // platinum-postsubmit once issues in cuttlefish are fixed } diff --git a/media/audioserver/Android.bp b/media/audioserver/Android.bp index e5f9907a508385e01e28661c060fbefb16e87965..828d861d7e3b2a00d23731f85e3bd8955c463a69 100644 --- a/media/audioserver/Android.bp +++ b/media/audioserver/Android.bp @@ -21,12 +21,14 @@ cc_binary { header_libs: [ "libaudiohal_headers", + "libmedia_headers", "libmediametrics_headers", ], shared_libs: [ "packagemanager_aidl-cpp", "libaaudioservice", + "libaudioclient", "libaudioflinger", "libaudiopolicyservice", "libaudioprocessing", @@ -50,7 +52,6 @@ cc_binary { "frameworks/av/media/libaaudio/include", "frameworks/av/media/libaaudio/src", "frameworks/av/media/libaaudio/src/binding", - "frameworks/av/media/libmedia/include", "frameworks/av/services/audioflinger", "frameworks/av/services/audiopolicy", "frameworks/av/services/audiopolicy/common/include", diff --git a/media/audioserver/main_audioserver.cpp b/media/audioserver/main_audioserver.cpp index c5ac7f995c3adb26a69236e73e447814d396bc57..e3db5b45b909b960978d295b003146b52588934d 100644 --- a/media/audioserver/main_audioserver.cpp +++ b/media/audioserver/main_audioserver.cpp @@ -17,11 +17,17 @@ #define LOG_TAG "audioserver" //#define LOG_NDEBUG 0 +#include + #include #include #include #include +#include +#include +#include +#include #include #include #include @@ -30,7 +36,6 @@ #include // from include_dirs -#include "aaudio/AAudioTesting.h" // aaudio_policy_t, AAUDIO_PROP_MMAP_POLICY, AAUDIO_POLICY_* #include "AudioFlinger.h" #include "AudioPolicyService.h" #include "AAudioService.h" @@ -39,6 +44,10 @@ using namespace android; +using android::media::audio::common::AudioMMapPolicy; +using android::media::audio::common::AudioMMapPolicyInfo; +using android::media::audio::common::AudioMMapPolicyType; + int main(int argc __unused, char **argv) { // TODO: update with refined parameters @@ -144,10 +153,24 @@ int main(int argc __unused, char **argv) // AAudioService should only be used in OC-MR1 and later. // And only enable the AAudioService if the system MMAP policy explicitly allows it. // This prevents a client from misusing AAudioService when it is not supported. - aaudio_policy_t mmapPolicy = property_get_int32(AAUDIO_PROP_MMAP_POLICY, - AAUDIO_POLICY_NEVER); - if (mmapPolicy == AAUDIO_POLICY_AUTO || mmapPolicy == AAUDIO_POLICY_ALWAYS) { + // If we cannot get audio flinger here, there must be some serious problems. In that case, + // attempting to call audio flinger on a null pointer could make the process crash + // and attract attentions. + sp af = AudioSystem::get_audio_flinger(); + std::vector policyInfos; + status_t status = af->getMmapPolicyInfos( + AudioMMapPolicyType::DEFAULT, &policyInfos); + // Initialize aaudio service when querying mmap policy succeeds and + // any of the policy supports MMAP. + if (status == NO_ERROR && + std::any_of(policyInfos.begin(), policyInfos.end(), [](const auto& info) { + return info.mmapPolicy == AudioMMapPolicy::AUTO || + info.mmapPolicy == AudioMMapPolicy::ALWAYS; + })) { AAudioService::instantiate(); + } else { + ALOGD("Do not init aaudio service, status %d, policy info size %zu", + status, policyInfos.size()); } ProcessState::self()->startThreadPool(); diff --git a/media/bufferpool/1.0/vts/OWNERS b/media/bufferpool/1.0/vts/OWNERS index 6733e0cbd142adf690ce8b3be124861152708065..db54d45abf852b6b1fb0b0e243413aea970166e7 100644 --- a/media/bufferpool/1.0/vts/OWNERS +++ b/media/bufferpool/1.0/vts/OWNERS @@ -1,6 +1,5 @@ # Media team lajos@google.com -pawin@google.com taklee@google.com wonsik@google.com diff --git a/media/bufferpool/2.0/Android.bp b/media/bufferpool/2.0/Android.bp index 0d1fe27623e7db8ded277401bfb95632dafc592b..930b0264ab53f0e0f118034ee57fcad50405417d 100644 --- a/media/bufferpool/2.0/Android.bp +++ b/media/bufferpool/2.0/Android.bp @@ -40,6 +40,12 @@ cc_library { defaults: ["libstagefright_bufferpool@2.0-default"], vendor_available: true, min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + "test_com.android.media.swcodec", + ], + // TODO: b/147147992 double_loadable: true, cflags: [ diff --git a/media/bufferpool/2.0/tests/OWNERS b/media/bufferpool/2.0/tests/OWNERS index 6733e0cbd142adf690ce8b3be124861152708065..db54d45abf852b6b1fb0b0e243413aea970166e7 100644 --- a/media/bufferpool/2.0/tests/OWNERS +++ b/media/bufferpool/2.0/tests/OWNERS @@ -1,6 +1,5 @@ # Media team lajos@google.com -pawin@google.com taklee@google.com wonsik@google.com diff --git a/media/codec2/OWNERS b/media/codec2/OWNERS index 46a9fcaf9401263da4dda924ea55110b0bb5b847..7d40041f2210b389b91fd46c9456e75d0e3ebbe3 100644 --- a/media/codec2/OWNERS +++ b/media/codec2/OWNERS @@ -1,5 +1,4 @@ set noparent wonsik@google.com lajos@google.com -pawin@google.com taklee@google.com diff --git a/media/codec2/components/aac/C2SoftAacDec.cpp b/media/codec2/components/aac/C2SoftAacDec.cpp index 57cdcd0a9deb7a87295463219bb9fc6c7644210a..4e4a9a1164eaa37999e99c9684d469a79bcf9c6e 100644 --- a/media/codec2/components/aac/C2SoftAacDec.cpp +++ b/media/codec2/components/aac/C2SoftAacDec.cpp @@ -221,6 +221,12 @@ public: .withFields({C2F(mDrcOutputLoudness, value).inRange(-57.75, 0.25)}) .withSetter(Setter::StrictValueWithNoDeps) .build()); + + addParameter(DefineParam(mChannelMask, C2_PARAMKEY_CHANNEL_MASK) + .withDefault(new C2StreamChannelMaskInfo::output(0u, 0)) + .withFields({C2F(mChannelMask, value).inRange(0, 4294967292)}) + .withSetter(Setter::StrictValueWithNoDeps) + .build()); } bool isAdts() const { return mAacFormat->value == C2Config::AAC_PACKAGING_ADTS; } @@ -255,6 +261,7 @@ private: std::shared_ptr mDrcAlbumMode; std::shared_ptr mMaxChannelCount; std::shared_ptr mDrcOutputLoudness; + std::shared_ptr mChannelMask; // TODO Add : C2StreamAacSbrModeTuning }; @@ -829,9 +836,11 @@ void C2SoftAacDec::process( C2StreamSampleRateInfo::output sampleRateInfo(0u, mStreamInfo->sampleRate); C2StreamChannelCountInfo::output channelCountInfo(0u, mStreamInfo->numChannels); + C2StreamChannelMaskInfo::output channelMaskInfo(0u, + maskFromCount(mStreamInfo->numChannels)); std::vector> failures; c2_status_t err = mIntf->config( - { &sampleRateInfo, &channelCountInfo }, + { &sampleRateInfo, &channelCountInfo, &channelMaskInfo }, C2_MAY_BLOCK, &failures); if (err == OK) { @@ -840,6 +849,7 @@ void C2SoftAacDec::process( C2FrameData &output = work->worklets.front()->output; output.configUpdate.push_back(C2Param::Copy(sampleRateInfo)); output.configUpdate.push_back(C2Param::Copy(channelCountInfo)); + output.configUpdate.push_back(C2Param::Copy(channelMaskInfo)); } else { ALOGE("Config Update failed"); mSignalledError = true; @@ -1056,6 +1066,47 @@ void C2SoftAacDec::drainDecoder() { } } +// definitions based on android.media.AudioFormat.CHANNEL_OUT_* +#define CHANNEL_OUT_FL 0x4 +#define CHANNEL_OUT_FR 0x8 +#define CHANNEL_OUT_FC 0x10 +#define CHANNEL_OUT_LFE 0x20 +#define CHANNEL_OUT_BL 0x40 +#define CHANNEL_OUT_BR 0x80 +#define CHANNEL_OUT_SL 0x800 +#define CHANNEL_OUT_SR 0x1000 + +uint32_t C2SoftAacDec::maskFromCount(uint32_t channelCount) { + // KEY_CHANNEL_MASK expects masks formatted according to Java android.media.AudioFormat + // where the two left-most bits are 0 for output channel mask + switch (channelCount) { + case 1: // mono is front left + return (CHANNEL_OUT_FL); + case 2: // stereo + return (CHANNEL_OUT_FL | CHANNEL_OUT_FR); + case 4: // 4.0 = stereo with backs + return (CHANNEL_OUT_FL | CHANNEL_OUT_FC + | CHANNEL_OUT_BL | CHANNEL_OUT_BR); + case 5: // 5.0 + return (CHANNEL_OUT_FL | CHANNEL_OUT_FC | CHANNEL_OUT_FR + | CHANNEL_OUT_BL | CHANNEL_OUT_BR); + case 6: // 5.1 = 5.0 + LFE + return (CHANNEL_OUT_FL | CHANNEL_OUT_FC | CHANNEL_OUT_FR + | CHANNEL_OUT_BL | CHANNEL_OUT_BR + | CHANNEL_OUT_LFE); + case 7: // 7.0 = 5.0 + Sides + return (CHANNEL_OUT_FL | CHANNEL_OUT_FC | CHANNEL_OUT_FR + | CHANNEL_OUT_BL | CHANNEL_OUT_BR + | CHANNEL_OUT_SL | CHANNEL_OUT_SR); + case 8: // 7.1 = 7.0 + LFE + return (CHANNEL_OUT_FL | CHANNEL_OUT_FC | CHANNEL_OUT_FR + | CHANNEL_OUT_BL | CHANNEL_OUT_BR | CHANNEL_OUT_SL | CHANNEL_OUT_SR + | CHANNEL_OUT_LFE); + default: + return 0; + } +} + class C2SoftAacDecFactory : public C2ComponentFactory { public: C2SoftAacDecFactory() : mHelper(std::static_pointer_cast( diff --git a/media/codec2/components/aac/C2SoftAacDec.h b/media/codec2/components/aac/C2SoftAacDec.h index a03fc705e754fbab262092e35166c2c075dc90df..b45f148146f8dba8f1b56fc509b453d17f2d877b 100644 --- a/media/codec2/components/aac/C2SoftAacDec.h +++ b/media/codec2/components/aac/C2SoftAacDec.h @@ -101,6 +101,7 @@ private: int32_t outputDelayRingBufferGetSamples(INT_PCM *samples, int numSamples); int32_t outputDelayRingBufferSamplesAvailable(); int32_t outputDelayRingBufferSpaceLeft(); + uint32_t maskFromCount(uint32_t channelCount); C2_DO_NOT_COPY(C2SoftAacDec); }; diff --git a/media/codec2/components/aom/Android.bp b/media/codec2/components/aom/Android.bp index cb9837f05429ad6aff1f0ce11eb1915bfb5300d7..a2a79d502cf222d03b72b64d80d7b2c7468cdf21 100644 --- a/media/codec2/components/aom/Android.bp +++ b/media/codec2/components/aom/Android.bp @@ -22,8 +22,4 @@ cc_library { srcs: ["C2SoftAomDec.cpp"], static_libs: ["libaom"], - - include_dirs: [ - "external/libaom/", - ], } diff --git a/media/codec2/components/aom/C2SoftAomDec.cpp b/media/codec2/components/aom/C2SoftAomDec.cpp index 971b009933a4631546c36184c26f0b9dcb1bbb9f..96b81d7b7fab86a07de3f7e3bb2c82f3f7802f95 100644 --- a/media/codec2/components/aom/C2SoftAomDec.cpp +++ b/media/codec2/components/aom/C2SoftAomDec.cpp @@ -261,8 +261,7 @@ C2SoftAomDec::C2SoftAomDec(const char* name, c2_node_id_t id, CREATE_DUMP_FILE(mInFile); CREATE_DUMP_FILE(mOutFile); - gettimeofday(&mTimeStart, nullptr); - gettimeofday(&mTimeEnd, nullptr); + mTimeStart = mTimeEnd = systemTime(); } C2SoftAomDec::~C2SoftAomDec() { @@ -463,19 +462,17 @@ void C2SoftAomDec::process(const std::unique_ptr& work, int64_t frameIndex = work->input.ordinal.frameIndex.peekll(); if (inSize) { uint8_t* bitstream = const_cast(rView.data() + inOffset); - int32_t decodeTime = 0; - int32_t delay = 0; DUMP_TO_FILE(mOutFile, bitstream, inSize); - GETTIME(&mTimeStart, nullptr); - TIME_DIFF(mTimeEnd, mTimeStart, delay); + mTimeStart = systemTime(); + nsecs_t delay = mTimeStart - mTimeEnd; aom_codec_err_t err = aom_codec_decode(mCodecCtx, bitstream, inSize, &frameIndex); - GETTIME(&mTimeEnd, nullptr); - TIME_DIFF(mTimeStart, mTimeEnd, decodeTime); - ALOGV("decodeTime=%4d delay=%4d\n", decodeTime, delay); + mTimeEnd = systemTime(); + nsecs_t decodeTime = mTimeEnd - mTimeStart; + ALOGV("decodeTime=%4" PRId64 " delay=%4" PRId64 "\n", decodeTime, delay); if (err != AOM_CODEC_OK) { ALOGE("av1 decoder failed to decode frame err: %d", err); diff --git a/media/codec2/components/aom/C2SoftAomDec.h b/media/codec2/components/aom/C2SoftAomDec.h index 4c82647d7b803a2022ca292b66b52be1b2201dde..8b953fed10cf28232944253e72a2b2c84b090e51 100644 --- a/media/codec2/components/aom/C2SoftAomDec.h +++ b/media/codec2/components/aom/C2SoftAomDec.h @@ -17,15 +17,12 @@ #ifndef ANDROID_C2_SOFT_AV1_DEC_H_ #define ANDROID_C2_SOFT_AV1_DEC_H_ +#include + #include #include "aom/aom_decoder.h" #include "aom/aomdx.h" -#define GETTIME(a, b) gettimeofday(a, b); -#define TIME_DIFF(start, end, diff) \ - diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \ - ((end).tv_usec - (start).tv_usec); - namespace android { struct C2SoftAomDec : public SimpleC2Component { @@ -60,8 +57,8 @@ struct C2SoftAomDec : public SimpleC2Component { char mOutFile[200]; #endif /* FILE_DUMP_ENABLE */ - struct timeval mTimeStart; // Time at the start of decode() - struct timeval mTimeEnd; // Time at the end of decode() + nsecs_t mTimeStart = 0; // Time at the start of decode() + nsecs_t mTimeEnd = 0; // Time at the end of decode() status_t initDecoder(); status_t destroyDecoder(); @@ -85,14 +82,13 @@ struct C2SoftAomDec : public SimpleC2Component { #define OUTPUT_DUMP_EXT "av1" #define GENERATE_FILE_NAMES() \ { \ - GETTIME(&mTimeStart, NULL); \ - strcpy(mInFile, ""); \ + nsecs_t now = systemTime(); \ ALOGD("GENERATE_FILE_NAMES"); \ - sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH, mTimeStart.tv_sec, \ - mTimeStart.tv_usec, INPUT_DUMP_EXT); \ + sprintf(mInFile, "%s_%" PRId64 ".%s", INPUT_DUMP_PATH, \ + now, INPUT_DUMP_EXT); \ strcpy(mOutFile, ""); \ - sprintf(mOutFile, "%s_%ld.%ld.%s", OUTPUT_DUMP_PATH, \ - mTimeStart.tv_sec, mTimeStart.tv_usec, OUTPUT_DUMP_EXT); \ + sprintf(mOutFile, "%s_%" PRId64 ".%s", OUTPUT_DUMP_PATH, \ + now, OUTPUT_DUMP_EXT); \ } #define CREATE_DUMP_FILE(m_filename) \ diff --git a/media/codec2/components/avc/Android.bp b/media/codec2/components/avc/Android.bp index 0be1bed4a49e1379b5c0f633ae00ea287d8a79cb..a7ae85b1399e3d284bfadf9be9513bff731be40b 100644 --- a/media/codec2/components/avc/Android.bp +++ b/media/codec2/components/avc/Android.bp @@ -19,10 +19,7 @@ cc_library { srcs: ["C2SoftAvcDec.cpp"], - include_dirs: [ - "external/libavc/decoder", - "external/libavc/common", - ], + export_include_dirs: ["."], } cc_library { @@ -37,10 +34,7 @@ cc_library { srcs: ["C2SoftAvcEnc.cpp"], - include_dirs: [ - "external/libavc/encoder", - "external/libavc/common", - ], + export_include_dirs: ["."], cflags: [ "-Wno-unused-variable", diff --git a/media/codec2/components/avc/C2SoftAvcDec.cpp b/media/codec2/components/avc/C2SoftAvcDec.cpp index cc4517ddcdd3482e54bb95e5213c598a0a13630f..953afc57cd33cc82f2b49f64b8815181c81aeca7 100644 --- a/media/codec2/components/avc/C2SoftAvcDec.cpp +++ b/media/codec2/components/avc/C2SoftAvcDec.cpp @@ -670,8 +670,7 @@ status_t C2SoftAvcDec::resetDecoder() { void C2SoftAvcDec::resetPlugin() { mSignalledOutputEos = false; - gettimeofday(&mTimeStart, nullptr); - gettimeofday(&mTimeEnd, nullptr); + mTimeStart = mTimeEnd = systemTime(); } status_t C2SoftAvcDec::deleteDecoder() { @@ -866,14 +865,13 @@ void C2SoftAvcDec::process( setParams(mStride, IVD_DECODE_HEADER); } - WORD32 delay; - GETTIME(&mTimeStart, nullptr); - TIME_DIFF(mTimeEnd, mTimeStart, delay); + mTimeStart = systemTime(); + nsecs_t delay = mTimeStart - mTimeEnd; (void) ivdec_api_function(mDecHandle, &s_h264d_decode_ip, &s_h264d_decode_op); - WORD32 decodeTime; - GETTIME(&mTimeEnd, nullptr); - TIME_DIFF(mTimeStart, mTimeEnd, decodeTime); - ALOGV("decodeTime=%6d delay=%6d numBytes=%6d", decodeTime, delay, + + mTimeEnd = systemTime(); + nsecs_t decodeTime = mTimeEnd - mTimeStart; + ALOGV("decodeTime=%" PRId64 " delay=%" PRId64 " numBytes=%6d", decodeTime, delay, ps_decode_op->u4_num_bytes_consumed); } if (IVD_MEM_ALLOC_FAILED == (ps_decode_op->u4_error_code & IVD_ERROR_MASK)) { diff --git a/media/codec2/components/avc/C2SoftAvcDec.h b/media/codec2/components/avc/C2SoftAvcDec.h index 59d5184861b209ba66f010fe1eb9ada84f22186c..36a463e0e46ad63c0245691e380e878c8a1bdfa7 100644 --- a/media/codec2/components/avc/C2SoftAvcDec.h +++ b/media/codec2/components/avc/C2SoftAvcDec.h @@ -18,6 +18,7 @@ #define ANDROID_C2_SOFT_AVC_DEC_H_ #include +#include #include @@ -43,19 +44,15 @@ namespace android { #define IVDEXT_CMD_CTL_SET_NUM_CORES \ (IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_SET_NUM_CORES #define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define GETTIME(a, b) gettimeofday(a, b); -#define TIME_DIFF(start, end, diff) \ - diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \ - ((end).tv_usec - (start).tv_usec); #ifdef FILE_DUMP_ENABLE #define INPUT_DUMP_PATH "/sdcard/clips/avcd_input" #define INPUT_DUMP_EXT "h264" #define GENERATE_FILE_NAMES() { \ - GETTIME(&mTimeStart, NULL); \ + nsecs_t now = systemTime(); \ strcpy(mInFile, ""); \ - sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH, \ - mTimeStart.tv_sec, mTimeStart.tv_usec, \ + sprintf(mInFile, "%s_%" PRId64 "d.%s", \ + INPUT_DUMP_PATH, now, \ INPUT_DUMP_EXT); \ } #define CREATE_DUMP_FILE(m_filename) { \ @@ -183,8 +180,8 @@ private: } mBitstreamColorAspects; // profile - struct timeval mTimeStart; - struct timeval mTimeEnd; + nsecs_t mTimeStart = 0; + nsecs_t mTimeEnd = 0; #ifdef FILE_DUMP_ENABLE char mInFile[200]; #endif /* FILE_DUMP_ENABLE */ diff --git a/media/codec2/components/avc/C2SoftAvcEnc.cpp b/media/codec2/components/avc/C2SoftAvcEnc.cpp index d65ffa58f3ff1dc0b1123d8d4fff5a9f991abd51..8b46d3f4952a170855ff60356bca528d761aee9d 100644 --- a/media/codec2/components/avc/C2SoftAvcEnc.cpp +++ b/media/codec2/components/avc/C2SoftAvcEnc.cpp @@ -656,8 +656,7 @@ void C2SoftAvcEnc::initEncParams() { mEntropyMode = DEFAULT_ENTROPY_MODE; mBframes = DEFAULT_B_FRAMES; - gettimeofday(&mTimeStart, nullptr); - gettimeofday(&mTimeEnd, nullptr); + mTimeStart = mTimeEnd = systemTime(); } c2_status_t C2SoftAvcEnc::setDimensions() { @@ -1515,7 +1514,8 @@ c2_status_t C2SoftAvcEnc::setEncodeArgs( vPlane = uPlane + yPlaneSize / 4; yStride = width; uStride = vStride = yStride / 2; - ConvertRGBToPlanarYUV(yPlane, yStride, height, conversionBuffer.size(), *input); + ConvertRGBToPlanarYUV(yPlane, yStride, height, conversionBuffer.size(), *input, + mColorAspects->matrix, mColorAspects->range); break; } case C2PlanarLayout::TYPE_YUV: { @@ -1650,8 +1650,7 @@ void C2SoftAvcEnc::process( work->worklets.front()->output.flags = work->input.flags; IV_STATUS_T status; - WORD32 timeDelay = 0; - WORD32 timeTaken = 0; + nsecs_t timeDelay = 0; uint64_t workIndex = work->input.ordinal.frameIndex.peekull(); // Initialize encoder if not already initialized @@ -1817,10 +1816,10 @@ void C2SoftAvcEnc::process( // mInFile, s_encode_ip.s_inp_buf.apv_bufs[0], // (mHeight * mStride * 3 / 2)); - GETTIME(&mTimeStart, nullptr); /* Compute time elapsed between end of previous decode() * to start of current decode() */ - TIME_DIFF(mTimeEnd, mTimeStart, timeDelay); + mTimeStart = systemTime(); + timeDelay = mTimeStart - mTimeEnd; status = ive_api_function(mCodecCtx, &s_video_encode_ip, &s_video_encode_op); if (IV_SUCCESS != status) { @@ -1844,11 +1843,11 @@ void C2SoftAvcEnc::process( mBuffers[ps_encode_ip->s_inp_buf.apv_bufs[0]] = inputBuffer; } - GETTIME(&mTimeEnd, nullptr); /* Compute time taken for decode() */ - TIME_DIFF(mTimeStart, mTimeEnd, timeTaken); + mTimeEnd = systemTime(); + nsecs_t timeTaken = mTimeEnd - mTimeStart; - ALOGV("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay, + ALOGV("timeTaken=%" PRId64 "d delay=%" PRId64 " numBytes=%6d", timeTaken, timeDelay, ps_encode_op->s_out_buf.u4_bytes); void *freed = ps_encode_op->s_inp_buf.apv_bufs[0]; diff --git a/media/codec2/components/avc/C2SoftAvcEnc.h b/media/codec2/components/avc/C2SoftAvcEnc.h index 1fecd9ed33a52e77209a9f037b0befae397a9663..293867d00963f9b9ede660cbd5f7fa1e099d33e4 100644 --- a/media/codec2/components/avc/C2SoftAvcEnc.h +++ b/media/codec2/components/avc/C2SoftAvcEnc.h @@ -18,6 +18,7 @@ #define ANDROID_C2_SOFT_AVC_ENC_H__ #include +#include #include @@ -115,14 +116,6 @@ namespace android { /** Used to remove warnings about unused parameters */ #define UNUSED(x) ((void)(x)) -/** Get time */ -#define GETTIME(a, b) gettimeofday(a, b); - -/** Compute difference between start and end */ -#define TIME_DIFF(start, end, diff) \ - diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \ - ((end).tv_usec - (start).tv_usec); - #define ive_aligned_malloc(alignment, size) memalign(alignment, size) #define ive_aligned_free(buf) free(buf) @@ -148,6 +141,7 @@ protected: virtual ~C2SoftAvcEnc(); private: + // RBE What does OMX have to do with the c2 plugin? // OMX input buffer's timestamp and flags typedef struct { int64_t mTimeUs; @@ -158,8 +152,8 @@ private: int32_t mStride; - struct timeval mTimeStart; // Time at the start of decode() - struct timeval mTimeEnd; // Time at the end of decode() + nsecs_t mTimeStart = 0; // Time at the start of decode() + nsecs_t mTimeEnd = 0; // Time at the end of decode() #ifdef FILE_DUMP_ENABLE char mInFile[200]; @@ -259,14 +253,14 @@ private: #define OUTPUT_DUMP_EXT "h264" #define GENERATE_FILE_NAMES() { \ - GETTIME(&mTimeStart, NULL); \ + nsecs_t now = systemTime(); \ strcpy(mInFile, ""); \ - sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH, \ - mTimeStart.tv_sec, mTimeStart.tv_usec, \ + sprintf(mInFile, "%s_%" PRId64 "d.%s", \ + INPUT_DUMP_PATH, now, \ INPUT_DUMP_EXT); \ strcpy(mOutFile, ""); \ - sprintf(mOutFile, "%s_%ld.%ld.%s", OUTPUT_DUMP_PATH,\ - mTimeStart.tv_sec, mTimeStart.tv_usec, \ + sprintf(mOutFile, "%s_%" PRId64 "d.%s", \ + OUTPUT_DUMP_PATH, now, \ OUTPUT_DUMP_EXT); \ } diff --git a/media/codec2/components/base/Android.bp b/media/codec2/components/base/Android.bp index 809f94277476f8295c93446e197fddef1c65074c..664647a6b5d7028a17539a35de0f26b82c2608a1 100644 --- a/media/codec2/components/base/Android.bp +++ b/media/codec2/components/base/Android.bp @@ -9,6 +9,16 @@ package { default_applicable_licenses: ["frameworks_av_license"], } +cc_library_headers { + name: "libcodec2_soft_common_headers", + defaults: ["libcodec2-impl-defaults"], + vendor_available: true, + + export_include_dirs: [ + "include", + ], +} + cc_library { name: "libcodec2_soft_common", defaults: ["libcodec2-impl-defaults"], @@ -39,6 +49,12 @@ cc_library { "libstagefright_foundation", // for Mutexed ], + min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + ], + sanitize: { misc_undefined: [ "unsigned-integer-overflow", @@ -85,6 +101,12 @@ cc_defaults { ], ldflags: ["-Wl,-Bsymbolic"], + + min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + ], } // public dependency for software codec implementation diff --git a/media/codec2/components/base/SimpleC2Component.cpp b/media/codec2/components/base/SimpleC2Component.cpp index e81b70ab53834c541f54f51bb91ee953e718f6a7..9d4f049e5266ada0afcbee93b49b41bb35cae723 100644 --- a/media/codec2/components/base/SimpleC2Component.cpp +++ b/media/codec2/components/base/SimpleC2Component.cpp @@ -48,23 +48,23 @@ void convertYUV420Planar8ToYV12(uint8_t *dstY, uint8_t *dstU, uint8_t *dstV, con if (isMonochrome) { // Fill with neutral U/V values. - for (size_t i = 0; i < height / 2; ++i) { - memset(dstV, kNeutralUVBitDepth8, width / 2); - memset(dstU, kNeutralUVBitDepth8, width / 2); + for (size_t i = 0; i < (height + 1) / 2; ++i) { + memset(dstV, kNeutralUVBitDepth8, (width + 1) / 2); + memset(dstU, kNeutralUVBitDepth8, (width + 1) / 2); dstV += dstUVStride; dstU += dstUVStride; } return; } - for (size_t i = 0; i < height / 2; ++i) { - memcpy(dstV, srcV, width / 2); + for (size_t i = 0; i < (height + 1) / 2; ++i) { + memcpy(dstV, srcV, (width + 1) / 2); srcV += srcVStride; dstV += dstUVStride; } - for (size_t i = 0; i < height / 2; ++i) { - memcpy(dstU, srcU, width / 2); + for (size_t i = 0; i < (height + 1) / 2; ++i) { + memcpy(dstU, srcU, (width + 1) / 2); srcU += srcUStride; dstU += dstUVStride; } diff --git a/media/codec2/components/cmds/Android.bp b/media/codec2/components/cmds/Android.bp index d6ffd12fc243703e121c11eae0f41d1259bce980..2a11c0156ef097789f59f989efaada1534351e81 100644 --- a/media/codec2/components/cmds/Android.bp +++ b/media/codec2/components/cmds/Android.bp @@ -15,9 +15,6 @@ cc_binary { "codec2.cpp", ], - include_dirs: [ - ], - header_libs: [ "libmediadrm_headers", ], diff --git a/media/codec2/components/gav1/Android.bp b/media/codec2/components/gav1/Android.bp index 7692d3786db3c9043c5c2906a0d22acfaa768e62..162339f0da2c28afb90f30475e49d6c72ebab9b8 100644 --- a/media/codec2/components/gav1/Android.bp +++ b/media/codec2/components/gav1/Android.bp @@ -22,4 +22,10 @@ cc_library { srcs: ["C2SoftGav1Dec.cpp"], static_libs: ["libgav1"], + + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + ], + } diff --git a/media/codec2/components/gav1/C2SoftGav1Dec.cpp b/media/codec2/components/gav1/C2SoftGav1Dec.cpp index ee37b06e353faaa6b04d059d8987d40eba0b86ed..4dec57fd2549644b94f2ac327a15aa988fd95830 100644 --- a/media/codec2/components/gav1/C2SoftGav1Dec.cpp +++ b/media/codec2/components/gav1/C2SoftGav1Dec.cpp @@ -33,6 +33,8 @@ namespace android { // codecname set and passed in as a compile flag from Android.bp constexpr char COMPONENT_NAME[] = CODECNAME; +constexpr size_t kMinInputBufferSize = 2 * 1024 * 1024; + class C2SoftGav1Dec::IntfImpl : public SimpleInterface::BaseParams { public: explicit IntfImpl(const std::shared_ptr &helper) @@ -54,8 +56,8 @@ class C2SoftGav1Dec::IntfImpl : public SimpleInterface::BaseParams { DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE) .withDefault(new C2StreamPictureSizeInfo::output(0u, 320, 240)) .withFields({ - C2F(mSize, width).inRange(2, 4096, 2), - C2F(mSize, height).inRange(2, 4096, 2), + C2F(mSize, width).inRange(2, 4096), + C2F(mSize, height).inRange(2, 4096), }) .withSetter(SizeSetter) .build()); @@ -111,8 +113,7 @@ class C2SoftGav1Dec::IntfImpl : public SimpleInterface::BaseParams { .build()); addParameter(DefineParam(mMaxInputSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE) - .withDefault(new C2StreamMaxBufferSizeInfo::input( - 0u, 320 * 240 * 3 / 4)) + .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, kMinInputBufferSize)) .withFields({ C2F(mMaxInputSize, value).any(), }) @@ -240,9 +241,9 @@ class C2SoftGav1Dec::IntfImpl : public SimpleInterface::BaseParams { bool mayBlock, C2P &me, const C2P &maxSize) { (void)mayBlock; - // assume compression ratio of 2 - me.set().value = - (((maxSize.v.width + 63) / 64) * ((maxSize.v.height + 63) / 64) * 3072); + // assume compression ratio of 2, but enforce a floor + me.set().value = c2_max((((maxSize.v.width + 63) / 64) + * ((maxSize.v.height + 63) / 64) * 3072), kMinInputBufferSize); return C2R::Ok(); } @@ -350,8 +351,7 @@ C2SoftGav1Dec::C2SoftGav1Dec(const char *name, c2_node_id_t id, std::make_shared>(name, id, intfImpl)), mIntf(intfImpl), mCodecCtx(nullptr) { - gettimeofday(&mTimeStart, nullptr); - gettimeofday(&mTimeEnd, nullptr); + mTimeStart = mTimeEnd = systemTime(); } C2SoftGav1Dec::~C2SoftGav1Dec() { onRelease(); } @@ -526,19 +526,17 @@ void C2SoftGav1Dec::process(const std::unique_ptr &work, int64_t frameIndex = work->input.ordinal.frameIndex.peekll(); if (inSize) { uint8_t *bitstream = const_cast(rView.data() + inOffset); - int32_t decodeTime = 0; - int32_t delay = 0; - GETTIME(&mTimeStart, nullptr); - TIME_DIFF(mTimeEnd, mTimeStart, delay); + mTimeStart = systemTime(); + nsecs_t delay = mTimeStart - mTimeEnd; const Libgav1StatusCode status = mCodecCtx->EnqueueFrame(bitstream, inSize, frameIndex, /*buffer_private_data=*/nullptr); - GETTIME(&mTimeEnd, nullptr); - TIME_DIFF(mTimeStart, mTimeEnd, decodeTime); - ALOGV("decodeTime=%4d delay=%4d\n", decodeTime, delay); + mTimeEnd = systemTime(); + nsecs_t decodeTime = mTimeEnd - mTimeStart; + ALOGV("decodeTime=%4" PRId64 " delay=%4" PRId64 "\n", decodeTime, delay); if (status != kLibgav1StatusOk) { ALOGE("av1 decoder failed to decode frame. status: %d.", status); @@ -688,8 +686,12 @@ bool C2SoftGav1Dec::outputBuffer(const std::shared_ptr &pool, C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}; - c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 16), mHeight, format, - usage, &block); + // We always create a graphic block that is width aligned to 16 and height + // aligned to 2. We set the correct "crop" value of the image in the call to + // createGraphicBuffer() by setting the correct image dimensions. + c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 16), + align(mHeight, 2), format, usage, + &block); if (err != C2_OK) { ALOGE("fetchGraphicBlock for Output failed with status %d", err); diff --git a/media/codec2/components/gav1/C2SoftGav1Dec.h b/media/codec2/components/gav1/C2SoftGav1Dec.h index e982c5b76575069a188ff9b38b5e3f8023e08dfb..3d4db550deb1981b31e6f1af54c4aa4d9154e6fc 100644 --- a/media/codec2/components/gav1/C2SoftGav1Dec.h +++ b/media/codec2/components/gav1/C2SoftGav1Dec.h @@ -17,6 +17,8 @@ #ifndef ANDROID_C2_SOFT_GAV1_DEC_H_ #define ANDROID_C2_SOFT_GAV1_DEC_H_ +#include + #include #include @@ -24,11 +26,6 @@ #include "libgav1/src/gav1/decoder.h" #include "libgav1/src/gav1/decoder_settings.h" -#define GETTIME(a, b) gettimeofday(a, b); -#define TIME_DIFF(start, end, diff) \ - diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \ - ((end).tv_usec - (start).tv_usec); - namespace android { struct C2SoftGav1Dec : public SimpleC2Component { @@ -85,8 +82,8 @@ struct C2SoftGav1Dec : public SimpleC2Component { } } mBitstreamColorAspects; - struct timeval mTimeStart; // Time at the start of decode() - struct timeval mTimeEnd; // Time at the end of decode() + nsecs_t mTimeStart = 0; // Time at the start of decode() + nsecs_t mTimeEnd = 0; // Time at the end of decode() bool initDecoder(); void getVuiParams(const libgav1::DecoderBuffer *buffer); diff --git a/media/codec2/components/hevc/C2SoftHevcDec.cpp b/media/codec2/components/hevc/C2SoftHevcDec.cpp index 5f5b2efc46851648761447e74446e8a2dfbbccdd..5a660c5ca14125541fd72b41871947f4db7fe4f6 100644 --- a/media/codec2/components/hevc/C2SoftHevcDec.cpp +++ b/media/codec2/components/hevc/C2SoftHevcDec.cpp @@ -34,6 +34,7 @@ namespace { constexpr char COMPONENT_NAME[] = "c2.android.hevc.decoder"; constexpr uint32_t kDefaultOutputDelay = 8; constexpr uint32_t kMaxOutputDelay = 16; +constexpr size_t kMinInputBufferSize = 2 * 1024 * 1024; } // namespace class C2SoftHevcDec::IntfImpl : public SimpleInterface::BaseParams { @@ -108,7 +109,7 @@ public: addParameter( DefineParam(mMaxInputSize, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE) - .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, 320 * 240 * 3 / 4)) + .withDefault(new C2StreamMaxBufferSizeInfo::input(0u, kMinInputBufferSize)) .withFields({ C2F(mMaxInputSize, value).any(), }) @@ -220,8 +221,9 @@ public: static C2R MaxInputSizeSetter(bool mayBlock, C2P &me, const C2P &maxSize) { (void)mayBlock; - // assume compression ratio of 2 - me.set().value = (((maxSize.v.width + 63) / 64) * ((maxSize.v.height + 63) / 64) * 3072); + // assume compression ratio of 2, but enforce a floor + me.set().value = c2_max((((maxSize.v.width + 63) / 64) + * ((maxSize.v.height + 63) / 64) * 3072), kMinInputBufferSize); return C2R::Ok(); } @@ -661,8 +663,7 @@ status_t C2SoftHevcDec::resetDecoder() { void C2SoftHevcDec::resetPlugin() { mSignalledOutputEos = false; - gettimeofday(&mTimeStart, nullptr); - gettimeofday(&mTimeEnd, nullptr); + mTimeStart = mTimeEnd = systemTime(); } status_t C2SoftHevcDec::deleteDecoder() { @@ -856,14 +857,13 @@ void C2SoftHevcDec::process( /* Decode header and get dimensions */ setParams(mStride, IVD_DECODE_HEADER); } - WORD32 delay; - GETTIME(&mTimeStart, nullptr); - TIME_DIFF(mTimeEnd, mTimeStart, delay); + + mTimeStart = systemTime(); + nsecs_t delay = mTimeStart - mTimeEnd; (void) ivdec_api_function(mDecHandle, ps_decode_ip, ps_decode_op); - WORD32 decodeTime; - GETTIME(&mTimeEnd, nullptr); - TIME_DIFF(mTimeStart, mTimeEnd, decodeTime); - ALOGV("decodeTime=%6d delay=%6d numBytes=%6d", decodeTime, delay, + mTimeEnd = systemTime(); + nsecs_t decodeTime = mTimeEnd - mTimeStart; + ALOGV("decodeTime=%6" PRId64 " delay=%6" PRId64 " numBytes=%6d", decodeTime, delay, ps_decode_op->u4_num_bytes_consumed); if (IVD_MEM_ALLOC_FAILED == (ps_decode_op->u4_error_code & IVD_ERROR_MASK)) { ALOGE("allocation failure in decoder"); diff --git a/media/codec2/components/hevc/C2SoftHevcDec.h b/media/codec2/components/hevc/C2SoftHevcDec.h index b9296e9cd37d3e893d4bcd28ce0e0ba4067c5e65..6abf69ed4319768635e38f0234acd78b3cf7c046 100644 --- a/media/codec2/components/hevc/C2SoftHevcDec.h +++ b/media/codec2/components/hevc/C2SoftHevcDec.h @@ -20,6 +20,7 @@ #include #include +#include #include #include "ihevc_typedefs.h" @@ -41,10 +42,6 @@ namespace android { #define IVDEXT_CMD_CTL_SET_NUM_CORES \ (IVD_CONTROL_API_COMMAND_TYPE_T)IHEVCD_CXA_CMD_CTL_SET_NUM_CORES #define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define GETTIME(a, b) gettimeofday(a, b); -#define TIME_DIFF(start, end, diff) \ - diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \ - ((end).tv_usec - (start).tv_usec); struct C2SoftHevcDec : public SimpleC2Component { @@ -142,8 +139,8 @@ struct C2SoftHevcDec : public SimpleC2Component { } mBitstreamColorAspects; // profile - struct timeval mTimeStart; - struct timeval mTimeEnd; + nsecs_t mTimeStart = 0; + nsecs_t mTimeEnd = 0; C2_DO_NOT_COPY(C2SoftHevcDec); }; diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.cpp b/media/codec2/components/hevc/C2SoftHevcEnc.cpp index 4f5caecc4cd71de2a2791f94f7469c8bbbb90b6e..60d5875264e01a66f831ae7778e9c8579753a6d1 100644 --- a/media/codec2/components/hevc/C2SoftHevcEnc.cpp +++ b/media/codec2/components/hevc/C2SoftHevcEnc.cpp @@ -591,8 +591,7 @@ C2SoftHevcEnc::C2SoftHevcEnc(const char* name, c2_node_id_t id, CREATE_DUMP_FILE(mInFile); CREATE_DUMP_FILE(mOutFile); - gettimeofday(&mTimeStart, nullptr); - gettimeofday(&mTimeEnd, nullptr); + mTimeStart = mTimeEnd = systemTime(); } C2SoftHevcEnc::~C2SoftHevcEnc() { @@ -902,7 +901,8 @@ c2_status_t C2SoftHevcEnc::setEncodeArgs(ihevce_inp_buf_t* ps_encode_ip, yStride = width; uStride = vStride = yStride / 2; ConvertRGBToPlanarYUV(yPlane, yStride, height, - conversionBuffer.size(), *input); + conversionBuffer.size(), *input, + mColorAspects->matrix, mColorAspects->range); break; } case C2PlanarLayout::TYPE_YUV: { @@ -1203,11 +1203,11 @@ void C2SoftHevcEnc::process(const std::unique_ptr& work, } } - uint64_t timeDelay = 0; - uint64_t timeTaken = 0; + nsecs_t timeDelay = 0; + nsecs_t timeTaken = 0; memset(&s_encode_op, 0, sizeof(s_encode_op)); - GETTIME(&mTimeStart, nullptr); - TIME_DIFF(mTimeEnd, mTimeStart, timeDelay); + mTimeStart = systemTime(); + timeDelay = mTimeStart - mTimeEnd; if (inputBuffer) { err = ihevce_encode(mCodecCtx, &s_encode_ip, &s_encode_op); @@ -1222,12 +1222,12 @@ void C2SoftHevcEnc::process(const std::unique_ptr& work, fillEmptyWork(work); } - GETTIME(&mTimeEnd, nullptr); /* Compute time taken for decode() */ - TIME_DIFF(mTimeStart, mTimeEnd, timeTaken); + mTimeEnd = systemTime(); + timeTaken = mTimeEnd - mTimeStart; - ALOGV("timeTaken=%6d delay=%6d numBytes=%6d", (int)timeTaken, - (int)timeDelay, s_encode_op.i4_bytes_generated); + ALOGV("timeTaken=%6" PRId64 " delay=%6" PRId64 " numBytes=%6d", timeTaken, + timeDelay, s_encode_op.i4_bytes_generated); if (s_encode_op.i4_bytes_generated) { finishWork(s_encode_op.u8_pts, work, pool, &s_encode_op); diff --git a/media/codec2/components/hevc/C2SoftHevcEnc.h b/media/codec2/components/hevc/C2SoftHevcEnc.h index 4217a8be5c5abfc3b473e7f47aa8b05dbab91392..ce9cec8bb8c7c581b70b326b6606c1bbe8601ce0 100644 --- a/media/codec2/components/hevc/C2SoftHevcEnc.h +++ b/media/codec2/components/hevc/C2SoftHevcEnc.h @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -27,14 +28,6 @@ namespace android { -/** Get time */ -#define GETTIME(a, b) gettimeofday(a, b) - -/** Compute difference between start and end */ -#define TIME_DIFF(start, end, diff) \ - diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \ - ((end).tv_usec - (start).tv_usec); - #define CODEC_MAX_CORES 4 #define MAX_B_FRAMES 1 #define MAX_RC_LOOKAHEAD 1 @@ -102,8 +95,8 @@ struct C2SoftHevcEnc : public SimpleC2Component { #endif /* FILE_DUMP_ENABLE */ // profile - struct timeval mTimeStart; - struct timeval mTimeEnd; + nsecs_t mTimeStart = 0; + nsecs_t mTimeEnd = 0; c2_status_t initEncParams(); c2_status_t initEncoder(); @@ -127,14 +120,12 @@ struct C2SoftHevcEnc : public SimpleC2Component { #define OUTPUT_DUMP_EXT "h265" #define GENERATE_FILE_NAMES() \ { \ - GETTIME(&mTimeStart, NULL); \ - strcpy(mInFile, ""); \ + nsecs_t now = systemTime(); \ ALOGD("GENERATE_FILE_NAMES"); \ - sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH, mTimeStart.tv_sec, \ - mTimeStart.tv_usec, INPUT_DUMP_EXT); \ - strcpy(mOutFile, ""); \ - sprintf(mOutFile, "%s_%ld.%ld.%s", OUTPUT_DUMP_PATH, \ - mTimeStart.tv_sec, mTimeStart.tv_usec, OUTPUT_DUMP_EXT); \ + sprintf(mInFile, "%s_%" PRId64 ".%s", INPUT_DUMP_PATH, \ + mTimeStart, INPUT_DUMP_EXT); \ + sprintf(mutFile, "%s_%" PRId64 ".%s", OUTPUT_DUMP_PATH, \ + mTimeStart, OUTPUT_DUMP_EXT); \ } #define CREATE_DUMP_FILE(m_filename) \ diff --git a/media/codec2/components/mpeg2/Android.bp b/media/codec2/components/mpeg2/Android.bp index daa10aef7680d7e84388b5d33bc43721530dbe66..a58044ce6be8b46382e0bf2af40329782fee29ba 100644 --- a/media/codec2/components/mpeg2/Android.bp +++ b/media/codec2/components/mpeg2/Android.bp @@ -17,9 +17,4 @@ cc_library { srcs: ["C2SoftMpeg2Dec.cpp"], static_libs: ["libmpeg2dec"], - - include_dirs: [ - "external/libmpeg2/decoder", - "external/libmpeg2/common", - ], } diff --git a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp index 5f9b30bf74bb5f4704473d7e6fdcb9e532b83c29..9a4191025f9b167404719056ac17f5d60256e002 100644 --- a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp +++ b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp @@ -731,8 +731,7 @@ status_t C2SoftMpeg2Dec::resetDecoder() { void C2SoftMpeg2Dec::resetPlugin() { mSignalledOutputEos = false; - gettimeofday(&mTimeStart, nullptr); - gettimeofday(&mTimeEnd, nullptr); + mTimeStart = mTimeEnd = systemTime(); } status_t C2SoftMpeg2Dec::deleteDecoder() { @@ -929,14 +928,12 @@ void C2SoftMpeg2Dec::process( } // If input dump is enabled, then write to file DUMP_TO_FILE(mInFile, s_decode_ip.pv_stream_buffer, s_decode_ip.u4_num_Bytes); - WORD32 delay; - GETTIME(&mTimeStart, nullptr); - TIME_DIFF(mTimeEnd, mTimeStart, delay); + nsecs_t delay = mTimeStart - mTimeEnd; (void) ivdec_api_function(mDecHandle, &s_decode_ip, &s_decode_op); - WORD32 decodeTime; - GETTIME(&mTimeEnd, nullptr); - TIME_DIFF(mTimeStart, mTimeEnd, decodeTime); - ALOGV("decodeTime=%6d delay=%6d numBytes=%6d ", decodeTime, delay, + + mTimeEnd = systemTime(); + nsecs_t decodeTime = mTimeEnd - mTimeStart; + ALOGV("decodeTime=%" PRId64 " delay=%" PRId64 " numBytes=%6d ", decodeTime, delay, s_decode_op.u4_num_bytes_consumed); if (IMPEG2D_UNSUPPORTED_DIMENSIONS == s_decode_op.u4_error_code) { ALOGV("unsupported resolution : %dx%d", s_decode_op.u4_pic_wd, s_decode_op.u4_pic_ht); diff --git a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h index 8a29c145063f31586dd14c4c18c36c47f4981bfc..3965bcc4fcc04438ed72697446a67cbf8ff6b5ad 100644 --- a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h +++ b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.h @@ -18,6 +18,7 @@ #define ANDROID_C2_SOFT_MPEG2_DEC_H_ #include +#include #include #include @@ -42,19 +43,14 @@ namespace android { #define IVDEXT_CMD_CTL_SET_NUM_CORES \ (IVD_CONTROL_API_COMMAND_TYPE_T)IMPEG2D_CMD_CTL_SET_NUM_CORES #define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define GETTIME(a, b) gettimeofday(a, b); -#define TIME_DIFF(start, end, diff) \ - diff = (((end).tv_sec - (start).tv_sec) * 1000000) + \ - ((end).tv_usec - (start).tv_usec); #ifdef FILE_DUMP_ENABLE #define INPUT_DUMP_PATH "/sdcard/clips/mpeg2d_input" #define INPUT_DUMP_EXT "m2v" #define GENERATE_FILE_NAMES() { \ - GETTIME(&mTimeStart, NULL); \ - strcpy(mInFile, ""); \ - sprintf(mInFile, "%s_%ld.%ld.%s", INPUT_DUMP_PATH, \ - mTimeStart.tv_sec, mTimeStart.tv_usec, \ + nsecs_t now = systemTime(); \ + sprintf(mInFile, "%s_%" PRId64 ".%s", \ + INPUT_DUMP_PATH, now, \ INPUT_DUMP_EXT); \ } #define CREATE_DUMP_FILE(m_filename) { \ @@ -183,8 +179,8 @@ struct C2SoftMpeg2Dec : public SimpleC2Component { } mBitstreamColorAspects; // profile - struct timeval mTimeStart; - struct timeval mTimeEnd; + nsecs_t mTimeStart = 0; + nsecs_t mTimeEnd = 0; #ifdef FILE_DUMP_ENABLE char mInFile[200]; #endif /* FILE_DUMP_ENABLE */ diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.cpp b/media/codec2/components/vpx/C2SoftVpxEnc.cpp index 617769b32693aaac93d8d151fab20b980ace99d5..f99ee248dfa7a8abe18b8a6811f902424f888d06 100644 --- a/media/codec2/components/vpx/C2SoftVpxEnc.cpp +++ b/media/codec2/components/vpx/C2SoftVpxEnc.cpp @@ -733,8 +733,14 @@ void C2SoftVpxEnc::process( switch (layout.type) { case C2PlanarLayout::TYPE_RGB: case C2PlanarLayout::TYPE_RGBA: { + std::shared_ptr colorAspects; + { + IntfImpl::Lock lock = mIntf->lock(); + colorAspects = mIntf->getCodedColorAspects_l(); + } ConvertRGBToPlanarYUV(mConversionBuffer.data(), stride, vstride, - mConversionBuffer.size(), *rView.get()); + mConversionBuffer.size(), *rView.get(), + colorAspects->matrix, colorAspects->range); vpx_img_wrap(&raw_frame, VPX_IMG_FMT_I420, width, height, mStrideAlign, mConversionBuffer.data()); break; diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.h b/media/codec2/components/vpx/C2SoftVpxEnc.h index e296c8fc3462a40e2f1651e37e5f205943f3b238..714fadb504d7065f68d711c9ea0aa8255693b101 100644 --- a/media/codec2/components/vpx/C2SoftVpxEnc.h +++ b/media/codec2/components/vpx/C2SoftVpxEnc.h @@ -265,6 +265,9 @@ class C2SoftVpxEnc::IntfImpl : public SimpleInterface::BaseParams { std::shared_ptr getTemporalLayers_l() const { return mLayering; } + std::shared_ptr getCodedColorAspects_l() const { + return mCodedColorAspects; + } uint32_t getSyncFramePeriod() const; static C2R ColorAspectsSetter(bool mayBlock, C2P &me); static C2R CodedColorAspectsSetter(bool mayBlock, C2P &me, diff --git a/media/codec2/core/Android.bp b/media/codec2/core/Android.bp index 64999b7ebc529db2dc55ea805c96c695e8a50f0b..7d5740ba9ad3ce42199a54fd1140e838c6d8c39a 100644 --- a/media/codec2/core/Android.bp +++ b/media/codec2/core/Android.bp @@ -11,6 +11,10 @@ cc_library_headers { name: "libcodec2_headers", vendor_available: true, min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + ], export_include_dirs: ["include"], } @@ -18,6 +22,10 @@ cc_library { name: "libcodec2", vendor_available: true, min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + ], vndk: { enabled: true, }, diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h index fca8f4fc35a4a5fe5fbebbb4ed2df5f7f35a916b..6ff3dbc90b5828fe337afcc5d207511cb6fe0d0d 100644 --- a/media/codec2/core/include/C2Config.h +++ b/media/codec2/core/include/C2Config.h @@ -79,6 +79,7 @@ struct C2Config { struct C2PlatformConfig { enum encoding_quality_level_t : uint32_t; ///< encoding quality level + enum tunnel_peek_mode_t: uint32_t; ///< tunnel peek mode }; namespace { @@ -277,6 +278,12 @@ enum C2ParamIndexKind : C2Param::type_index_t { // encoding statistics, average block qp of a frame kParamIndexAverageBlockQuantization, // int32 + + // channel mask for decoded audio + kParamIndexAndroidChannelMask, // uint32 + + // allow tunnel peek behavior to be unspecified for app compatibility + kParamIndexTunnelPeekMode, // tunnel mode, enum }; } @@ -2008,6 +2015,14 @@ typedef C2StreamParam C2Strea constexpr char C2_PARAMKEY_MAX_CHANNEL_COUNT[] = "raw.max-channel-count"; constexpr char C2_PARAMKEY_MAX_CODED_CHANNEL_COUNT[] = "coded.max-channel-count"; +/** + * Audio channel mask. Used by decoder to express audio channel mask of decoded content. + * Channel representation is specified according to the Java android.media.AudioFormat + * CHANNEL_OUT_* constants. + */ + typedef C2StreamParam C2StreamChannelMaskInfo; + const char C2_PARAMKEY_CHANNEL_MASK[] = "raw.channel-mask"; + /** * Audio sample format (PCM encoding) */ @@ -2471,6 +2486,28 @@ typedef C2StreamParam C2StreamTunnelStartRender; constexpr char C2_PARAMKEY_TUNNEL_START_RENDER[] = "output.tunnel-start-render"; +/** Tunnel Peek Mode. */ +C2ENUM(C2PlatformConfig::tunnel_peek_mode_t, uint32_t, + UNSPECIFIED_PEEK = 0, + SPECIFIED_PEEK = 1 +); + +/** + * Tunnel Peek Mode Tuning parameter. + * + * If set to UNSPECIFIED_PEEK_MODE, the decoder is free to ignore the + * C2StreamTunnelHoldRender and C2StreamTunnelStartRender flags and associated + * features. Additionally, it becomes up to the decoder to display any frame + * before receiving synchronization information. + * + * Note: This parameter allows a decoder to ignore the video peek machinery and + * to revert to its preferred behavior. + */ +typedef C2StreamParam, + kParamIndexTunnelPeekMode> C2StreamTunnelPeekModeTuning; +constexpr char C2_PARAMKEY_TUNNEL_PEEK_MODE[] = + "output.tunnel-peek-mode"; + /** * Encoding quality level signaling. * diff --git a/media/codec2/hidl/1.0/utils/Android.bp b/media/codec2/hidl/1.0/utils/Android.bp index 122aacdf42f5ac1f967eb6c0fb49e4ef8887efc0..db7874d43207b9cfea0289e6901fc743ee6b24d6 100644 --- a/media/codec2/hidl/1.0/utils/Android.bp +++ b/media/codec2/hidl/1.0/utils/Android.bp @@ -57,6 +57,10 @@ cc_library { name: "libcodec2_hidl@1.0", vendor_available: true, min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + ], defaults: ["hidl_defaults"], diff --git a/media/codec2/hidl/1.0/vts/functional/common/Android.bp b/media/codec2/hidl/1.0/vts/functional/common/Android.bp index 4106be8403fd07d4734f76da9f4cf1efeae49f78..be4bafaf9c75e667ee1a05dde2bf9a3407203522 100644 --- a/media/codec2/hidl/1.0/vts/functional/common/Android.bp +++ b/media/codec2/hidl/1.0/vts/functional/common/Android.bp @@ -14,8 +14,8 @@ cc_library_static { "libcodec2-hidl-client-defaults", ], - include_dirs: [ - "frameworks/av/media/codec2/hidl/client/include", + header_libs: [ + "libcodec2_client_headers", ], srcs: [ diff --git a/media/codec2/hidl/1.1/utils/Android.bp b/media/codec2/hidl/1.1/utils/Android.bp index 0eeedb603a951bda04994bdae708ce9b0fe1ea92..ed77a151598ba88a4bf99d111e495d4c90de2ab2 100644 --- a/media/codec2/hidl/1.1/utils/Android.bp +++ b/media/codec2/hidl/1.1/utils/Android.bp @@ -67,6 +67,11 @@ cc_library { name: "libcodec2_hidl@1.1", vendor_available: true, min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + ], + defaults: ["hidl_defaults"], diff --git a/media/codec2/hidl/client/Android.bp b/media/codec2/hidl/client/Android.bp index 0e52813d4e6e2ce2832199cd11bf630210254427..f32711db420494044540d33a9ca217cb17996511 100644 --- a/media/codec2/hidl/client/Android.bp +++ b/media/codec2/hidl/client/Android.bp @@ -7,6 +7,24 @@ package { default_applicable_licenses: ["frameworks_av_license"], } +cc_library_headers { + name: "libcodec2_client_headers", + export_include_dirs: ["include"], + vendor_available: true, + apex_available: [ + "//apex_available:platform", + "com.android.media", + "com.android.media.swcodec", + ], + min_sdk_version: "29", + host_supported: true, + target: { + darwin: { + enabled: false, + }, + }, +} + cc_library { name: "libcodec2_client", diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp index 42b3c4334d4433979d6548e3392278615b81fb54..0acf7d726c9a1d66f66a0da7a516c81c3b96e9cf 100644 --- a/media/codec2/hidl/client/client.cpp +++ b/media/codec2/hidl/client/client.cpp @@ -1502,6 +1502,7 @@ c2_status_t Codec2Client::Component::setOutputSurface( igbp = new B2HGraphicBufferProducer2(surface); } + std::scoped_lock lock(mOutputMutex); std::shared_ptr syncObj; if (!surface) { @@ -1586,6 +1587,24 @@ void Codec2Client::Component::setOutputSurfaceMaxDequeueCount( mOutputBufferQueue->updateMaxDequeueBufferCount(maxDequeueCount); } +void Codec2Client::Component::stopUsingOutputSurface( + C2BlockPool::local_id_t blockPoolId) { + std::scoped_lock lock(mOutputMutex); + mOutputBufferQueue->stop(); + Return transStatus = mBase1_0->setOutputSurface( + static_cast(blockPoolId), nullptr); + if (!transStatus.isOk()) { + LOG(ERROR) << "setOutputSurface(stopUsingOutputSurface) -- transaction failed."; + } else { + c2_status_t status = + static_cast(static_cast(transStatus)); + if (status != C2_OK) { + LOG(DEBUG) << "setOutputSurface(stopUsingOutputSurface) -- call failed: " + << status << "."; + } + } +} + c2_status_t Codec2Client::Component::connectToInputSurface( const std::shared_ptr& inputSurface, std::shared_ptr* connection) { diff --git a/media/codec2/hidl/client/include/codec2/hidl/client.h b/media/codec2/hidl/client/include/codec2/hidl/client.h index 347e58a74a482db5b38a51a86f73f826b06befb2..49d9b28ba6effd75d94d78d59a549279a95bdcf6 100644 --- a/media/codec2/hidl/client/include/codec2/hidl/client.h +++ b/media/codec2/hidl/client/include/codec2/hidl/client.h @@ -411,6 +411,10 @@ struct Codec2Client::Component : public Codec2Client::Configurable { // Set max dequeue count for output surface. void setOutputSurfaceMaxDequeueCount(int maxDequeueCount); + // Stop using the current output surface. + void stopUsingOutputSurface( + C2BlockPool::local_id_t blockPoolId); + // Connect to a given InputSurface. c2_status_t connectToInputSurface( const std::shared_ptr& inputSurface, @@ -441,6 +445,11 @@ protected: struct OutputBufferQueue; std::unique_ptr mOutputBufferQueue; + // (b/202903117) Sometimes MediaCodec::setSurface races between normal + // setSurface and setSurface with ReleaseSurface due to timing issues. + // In order to prevent the race condition mutex is added. + std::mutex mOutputMutex; + static c2_status_t setDeathListener( const std::shared_ptr& component, const std::shared_ptr& listener); diff --git a/media/codec2/hidl/client/include/codec2/hidl/output.h b/media/codec2/hidl/client/include/codec2/hidl/output.h index 877148a7ff60a28bb60da51fe349e0b33ffb78c6..a13edf3e76fc4a73ad8f067148e28e836f054f91 100644 --- a/media/codec2/hidl/client/include/codec2/hidl/output.h +++ b/media/codec2/hidl/client/include/codec2/hidl/output.h @@ -50,6 +50,10 @@ struct OutputBufferQueue { int maxDequeueBufferCount, std::shared_ptr *syncObj); + // Stop using the current output surface. Pending buffer opeations will not + // perform anymore. + void stop(); + // Render a graphic block to current surface. status_t outputBuffer( const C2ConstGraphicBlock& block, @@ -81,6 +85,7 @@ private: sp mBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS]; // find a better way std::weak_ptr<_C2BlockPoolData> mPoolDatas[BufferQueueDefs::NUM_BUFFER_SLOTS]; std::shared_ptr mSyncMem; + bool mStopped; bool registerBuffer(const C2ConstGraphicBlock& block); }; diff --git a/media/codec2/hidl/client/output.cpp b/media/codec2/hidl/client/output.cpp index de34c24aca7f8d72e2cc7c84c91582c82b2933b0..f789030abb45ab370197ab447b015b4e3549ecd1 100644 --- a/media/codec2/hidl/client/output.cpp +++ b/media/codec2/hidl/client/output.cpp @@ -169,7 +169,7 @@ bool getBufferQueueAssignment(const C2ConstGraphicBlock& block, } // unnamed namespace OutputBufferQueue::OutputBufferQueue() - : mGeneration{0}, mBqId{0} { + : mGeneration{0}, mBqId{0}, mStopped{false} { } OutputBufferQueue::~OutputBufferQueue() { @@ -219,6 +219,8 @@ bool OutputBufferQueue::configure(const sp& igbp, poolDatas[BufferQueueDefs::NUM_BUFFER_SLOTS]; { std::scoped_lock l(mMutex); + bool stopped = mStopped; + mStopped = false; if (generation == mGeneration) { // case of old BlockPool destruction C2SyncVariables *var = mSyncMem ? mSyncMem->mem() : nullptr; @@ -258,7 +260,7 @@ bool OutputBufferQueue::configure(const sp& igbp, return false; } for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; ++i) { - if (mBqId == 0 || !mBuffers[i]) { + if (mBqId == 0 || !mBuffers[i] || stopped) { continue; } std::shared_ptr<_C2BlockPoolData> data = mPoolDatas[i].lock(); @@ -317,6 +319,12 @@ bool OutputBufferQueue::configure(const sp& igbp, return true; } +void OutputBufferQueue::stop() { + std::scoped_lock l(mMutex); + mStopped = true; + mOwner.reset(); // destructor of the block will not triger IGBP::cancel() +} + bool OutputBufferQueue::registerBuffer(const C2ConstGraphicBlock& block) { std::shared_ptr<_C2BlockPoolData> data = _C2BlockFactory::GetGraphicBlockPoolData(block); @@ -325,7 +333,7 @@ bool OutputBufferQueue::registerBuffer(const C2ConstGraphicBlock& block) { } std::scoped_lock l(mMutex); - if (!mIgbp) { + if (!mIgbp || mStopped) { return false; } @@ -371,11 +379,17 @@ status_t OutputBufferQueue::outputBuffer( std::shared_ptr syncMem; mMutex.lock(); + bool stopped = mStopped; sp outputIgbp = mIgbp; uint32_t outputGeneration = mGeneration; syncMem = mSyncMem; mMutex.unlock(); + if (stopped) { + LOG(INFO) << "outputBuffer -- already stopped."; + return DEAD_OBJECT; + } + status_t status = attachToBufferQueue( block, outputIgbp, outputGeneration, &bqSlot, syncMem); @@ -408,12 +422,18 @@ status_t OutputBufferQueue::outputBuffer( std::shared_ptr syncMem; mMutex.lock(); + bool stopped = mStopped; sp outputIgbp = mIgbp; uint32_t outputGeneration = mGeneration; uint64_t outputBqId = mBqId; syncMem = mSyncMem; mMutex.unlock(); + if (stopped) { + LOG(INFO) << "outputBuffer -- already stopped."; + return DEAD_OBJECT; + } + if (!outputIgbp) { LOG(VERBOSE) << "outputBuffer -- output surface is null."; return NO_INIT; @@ -467,7 +487,7 @@ void OutputBufferQueue::updateMaxDequeueBufferCount(int maxDequeueBufferCount) { mMutex.lock(); mMaxDequeueBufferCount = maxDequeueBufferCount; auto syncVar = mSyncMem ? mSyncMem->mem() : nullptr; - if (syncVar) { + if (syncVar && !mStopped) { syncVar->lock(); syncVar->updateMaxDequeueCountLocked(maxDequeueBufferCount); syncVar->unlock(); diff --git a/media/codec2/sfplugin/Android.bp b/media/codec2/sfplugin/Android.bp index 134bc53c738660db543f80eb75bc5b662e080519..5a652a364d57bfef9baff74447d687259e957eb8 100644 --- a/media/codec2/sfplugin/Android.bp +++ b/media/codec2/sfplugin/Android.bp @@ -7,6 +7,12 @@ package { default_applicable_licenses: ["frameworks_av_license"], } +cc_library_headers { + name: "libsfplugin_ccodec_internal_headers", + export_include_dirs: ["."], + // only for internal tests, perhaps restrict via visibility clause +} + cc_library_shared { name: "libsfplugin_ccodec", diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp index 39e5bd89638f67bd94b47b7ee384346bdeb1c638..296d7ed771bcd9da2543a8503da659eaee62142c 100644 --- a/media/codec2/sfplugin/CCodec.cpp +++ b/media/codec2/sfplugin/CCodec.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -1011,7 +1012,9 @@ void CCodec::configure(const sp &msg) { // Query vendor format for Flexible YUV std::vector> heapParams; C2StoreFlexiblePixelFormatDescriptorsInfo *pixelFormatInfo = nullptr; - if (mClient->query( + int vendorSdkVersion = base::GetIntProperty( + "ro.vendor.build.version.sdk", android_get_device_api_level()); + if (vendorSdkVersion >= __ANDROID_API_S__ && mClient->query( {}, {C2StoreFlexiblePixelFormatDescriptorsInfo::PARAM_TYPE}, C2_MAY_BLOCK, @@ -1071,6 +1074,17 @@ void CCodec::configure(const sp &msg) { } } else { if ((config->mDomain & Config::IS_ENCODER) || !surface) { + if (vendorSdkVersion < __ANDROID_API_S__ && + (format == COLOR_FormatYUV420Flexible || + format == COLOR_FormatYUV420Planar || + format == COLOR_FormatYUV420PackedPlanar || + format == COLOR_FormatYUV420SemiPlanar || + format == COLOR_FormatYUV420PackedSemiPlanar)) { + // pre-S framework used to map these color formats into YV12. + // Codecs from older vendor partition may be relying on + // this assumption. + format = HAL_PIXEL_FORMAT_YV12; + } switch (format) { case COLOR_FormatYUV420Flexible: format = COLOR_FormatYUV420Planar; @@ -1421,7 +1435,7 @@ void CCodec::configure(const sp &msg) { int64_t blockUsage = usage.value | C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE; std::shared_ptr block = FetchGraphicBlock( - width, height, pixelFormat, blockUsage, {comp->getName()}); + width, height, componentColorFormat, blockUsage, {comp->getName()}); sp buffer; if (block) { buffer = GraphicBlockBuffer::Allocate( @@ -1535,6 +1549,9 @@ sp CCodec::CreateOmxInputSurface() { using namespace android::hardware::graphics::bufferqueue::V1_0::utils; typedef android::hardware::media::omx::V1_0::Status OmxStatus; android::sp omx = IOmx::getService(); + if (omx == nullptr) { + return nullptr; + } typedef android::hardware::graphics::bufferqueue::V1_0:: IGraphicBufferProducer HGraphicBufferProducer; typedef android::hardware::media::omx::V1_0:: @@ -1813,15 +1830,20 @@ void CCodec::start() { return; } - err2 = mChannel->requestInitialInputBuffers(); - + // preparation of input buffers may not succeed due to the lack of + // memory; returning correct error code (NO_MEMORY) as an error allows + // MediaCodec to try reclaim and restart codec gracefully. + std::map> clientInputBuffers; + err2 = mChannel->prepareInitialInputBuffers(&clientInputBuffers); if (err2 != OK) { - ALOGE("Initial request for Input Buffers failed"); - mCallback->onError(err2,ACTION_CODE_FATAL); + ALOGE("Initial preparation for Input Buffers failed"); + mCallback->onError(err2, ACTION_CODE_FATAL); return; } + mCallback->onStartCompleted(); + mChannel->requestInitialInputBuffers(std::move(clientInputBuffers)); } void CCodec::initiateShutdown(bool keepComponentAllocated) { @@ -2115,7 +2137,14 @@ void CCodec::signalResume() { state->set(RUNNING); } - (void)mChannel->requestInitialInputBuffers(); + std::map> clientInputBuffers; + status_t err = mChannel->prepareInitialInputBuffers(&clientInputBuffers); + if (err != OK) { + ALOGE("Resume request for Input Buffers failed"); + mCallback->onError(err, ACTION_CODE_FATAL); + return; + } + mChannel->requestInitialInputBuffers(std::move(clientInputBuffers)); } void CCodec::signalSetParameters(const sp &msg) { diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp index a00adde2a95cff65e1df6406f3c0590d58c58881..5ecb1307f088a37601aca4863d9ec2156ea95102 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.cpp +++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp @@ -432,6 +432,10 @@ status_t CCodecBufferChannel::attachEncryptedBuffer( for (size_t i = 0; i < numSubSamples; ++i) { size += subSamples[i].mNumBytesOfClearData + subSamples[i].mNumBytesOfEncryptedData; } + if (size == 0) { + buffer->setRange(0, 0); + return OK; + } std::shared_ptr pool = mBlockPools.lock()->inputPool; std::shared_ptr block; c2_status_t err = pool->fetchLinearBlock( @@ -439,6 +443,8 @@ status_t CCodecBufferChannel::attachEncryptedBuffer( secure ? kSecureUsage : kDefaultReadWriteUsage, &block); if (err != C2_OK) { + ALOGI("[%s] attachEncryptedBuffer: fetchLinearBlock failed: size = %zu (%s) err = %d", + mName, size, secure ? "secure" : "non-secure", err); return NO_MEMORY; } if (!secure) { @@ -463,18 +469,9 @@ status_t CCodecBufferChannel::attachEncryptedBuffer( key, iv, mode, pattern, src, 0, subSamples, numSubSamples, dst, &errorDetailMsg); if (result < 0) { + ALOGI("[%s] attachEncryptedBuffer: decrypt failed: result = %zd", mName, result); return result; } - if (dst.type == DrmBufferType::SHARED_MEMORY) { - C2WriteView view = block->map().get(); - if (view.error() != C2_OK) { - return false; - } - if (view.size() < result) { - return false; - } - memcpy(view.data(), mDecryptDestination->unsecurePointer(), result); - } } else { // Here we cast CryptoPlugin::SubSample to hardware::cas::native::V1_0::SubSample // directly, the structure definitions should match as checked in DescramblerImpl.cpp. @@ -523,16 +520,22 @@ status_t CCodecBufferChannel::attachEncryptedBuffer( } if (result < codecDataOffset) { - ALOGD("invalid codec data offset: %zd, result %zd", codecDataOffset, result); + ALOGD("[%s] invalid codec data offset: %zd, result %zd", + mName, codecDataOffset, result); return BAD_VALUE; } } if (!secure) { C2WriteView view = block->map().get(); if (view.error() != C2_OK) { + ALOGI("[%s] attachEncryptedBuffer: block map error: %d (non-secure)", + mName, view.error()); return UNKNOWN_ERROR; } if (view.size() < result) { + ALOGI("[%s] attachEncryptedBuffer: block size too small: size=%u result=%zd " + "(non-secure)", + mName, view.size(), result); return UNKNOWN_ERROR; } memcpy(view.data(), mDecryptDestination->unsecurePointer(), result); @@ -540,6 +543,7 @@ status_t CCodecBufferChannel::attachEncryptedBuffer( std::shared_ptr c2Buffer{C2Buffer::CreateLinearBuffer( block->share(codecDataOffset, result - codecDataOffset, C2Fence{}))}; if (!buffer->copy(c2Buffer)) { + ALOGI("[%s] attachEncryptedBuffer: buffer copy failed", mName); return -ENOSYS; } return OK; @@ -1012,6 +1016,10 @@ void CCodecBufferChannel::getInputBufferArray(Vector> *arra array->clear(); Mutexed::Locked input(mInput); + if (!input->buffers) { + ALOGE("getInputBufferArray: No Input Buffers allocated"); + return; + } if (!input->buffers->isArrayMode()) { input->buffers = input->buffers->toArrayMode(input->numSlots); } @@ -1022,7 +1030,10 @@ void CCodecBufferChannel::getInputBufferArray(Vector> *arra void CCodecBufferChannel::getOutputBufferArray(Vector> *array) { array->clear(); Mutexed::Locked output(mOutput); - + if (!output->buffers) { + ALOGE("getOutputBufferArray: No Output Buffers allocated"); + return; + } if (!output->buffers->isArrayMode()) { output->buffers = output->buffers->toArrayMode(output->numSlots); } @@ -1470,54 +1481,47 @@ status_t CCodecBufferChannel::start( return OK; } -status_t CCodecBufferChannel::requestInitialInputBuffers() { +status_t CCodecBufferChannel::prepareInitialInputBuffers( + std::map> *clientInputBuffers) { if (mInputSurface) { return OK; } - C2StreamBufferTypeSetting::output oStreamFormat(0u); - C2PrependHeaderModeSetting prepend(PREPEND_HEADER_TO_NONE); - c2_status_t err = mComponent->query({ &oStreamFormat, &prepend }, {}, C2_DONT_BLOCK, nullptr); - if (err != C2_OK && err != C2_BAD_INDEX) { - return UNKNOWN_ERROR; - } size_t numInputSlots = mInput.lock()->numSlots; - struct ClientInputBuffer { - size_t index; - sp buffer; - size_t capacity; - }; - std::list clientInputBuffers; - { Mutexed::Locked input(mInput); - while (clientInputBuffers.size() < numInputSlots) { - ClientInputBuffer clientInputBuffer; - if (!input->buffers->requestNewBuffer(&clientInputBuffer.index, - &clientInputBuffer.buffer)) { + while (clientInputBuffers->size() < numInputSlots) { + size_t index; + sp buffer; + if (!input->buffers->requestNewBuffer(&index, &buffer)) { break; } - clientInputBuffer.capacity = clientInputBuffer.buffer->capacity(); - clientInputBuffers.emplace_back(std::move(clientInputBuffer)); + clientInputBuffers->emplace(index, buffer); } } - if (clientInputBuffers.empty()) { + if (clientInputBuffers->empty()) { ALOGW("[%s] start: cannot allocate memory at all", mName); return NO_MEMORY; - } else if (clientInputBuffers.size() < numInputSlots) { + } else if (clientInputBuffers->size() < numInputSlots) { ALOGD("[%s] start: cannot allocate memory for all slots, " "only %zu buffers allocated", - mName, clientInputBuffers.size()); + mName, clientInputBuffers->size()); } else { ALOGV("[%s] %zu initial input buffers available", - mName, clientInputBuffers.size()); + mName, clientInputBuffers->size()); + } + return OK; +} + +status_t CCodecBufferChannel::requestInitialInputBuffers( + std::map> &&clientInputBuffers) { + C2StreamBufferTypeSetting::output oStreamFormat(0u); + C2PrependHeaderModeSetting prepend(PREPEND_HEADER_TO_NONE); + c2_status_t err = mComponent->query({ &oStreamFormat, &prepend }, {}, C2_DONT_BLOCK, nullptr); + if (err != C2_OK && err != C2_BAD_INDEX) { + return UNKNOWN_ERROR; } - // Sort input buffers by their capacities in increasing order. - clientInputBuffers.sort( - [](const ClientInputBuffer& a, const ClientInputBuffer& b) { - return a.capacity < b.capacity; - }); std::list> flushedConfigs; mFlushedConfigs.lock()->swap(flushedConfigs); @@ -1539,25 +1543,31 @@ status_t CCodecBufferChannel::requestInitialInputBuffers() { } } if (oStreamFormat.value == C2BufferData::LINEAR && - (!prepend || prepend.value == PREPEND_HEADER_TO_NONE)) { - sp buffer = clientInputBuffers.front().buffer; + (!prepend || prepend.value == PREPEND_HEADER_TO_NONE) && + !clientInputBuffers.empty()) { + size_t minIndex = clientInputBuffers.begin()->first; + sp minBuffer = clientInputBuffers.begin()->second; + for (const auto &[index, buffer] : clientInputBuffers) { + if (minBuffer->capacity() > buffer->capacity()) { + minIndex = index; + minBuffer = buffer; + } + } // WORKAROUND: Some apps expect CSD available without queueing // any input. Queue an empty buffer to get the CSD. - buffer->setRange(0, 0); - buffer->meta()->clear(); - buffer->meta()->setInt64("timeUs", 0); - if (queueInputBufferInternal(buffer) != OK) { + minBuffer->setRange(0, 0); + minBuffer->meta()->clear(); + minBuffer->meta()->setInt64("timeUs", 0); + if (queueInputBufferInternal(minBuffer) != OK) { ALOGW("[%s] Error while queueing an empty buffer to get CSD", mName); return UNKNOWN_ERROR; } - clientInputBuffers.pop_front(); + clientInputBuffers.erase(minIndex); } - for (const ClientInputBuffer& clientInputBuffer: clientInputBuffers) { - mCallback->onInputBufferAvailable( - clientInputBuffer.index, - clientInputBuffer.buffer); + for (const auto &[index, buffer] : clientInputBuffers) { + mCallback->onInputBufferAvailable(index, buffer); } return OK; @@ -1583,6 +1593,14 @@ void CCodecBufferChannel::reset() { Mutexed::Locked output(mOutput); output->buffers.reset(); } + if (mOutputSurface.lock()->surface) { + C2BlockPool::local_id_t outputPoolId; + { + Mutexed::Locked pools(mBlockPools); + outputPoolId = pools->outputPoolId; + } + mComponent->stopUsingOutputSurface(outputPoolId); + } } void CCodecBufferChannel::release() { @@ -2023,6 +2041,9 @@ void CCodecBufferChannel::sendOutputBuffers() { sp outBuffer; std::shared_ptr c2Buffer; + constexpr int kMaxReallocTry = 5; + int reallocTryNum = 0; + while (true) { Mutexed::Locked output(mOutput); if (!output->buffers) { @@ -2030,6 +2051,9 @@ void CCodecBufferChannel::sendOutputBuffers() { } action = output->buffers->popFromStashAndRegister( &c2Buffer, &index, &outBuffer); + if (action != OutputBuffers::REALLOCATE) { + reallocTryNum = 0; + } switch (action) { case OutputBuffers::SKIP: return; @@ -2040,6 +2064,13 @@ void CCodecBufferChannel::sendOutputBuffers() { mCallback->onOutputBufferAvailable(index, outBuffer); break; case OutputBuffers::REALLOCATE: + if (++reallocTryNum > kMaxReallocTry) { + output.unlock(); + ALOGE("[%s] sendOutputBuffers: tried %d realloc and failed", + mName, kMaxReallocTry); + mCCodecCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL); + return; + } if (!output->buffers->isArrayMode()) { output->buffers = output->buffers->toArrayMode(output->numSlots); diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h index b3a5f4b2481a9442b6e5e51243fbe061b837d65b..f29a22521e7758058fce9eb83436bce463925259 100644 --- a/media/codec2/sfplugin/CCodecBufferChannel.h +++ b/media/codec2/sfplugin/CCodecBufferChannel.h @@ -130,9 +130,23 @@ public: bool buffersBoundToCodec); /** - * Request initial input buffers to be filled by client. + * Prepare initial input buffers to be filled by client. + * + * \param clientInputBuffers[out] pointer to slot index -> buffer map. + * On success, it contains prepared + * initial input buffers. + */ + status_t prepareInitialInputBuffers( + std::map> *clientInputBuffers); + + /** + * Request initial input buffers as prepared in clientInputBuffers. + * + * \param clientInputBuffers[in] slot index -> buffer map with prepared + * initial input buffers. */ - status_t requestInitialInputBuffers(); + status_t requestInitialInputBuffers( + std::map> &&clientInputBuffers); /** * Stop queueing buffers to the component. This object should never queue diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp index 8f61129769a3f3c8afa93129116e2bf43dcb11ab..0f4a8d82a3d848e79c0094810f0e86a62fb170e8 100644 --- a/media/codec2/sfplugin/CCodecBuffers.cpp +++ b/media/codec2/sfplugin/CCodecBuffers.cpp @@ -35,7 +35,7 @@ namespace { constexpr uint32_t PIXEL_FORMAT_UNKNOWN = 0; -sp AllocateGraphicBuffer( +sp AllocateInputGraphicBuffer( const std::shared_ptr &pool, const sp &format, uint32_t pixelFormat, @@ -47,9 +47,13 @@ sp AllocateGraphicBuffer( return nullptr; } + int64_t usageValue = 0; + (void)format->findInt64("android._C2MemoryUsage", &usageValue); + C2MemoryUsage fullUsage{usageValue | usage.expected}; + std::shared_ptr block; c2_status_t err = pool->fetchGraphicBlock( - width, height, pixelFormat, usage, &block); + width, height, pixelFormat, fullUsage, &block); if (err != C2_OK) { ALOGD("fetch graphic block failed: %d", err); return nullptr; @@ -945,6 +949,10 @@ sp EncryptedLinearInputBuffers::Alloc( return nullptr; } + int64_t usageValue = 0; + (void)format->findInt64("android._C2MemoryUsage", &usageValue); + usage = C2MemoryUsage(usage.expected | usageValue); + std::shared_ptr block; c2_status_t err = pool->fetchLinearBlock(capacity, usage, &block); if (err != C2_OK || block == nullptr) { @@ -1089,7 +1097,7 @@ std::unique_ptr GraphicInputBuffers::toArrayMode(size_t size) { [pool = mPool, format = mFormat, lbp = mLocalBufferPool, pixelFormat]() -> sp { C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; - return AllocateGraphicBuffer( + return AllocateInputGraphicBuffer( pool, format, pixelFormat, usage, lbp); }); return std::move(array); @@ -1100,10 +1108,8 @@ size_t GraphicInputBuffers::numActiveSlots() const { } sp GraphicInputBuffers::createNewBuffer() { - int64_t usageValue = 0; - (void)mFormat->findInt64("android._C2MemoryUsage", &usageValue); - C2MemoryUsage usage{usageValue | C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE}; - return AllocateGraphicBuffer( + C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; + return AllocateInputGraphicBuffer( mPool, mFormat, extractPixelFormat(mFormat), usage, mLocalBufferPool); } diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp index c293d044e51d896c410173bbac5f3187d908b5f3..5208be67d9748ede35902ab9a9d2dc6cca557517 100644 --- a/media/codec2/sfplugin/CCodecConfig.cpp +++ b/media/codec2/sfplugin/CCodecConfig.cpp @@ -931,6 +931,9 @@ void CCodecConfig::initializeStandardParams() { add(ConfigMapper(KEY_MAX_OUTPUT_CHANNEL_COUNT, C2_PARAMKEY_MAX_CHANNEL_COUNT, "value") .limitTo(D::AUDIO & (D::CONFIG | D::PARAM | D::READ))); + add(ConfigMapper(KEY_CHANNEL_MASK, C2_PARAMKEY_CHANNEL_MASK, "value") + .limitTo(D::AUDIO & D::DECODER & D::READ)); + add(ConfigMapper(KEY_AAC_SBR_MODE, C2_PARAMKEY_AAC_SBR_MODE, "value") .limitTo(D::AUDIO & D::ENCODER & (D::CONFIG | D::PARAM | D::READ)) .withMapper([](C2Value v) -> C2Value { @@ -985,6 +988,16 @@ void CCodecConfig::initializeStandardParams() { return value == 0 ? C2_FALSE : C2_TRUE; })); + add(ConfigMapper("android._tunnel-peek-set-legacy", C2_PARAMKEY_TUNNEL_PEEK_MODE, "value") + .limitTo(D::PARAM & D::VIDEO & D::DECODER) + .withMapper([](C2Value v) -> C2Value { + int32_t value = 0; + (void)v.get(&value); + return value == 0 + ? C2Value(C2PlatformConfig::SPECIFIED_PEEK) + : C2Value(C2PlatformConfig::UNSPECIFIED_PEEK); + })); + add(ConfigMapper(KEY_VIDEO_QP_AVERAGE, C2_PARAMKEY_AVERAGE_QP, "value") .limitTo(D::ENCODER & D::VIDEO & D::READ)); diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp index 6084ee330c25ef09cbd985c95fb6cb42bf86d569..cde4c72f070cd8b5af71d02326eac9ae26ada0fd 100644 --- a/media/codec2/sfplugin/Codec2Buffer.cpp +++ b/media/codec2/sfplugin/Codec2Buffer.cpp @@ -268,6 +268,39 @@ public: mInitCheck = BAD_VALUE; return; } + std::optional clientBitDepth = {}; + switch (mClientColorFormat) { + case COLOR_FormatYUVP010: + clientBitDepth = 10; + break; + case COLOR_FormatYUV411PackedPlanar: + case COLOR_FormatYUV411Planar: + case COLOR_FormatYUV420Flexible: + case COLOR_FormatYUV420PackedPlanar: + case COLOR_FormatYUV420PackedSemiPlanar: + case COLOR_FormatYUV420Planar: + case COLOR_FormatYUV420SemiPlanar: + case COLOR_FormatYUV422Flexible: + case COLOR_FormatYUV422PackedPlanar: + case COLOR_FormatYUV422PackedSemiPlanar: + case COLOR_FormatYUV422Planar: + case COLOR_FormatYUV422SemiPlanar: + case COLOR_FormatYUV444Flexible: + case COLOR_FormatYUV444Interleaved: + clientBitDepth = 8; + break; + default: + // no-op; used with optional + break; + + } + // conversion fails if client bit-depth and the component bit-depth differs + if ((clientBitDepth) && (bitDepth != clientBitDepth.value())) { + ALOGD("Bit depth of client: %d and component: %d differs", + *clientBitDepth, bitDepth); + mInitCheck = BAD_VALUE; + return; + } C2PlaneInfo yPlane = layout.planes[C2PlanarLayout::PLANE_Y]; C2PlaneInfo uPlane = layout.planes[C2PlanarLayout::PLANE_U]; C2PlaneInfo vPlane = layout.planes[C2PlanarLayout::PLANE_V]; diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp index 3f9a40dd94551031844b88da0c15dc1ca7fb9b79..1c362aece203d7fd66091850e9694a4972bb7ade 100644 --- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp +++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp @@ -309,8 +309,13 @@ void addSupportedColorFormats( if (trait.name.find("android") != std::string::npos) { addDefaultColorFormat(COLOR_FormatSurface); } - for (int32_t colorFormat : supportedColorFormats) { - caps->addColorFormat(colorFormat); + + static const int kVendorSdkVersion = ::android::base::GetIntProperty( + "ro.vendor.build.version.sdk", android_get_device_api_level()); + if (kVendorSdkVersion >= __ANDROID_API_T__) { + for (int32_t colorFormat : supportedColorFormats) { + caps->addColorFormat(colorFormat); + } } } } diff --git a/media/codec2/sfplugin/tests/Android.bp b/media/codec2/sfplugin/tests/Android.bp index 92f37540aa1a45e7615898ba4692efd0feb4e2a4..246e5631dfd47ca4dfe14d2813886f08f1d884b9 100644 --- a/media/codec2/sfplugin/tests/Android.bp +++ b/media/codec2/sfplugin/tests/Android.bp @@ -23,8 +23,8 @@ cc_test { "libcodec2-internal-defaults", ], - include_dirs: [ - "frameworks/av/media/codec2/sfplugin", + header_libs: [ + "libsfplugin_ccodec_internal_headers", ], shared_libs: [ @@ -60,13 +60,10 @@ cc_test { "MediaCodec_sanity_test.cpp", ], - include_dirs: [ - "frameworks/av/media/codec2/sfplugin", - ], - header_libs: [ "libmediadrm_headers", "libmediametrics_headers", + "libsfplugin_ccodec_internal_headers", ], shared_libs: [ diff --git a/media/codec2/sfplugin/utils/Android.bp b/media/codec2/sfplugin/utils/Android.bp index 5c2f11078819d3c353fb28a8c02538ceb12affcc..54a6fb16edf0d235958a8921a657faa60cb559d4 100644 --- a/media/codec2/sfplugin/utils/Android.bp +++ b/media/codec2/sfplugin/utils/Android.bp @@ -7,10 +7,23 @@ package { default_applicable_licenses: ["frameworks_av_license"], } +cc_library_headers { + name: "libsfplugin_ccodec_utils_headers", + vendor_available: true, + min_sdk_version: "29", + apex_available: [ "//apex_available:platform", "com.android.media.swcodec", ], + + export_include_dirs: [ + ".", + ], +} + cc_library { name: "libsfplugin_ccodec_utils", vendor_available: true, min_sdk_version: "29", + apex_available: [ "//apex_available:platform", "com.android.media.swcodec", ], + double_loadable: true, srcs: [ diff --git a/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp b/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp index 7fc4c2735f8875461ce5d01fcac8917f9e2482e5..807841e4c6dbd32f84ad5a0d1a7b40c1b593d5b7 100644 --- a/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp +++ b/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp @@ -121,7 +121,10 @@ static status_t _ImageCopy(View &view, const MediaImage2 *img, ImagePixel *imgBa } // namespace status_t ImageCopy(uint8_t *imgBase, const MediaImage2 *img, const C2GraphicView &view) { - if (view.crop().width != img->mWidth || view.crop().height != img->mHeight) { + if (img == nullptr + || imgBase == nullptr + || view.crop().width != img->mWidth + || view.crop().height != img->mHeight) { return BAD_VALUE; } const uint8_t* src_y = view.data()[0]; @@ -203,7 +206,10 @@ status_t ImageCopy(uint8_t *imgBase, const MediaImage2 *img, const C2GraphicView } status_t ImageCopy(C2GraphicView &view, const uint8_t *imgBase, const MediaImage2 *img) { - if (view.crop().width != img->mWidth || view.crop().height != img->mHeight) { + if (img == nullptr + || imgBase == nullptr + || view.crop().width != img->mWidth + || view.crop().height != img->mHeight) { return BAD_VALUE; } const uint8_t* src_y = imgBase + img->mPlane[0].mOffset; @@ -532,12 +538,14 @@ MediaImage2 CreateYUV420SemiPlanarMediaImage2( // Matrix coefficient to convert RGB to Planar YUV data. // Each sub-array represents the 3X3 coeff used with R, G and B static const int16_t bt601Matrix[2][3][3] = { - { { 76, 150, 29 }, { -43, -85, 128 }, { 128, -107, -21 } }, /* RANGE_FULL */ + { { 77, 150, 29 }, { -43, -85, 128 }, { 128, -107, -21 } }, /* RANGE_FULL */ { { 66, 129, 25 }, { -38, -74, 112 }, { 112, -94, -18 } }, /* RANGE_LIMITED */ }; static const int16_t bt709Matrix[2][3][3] = { - { { 54, 183, 18 }, { -29, -99, 128 }, { 128, -116, -12 } }, /* RANGE_FULL */ + // TRICKY: 18 is adjusted to 19 so that sum of row 1 is 256 + { { 54, 183, 19 }, { -29, -99, 128 }, { 128, -116, -12 } }, /* RANGE_FULL */ + // TRICKY: -87 is adjusted to -86 so that sum of row 2 is 0 { { 47, 157, 16 }, { -26, -86, 112 }, { 112, -102, -10 } }, /* RANGE_LIMITED */ }; diff --git a/media/codec2/tests/Android.bp b/media/codec2/tests/Android.bp index 68db7b2b91ae3d6b08488032ca6c80556dbd6d67..2217235dd9967128a35ba12f9565ac577288baea 100644 --- a/media/codec2/tests/Android.bp +++ b/media/codec2/tests/Android.bp @@ -15,12 +15,9 @@ cc_test { "C2Param_test.cpp", ], - include_dirs: [ - "frameworks/av/media/codec2/vndk/include", - ], - header_libs: [ "libcodec2_headers", + "libcodec2_vndk_headers", ], // param tests must not depend on any codec2 libraries as all params should be templated @@ -49,9 +46,6 @@ cc_test { "vndk/C2BufferTest.cpp", ], - include_dirs: [ - ], - shared_libs: [ "libcodec2", "libcodec2_vndk", diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp index 27cd1f817b558363c8b1c7ef4923ca83a704aa17..1d8aea346d4ce1e586155600ef4d30e701077d49 100644 --- a/media/codec2/vndk/Android.bp +++ b/media/codec2/vndk/Android.bp @@ -18,6 +18,25 @@ cc_library_headers { vendor_available: true, min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + ], + +} + +cc_library_headers { + name: "libcodec2_vndk_headers", + vendor_available: true, + min_sdk_version: "29", + + export_include_dirs: [ + "include", + ], + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + ], } // !!!DO NOT DEPEND ON THIS SHARED LIBRARY DIRECTLY!!! @@ -28,6 +47,11 @@ cc_library { min_sdk_version: "29", // TODO: b/147147883 double_loadable: true, + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + ], + srcs: [ "C2AllocatorBlob.cpp", @@ -54,6 +78,7 @@ cc_library { export_shared_lib_headers: [ "libbase", + "libdmabufheap", "android.hardware.media.bufferpool@2.0", ], @@ -76,7 +101,6 @@ cc_library { "libdmabufheap", "libfmq", "libgralloctypes", - "libhardware", "libhidlbase", "libion", "liblog", @@ -125,7 +149,6 @@ cc_defaults { shared_libs: [ "libui", "libdl", - "libhardware", "libvndksupport", "libprocessgroup", ], diff --git a/media/codec2/vndk/C2AllocatorIon.cpp b/media/codec2/vndk/C2AllocatorIon.cpp index 7b593eeeeb0cc4b55221e84d5ae95ffcc5b2f597..a6a733ebd21859566a710b935e0e4cc31e49a7ff 100644 --- a/media/codec2/vndk/C2AllocatorIon.cpp +++ b/media/codec2/vndk/C2AllocatorIon.cpp @@ -31,6 +31,7 @@ #include #include +#include namespace android { @@ -180,7 +181,7 @@ public: c2_status_t map(size_t offset, size_t size, C2MemoryUsage usage, C2Fence *fence, void **addr) { (void)fence; // TODO: wait for fence *addr = nullptr; - if (!mMappings.empty()) { + if (!mMappings.lock()->empty()) { ALOGV("multiple map"); // TODO: technically we should return DUPLICATE here, but our block views don't // actually unmap, so we end up remapping an ion buffer multiple times. @@ -207,47 +208,44 @@ public: c2_status_t err = mapInternal(mapSize, mapOffset, alignmentBytes, prot, flags, &(map.addr), addr); if (map.addr) { - std::lock_guard guard(mMutexMappings); - mMappings.push_back(map); + mMappings.lock()->push_back(map); } return err; } c2_status_t unmap(void *addr, size_t size, C2Fence *fence) { - if (mMappings.empty()) { + Mutexed>::Locked mappings(mMappings); + if (mappings->empty()) { ALOGD("tried to unmap unmapped buffer"); return C2_NOT_FOUND; } - { // Scope for the lock_guard of mMutexMappings. - std::lock_guard guard(mMutexMappings); - for (auto it = mMappings.begin(); it != mMappings.end(); ++it) { - if (addr != (uint8_t *)it->addr + it->alignmentBytes || - size + it->alignmentBytes != it->size) { - continue; - } - int err = munmap(it->addr, it->size); - if (err != 0) { - ALOGD("munmap failed"); - return c2_map_errno(errno); - } - if (fence) { - *fence = C2Fence(); // not using fences - } - (void)mMappings.erase(it); - ALOGV("successfully unmapped: addr=%p size=%zu fd=%d", addr, size, - mHandle.bufferFd()); - return C2_OK; + for (auto it = mappings->begin(); it != mappings->end(); ++it) { + if (addr != (uint8_t *)it->addr + it->alignmentBytes || + size + it->alignmentBytes != it->size) { + continue; } + int err = munmap(it->addr, it->size); + if (err != 0) { + ALOGD("munmap failed"); + return c2_map_errno(errno); + } + if (fence) { + *fence = C2Fence(); // not using fences + } + (void)mappings->erase(it); + ALOGV("successfully unmapped: addr=%p size=%zu fd=%d", addr, size, + mHandle.bufferFd()); + return C2_OK; } ALOGD("unmap failed to find specified map"); return C2_BAD_VALUE; } virtual ~Impl() { - if (!mMappings.empty()) { + Mutexed>::Locked mappings(mMappings); + if (!mappings->empty()) { ALOGD("Dangling mappings!"); - std::lock_guard guard(mMutexMappings); - for (const Mapping &map : mMappings) { + for (const Mapping &map : *mappings) { (void)munmap(map.addr, map.size); } } @@ -325,8 +323,7 @@ protected: size_t alignmentBytes; size_t size; }; - std::list mMappings; - std::mutex mMutexMappings; + Mutexed> mMappings; }; class C2AllocationIon::ImplV2 : public C2AllocationIon::Impl { diff --git a/media/codec2/vndk/C2DmaBufAllocator.cpp b/media/codec2/vndk/C2DmaBufAllocator.cpp index 1aa3d69c0c0a4dcfa96c79145f3a9664a0d673d5..c470171935c1569324248618615700eb528b0193 100644 --- a/media/codec2/vndk/C2DmaBufAllocator.cpp +++ b/media/codec2/vndk/C2DmaBufAllocator.cpp @@ -31,6 +31,7 @@ #include #include +#include namespace android { @@ -161,7 +162,7 @@ class C2DmaBufAllocation : public C2LinearAllocation { size_t alignmentBytes; size_t size; }; - std::list mMappings; + Mutexed> mMappings; // TODO: we could make this encapsulate shared_ptr and copiable C2_DO_NOT_COPY(C2DmaBufAllocation); @@ -171,7 +172,7 @@ c2_status_t C2DmaBufAllocation::map(size_t offset, size_t size, C2MemoryUsage us void** addr) { (void)fence; // TODO: wait for fence *addr = nullptr; - if (!mMappings.empty()) { + if (!mMappings.lock()->empty()) { ALOGV("multiple map"); // TODO: technically we should return DUPLICATE here, but our block views // don't actually unmap, so we end up remapping the buffer multiple times. @@ -199,17 +200,18 @@ c2_status_t C2DmaBufAllocation::map(size_t offset, size_t size, C2MemoryUsage us c2_status_t err = mapInternal(mapSize, mapOffset, alignmentBytes, prot, flags, &(map.addr), addr); if (map.addr) { - mMappings.push_back(map); + mMappings.lock()->push_back(map); } return err; } c2_status_t C2DmaBufAllocation::unmap(void* addr, size_t size, C2Fence* fence) { - if (mMappings.empty()) { + Mutexed>::Locked mappings(mMappings); + if (mappings->empty()) { ALOGD("tried to unmap unmapped buffer"); return C2_NOT_FOUND; } - for (auto it = mMappings.begin(); it != mMappings.end(); ++it) { + for (auto it = mappings->begin(); it != mappings->end(); ++it) { if (addr != (uint8_t*)it->addr + it->alignmentBytes || size + it->alignmentBytes != it->size) { continue; @@ -222,7 +224,7 @@ c2_status_t C2DmaBufAllocation::unmap(void* addr, size_t size, C2Fence* fence) { if (fence) { *fence = C2Fence(); // not using fences } - (void)mMappings.erase(it); + (void)mappings->erase(it); ALOGV("successfully unmapped: %d", mHandle.bufferFd()); return C2_OK; } @@ -253,9 +255,10 @@ const C2Handle* C2DmaBufAllocation::handle() const { } C2DmaBufAllocation::~C2DmaBufAllocation() { - if (!mMappings.empty()) { + Mutexed>::Locked mappings(mMappings); + if (!mappings->empty()) { ALOGD("Dangling mappings!"); - for (const Mapping& map : mMappings) { + for (const Mapping& map : *mappings) { int err = munmap(map.addr, map.size); if (err) ALOGD("munmap failed"); } diff --git a/media/codec2/vndk/C2Fence.cpp b/media/codec2/vndk/C2Fence.cpp index c1fb956a9f6e30fc287cd96061210d90460cdb8d..aa908a84af3aff6b90966b126abf851080765e8f 100644 --- a/media/codec2/vndk/C2Fence.cpp +++ b/media/codec2/vndk/C2Fence.cpp @@ -148,7 +148,7 @@ public: } virtual native_handle_t *createNativeHandle() const { - ALOG_ASSERT(false, "Cannot create native handle from surface fence"); + ALOGD("Cannot create native handle from surface fence"); return nullptr; } @@ -287,7 +287,8 @@ C2Fence _C2FenceFactory::CreateFromNativeHandle(const native_handle_t* handle) { p = SyncFenceImpl::CreateFromNativeHandle(handle); break; default: - ALOGW("Unsupported fence type %d", type); + ALOGD("Unsupported fence type %d", type); + // return a null-fence in this case break; } if (p && !p->valid()) { diff --git a/media/codec2/vndk/include/C2BqBufferPriv.h b/media/codec2/vndk/include/C2BqBufferPriv.h index bec978a3716d1e15a7cc2d8f1a66ca8f8c40c033..29aad5ece41a7f3b25e905cee0725eb1581028c7 100644 --- a/media/codec2/vndk/include/C2BqBufferPriv.h +++ b/media/codec2/vndk/include/C2BqBufferPriv.h @@ -129,8 +129,9 @@ public: // Create a local BlockPoolData. C2BufferQueueBlockPoolData( uint32_t generation, uint64_t bqId, int32_t bqSlot, + const std::shared_ptr &owner, const android::sp& producer, - std::shared_ptr, int noUse); + std::shared_ptr); virtual ~C2BufferQueueBlockPoolData() override; diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp index 63b0f39ee3d372ed4b219d5ae41be82bbec37374..e67e42fac6b2ca3c37ef902fbb8aa0ba669c4038 100644 --- a/media/codec2/vndk/platform/C2BqBuffer.cpp +++ b/media/codec2/vndk/platform/C2BqBuffer.cpp @@ -542,7 +542,7 @@ private: std::make_shared( slotBuffer->getGenerationNumber(), mProducerId, slot, - mProducer, mSyncMem, 0); + mIgbpValidityToken, mProducer, mSyncMem); mPoolDatas[slot] = poolData; *block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData); return C2_OK; @@ -572,10 +572,11 @@ public: Impl(const std::shared_ptr &allocator) : mInit(C2_OK), mProducerId(0), mGeneration(0), mConsumerUsage(0), mDqFailure(0), mLastDqTs(0), - mLastDqLogTs(0), mAllocator(allocator) { + mLastDqLogTs(0), mAllocator(allocator), mIgbpValidityToken(std::make_shared(0)) { } ~Impl() { + mIgbpValidityToken.reset(); for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) { mBuffers[i].clear(); } @@ -618,7 +619,7 @@ public: } std::shared_ptr poolData = std::make_shared( - 0, (uint64_t)0, ~0, nullptr, nullptr, 0); + 0, (uint64_t)0, ~0, nullptr, nullptr, nullptr); *block = _C2BlockFactory::CreateGraphicBlock(alloc, poolData); ALOGV("allocated a buffer successfully"); @@ -694,8 +695,7 @@ public: mProducer = nullptr; mProducerId = 0; mGeneration = 0; - ALOGW("invalid producer producer(%d), generation(%d)", - (bool)producer, bqInformation); + ALOGD("configuring null producer: igbp_information(%d)", bqInformation); } oldMem = mSyncMem; // preven destruction while locked. mSyncMem = c2SyncMem; @@ -720,6 +720,10 @@ public: } } } + } else { + // old buffers should not be cancelled since the associated IGBP + // is no longer valid. + mIgbpValidityToken = std::make_shared(0); } for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) { mBuffers[i] = buffers[i]; @@ -761,6 +765,20 @@ private: std::weak_ptr mPoolDatas[NUM_BUFFER_SLOTS]; std::shared_ptr mSyncMem; + + // IGBP invalidation notification token. + // The buffers(C2BufferQueueBlockPoolData) has the reference to the IGBP where + // they belong in order to call IGBP::cancelBuffer() when they are of no use. + // + // In certain cases, IGBP is no longer used by this class(actually MediaCodec) + // any more and the situation needs to be addressed quickly. In order to + // achieve those, std::shared_ptr<> is used as a token for quick IGBP invalidation + // notification from the buffers. + // + // The buffer side will have the reference of the token as std::weak_ptr<>. + // if the token has been expired, the buffers will not call IGBP::cancelBuffer() + // when they are no longer used. + std::shared_ptr mIgbpValidityToken; }; C2BufferQueueBlockPoolData::C2BufferQueueBlockPoolData( @@ -776,14 +794,14 @@ C2BufferQueueBlockPoolData::C2BufferQueueBlockPoolData( C2BufferQueueBlockPoolData::C2BufferQueueBlockPoolData( uint32_t generation, uint64_t bqId, int32_t bqSlot, + const std::shared_ptr& owner, const android::sp& producer, - std::shared_ptr syncMem, int noUse) : + std::shared_ptr syncMem) : mLocal(true), mHeld(true), mGeneration(generation), mBqId(bqId), mBqSlot(bqSlot), mCurrentGeneration(generation), mCurrentBqId(bqId), mTransfer(false), mAttach(false), mDisplay(false), - mIgbp(producer), mSyncMem(syncMem) { - (void)noUse; + mOwner(owner), mIgbp(producer), mSyncMem(syncMem) { } C2BufferQueueBlockPoolData::~C2BufferQueueBlockPoolData() { @@ -792,7 +810,7 @@ C2BufferQueueBlockPoolData::~C2BufferQueueBlockPoolData() { } if (mLocal) { - if (mGeneration == mCurrentGeneration && mBqId == mCurrentBqId) { + if (mGeneration == mCurrentGeneration && mBqId == mCurrentBqId && !mOwner.expired()) { C2SyncVariables *syncVar = mSyncMem ? mSyncMem->mem() : nullptr; if (syncVar) { syncVar->lock(); diff --git a/media/codecs/amrnb/common/Android.bp b/media/codecs/amrnb/common/Android.bp index bae65f3b2c8ed25f3fb194f8ea2af9b6c14a1502..0bc6ed266a65e28948b83273578d6c05d9e63944 100644 --- a/media/codecs/amrnb/common/Android.bp +++ b/media/codecs/amrnb/common/Android.bp @@ -22,6 +22,10 @@ cc_library { vendor_available: true, host_supported: true, min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + ], srcs: [ "src/add.cpp", diff --git a/media/codecs/amrnb/dec/Android.bp b/media/codecs/amrnb/dec/Android.bp index 1083b8293bbc0079d6402896bd07a1b61366a66b..70741d25aac213fe199a923fbd6a5b34987a6886 100644 --- a/media/codecs/amrnb/dec/Android.bp +++ b/media/codecs/amrnb/dec/Android.bp @@ -35,6 +35,10 @@ cc_library_static { vendor_available: true, host_supported: true, min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + ], srcs: [ "src/a_refl.cpp", diff --git a/media/codecs/amrnb/enc/Android.bp b/media/codecs/amrnb/enc/Android.bp index 9e947e957262f0d6ef4f36ade063c517c783efaa..3c6566e8c87724b5935e663ee5f5ef311c432f9f 100644 --- a/media/codecs/amrnb/enc/Android.bp +++ b/media/codecs/amrnb/enc/Android.bp @@ -34,6 +34,10 @@ cc_library_static { name: "libstagefright_amrnbenc", vendor_available: true, min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + ], srcs: [ "src/amrencode.cpp", diff --git a/media/codecs/amrwb/dec/Android.bp b/media/codecs/amrwb/dec/Android.bp index 228ea800b1f146f444d05e0ffa1f5724def83595..f16b0feb415820416ddc3e8299c2e0d5603bb844 100644 --- a/media/codecs/amrwb/dec/Android.bp +++ b/media/codecs/amrwb/dec/Android.bp @@ -35,6 +35,10 @@ cc_library_static { vendor_available: true, host_supported: true, min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + ], srcs: [ "src/agc2_amr_wb.cpp", diff --git a/media/codecs/amrwb/enc/Android.bp b/media/codecs/amrwb/enc/Android.bp index cc72eb76f819a6a78e757b94bb7fbb8a4b02619a..87801362de6d2a730aadb1346914d54092519749 100644 --- a/media/codecs/amrwb/enc/Android.bp +++ b/media/codecs/amrwb/enc/Android.bp @@ -21,6 +21,10 @@ cc_library_static { name: "libstagefright_amrwbenc", vendor_available: true, min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + ], srcs: [ "src/autocorr.c", @@ -139,11 +143,6 @@ cc_library_static { }, }, - include_dirs: [ - "frameworks/av/include", - "frameworks/av/media/libstagefright/include", - ], - local_include_dirs: ["src"], export_include_dirs: ["inc"], diff --git a/media/codecs/mp3dec/Android.bp b/media/codecs/mp3dec/Android.bp index 015b8b64ccfe781fe6e6da33e1675eb7aa8b9e55..6659ea54f3c58d2a2e17292275dab1c5cd21bcb9 100644 --- a/media/codecs/mp3dec/Android.bp +++ b/media/codecs/mp3dec/Android.bp @@ -47,6 +47,10 @@ cc_library_static { name: "libstagefright_mp3dec", vendor_available: true, min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + ], host_supported:true, srcs: [ @@ -108,8 +112,6 @@ cc_library_static { cfi: true, }, - include_dirs: ["frameworks/av/media/libstagefright/include"], - header_libs: ["libstagefright_mp3dec_headers"], export_header_lib_headers: ["libstagefright_mp3dec_headers"], diff --git a/media/codecs/mp3dec/src/pvmp3_stereo_proc.cpp b/media/codecs/mp3dec/src/pvmp3_stereo_proc.cpp index c04f7f3c72ee3e23718902b42e9d477321323a39..b3ecc7701e1a330615c7990e911f030ac63aa4d1 100644 --- a/media/codecs/mp3dec/src/pvmp3_stereo_proc.cpp +++ b/media/codecs/mp3dec/src/pvmp3_stereo_proc.cpp @@ -219,6 +219,7 @@ void pvmp3_st_mid_side(int32 xr[SUBBANDS_NUMBER*FILTERBANK_BANDS], ; FUNCTION CODE ----------------------------------------------------------------------------*/ +// deliberately plays near overflow points of int32 #if __has_attribute(no_sanitize) __attribute__((no_sanitize("integer"))) #endif diff --git a/media/extractors/Android.bp b/media/extractors/Android.bp index 7513cb15bfb0c7bfde832df8fb0403eec10a913a..f654ecd37ab834ea72d67460b5c620bc4e3266b3 100644 --- a/media/extractors/Android.bp +++ b/media/extractors/Android.bp @@ -24,21 +24,19 @@ package { cc_defaults { name: "extractor-defaults", - include_dirs: [ - "frameworks/av/media/libstagefright/include", - ], - shared_libs: [ "liblog", ], - // extractors are supposed to work on Q(29) + // extractors are expected to run on Q(29) min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.media", + ], relative_install_path: "extractors", - compile_multilib: "first", - cflags: [ "-Werror", "-Wall", diff --git a/media/extractors/TEST_MAPPING b/media/extractors/TEST_MAPPING index 4984b8fccdeb617b086fcd626c2c203694ebb8d4..a7c2cfe2933c2af7d406c9a7aa271761c0471bb7 100644 --- a/media/extractors/TEST_MAPPING +++ b/media/extractors/TEST_MAPPING @@ -1,6 +1,9 @@ { "presubmit": [ + { + "name": "CtsMediaTranscodingTestCases" + } // TODO(b/153661591) enable test once the bug is fixed // This tests the extractor path // { diff --git a/media/extractors/aac/Android.bp b/media/extractors/aac/Android.bp index 7bf3a1397d66fc0f747a01777c68a9446adcb7d5..a92642265679d93ab00ff5eff20a57112d03c430 100644 --- a/media/extractors/aac/Android.bp +++ b/media/extractors/aac/Android.bp @@ -21,6 +21,10 @@ cc_library { srcs: ["AACExtractor.cpp"], + export_include_dirs: [ + "include", + ], + static_libs: [ "libstagefright_foundation", "libstagefright_metadatautils", diff --git a/media/extractors/aac/AACExtractor.h b/media/extractors/aac/include/AACExtractor.h similarity index 100% rename from media/extractors/aac/AACExtractor.h rename to media/extractors/aac/include/AACExtractor.h diff --git a/media/extractors/amr/Android.bp b/media/extractors/amr/Android.bp index 712360d10f9d4a6ff7d7a929f733d8957a75a8a8..121b7a3bca1f78dbf91b0da2c4eb47e94c48bb8c 100644 --- a/media/extractors/amr/Android.bp +++ b/media/extractors/amr/Android.bp @@ -21,6 +21,10 @@ cc_library { srcs: ["AMRExtractor.cpp"], + export_include_dirs: [ + "include", + ], + static_libs: [ "libstagefright_foundation", ], diff --git a/media/extractors/amr/AMRExtractor.h b/media/extractors/amr/include/AMRExtractor.h similarity index 100% rename from media/extractors/amr/AMRExtractor.h rename to media/extractors/amr/include/AMRExtractor.h diff --git a/media/extractors/flac/Android.bp b/media/extractors/flac/Android.bp index f6ce9695433b5a093d6ae18eb22720fb05c7f81f..834f4adc379619ad99b2f7a5c619afd560358bad 100644 --- a/media/extractors/flac/Android.bp +++ b/media/extractors/flac/Android.bp @@ -23,8 +23,8 @@ cc_library { srcs: ["FLACExtractor.cpp"], - include_dirs: [ - "external/flac/include", + export_include_dirs: [ + "include", ], shared_libs: [ diff --git a/media/extractors/flac/FLACExtractor.h b/media/extractors/flac/include/FLACExtractor.h similarity index 100% rename from media/extractors/flac/FLACExtractor.h rename to media/extractors/flac/include/FLACExtractor.h diff --git a/media/extractors/fuzzers/Android.bp b/media/extractors/fuzzers/Android.bp index 0e54b58d4a9ea86709a48bef32a2565d9583d24a..490e195bcf1eeca8165c4cf6dd84164b835bee99 100644 --- a/media/extractors/fuzzers/Android.bp +++ b/media/extractors/fuzzers/Android.bp @@ -80,11 +80,6 @@ cc_defaults { defaults: ["extractor-fuzzer-defaults"], host_supported: true, - include_dirs: [ - "frameworks/av/media/extractors/mpeg2", - "frameworks/av/media/libstagefright", - ], - static_libs: [ "libstagefright_foundation_without_imemory", "libstagefright_mpeg2support", @@ -124,14 +119,6 @@ cc_fuzz { "mp4_extractor_fuzzer.cpp", ], - include_dirs: [ - "frameworks/av/media/extractors/mp4", - ], - - header_libs: [ - "libaudioclient_headers", - ], - static_libs: [ "libstagefright_id3", "libstagefright_esds", @@ -150,10 +137,6 @@ cc_fuzz { "wav_extractor_fuzzer.cpp", ], - include_dirs: [ - "frameworks/av/media/extractors/wav", - ], - static_libs: [ "libfifo", "libwavextractor", @@ -173,10 +156,6 @@ cc_fuzz { "amr_extractor_fuzzer.cpp", ], - include_dirs: [ - "frameworks/av/media/extractors/amr", - ], - static_libs: [ "libamrextractor", ], @@ -193,10 +172,6 @@ cc_fuzz { "mkv_extractor_fuzzer.cpp", ], - include_dirs: [ - "frameworks/av/media/extractors/mkv", - ], - static_libs: [ "libwebm", "libstagefright_flacdec", @@ -217,9 +192,6 @@ cc_fuzz { "ogg_extractor_fuzzer.cpp", ], - include_dirs: [ - "frameworks/av/media/extractors/ogg", - ], static_libs: [ "libstagefright_metadatautils", @@ -265,10 +237,6 @@ cc_fuzz { "mp3_extractor_fuzzer.cpp", ], - include_dirs: [ - "frameworks/av/media/extractors/mp3", - ], - static_libs: [ "libfifo", "libmp3extractor", @@ -285,10 +253,6 @@ cc_fuzz { "aac_extractor_fuzzer.cpp", ], - include_dirs: [ - "frameworks/av/media/extractors/aac", - ], - static_libs: [ "libaacextractor", "libstagefright_metadatautils", @@ -304,10 +268,6 @@ cc_fuzz { "flac_extractor_fuzzer.cpp", ], - include_dirs: [ - "frameworks/av/media/extractors/flac", - ], - static_libs: [ "libstagefright_metadatautils", "libFLAC", @@ -329,10 +289,6 @@ cc_fuzz { "midi_extractor_fuzzer.cpp", ], - include_dirs: [ - "frameworks/av/media/extractors/midi", - ], - static_libs: [ "libsonivox", "libmedia_midiiowrapper", diff --git a/media/extractors/midi/Android.bp b/media/extractors/midi/Android.bp index 08a6fa04e224ae8df6c9a620b46f517cfa10d497..feabf9e0fc39906d16dd969c13386172fd7c941d 100644 --- a/media/extractors/midi/Android.bp +++ b/media/extractors/midi/Android.bp @@ -23,6 +23,10 @@ cc_library { srcs: ["MidiExtractor.cpp"], + export_include_dirs: [ + "include", + ], + header_libs: [ "libmedia_datasource_headers", ], diff --git a/media/extractors/midi/MidiExtractor.h b/media/extractors/midi/include/MidiExtractor.h similarity index 100% rename from media/extractors/midi/MidiExtractor.h rename to media/extractors/midi/include/MidiExtractor.h diff --git a/media/extractors/mkv/Android.bp b/media/extractors/mkv/Android.bp index 54c5b271eb4b0bb97c73998cde2fd243da3b54b8..98ce3050f96577f38e64bf9285e8efa548dedfba 100644 --- a/media/extractors/mkv/Android.bp +++ b/media/extractors/mkv/Android.bp @@ -21,10 +21,8 @@ cc_library { srcs: ["MatroskaExtractor.cpp"], - include_dirs: [ - "external/flac/include", - "external/libvpx/libwebm", - "frameworks/av/media/libstagefright/flac/dec", + export_include_dirs: [ + "include", ], shared_libs: [ diff --git a/media/extractors/mkv/MatroskaExtractor.h b/media/extractors/mkv/include/MatroskaExtractor.h similarity index 100% rename from media/extractors/mkv/MatroskaExtractor.h rename to media/extractors/mkv/include/MatroskaExtractor.h diff --git a/media/extractors/mp3/Android.bp b/media/extractors/mp3/Android.bp index 75b9b7b368f85df921df3881db714ff6c9233376..396a13a3e7ab07bdeb74439661def85d15f96888 100644 --- a/media/extractors/mp3/Android.bp +++ b/media/extractors/mp3/Android.bp @@ -16,6 +16,10 @@ cc_library { "XINGSeeker.cpp", ], + export_include_dirs: [ + "include", + ], + static_libs: [ "libutils", "libstagefright_id3", diff --git a/media/extractors/mp3/MP3Extractor.h b/media/extractors/mp3/include/MP3Extractor.h similarity index 100% rename from media/extractors/mp3/MP3Extractor.h rename to media/extractors/mp3/include/MP3Extractor.h diff --git a/media/extractors/mp3/MP3Seeker.h b/media/extractors/mp3/include/MP3Seeker.h similarity index 100% rename from media/extractors/mp3/MP3Seeker.h rename to media/extractors/mp3/include/MP3Seeker.h diff --git a/media/extractors/mp3/VBRISeeker.h b/media/extractors/mp3/include/VBRISeeker.h similarity index 100% rename from media/extractors/mp3/VBRISeeker.h rename to media/extractors/mp3/include/VBRISeeker.h diff --git a/media/extractors/mp3/XINGSeeker.h b/media/extractors/mp3/include/XINGSeeker.h similarity index 100% rename from media/extractors/mp3/XINGSeeker.h rename to media/extractors/mp3/include/XINGSeeker.h diff --git a/media/extractors/mp4/Android.bp b/media/extractors/mp4/Android.bp index 7fa6bfd423ed16261b8aff4ff24034fdc4a19918..540d75ddd56ef813c1bcc165bb1da88858bc4344 100644 --- a/media/extractors/mp4/Android.bp +++ b/media/extractors/mp4/Android.bp @@ -15,6 +15,15 @@ license { ], } +cc_library_headers { + name: "libmp4extractor_headers", + host_supported: true, + + export_include_dirs: [ + "include", + ], +} + cc_library { name: "libmp4extractor", defaults: ["extractor-defaults"], @@ -27,6 +36,10 @@ cc_library { "SampleTable.cpp", ], + export_include_dirs: [ + "include", + ], + static_libs: [ "libstagefright_esds", "libstagefright_foundation", diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp index 5ca874e24d0983dcb772d0378df65bb11ad134c0..5a039928e4bf9527857f389c65af8ef7fc40c516 100644 --- a/media/extractors/mp4/MPEG4Extractor.cpp +++ b/media/extractors/mp4/MPEG4Extractor.cpp @@ -4786,7 +4786,7 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( if (len2 == 0) { return ERROR_MALFORMED; } - if (offset >= csd_size || csd[offset] != 0x01) { + if (offset + len1 > csd_size || csd[offset] != 0x01) { return ERROR_MALFORMED; } diff --git a/media/extractors/mp4/AC4Parser.h b/media/extractors/mp4/include/AC4Parser.h similarity index 100% rename from media/extractors/mp4/AC4Parser.h rename to media/extractors/mp4/include/AC4Parser.h diff --git a/media/extractors/mp4/ItemTable.h b/media/extractors/mp4/include/ItemTable.h similarity index 100% rename from media/extractors/mp4/ItemTable.h rename to media/extractors/mp4/include/ItemTable.h diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/include/MPEG4Extractor.h similarity index 100% rename from media/extractors/mp4/MPEG4Extractor.h rename to media/extractors/mp4/include/MPEG4Extractor.h diff --git a/media/extractors/mp4/SampleIterator.h b/media/extractors/mp4/include/SampleIterator.h similarity index 100% rename from media/extractors/mp4/SampleIterator.h rename to media/extractors/mp4/include/SampleIterator.h diff --git a/media/extractors/mp4/SampleTable.h b/media/extractors/mp4/include/SampleTable.h similarity index 100% rename from media/extractors/mp4/SampleTable.h rename to media/extractors/mp4/include/SampleTable.h diff --git a/media/extractors/mpeg2/Android.bp b/media/extractors/mpeg2/Android.bp index 4f0796e53176219e1f6edc3ad86f7b1770e34df6..aa59a0ce870e7ac623020875ee5f2b5cdfc7a0ef 100644 --- a/media/extractors/mpeg2/Android.bp +++ b/media/extractors/mpeg2/Android.bp @@ -38,6 +38,10 @@ cc_library { "MPEG2TSExtractor.cpp", ], + export_include_dirs: [ + "include", + ], + shared_libs: [ "libbase", "libcgrouprc#29", diff --git a/media/extractors/mpeg2/MPEG2PSExtractor.cpp b/media/extractors/mpeg2/MPEG2PSExtractor.cpp index d431b05d22c80d6834f3678376a6cbef3570b36b..afd28efe7a52bc8a3797abe04dab68061b4f73f4 100644 --- a/media/extractors/mpeg2/MPEG2PSExtractor.cpp +++ b/media/extractors/mpeg2/MPEG2PSExtractor.cpp @@ -20,9 +20,6 @@ #include "MPEG2PSExtractor.h" -#include -#include - #include #include #include @@ -33,6 +30,8 @@ #include #include #include +#include +#include #include #include diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.cpp b/media/extractors/mpeg2/MPEG2TSExtractor.cpp index 2e68809b6f2c9becc5c531aa30d74c16fd53b32d..9a3cd9227756394605418e3bb86444d3524acc11 100644 --- a/media/extractors/mpeg2/MPEG2TSExtractor.cpp +++ b/media/extractors/mpeg2/MPEG2TSExtractor.cpp @@ -35,10 +35,9 @@ #include #include #include +#include #include -#include - #include #include diff --git a/media/extractors/mpeg2/MPEG2PSExtractor.h b/media/extractors/mpeg2/include/MPEG2PSExtractor.h similarity index 100% rename from media/extractors/mpeg2/MPEG2PSExtractor.h rename to media/extractors/mpeg2/include/MPEG2PSExtractor.h diff --git a/media/extractors/mpeg2/MPEG2TSExtractor.h b/media/extractors/mpeg2/include/MPEG2TSExtractor.h similarity index 99% rename from media/extractors/mpeg2/MPEG2TSExtractor.h rename to media/extractors/mpeg2/include/MPEG2TSExtractor.h index fd77b081b4058c10b9ebd05d088c52eaa6d2437b..0e3e484b6de272ec574a2d5f1e4988181d4129f7 100644 --- a/media/extractors/mpeg2/MPEG2TSExtractor.h +++ b/media/extractors/mpeg2/include/MPEG2TSExtractor.h @@ -23,12 +23,11 @@ #include #include #include +#include #include #include #include -#include - namespace android { struct AMessage; diff --git a/media/extractors/ogg/Android.bp b/media/extractors/ogg/Android.bp index d7540c4b179cd96e54b58c971a22380c9e70d5ea..dc3c25c4f18b50eb695461f8028734fae0c46b14 100644 --- a/media/extractors/ogg/Android.bp +++ b/media/extractors/ogg/Android.bp @@ -22,8 +22,8 @@ cc_library { srcs: ["OggExtractor.cpp"], - include_dirs: [ - "external/tremolo", + export_include_dirs: [ + "include", ], header_libs: [ diff --git a/media/extractors/ogg/OggExtractor.h b/media/extractors/ogg/include/OggExtractor.h similarity index 100% rename from media/extractors/ogg/OggExtractor.h rename to media/extractors/ogg/include/OggExtractor.h diff --git a/media/extractors/tests/Android.bp b/media/extractors/tests/Android.bp index 23c74f74d4cfaf6aceec98b3af121b6ea9775bcf..3c3bbdc0e9a3146baf2774596c08f103bde75009 100644 --- a/media/extractors/tests/Android.bp +++ b/media/extractors/tests/Android.bp @@ -79,11 +79,6 @@ cc_test { "libbase", ], - include_dirs: [ - "frameworks/av/media/extractors/", - "frameworks/av/media/libstagefright/", - ], - compile_multilib: "first", cflags: [ diff --git a/media/extractors/tests/ExtractorUnitTest.cpp b/media/extractors/tests/ExtractorUnitTest.cpp index 84ec1f2167b5a9dde5f5018b4c83f2df196fb83d..2bd9c6acbb0fdb0074063148a18527451df3df02 100644 --- a/media/extractors/tests/ExtractorUnitTest.cpp +++ b/media/extractors/tests/ExtractorUnitTest.cpp @@ -27,18 +27,18 @@ #include #include -#include "aac/AACExtractor.h" -#include "amr/AMRExtractor.h" -#include "flac/FLACExtractor.h" -#include "midi/MidiExtractor.h" -#include "mkv/MatroskaExtractor.h" -#include "mp3/MP3Extractor.h" -#include "mp4/MPEG4Extractor.h" -#include "mp4/SampleTable.h" -#include "mpeg2/MPEG2PSExtractor.h" -#include "mpeg2/MPEG2TSExtractor.h" -#include "ogg/OggExtractor.h" -#include "wav/WAVExtractor.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "ExtractorUnitTestEnvironment.h" diff --git a/media/extractors/wav/Android.bp b/media/extractors/wav/Android.bp index 76546b889ddb2b3a1191f613ea5da3900a91dc31..cdf587c73d5bfc646d27e894093ddbe091d6a89c 100644 --- a/media/extractors/wav/Android.bp +++ b/media/extractors/wav/Android.bp @@ -22,8 +22,8 @@ cc_library { srcs: ["WAVExtractor.cpp"], - include_dirs: [ - "frameworks/av/media/libstagefright/include", + export_include_dirs: [ + "include", ], shared_libs: [ diff --git a/media/extractors/wav/WAVExtractor.h b/media/extractors/wav/include/WAVExtractor.h similarity index 100% rename from media/extractors/wav/WAVExtractor.h rename to media/extractors/wav/include/WAVExtractor.h diff --git a/media/libaaudio/TEST_MAPPING b/media/libaaudio/TEST_MAPPING new file mode 100644 index 0000000000000000000000000000000000000000..3de5a9fa85ee91fcf87cb4eae6150daaecb2ec6d --- /dev/null +++ b/media/libaaudio/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "CtsNativeMediaAAudioTestCases", + "options" : [ + { + "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic.*" + } + ] + } + ] +} diff --git a/media/libaaudio/examples/utils/AAudioSimplePlayer.h b/media/libaaudio/examples/utils/AAudioSimplePlayer.h index 7daac20b980292ee416aae34303c5727ededdf1e..956b3cd9361954e5d81900b409f5f901f873f7aa 100644 --- a/media/libaaudio/examples/utils/AAudioSimplePlayer.h +++ b/media/libaaudio/examples/utils/AAudioSimplePlayer.h @@ -40,6 +40,31 @@ typedef struct Timestamp { int64_t nanoseconds; } Timestamp; +static constexpr int32_t kWorkloadScaler = 500; + +// Linear congruential random number generator. +static uint32_t s_random16() { + static uint32_t seed = 1234; + seed = ((seed * 31421) + 6927) & 0x0FFFF; + return seed; +} + +/** + * The random number generator is good for burning CPU because the compiler cannot + * easily optimize away the computation. + * @param workload number of times to execute the loop + * @return a white noise value between -1.0 and +1.0 + */ +static float s_burnCPU(int32_t workload) { + uint32_t random = 0; + for (int32_t i = 0; i < workload; i++) { + for (int32_t j = 0; j < 10; j++) { + random = random ^ s_random16(); + } + } + return (random - 32768) * (1.0 / 32768); +} + /** * Simple wrapper for AAudio that opens an output stream either in callback or blocking write mode. */ @@ -268,11 +293,13 @@ typedef struct SineThreadedData_s { int32_t timestampCount = 0; // in timestamps int32_t sampleRate = 48000; int32_t prefixToneFrames = 0; + double workload = 0.0; bool sweepSetup = false; int scheduler = 0; bool schedulerChecked = false; int32_t hangTimeMSec = 0; + int cpuAffinity = -1; AAudioSimplePlayer simplePlayer; int32_t callbackCount = 0; @@ -304,6 +331,14 @@ typedef struct SineThreadedData_s { } SineThreadedData_t; +int setCpuAffinity(int cpuIndex) { +cpu_set_t cpu_set; + CPU_ZERO(&cpu_set); + CPU_SET(cpuIndex, &cpu_set); + int err = sched_setaffinity((pid_t) 0, sizeof(cpu_set_t), &cpu_set); + return err == 0 ? 0 : -errno; +} + // Callback function that fills the audio output buffer. aaudio_data_callback_result_t SimplePlayerDataCallbackProc( AAudioStream *stream, @@ -319,6 +354,10 @@ aaudio_data_callback_result_t SimplePlayerDataCallbackProc( } SineThreadedData_t *sineData = (SineThreadedData_t *) userData; + if (sineData->cpuAffinity >= 0) { + setCpuAffinity(sineData->cpuAffinity); + sineData->cpuAffinity = -1; + } // Play an initial high tone so we can tell whether the beginning was truncated. if (!sineData->sweepSetup && sineData->framesTotal >= sineData->prefixToneFrames) { sineData->setupSineSweeps(); @@ -398,6 +437,8 @@ aaudio_data_callback_result_t SimplePlayerDataCallbackProc( return AAUDIO_CALLBACK_RESULT_STOP; } + s_burnCPU((int32_t)(sineData->workload * kWorkloadScaler * numFrames)); + sineData->callbackCount++; sineData->framesTotal += numFrames; return AAUDIO_CALLBACK_RESULT_CONTINUE; diff --git a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp index cdc987b8f2c5431820f635ae6834f2af5492be28..400fc7cad9692f1a9c356e2dd849b2350d2d39d5 100644 --- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp +++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp @@ -31,10 +31,10 @@ #include "AAudioSimplePlayer.h" #include "AAudioArgsParser.h" -#define APP_VERSION "0.1.8" - -constexpr int32_t kDefaultHangTimeMSec = 10; +#define APP_VERSION "0.2.1" +static constexpr int32_t kDefaultHangTimeMSec = 10; +static constexpr int32_t kWorkPeriodSeconds = 6; /** * Open stream, play some sine waves, then close the stream. * @@ -44,7 +44,11 @@ constexpr int32_t kDefaultHangTimeMSec = 10; static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser, int32_t loopCount, int32_t prefixToneMsec, - int32_t hangTimeMSec) + int32_t hangTimeMSec, + int cpuAffinity, + double lowWorkLoad, + double highWorkLoad, + int32_t workPeriodSeconds) { SineThreadedData_t myData; AAudioSimplePlayer &player = myData.simplePlayer; @@ -57,6 +61,7 @@ static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser, myData.schedulerChecked = false; myData.callbackCount = 0; myData.hangTimeMSec = hangTimeMSec; // test AAudioStream_getXRunCount() + myData.cpuAffinity = cpuAffinity; result = player.open(argParser, SimplePlayerDataCallbackProc, @@ -111,8 +116,8 @@ static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser, } // Play a sine wave in the background. - printf("Sleep for %d seconds while audio plays in a callback thread. %d of %d\n", - argParser.getDurationSeconds(), (loopIndex + 1), loopCount); + printf("Monitor for %d seconds while audio plays in a callback thread. %d of %d, %d\n", + argParser.getDurationSeconds(), (loopIndex + 1), loopCount, workPeriodSeconds); startedAtNanos = getNanoseconds(CLOCK_MONOTONIC); for (int second = 0; second < durationSeconds; second++) { // Sleep a while. Wake up early if there is an error, for example a DISCONNECT. @@ -123,13 +128,17 @@ static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser, const int32_t framesWritten = (int32_t) AAudioStream_getFramesWritten(player.getStream()); const int32_t framesRead = (int32_t) AAudioStream_getFramesRead(player.getStream()); const int32_t xruns = AAudioStream_getXRunCount(player.getStream()); + myData.workload = ((second % (2 * workPeriodSeconds)) < workPeriodSeconds) + ? lowWorkLoad : highWorkLoad; printf(" waker result = %d, at %6d millis" - ", second = %3d, frames written %8d - read %8d = %8d, underruns = %d\n", + ", second = %3d, frames written %8d - read %8d = %8d" + ", work = %5.1f, underruns = %d\n", result, (int) millis, second, framesWritten, framesRead, framesWritten - framesRead, + myData.workload, xruns); if (result != AAUDIO_OK) { disconnected = (result == AAUDIO_ERROR_DISCONNECTED); @@ -220,6 +229,11 @@ static void usage() { AAudioArgsParser::usage(); printf(" -l{count} loopCount start/stop, every other one is silent\n"); printf(" -t{msec} play a high pitched tone at the beginning\n"); + printf(" -w{workload} set base workload, default 0.0\n"); + printf(" -W{workload} alternate between this higher workload and base workload\n"); + printf(" -Z{duration} number of seconds to spend at each workload, default = %d\n", + kWorkPeriodSeconds); + printf(" -a{cpu} set CPU affinity, default none\n"); printf(" -h{msec} force periodic underruns by hanging in callback\n"); printf(" If no value specified then %d used.\n", kDefaultHangTimeMSec); @@ -232,6 +246,10 @@ int main(int argc, const char **argv) int32_t loopCount = 1; int32_t prefixToneMsec = 0; int32_t hangTimeMSec = 0; + int cpuAffinity = -1; + double lowWorkLoad = 0.0; + double highWorkLoad = -1.0; + int32_t workPeriodSeconds = kWorkPeriodSeconds; // Make printf print immediately so that debug info is not stuck // in a buffer if we hang or crash. @@ -247,6 +265,9 @@ int main(int argc, const char **argv) if (arg[0] == '-') { char option = arg[1]; switch (option) { + case 'a': + cpuAffinity = atoi(&arg[2]); + break; case 'l': loopCount = atoi(&arg[2]); break; @@ -258,6 +279,15 @@ int main(int argc, const char **argv) ? atoi(&arg[2]) : kDefaultHangTimeMSec; break; + case 'w': + lowWorkLoad = atof(&arg[2]); + break; + case 'W': + highWorkLoad = atof(&arg[2]); + break; + case 'Z': + workPeriodSeconds = atoi(&arg[2]); + break; default: usage(); exit(EXIT_FAILURE); @@ -271,9 +301,21 @@ int main(int argc, const char **argv) } } + if (highWorkLoad > 0) { + if (highWorkLoad < lowWorkLoad) { + printf("ERROR - -W%f workload lower than -w%f workload", highWorkLoad, lowWorkLoad); + return EXIT_FAILURE; + } + } else { + highWorkLoad = lowWorkLoad; // high not specified so use low + } + // Keep looping until we can complete the test without disconnecting. while((result = testOpenPlayClose(argParser, loopCount, - prefixToneMsec, hangTimeMSec)) + prefixToneMsec, hangTimeMSec, + cpuAffinity, + lowWorkLoad, highWorkLoad, + workPeriodSeconds)) == AAUDIO_ERROR_DISCONNECTED); return (result) ? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/media/libaaudio/fuzzer/Android.bp b/media/libaaudio/fuzzer/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..2a121917f36b9e548d0c99c00f6d5dc839529527 --- /dev/null +++ b/media/libaaudio/fuzzer/Android.bp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2021 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. + */ + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_av_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_av_license"], +} + +cc_fuzz { + name: "libaaudio_fuzzer", + srcs: [ + "libaaudio_fuzzer.cpp", + ], + header_libs: [ + "libaaudio_headers", + ], + shared_libs: [ + "libbinder", + "libaudiomanager", + "libaudiopolicy", + "libaudioclient_aidl_conversion", + "libutils", + ], + static_libs: [ + "android.media.audio.common.types-V1-cpp", + "liblog", + "libcutils", + "libaaudio", + "libjsoncpp", + "libbase_ndk", + "libcgrouprc", + "libaudioutils", + "libaudioclient", + "aaudio-aidl-cpp", + "libmedia_helper", + "libmediametrics", + "libprocessgroup", + "av-types-aidl-cpp", + "libaaudio_internal", + "libcgrouprc_format", + "audiopolicy-aidl-cpp", + "audioflinger-aidl-cpp", + "audiopolicy-types-aidl-cpp", + "audioclient-types-aidl-cpp", + "shared-file-region-aidl-cpp", + "framework-permission-aidl-cpp", + "mediametricsservice-aidl-cpp", + ], + fuzz_config: { + cc: [ + "android-media-fuzzing-reports@google.com", + ], + componentid: 155276, + }, +} diff --git a/media/libaaudio/fuzzer/README.md b/media/libaaudio/fuzzer/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4ba15c5a2babb33138b6cbdd12b279a895b4d554 --- /dev/null +++ b/media/libaaudio/fuzzer/README.md @@ -0,0 +1,77 @@ +# Fuzzer for libaaudio + +## Plugin Design Considerations +The fuzzer plugin for `libaaudio` are designed based on the understanding of the +source code and tries to achieve the following: + +##### Maximize code coverage +The configuration parameters are not hardcoded, but instead selected based on +incoming data. This ensures more code paths are reached by the fuzzer. + +Fuzzers assigns values to the following parameters to pass on to libaaudio: +1. Device Id (parameter name: `deviceId`) +2. Sampling Rate (parameter name: `sampleRate`) +3. Number of channels (parameter name: `channelCount`) +4. Audio Travel Direction (parameter name: `direction`) +5. Audio Format (parameter name: `format`) +6. Audio Sharing Mode (parameter name: `sharingMode`) +7. Audio Usage (parameter name: `usage`) +8. Audio Content type (parameter name: `contentType`) +9. Audio Input Preset (parameter name: `inputPreset`) +10. Audio Privacy Sensitivity (parameter name: `privacySensitive`) +11. Buffer Capacity In Frames (parameter name: `frames`) +12. Performance Mode (parameter name: `mode`) +13. Allowed Capture Policy (parameter name: `allowedCapturePolicy`) +14. Session Id (parameter name: `sessionId`) +15. Frames per Data Callback (parameter name: `framesPerDataCallback`) +16. MMap Policy (parameter name: `policy`) + +| Parameter| Valid Values| Configured Value| +|------------- |-------------| ----- | +| `deviceId` | Any value of type `int32_t` | Value obtained from FuzzedDataProvider | +| `sampleRate` | Any value of type `int32_t` | Value obtained from FuzzedDataProvider | +| `channelCount` | Any value of type `int32_t` | Value obtained from FuzzedDataProvider | +| `direction` | 0. `AAUDIO_DIRECTION_OUTPUT` 1. `AAUDIO_DIRECTION_INPUT` | Value obtained from FuzzedDataProvider | +| `format` | 0. `AAUDIO_FORMAT_INVALID` 1. `AAUDIO_FORMAT_UNSPECIFIED` 2. `AAUDIO_FORMAT_PCM_I16` 3. `AAUDIO_FORMAT_PCM_FLOAT` | Value obtained from FuzzedDataProvider | +| `sharingMode` | 0. `AAUDIO_SHARING_MODE_EXCLUSIVE` 1. `AAUDIO_SHARING_MODE_SHARED` | Value obtained from FuzzedDataProvider | +| `usage` | 0. `AAUDIO_USAGE_MEDIA` 1. `AAUDIO_USAGE_VOICE_COMMUNICATION` 2. `AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING` 3. `AAUDIO_USAGE_ALARM` 4. `AAUDIO_USAGE_NOTIFICATION` 5. `AAUDIO_USAGE_NOTIFICATION_RINGTONE` 6. `AAUDIO_USAGE_NOTIFICATION_EVENT` 7. `AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY` 8. `AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE` 9. `AAUDIO_USAGE_ASSISTANCE_SONIFICATION` 10. `AAUDIO_USAGE_GAME` 11. `AAUDIO_USAGE_ASSISTANT` 12. `AAUDIO_SYSTEM_USAGE_EMERGENCY` 13. `AAUDIO_SYSTEM_USAGE_SAFETY` 14. `AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS` 15. `AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT` | Value obtained from FuzzedDataProvider | +| `contentType` | 0. `AAUDIO_CONTENT_TYPE_SPEECH` 1. `AAUDIO_CONTENT_TYPE_MUSIC` 2. `AAUDIO_CONTENT_TYPE_MOVIE` 3. `AAUDIO_CONTENT_TYPE_SONIFICATION` | Value obtained from FuzzedDataProvider | +| `inputPreset` | 0. `AAUDIO_INPUT_PRESET_GENERIC` 1. `AAUDIO_INPUT_PRESET_CAMCORDER` 2. `AAUDIO_INPUT_PRESET_VOICE_RECOGNITION` 3. `AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION` 4. `AAUDIO_INPUT_PRESET_UNPROCESSED` 5. `AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE` | Value obtained from FuzzedDataProvider | +| `privacySensitive` | 0. `true` 1. `false` | Value obtained from FuzzedDataProvider | +| `frames` | Any value of type `int32_t` | Value obtained from FuzzedDataProvider | +| `mode` | 0. `AAUDIO_PERFORMANCE_MODE_NONE` 1. `AAUDIO_PERFORMANCE_MODE_POWER_SAVING` 2. `AAUDIO_PERFORMANCE_MODE_LOW_LATENCY` | Value obtained from FuzzedDataProvider | +| `allowedCapturePolicy` | 0. `AAUDIO_ALLOW_CAPTURE_BY_ALL` 1. `AAUDIO_ALLOW_CAPTURE_BY_SYSTEM` 2. `AAUDIO_ALLOW_CAPTURE_BY_NONE` | Value obtained from FuzzedDataProvider | +| `sessionId` | 0. `AAUDIO_SESSION_ID_NONE` 1. `AAUDIO_SESSION_ID_ALLOCATE` | Value obtained from FuzzedDataProvider | +| `framesPerDataCallback` | Any value of type `int32_t` | Value obtained from FuzzedDataProvider | +| `policy` | 0. `AAUDIO_POLICY_NEVER` 1. `AAUDIO_POLICY_AUTO` 2. `AAUDIO_POLICY_ALWAYS` | Value obtained from FuzzedDataProvider | + +This also ensures that the plugin is always deterministic for any given input. + +##### Maximize utilization of input data +The plugin feed the entire input data to the module. +This ensures that the plugins tolerates any kind of input (empty, huge, +malformed, etc) and doesn't `exit()` on any input and thereby increasing the +chance of identifying vulnerabilities. + +## Build + +This describes steps to build libaaudio_fuzzer binary. + +### Android + +#### Steps to build +Build the fuzzer +``` + $ mm -j$(nproc) libaaudio_fuzzer +``` +### Steps to run + +To run on device +``` + $ adb sync data + $ adb shell /data/fuzz/arm64/libaaudio_fuzzer/libaaudio_fuzzer +``` + +## References: + * http://llvm.org/docs/LibFuzzer.html + * https://github.com/google/oss-fuzz diff --git a/media/libaaudio/fuzzer/libaaudio_fuzzer.cpp b/media/libaaudio/fuzzer/libaaudio_fuzzer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0233ee1b374004492b6478df669c1bd4c9296932 --- /dev/null +++ b/media/libaaudio/fuzzer/libaaudio_fuzzer.cpp @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "aaudio/AAudio.h" +#include "aaudio/AAudioTesting.h" +#include + +constexpr int32_t kRandomStringLength = 256; + +constexpr int64_t kNanosPerMillisecond = 1000 * 1000; + +constexpr aaudio_direction_t kDirections[] = { + AAUDIO_DIRECTION_OUTPUT, AAUDIO_DIRECTION_INPUT, AAUDIO_UNSPECIFIED}; + +constexpr aaudio_performance_mode_t kPerformanceModes[] = { + AAUDIO_PERFORMANCE_MODE_NONE, AAUDIO_PERFORMANCE_MODE_POWER_SAVING, + AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, AAUDIO_UNSPECIFIED}; + +constexpr aaudio_format_t kFormats[] = { + AAUDIO_FORMAT_INVALID, AAUDIO_FORMAT_UNSPECIFIED, + AAUDIO_FORMAT_PCM_I16, AAUDIO_FORMAT_PCM_FLOAT, + AAUDIO_FORMAT_PCM_I24_PACKED, AAUDIO_FORMAT_PCM_I32}; + +constexpr aaudio_sharing_mode_t kSharingModes[] = { + AAUDIO_SHARING_MODE_EXCLUSIVE, AAUDIO_SHARING_MODE_SHARED}; + +constexpr int32_t kSampleRates[] = {AAUDIO_UNSPECIFIED, + 8000, + 11025, + 16000, + 22050, + 32000, + 44100, + 48000, + 88200, + 96000}; + +constexpr aaudio_usage_t kUsages[] = { + AAUDIO_USAGE_MEDIA, + AAUDIO_USAGE_VOICE_COMMUNICATION, + AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING, + AAUDIO_USAGE_ALARM, + AAUDIO_USAGE_NOTIFICATION, + AAUDIO_USAGE_NOTIFICATION_RINGTONE, + AAUDIO_USAGE_NOTIFICATION_EVENT, + AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY, + AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE, + AAUDIO_USAGE_ASSISTANCE_SONIFICATION, + AAUDIO_USAGE_GAME, + AAUDIO_USAGE_ASSISTANT, + AAUDIO_SYSTEM_USAGE_EMERGENCY, + AAUDIO_SYSTEM_USAGE_SAFETY, + AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS, + AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT, + AAUDIO_UNSPECIFIED}; + +constexpr aaudio_content_type_t kContentTypes[] = { + AAUDIO_CONTENT_TYPE_SPEECH, AAUDIO_CONTENT_TYPE_MUSIC, + AAUDIO_CONTENT_TYPE_MOVIE, AAUDIO_CONTENT_TYPE_SONIFICATION, + AAUDIO_UNSPECIFIED}; + +constexpr aaudio_input_preset_t kInputPresets[] = { + AAUDIO_INPUT_PRESET_GENERIC, + AAUDIO_INPUT_PRESET_CAMCORDER, + AAUDIO_INPUT_PRESET_VOICE_RECOGNITION, + AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION, + AAUDIO_INPUT_PRESET_UNPROCESSED, + AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE, + AAUDIO_UNSPECIFIED}; + +constexpr aaudio_allowed_capture_policy_t kAllowedCapturePolicies[] = { + AAUDIO_ALLOW_CAPTURE_BY_ALL, AAUDIO_ALLOW_CAPTURE_BY_SYSTEM, + AAUDIO_ALLOW_CAPTURE_BY_NONE, AAUDIO_UNSPECIFIED}; + +constexpr aaudio_session_id_t kSessionIds[] = { + AAUDIO_SESSION_ID_NONE, AAUDIO_SESSION_ID_ALLOCATE, AAUDIO_UNSPECIFIED}; + +constexpr aaudio_policy_t kPolicies[] = { + AAUDIO_POLICY_NEVER, AAUDIO_POLICY_AUTO, AAUDIO_POLICY_ALWAYS, + AAUDIO_UNSPECIFIED}; + +class LibAaudioFuzzer { +public: + ~LibAaudioFuzzer() { deInit(); } + bool init(); + void process(const uint8_t *data, size_t size); + void deInit(); + +private: + AAudioStreamBuilder *mAaudioBuilder = nullptr; + AAudioStream *mAaudioStream = nullptr; +}; + +bool LibAaudioFuzzer::init() { + aaudio_result_t result = AAudio_createStreamBuilder(&mAaudioBuilder); + if ((result != AAUDIO_OK) || (!mAaudioBuilder)) { + return false; + } + return true; +} + +void LibAaudioFuzzer::process(const uint8_t *data, size_t size) { + FuzzedDataProvider fdp(data, size); + aaudio_performance_mode_t mode = + fdp.PickValueInArray({fdp.PickValueInArray(kPerformanceModes), + fdp.ConsumeIntegral()}); + AAudioStreamBuilder_setPerformanceMode(mAaudioBuilder, mode); + + int32_t deviceId = fdp.PickValueInArray( + {AAUDIO_UNSPECIFIED, fdp.ConsumeIntegral()}); + AAudioStreamBuilder_setDeviceId(mAaudioBuilder, deviceId); + + std::string packageName = fdp.PickValueInArray( + {"android.nativemedia.aaudio", "android.app.appops.cts", + fdp.ConsumeRandomLengthString(kRandomStringLength)}); + AAudioStreamBuilder_setPackageName(mAaudioBuilder, packageName.c_str()); + + std::string attributionTag = + fdp.ConsumeRandomLengthString(kRandomStringLength); + AAudioStreamBuilder_setAttributionTag(mAaudioBuilder, attributionTag.c_str()); + + int32_t sampleRate = fdp.PickValueInArray(kSampleRates); + AAudioStreamBuilder_setSampleRate(mAaudioBuilder, sampleRate); + + int32_t channelCount = fdp.PickValueInArray( + {AAUDIO_UNSPECIFIED, fdp.ConsumeIntegral()}); + AAudioStreamBuilder_setChannelCount(mAaudioBuilder, channelCount); + + aaudio_direction_t direction = fdp.PickValueInArray( + {fdp.PickValueInArray(kDirections), fdp.ConsumeIntegral()}); + AAudioStreamBuilder_setDirection(mAaudioBuilder, direction); + + aaudio_format_t format = fdp.PickValueInArray( + {fdp.PickValueInArray(kFormats), fdp.ConsumeIntegral()}); + AAudioStreamBuilder_setFormat(mAaudioBuilder, format); + + aaudio_sharing_mode_t sharingMode = fdp.PickValueInArray( + {fdp.PickValueInArray(kSharingModes), fdp.ConsumeIntegral()}); + AAudioStreamBuilder_setSharingMode(mAaudioBuilder, sharingMode); + + aaudio_usage_t usage = fdp.PickValueInArray( + {fdp.PickValueInArray(kUsages), fdp.ConsumeIntegral()}); + AAudioStreamBuilder_setUsage(mAaudioBuilder, usage); + + aaudio_content_type_t contentType = fdp.PickValueInArray( + {fdp.PickValueInArray(kContentTypes), fdp.ConsumeIntegral()}); + AAudioStreamBuilder_setContentType(mAaudioBuilder, contentType); + + aaudio_input_preset_t inputPreset = fdp.PickValueInArray( + {fdp.PickValueInArray(kInputPresets), fdp.ConsumeIntegral()}); + AAudioStreamBuilder_setInputPreset(mAaudioBuilder, inputPreset); + + bool privacySensitive = fdp.ConsumeBool(); + AAudioStreamBuilder_setPrivacySensitive(mAaudioBuilder, privacySensitive); + + int32_t frames = fdp.PickValueInArray( + {AAUDIO_UNSPECIFIED, fdp.ConsumeIntegral()}); + AAudioStreamBuilder_setBufferCapacityInFrames(mAaudioBuilder, frames); + + aaudio_allowed_capture_policy_t allowedCapturePolicy = + fdp.PickValueInArray({fdp.PickValueInArray(kAllowedCapturePolicies), + fdp.ConsumeIntegral()}); + AAudioStreamBuilder_setAllowedCapturePolicy(mAaudioBuilder, + allowedCapturePolicy); + + aaudio_session_id_t sessionId = fdp.PickValueInArray( + {fdp.PickValueInArray(kSessionIds), fdp.ConsumeIntegral()}); + AAudioStreamBuilder_setSessionId(mAaudioBuilder, sessionId); + + AAudioStreamBuilder_setDataCallback(mAaudioBuilder, nullptr, nullptr); + AAudioStreamBuilder_setErrorCallback(mAaudioBuilder, nullptr, nullptr); + + int32_t framesPerDataCallback = fdp.PickValueInArray( + {AAUDIO_UNSPECIFIED, fdp.ConsumeIntegral()}); + AAudioStreamBuilder_setFramesPerDataCallback(mAaudioBuilder, + framesPerDataCallback); + + aaudio_policy_t policy = fdp.PickValueInArray( + {fdp.PickValueInArray(kPolicies), fdp.ConsumeIntegral()}); + AAudio_setMMapPolicy(policy); + (void)AAudio_getMMapPolicy(); + + aaudio_result_t result = + AAudioStreamBuilder_openStream(mAaudioBuilder, &mAaudioStream); + if ((result != AAUDIO_OK) || (!mAaudioStream)) { + return; + } + + int32_t framesPerBurst = AAudioStream_getFramesPerBurst(mAaudioStream); + uint8_t numberOfBursts = fdp.ConsumeIntegral(); + int32_t maxFrames = numberOfBursts * framesPerBurst; + int32_t requestedBufferSize = + fdp.ConsumeIntegral() * framesPerBurst; + AAudioStream_setBufferSizeInFrames(mAaudioStream, requestedBufferSize); + + int64_t position = 0, nanoseconds = 0; + AAudioStream_getTimestamp(mAaudioStream, CLOCK_MONOTONIC, &position, + &nanoseconds); + + AAudioStream_requestStart(mAaudioStream); + + aaudio_format_t actualFormat = AAudioStream_getFormat(mAaudioStream); + int32_t actualChannelCount = AAudioStream_getChannelCount(mAaudioStream); + + int32_t count = fdp.ConsumeIntegral(); + direction = AAudioStream_getDirection(mAaudioStream); + + if (actualFormat == AAUDIO_FORMAT_PCM_I16) { + std::vector inputShortData(maxFrames * actualChannelCount, 0x0); + if (direction == AAUDIO_DIRECTION_INPUT) { + AAudioStream_read(mAaudioStream, inputShortData.data(), maxFrames, + count * kNanosPerMillisecond); + } else if (direction == AAUDIO_DIRECTION_OUTPUT) { + AAudioStream_write(mAaudioStream, inputShortData.data(), maxFrames, + count * kNanosPerMillisecond); + } + } else if (actualFormat == AAUDIO_FORMAT_PCM_FLOAT) { + std::vector inputFloatData(maxFrames * actualChannelCount, 0x0); + if (direction == AAUDIO_DIRECTION_INPUT) { + AAudioStream_read(mAaudioStream, inputFloatData.data(), maxFrames, + count * kNanosPerMillisecond); + } else if (direction == AAUDIO_DIRECTION_OUTPUT) { + AAudioStream_write(mAaudioStream, inputFloatData.data(), maxFrames, + count * kNanosPerMillisecond); + } + } + + aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNKNOWN; + AAudioStream_waitForStateChange(mAaudioStream, AAUDIO_STREAM_STATE_UNKNOWN, + &state, count * kNanosPerMillisecond); + (void)AAudio_convertStreamStateToText(state); + + (void)AAudioStream_getUsage(mAaudioStream); + (void)AAudioStream_getSampleRate(mAaudioStream); + (void)AAudioStream_getState(mAaudioStream); + (void)AAudioStream_getSamplesPerFrame(mAaudioStream); + (void)AAudioStream_getContentType(mAaudioStream); + (void)AAudioStream_getInputPreset(mAaudioStream); + (void)AAudioStream_isPrivacySensitive(mAaudioStream); + (void)AAudioStream_getAllowedCapturePolicy(mAaudioStream); + (void)AAudioStream_getPerformanceMode(mAaudioStream); + (void)AAudioStream_getDeviceId(mAaudioStream); + (void)AAudioStream_getSharingMode(mAaudioStream); + (void)AAudioStream_getSessionId(mAaudioStream); + (void)AAudioStream_getFramesRead(mAaudioStream); + (void)AAudioStream_getFramesWritten(mAaudioStream); + (void)AAudioStream_getXRunCount(mAaudioStream); + (void)AAudioStream_getBufferCapacityInFrames(mAaudioStream); + (void)AAudioStream_getBufferSizeInFrames(mAaudioStream); + (void)AAudioStream_isMMapUsed(mAaudioStream); + + AAudioStream_requestPause(mAaudioStream); + AAudioStream_requestFlush(mAaudioStream); + AAudioStream_release(mAaudioStream); + AAudioStream_requestStop(mAaudioStream); +} + +void LibAaudioFuzzer::deInit() { + if (mAaudioBuilder) { + AAudioStreamBuilder_delete(mAaudioBuilder); + } + if (mAaudioStream) { + AAudioStream_close(mAaudioStream); + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + LibAaudioFuzzer libAaudioFuzzer; + if (libAaudioFuzzer.init()) { + libAaudioFuzzer.process(data, size); + } + return 0; +} diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h index 4ebb5302850336290768e71ab4613af11c443d81..0c4a8f78bfa7aa1380973e460ec2cf59f72602ce 100644 --- a/media/libaaudio/include/aaudio/AAudio.h +++ b/media/libaaudio/include/aaudio/AAudio.h @@ -20,7 +20,7 @@ */ /** - * @file AAudio.h + * @file aaudio/AAudio.h */ /** @@ -778,8 +778,16 @@ AAUDIO_API aaudio_result_t AAudio_createStreamBuilder(AAudioStreamBuilder** buil __INTRODUCED_IN(26); /** - * Request an audio device identified device using an ID. - * On Android, for example, the ID could be obtained from the Java AudioManager. + * Request an audio device identified by an ID. + * + * The ID could be obtained from the Java AudioManager. + * AudioManager.getDevices() returns an array of {@link AudioDeviceInfo}, + * which contains a getId() method. That ID can be passed to this function. + * + * It is possible that you may not get the device that you requested. + * So if it is important to you, you should call + * AAudioStream_getDeviceId() after the stream is opened to + * verify the actual ID. * * The default, if you do not call this function, is {@link #AAUDIO_UNSPECIFIED}, * in which case the primary device will be used. @@ -798,8 +806,11 @@ AAUDIO_API void AAudioStreamBuilder_setDeviceId(AAudioStreamBuilder* builder, * This is usually {@code Context#getPackageName()}. * * The default, if you do not call this function, is a random package in the calling uid. - * The vast majority of apps have only one package per calling UID. If the package - * name does not match the calling UID, then requests will be rejected. + * The vast majority of apps have only one package per calling UID. + * If an invalid package name is set, input streams may not be given permission to + * record when started. + * + * The package name is usually the applicationId in your app's build.gradle file. * * Available since API level 31. * diff --git a/media/libaaudio/include/aaudio/AAudioTesting.h b/media/libaaudio/include/aaudio/AAudioTesting.h index 02ec4116323d7ba42402469f99040d355311a91c..0f2d7a2d7b093af2d397cf4478ea6926af51e1fa 100644 --- a/media/libaaudio/include/aaudio/AAudioTesting.h +++ b/media/libaaudio/include/aaudio/AAudioTesting.h @@ -49,6 +49,12 @@ enum { }; typedef int32_t aaudio_policy_t; +// Internal error codes. Only used by the framework. +enum { + AAUDIO_INTERNAL_ERROR_BASE = -1000, + AAUDIO_ERROR_STANDBY, +}; + /** * Control whether AAudioStreamBuilder_openStream() will use the new MMAP data path * or the older "Legacy" data path. diff --git a/media/libaaudio/scripts/measure_device_power.py b/media/libaaudio/scripts/measure_device_power.py new file mode 100755 index 0000000000000000000000000000000000000000..1f90933438656fc8e8328af468bc86e5ca65f39b --- /dev/null +++ b/media/libaaudio/scripts/measure_device_power.py @@ -0,0 +1,288 @@ +#!/usr/bin/python3 +""" + * Copyright (C) 2021 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. +""" + +''' +Measure CPU related power on Pixel 6 or later devices using ODPM, +the On Device Power Measurement tool. +Generate a CSV report for putting in a spreadsheet +''' + +import argparse +import os +import re +import subprocess +import sys +import time + +# defaults +PRE_DELAY_SECONDS = 0.5 # time to sleep before command to avoid adb unroot error +DEFAULT_NUM_ITERATIONS = 5 +DEFAULT_FILE_NAME = 'energy_commands.txt' + +''' +Default rail assignments +philburk-macbookpro3:expt philburk$ adb shell cat /sys/bus/iio/devices/iio\:device0/energy_value +t=349894 +CH0(T=349894)[S10M_VDD_TPU], 5578756 +CH1(T=349894)[VSYS_PWR_MODEM], 29110940 +CH2(T=349894)[VSYS_PWR_RFFE], 3166046 +CH3(T=349894)[S2M_VDD_CPUCL2], 30203502 +CH4(T=349894)[S3M_VDD_CPUCL1], 23377533 +CH5(T=349894)[S4M_VDD_CPUCL0], 46356942 +CH6(T=349894)[S5M_VDD_INT], 10771876 +CH7(T=349894)[S1M_VDD_MIF], 21091363 +philburk-macbookpro3:expt philburk$ adb shell cat /sys/bus/iio/devices/iio\:device1/energy_value +t=359458 +CH0(T=359458)[VSYS_PWR_WLAN_BT], 45993209 +CH1(T=359458)[L2S_VDD_AOC_RET], 2822928 +CH2(T=359458)[S9S_VDD_AOC], 6923706 +CH3(T=359458)[S5S_VDDQ_MEM], 4658202 +CH4(T=359458)[S10S_VDD2L], 5506273 +CH5(T=359458)[S4S_VDD2H_MEM], 14254574 +CH6(T=359458)[S2S_VDD_G3D], 5315420 +CH7(T=359458)[VSYS_PWR_DISPLAY], 81221665 +''' + +''' +LDO2M(L2M_ALIVE):DDR -> DRAM Array Core Power +BUCK4S(S4S_VDD2H_MEM):DDR -> Normal operation data and control path logic circuits +BUCK5S(S5S_VDDQ_MEM):DDR -> LPDDR I/O interface +BUCK10S(S10S_VDD2L):DDR -> DVFSC (1600Mbps or lower) operation data and control path logic circuits +BUCK1M (S1M_VDD_MIF): SoC side Memory InterFace and Controller +''' + +# Map between rail name and human readable name. +ENERGY_DICTIONARY = { \ + 'S4M_VDD_CPUCL0': 'CPU0', \ + 'S3M_VDD_CPUCL1': 'CPU1', \ + 'S2M_VDD_CPUCL2': 'CPU2', \ + 'S1M_VDD_MIF': 'MIF', \ + 'L2M_ALIVE': 'DDRAC', \ + 'S4S_VDD2H_MEM': 'DDRNO', \ + 'S10S_VDD2L': 'DDR16', \ + 'S5S_VDDQ_MEM': 'DDRIO', \ + 'VSYS_PWR_DISPLAY': 'SCREEN'} + +SORTED_ENERGY_LIST = sorted(ENERGY_DICTIONARY, key=ENERGY_DICTIONARY.get) + +# Sometimes adb returns 1 for no apparent reason. +# So try several times. +# @return 0 on success +def adbTryMultiple(command): + returnCode = 1 + count = 0 + limit = 5 + while count < limit and returnCode != 0: + print(('Try to adb {} {} of {}'.format(command, count, limit))) + subprocess.call(["adb", "wait-for-device"]) + time.sleep(PRE_DELAY_SECONDS) + returnCode = subprocess.call(["adb", command]) + print(('returnCode = {}'.format(returnCode))) + count += 1 + return returnCode + +# Sometimes "adb root" returns 1! +# So try several times. +# @return 0 on success +def adbRoot(): + return adbTryMultiple("root"); + +# Sometimes "adb unroot" returns 1! +# So try several times. +# @return 0 on success +def adbUnroot(): + return adbTryMultiple("unroot"); + +# @param commandString String containing shell command +# @return Both the stdout and stderr of the commands run +def runCommand(commandString): + print(commandString) + if commandString == "adb unroot": + result = adbUnroot() + elif commandString == "adb root": + result = adbRoot() + else: + commandArray = commandString.split(' ') + result = subprocess.run(commandArray, check=True, capture_output=True).stdout + return result + +# @param commandString String containing ADB command +# @return Both the stdout and stderr of the commands run +def adbCommand(commandString): + if commandString == "unroot": + result = adbUnroot() + elif commandString == "root": + result = adbRoot() + else: + print(("adb " + commandString)) + commandArray = ["adb"] + commandString.split(' ') + subprocess.call(["adb", "wait-for-device"]) + result = subprocess.run(commandArray, check=True, capture_output=True).stdout + return result + +# Parse a line that looks like "CH3(T=10697635)[S2M_VDD_CPUCL2], 116655335" +# Use S2M_VDD_CPUCL2 as the tag and set value to the number +# in the report dictionary. +def parseEnergyValue(string): + return tuple(re.split('\[|\], +', string)[1:]) + +# Read accumulated energy into a dictionary. +def measureEnergyForDevice(deviceIndex, report): + # print("measureEnergyForDevice " + str(deviceIndex)) + tableBytes = adbCommand( \ + 'shell cat /sys/bus/iio/devices/iio\:device{}/energy_value'\ + .format(deviceIndex)) + table = tableBytes.decode("utf-8") + # print(table) + for count, line in enumerate(table.splitlines()): + if count > 0: + tagEnergy = parseEnergyValue(line) + report[tagEnergy[0]] = int(tagEnergy[1].strip()) + # print(report) + +def measureEnergyOnce(): + adbCommand("root") + report = {} + d0 = measureEnergyForDevice(0, report) + d1 = measureEnergyForDevice(1, report) + adbUnroot() + return report + +# Subtract numeric values for matching keys. +def subtractReports(A, B): + return {x: A[x] - B[x] for x in A if x in B} + +# Add numeric values for matching keys. +def addReports(A, B): + return {x: A[x] + B[x] for x in A if x in B} + +# Divide numeric values by divisor. +# @return Modified copy of report. +def divideReport(report, divisor): + return {key: val / divisor for key, val in list(report.items())} + +# Generate a dictionary that is the difference between two measurements over time. +def measureEnergyOverTime(duration): + report1 = measureEnergyOnce() + print(("Measure energy for " + str(duration) + " seconds.")) + time.sleep(duration) + report2 = measureEnergyOnce() + return subtractReports(report2, report1) + +# Generate a CSV string containing the human readable headers. +def formatEnergyHeader(): + header = "" + for tag in SORTED_ENERGY_LIST: + header += ENERGY_DICTIONARY[tag] + ", " + return header + +# Generate a CSV string containing the numeric values. +def formatEnergyData(report): + data = "" + for tag in SORTED_ENERGY_LIST: + if tag in list(report.keys()): + data += str(report[tag]) + ", " + else: + data += "-1," + return data + +def printEnergyReport(report): + s = "\n" + s += "Values are in microWattSeconds\n" + s += "Report below is CSV format for pasting into a spreadsheet:\n" + s += formatEnergyHeader() + "\n" + s += formatEnergyData(report) + "\n" + print(s) + +# Generate a dictionary that is the difference between two measurements +# before and after executing the command. +def measureEnergyForCommand(command): + report1 = measureEnergyOnce() + print(("Measure energy for: " + command)) + result = runCommand(command) + report2 = measureEnergyOnce() + # print(result) + return subtractReports(report2, report1) + +# Average the results of several measurements for one command. +def averageEnergyForCommand(command, count): + print("=================== #0\n") + sumReport = measureEnergyForCommand(command) + for i in range(1, count): + print(("=================== #" + str(i) + "\n")) + report = measureEnergyForCommand(command) + sumReport = addReports(sumReport, report) + print(sumReport) + return divideReport(sumReport, count) + +# Parse a list of commands in a file. +# Lines ending in "\" are continuation lines. +# Lines beginning with "#" are comments. +def measureEnergyForCommands(fileName): + finalReport = "------------------------------------\n" + finalReport += "comment, command, " + formatEnergyHeader() + "\n" + comment = "" + try: + fp = open(fileName) + line = fp.readline() + while line: + command = line.strip() + if command.startswith("#"): + # ignore comment + print((command + "\n")) + comment = command[1:].strip() # remove leading '#' + elif command.endswith('\\'): + command = command[:-1].strip() # remove trailing '\' + runCommand(command) + elif command: + report = averageEnergyForCommand(command, DEFAULT_NUM_ITERATIONS) + finalReport += comment + ", " + command + ", " + formatEnergyData(report) + "\n" + print(finalReport) + line = fp.readline() + finally: + fp.close() + return finalReport + +def main(): + # parse command line args + parser = argparse.ArgumentParser() + parser.add_argument('-s', '--seconds', + help="Measure power for N seconds. Ignore scriptFile.", + type=float) + parser.add_argument("fileName", + nargs = '?', + help="Path to file containing commands to be measured." + + " Default path = " + DEFAULT_FILE_NAME + "." + + " Lines ending in '\' are continuation lines." + + " Lines beginning with '#' are comments.", + default=DEFAULT_FILE_NAME) + args=parser.parse_args(); + + print(("seconds = " + str(args.seconds))) + print(("fileName = " + str(args.fileName))) + # Process command line + if args.seconds: + report = measureEnergyOverTime(args.seconds) + printEnergyReport(report) + else: + report = measureEnergyForCommands(args.fileName) + print(report) + print("Finished.\n") + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/media/libaaudio/scripts/setup_odpm_cpu_rails.sh b/media/libaaudio/scripts/setup_odpm_cpu_rails.sh new file mode 100755 index 0000000000000000000000000000000000000000..e9241b9dcce4913f470c744b66ea42804f8f8e52 --- /dev/null +++ b/media/libaaudio/scripts/setup_odpm_cpu_rails.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Configure ODPM rails to measure CPU specific power. +# See go/odpm-p21-userguide + +adb root + +# LDO2M(L2M_ALIVE) - DRAM Array Core Power +adb shell 'echo "CH0=LDO2M" > /sys/bus/iio/devices/iio\:device0/enabled_rails' + +# These are the defaults. +# BUCK2M(S2M_VDD_CPUCL2):CPU(BIG) +# adb shell 'echo "CH3=BUCK2M" > /sys/bus/iio/devices/iio\:device0/enabled_rails' +# BUCK3M(S3M_VDD_CPUCL1):CPU(MID) +# adb shell 'echo "CH4=BUCK3M" > /sys/bus/iio/devices/iio\:device0/enabled_rails' +# BUCK4M(S4M_VDD_CPUCL0):CPU(LITTLE) +# adb shell 'echo "CH5=BUCK4M" > /sys/bus/iio/devices/iio\:device0/enabled_rails' +# BUCK1M(S1M_VDD_MIF):MIF +# adb shell 'echo "CH7=BUCK1M" > /sys/bus/iio/devices/iio\:device0/enabled_rails' + +# These are default on device1. +# BUCK5S(S5S_VDDQ_MEM):DDR +# adb shell 'echo "CH3=BUCK5S" > /sys/bus/iio/devices/iio\:device1/enabled_rails' +# BUCK10S(S10S_VDD2L):DDR +# adb shell 'echo "CH4=BUCK10S" > /sys/bus/iio/devices/iio\:device1/enabled_rails' +# BUCK4S(S4S_VDD2H_MEM):DDR +# adb shell 'echo "CH5=BUCK4S" > /sys/bus/iio/devices/iio\:device1/enabled_rails' + +adb shell 'cat /sys/bus/iio/devices/iio\:device0/enabled_rails' +adb shell 'cat /sys/bus/iio/devices/iio\:device1/enabled_rails' + +adb unroot + diff --git a/media/libaaudio/scripts/synthmark_tests.txt b/media/libaaudio/scripts/synthmark_tests.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b6d47e8a44d18417e126c9e2577e7096563070a --- /dev/null +++ b/media/libaaudio/scripts/synthmark_tests.txt @@ -0,0 +1,50 @@ +# Measure energy consumption with synthmark. + +# None +adb shell synthmark -tj -n1 -N50 -B2 -z0 +adb shell synthmark -tj -n1 -N75 -B2 -z0 +adb shell synthmark -tj -n1 -N100 -B2 -z0 + +# ADPF PID +adb shell synthmark -tj -n1 -N50 -B2 -z1 +adb shell synthmark -tj -n1 -N75 -B2 -z1 +adb shell synthmark -tj -n1 -N100 -B2 -z1 + +# ADPF <400 RR +# adb root \ +# adb shell setprop vendor.powerhal.adpf.uclamp_min.high_limit 400 \ +# adb shell synthmark -tj -n1 -N50 -B2 -z1 +# adb shell synthmark -tj -n1 -N75 -B2 -z1 +# adb shell synthmark -tj -n1 -N100 -B2 -z1 + +# ADPF <500 RR +# adb root \ +# adb shell setprop vendor.powerhal.adpf.uclamp_min.high_limit 500 \ +# adb shell synthmark -tj -n1 -N50 -B2 -z1 +# adb shell synthmark -tj -n1 -N75 -B2 -z1 +# adb shell synthmark -tj -n1 -N100 -B2 -z1 + +# ADPF <600 RR +# adb root \ +# adb shell setprop vendor.powerhal.adpf.uclamp_min.high_limit 600 \ +# adb shell synthmark -tj -n1 -N50 -B2 -z1 +# adb shell synthmark -tj -n1 -N75 -B2 -z1 +# adb shell synthmark -tj -n1 -N100 -B2 -z1 + +# uclamp +# adb root \ +# adb shell synthmark -tj -n1 -N75 -B2 -u1 + +# steady +# adb shell synthmark -tj -n75 -B2 -u0 + +# CPU affinity +# adb shell synthmark -tj -n1 -N75 -B2 -u0 -c1 +# adb shell synthmark -tj -n1 -N75 -B2 -u0 -c4 +# adb shell synthmark -tj -n1 -N75 -B2 -u0 -c6 + +# steady + affinity +# adb shell synthmark -tj -n75 -B2 -u0 -c1 +# adb shell synthmark -tj -n75 -B2 -u0 -c4 +# adb shell synthmark -tj -n75 -B2 -u0 -c6 + diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp index 33a5c7fd824a42ec41785b3139325644bdb95604..363d219d9d626f7b9ddb0b15cb7cbe9ea5aaee56 100644 --- a/media/libaaudio/src/Android.bp +++ b/media/libaaudio/src/Android.bp @@ -7,6 +7,65 @@ package { default_applicable_licenses: ["frameworks_av_license"], } +tidy_errors = [ + // https://clang.llvm.org/extra/clang-tidy/checks/list.html + // For many categories, the checks are too many to specify individually. + // Feel free to disable as needed - as warnings are generally ignored, + // we treat warnings as errors. + "android-*", + "bugprone-*", + "cert-*", + "clang-analyzer-security*", + "google-*", + "misc-*", + //"modernize-*", // explicitly list the modernize as they can be subjective. + "modernize-avoid-bind", + //"modernize-avoid-c-arrays", // std::array<> can be verbose + "modernize-concat-nested-namespaces", + //"modernize-deprecated-headers", // C headers still ok even if there is C++ equivalent. + "modernize-deprecated-ios-base-aliases", + "modernize-loop-convert", + "modernize-make-shared", + "modernize-make-unique", + "modernize-pass-by-value", + "modernize-raw-string-literal", + "modernize-redundant-void-arg", + "modernize-replace-auto-ptr", + "modernize-replace-random-shuffle", + "modernize-return-braced-init-list", + "modernize-shrink-to-fit", + "modernize-unary-static-assert", + // "modernize-use-auto", // found in AAudioAudio.cpp + "modernize-use-bool-literals", + "modernize-use-default-member-init", + "modernize-use-emplace", + "modernize-use-equals-default", + "modernize-use-equals-delete", + // "modernize-use-nodiscard", // found in aidl generated files + "modernize-use-noexcept", + "modernize-use-nullptr", + // "modernize-use-override", // found in aidl generated files + // "modernize-use-trailing-return-type", // not necessarily more readable + "modernize-use-transparent-functors", + "modernize-use-uncaught-exceptions", + // "modernize-use-using", // found typedef in several files + "performance-*", + + // Remove some pedantic stylistic requirements. + "-android-cloexec-dup", // found in SharedMemoryParcelable.cpp + "-bugprone-macro-parentheses", // found in SharedMemoryParcelable.h + "-bugprone-narrowing-conversions", // found in several interface from size_t to int32_t + + "-google-readability-casting", // C++ casts not always necessary and may be verbose + "-google-readability-todo", // do not require TODO(info) + "-google-build-using-namespace", // Reenable and fix later. + "-google-global-names-in-headers", // found in several files + + "-misc-non-private-member-variables-in-classes", // found in aidl generated files + + "-performance-no-int-to-ptr", // found in SharedMemoryParcelable.h +] + cc_library { name: "libaaudio", @@ -34,7 +93,6 @@ cc_library { "-Wno-unused-parameter", "-Wall", "-Werror", - // By default, all symbols are hidden. // "-fvisibility=hidden", // AAUDIO_API is used to explicitly export a function or a variable as a visible symbol. @@ -52,7 +110,7 @@ cc_library { "libcutils", "libutils", "libbinder", - "libpermission", + "framework-permission-aidl-cpp", ], sanitize: { @@ -64,6 +122,13 @@ cc_library { symbol_file: "libaaudio.map.txt", versions: ["28"], }, + + tidy: true, + tidy_checks: tidy_errors, + tidy_checks_as_errors: tidy_errors, + tidy_flags: [ + "-format-style=file", + ] } cc_library { @@ -102,6 +167,8 @@ cc_library { "libbinder", "framework-permission-aidl-cpp", "aaudio-aidl-cpp", + "android.media.audio.common.types-V1-cpp", + "audioclient-types-aidl-cpp", "libaudioclient_aidl_conversion", ], @@ -139,10 +206,16 @@ cc_library { "binding/RingBufferParcelable.cpp", "binding/SharedMemoryParcelable.cpp", "binding/SharedRegionParcelable.cpp", - "flowgraph/AudioProcessorBase.cpp", + "flowgraph/ChannelCountConverter.cpp", "flowgraph/ClipToRange.cpp", + "flowgraph/FlowGraphNode.cpp", + "flowgraph/ManyToMultiConverter.cpp", + "flowgraph/MonoBlend.cpp", "flowgraph/MonoToMultiConverter.cpp", + "flowgraph/MultiToMonoConverter.cpp", + "flowgraph/MultiToManyConverter.cpp", "flowgraph/RampLinear.cpp", + "flowgraph/SampleRateConverter.cpp", "flowgraph/SinkFloat.cpp", "flowgraph/SinkI16.cpp", "flowgraph/SinkI24.cpp", @@ -151,11 +224,26 @@ cc_library { "flowgraph/SourceI16.cpp", "flowgraph/SourceI24.cpp", "flowgraph/SourceI32.cpp", + "flowgraph/resampler/IntegerRatio.cpp", + "flowgraph/resampler/LinearResampler.cpp", + "flowgraph/resampler/MultiChannelResampler.cpp", + "flowgraph/resampler/PolyphaseResampler.cpp", + "flowgraph/resampler/PolyphaseResamplerMono.cpp", + "flowgraph/resampler/PolyphaseResamplerStereo.cpp", + "flowgraph/resampler/SincResampler.cpp", + "flowgraph/resampler/SincResamplerStereo.cpp", ], sanitize: { integer_overflow: true, misc_undefined: ["bounds"], }, + + tidy: true, + tidy_checks: tidy_errors, + tidy_checks_as_errors: tidy_errors, + tidy_flags: [ + "-format-style=file", + ] } aidl_interface { @@ -172,19 +260,15 @@ aidl_interface { "binding/aidl/aaudio/IAAudioService.aidl", ], imports: [ - "audio_common-aidl", + "android.media.audio.common.types-V1", + "audioclient-types-aidl", "shared-file-region-aidl", - "framework-permission-aidl" + "framework-permission-aidl", ], backend: { - cpp: { - enabled: true, - }, java: { - // TODO: need to have audio_common-aidl available in Java to enable - // this. - enabled: false, + sdk_version: "module_current", }, }, } diff --git a/media/libaaudio/src/binding/AAudioBinderAdapter.cpp b/media/libaaudio/src/binding/AAudioBinderAdapter.cpp index 6e3a1c8f4b1ec6299e6eb00ef6dac8f78815244b..42d81ca47e71ea929b9b09277ae68e896a0d73ee 100644 --- a/media/libaaudio/src/binding/AAudioBinderAdapter.cpp +++ b/media/libaaudio/src/binding/AAudioBinderAdapter.cpp @@ -124,4 +124,16 @@ aaudio_result_t AAudioBinderAdapter::unregisterAudioThread(aaudio_handle_t strea return result; } +aaudio_result_t AAudioBinderAdapter::exitStandby(aaudio_handle_t streamHandle, + AudioEndpointParcelable &endpointOut) { + aaudio_result_t result; + Endpoint endpoint; + Status status = mDelegate->exitStandby(streamHandle, &endpoint, &result); + if (!status.isOk()) { + result = AAudioConvert_androidToAAudioResult(statusTFromBinderStatus(status)); + } + endpointOut = std::move(endpoint); + return result; +} + } // namespace aaudio diff --git a/media/libaaudio/src/binding/AAudioBinderAdapter.h b/media/libaaudio/src/binding/AAudioBinderAdapter.h index 5e9ab57f1ed7b62f4ac8fb0d44788419e71b3565..d1707839d5450dc16206273eb4eaa4fa3d4eafd7 100644 --- a/media/libaaudio/src/binding/AAudioBinderAdapter.h +++ b/media/libaaudio/src/binding/AAudioBinderAdapter.h @@ -57,6 +57,9 @@ public: aaudio_result_t unregisterAudioThread(aaudio_handle_t streamHandle, pid_t clientThreadId) override; + aaudio_result_t exitStandby(aaudio_handle_t streamHandle, + AudioEndpointParcelable &parcelable) override; + private: IAAudioService* const mDelegate; }; diff --git a/media/libaaudio/src/binding/AAudioBinderClient.cpp b/media/libaaudio/src/binding/AAudioBinderClient.cpp index fa5a2da4f843a32aeaf1a677845b2107bdd690cc..8e5facc5e0f83f4050543f04183c0bb77c1c0426 100644 --- a/media/libaaudio/src/binding/AAudioBinderClient.cpp +++ b/media/libaaudio/src/binding/AAudioBinderClient.cpp @@ -36,13 +36,10 @@ using android::String16; using android::IServiceManager; using android::defaultServiceManager; using android::interface_cast; -using android::IInterface; using android::Mutex; using android::ProcessState; using android::sp; using android::status_t; -using android::wp; -using android::binder::Status; using namespace aaudio; @@ -93,7 +90,7 @@ std::shared_ptr AAudioBinderClient::getAAudioService() { ALOGE("%s() - linkToDeath() returned %d", __func__, status); } aaudioService = interface_cast(binder); - mAdapter.reset(new Adapter(aaudioService, mAAudioClient)); + mAdapter = std::make_shared(aaudioService, mAAudioClient); needToRegister = true; // Make sure callbacks can be received by mAAudioClient ProcessState::self()->startThreadPool(); @@ -204,3 +201,11 @@ aaudio_result_t AAudioBinderClient::unregisterAudioThread(aaudio_handle_t stream return service->unregisterAudioThread(streamHandle, clientThreadId); } + +aaudio_result_t AAudioBinderClient::exitStandby(aaudio_handle_t streamHandle, + AudioEndpointParcelable &endpointOut) { + std::shared_ptr service = getAAudioService(); + if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE; + + return service->exitStandby(streamHandle, endpointOut); +} diff --git a/media/libaaudio/src/binding/AAudioBinderClient.h b/media/libaaudio/src/binding/AAudioBinderClient.h index 6a7b6395682903fa4e40f95b6920274a58273c21..0968f4cdfca898cb48fe15cdfecf4f4487b6e326 100644 --- a/media/libaaudio/src/binding/AAudioBinderClient.h +++ b/media/libaaudio/src/binding/AAudioBinderClient.h @@ -108,7 +108,10 @@ public: return AAUDIO_ERROR_UNAVAILABLE; } - void onStreamChange(aaudio_handle_t handle, int32_t opcode, int32_t value) { + aaudio_result_t exitStandby(aaudio_handle_t streamHandle, + AudioEndpointParcelable &endpointOut) override; + + void onStreamChange(aaudio_handle_t /*handle*/, int32_t /*opcode*/, int32_t /*value*/) { // TODO This is just a stub so we can have a client Binder to pass to the service. // TODO Implemented in a later CL. ALOGW("onStreamChange called!"); @@ -116,7 +119,7 @@ public: class AAudioClient : public android::IBinder::DeathRecipient, public BnAAudioClient { public: - AAudioClient(android::wp aaudioBinderClient) + explicit AAudioClient(const android::wp& aaudioBinderClient) : mBinderClient(aaudioBinderClient) { } @@ -150,10 +153,10 @@ public: class Adapter : public AAudioBinderAdapter { public: Adapter(const android::sp& delegate, - const android::sp& aaudioClient) + android::sp aaudioClient) : AAudioBinderAdapter(delegate.get()), mDelegate(delegate), - mAAudioClient(aaudioClient) {} + mAAudioClient(std::move(aaudioClient)) {} virtual ~Adapter() { if (mDelegate != nullptr) { diff --git a/media/libaaudio/src/binding/AAudioServiceInterface.h b/media/libaaudio/src/binding/AAudioServiceInterface.h index 5d11512e03529a12d95fe215b29763b2aa29c9d4..e901767eb95693156546dc8076d5b5e0f1194085 100644 --- a/media/libaaudio/src/binding/AAudioServiceInterface.h +++ b/media/libaaudio/src/binding/AAudioServiceInterface.h @@ -37,7 +37,7 @@ namespace aaudio { class AAudioServiceInterface { public: - AAudioServiceInterface() {}; + AAudioServiceInterface() = default; virtual ~AAudioServiceInterface() = default; virtual void registerClient(const android::sp& client) = 0; @@ -95,6 +95,16 @@ public: virtual aaudio_result_t stopClient(aaudio_handle_t streamHandle, audio_port_handle_t clientHandle) = 0; + + /** + * Exit the standby mode. + * + * @param streamHandle the stream handle + * @param parcelable contains new data queue information + * @return the result of the execution + */ + virtual aaudio_result_t exitStandby(aaudio_handle_t streamHandle, + AudioEndpointParcelable &parcelable) = 0; }; } /* namespace aaudio */ diff --git a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp index bec439339377cbd77228f4610ab62ca69ba059c3..b60bac2f034d39b189843afc38894f853869bc21 100644 --- a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp +++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp @@ -23,11 +23,13 @@ #include #include +#include + #include "binding/AAudioStreamConfiguration.h" using namespace aaudio; -using android::media::audio::common::AudioFormat; +using android::media::audio::common::AudioFormatDescription; AAudioStreamConfiguration::AAudioStreamConfiguration(const StreamParameters& parcelable) { setChannelMask(parcelable.channelMask); @@ -35,8 +37,9 @@ AAudioStreamConfiguration::AAudioStreamConfiguration(const StreamParameters& par setDeviceId(parcelable.deviceId); static_assert(sizeof(aaudio_sharing_mode_t) == sizeof(parcelable.sharingMode)); setSharingMode(parcelable.sharingMode); - static_assert(sizeof(audio_format_t) == sizeof(parcelable.audioFormat)); - setFormat(static_cast(parcelable.audioFormat)); + auto convFormat = android::aidl2legacy_AudioFormatDescription_audio_format_t( + parcelable.audioFormat); + setFormat(convFormat.ok() ? convFormat.value() : AUDIO_FORMAT_INVALID); static_assert(sizeof(aaudio_direction_t) == sizeof(parcelable.direction)); setDirection(parcelable.direction); static_assert(sizeof(audio_usage_t) == sizeof(parcelable.usage)); @@ -75,8 +78,14 @@ StreamParameters AAudioStreamConfiguration::parcelable() const { result.deviceId = getDeviceId(); static_assert(sizeof(aaudio_sharing_mode_t) == sizeof(result.sharingMode)); result.sharingMode = getSharingMode(); - static_assert(sizeof(audio_format_t) == sizeof(result.audioFormat)); - result.audioFormat = static_cast(getFormat()); + auto convAudioFormat = android::legacy2aidl_audio_format_t_AudioFormatDescription(getFormat()); + if (convAudioFormat.ok()) { + result.audioFormat = convAudioFormat.value(); + } else { + result.audioFormat = AudioFormatDescription{}; + result.audioFormat.type = + android::media::audio::common::AudioFormatType::SYS_RESERVED_INVALID; + } static_assert(sizeof(aaudio_direction_t) == sizeof(result.direction)); result.direction = getDirection(); static_assert(sizeof(audio_usage_t) == sizeof(result.usage)); diff --git a/media/libaaudio/src/binding/AAudioStreamRequest.cpp b/media/libaaudio/src/binding/AAudioStreamRequest.cpp index 8d90034dea9468392ddf0a9385fc11a761402563..a4cc2bd9cbe54081aef0f4719eaf1cbf98c9136d 100644 --- a/media/libaaudio/src/binding/AAudioStreamRequest.cpp +++ b/media/libaaudio/src/binding/AAudioStreamRequest.cpp @@ -30,7 +30,7 @@ using namespace aaudio; AAudioStreamRequest::AAudioStreamRequest(const StreamRequest& parcelable) : - mConfiguration(std::move(parcelable.params)), + mConfiguration(parcelable.params), mAttributionSource(parcelable.attributionSource), mSharingModeMatchRequired(parcelable.sharingModeMatchRequired), mInService(parcelable.inService) { @@ -38,7 +38,7 @@ AAudioStreamRequest::AAudioStreamRequest(const StreamRequest& parcelable) : StreamRequest AAudioStreamRequest::parcelable() const { StreamRequest result; - result.params = std::move(mConfiguration).parcelable(); + result.params = mConfiguration.parcelable(); result.attributionSource = mAttributionSource; result.sharingModeMatchRequired = mSharingModeMatchRequired; result.inService = mInService; diff --git a/media/libaaudio/src/binding/AudioEndpointParcelable.cpp b/media/libaaudio/src/binding/AudioEndpointParcelable.cpp index aa4ac273fc982b54799b3e9167e091d3f409d879..b1262df8e78479e326713f598c28dbda087c6fb7 100644 --- a/media/libaaudio/src/binding/AudioEndpointParcelable.cpp +++ b/media/libaaudio/src/binding/AudioEndpointParcelable.cpp @@ -29,17 +29,15 @@ #include "binding/AudioEndpointParcelable.h" using android::base::unique_fd; -using android::media::SharedFileRegion; -using android::NO_ERROR; using android::status_t; using namespace aaudio; AudioEndpointParcelable::AudioEndpointParcelable(Endpoint&& parcelable) - : mUpMessageQueueParcelable(std::move(parcelable.upMessageQueueParcelable)), - mDownMessageQueueParcelable(std::move(parcelable.downMessageQueueParcelable)), - mUpDataQueueParcelable(std::move(parcelable.upDataQueueParcelable)), - mDownDataQueueParcelable(std::move(parcelable.downDataQueueParcelable)), + : mUpMessageQueueParcelable(parcelable.upMessageQueueParcelable), + mDownMessageQueueParcelable(parcelable.downMessageQueueParcelable), + mUpDataQueueParcelable(parcelable.upDataQueueParcelable), + mDownDataQueueParcelable(parcelable.downDataQueueParcelable), mNumSharedMemories(parcelable.sharedMemories.size()) { for (size_t i = 0; i < parcelable.sharedMemories.size() && i < MAX_SHARED_MEMORIES; ++i) { // Re-construct. @@ -56,10 +54,10 @@ AudioEndpointParcelable& AudioEndpointParcelable::operator=(Endpoint&& parcelabl Endpoint AudioEndpointParcelable::parcelable()&& { Endpoint result; - result.upMessageQueueParcelable = std::move(mUpMessageQueueParcelable).parcelable(); - result.downMessageQueueParcelable = std::move(mDownMessageQueueParcelable).parcelable(); - result.upDataQueueParcelable = std::move(mUpDataQueueParcelable).parcelable(); - result.downDataQueueParcelable = std::move(mDownDataQueueParcelable).parcelable(); + result.upMessageQueueParcelable = mUpMessageQueueParcelable.parcelable(); + result.downMessageQueueParcelable = mDownMessageQueueParcelable.parcelable(); + result.upDataQueueParcelable = mUpDataQueueParcelable.parcelable(); + result.downDataQueueParcelable = mDownDataQueueParcelable.parcelable(); result.sharedMemories.reserve(std::min(mNumSharedMemories, MAX_SHARED_MEMORIES)); for (size_t i = 0; i < mNumSharedMemories && i < MAX_SHARED_MEMORIES; ++i) { result.sharedMemories.emplace_back(std::move(mSharedMemories[i]).parcelable()); @@ -81,6 +79,22 @@ int32_t AudioEndpointParcelable::addFileDescriptor(const unique_fd& fd, return index; } +void AudioEndpointParcelable::closeDataFileDescriptor() { + const int32_t curDataMemoryIndex = mDownDataQueueParcelable.getSharedMemoryIndex(); + mSharedMemories[curDataMemoryIndex].closeAndReleaseFd(); +} + +void AudioEndpointParcelable::updateDataFileDescriptor( + AudioEndpointParcelable* endpointParcelable) { + const int32_t curDataMemoryIndex = mDownDataQueueParcelable.getSharedMemoryIndex(); + const int32_t newDataMemoryIndex = + endpointParcelable->mDownDataQueueParcelable.getSharedMemoryIndex(); + mSharedMemories[curDataMemoryIndex].close(); + mSharedMemories[curDataMemoryIndex].setup( + endpointParcelable->mSharedMemories[newDataMemoryIndex]); + mDownDataQueueParcelable.updateMemory(endpointParcelable->mDownDataQueueParcelable); +} + aaudio_result_t AudioEndpointParcelable::resolve(EndpointDescriptor *descriptor) { aaudio_result_t result = mUpMessageQueueParcelable.resolve(mSharedMemories, &descriptor->upMessageQueueDescriptor); @@ -94,6 +108,10 @@ aaudio_result_t AudioEndpointParcelable::resolve(EndpointDescriptor *descriptor) return result; } +aaudio_result_t AudioEndpointParcelable::resolveDataQueue(RingBufferDescriptor *descriptor) { + return mDownDataQueueParcelable.resolve(mSharedMemories, descriptor); +} + aaudio_result_t AudioEndpointParcelable::close() { int err = 0; for (int i = 0; i < mNumSharedMemories; i++) { diff --git a/media/libaaudio/src/binding/AudioEndpointParcelable.h b/media/libaaudio/src/binding/AudioEndpointParcelable.h index 5237a1ab646c5b9a2d6b6fc3e7163ebad14feaf9..5d2c38f54d228f33155b5e95ec8e8831acf8e749 100644 --- a/media/libaaudio/src/binding/AudioEndpointParcelable.h +++ b/media/libaaudio/src/binding/AudioEndpointParcelable.h @@ -43,7 +43,7 @@ public: // Ctor/assignment from a parcelable representation. // Since the parcelable object owns unique FDs (for shared memory blocks), move semantics are // provided to avoid the need to dupe. - AudioEndpointParcelable(Endpoint&& parcelable); + explicit AudioEndpointParcelable(Endpoint&& parcelable); AudioEndpointParcelable& operator=(Endpoint&& parcelable); /** @@ -52,7 +52,20 @@ public: */ int32_t addFileDescriptor(const android::base::unique_fd& fd, int32_t sizeInBytes); + /** + * Close current data file descriptor. The duplicated file descriptor will be close. + */ + void closeDataFileDescriptor(); + + /** + * Update current data file descriptor with given endpoint parcelable. + * @param endpointParcelable an endpoint parcelable that contains new data file + * descriptor information + */ + void updateDataFileDescriptor(AudioEndpointParcelable* endpointParcelable); + aaudio_result_t resolve(EndpointDescriptor *descriptor); + aaudio_result_t resolveDataQueue(RingBufferDescriptor *descriptor); aaudio_result_t close(); diff --git a/media/libaaudio/src/binding/RingBufferParcelable.cpp b/media/libaaudio/src/binding/RingBufferParcelable.cpp index a4b3cecc277a98d10b745a90cc493a0c1589b72e..3bc51d08baf3368097e28487caed8fb0096f939a 100644 --- a/media/libaaudio/src/binding/RingBufferParcelable.cpp +++ b/media/libaaudio/src/binding/RingBufferParcelable.cpp @@ -30,9 +30,10 @@ using namespace aaudio; RingBufferParcelable::RingBufferParcelable(const RingBuffer& parcelable) - : mReadCounterParcelable(std::move(parcelable.readCounterParcelable)), - mWriteCounterParcelable(std::move(parcelable.writeCounterParcelable)), - mDataParcelable(std::move(parcelable.dataParcelable)), + : mReadCounterParcelable(parcelable.readCounterParcelable), + mWriteCounterParcelable(parcelable.writeCounterParcelable), + mDataParcelable(parcelable.dataParcelable), + mSharedMemoryIndex(parcelable.sharedMemoryIndex), mBytesPerFrame(parcelable.bytesPerFrame), mFramesPerBurst(parcelable.framesPerBurst), mCapacityInFrames(parcelable.capacityInFrames), @@ -42,9 +43,10 @@ RingBufferParcelable::RingBufferParcelable(const RingBuffer& parcelable) RingBuffer RingBufferParcelable::parcelable() const { RingBuffer result; - result.readCounterParcelable = std::move(mReadCounterParcelable).parcelable(); - result.writeCounterParcelable = std::move(mWriteCounterParcelable).parcelable(); - result.dataParcelable = std::move(mDataParcelable).parcelable(); + result.readCounterParcelable = mReadCounterParcelable.parcelable(); + result.writeCounterParcelable = mWriteCounterParcelable.parcelable(); + result.dataParcelable = mDataParcelable.parcelable(); + result.sharedMemoryIndex = mSharedMemoryIndex; result.bytesPerFrame = mBytesPerFrame; result.framesPerBurst = mFramesPerBurst; result.capacityInFrames = mCapacityInFrames; @@ -60,6 +62,7 @@ void RingBufferParcelable::setupMemory(int32_t sharedMemoryIndex, int32_t readCounterOffset, int32_t writeCounterOffset, int32_t counterSizeBytes) { + mSharedMemoryIndex = sharedMemoryIndex; mReadCounterParcelable.setup(sharedMemoryIndex, readCounterOffset, counterSizeBytes); mWriteCounterParcelable.setup(sharedMemoryIndex, writeCounterOffset, counterSizeBytes); mDataParcelable.setup(sharedMemoryIndex, dataMemoryOffset, dataSizeInBytes); @@ -68,12 +71,13 @@ void RingBufferParcelable::setupMemory(int32_t sharedMemoryIndex, void RingBufferParcelable::setupMemory(int32_t sharedMemoryIndex, int32_t dataMemoryOffset, int32_t dataSizeInBytes) { + mSharedMemoryIndex = sharedMemoryIndex; mReadCounterParcelable.setup(sharedMemoryIndex, 0, 0); mWriteCounterParcelable.setup(sharedMemoryIndex, 0, 0); mDataParcelable.setup(sharedMemoryIndex, dataMemoryOffset, dataSizeInBytes); } -int32_t RingBufferParcelable::getBytesPerFrame() { +int32_t RingBufferParcelable::getBytesPerFrame() const { return mBytesPerFrame; } @@ -81,7 +85,7 @@ void RingBufferParcelable::setBytesPerFrame(int32_t bytesPerFrame) { mBytesPerFrame = bytesPerFrame; } -int32_t RingBufferParcelable::getFramesPerBurst() { +int32_t RingBufferParcelable::getFramesPerBurst() const { return mFramesPerBurst; } @@ -89,7 +93,7 @@ void RingBufferParcelable::setFramesPerBurst(int32_t framesPerBurst) { mFramesPerBurst = framesPerBurst; } -int32_t RingBufferParcelable::getCapacityInFrames() { +int32_t RingBufferParcelable::getCapacityInFrames() const { return mCapacityInFrames; } @@ -124,6 +128,14 @@ aaudio_result_t RingBufferParcelable::resolve(SharedMemoryParcelable *memoryParc return AAUDIO_OK; } +void RingBufferParcelable::updateMemory(const RingBufferParcelable& parcelable) { + setupMemory(mSharedMemoryIndex, 0, + parcelable.getCapacityInFrames() * parcelable.getBytesPerFrame()); + setBytesPerFrame(parcelable.getBytesPerFrame()); + setFramesPerBurst(parcelable.getFramesPerBurst()); + setCapacityInFrames(parcelable.getCapacityInFrames()); +} + aaudio_result_t RingBufferParcelable::validate() const { if (mCapacityInFrames < 0 || mCapacityInFrames >= 32 * 1024) { ALOGE("invalid mCapacityInFrames = %d", mCapacityInFrames); diff --git a/media/libaaudio/src/binding/RingBufferParcelable.h b/media/libaaudio/src/binding/RingBufferParcelable.h index 2508ceabe332db10538dab7dee16bb0ad5e773cc..29d0d862f09c35a08a06f5967b242ac4130a126c 100644 --- a/media/libaaudio/src/binding/RingBufferParcelable.h +++ b/media/libaaudio/src/binding/RingBufferParcelable.h @@ -46,15 +46,15 @@ public: int32_t dataMemoryOffset, int32_t dataSizeInBytes); - int32_t getBytesPerFrame(); + int32_t getBytesPerFrame() const; void setBytesPerFrame(int32_t bytesPerFrame); - int32_t getFramesPerBurst(); + int32_t getFramesPerBurst() const; void setFramesPerBurst(int32_t framesPerBurst); - int32_t getCapacityInFrames(); + int32_t getCapacityInFrames() const; void setCapacityInFrames(int32_t capacityInFrames); @@ -62,6 +62,12 @@ public: aaudio_result_t resolve(SharedMemoryParcelable *memoryParcels, RingBufferDescriptor *descriptor); + void updateMemory(const RingBufferParcelable& parcelable); + + int32_t getSharedMemoryIndex() const { + return mSharedMemoryIndex; + } + void dump(); // Extract a parcelable representation of this object. @@ -71,6 +77,7 @@ private: SharedRegionParcelable mReadCounterParcelable; SharedRegionParcelable mWriteCounterParcelable; SharedRegionParcelable mDataParcelable; + int32_t mSharedMemoryIndex = -1; int32_t mBytesPerFrame = 0; // index is in frames int32_t mFramesPerBurst = 0; // for ISOCHRONOUS queues int32_t mCapacityInFrames = 0; // zero if unused diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.cpp b/media/libaaudio/src/binding/SharedMemoryParcelable.cpp index eef238f4b45523b26a91854784d542d156d1c7c7..741aefca30098c09debff16c49709e6cb1810e52 100644 --- a/media/libaaudio/src/binding/SharedMemoryParcelable.cpp +++ b/media/libaaudio/src/binding/SharedMemoryParcelable.cpp @@ -32,7 +32,6 @@ #include "binding/SharedMemoryParcelable.h" using android::base::unique_fd; -using android::NO_ERROR; using android::status_t; using android::media::SharedFileRegion; @@ -65,6 +64,10 @@ void SharedMemoryParcelable::setup(const unique_fd& fd, int32_t sizeInBytes) { mSizeInBytes = sizeInBytes; } +void SharedMemoryParcelable::setup(const SharedMemoryParcelable &sharedMemoryParcelable) { + setup(sharedMemoryParcelable.mFd, sharedMemoryParcelable.mSizeInBytes); +} + aaudio_result_t SharedMemoryParcelable::close() { if (mResolvedAddress != MMAP_UNRESOLVED_ADDRESS) { int err = munmap(mResolvedAddress, mSizeInBytes); @@ -77,8 +80,16 @@ aaudio_result_t SharedMemoryParcelable::close() { return AAUDIO_OK; } +aaudio_result_t SharedMemoryParcelable::closeAndReleaseFd() { + aaudio_result_t result = close(); + if (result == AAUDIO_OK) { + mFd.reset(); + } + return result; +} + aaudio_result_t SharedMemoryParcelable::resolveSharedMemory(const unique_fd& fd) { - mResolvedAddress = (uint8_t *) mmap(0, mSizeInBytes, PROT_READ | PROT_WRITE, + mResolvedAddress = (uint8_t *) mmap(nullptr, mSizeInBytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0); if (mResolvedAddress == MMAP_UNRESOLVED_ADDRESS) { ALOGE("mmap() failed for fd = %d, nBytes = %" PRId64 ", errno = %s", diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.h b/media/libaaudio/src/binding/SharedMemoryParcelable.h index 1f2c3350424e5415024fb5f917fa4184f62b8b28..7762fef5caa973b28db818e32416b819cb0d70d5 100644 --- a/media/libaaudio/src/binding/SharedMemoryParcelable.h +++ b/media/libaaudio/src/binding/SharedMemoryParcelable.h @@ -52,12 +52,16 @@ public: */ void setup(const android::base::unique_fd& fd, int32_t sizeInBytes); + void setup(const SharedMemoryParcelable& sharedMemoryParcelable); + // mmap() shared memory aaudio_result_t resolve(int32_t offsetInBytes, int32_t sizeInBytes, void **regionAddressPtr); // munmap() any mapped memory aaudio_result_t close(); + aaudio_result_t closeAndReleaseFd(); + int32_t getSizeInBytes(); void dump(); diff --git a/media/libaaudio/src/binding/SharedRegionParcelable.cpp b/media/libaaudio/src/binding/SharedRegionParcelable.cpp index 56b99c07874247789084869c3c806f516b563c3a..6fa109b33d5531559b5f2957b905e818c5bb9ef6 100644 --- a/media/libaaudio/src/binding/SharedRegionParcelable.cpp +++ b/media/libaaudio/src/binding/SharedRegionParcelable.cpp @@ -29,10 +29,7 @@ #include "binding/SharedMemoryParcelable.h" #include "binding/SharedRegionParcelable.h" -using android::NO_ERROR; using android::status_t; -using android::Parcel; -using android::Parcelable; using namespace aaudio; diff --git a/media/libaaudio/src/binding/aidl/aaudio/IAAudioService.aidl b/media/libaaudio/src/binding/aidl/aaudio/IAAudioService.aidl index 44d2211a1f31f2d181154647909d2f64e67661b8..485c2e288cd2510172df507fdc1e30c1c03a9a18 100644 --- a/media/libaaudio/src/binding/aidl/aaudio/IAAudioService.aidl +++ b/media/libaaudio/src/binding/aidl/aaudio/IAAudioService.aidl @@ -78,4 +78,6 @@ interface IAAudioService { int unregisterAudioThread(int streamHandle, int clientThreadId); + + int exitStandby(int streamHandle, out Endpoint endpoint); } diff --git a/media/libaaudio/src/binding/aidl/aaudio/RingBuffer.aidl b/media/libaaudio/src/binding/aidl/aaudio/RingBuffer.aidl index a58b33adf2245607201bce4c083db9a5454efd85..dd6449396294f049a9927b6fc76d497018283223 100644 --- a/media/libaaudio/src/binding/aidl/aaudio/RingBuffer.aidl +++ b/media/libaaudio/src/binding/aidl/aaudio/RingBuffer.aidl @@ -26,4 +26,5 @@ parcelable RingBuffer { int framesPerBurst; // for ISOCHRONOUS queues int capacityInFrames; // zero if unused int /* RingbufferFlags */ flags; // = RingbufferFlags::NONE; + int sharedMemoryIndex; } \ No newline at end of file diff --git a/media/libaaudio/src/binding/aidl/aaudio/StreamParameters.aidl b/media/libaaudio/src/binding/aidl/aaudio/StreamParameters.aidl index a6541e19269005a71d61487e58cebb1cc17959ed..983e1937f66dc6601b40a6cdc2b7323698dc64a6 100644 --- a/media/libaaudio/src/binding/aidl/aaudio/StreamParameters.aidl +++ b/media/libaaudio/src/binding/aidl/aaudio/StreamParameters.aidl @@ -16,14 +16,14 @@ package aaudio; -import android.media.audio.common.AudioFormat; +import android.media.audio.common.AudioFormatDescription; parcelable StreamParameters { int channelMask; // = AAUDIO_UNSPECIFIED; int sampleRate; // = AAUDIO_UNSPECIFIED; int deviceId; // = AAUDIO_UNSPECIFIED; int /* aaudio_sharing_mode_t */ sharingMode; // = AAUDIO_SHARING_MODE_SHARED; - AudioFormat audioFormat; // = AUDIO_FORMAT_DEFAULT; + AudioFormatDescription audioFormat; // = AUDIO_FORMAT_DEFAULT; int /* aaudio_direction_t */ direction; // = AAUDIO_DIRECTION_OUTPUT; int /* aaudio_usage_t */ usage; // = AAUDIO_UNSPECIFIED; int /* aaudio_content_type_t */ contentType; // = AAUDIO_UNSPECIFIED; diff --git a/media/libaaudio/src/client/AAudioFlowGraph.cpp b/media/libaaudio/src/client/AAudioFlowGraph.cpp index 61b50f3fa8e6217e06ffb5bd390f06d62a4bd2bd..2ed3e3c2e0b5ab2efdfea780dbfdde60bfb8d3fd 100644 --- a/media/libaaudio/src/client/AAudioFlowGraph.cpp +++ b/media/libaaudio/src/client/AAudioFlowGraph.cpp @@ -21,7 +21,10 @@ #include "AAudioFlowGraph.h" #include +#include +#include #include +#include #include #include #include @@ -32,17 +35,22 @@ #include #include -using namespace flowgraph; +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; aaudio_result_t AAudioFlowGraph::configure(audio_format_t sourceFormat, int32_t sourceChannelCount, audio_format_t sinkFormat, - int32_t sinkChannelCount) { - AudioFloatOutputPort *lastOutput = nullptr; + int32_t sinkChannelCount, + bool useMonoBlend, + float audioBalance, + bool isExclusive) { + FlowGraphPortFloatOutput *lastOutput = nullptr; // TODO change back to ALOGD - ALOGI("%s() source format = 0x%08x, channels = %d, sink format = 0x%08x, channels = %d", - __func__, sourceFormat, sourceChannelCount, sinkFormat, sinkChannelCount); + ALOGI("%s() source format = 0x%08x, channels = %d, sink format = 0x%08x, channels = %d, " + "useMonoBlend = %d, audioBalance = %f, isExclusive %d", + __func__, sourceFormat, sourceChannelCount, sinkFormat, sinkChannelCount, + useMonoBlend, audioBalance, isExclusive); switch (sourceFormat) { case AUDIO_FORMAT_PCM_FLOAT: @@ -63,10 +71,11 @@ aaudio_result_t AAudioFlowGraph::configure(audio_format_t sourceFormat, } lastOutput = &mSource->output; - // Apply volume as a ramp to avoid pops. - mVolumeRamp = std::make_unique(sourceChannelCount); - lastOutput->connect(&mVolumeRamp->input); - lastOutput = &mVolumeRamp->output; + if (useMonoBlend) { + mMonoBlend = std::make_unique(sourceChannelCount); + lastOutput->connect(&mMonoBlend->input); + lastOutput = &mMonoBlend->output; + } // For a pure float graph, there is chance that the data range may be very large. // So we should clip to a reasonable value that allows a little headroom. @@ -86,6 +95,26 @@ aaudio_result_t AAudioFlowGraph::configure(audio_format_t sourceFormat, return AAUDIO_ERROR_UNIMPLEMENTED; } + // Apply volume ramps for only exclusive streams. + if (isExclusive) { + // Apply volume ramps to set the left/right audio balance and target volumes. + // The signals will be decoupled, volume ramps will be applied, before the signals are + // combined again. + mMultiToManyConverter = std::make_unique(sinkChannelCount); + mManyToMultiConverter = std::make_unique(sinkChannelCount); + lastOutput->connect(&mMultiToManyConverter->input); + for (int i = 0; i < sinkChannelCount; i++) { + mVolumeRamps.emplace_back(std::make_unique(1)); + mPanningVolumes.emplace_back(1.0f); + lastOutput = mMultiToManyConverter->outputs[i].get(); + lastOutput->connect(&(mVolumeRamps[i].get()->input)); + lastOutput = &(mVolumeRamps[i].get()->output); + lastOutput->connect(mManyToMultiConverter->inputs[i].get()); + } + lastOutput = &mManyToMultiConverter->output; + setAudioBalance(audioBalance); + } + switch (sinkFormat) { case AUDIO_FORMAT_PCM_FLOAT: mSink = std::make_unique(sinkChannelCount); @@ -117,9 +146,32 @@ void AAudioFlowGraph::process(const void *source, void *destination, int32_t num * @param volume between 0.0 and 1.0 */ void AAudioFlowGraph::setTargetVolume(float volume) { - mVolumeRamp->setTarget(volume); + for (int i = 0; i < mVolumeRamps.size(); i++) { + mVolumeRamps[i]->setTarget(volume * mPanningVolumes[i]); + } + mTargetVolume = volume; +} + +/** + * @param audioBalance between -1.0 and 1.0 + */ +void AAudioFlowGraph::setAudioBalance(float audioBalance) { + if (mPanningVolumes.size() >= 2) { + float leftMultiplier = 0; + float rightMultiplier = 0; + mBalance.computeStereoBalance(audioBalance, &leftMultiplier, &rightMultiplier); + mPanningVolumes[0] = leftMultiplier; + mPanningVolumes[1] = rightMultiplier; + mVolumeRamps[0]->setTarget(mTargetVolume * leftMultiplier); + mVolumeRamps[1]->setTarget(mTargetVolume * rightMultiplier); + } } +/** + * @param numFrames to slowly adjust for volume changes + */ void AAudioFlowGraph::setRampLengthInFrames(int32_t numFrames) { - mVolumeRamp->setLengthInFrames(numFrames); + for (auto& ramp : mVolumeRamps) { + ramp->setLengthInFrames(numFrames); + } } diff --git a/media/libaaudio/src/client/AAudioFlowGraph.h b/media/libaaudio/src/client/AAudioFlowGraph.h index a49f64ebfe313996f1be3ade3198f090cee0b8fb..602c17f2457d329e5c4caeaa91f32e293978faf2 100644 --- a/media/libaaudio/src/client/AAudioFlowGraph.h +++ b/media/libaaudio/src/client/AAudioFlowGraph.h @@ -23,8 +23,12 @@ #include #include +#include #include +#include +#include #include +#include #include class AAudioFlowGraph { @@ -36,12 +40,19 @@ public: * @param sourceChannelCount * @param sinkFormat * @param sinkChannelCount + * @param useMonoBlend + * @param audioBalance + * @param channelMask + * @param isExclusive * @return */ aaudio_result_t configure(audio_format_t sourceFormat, int32_t sourceChannelCount, audio_format_t sinkFormat, - int32_t sinkChannelCount); + int32_t sinkChannelCount, + bool useMonoBlend, + float audioBalance, + bool isExclusive); void process(const void *source, void *destination, int32_t numFrames); @@ -50,14 +61,30 @@ public: */ void setTargetVolume(float volume); + /** + * @param audioBalance between -1.0 and 1.0 + */ + void setAudioBalance(float audioBalance); + + /** + * @param numFrames to slowly adjust for volume changes + */ void setRampLengthInFrames(int32_t numFrames); private: - std::unique_ptr mSource; - std::unique_ptr mVolumeRamp; - std::unique_ptr mClipper; - std::unique_ptr mChannelConverter; - std::unique_ptr mSink; + std::unique_ptr mSource; + std::unique_ptr mMonoBlend; + std::unique_ptr mClipper; + std::unique_ptr mChannelConverter; + std::unique_ptr + mManyToMultiConverter; + std::unique_ptr + mMultiToManyConverter; + std::vector> mVolumeRamps; + std::vector mPanningVolumes; + float mTargetVolume = 1.0f; + android::audio_utils::Balance mBalance; + std::unique_ptr mSink; }; diff --git a/media/libaaudio/src/client/AudioEndpoint.cpp b/media/libaaudio/src/client/AudioEndpoint.cpp index ebc9f2b6ecab81d5970b79d9b00fc5845f22034e..e780f4f96652f51ea6ee6120d026d28a2fc3f1aa 100644 --- a/media/libaaudio/src/client/AudioEndpoint.cpp +++ b/media/libaaudio/src/client/AudioEndpoint.cpp @@ -31,13 +31,6 @@ using namespace aaudio; #define RIDICULOUSLY_LARGE_BUFFER_CAPACITY (256 * 1024) #define RIDICULOUSLY_LARGE_FRAME_SIZE 4096 -AudioEndpoint::AudioEndpoint() - : mFreeRunning(false) - , mDataReadCounter(0) - , mDataWriteCounter(0) -{ -} - // TODO Consider moving to a method in RingBufferDescriptor static aaudio_result_t AudioEndpoint_validateQueueDescriptor(const char *type, const RingBufferDescriptor *descriptor) { @@ -146,38 +139,49 @@ aaudio_result_t AudioEndpoint::configure(const EndpointDescriptor *pEndpointDesc ); // ============================ data queue ============================= - descriptor = &pEndpointDescriptor->dataQueueDescriptor; - ALOGV("configure() data framesPerBurst = %d", descriptor->framesPerBurst); + result = configureDataQueue(pEndpointDescriptor->dataQueueDescriptor, direction); + + return result; +} + +aaudio_result_t AudioEndpoint::configureDataQueue(const RingBufferDescriptor& descriptor, + aaudio_direction_t direction) { + aaudio_result_t result = AudioEndpoint_validateQueueDescriptor("data", &descriptor); + if (result != AAUDIO_OK) { + return result; + } + + ALOGV("configure() data framesPerBurst = %d", descriptor.framesPerBurst); ALOGV("configure() data readCounterAddress = %p", - descriptor->readCounterAddress); + descriptor.readCounterAddress); // An example of free running is when the other side is read or written by hardware DMA // or a DSP. It does not update its counter so we have to update it. int64_t *remoteCounter = (direction == AAUDIO_DIRECTION_OUTPUT) - ? descriptor->readCounterAddress // read by other side - : descriptor->writeCounterAddress; // written by other side + ? descriptor.readCounterAddress // read by other side + : descriptor.writeCounterAddress; // written by other side mFreeRunning = (remoteCounter == nullptr); ALOGV("configure() mFreeRunning = %d", mFreeRunning ? 1 : 0); - int64_t *readCounterAddress = (descriptor->readCounterAddress == nullptr) + int64_t *readCounterAddress = (descriptor.readCounterAddress == nullptr) ? &mDataReadCounter - : descriptor->readCounterAddress; - int64_t *writeCounterAddress = (descriptor->writeCounterAddress == nullptr) + : descriptor.readCounterAddress; + int64_t *writeCounterAddress = (descriptor.writeCounterAddress == nullptr) ? &mDataWriteCounter - : descriptor->writeCounterAddress; + : descriptor.writeCounterAddress; // Clear buffer to avoid an initial glitch on some devices. - size_t bufferSizeBytes = descriptor->capacityInFrames * descriptor->bytesPerFrame; - memset(descriptor->dataAddress, 0, bufferSizeBytes); + size_t bufferSizeBytes = descriptor.capacityInFrames * descriptor.bytesPerFrame; + memset(descriptor.dataAddress, 0, bufferSizeBytes); mDataQueue = std::make_unique( - descriptor->bytesPerFrame, - descriptor->capacityInFrames, + descriptor.bytesPerFrame, + descriptor.capacityInFrames, readCounterAddress, writeCounterAddress, - descriptor->dataAddress + descriptor.dataAddress ); - uint32_t threshold = descriptor->capacityInFrames / 2; + uint32_t threshold = descriptor.capacityInFrames / 2; mDataQueue->setThreshold(threshold); return result; } @@ -188,47 +192,66 @@ aaudio_result_t AudioEndpoint::readUpCommand(AAudioServiceMessage *commandPtr) } int32_t AudioEndpoint::getEmptyFramesAvailable(WrappingBuffer *wrappingBuffer) { - return mDataQueue->getEmptyRoomAvailable(wrappingBuffer); + return mDataQueue == nullptr ? 0 : mDataQueue->getEmptyRoomAvailable(wrappingBuffer); } int32_t AudioEndpoint::getEmptyFramesAvailable() { - return mDataQueue->getEmptyFramesAvailable(); + return mDataQueue == nullptr ? 0 : mDataQueue->getEmptyFramesAvailable(); } int32_t AudioEndpoint::getFullFramesAvailable(WrappingBuffer *wrappingBuffer) { - return mDataQueue->getFullDataAvailable(wrappingBuffer); + return mDataQueue == nullptr ? 0 : mDataQueue->getFullDataAvailable(wrappingBuffer); } int32_t AudioEndpoint::getFullFramesAvailable() { - return mDataQueue->getFullFramesAvailable(); + return mDataQueue == nullptr ? 0 : mDataQueue->getFullFramesAvailable(); +} + +android::fifo_frames_t AudioEndpoint::read(void *buffer, android::fifo_frames_t numFrames) { + return mDataQueue == nullptr ? 0 : mDataQueue->read(buffer, numFrames); +} + +android::fifo_frames_t AudioEndpoint::write(void *buffer, android::fifo_frames_t numFrames) { + return mDataQueue == nullptr ? 0 : mDataQueue->write(buffer, numFrames); } void AudioEndpoint::advanceWriteIndex(int32_t deltaFrames) { - mDataQueue->advanceWriteIndex(deltaFrames); + if (mDataQueue != nullptr) { + mDataQueue->advanceWriteIndex(deltaFrames); + } } void AudioEndpoint::advanceReadIndex(int32_t deltaFrames) { - mDataQueue->advanceReadIndex(deltaFrames); + if (mDataQueue != nullptr) { + mDataQueue->advanceReadIndex(deltaFrames); + } } void AudioEndpoint::setDataReadCounter(fifo_counter_t framesRead) { - mDataQueue->setReadCounter(framesRead); + if (mDataQueue != nullptr) { + mDataQueue->setReadCounter(framesRead); + } } fifo_counter_t AudioEndpoint::getDataReadCounter() const { - return mDataQueue->getReadCounter(); + return mDataQueue == nullptr ? 0 : mDataQueue->getReadCounter(); } void AudioEndpoint::setDataWriteCounter(fifo_counter_t framesRead) { - mDataQueue->setWriteCounter(framesRead); + if (mDataQueue != nullptr) { + mDataQueue->setWriteCounter(framesRead); + } } fifo_counter_t AudioEndpoint::getDataWriteCounter() const { - return mDataQueue->getWriteCounter(); + return mDataQueue == nullptr ? 0 : mDataQueue->getWriteCounter(); } int32_t AudioEndpoint::setBufferSizeInFrames(int32_t requestedFrames, int32_t *actualFrames) { + if (mDataQueue == nullptr) { + return AAUDIO_ERROR_INVALID_STATE; + } if (requestedFrames < ENDPOINT_DATA_QUEUE_SIZE_MIN) { requestedFrames = ENDPOINT_DATA_QUEUE_SIZE_MIN; } @@ -238,11 +261,11 @@ int32_t AudioEndpoint::setBufferSizeInFrames(int32_t requestedFrames, } int32_t AudioEndpoint::getBufferSizeInFrames() const { - return mDataQueue->getThreshold(); + return mDataQueue == nullptr ? 0 : mDataQueue->getThreshold(); } int32_t AudioEndpoint::getBufferCapacityInFrames() const { - return (int32_t)mDataQueue->getBufferCapacityInFrames(); + return mDataQueue == nullptr ? 0 : (int32_t)mDataQueue->getBufferCapacityInFrames(); } void AudioEndpoint::dump() const { @@ -251,5 +274,7 @@ void AudioEndpoint::dump() const { } void AudioEndpoint::eraseDataMemory() { - mDataQueue->eraseMemory(); + if (mDataQueue != nullptr) { + mDataQueue->eraseMemory(); + } } diff --git a/media/libaaudio/src/client/AudioEndpoint.h b/media/libaaudio/src/client/AudioEndpoint.h index 4c8d60f28dc94dfb77131ea319376f3e22e18583..01dd05ab9943fd4d8f6c9a2ddf667e2f2dd8fa92 100644 --- a/media/libaaudio/src/client/AudioEndpoint.h +++ b/media/libaaudio/src/client/AudioEndpoint.h @@ -17,6 +17,8 @@ #ifndef ANDROID_AAUDIO_AUDIO_ENDPOINT_H #define ANDROID_AAUDIO_AUDIO_ENDPOINT_H +#include + #include #include "binding/AAudioServiceMessage.h" @@ -34,7 +36,7 @@ namespace aaudio { class AudioEndpoint { public: - AudioEndpoint(); + AudioEndpoint() = default; /** * Configure based on the EndPointDescriptor_t. @@ -42,6 +44,9 @@ public: aaudio_result_t configure(const EndpointDescriptor *pEndpointDescriptor, aaudio_direction_t direction); + aaudio_result_t configureDataQueue(const RingBufferDescriptor &descriptor, + aaudio_direction_t direction); + /** * Read from a command passed up from the Server. * @return 1 if command received, 0 for no command, or negative error. @@ -56,6 +61,10 @@ public: int32_t getFullFramesAvailable(); + android::fifo_frames_t read(void* buffer, android::fifo_frames_t numFrames); + + android::fifo_frames_t write(void* buffer, android::fifo_frames_t numFrames); + void advanceReadIndex(int32_t deltaFrames); void advanceWriteIndex(int32_t deltaFrames); @@ -85,19 +94,31 @@ public: int32_t getBufferCapacityInFrames() const; + void setThreshold(int32_t frames) { + mDataQueue->setThreshold(frames); + } + + int32_t getThreshold() { + return mDataQueue->getThreshold(); + } + /** * Write zeros to the data queue memory. */ void eraseDataMemory(); + void freeDataQueue(); + void dump() const; private: std::unique_ptr mUpCommandQueue; std::unique_ptr mDataQueue; - bool mFreeRunning; - android::fifo_counter_t mDataReadCounter; // only used if free-running - android::fifo_counter_t mDataWriteCounter; // only used if free-running + bool mFreeRunning{false}; + android::fifo_counter_t mDataReadCounter{0}; // only used if free-running + android::fifo_counter_t mDataWriteCounter{0}; // only used if free-running + + std::mutex mDataQueueLock; }; } // namespace aaudio diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp index f933b2930d28f185dbc7a023087898d9dcbae2eb..9f0564f4bfafbd076caf5fd888ce90ec5217cc39 100644 --- a/media/libaaudio/src/client/AudioStreamInternal.cpp +++ b/media/libaaudio/src/client/AudioStreamInternal.cpp @@ -27,6 +27,8 @@ #include #include +#include +#include #include #include @@ -49,8 +51,6 @@ // This is needed to make sense of the logs more easily. #define LOG_TAG (mInService ? "AudioStreamInternal_Service" : "AudioStreamInternal_Client") -using android::Mutex; -using android::WrappingBuffer; using android::content::AttributionSourceState; using namespace aaudio; @@ -81,8 +81,6 @@ AudioStreamInternal::~AudioStreamInternal() { aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) { aaudio_result_t result = AAUDIO_OK; - int32_t framesPerBurst; - int32_t framesPerHardwareBurst; AAudioStreamRequest request; AAudioStreamConfiguration configurationOutput; @@ -97,9 +95,6 @@ aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) { return result; } - const int32_t burstMinMicros = AAudioProperty_getHardwareBurstMinMicros(); - int32_t burstMicros = 0; - const audio_format_t requestedFormat = getFormat(); // We have to do volume scaling. So we prefer FLOAT format. if (requestedFormat == AUDIO_FORMAT_DEFAULT) { @@ -215,12 +210,28 @@ aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) { goto error; } - framesPerHardwareBurst = mEndpointDescriptor.dataQueueDescriptor.framesPerBurst; + if ((result = configureDataInformation(builder.getFramesPerDataCallback())) != AAUDIO_OK) { + goto error; + } + + setState(AAUDIO_STREAM_STATE_OPEN); + + return result; + +error: + safeReleaseClose(); + return result; +} + +aaudio_result_t AudioStreamInternal::configureDataInformation(int32_t callbackFrames) { + int32_t framesPerHardwareBurst = mEndpointDescriptor.dataQueueDescriptor.framesPerBurst; // Scale up the burst size to meet the minimum equivalent in microseconds. // This is to avoid waking the CPU too often when the HW burst is very small // or at high sample rates. - framesPerBurst = framesPerHardwareBurst; + int32_t framesPerBurst = framesPerHardwareBurst; + int32_t burstMicros = 0; + const int32_t burstMinMicros = android::AudioSystem::getAAudioHardwareBurstMinUsec(); do { if (burstMicros > 0) { // skip first loop framesPerBurst *= 2; @@ -233,8 +244,7 @@ aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) { // Validate final burst size. if (framesPerBurst < MIN_FRAMES_PER_BURST || framesPerBurst > MAX_FRAMES_PER_BURST) { ALOGE("%s - framesPerBurst out of range = %d", __func__, framesPerBurst); - result = AAUDIO_ERROR_OUT_OF_RANGE; - goto error; + return AAUDIO_ERROR_OUT_OF_RANGE; } setFramesPerBurst(framesPerBurst); // only save good value @@ -242,26 +252,21 @@ aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) { if (mBufferCapacityInFrames < getFramesPerBurst() || mBufferCapacityInFrames > MAX_BUFFER_CAPACITY_IN_FRAMES) { ALOGE("%s - bufferCapacity out of range = %d", __func__, mBufferCapacityInFrames); - result = AAUDIO_ERROR_OUT_OF_RANGE; - goto error; + return AAUDIO_ERROR_OUT_OF_RANGE; } mClockModel.setSampleRate(getSampleRate()); mClockModel.setFramesPerBurst(framesPerHardwareBurst); if (isDataCallbackSet()) { - mCallbackFrames = builder.getFramesPerDataCallback(); + mCallbackFrames = callbackFrames; if (mCallbackFrames > getBufferCapacity() / 2) { ALOGW("%s - framesPerCallback too big = %d, capacity = %d", __func__, mCallbackFrames, getBufferCapacity()); - result = AAUDIO_ERROR_OUT_OF_RANGE; - goto error; - + return AAUDIO_ERROR_OUT_OF_RANGE; } else if (mCallbackFrames < 0) { ALOGW("%s - framesPerCallback negative", __func__); - result = AAUDIO_ERROR_OUT_OF_RANGE; - goto error; - + return AAUDIO_ERROR_OUT_OF_RANGE; } if (mCallbackFrames == AAUDIO_UNSPECIFIED) { mCallbackFrames = getFramesPerBurst(); @@ -271,6 +276,18 @@ aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) { mCallbackBuffer = std::make_unique(callbackBufferSize); } + // Exclusive output streams should combine channels when mono audio adjustment + // is enabled. They should also adjust for audio balance. + if ((getDirection() == AAUDIO_DIRECTION_OUTPUT) && + (getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE)) { + bool isMasterMono = false; + android::AudioSystem::getMasterMono(&isMasterMono); + setRequireMonoBlend(isMasterMono); + float audioBalance = 0; + android::AudioSystem::getMasterBalance(&audioBalance); + setAudioBalance(audioBalance); + } + // For debugging and analyzing the distribution of MMAP timestamps. // For OUTPUT, use a NEGATIVE offset to move the CPU writes further BEFORE the HW reads. // For INPUT, use a POSITIVE offset to move the CPU reads further AFTER the HW writes. @@ -290,14 +307,7 @@ aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) { } setBufferSize(mBufferCapacityInFrames / 2); // Default buffer size to match Q - - setState(AAUDIO_STREAM_STATE_OPEN); - - return result; - -error: - safeReleaseClose(); - return result; + return AAUDIO_OK; } // This must be called under mStreamLock. @@ -338,11 +348,65 @@ static void *aaudio_callback_thread_proc(void *context) { AudioStreamInternal *stream = (AudioStreamInternal *)context; //LOGD("oboe_callback_thread, stream = %p", stream); - if (stream != NULL) { + if (stream != nullptr) { return stream->callbackLoop(); } else { - return NULL; + return nullptr; + } +} + +aaudio_result_t AudioStreamInternal::exitStandby_l() { + AudioEndpointParcelable endpointParcelable; + // The stream is in standby mode, copy all available data and then close the duplicated + // shared file descriptor so that it won't cause issue when the HAL try to reallocate new + // shared file descriptor when exiting from standby. + // Cache current read counter, which will be reset to new read and write counter + // when the new data queue and endpoint are reconfigured. + const android::fifo_counter_t readCounter = mAudioEndpoint->getDataReadCounter(); + // Cache the buffer size which may be from client. + const int32_t previousBufferSize = mBufferSizeInFrames; + // Copy all available data from current data queue. + uint8_t buffer[getBufferCapacity() * getBytesPerFrame()]; + android::fifo_frames_t fullFramesAvailable = + mAudioEndpoint->read(buffer, getBufferCapacity()); + mEndPointParcelable.closeDataFileDescriptor(); + aaudio_result_t result = mServiceInterface.exitStandby( + mServiceStreamHandle, endpointParcelable); + if (result != AAUDIO_OK) { + ALOGE("Failed to exit standby, error=%d", result); + goto exit; } + // Reconstruct data queue descriptor using new shared file descriptor. + mEndPointParcelable.updateDataFileDescriptor(&endpointParcelable); + result = mEndPointParcelable.resolveDataQueue(&mEndpointDescriptor.dataQueueDescriptor); + if (result != AAUDIO_OK) { + ALOGE("Failed to resolve data queue after exiting standby, error=%d", result); + goto exit; + } + // Reconfigure audio endpoint with new data queue descriptor. + mAudioEndpoint->configureDataQueue( + mEndpointDescriptor.dataQueueDescriptor, getDirection()); + // Set read and write counters with previous read counter, the later write action + // will make the counter at the correct place. + mAudioEndpoint->setDataReadCounter(readCounter); + mAudioEndpoint->setDataWriteCounter(readCounter); + result = configureDataInformation(mCallbackFrames); + if (result != AAUDIO_OK) { + ALOGE("Failed to configure data information after exiting standby, error=%d", result); + goto exit; + } + // Write data from previous data buffer to new endpoint. + if (android::fifo_frames_t framesWritten = + mAudioEndpoint->write(buffer, fullFramesAvailable); + framesWritten != fullFramesAvailable) { + ALOGW("Some data lost after exiting standby, frames written: %d, " + "frames to write: %d", framesWritten, fullFramesAvailable); + } + // Reset previous buffer size as it may be requested by the client. + setBufferSize(previousBufferSize); + +exit: + return result; } /* @@ -381,8 +445,15 @@ aaudio_result_t AudioStreamInternal::requestStart_l() prepareBuffersForStart(); // tell subclasses to get ready aaudio_result_t result = mServiceInterface.startStream(mServiceStreamHandle); - if (result == AAUDIO_ERROR_INVALID_HANDLE) { - ALOGD("%s() INVALID_HANDLE, stream was probably stolen", __func__); + if (result == AAUDIO_ERROR_STANDBY) { + // The stream is at standby mode. Need to exit standby before starting the stream. + result = exitStandby_l(); + if (result == AAUDIO_OK) { + result = mServiceInterface.startStream(mServiceStreamHandle); + } + } + if (result != AAUDIO_OK) { + ALOGD("%s() error = %d, stream was probably stolen", __func__, result); // Stealing was added in R. Coerce result to improve backward compatibility. result = AAUDIO_ERROR_DISCONNECTED; setState(AAUDIO_STREAM_STATE_DISCONNECTED); @@ -402,6 +473,7 @@ aaudio_result_t AudioStreamInternal::requestStart_l() result = createThread_l(periodNanos, aaudio_callback_thread_proc, this); } if (result != AAUDIO_OK) { + // TODO(b/214607638): Do we want to roll back to original state or keep as disconnected? setState(originalState); } return result; @@ -430,7 +502,7 @@ aaudio_result_t AudioStreamInternal::stopCallback_l() if (isDataCallbackSet() && (isActive() || getState() == AAUDIO_STREAM_STATE_DISCONNECTED)) { mCallbackEnabled.store(false); - aaudio_result_t result = joinThread_l(NULL); // may temporarily unlock mStreamLock + aaudio_result_t result = joinThread_l(nullptr); // may temporarily unlock mStreamLock if (result == AAUDIO_ERROR_INVALID_HANDLE) { ALOGD("%s() INVALID_HANDLE, stream was probably stolen", __func__); result = AAUDIO_OK; @@ -517,7 +589,7 @@ aaudio_result_t AudioStreamInternal::stopClient(audio_port_handle_t portHandle) return result; } -aaudio_result_t AudioStreamInternal::getTimestamp(clockid_t clockId, +aaudio_result_t AudioStreamInternal::getTimestamp(clockid_t /*clockId*/, int64_t *framePosition, int64_t *timeNanoseconds) { // Generated in server and passed to client. Return latest. diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h index fbe4c13efd17203c2ee1503657a20529e3dd4084..2367572f95afdc360b950891cc9da5cd160b53d3 100644 --- a/media/libaaudio/src/client/AudioStreamInternal.h +++ b/media/libaaudio/src/client/AudioStreamInternal.h @@ -116,7 +116,7 @@ protected: virtual void prepareBuffersForStart() {} - virtual void advanceClientToMatchServerPosition(int32_t serverMargin = 0) = 0; + virtual void advanceClientToMatchServerPosition(int32_t serverMargin) = 0; virtual void onFlushFromServer() {} @@ -184,9 +184,14 @@ private: aaudio_result_t writeNowWithConversion(const void *buffer, int32_t numFrames); + // Exit the stream from standby, will reconstruct data path. + aaudio_result_t exitStandby_l() REQUIRES(mStreamLock); + // Adjust timing model based on timestamp from service. void processTimestamp(uint64_t position, int64_t time); + aaudio_result_t configureDataInformation(int32_t callbackFrames); + // Thread on other side of FIFO will have wakeup jitter. // By delaying slightly we can avoid waking up before other side is ready. const int32_t mWakeupDelayNanos; // delay past typical wakeup jitter diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp index 2da54066f3c0dce0cc2c217413774284c1dba3d4..1efccb130d74c9048f48f63ee80b1005de2426fa 100644 --- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp +++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp @@ -46,8 +46,6 @@ AudioStreamInternalCapture::AudioStreamInternalCapture(AAudioServiceInterface & } -AudioStreamInternalCapture::~AudioStreamInternalCapture() {} - void AudioStreamInternalCapture::advanceClientToMatchServerPosition(int32_t serverMargin) { int64_t readCounter = mAudioEndpoint->getDataReadCounter(); int64_t writeCounter = mAudioEndpoint->getDataWriteCounter() + serverMargin; @@ -109,7 +107,7 @@ aaudio_result_t AudioStreamInternalCapture::processDataNow(void *buffer, int32_t if (mNeedCatchUp.isRequested()) { // Catch an MMAP pointer that is already advancing. // This will avoid initial underruns caused by a slow cold start. - advanceClientToMatchServerPosition(); + advanceClientToMatchServerPosition(0 /*serverMargin*/); mNeedCatchUp.acknowledge(); } @@ -228,7 +226,7 @@ int64_t AudioStreamInternalCapture::getFramesRead() { void *AudioStreamInternalCapture::callbackLoop() { aaudio_result_t result = AAUDIO_OK; aaudio_data_callback_result_t callbackResult = AAUDIO_CALLBACK_RESULT_CONTINUE; - if (!isDataCallbackSet()) return NULL; + if (!isDataCallbackSet()) return nullptr; // result might be a frame count while (mCallbackEnabled.load() && isActive() && (result >= 0)) { @@ -260,5 +258,5 @@ void *AudioStreamInternalCapture::callbackLoop() { ALOGD("callbackLoop() exiting, result = %d, isActive() = %d", result, (int) isActive()); - return NULL; + return nullptr; } diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.h b/media/libaaudio/src/client/AudioStreamInternalCapture.h index 251a7f2b11aa72f6896a1e674576795443e9e6c3..87017deb6fe6d21b38d414d0592c2466fe55281c 100644 --- a/media/libaaudio/src/client/AudioStreamInternalCapture.h +++ b/media/libaaudio/src/client/AudioStreamInternalCapture.h @@ -28,8 +28,9 @@ namespace aaudio { class AudioStreamInternalCapture : public AudioStreamInternal { public: - AudioStreamInternalCapture(AAudioServiceInterface &serviceInterface, bool inService = false); - virtual ~AudioStreamInternalCapture(); + explicit AudioStreamInternalCapture(AAudioServiceInterface &serviceInterface, + bool inService = false); + virtual ~AudioStreamInternalCapture() = default; aaudio_result_t read(void *buffer, int32_t numFrames, @@ -45,7 +46,7 @@ public: } protected: - void advanceClientToMatchServerPosition(int32_t serverOffset = 0) override; + void advanceClientToMatchServerPosition(int32_t serverOffset) override; /** * Low level data processing that will not block. It will just read or write as much as it can. diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp index 71bde9068fc4574a8e0bc739131b640db1c3c270..7c7a969461c3d06dce6d0010e086e5b7c7ad2ada 100644 --- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp +++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp @@ -44,8 +44,6 @@ AudioStreamInternalPlay::AudioStreamInternalPlay(AAudioServiceInterface &servic } -AudioStreamInternalPlay::~AudioStreamInternalPlay() {} - constexpr int kRampMSec = 10; // time to apply a change in volume aaudio_result_t AudioStreamInternalPlay::open(const AudioStreamBuilder &builder) { @@ -54,7 +52,10 @@ aaudio_result_t AudioStreamInternalPlay::open(const AudioStreamBuilder &builder) result = mFlowGraph.configure(getFormat(), getSamplesPerFrame(), getDeviceFormat(), - getDeviceChannelCount()); + getDeviceChannelCount(), + getRequireMonoBlend(), + getAudioBalance(), + (getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE)); if (result != AAUDIO_OK) { safeReleaseClose(); @@ -115,7 +116,7 @@ void AudioStreamInternalPlay::advanceClientToMatchServerPosition(int32_t serverM } void AudioStreamInternalPlay::onFlushFromServer() { - advanceClientToMatchServerPosition(); + advanceClientToMatchServerPosition(0 /*serverMargin*/); } // Write the data, block if needed and timeoutMillis > 0 @@ -201,10 +202,18 @@ aaudio_result_t AudioStreamInternalPlay::processDataNow(void *buffer, int32_t nu break; case AAUDIO_STREAM_STATE_STARTED: { - // Sleep until the readCounter catches up and we only have - // the getBufferSize() frames of data sitting in the buffer. - int64_t nextReadPosition = mAudioEndpoint->getDataWriteCounter() - getBufferSize(); - wakeTime = mClockModel.convertPositionToTime(nextReadPosition); + // Calculate when there will be room available to write to the buffer. + // If the appBufferSize is smaller than the endpointBufferSize then + // we will have room to write data beyond the appBufferSize. + // That is a technique used to reduce glitches without adding latency. + const int32_t appBufferSize = getBufferSize(); + // The endpoint buffer size is set to the maximum that can be written. + // If we use it then we must carve out some room to write data when we wake up. + const int32_t endBufferSize = mAudioEndpoint->getBufferSizeInFrames() + - getFramesPerBurst(); + const int32_t bestBufferSize = std::min(appBufferSize, endBufferSize); + int64_t targetReadPosition = mAudioEndpoint->getDataWriteCounter() - bestBufferSize; + wakeTime = mClockModel.convertPositionToTime(targetReadPosition); } break; default: @@ -281,7 +290,7 @@ void *AudioStreamInternalPlay::callbackLoop() { ALOGD("%s() entering >>>>>>>>>>>>>>>", __func__); aaudio_result_t result = AAUDIO_OK; aaudio_data_callback_result_t callbackResult = AAUDIO_CALLBACK_RESULT_CONTINUE; - if (!isDataCallbackSet()) return NULL; + if (!isDataCallbackSet()) return nullptr; int64_t timeoutNanos = calculateReasonableTimeout(mCallbackFrames); // result might be a frame count @@ -309,7 +318,7 @@ void *AudioStreamInternalPlay::callbackLoop() { ALOGD("%s() exiting, result = %d, isActive() = %d <<<<<<<<<<<<<<", __func__, result, (int) isActive()); - return NULL; + return nullptr; } //------------------------------------------------------------------------------ diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.h b/media/libaaudio/src/client/AudioStreamInternalPlay.h index 03c957d3634dc2e52f1eb3c7eaf6f9a38cbf4e8a..e7618079bb2fbd8aeed0f3e55d5ecb14340d9c89 100644 --- a/media/libaaudio/src/client/AudioStreamInternalPlay.h +++ b/media/libaaudio/src/client/AudioStreamInternalPlay.h @@ -30,8 +30,9 @@ namespace aaudio { class AudioStreamInternalPlay : public AudioStreamInternal { public: - AudioStreamInternalPlay(AAudioServiceInterface &serviceInterface, bool inService = false); - virtual ~AudioStreamInternalPlay(); + explicit AudioStreamInternalPlay(AAudioServiceInterface &serviceInterface, + bool inService = false); + virtual ~AudioStreamInternalPlay() = default; aaudio_result_t open(const AudioStreamBuilder &builder) override; @@ -66,7 +67,7 @@ protected: void prepareBuffersForStart() override; - void advanceClientToMatchServerPosition(int32_t serverMargin = 0) override; + void advanceClientToMatchServerPosition(int32_t serverMargin) override; void onFlushFromServer() override; diff --git a/media/libaaudio/src/client/IsochronousClockModel.cpp b/media/libaaudio/src/client/IsochronousClockModel.cpp index f0dcd448594e64cd79b13717839d4f3e2b723357..692127118ce4000e6abf6a27b284148ec6554198 100644 --- a/media/libaaudio/src/client/IsochronousClockModel.cpp +++ b/media/libaaudio/src/client/IsochronousClockModel.cpp @@ -43,14 +43,7 @@ using namespace android::audio_utils; // and dumped to the log when the stream is stopped. IsochronousClockModel::IsochronousClockModel() - : mMarkerFramePosition(0) - , mMarkerNanoTime(0) - , mSampleRate(48000) - , mFramesPerBurst(48) - , mBurstPeriodNanos(0) // this will be updated before use - , mMaxMeasuredLatenessNanos(0) - , mLatenessForDriftNanos(kInitialLatenessForDriftNanos) - , mState(STATE_STOPPED) + : mLatenessForDriftNanos(kInitialLatenessForDriftNanos) { if ((AAudioProperty_getLogMask() & AAUDIO_LOG_CLOCK_MODEL_HISTOGRAM) != 0) { mHistogramMicros = std::make_unique(kHistogramBinCount, diff --git a/media/libaaudio/src/client/IsochronousClockModel.h b/media/libaaudio/src/client/IsochronousClockModel.h index 628001358778127c82ecdee621b49c590472da41..3007237866e0911139f65df36f1e0c459231b9b7 100644 --- a/media/libaaudio/src/client/IsochronousClockModel.h +++ b/media/libaaudio/src/client/IsochronousClockModel.h @@ -149,16 +149,16 @@ private: static constexpr int32_t kHistogramBinWidthMicros = 50; static constexpr int32_t kHistogramBinCount = 128; - int64_t mMarkerFramePosition; // Estimated HW position. - int64_t mMarkerNanoTime; // Estimated HW time. - int32_t mSampleRate; - int32_t mFramesPerBurst; // number of frames transferred at one time. - int32_t mBurstPeriodNanos; // Time between HW bursts. + int64_t mMarkerFramePosition{0}; // Estimated HW position. + int64_t mMarkerNanoTime{0}; // Estimated HW time. + int32_t mSampleRate{48000}; + int32_t mFramesPerBurst{48}; // number of frames transferred at one time. + int32_t mBurstPeriodNanos{0}; // Time between HW bursts. // Includes mBurstPeriodNanos because we sample randomly over time. - int32_t mMaxMeasuredLatenessNanos; + int32_t mMaxMeasuredLatenessNanos{0}; // Threshold for lateness that triggers a drift later in time. int32_t mLatenessForDriftNanos; - clock_model_state_t mState; // State machine handles startup sequence. + clock_model_state_t mState{STATE_STOPPED}; // State machine handles startup sequence. int32_t mTimestampCount = 0; // For logging. diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp index a990850a0327a4fde901ec94560e35f93583fcb7..31fd011d4c0bf426544154f6b128b61e76d1b884 100644 --- a/media/libaaudio/src/core/AAudioStreamParameters.cpp +++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp @@ -30,9 +30,6 @@ using namespace aaudio; // HDMI supports up to 32 channels at 1536000 Hz. #define SAMPLE_RATE_HZ_MAX 1600000 -AAudioStreamParameters::AAudioStreamParameters() {} -AAudioStreamParameters::~AAudioStreamParameters() {} - void AAudioStreamParameters::copyFrom(const AAudioStreamParameters &other) { mSamplesPerFrame = other.mSamplesPerFrame; mSampleRate = other.mSampleRate; diff --git a/media/libaaudio/src/core/AAudioStreamParameters.h b/media/libaaudio/src/core/AAudioStreamParameters.h index fed036b00c3e6aacdbde4963de6e42116beee923..cb998bf052fdd988379e8efabdc6334829b46179 100644 --- a/media/libaaudio/src/core/AAudioStreamParameters.h +++ b/media/libaaudio/src/core/AAudioStreamParameters.h @@ -26,8 +26,8 @@ namespace aaudio { class AAudioStreamParameters { public: - AAudioStreamParameters(); - virtual ~AAudioStreamParameters(); + AAudioStreamParameters() = default; + virtual ~AAudioStreamParameters() = default; int32_t getDeviceId() const { return mDeviceId; @@ -150,7 +150,7 @@ public: } // TODO b/182392769: reexamine if Identity can be used - void setOpPackageName(const std::optional opPackageName) { + void setOpPackageName(const std::optional& opPackageName) { mOpPackageName = opPackageName; } @@ -158,7 +158,7 @@ public: return mAttributionTag; } - void setAttributionTag(const std::optional attributionTag) { + void setAttributionTag(const std::optional& attributionTag) { mAttributionTag = attributionTag; } diff --git a/media/libaaudio/src/core/AudioGlobal.h b/media/libaaudio/src/core/AudioGlobal.h index 1e88d15df7d307bfca4d71d36f2d97c2377bcd7b..6c22744bb7869f783a3dd7ca7f735c5a1d526171 100644 --- a/media/libaaudio/src/core/AudioGlobal.h +++ b/media/libaaudio/src/core/AudioGlobal.h @@ -31,7 +31,8 @@ const char* AudioGlobal_convertPerformanceModeToText(aaudio_performance_mode_t m const char* AudioGlobal_convertResultToText(aaudio_result_t returnCode); const char* AudioGlobal_convertSharingModeToText(aaudio_sharing_mode_t mode); const char* AudioGlobal_convertStreamStateToText(aaudio_stream_state_t state); -} + +} // namespace aaudio #endif // AAUDIO_AUDIOGLOBAL_H diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h index 7896e633122cd36fa1fca051d171aa98274bb1dc..5fb4528c35e708bee1a0dae9f5ca2d6d9b7f7f67 100644 --- a/media/libaaudio/src/core/AudioStream.h +++ b/media/libaaudio/src/core/AudioStream.h @@ -277,6 +277,14 @@ public: return mIsPrivacySensitive; } + bool getRequireMonoBlend() const { + return mRequireMonoBlend; + } + + float getAudioBalance() const { + return mAudioBalance; + } + /** * This is only valid after setChannelMask() and setFormat() * have been called. @@ -447,7 +455,7 @@ protected: // PlayerBase allows the system to control the stream volume. class MyPlayerBase : public android::PlayerBase { public: - MyPlayerBase() {}; + MyPlayerBase() = default; virtual ~MyPlayerBase() = default; @@ -576,7 +584,7 @@ protected: * @param numFrames * @return original pointer or the conversion buffer */ - virtual const void * maybeConvertDeviceData(const void *audioData, int32_t numFrames) { + virtual const void * maybeConvertDeviceData(const void *audioData, int32_t /*numFrames*/) { return audioData; } @@ -631,6 +639,20 @@ protected: mIsPrivacySensitive = privacySensitive; } + /** + * This should not be called after the open() call. + */ + void setRequireMonoBlend(bool requireMonoBlend) { + mRequireMonoBlend = requireMonoBlend; + } + + /** + * This should not be called after the open() call. + */ + void setAudioBalance(float audioBalance) { + mAudioBalance = audioBalance; + } + std::string mMetricsId; // set once during open() std::mutex mStreamLock; @@ -672,6 +694,8 @@ private: aaudio_input_preset_t mInputPreset = AAUDIO_UNSPECIFIED; aaudio_allowed_capture_policy_t mAllowedCapturePolicy = AAUDIO_ALLOW_CAPTURE_BY_ALL; bool mIsPrivacySensitive = false; + bool mRequireMonoBlend = false; + float mAudioBalance = 0; int32_t mSessionId = AAUDIO_UNSPECIFIED; diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp index 5e1e007fd75979bddef9c1f10cec126d3113c189..a100aa92bace2138e6f802e62e4b23973df31ed2 100644 --- a/media/libaaudio/src/core/AudioStreamBuilder.cpp +++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp @@ -20,9 +20,14 @@ #include #include +#include #include #include +#include +#include +#include +#include #include "binding/AAudioBinderClient.h" #include "client/AudioStreamInternalCapture.h" @@ -35,6 +40,10 @@ using namespace aaudio; +using android::media::audio::common::AudioMMapPolicy; +using android::media::audio::common::AudioMMapPolicyInfo; +using android::media::audio::common::AudioMMapPolicyType; + #define AAUDIO_MMAP_POLICY_DEFAULT AAUDIO_POLICY_NEVER #define AAUDIO_MMAP_EXCLUSIVE_POLICY_DEFAULT AAUDIO_POLICY_NEVER @@ -53,16 +62,10 @@ using namespace aaudio; /* * AudioStreamBuilder */ -AudioStreamBuilder::AudioStreamBuilder() { -} - -AudioStreamBuilder::~AudioStreamBuilder() { -} - static aaudio_result_t builder_createStream(aaudio_direction_t direction, - aaudio_sharing_mode_t sharingMode, - bool tryMMap, - android::sp &stream) { + aaudio_sharing_mode_t /*sharingMode*/, + bool tryMMap, + android::sp &stream) { aaudio_result_t result = AAUDIO_OK; switch (direction) { @@ -92,6 +95,37 @@ static aaudio_result_t builder_createStream(aaudio_direction_t direction, return result; } +namespace { + +aaudio_policy_t aidl2legacy_aaudio_policy(AudioMMapPolicy aidl) { + switch (aidl) { + case AudioMMapPolicy::NEVER: + return AAUDIO_POLICY_NEVER; + case AudioMMapPolicy::AUTO: + return AAUDIO_POLICY_AUTO; + case AudioMMapPolicy::ALWAYS: + return AAUDIO_POLICY_ALWAYS; + case AudioMMapPolicy::UNSPECIFIED: + default: + return AAUDIO_UNSPECIFIED; + } +} + +// The aaudio policy will be ALWAYS, NEVER, UNSPECIFIED only when all policy info are +// ALWAYS, NEVER or UNSPECIFIED. Otherwise, the aaudio policy will be AUTO. +aaudio_policy_t getAAudioPolicy( + const std::vector& policyInfos) { + if (policyInfos.empty()) return AAUDIO_POLICY_AUTO; + for (size_t i = 1; i < policyInfos.size(); ++i) { + if (policyInfos.at(i).mmapPolicy != policyInfos.at(0).mmapPolicy) { + return AAUDIO_POLICY_AUTO; + } + } + return aidl2legacy_aaudio_policy(policyInfos.at(0).mmapPolicy); +} + +} // namespace + // Try to open using MMAP path if that is allowed. // Fall back to Legacy path if MMAP not available. // Exact behavior is controlled by MMapPolicy. @@ -110,25 +144,32 @@ aaudio_result_t AudioStreamBuilder::build(AudioStream** streamPtr) { return result; } + std::vector policyInfos; // The API setting is the highest priority. aaudio_policy_t mmapPolicy = AudioGlobal_getMMapPolicy(); // If not specified then get from a system property. - if (mmapPolicy == AAUDIO_UNSPECIFIED) { - mmapPolicy = AAudioProperty_getMMapPolicy(); + if (mmapPolicy == AAUDIO_UNSPECIFIED && android::AudioSystem::getMmapPolicyInfo( + AudioMMapPolicyType::DEFAULT, &policyInfos) == NO_ERROR) { + mmapPolicy = getAAudioPolicy(policyInfos); } // If still not specified then use the default. if (mmapPolicy == AAUDIO_UNSPECIFIED) { mmapPolicy = AAUDIO_MMAP_POLICY_DEFAULT; } - int32_t mapExclusivePolicy = AAudioProperty_getMMapExclusivePolicy(); - if (mapExclusivePolicy == AAUDIO_UNSPECIFIED) { - mapExclusivePolicy = AAUDIO_MMAP_EXCLUSIVE_POLICY_DEFAULT; + policyInfos.clear(); + aaudio_policy_t mmapExclusivePolicy = AAUDIO_UNSPECIFIED; + if (android::AudioSystem::getMmapPolicyInfo( + AudioMMapPolicyType::EXCLUSIVE, &policyInfos) == NO_ERROR) { + mmapExclusivePolicy = getAAudioPolicy(policyInfos); + } + if (mmapExclusivePolicy == AAUDIO_UNSPECIFIED) { + mmapExclusivePolicy = AAUDIO_MMAP_EXCLUSIVE_POLICY_DEFAULT; } aaudio_sharing_mode_t sharingMode = getSharingMode(); if ((sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) - && (mapExclusivePolicy == AAUDIO_POLICY_NEVER)) { + && (mmapExclusivePolicy == AAUDIO_POLICY_NEVER)) { ALOGD("%s() EXCLUSIVE sharing mode not supported. Use SHARED.", __func__); sharingMode = AAUDIO_SHARING_MODE_SHARED; setSharingMode(sharingMode); @@ -280,9 +321,8 @@ void AudioStreamBuilder::logParameters() const { mFramesPerDataCallback); ALOGI("usage = %6d, contentType = %d, inputPreset = %d, allowedCapturePolicy = %d", getUsage(), getContentType(), getInputPreset(), getAllowedCapturePolicy()); - ALOGI("privacy sensitive = %s", isPrivacySensitive() ? "true" : "false"); - ALOGI("opPackageName = %s", !getOpPackageName().has_value() ? - "(null)" : getOpPackageName().value().c_str()); - ALOGI("attributionTag = %s", !getAttributionTag().has_value() ? - "(null)" : getAttributionTag().value().c_str()); + ALOGI("privacy sensitive = %s, opPackageName = %s, attributionTag = %s", + isPrivacySensitive() ? "true" : "false", + !getOpPackageName().has_value() ? "(null)" : getOpPackageName().value().c_str(), + !getAttributionTag().has_value() ? "(null)" : getAttributionTag().value().c_str()); } diff --git a/media/libaaudio/src/core/AudioStreamBuilder.h b/media/libaaudio/src/core/AudioStreamBuilder.h index 9f93341ea48271ba052dd3679b4faf2851facb54..f91c25a183addacc83a7ea7c1f8629e652dbf1f9 100644 --- a/media/libaaudio/src/core/AudioStreamBuilder.h +++ b/media/libaaudio/src/core/AudioStreamBuilder.h @@ -31,9 +31,9 @@ namespace aaudio { */ class AudioStreamBuilder : public AAudioStreamParameters { public: - AudioStreamBuilder(); + AudioStreamBuilder() = default; - ~AudioStreamBuilder(); + ~AudioStreamBuilder() = default; bool isSharingModeMatchRequired() const { return mSharingModeMatchRequired; diff --git a/media/libaaudio/src/fifo/FifoBuffer.h b/media/libaaudio/src/fifo/FifoBuffer.h index 37548f0cfb9c4d796db9d6ea456156badc3fdd27..7b0aca1f8250432ba1cefdd4cb24e6f225960b36 100644 --- a/media/libaaudio/src/fifo/FifoBuffer.h +++ b/media/libaaudio/src/fifo/FifoBuffer.h @@ -38,7 +38,7 @@ struct WrappingBuffer { class FifoBuffer { public: - FifoBuffer(int32_t bytesPerFrame); + explicit FifoBuffer(int32_t bytesPerFrame); virtual ~FifoBuffer() = default; @@ -162,6 +162,6 @@ private: uint8_t *mExternalStorage = nullptr; }; -} // android +} // namespace android #endif //FIFO_FIFO_BUFFER_H diff --git a/media/libaaudio/src/fifo/FifoController.h b/media/libaaudio/src/fifo/FifoController.h index 057a94e6990714ac9ffc8f0408303d09313d351f..e15d444f2d386fb9f456a94d665385d19e71ad98 100644 --- a/media/libaaudio/src/fifo/FifoController.h +++ b/media/libaaudio/src/fifo/FifoController.h @@ -36,7 +36,7 @@ public: , mWriteCounter(0) {} - virtual ~FifoController() {} + virtual ~FifoController() = default; // TODO review use of memory barriers, probably incorrect virtual fifo_counter_t getReadCounter() override { @@ -57,6 +57,6 @@ private: std::atomic mWriteCounter; }; -} // android +} // namespace android #endif //FIFO_FIFO_CONTROLLER_H diff --git a/media/libaaudio/src/fifo/FifoControllerBase.cpp b/media/libaaudio/src/fifo/FifoControllerBase.cpp index 1dece0eb976150a85bf5144c60b7afe4f3b411c4..ad6d0415fe3c6af4b59a33ba9e8f92f2da30afca 100644 --- a/media/libaaudio/src/fifo/FifoControllerBase.cpp +++ b/media/libaaudio/src/fifo/FifoControllerBase.cpp @@ -29,9 +29,6 @@ FifoControllerBase::FifoControllerBase(fifo_frames_t capacity, fifo_frames_t thr { } -FifoControllerBase::~FifoControllerBase() { -} - fifo_frames_t FifoControllerBase::getFullFramesAvailable() { fifo_frames_t temp = 0; __builtin_sub_overflow(getWriteCounter(), getReadCounter(), &temp); diff --git a/media/libaaudio/src/fifo/FifoControllerBase.h b/media/libaaudio/src/fifo/FifoControllerBase.h index 1edb8a3cb5c0939a39653f5d7db0275422a0528c..2a6173b0ff65c07886ca68d685ea6d214702c1c5 100644 --- a/media/libaaudio/src/fifo/FifoControllerBase.h +++ b/media/libaaudio/src/fifo/FifoControllerBase.h @@ -43,7 +43,7 @@ public: */ FifoControllerBase(fifo_frames_t capacity, fifo_frames_t threshold); - virtual ~FifoControllerBase(); + virtual ~FifoControllerBase() = default; // Abstract methods to be implemented in subclasses. /** @@ -123,6 +123,6 @@ private: fifo_frames_t mThreshold; }; -} // android +} // namespace android #endif // FIFO_FIFO_CONTROLLER_BASE_H diff --git a/media/libaaudio/src/fifo/FifoControllerIndirect.h b/media/libaaudio/src/fifo/FifoControllerIndirect.h index ec48e5778d2d8bc3b3f7d1fc59b1ac671906777d..a59225adf0ec785ae211e441252b0c6b101b05de 100644 --- a/media/libaaudio/src/fifo/FifoControllerIndirect.h +++ b/media/libaaudio/src/fifo/FifoControllerIndirect.h @@ -44,7 +44,7 @@ public: setReadCounter(0); setWriteCounter(0); } - virtual ~FifoControllerIndirect() {}; + virtual ~FifoControllerIndirect() = default; // TODO review use of memory barriers, probably incorrect virtual fifo_counter_t getReadCounter() override { @@ -68,6 +68,6 @@ private: std::atomic * mWriteCounterAddress; }; -} // android +} // namespace android #endif //FIFO_FIFO_CONTROLLER_INDIRECT_H diff --git a/media/libaaudio/src/flowgraph/AudioProcessorBase.cpp b/media/libaaudio/src/flowgraph/AudioProcessorBase.cpp deleted file mode 100644 index 5667fdbc7b58333af1b026ec8faebaa9fb795be7..0000000000000000000000000000000000000000 --- a/media/libaaudio/src/flowgraph/AudioProcessorBase.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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. - */ - -#include -#include -#include "AudioProcessorBase.h" - -using namespace flowgraph; - -/***************************************************************************/ -int32_t AudioProcessorBase::pullData(int64_t framePosition, int32_t numFrames) { - if (framePosition > mLastFramePosition) { - mLastFramePosition = framePosition; - mFramesValid = onProcess(framePosition, numFrames); - } - return mFramesValid; -} - -/***************************************************************************/ -AudioFloatBlockPort::AudioFloatBlockPort(AudioProcessorBase &parent, - int32_t samplesPerFrame, - int32_t framesPerBlock) - : AudioPort(parent, samplesPerFrame) - , mFramesPerBlock(framesPerBlock) - , mSampleBlock(NULL) { - int32_t numFloats = framesPerBlock * getSamplesPerFrame(); - mSampleBlock = new float[numFloats]{0.0f}; -} - -AudioFloatBlockPort::~AudioFloatBlockPort() { - delete[] mSampleBlock; -} - -/***************************************************************************/ -int32_t AudioFloatOutputPort::pullData(int64_t framePosition, int32_t numFrames) { - numFrames = std::min(getFramesPerBlock(), numFrames); - return mParent.pullData(framePosition, numFrames); -} - -// These need to be in the .cpp file because of forward cross references. -void AudioFloatOutputPort::connect(AudioFloatInputPort *port) { - port->connect(this); -} - -void AudioFloatOutputPort::disconnect(AudioFloatInputPort *port) { - port->disconnect(this); -} - -/***************************************************************************/ -int32_t AudioFloatInputPort::pullData(int64_t framePosition, int32_t numFrames) { - return (mConnected == NULL) - ? std::min(getFramesPerBlock(), numFrames) - : mConnected->pullData(framePosition, numFrames); -} - -float *AudioFloatInputPort::getBlock() { - if (mConnected == NULL) { - return AudioFloatBlockPort::getBlock(); // loaded using setValue() - } else { - return mConnected->getBlock(); - } -} - -/***************************************************************************/ -int32_t AudioSink::pull(int32_t numFrames) { - int32_t actualFrames = input.pullData(mFramePosition, numFrames); - mFramePosition += actualFrames; - return actualFrames; -} \ No newline at end of file diff --git a/media/libaaudio/src/flowgraph/AudioProcessorBase.h b/media/libaaudio/src/flowgraph/AudioProcessorBase.h deleted file mode 100644 index 972932ff46d657050ba2d1b87c47da4b84e2a3ba..0000000000000000000000000000000000000000 --- a/media/libaaudio/src/flowgraph/AudioProcessorBase.h +++ /dev/null @@ -1,293 +0,0 @@ -/* - * 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. - */ - -/* - * AudioProcessorBase.h - * - * Audio processing node and ports that can be used in a simple data flow graph. - */ - -#ifndef FLOWGRAPH_AUDIO_PROCESSOR_BASE_H -#define FLOWGRAPH_AUDIO_PROCESSOR_BASE_H - -#include -#include -#include -#include -#include -#include - -// TODO consider publishing all header files under "include/libaaudio/FlowGraph.h" - -namespace flowgraph { - -// Default block size that can be overridden when the AudioFloatBlockPort is created. -// If it is too small then we will have too much overhead from switching between nodes. -// If it is too high then we will thrash the caches. -constexpr int kDefaultBlockSize = 8; // arbitrary - -class AudioFloatInputPort; - -/***************************************************************************/ -class AudioProcessorBase { -public: - virtual ~AudioProcessorBase() = default; - - /** - * Perform custom function. - * - * @param framePosition index of first frame to be processed - * @param numFrames maximum number of frames requested for processing - * @return number of frames actually processed - */ - virtual int32_t onProcess(int64_t framePosition, int32_t numFrames) = 0; - - /** - * If the framePosition is at or after the last frame position then call onProcess(). - * This prevents infinite recursion in case of cyclic graphs. - * It also prevents nodes upstream from a branch from being executed twice. - * - * @param framePosition - * @param numFrames - * @return - */ - int32_t pullData(int64_t framePosition, int32_t numFrames); - -protected: - int64_t mLastFramePosition = -1; // Start at -1 so that the first pull works. - -private: - int32_t mFramesValid = 0; // num valid frames in the block -}; - -/***************************************************************************/ -/** - * This is a connector that allows data to flow between modules. - */ -class AudioPort { -public: - AudioPort(AudioProcessorBase &parent, int32_t samplesPerFrame) - : mParent(parent) - , mSamplesPerFrame(samplesPerFrame) { - } - - // Ports are often declared public. So let's make them non-copyable. - AudioPort(const AudioPort&) = delete; - AudioPort& operator=(const AudioPort&) = delete; - - int32_t getSamplesPerFrame() const { - return mSamplesPerFrame; - } - -protected: - AudioProcessorBase &mParent; - -private: - const int32_t mSamplesPerFrame = 1; -}; - -/***************************************************************************/ -/** - * This port contains a float type buffer. - * The size is framesPerBlock * samplesPerFrame). - */ -class AudioFloatBlockPort : public AudioPort { -public: - AudioFloatBlockPort(AudioProcessorBase &mParent, - int32_t samplesPerFrame, - int32_t framesPerBlock = kDefaultBlockSize - ); - - virtual ~AudioFloatBlockPort(); - - int32_t getFramesPerBlock() const { - return mFramesPerBlock; - } - -protected: - - /** - * @return buffer internal to the port or from a connected port - */ - virtual float *getBlock() { - return mSampleBlock; - } - - -private: - const int32_t mFramesPerBlock = 1; - float *mSampleBlock = nullptr; // allocated in constructor -}; - -/***************************************************************************/ -/** - * The results of a module are stored in the buffer of the output ports. - */ -class AudioFloatOutputPort : public AudioFloatBlockPort { -public: - AudioFloatOutputPort(AudioProcessorBase &parent, int32_t samplesPerFrame) - : AudioFloatBlockPort(parent, samplesPerFrame) { - } - - virtual ~AudioFloatOutputPort() = default; - - using AudioFloatBlockPort::getBlock; - - /** - * Call the parent module's onProcess() method. - * That may pull data from its inputs and recursively - * process the entire graph. - * @return number of frames actually pulled - */ - int32_t pullData(int64_t framePosition, int32_t numFrames); - - /** - * Connect to the input of another module. - * An input port can only have one connection. - * An output port can have multiple connections. - * If you connect a second output port to an input port - * then it overwrites the previous connection. - * - * This not thread safe. Do not modify the graph topology form another thread while running. - */ - void connect(AudioFloatInputPort *port); - - /** - * Disconnect from the input of another module. - * This not thread safe. - */ - void disconnect(AudioFloatInputPort *port); -}; - -/***************************************************************************/ -class AudioFloatInputPort : public AudioFloatBlockPort { -public: - AudioFloatInputPort(AudioProcessorBase &parent, int32_t samplesPerFrame) - : AudioFloatBlockPort(parent, samplesPerFrame) { - } - - virtual ~AudioFloatInputPort() = default; - - /** - * If connected to an output port then this will return - * that output ports buffers. - * If not connected then it returns the input ports own buffer - * which can be loaded using setValue(). - */ - float *getBlock() override; - - /** - * Pull data from any output port that is connected. - */ - int32_t pullData(int64_t framePosition, int32_t numFrames); - - /** - * Write every value of the float buffer. - * This value will be ignored if an output port is connected - * to this port. - */ - void setValue(float value) { - int numFloats = kDefaultBlockSize * getSamplesPerFrame(); - float *buffer = getBlock(); - for (int i = 0; i < numFloats; i++) { - *buffer++ = value; - } - } - - /** - * Connect to the output of another module. - * An input port can only have one connection. - * An output port can have multiple connections. - * This not thread safe. - */ - void connect(AudioFloatOutputPort *port) { - assert(getSamplesPerFrame() == port->getSamplesPerFrame()); - mConnected = port; - } - - void disconnect(AudioFloatOutputPort *port) { - assert(mConnected == port); - (void) port; - mConnected = nullptr; - } - - void disconnect() { - mConnected = nullptr; - } - -private: - AudioFloatOutputPort *mConnected = nullptr; -}; - -/***************************************************************************/ -class AudioSource : public AudioProcessorBase { -public: - explicit AudioSource(int32_t channelCount) - : output(*this, channelCount) { - } - - virtual ~AudioSource() = default; - - AudioFloatOutputPort output; - - void setData(const void *data, int32_t numFrames) { - mData = data; - mSizeInFrames = numFrames; - mFrameIndex = 0; - } - -protected: - const void *mData = nullptr; - int32_t mSizeInFrames = 0; // number of frames in mData - int32_t mFrameIndex = 0; // index of next frame to be processed -}; - -/***************************************************************************/ -class AudioSink : public AudioProcessorBase { -public: - explicit AudioSink(int32_t channelCount) - : input(*this, channelCount) { - } - - virtual ~AudioSink() = default; - - AudioFloatInputPort input; - - /** - * Do nothing. The work happens in the read() method. - * - * @param framePosition index of first frame to be processed - * @param numFrames - * @return number of frames actually processed - */ - int32_t onProcess(int64_t framePosition, int32_t numFrames) override { - (void) framePosition; - (void) numFrames; - return 0; - }; - - virtual int32_t read(void *data, int32_t numFrames) = 0; - -protected: - int32_t pull(int32_t numFrames); - -private: - int64_t mFramePosition = 0; -}; - -} /* namespace flowgraph */ - -#endif /* FLOWGRAPH_AUDIO_PROCESSOR_BASE_H */ diff --git a/media/libaaudio/src/flowgraph/ChannelCountConverter.cpp b/media/libaaudio/src/flowgraph/ChannelCountConverter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dc804277d5b7dc75a4233fe90d389973b4d78db6 --- /dev/null +++ b/media/libaaudio/src/flowgraph/ChannelCountConverter.cpp @@ -0,0 +1,52 @@ +/* + * 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. + */ + +#include +#include "FlowGraphNode.h" +#include "ChannelCountConverter.h" + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +ChannelCountConverter::ChannelCountConverter( + int32_t inputChannelCount, + int32_t outputChannelCount) + : input(*this, inputChannelCount) + , output(*this, outputChannelCount) { +} + +ChannelCountConverter::~ChannelCountConverter() = default; + +int32_t ChannelCountConverter::onProcess(int32_t numFrames) { + const float *inputBuffer = input.getBuffer(); + float *outputBuffer = output.getBuffer(); + int32_t inputChannelCount = input.getSamplesPerFrame(); + int32_t outputChannelCount = output.getSamplesPerFrame(); + for (int i = 0; i < numFrames; i++) { + int inputChannel = 0; + for (int outputChannel = 0; outputChannel < outputChannelCount; outputChannel++) { + // Copy input channels to output channels. + // Wrap if we run out of inputs. + // Discard if we run out of outputs. + outputBuffer[outputChannel] = inputBuffer[inputChannel]; + inputChannel = (inputChannel == inputChannelCount) + ? 0 : inputChannel + 1; + } + inputBuffer += inputChannelCount; + outputBuffer += outputChannelCount; + } + return numFrames; +} + diff --git a/media/libaaudio/src/flowgraph/ChannelCountConverter.h b/media/libaaudio/src/flowgraph/ChannelCountConverter.h new file mode 100644 index 0000000000000000000000000000000000000000..858f4d4f1459b4f5112fc261959c66eb98b70218 --- /dev/null +++ b/media/libaaudio/src/flowgraph/ChannelCountConverter.h @@ -0,0 +1,52 @@ +/* + * 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 FLOWGRAPH_CHANNEL_COUNT_CONVERTER_H +#define FLOWGRAPH_CHANNEL_COUNT_CONVERTER_H + +#include +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +/** + * Change the number of number of channels without mixing. + * When increasing the channel count, duplicate input channels. + * When decreasing the channel count, drop input channels. + */ + class ChannelCountConverter : public FlowGraphNode { + public: + explicit ChannelCountConverter( + int32_t inputChannelCount, + int32_t outputChannelCount); + + virtual ~ChannelCountConverter(); + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "ChannelCountConverter"; + } + + FlowGraphPortFloatInput input; + FlowGraphPortFloatOutput output; + }; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_CHANNEL_COUNT_CONVERTER_H diff --git a/media/libaaudio/src/flowgraph/ClipToRange.cpp b/media/libaaudio/src/flowgraph/ClipToRange.cpp index bd9c22adfb892916711397764e567c763bd2967c..c6ad0b0fef4c020a8a9b497e0b4f36ec05cdd7d7 100644 --- a/media/libaaudio/src/flowgraph/ClipToRange.cpp +++ b/media/libaaudio/src/flowgraph/ClipToRange.cpp @@ -16,25 +16,23 @@ #include #include -#include "AudioProcessorBase.h" +#include "FlowGraphNode.h" #include "ClipToRange.h" -using namespace flowgraph; +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; ClipToRange::ClipToRange(int32_t channelCount) - : input(*this, channelCount) - , output(*this, channelCount) { + : FlowGraphFilter(channelCount) { } -int32_t ClipToRange::onProcess(int64_t framePosition, int32_t numFrames) { - int32_t framesToProcess = input.pullData(framePosition, numFrames); - const float *inputBuffer = input.getBlock(); - float *outputBuffer = output.getBlock(); +int32_t ClipToRange::onProcess(int32_t numFrames) { + const float *inputBuffer = input.getBuffer(); + float *outputBuffer = output.getBuffer(); - int32_t numSamples = framesToProcess * output.getSamplesPerFrame(); + int32_t numSamples = numFrames * output.getSamplesPerFrame(); for (int32_t i = 0; i < numSamples; i++) { *outputBuffer++ = std::min(mMaximum, std::max(mMinimum, *inputBuffer++)); } - return framesToProcess; + return numFrames; } diff --git a/media/libaaudio/src/flowgraph/ClipToRange.h b/media/libaaudio/src/flowgraph/ClipToRange.h index 9eef254942c405b1dc0a0229e4b758e7a1f1c605..2fddeee7103c11d9e46473bf4910a07dd22b348f 100644 --- a/media/libaaudio/src/flowgraph/ClipToRange.h +++ b/media/libaaudio/src/flowgraph/ClipToRange.h @@ -21,22 +21,22 @@ #include #include -#include "AudioProcessorBase.h" +#include "FlowGraphNode.h" -namespace flowgraph { +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { // This is 3 dB, (10^(3/20)), to match the maximum headroom in AudioTrack for float data. // It is designed to allow occasional transient peaks. constexpr float kDefaultMaxHeadroom = 1.41253754f; constexpr float kDefaultMinHeadroom = -kDefaultMaxHeadroom; -class ClipToRange : public AudioProcessorBase { +class ClipToRange : public FlowGraphFilter { public: explicit ClipToRange(int32_t channelCount); virtual ~ClipToRange() = default; - int32_t onProcess(int64_t framePosition, int32_t numFrames) override; + int32_t onProcess(int32_t numFrames) override; void setMinimum(float min) { mMinimum = min; @@ -54,14 +54,15 @@ public: return mMaximum; } - AudioFloatInputPort input; - AudioFloatOutputPort output; + const char *getName() override { + return "ClipToRange"; + } private: float mMinimum = kDefaultMinHeadroom; float mMaximum = kDefaultMaxHeadroom; }; -} /* namespace flowgraph */ +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ #endif //FLOWGRAPH_CLIP_TO_RANGE_H diff --git a/media/libaaudio/src/flowgraph/FlowGraphNode.cpp b/media/libaaudio/src/flowgraph/FlowGraphNode.cpp new file mode 100644 index 0000000000000000000000000000000000000000..012abe7ef106795b663edd2af6fcf77bbce2476f --- /dev/null +++ b/media/libaaudio/src/flowgraph/FlowGraphNode.cpp @@ -0,0 +1,114 @@ +/* + * 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. + */ + +#include "stdio.h" +#include +#include +#include "FlowGraphNode.h" + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +/***************************************************************************/ +int32_t FlowGraphNode::pullData(int32_t numFrames, int64_t callCount) { + int32_t frameCount = numFrames; + // Prevent recursion and multiple execution of nodes. + if (callCount > mLastCallCount) { + mLastCallCount = callCount; + if (mDataPulledAutomatically) { + // Pull from all the upstream nodes. + for (auto &port : mInputPorts) { + // TODO fix bug of leaving unused data in some ports if using multiple AudioSource + frameCount = port.get().pullData(callCount, frameCount); + } + } + if (frameCount > 0) { + frameCount = onProcess(frameCount); + } + mLastFrameCount = frameCount; + } else { + frameCount = mLastFrameCount; + } + return frameCount; +} + +void FlowGraphNode::pullReset() { + if (!mBlockRecursion) { + mBlockRecursion = true; // for cyclic graphs + // Pull reset from all the upstream nodes. + for (auto &port : mInputPorts) { + port.get().pullReset(); + } + mBlockRecursion = false; + reset(); + } +} + +void FlowGraphNode::reset() { + mLastFrameCount = 0; + mLastCallCount = kInitialCallCount; +} + +/***************************************************************************/ +FlowGraphPortFloat::FlowGraphPortFloat(FlowGraphNode &parent, + int32_t samplesPerFrame, + int32_t framesPerBuffer) + : FlowGraphPort(parent, samplesPerFrame) + , mFramesPerBuffer(framesPerBuffer) + , mBuffer(nullptr) { + size_t numFloats = static_cast(framesPerBuffer) * getSamplesPerFrame(); + mBuffer = std::make_unique(numFloats); +} + +/***************************************************************************/ +int32_t FlowGraphPortFloatOutput::pullData(int64_t callCount, int32_t numFrames) { + numFrames = std::min(getFramesPerBuffer(), numFrames); + return mContainingNode.pullData(numFrames, callCount); +} + +void FlowGraphPortFloatOutput::pullReset() { + mContainingNode.pullReset(); +} + +// These need to be in the .cpp file because of forward cross references. +void FlowGraphPortFloatOutput::connect(FlowGraphPortFloatInput *port) { + port->connect(this); +} + +void FlowGraphPortFloatOutput::disconnect(FlowGraphPortFloatInput *port) { + port->disconnect(this); +} + +/***************************************************************************/ +int32_t FlowGraphPortFloatInput::pullData(int64_t callCount, int32_t numFrames) { + return (mConnected == nullptr) + ? std::min(getFramesPerBuffer(), numFrames) + : mConnected->pullData(callCount, numFrames); +} +void FlowGraphPortFloatInput::pullReset() { + if (mConnected != nullptr) mConnected->pullReset(); +} + +float *FlowGraphPortFloatInput::getBuffer() { + if (mConnected == nullptr) { + return FlowGraphPortFloat::getBuffer(); // loaded using setValue() + } else { + return mConnected->getBuffer(); + } +} + +int32_t FlowGraphSink::pullData(int32_t numFrames) { + return FlowGraphNode::pullData(numFrames, getLastCallCount() + 1); +} diff --git a/media/libaaudio/src/flowgraph/FlowGraphNode.h b/media/libaaudio/src/flowgraph/FlowGraphNode.h new file mode 100644 index 0000000000000000000000000000000000000000..2884c081fb6b61a76a87b4c9fa6e79080dc2ed76 --- /dev/null +++ b/media/libaaudio/src/flowgraph/FlowGraphNode.h @@ -0,0 +1,450 @@ +/* + * 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. + */ + +/* + * FlowGraph.h + * + * Processing node and ports that can be used in a simple data flow graph. + * This was designed to work with audio but could be used for other + * types of data. + */ + +#ifndef FLOWGRAPH_FLOW_GRAPH_NODE_H +#define FLOWGRAPH_FLOW_GRAPH_NODE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +// TODO Move these classes into separate files. +// TODO Review use of raw pointers for connect(). Maybe use smart pointers but need to avoid +// run-time deallocation in audio thread. + +// Set flags FLOWGRAPH_ANDROID_INTERNAL and FLOWGRAPH_OUTER_NAMESPACE based on whether compiler +// flag __ANDROID_NDK__ is defined. __ANDROID_NDK__ should be defined in oboe and not aaudio. + +#ifndef FLOWGRAPH_ANDROID_INTERNAL +#ifdef __ANDROID_NDK__ +#define FLOWGRAPH_ANDROID_INTERNAL 0 +#else +#define FLOWGRAPH_ANDROID_INTERNAL 1 +#endif // __ANDROID_NDK__ +#endif // FLOWGRAPH_ANDROID_INTERNAL + +#ifndef FLOWGRAPH_OUTER_NAMESPACE +#ifdef __ANDROID_NDK__ +#define FLOWGRAPH_OUTER_NAMESPACE oboe +#else +#define FLOWGRAPH_OUTER_NAMESPACE aaudio +#endif // __ANDROID_NDK__ +#endif // FLOWGRAPH_OUTER_NAMESPACE + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +// Default block size that can be overridden when the FlowGraphPortFloat is created. +// If it is too small then we will have too much overhead from switching between nodes. +// If it is too high then we will thrash the caches. +constexpr int kDefaultBufferSize = 8; // arbitrary + +class FlowGraphPort; +class FlowGraphPortFloatInput; + +/***************************************************************************/ +/** + * Base class for all nodes in the flowgraph. + */ +class FlowGraphNode { +public: + FlowGraphNode() = default; + virtual ~FlowGraphNode() = default; + + /** + * Read from the input ports, + * generate multiple frames of data then write the results to the output ports. + * + * @param numFrames maximum number of frames requested for processing + * @return number of frames actually processed + */ + virtual int32_t onProcess(int32_t numFrames) = 0; + + /** + * If the callCount is at or after the previous callCount then call + * pullData on all of the upstreamNodes. + * Then call onProcess(). + * This prevents infinite recursion in case of cyclic graphs. + * It also prevents nodes upstream from a branch from being executed twice. + * + * @param callCount + * @param numFrames + * @return number of frames valid + */ + int32_t pullData(int32_t numFrames, int64_t callCount); + + /** + * Recursively reset all the nodes in the graph, starting from a Sink. + * + * This must not be called at the same time as pullData! + */ + void pullReset(); + + /** + * Reset framePosition counters. + */ + virtual void reset(); + + void addInputPort(FlowGraphPort &port) { + mInputPorts.emplace_back(port); + } + + bool isDataPulledAutomatically() const { + return mDataPulledAutomatically; + } + + /** + * Set true if you want the data pulled through the graph automatically. + * This is the default. + * + * Set false if you want to pull the data from the input ports in the onProcess() method. + * You might do this, for example, in a sample rate converting node. + * + * @param automatic + */ + void setDataPulledAutomatically(bool automatic) { + mDataPulledAutomatically = automatic; + } + + virtual const char *getName() { + return "FlowGraph"; + } + + int64_t getLastCallCount() { + return mLastCallCount; + } + +protected: + + static constexpr int64_t kInitialCallCount = -1; + int64_t mLastCallCount = kInitialCallCount; + + std::vector> mInputPorts; + +private: + bool mDataPulledAutomatically = true; + bool mBlockRecursion = false; + int32_t mLastFrameCount = 0; + +}; + +/***************************************************************************/ +/** + * This is a connector that allows data to flow between modules. + * + * The ports are the primary means of interacting with a module. + * So they are generally declared as public. + * + */ +class FlowGraphPort { +public: + FlowGraphPort(FlowGraphNode &parent, int32_t samplesPerFrame) + : mContainingNode(parent) + , mSamplesPerFrame(samplesPerFrame) { + } + + virtual ~FlowGraphPort() = default; + + // Ports are often declared public. So let's make them non-copyable. + FlowGraphPort(const FlowGraphPort&) = delete; + FlowGraphPort& operator=(const FlowGraphPort&) = delete; + + int32_t getSamplesPerFrame() const { + return mSamplesPerFrame; + } + + virtual int32_t pullData(int64_t framePosition, int32_t numFrames) = 0; + + virtual void pullReset() {} + +protected: + FlowGraphNode &mContainingNode; + +private: + const int32_t mSamplesPerFrame = 1; +}; + +/***************************************************************************/ +/** + * This port contains a 32-bit float buffer that can contain several frames of data. + * Processing the data in a block improves performance. + * + * The size is framesPerBuffer * samplesPerFrame). + */ +class FlowGraphPortFloat : public FlowGraphPort { +public: + FlowGraphPortFloat(FlowGraphNode &parent, + int32_t samplesPerFrame, + int32_t framesPerBuffer = kDefaultBufferSize + ); + + virtual ~FlowGraphPortFloat() = default; + + int32_t getFramesPerBuffer() const { + return mFramesPerBuffer; + } + +protected: + + /** + * @return buffer internal to the port or from a connected port + */ + virtual float *getBuffer() { + return mBuffer.get(); + } + +private: + const int32_t mFramesPerBuffer = 1; + std::unique_ptr mBuffer; // allocated in constructor +}; + +/***************************************************************************/ +/** + * The results of a node's processing are stored in the buffers of the output ports. + */ +class FlowGraphPortFloatOutput : public FlowGraphPortFloat { +public: + FlowGraphPortFloatOutput(FlowGraphNode &parent, int32_t samplesPerFrame) + : FlowGraphPortFloat(parent, samplesPerFrame) { + } + + virtual ~FlowGraphPortFloatOutput() = default; + + using FlowGraphPortFloat::getBuffer; + + /** + * Connect to the input of another module. + * An input port can only have one connection. + * An output port can have multiple connections. + * If you connect a second output port to an input port + * then it overwrites the previous connection. + * + * This not thread safe. Do not modify the graph topology from another thread while running. + * Also do not delete a module while it is connected to another port if the graph is running. + */ + void connect(FlowGraphPortFloatInput *port); + + /** + * Disconnect from the input of another module. + * This not thread safe. + */ + void disconnect(FlowGraphPortFloatInput *port); + + /** + * Call the parent module's onProcess() method. + * That may pull data from its inputs and recursively + * process the entire graph. + * @return number of frames actually pulled + */ + int32_t pullData(int64_t framePosition, int32_t numFrames) override; + + + void pullReset() override; + +}; + +/***************************************************************************/ + +/** + * An input port for streaming audio data. + * You can set a value that will be used for processing. + * If you connect an output port to this port then its value will be used instead. + */ +class FlowGraphPortFloatInput : public FlowGraphPortFloat { +public: + FlowGraphPortFloatInput(FlowGraphNode &parent, int32_t samplesPerFrame) + : FlowGraphPortFloat(parent, samplesPerFrame) { + // Add to parent so it can pull data from each input. + parent.addInputPort(*this); + } + + virtual ~FlowGraphPortFloatInput() = default; + + /** + * If connected to an output port then this will return + * that output ports buffers. + * If not connected then it returns the input ports own buffer + * which can be loaded using setValue(). + */ + float *getBuffer() override; + + /** + * Write every value of the float buffer. + * This value will be ignored if an output port is connected + * to this port. + */ + void setValue(float value) { + int numFloats = kDefaultBufferSize * getSamplesPerFrame(); + float *buffer = getBuffer(); + for (int i = 0; i < numFloats; i++) { + *buffer++ = value; + } + } + + /** + * Connect to the output of another module. + * An input port can only have one connection. + * An output port can have multiple connections. + * This not thread safe. + */ + void connect(FlowGraphPortFloatOutput *port) { + assert(getSamplesPerFrame() == port->getSamplesPerFrame()); + mConnected = port; + } + + void disconnect(FlowGraphPortFloatOutput *port) { + assert(mConnected == port); + (void) port; + mConnected = nullptr; + } + + void disconnect() { + mConnected = nullptr; + } + + /** + * Pull data from any output port that is connected. + */ + int32_t pullData(int64_t framePosition, int32_t numFrames) override; + + void pullReset() override; + +private: + FlowGraphPortFloatOutput *mConnected = nullptr; +}; + +/***************************************************************************/ + +/** + * Base class for an edge node in a graph that has no upstream nodes. + * It outputs data but does not consume data. + * By default, it will read its data from an external buffer. + */ +class FlowGraphSource : public FlowGraphNode { +public: + explicit FlowGraphSource(int32_t channelCount) + : output(*this, channelCount) { + } + + virtual ~FlowGraphSource() = default; + + FlowGraphPortFloatOutput output; +}; + +/***************************************************************************/ + +/** + * Base class for an edge node in a graph that has no upstream nodes. + * It outputs data but does not consume data. + * By default, it will read its data from an external buffer. + */ +class FlowGraphSourceBuffered : public FlowGraphSource { +public: + explicit FlowGraphSourceBuffered(int32_t channelCount) + : FlowGraphSource(channelCount) {} + + virtual ~FlowGraphSourceBuffered() = default; + + /** + * Specify buffer that the node will read from. + * + * @param data TODO Consider using std::shared_ptr. + * @param numFrames + */ + void setData(const void *data, int32_t numFrames) { + mData = data; + mSizeInFrames = numFrames; + mFrameIndex = 0; + } + +protected: + const void *mData = nullptr; + int32_t mSizeInFrames = 0; // number of frames in mData + int32_t mFrameIndex = 0; // index of next frame to be processed +}; + +/***************************************************************************/ +/** + * Base class for an edge node in a graph that has no downstream nodes. + * It consumes data but does not output data. + * This graph will be executed when data is read() from this node + * by pulling data from upstream nodes. + */ +class FlowGraphSink : public FlowGraphNode { +public: + explicit FlowGraphSink(int32_t channelCount) + : input(*this, channelCount) { + } + + virtual ~FlowGraphSink() = default; + + FlowGraphPortFloatInput input; + + /** + * Do nothing. The work happens in the read() method. + * + * @param numFrames + * @return number of frames actually processed + */ + int32_t onProcess(int32_t numFrames) override { + return numFrames; + } + + virtual int32_t read(void *data, int32_t numFrames) = 0; + +protected: + /** + * Pull data through the graph using this nodes last callCount. + * @param numFrames + * @return + */ + int32_t pullData(int32_t numFrames); +}; + +/***************************************************************************/ +/** + * Base class for a node that has an input and an output with the same number of channels. + * This may include traditional filters, eg. FIR, but also include + * any processing node that converts input to output. + */ +class FlowGraphFilter : public FlowGraphNode { +public: + explicit FlowGraphFilter(int32_t channelCount) + : input(*this, channelCount) + , output(*this, channelCount) { + } + + virtual ~FlowGraphFilter() = default; + + FlowGraphPortFloatInput input; + FlowGraphPortFloatOutput output; +}; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif /* FLOWGRAPH_FLOW_GRAPH_NODE_H */ diff --git a/media/libaaudio/src/flowgraph/FlowgraphUtilities.h b/media/libaaudio/src/flowgraph/FlowgraphUtilities.h index b750410d191e0a6975888deb43d123c351a4d26d..5e90588ce9f25f043ec2448e0359a925fd708c8a 100644 --- a/media/libaaudio/src/flowgraph/FlowgraphUtilities.h +++ b/media/libaaudio/src/flowgraph/FlowgraphUtilities.h @@ -19,7 +19,7 @@ #include -using namespace flowgraph; +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; class FlowgraphUtilities { public: @@ -39,9 +39,9 @@ static int32_t clamp32FromFloat(float f) static const float limneg = -1.; if (f <= limneg) { - return -0x80000000; /* or 0x80000000 */ + return INT32_MIN; } else if (f >= limpos) { - return 0x7fffffff; + return INT32_MAX; } f *= scale; /* integer conversion is through truncation (though int to float is not). diff --git a/media/libaaudio/src/flowgraph/ManyToMultiConverter.cpp b/media/libaaudio/src/flowgraph/ManyToMultiConverter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4f973bc3ff8e7c219c1214de34d4e190bc01d846 --- /dev/null +++ b/media/libaaudio/src/flowgraph/ManyToMultiConverter.cpp @@ -0,0 +1,47 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "ManyToMultiConverter.h" + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +ManyToMultiConverter::ManyToMultiConverter(int32_t channelCount) + : inputs(channelCount) + , output(*this, channelCount) { + for (int i = 0; i < channelCount; i++) { + inputs[i] = std::make_unique(*this, 1); + } +} + +int32_t ManyToMultiConverter::onProcess(int32_t numFrames) { + int32_t channelCount = output.getSamplesPerFrame(); + + for (int ch = 0; ch < channelCount; ch++) { + const float *inputBuffer = inputs[ch]->getBuffer(); + float *outputBuffer = output.getBuffer() + ch; + + for (int i = 0; i < numFrames; i++) { + // read one, write into the proper interleaved output channel + float sample = *inputBuffer++; + *outputBuffer = sample; + outputBuffer += channelCount; // advance to next multichannel frame + } + } + return numFrames; +} + diff --git a/media/libaaudio/src/flowgraph/ManyToMultiConverter.h b/media/libaaudio/src/flowgraph/ManyToMultiConverter.h new file mode 100644 index 0000000000000000000000000000000000000000..50644cf913dc4073796f08edd4dc42d45c61d403 --- /dev/null +++ b/media/libaaudio/src/flowgraph/ManyToMultiConverter.h @@ -0,0 +1,53 @@ +/* + * Copyright 2018 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 FLOWGRAPH_MANY_TO_MULTI_CONVERTER_H +#define FLOWGRAPH_MANY_TO_MULTI_CONVERTER_H + +#include +#include +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +/** + * Combine multiple mono inputs into one interleaved multi-channel output. + */ +class ManyToMultiConverter : public flowgraph::FlowGraphNode { +public: + explicit ManyToMultiConverter(int32_t channelCount); + + virtual ~ManyToMultiConverter() = default; + + int32_t onProcess(int numFrames) override; + + void setEnabled(bool /*enabled*/) {} + + std::vector> inputs; + flowgraph::FlowGraphPortFloatOutput output; + + const char *getName() override { + return "ManyToMultiConverter"; + } + +private: +}; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_MANY_TO_MULTI_CONVERTER_H diff --git a/media/libaaudio/src/flowgraph/MonoBlend.cpp b/media/libaaudio/src/flowgraph/MonoBlend.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4fd75e19df25cad0feab55c3169865702c260fee --- /dev/null +++ b/media/libaaudio/src/flowgraph/MonoBlend.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "MonoBlend.h" + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +MonoBlend::MonoBlend(int32_t channelCount) + : FlowGraphFilter(channelCount) + , mInvChannelCount(1. / channelCount) +{ +} + +int32_t MonoBlend::onProcess(int32_t numFrames) { + int32_t channelCount = output.getSamplesPerFrame(); + const float *inputBuffer = input.getBuffer(); + float *outputBuffer = output.getBuffer(); + + for (size_t i = 0; i < numFrames; ++i) { + float accum = 0; + for (size_t j = 0; j < channelCount; ++j) { + accum += *inputBuffer++; + } + accum *= mInvChannelCount; + for (size_t j = 0; j < channelCount; ++j) { + *outputBuffer++ = accum; + } + } + + return numFrames; +} diff --git a/media/libaaudio/src/flowgraph/MonoBlend.h b/media/libaaudio/src/flowgraph/MonoBlend.h new file mode 100644 index 0000000000000000000000000000000000000000..f8d44ffb848bb839bb0045251c7e402c8773a776 --- /dev/null +++ b/media/libaaudio/src/flowgraph/MonoBlend.h @@ -0,0 +1,48 @@ +/* + * Copyright 2021 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 FLOWGRAPH_MONO_BLEND_H +#define FLOWGRAPH_MONO_BLEND_H + +#include +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +/** + * Combine data between multiple channels so each channel is an average + * of all channels. + */ +class MonoBlend : public FlowGraphFilter { +public: + explicit MonoBlend(int32_t channelCount); + + virtual ~MonoBlend() = default; + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "MonoBlend"; + } +private: + const float mInvChannelCount; +}; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_MONO_BLEND diff --git a/media/libaaudio/src/flowgraph/MonoToMultiConverter.cpp b/media/libaaudio/src/flowgraph/MonoToMultiConverter.cpp index 78aad52fa2949d49cc19b5637e1257d025976a0c..33eed5232aae80d989a77e4dd5d459271b917611 100644 --- a/media/libaaudio/src/flowgraph/MonoToMultiConverter.cpp +++ b/media/libaaudio/src/flowgraph/MonoToMultiConverter.cpp @@ -14,34 +14,28 @@ * limitations under the License. */ - #include -#include "AudioProcessorBase.h" +#include "FlowGraphNode.h" #include "MonoToMultiConverter.h" -using namespace flowgraph; +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; -MonoToMultiConverter::MonoToMultiConverter(int32_t channelCount) +MonoToMultiConverter::MonoToMultiConverter(int32_t outputChannelCount) : input(*this, 1) - , output(*this, channelCount) { + , output(*this, outputChannelCount) { } -MonoToMultiConverter::~MonoToMultiConverter() { } - -int32_t MonoToMultiConverter::onProcess(int64_t framePosition, int32_t numFrames) { - int32_t framesToProcess = input.pullData(framePosition, numFrames); - - const float *inputBuffer = input.getBlock(); - float *outputBuffer = output.getBlock(); +int32_t MonoToMultiConverter::onProcess(int32_t numFrames) { + const float *inputBuffer = input.getBuffer(); + float *outputBuffer = output.getBuffer(); int32_t channelCount = output.getSamplesPerFrame(); - // TODO maybe move to audio_util as audio_mono_to_multi() - for (int i = 0; i < framesToProcess; i++) { + for (int i = 0; i < numFrames; i++) { // read one, write many float sample = *inputBuffer++; for (int channel = 0; channel < channelCount; channel++) { *outputBuffer++ = sample; } } - return framesToProcess; + return numFrames; } diff --git a/media/libaaudio/src/flowgraph/MonoToMultiConverter.h b/media/libaaudio/src/flowgraph/MonoToMultiConverter.h index 34d53c713a7b3872366bce7b8084c72fa441c4e1..762edb0a649450a4cb7bd620efd5ae697a6b708f 100644 --- a/media/libaaudio/src/flowgraph/MonoToMultiConverter.h +++ b/media/libaaudio/src/flowgraph/MonoToMultiConverter.h @@ -14,29 +14,36 @@ * limitations under the License. */ - #ifndef FLOWGRAPH_MONO_TO_MULTI_CONVERTER_H #define FLOWGRAPH_MONO_TO_MULTI_CONVERTER_H #include #include -#include "AudioProcessorBase.h" +#include "FlowGraphNode.h" -namespace flowgraph { +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { -class MonoToMultiConverter : public AudioProcessorBase { +/** + * Convert a monophonic stream to a multi-channel interleaved stream + * with the same signal on each channel. + */ +class MonoToMultiConverter : public FlowGraphNode { public: - explicit MonoToMultiConverter(int32_t channelCount); + explicit MonoToMultiConverter(int32_t outputChannelCount); + + virtual ~MonoToMultiConverter() = default; - virtual ~MonoToMultiConverter(); + int32_t onProcess(int32_t numFrames) override; - int32_t onProcess(int64_t framePosition, int32_t numFrames) override; + const char *getName() override { + return "MonoToMultiConverter"; + } - AudioFloatInputPort input; - AudioFloatOutputPort output; + FlowGraphPortFloatInput input; + FlowGraphPortFloatOutput output; }; -} /* namespace flowgraph */ +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ #endif //FLOWGRAPH_MONO_TO_MULTI_CONVERTER_H diff --git a/media/libaaudio/src/flowgraph/MultiToManyConverter.cpp b/media/libaaudio/src/flowgraph/MultiToManyConverter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5cdf594bc7998ad2e868d8efef602667286a9ecc --- /dev/null +++ b/media/libaaudio/src/flowgraph/MultiToManyConverter.cpp @@ -0,0 +1,47 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "FlowGraphNode.h" +#include "MultiToManyConverter.h" + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +MultiToManyConverter::MultiToManyConverter(int32_t channelCount) + : outputs(channelCount) + , input(*this, channelCount) { + for (int i = 0; i < channelCount; i++) { + outputs[i] = std::make_unique(*this, 1); + } +} + +MultiToManyConverter::~MultiToManyConverter() = default; + +int32_t MultiToManyConverter::onProcess(int32_t numFrames) { + int32_t channelCount = input.getSamplesPerFrame(); + + for (int ch = 0; ch < channelCount; ch++) { + const float *inputBuffer = input.getBuffer() + ch; + float *outputBuffer = outputs[ch]->getBuffer(); + + for (int i = 0; i < numFrames; i++) { + *outputBuffer++ = *inputBuffer; + inputBuffer += channelCount; + } + } + + return numFrames; +} diff --git a/media/libaaudio/src/flowgraph/MultiToManyConverter.h b/media/libaaudio/src/flowgraph/MultiToManyConverter.h new file mode 100644 index 0000000000000000000000000000000000000000..dee40a2a4977835338c78b94f2c71e49227e90a9 --- /dev/null +++ b/media/libaaudio/src/flowgraph/MultiToManyConverter.h @@ -0,0 +1,49 @@ +/* + * Copyright 2021 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 FLOWGRAPH_MULTI_TO_MANY_CONVERTER_H +#define FLOWGRAPH_MULTI_TO_MANY_CONVERTER_H + +#include +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +/** + * Convert a multi-channel interleaved stream to multiple mono-channel + * outputs + */ + class MultiToManyConverter : public FlowGraphNode { + public: + explicit MultiToManyConverter(int32_t channelCount); + + virtual ~MultiToManyConverter(); + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "MultiToManyConverter"; + } + + std::vector> outputs; + flowgraph::FlowGraphPortFloatInput input; + }; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_MULTI_TO_MANY_CONVERTER_H \ No newline at end of file diff --git a/media/libaaudio/src/flowgraph/MultiToMonoConverter.cpp b/media/libaaudio/src/flowgraph/MultiToMonoConverter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..467f95ea6305e49aa1bcd73a13e54a022b2a5ae0 --- /dev/null +++ b/media/libaaudio/src/flowgraph/MultiToMonoConverter.cpp @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#include +#include "FlowGraphNode.h" +#include "MultiToMonoConverter.h" + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; + +MultiToMonoConverter::MultiToMonoConverter(int32_t inputChannelCount) + : input(*this, inputChannelCount) + , output(*this, 1) { +} + +MultiToMonoConverter::~MultiToMonoConverter() = default; + +int32_t MultiToMonoConverter::onProcess(int32_t numFrames) { + const float *inputBuffer = input.getBuffer(); + float *outputBuffer = output.getBuffer(); + int32_t channelCount = input.getSamplesPerFrame(); + for (int i = 0; i < numFrames; i++) { + // read first channel of multi stream, write many + *outputBuffer++ = *inputBuffer; + inputBuffer += channelCount; + } + return numFrames; +} + diff --git a/media/libaaudio/src/flowgraph/MultiToMonoConverter.h b/media/libaaudio/src/flowgraph/MultiToMonoConverter.h new file mode 100644 index 0000000000000000000000000000000000000000..bf5b7b601b362e0ab69d47b550e62cc957da4e79 --- /dev/null +++ b/media/libaaudio/src/flowgraph/MultiToMonoConverter.h @@ -0,0 +1,49 @@ +/* + * 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 FLOWGRAPH_MULTI_TO_MONO_CONVERTER_H +#define FLOWGRAPH_MULTI_TO_MONO_CONVERTER_H + +#include +#include + +#include "FlowGraphNode.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +/** + * Convert a multi-channel interleaved stream to a monophonic stream + * by extracting channel[0]. + */ + class MultiToMonoConverter : public FlowGraphNode { + public: + explicit MultiToMonoConverter(int32_t inputChannelCount); + + virtual ~MultiToMonoConverter(); + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "MultiToMonoConverter"; + } + + FlowGraphPortFloatInput input; + FlowGraphPortFloatOutput output; + }; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_MULTI_TO_MONO_CONVERTER_H diff --git a/media/libaaudio/src/flowgraph/RampLinear.cpp b/media/libaaudio/src/flowgraph/RampLinear.cpp index a260828696dc1bd6297346a0e90ef804f03ca660..80ac72ad47eaaf37704fbce1324cb26efdff15ee 100644 --- a/media/libaaudio/src/flowgraph/RampLinear.cpp +++ b/media/libaaudio/src/flowgraph/RampLinear.cpp @@ -14,20 +14,15 @@ * limitations under the License. */ -#define LOG_TAG "RampLinear" -//#define LOG_NDEBUG 0 -#include - #include #include -#include "AudioProcessorBase.h" +#include "FlowGraphNode.h" #include "RampLinear.h" -using namespace flowgraph; +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; RampLinear::RampLinear(int32_t channelCount) - : input(*this, channelCount) - , output(*this, channelCount) { + : FlowGraphFilter(channelCount) { mTarget.store(1.0f); } @@ -37,16 +32,19 @@ void RampLinear::setLengthInFrames(int32_t frames) { void RampLinear::setTarget(float target) { mTarget.store(target); + // If the ramp has not been used then start immediately at this level. + if (mLastCallCount == kInitialCallCount) { + forceCurrent(target); + } } float RampLinear::interpolateCurrent() { return mLevelTo - (mRemaining * mScaler); } -int32_t RampLinear::onProcess(int64_t framePosition, int32_t numFrames) { - int32_t framesToProcess = input.pullData(framePosition, numFrames); - const float *inputBuffer = input.getBlock(); - float *outputBuffer = output.getBlock(); +int32_t RampLinear::onProcess(int32_t numFrames) { + const float *inputBuffer = input.getBuffer(); + float *outputBuffer = output.getBuffer(); int32_t channelCount = output.getSamplesPerFrame(); float target = getTarget(); @@ -55,12 +53,10 @@ int32_t RampLinear::onProcess(int64_t framePosition, int32_t numFrames) { mLevelFrom = interpolateCurrent(); mLevelTo = target; mRemaining = mLengthInFrames; - ALOGV("%s() mLevelFrom = %f, mLevelTo = %f, mRemaining = %d, mScaler = %f", - __func__, mLevelFrom, mLevelTo, mRemaining, mScaler); mScaler = (mLevelTo - mLevelFrom) / mLengthInFrames; // for interpolation } - int32_t framesLeft = framesToProcess; + int32_t framesLeft = numFrames; if (mRemaining > 0) { // Ramping? This doesn't happen very often. int32_t framesToRamp = std::min(framesLeft, mRemaining); @@ -81,5 +77,5 @@ int32_t RampLinear::onProcess(int64_t framePosition, int32_t numFrames) { *outputBuffer++ = *inputBuffer++ * mLevelTo; } - return framesToProcess; + return numFrames; } diff --git a/media/libaaudio/src/flowgraph/RampLinear.h b/media/libaaudio/src/flowgraph/RampLinear.h index bdc8f41c66b4185dd645c62e0f71296559614b00..3839d6ef968b1c22fea5f3b23113a98b15aedeed 100644 --- a/media/libaaudio/src/flowgraph/RampLinear.h +++ b/media/libaaudio/src/flowgraph/RampLinear.h @@ -21,17 +21,25 @@ #include #include -#include "AudioProcessorBase.h" +#include "FlowGraphNode.h" -namespace flowgraph { +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { -class RampLinear : public AudioProcessorBase { +/** + * When the target is modified then the output will ramp smoothly + * between the original and the new target value. + * This can be used to smooth out control values and reduce pops. + * + * The target may be updated while a ramp is in progress, which will trigger + * a new ramp from the current value. + */ +class RampLinear : public FlowGraphFilter { public: explicit RampLinear(int32_t channelCount); virtual ~RampLinear() = default; - int32_t onProcess(int64_t framePosition, int32_t numFrames) override; + int32_t onProcess(int32_t numFrames) override; /** * This is used for the next ramp. @@ -66,8 +74,9 @@ public: mLevelTo = level; } - AudioFloatInputPort input; - AudioFloatOutputPort output; + const char *getName() override { + return "RampLinear"; + } private: @@ -82,6 +91,6 @@ private: float mLevelTo = 0.0f; }; -} /* namespace flowgraph */ +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ #endif //FLOWGRAPH_RAMP_LINEAR_H diff --git a/media/libaaudio/src/flowgraph/SampleRateConverter.cpp b/media/libaaudio/src/flowgraph/SampleRateConverter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a15fcb8ce91fc76194a2698c28d30d63d456fb26 --- /dev/null +++ b/media/libaaudio/src/flowgraph/SampleRateConverter.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SampleRateConverter.h" + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; +using namespace RESAMPLER_OUTER_NAMESPACE::resampler; + +SampleRateConverter::SampleRateConverter(int32_t channelCount, + MultiChannelResampler &resampler) + : FlowGraphFilter(channelCount) + , mResampler(resampler) { + setDataPulledAutomatically(false); +} + +void SampleRateConverter::reset() { + FlowGraphNode::reset(); + mInputCursor = kInitialCallCount; +} + +// Return true if there is a sample available. +bool SampleRateConverter::isInputAvailable() { + // If we have consumed all of the input data then go out and get some more. + if (mInputCursor >= mNumValidInputFrames) { + mInputCallCount++; + mNumValidInputFrames = input.pullData(mInputCallCount, input.getFramesPerBuffer()); + mInputCursor = 0; + } + return (mInputCursor < mNumValidInputFrames); +} + +const float *SampleRateConverter::getNextInputFrame() { + const float *inputBuffer = input.getBuffer(); + return &inputBuffer[mInputCursor++ * input.getSamplesPerFrame()]; +} + +int32_t SampleRateConverter::onProcess(int32_t numFrames) { + float *outputBuffer = output.getBuffer(); + int32_t channelCount = output.getSamplesPerFrame(); + int framesLeft = numFrames; + while (framesLeft > 0) { + // Gather input samples as needed. + if(mResampler.isWriteNeeded()) { + if (isInputAvailable()) { + const float *frame = getNextInputFrame(); + mResampler.writeNextFrame(frame); + } else { + break; + } + } else { + // Output frame is interpolated from input samples. + mResampler.readNextFrame(outputBuffer); + outputBuffer += channelCount; + framesLeft--; + } + } + return numFrames - framesLeft; +} diff --git a/media/libaaudio/src/flowgraph/SampleRateConverter.h b/media/libaaudio/src/flowgraph/SampleRateConverter.h new file mode 100644 index 0000000000000000000000000000000000000000..f883e6ce285bd0c01f156a290182df6c9d396e1e --- /dev/null +++ b/media/libaaudio/src/flowgraph/SampleRateConverter.h @@ -0,0 +1,63 @@ +/* + * Copyright 2019 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 FLOWGRAPH_SAMPLE_RATE_CONVERTER_H +#define FLOWGRAPH_SAMPLE_RATE_CONVERTER_H + +#include +#include + +#include "FlowGraphNode.h" +#include "resampler/MultiChannelResampler.h" + +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { + +class SampleRateConverter : public FlowGraphFilter { +public: + explicit SampleRateConverter(int32_t channelCount, + resampler::MultiChannelResampler &mResampler); + + virtual ~SampleRateConverter() = default; + + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "SampleRateConverter"; + } + + void reset() override; + +private: + + // Return true if there is a sample available. + bool isInputAvailable(); + + // This assumes data is available. Only call after calling isInputAvailable(). + const float *getNextInputFrame(); + + resampler::MultiChannelResampler &mResampler; + + int32_t mInputCursor = 0; // offset into the input port buffer + int32_t mNumValidInputFrames = 0; // number of valid frames currently in the input port buffer + // We need our own callCount for upstream calls because calls occur at a different rate. + // This means we cannot have cyclic graphs or merges that contain an SRC. + int64_t mInputCallCount = 0; + +}; + +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ + +#endif //FLOWGRAPH_SAMPLE_RATE_CONVERTER_H diff --git a/media/libaaudio/src/flowgraph/SinkFloat.cpp b/media/libaaudio/src/flowgraph/SinkFloat.cpp index fb3dcbc937addde516bfeb3d1d3339886abf69fa..940a66be8816f8a75cf9921c31c393b2e60f09a0 100644 --- a/media/libaaudio/src/flowgraph/SinkFloat.cpp +++ b/media/libaaudio/src/flowgraph/SinkFloat.cpp @@ -16,31 +16,31 @@ #include #include -#include "AudioProcessorBase.h" +#include "FlowGraphNode.h" #include "SinkFloat.h" -using namespace flowgraph; +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; SinkFloat::SinkFloat(int32_t channelCount) - : AudioSink(channelCount) { + : FlowGraphSink(channelCount) { } int32_t SinkFloat::read(void *data, int32_t numFrames) { float *floatData = (float *) data; - int32_t channelCount = input.getSamplesPerFrame(); + const int32_t channelCount = input.getSamplesPerFrame(); int32_t framesLeft = numFrames; while (framesLeft > 0) { // Run the graph and pull data through the input port. - int32_t framesRead = pull(framesLeft); - if (framesRead <= 0) { + int32_t framesPulled = pullData(framesLeft); + if (framesPulled <= 0) { break; } - const float *signal = input.getBlock(); - int32_t numSamples = framesRead * channelCount; + const float *signal = input.getBuffer(); + int32_t numSamples = framesPulled * channelCount; memcpy(floatData, signal, numSamples * sizeof(float)); floatData += numSamples; - framesLeft -= framesRead; + framesLeft -= framesPulled; } return numFrames - framesLeft; } diff --git a/media/libaaudio/src/flowgraph/SinkFloat.h b/media/libaaudio/src/flowgraph/SinkFloat.h index 7775c08135a7693ceb39d44f21c15c7cca425376..3be3f5d49e389998e601c7dc4a8c23d1ef9a7d8f 100644 --- a/media/libaaudio/src/flowgraph/SinkFloat.h +++ b/media/libaaudio/src/flowgraph/SinkFloat.h @@ -21,18 +21,25 @@ #include #include -#include "AudioProcessorBase.h" +#include "FlowGraphNode.h" -namespace flowgraph { +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { -class SinkFloat : public AudioSink { +/** + * AudioSink that lets you read data as 32-bit floats. + */ +class SinkFloat : public FlowGraphSink { public: explicit SinkFloat(int32_t channelCount); + ~SinkFloat() override = default; int32_t read(void *data, int32_t numFrames) override; + const char *getName() override { + return "SinkFloat"; + } }; -} /* namespace flowgraph */ +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ #endif //FLOWGRAPH_SINK_FLOAT_H diff --git a/media/libaaudio/src/flowgraph/SinkI16.cpp b/media/libaaudio/src/flowgraph/SinkI16.cpp index ffec8f537126a5244c7303af58cdfbf286947f7c..690431ceb7758f457173bb85dbee921cb761aeb7 100644 --- a/media/libaaudio/src/flowgraph/SinkI16.cpp +++ b/media/libaaudio/src/flowgraph/SinkI16.cpp @@ -17,17 +17,16 @@ #include #include -#ifdef __ANDROID__ +#include "SinkI16.h" + +#if FLOWGRAPH_ANDROID_INTERNAL #include #endif -#include "AudioProcessorBase.h" -#include "SinkI16.h" - -using namespace flowgraph; +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; SinkI16::SinkI16(int32_t channelCount) - : AudioSink(channelCount) {} + : FlowGraphSink(channelCount) {} int32_t SinkI16::read(void *data, int32_t numFrames) { int16_t *shortData = (int16_t *) data; @@ -36,13 +35,13 @@ int32_t SinkI16::read(void *data, int32_t numFrames) { int32_t framesLeft = numFrames; while (framesLeft > 0) { // Run the graph and pull data through the input port. - int32_t framesRead = pull(framesLeft); + int32_t framesRead = pullData(framesLeft); if (framesRead <= 0) { break; } - const float *signal = input.getBlock(); + const float *signal = input.getBuffer(); int32_t numSamples = framesRead * channelCount; -#ifdef __ANDROID__ +#if FLOWGRAPH_ANDROID_INTERNAL memcpy_to_i16_from_float(shortData, signal, numSamples); shortData += numSamples; signal += numSamples; diff --git a/media/libaaudio/src/flowgraph/SinkI16.h b/media/libaaudio/src/flowgraph/SinkI16.h index 6d86266e954b885785af36c4fa81c3222992f1f0..bf124f55b112eac9e63ab9ab39c7a9b3ce354ab0 100644 --- a/media/libaaudio/src/flowgraph/SinkI16.h +++ b/media/libaaudio/src/flowgraph/SinkI16.h @@ -20,17 +20,24 @@ #include #include -#include "AudioProcessorBase.h" +#include "FlowGraphNode.h" -namespace flowgraph { +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { -class SinkI16 : public AudioSink { +/** + * AudioSink that lets you read data as 16-bit signed integers. + */ +class SinkI16 : public FlowGraphSink { public: explicit SinkI16(int32_t channelCount); int32_t read(void *data, int32_t numFrames) override; + + const char *getName() override { + return "SinkI16"; + } }; -} /* namespace flowgraph */ +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ #endif //FLOWGRAPH_SINK_I16_H diff --git a/media/libaaudio/src/flowgraph/SinkI24.cpp b/media/libaaudio/src/flowgraph/SinkI24.cpp index 0cb077d45356b890c96a6aa7b76bfc501b8f9de3..d4f68b684792ac32aff1862321a85cb2bcbaf2fe 100644 --- a/media/libaaudio/src/flowgraph/SinkI24.cpp +++ b/media/libaaudio/src/flowgraph/SinkI24.cpp @@ -15,19 +15,20 @@ */ #include -#include +#include -#ifdef __ANDROID__ -#include -#endif -#include "AudioProcessorBase.h" +#include "FlowGraphNode.h" #include "SinkI24.h" -using namespace flowgraph; +#if FLOWGRAPH_ANDROID_INTERNAL +#include +#endif + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; SinkI24::SinkI24(int32_t channelCount) - : AudioSink(channelCount) {} + : FlowGraphSink(channelCount) {} int32_t SinkI24::read(void *data, int32_t numFrames) { uint8_t *byteData = (uint8_t *) data; @@ -36,13 +37,13 @@ int32_t SinkI24::read(void *data, int32_t numFrames) { int32_t framesLeft = numFrames; while (framesLeft > 0) { // Run the graph and pull data through the input port. - int32_t framesRead = pull(framesLeft); + int32_t framesRead = pullData(framesLeft); if (framesRead <= 0) { break; } - const float *floatData = input.getBlock(); + const float *floatData = input.getBuffer(); int32_t numSamples = framesRead * channelCount; -#ifdef __ANDROID__ +#if FLOWGRAPH_ANDROID_INTERNAL memcpy_to_p24_from_float(byteData, floatData, numSamples); static const int kBytesPerI24Packed = 3; byteData += numSamples * kBytesPerI24Packed; diff --git a/media/libaaudio/src/flowgraph/SinkI24.h b/media/libaaudio/src/flowgraph/SinkI24.h index 5b9b5054217bda47a3b19b474e6fb5332e9dae8a..6b4135e97ea9f076f4c065c68b2f0e1c45bfd107 100644 --- a/media/libaaudio/src/flowgraph/SinkI24.h +++ b/media/libaaudio/src/flowgraph/SinkI24.h @@ -20,17 +20,25 @@ #include #include -#include "AudioProcessorBase.h" +#include "FlowGraphNode.h" -namespace flowgraph { +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { -class SinkI24 : public AudioSink { +/** + * AudioSink that lets you read data as packed 24-bit signed integers. + * The sample size is 3 bytes. + */ +class SinkI24 : public FlowGraphSink { public: explicit SinkI24(int32_t channelCount); int32_t read(void *data, int32_t numFrames) override; + + const char *getName() override { + return "SinkI24"; + } }; -} /* namespace flowgraph */ +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ #endif //FLOWGRAPH_SINK_I24_H diff --git a/media/libaaudio/src/flowgraph/SinkI32.cpp b/media/libaaudio/src/flowgraph/SinkI32.cpp index eab863d2e7a7d4a0e6bfc5a4d7971a2e7602893e..b14b3d2a42f366f547259fde34f2fca42d1ef32f 100644 --- a/media/libaaudio/src/flowgraph/SinkI32.cpp +++ b/media/libaaudio/src/flowgraph/SinkI32.cpp @@ -14,18 +14,18 @@ * limitations under the License. */ -#ifdef __ANDROID__ -#include -#endif - -#include "AudioProcessorBase.h" +#include "FlowGraphNode.h" #include "FlowgraphUtilities.h" #include "SinkI32.h" -using namespace flowgraph; +#if FLOWGRAPH_ANDROID_INTERNAL +#include +#endif + +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; SinkI32::SinkI32(int32_t channelCount) - : AudioSink(channelCount) {} + : FlowGraphSink(channelCount) {} int32_t SinkI32::read(void *data, int32_t numFrames) { int32_t *intData = (int32_t *) data; @@ -34,13 +34,13 @@ int32_t SinkI32::read(void *data, int32_t numFrames) { int32_t framesLeft = numFrames; while (framesLeft > 0) { // Run the graph and pull data through the input port. - int32_t framesRead = pull(framesLeft); + int32_t framesRead = pullData(framesLeft); if (framesRead <= 0) { break; } - const float *signal = input.getBlock(); + const float *signal = input.getBuffer(); int32_t numSamples = framesRead * channelCount; -#ifdef __ANDROID__ +#if FLOWGRAPH_ANDROID_INTERNAL memcpy_to_i32_from_float(intData, signal, numSamples); intData += numSamples; signal += numSamples; diff --git a/media/libaaudio/src/flowgraph/SinkI32.h b/media/libaaudio/src/flowgraph/SinkI32.h index 09d23b7ceb70edc278a342c78485d884fdcbbb44..35507ea1a4db5dd130726a5770cc2c5094384e93 100644 --- a/media/libaaudio/src/flowgraph/SinkI32.h +++ b/media/libaaudio/src/flowgraph/SinkI32.h @@ -19,18 +19,22 @@ #include -#include "AudioProcessorBase.h" +#include "FlowGraphNode.h" -namespace flowgraph { +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { -class SinkI32 : public AudioSink { +class SinkI32 : public FlowGraphSink { public: explicit SinkI32(int32_t channelCount); ~SinkI32() override = default; int32_t read(void *data, int32_t numFrames) override; + + const char *getName() override { + return "SinkI32"; + } }; -} /* namespace flowgraph */ +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ #endif //FLOWGRAPH_SINK_I32_H diff --git a/media/libaaudio/src/flowgraph/SourceFloat.cpp b/media/libaaudio/src/flowgraph/SourceFloat.cpp index 4bb674fdfca78c91aaff020bc134e3f4babb8e64..a0c88275a03931a4e6af391f7c354537aced17bb 100644 --- a/media/libaaudio/src/flowgraph/SourceFloat.cpp +++ b/media/libaaudio/src/flowgraph/SourceFloat.cpp @@ -16,23 +16,22 @@ #include #include -#include "AudioProcessorBase.h" +#include "FlowGraphNode.h" #include "SourceFloat.h" -using namespace flowgraph; +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; SourceFloat::SourceFloat(int32_t channelCount) - : AudioSource(channelCount) { + : FlowGraphSourceBuffered(channelCount) { } -int32_t SourceFloat::onProcess(int64_t framePosition, int32_t numFrames) { +int32_t SourceFloat::onProcess(int32_t numFrames) { + float *outputBuffer = output.getBuffer(); + const int32_t channelCount = output.getSamplesPerFrame(); - float *outputBuffer = output.getBlock(); - int32_t channelCount = output.getSamplesPerFrame(); - - int32_t framesLeft = mSizeInFrames - mFrameIndex; - int32_t framesToProcess = std::min(numFrames, framesLeft); - int32_t numSamples = framesToProcess * channelCount; + const int32_t framesLeft = mSizeInFrames - mFrameIndex; + const int32_t framesToProcess = std::min(numFrames, framesLeft); + const int32_t numSamples = framesToProcess * channelCount; const float *floatBase = (float *) mData; const float *floatData = &floatBase[mFrameIndex * channelCount]; diff --git a/media/libaaudio/src/flowgraph/SourceFloat.h b/media/libaaudio/src/flowgraph/SourceFloat.h index e6eed9f90afb10ac2389582a55959a93db4ef7d3..78053e511866c9cf0fa015d70e9d30903d7d325c 100644 --- a/media/libaaudio/src/flowgraph/SourceFloat.h +++ b/media/libaaudio/src/flowgraph/SourceFloat.h @@ -20,17 +20,25 @@ #include #include -#include "AudioProcessorBase.h" +#include "FlowGraphNode.h" -namespace flowgraph { +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { -class SourceFloat : public AudioSource { +/** + * AudioSource that reads a block of pre-defined float data. + */ +class SourceFloat : public FlowGraphSourceBuffered { public: explicit SourceFloat(int32_t channelCount); + ~SourceFloat() override = default; + + int32_t onProcess(int32_t numFrames) override; - int32_t onProcess(int64_t framePosition, int32_t numFrames) override; + const char *getName() override { + return "SourceFloat"; + } }; -} /* namespace flowgraph */ +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ #endif //FLOWGRAPH_SOURCE_FLOAT_H diff --git a/media/libaaudio/src/flowgraph/SourceI16.cpp b/media/libaaudio/src/flowgraph/SourceI16.cpp index c3fcec28e25b9f493b34528daa97f7644aa7be42..16cd2b3dc835cf0cf4d792ef8a16e91b80e49f36 100644 --- a/media/libaaudio/src/flowgraph/SourceI16.cpp +++ b/media/libaaudio/src/flowgraph/SourceI16.cpp @@ -17,21 +17,21 @@ #include #include -#ifdef __ANDROID__ +#include "FlowGraphNode.h" +#include "SourceI16.h" + +#if FLOWGRAPH_ANDROID_INTERNAL #include #endif -#include "AudioProcessorBase.h" -#include "SourceI16.h" - -using namespace flowgraph; +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; SourceI16::SourceI16(int32_t channelCount) - : AudioSource(channelCount) { + : FlowGraphSourceBuffered(channelCount) { } -int32_t SourceI16::onProcess(int64_t framePosition, int32_t numFrames) { - float *floatData = output.getBlock(); +int32_t SourceI16::onProcess(int32_t numFrames) { + float *floatData = output.getBuffer(); int32_t channelCount = output.getSamplesPerFrame(); int32_t framesLeft = mSizeInFrames - mFrameIndex; @@ -41,7 +41,7 @@ int32_t SourceI16::onProcess(int64_t framePosition, int32_t numFrames) { const int16_t *shortBase = static_cast(mData); const int16_t *shortData = &shortBase[mFrameIndex * channelCount]; -#ifdef __ANDROID__ +#if FLOWGRAPH_ANDROID_INTERNAL memcpy_to_float_from_i16(floatData, shortData, numSamples); #else for (int i = 0; i < numSamples; i++) { diff --git a/media/libaaudio/src/flowgraph/SourceI16.h b/media/libaaudio/src/flowgraph/SourceI16.h index 2b116cfb2d311923cb8deccc96a0205c23684de0..923890cb5033b961c6d67ce123e48d95cb0dd979 100644 --- a/media/libaaudio/src/flowgraph/SourceI16.h +++ b/media/libaaudio/src/flowgraph/SourceI16.h @@ -20,17 +20,23 @@ #include #include -#include "AudioProcessorBase.h" +#include "FlowGraphNode.h" -namespace flowgraph { - -class SourceI16 : public AudioSource { +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { +/** + * AudioSource that reads a block of pre-defined 16-bit integer data. + */ +class SourceI16 : public FlowGraphSourceBuffered { public: explicit SourceI16(int32_t channelCount); - int32_t onProcess(int64_t framePosition, int32_t numFrames) override; + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "SourceI16"; + } }; -} /* namespace flowgraph */ +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ #endif //FLOWGRAPH_SOURCE_I16_H diff --git a/media/libaaudio/src/flowgraph/SourceI24.cpp b/media/libaaudio/src/flowgraph/SourceI24.cpp index 097954ec8a0d1cab2787739b4dee20b866a98120..d54b9588c7e48794f3697a109066329986d2f75b 100644 --- a/media/libaaudio/src/flowgraph/SourceI24.cpp +++ b/media/libaaudio/src/flowgraph/SourceI24.cpp @@ -15,25 +15,25 @@ */ #include -#include +#include -#ifdef __ANDROID__ +#include "FlowGraphNode.h" +#include "SourceI24.h" + +#if FLOWGRAPH_ANDROID_INTERNAL #include #endif -#include "AudioProcessorBase.h" -#include "SourceI24.h" - -using namespace flowgraph; +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; constexpr int kBytesPerI24Packed = 3; SourceI24::SourceI24(int32_t channelCount) - : AudioSource(channelCount) { + : FlowGraphSourceBuffered(channelCount) { } -int32_t SourceI24::onProcess(int64_t framePosition, int32_t numFrames) { - float *floatData = output.getBlock(); +int32_t SourceI24::onProcess(int32_t numFrames) { + float *floatData = output.getBuffer(); int32_t channelCount = output.getSamplesPerFrame(); int32_t framesLeft = mSizeInFrames - mFrameIndex; @@ -43,7 +43,7 @@ int32_t SourceI24::onProcess(int64_t framePosition, int32_t numFrames) { const uint8_t *byteBase = (uint8_t *) mData; const uint8_t *byteData = &byteBase[mFrameIndex * channelCount * kBytesPerI24Packed]; -#ifdef __ANDROID__ +#if FLOWGRAPH_ANDROID_INTERNAL memcpy_to_float_from_p24(floatData, byteData, numSamples); #else static const float scale = 1. / (float)(1UL << 31); diff --git a/media/libaaudio/src/flowgraph/SourceI24.h b/media/libaaudio/src/flowgraph/SourceI24.h index 2ed6f18b9b3f312090e437018d7abd13c007aea3..fb66d4a4db746f94450180aabe3bbd69a0139d4d 100644 --- a/media/libaaudio/src/flowgraph/SourceI24.h +++ b/media/libaaudio/src/flowgraph/SourceI24.h @@ -17,19 +17,27 @@ #ifndef FLOWGRAPH_SOURCE_I24_H #define FLOWGRAPH_SOURCE_I24_H -#include +#include +#include -#include "AudioProcessorBase.h" +#include "FlowGraphNode.h" -namespace flowgraph { +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { -class SourceI24 : public AudioSource { +/** + * AudioSource that reads a block of pre-defined 24-bit packed integer data. + */ +class SourceI24 : public FlowGraphSourceBuffered { public: explicit SourceI24(int32_t channelCount); - int32_t onProcess(int64_t framePosition, int32_t numFrames) override; + int32_t onProcess(int32_t numFrames) override; + + const char *getName() override { + return "SourceI24"; + } }; -} /* namespace flowgraph */ +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ #endif //FLOWGRAPH_SOURCE_I24_H diff --git a/media/libaaudio/src/flowgraph/SourceI32.cpp b/media/libaaudio/src/flowgraph/SourceI32.cpp index e8177ad20c55ac98909a7e1eb1b3735510b34989..b1c8f75ab6e28570f9e14ae0c3ad4a6a35acdc26 100644 --- a/media/libaaudio/src/flowgraph/SourceI32.cpp +++ b/media/libaaudio/src/flowgraph/SourceI32.cpp @@ -17,31 +17,31 @@ #include #include -#ifdef __ANDROID__ +#include "FlowGraphNode.h" +#include "SourceI32.h" + +#if FLOWGRAPH_ANDROID_INTERNAL #include #endif -#include "AudioProcessorBase.h" -#include "SourceI32.h" - -using namespace flowgraph; +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; SourceI32::SourceI32(int32_t channelCount) - : AudioSource(channelCount) { + : FlowGraphSourceBuffered(channelCount) { } -int32_t SourceI32::onProcess(int64_t framePosition, int32_t numFrames) { - float *floatData = output.getBlock(); - int32_t channelCount = output.getSamplesPerFrame(); +int32_t SourceI32::onProcess(int32_t numFrames) { + float *floatData = output.getBuffer(); + const int32_t channelCount = output.getSamplesPerFrame(); - int32_t framesLeft = mSizeInFrames - mFrameIndex; - int32_t framesToProcess = std::min(numFrames, framesLeft); - int32_t numSamples = framesToProcess * channelCount; + const int32_t framesLeft = mSizeInFrames - mFrameIndex; + const int32_t framesToProcess = std::min(numFrames, framesLeft); + const int32_t numSamples = framesToProcess * channelCount; const int32_t *intBase = static_cast(mData); const int32_t *intData = &intBase[mFrameIndex * channelCount]; -#ifdef __ANDROID__ +#if FLOWGRAPH_ANDROID_INTERNAL memcpy_to_float_from_i32(floatData, intData, numSamples); #else for (int i = 0; i < numSamples; i++) { diff --git a/media/libaaudio/src/flowgraph/SourceI32.h b/media/libaaudio/src/flowgraph/SourceI32.h index e50f9be453f69d8d61359cba39222959da28d903..71094690402f52af1f2b482f37b63d4dd7306741 100644 --- a/media/libaaudio/src/flowgraph/SourceI32.h +++ b/media/libaaudio/src/flowgraph/SourceI32.h @@ -19,21 +19,24 @@ #include -#include "AudioProcessorBase.h" +#include "FlowGraphNode.h" -namespace flowgraph { +namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph { -class SourceI32 : public AudioSource { +class SourceI32 : public FlowGraphSourceBuffered { public: explicit SourceI32(int32_t channelCount); ~SourceI32() override = default; - int32_t onProcess(int64_t framePosition, int32_t numFrames) override; + int32_t onProcess(int32_t numFrames) override; + const char *getName() override { + return "SourceI32"; + } private: static constexpr float kScale = 1.0 / (1UL << 31); }; -} /* namespace flowgraph */ +} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */ #endif //FLOWGRAPH_SOURCE_I32_H diff --git a/media/libaaudio/src/flowgraph/resampler/HyperbolicCosineWindow.h b/media/libaaudio/src/flowgraph/resampler/HyperbolicCosineWindow.h new file mode 100644 index 0000000000000000000000000000000000000000..76ec0e70d5910c121a7fffba4bc7d997bb6299e5 --- /dev/null +++ b/media/libaaudio/src/flowgraph/resampler/HyperbolicCosineWindow.h @@ -0,0 +1,71 @@ +/* + * Copyright 2019 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 RESAMPLER_HYPERBOLIC_COSINE_WINDOW_H +#define RESAMPLER_HYPERBOLIC_COSINE_WINDOW_H + +#include + +#include "ResamplerDefinitions.h" + +namespace RESAMPLER_OUTER_NAMESPACE::resampler { + +/** + * Calculate a HyperbolicCosineWindow window centered at 0. + * This can be used in place of a Kaiser window. + * + * The code is based on an anonymous contribution by "a concerned citizen": + * https://dsp.stackexchange.com/questions/37714/kaiser-window-approximation + */ +class HyperbolicCosineWindow { +public: + HyperbolicCosineWindow() { + setStopBandAttenuation(60); + } + + /** + * @param attenuation typical values range from 30 to 90 dB + * @return beta + */ + double setStopBandAttenuation(double attenuation) { + double alpha = ((-325.1e-6 * attenuation + 0.1677) * attenuation) - 3.149; + setAlpha(alpha); + return alpha; + } + + void setAlpha(double alpha) { + mAlpha = alpha; + mInverseCoshAlpha = 1.0 / cosh(alpha); + } + + /** + * @param x ranges from -1.0 to +1.0 + */ + double operator()(double x) { + double x2 = x * x; + if (x2 >= 1.0) return 0.0; + double w = mAlpha * sqrt(1.0 - x2); + return cosh(w) * mInverseCoshAlpha; + } + +private: + double mAlpha = 0.0; + double mInverseCoshAlpha = 1.0; +}; + +} /* namespace RESAMPLER_OUTER_NAMESPACE::resampler */ + +#endif //RESAMPLER_HYPERBOLIC_COSINE_WINDOW_H diff --git a/media/libaaudio/src/flowgraph/resampler/IntegerRatio.cpp b/media/libaaudio/src/flowgraph/resampler/IntegerRatio.cpp new file mode 100644 index 0000000000000000000000000000000000000000..39e9b245963d8ca1b027bc26eed4f144f166a4f9 --- /dev/null +++ b/media/libaaudio/src/flowgraph/resampler/IntegerRatio.cpp @@ -0,0 +1,50 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "IntegerRatio.h" + +using namespace RESAMPLER_OUTER_NAMESPACE::resampler; + +// Enough primes to cover the common sample rates. +static const int kPrimes[] = { + 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, + 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, + 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, + 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199}; + +void IntegerRatio::reduce() { + for (int prime : kPrimes) { + if (mNumerator < prime || mDenominator < prime) { + break; + } + + // Find biggest prime factor for numerator. + while (true) { + int top = mNumerator / prime; + int bottom = mDenominator / prime; + if ((top >= 1) + && (bottom >= 1) + && (top * prime == mNumerator) // divided evenly? + && (bottom * prime == mDenominator)) { + mNumerator = top; + mDenominator = bottom; + } else { + break; + } + } + + } +} diff --git a/media/libaaudio/src/flowgraph/resampler/IntegerRatio.h b/media/libaaudio/src/flowgraph/resampler/IntegerRatio.h new file mode 100644 index 0000000000000000000000000000000000000000..a6b524ce1788ad006eeda618cd46d08bccb5a0ef --- /dev/null +++ b/media/libaaudio/src/flowgraph/resampler/IntegerRatio.h @@ -0,0 +1,54 @@ +/* + * Copyright 2019 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 RESAMPLER_INTEGER_RATIO_H +#define RESAMPLER_INTEGER_RATIO_H + +#include + +#include "ResamplerDefinitions.h" + +namespace RESAMPLER_OUTER_NAMESPACE::resampler { + +/** + * Represent the ratio of two integers. + */ +class IntegerRatio { +public: + IntegerRatio(int32_t numerator, int32_t denominator) + : mNumerator(numerator), mDenominator(denominator) {} + + /** + * Reduce by removing common prime factors. + */ + void reduce(); + + int32_t getNumerator() { + return mNumerator; + } + + int32_t getDenominator() { + return mDenominator; + } + +private: + int32_t mNumerator; + int32_t mDenominator; +}; + +} /* namespace RESAMPLER_OUTER_NAMESPACE::resampler */ + +#endif //RESAMPLER_INTEGER_RATIO_H diff --git a/media/libaaudio/src/flowgraph/resampler/KaiserWindow.h b/media/libaaudio/src/flowgraph/resampler/KaiserWindow.h new file mode 100644 index 0000000000000000000000000000000000000000..f99f9b4df902fe17c29bdbcfb72154e6d11e3837 --- /dev/null +++ b/media/libaaudio/src/flowgraph/resampler/KaiserWindow.h @@ -0,0 +1,90 @@ +/* + * Copyright 2019 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 RESAMPLER_KAISER_WINDOW_H +#define RESAMPLER_KAISER_WINDOW_H + +#include + +#include "ResamplerDefinitions.h" + +namespace RESAMPLER_OUTER_NAMESPACE::resampler { + +/** + * Calculate a Kaiser window centered at 0. + */ +class KaiserWindow { +public: + KaiserWindow() { + setStopBandAttenuation(60); + } + + /** + * @param attenuation typical values range from 30 to 90 dB + * @return beta + */ + double setStopBandAttenuation(double attenuation) { + double beta = 0.0; + if (attenuation > 50) { + beta = 0.1102 * (attenuation - 8.7); + } else if (attenuation >= 21) { + double a21 = attenuation - 21; + beta = 0.5842 * pow(a21, 0.4) + (0.07886 * a21); + } + setBeta(beta); + return beta; + } + + void setBeta(double beta) { + mBeta = beta; + mInverseBesselBeta = 1.0 / bessel(beta); + } + + /** + * @param x ranges from -1.0 to +1.0 + */ + double operator()(double x) { + double x2 = x * x; + if (x2 >= 1.0) return 0.0; + double w = mBeta * sqrt(1.0 - x2); + return bessel(w) * mInverseBesselBeta; + } + + // Approximation of a + // modified zero order Bessel function of the first kind. + // Based on a discussion at: + // https://dsp.stackexchange.com/questions/37714/kaiser-window-approximation + static double bessel(double x) { + double y = cosh(0.970941817426052 * x); + y += cosh(0.8854560256532099 * x); + y += cosh(0.7485107481711011 * x); + y += cosh(0.5680647467311558 * x); + y += cosh(0.3546048870425356 * x); + y += cosh(0.120536680255323 * x); + y *= 2; + y += cosh(x); + y /= 13; + return y; + } + +private: + double mBeta = 0.0; + double mInverseBesselBeta = 1.0; +}; + +} /* namespace RESAMPLER_OUTER_NAMESPACE::resampler */ + +#endif //RESAMPLER_KAISER_WINDOW_H diff --git a/media/libaaudio/src/flowgraph/resampler/LinearResampler.cpp b/media/libaaudio/src/flowgraph/resampler/LinearResampler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cb4932a5f89e9bd50f15e7f363caaecc699034ca --- /dev/null +++ b/media/libaaudio/src/flowgraph/resampler/LinearResampler.cpp @@ -0,0 +1,42 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "LinearResampler.h" + +using namespace RESAMPLER_OUTER_NAMESPACE::resampler; + +LinearResampler::LinearResampler(const MultiChannelResampler::Builder &builder) + : MultiChannelResampler(builder) { + mPreviousFrame = std::make_unique(getChannelCount()); + mCurrentFrame = std::make_unique(getChannelCount()); +} + +void LinearResampler::writeFrame(const float *frame) { + memcpy(mPreviousFrame.get(), mCurrentFrame.get(), sizeof(float) * getChannelCount()); + memcpy(mCurrentFrame.get(), frame, sizeof(float) * getChannelCount()); +} + +void LinearResampler::readFrame(float *frame) { + float *previous = mPreviousFrame.get(); + float *current = mCurrentFrame.get(); + float phase = (float) getIntegerPhase() / mDenominator; + // iterate across samples in the frame + for (int channel = 0; channel < getChannelCount(); channel++) { + float f0 = *previous++; + float f1 = *current++; + *frame++ = f0 + (phase * (f1 - f0)); + } +} diff --git a/media/libaaudio/src/flowgraph/resampler/LinearResampler.h b/media/libaaudio/src/flowgraph/resampler/LinearResampler.h new file mode 100644 index 0000000000000000000000000000000000000000..5434379cf98bbaede83cbd50d2119a45b67de519 --- /dev/null +++ b/media/libaaudio/src/flowgraph/resampler/LinearResampler.h @@ -0,0 +1,47 @@ +/* + * Copyright 2019 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 RESAMPLER_LINEAR_RESAMPLER_H +#define RESAMPLER_LINEAR_RESAMPLER_H + +#include +#include +#include + +#include "MultiChannelResampler.h" +#include "ResamplerDefinitions.h" + +namespace RESAMPLER_OUTER_NAMESPACE::resampler { + +/** + * Simple resampler that uses bi-linear interpolation. + */ +class LinearResampler : public MultiChannelResampler { +public: + explicit LinearResampler(const MultiChannelResampler::Builder &builder); + + void writeFrame(const float *frame) override; + + void readFrame(float *frame) override; + +private: + std::unique_ptr mPreviousFrame; + std::unique_ptr mCurrentFrame; +}; + +} /* namespace RESAMPLER_OUTER_NAMESPACE::resampler */ + +#endif //RESAMPLER_LINEAR_RESAMPLER_H diff --git a/media/libaaudio/src/flowgraph/resampler/MultiChannelResampler.cpp b/media/libaaudio/src/flowgraph/resampler/MultiChannelResampler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7193ff35dbfb1c4a881931c0cd820931e70cfd6e --- /dev/null +++ b/media/libaaudio/src/flowgraph/resampler/MultiChannelResampler.cpp @@ -0,0 +1,172 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "IntegerRatio.h" +#include "LinearResampler.h" +#include "MultiChannelResampler.h" +#include "PolyphaseResampler.h" +#include "PolyphaseResamplerMono.h" +#include "PolyphaseResamplerStereo.h" +#include "SincResampler.h" +#include "SincResamplerStereo.h" + +using namespace RESAMPLER_OUTER_NAMESPACE::resampler; + +MultiChannelResampler::MultiChannelResampler(const MultiChannelResampler::Builder &builder) + : mNumTaps(builder.getNumTaps()) + , mX(static_cast(builder.getChannelCount()) + * static_cast(builder.getNumTaps()) * 2) + , mSingleFrame(builder.getChannelCount()) + , mChannelCount(builder.getChannelCount()) + { + // Reduce sample rates to the smallest ratio. + // For example 44100/48000 would become 147/160. + IntegerRatio ratio(builder.getInputRate(), builder.getOutputRate()); + ratio.reduce(); + mNumerator = ratio.getNumerator(); + mDenominator = ratio.getDenominator(); + mIntegerPhase = mDenominator; +} + +// static factory method +MultiChannelResampler *MultiChannelResampler::make(int32_t channelCount, + int32_t inputRate, + int32_t outputRate, + Quality quality) { + Builder builder; + builder.setInputRate(inputRate); + builder.setOutputRate(outputRate); + builder.setChannelCount(channelCount); + + switch (quality) { + case Quality::Fastest: + builder.setNumTaps(2); + break; + case Quality::Low: + builder.setNumTaps(4); + break; + case Quality::Medium: + default: + builder.setNumTaps(8); + break; + case Quality::High: + builder.setNumTaps(16); + break; + case Quality::Best: + builder.setNumTaps(32); + break; + } + + // Set the cutoff frequency so that we do not get aliasing when down-sampling. + if (inputRate > outputRate) { + builder.setNormalizedCutoff(kDefaultNormalizedCutoff); + } + return builder.build(); +} + +MultiChannelResampler *MultiChannelResampler::Builder::build() { + if (getNumTaps() == 2) { + // Note that this does not do low pass filteringh. + return new LinearResampler(*this); + } + IntegerRatio ratio(getInputRate(), getOutputRate()); + ratio.reduce(); + bool usePolyphase = (getNumTaps() * ratio.getDenominator()) <= kMaxCoefficients; + if (usePolyphase) { + if (getChannelCount() == 1) { + return new PolyphaseResamplerMono(*this); + } else if (getChannelCount() == 2) { + return new PolyphaseResamplerStereo(*this); + } else { + return new PolyphaseResampler(*this); + } + } else { + // Use less optimized resampler that uses a float phaseIncrement. + // TODO mono resampler + if (getChannelCount() == 2) { + return new SincResamplerStereo(*this); + } else { + return new SincResampler(*this); + } + } +} + +void MultiChannelResampler::writeFrame(const float *frame) { + // Move cursor before write so that cursor points to last written frame in read. + if (--mCursor < 0) { + mCursor = getNumTaps() - 1; + } + float *dest = &mX[static_cast(mCursor) * static_cast(getChannelCount())]; + int offset = getNumTaps() * getChannelCount(); + for (int channel = 0; channel < getChannelCount(); channel++) { + // Write twice so we avoid having to wrap when reading. + dest[channel] = dest[channel + offset] = frame[channel]; + } +} + +float MultiChannelResampler::sinc(float radians) { + if (abs(radians) < 1.0e-9) return 1.0f; // avoid divide by zero + return sinf(radians) / radians; // Sinc function +} + +// Generate coefficients in the order they will be used by readFrame(). +// This is more complicated but readFrame() is called repeatedly and should be optimized. +void MultiChannelResampler::generateCoefficients(int32_t inputRate, + int32_t outputRate, + int32_t numRows, + double phaseIncrement, + float normalizedCutoff) { + mCoefficients.resize(static_cast(getNumTaps()) * static_cast(numRows)); + int coefficientIndex = 0; + double phase = 0.0; // ranges from 0.0 to 1.0, fraction between samples + // Stretch the sinc function for low pass filtering. + const float cutoffScaler = normalizedCutoff * + ((outputRate < inputRate) + ? ((float)outputRate / inputRate) + : ((float)inputRate / outputRate)); + const int numTapsHalf = getNumTaps() / 2; // numTaps must be even. + const float numTapsHalfInverse = 1.0f / numTapsHalf; + for (int i = 0; i < numRows; i++) { + float tapPhase = phase - numTapsHalf; + float gain = 0.0; // sum of raw coefficients + int gainCursor = coefficientIndex; + for (int tap = 0; tap < getNumTaps(); tap++) { + float radians = tapPhase * M_PI; + +#if MCR_USE_KAISER + float window = mKaiserWindow(tapPhase * numTapsHalfInverse); +#else + float window = mCoshWindow(static_cast(tapPhase) * numTapsHalfInverse); +#endif + float coefficient = sinc(radians * cutoffScaler) * window; + mCoefficients.at(coefficientIndex++) = coefficient; + gain += coefficient; + tapPhase += 1.0; + } + phase += phaseIncrement; + while (phase >= 1.0) { + phase -= 1.0; + } + + // Correct for gain variations. + float gainCorrection = 1.0 / gain; // normalize the gain + for (int tap = 0; tap < getNumTaps(); tap++) { + mCoefficients.at(gainCursor + tap) *= gainCorrection; + } + } +} diff --git a/media/libaaudio/src/flowgraph/resampler/MultiChannelResampler.h b/media/libaaudio/src/flowgraph/resampler/MultiChannelResampler.h new file mode 100644 index 0000000000000000000000000000000000000000..717f3fda5573aa9574071792d4efd3ce1009e4e4 --- /dev/null +++ b/media/libaaudio/src/flowgraph/resampler/MultiChannelResampler.h @@ -0,0 +1,274 @@ +/* + * Copyright 2019 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 RESAMPLER_MULTICHANNEL_RESAMPLER_H +#define RESAMPLER_MULTICHANNEL_RESAMPLER_H + +#include +#include +#include +#include + +#ifndef MCR_USE_KAISER +// It appears from the spectrogram that the HyperbolicCosine window leads to fewer artifacts. +// And it is faster to calculate. +#define MCR_USE_KAISER 0 +#endif + +#if MCR_USE_KAISER +#include "KaiserWindow.h" +#else +#include "HyperbolicCosineWindow.h" +#endif + +#include "ResamplerDefinitions.h" + +namespace RESAMPLER_OUTER_NAMESPACE::resampler { + +class MultiChannelResampler { + +public: + + enum class Quality : int32_t { + Fastest, + Low, + Medium, + High, + Best, + }; + + class Builder { + public: + /** + * Construct an optimal resampler based on the specified parameters. + * @return address of a resampler + */ + MultiChannelResampler *build(); + + /** + * The number of taps in the resampling filter. + * More taps gives better quality but uses more CPU time. + * This typically ranges from 4 to 64. Default is 16. + * + * For polyphase filters, numTaps must be a multiple of four for loop unrolling. + * @param numTaps number of taps for the filter + * @return address of this builder for chaining calls + */ + Builder *setNumTaps(int32_t numTaps) { + mNumTaps = numTaps; + return this; + } + + /** + * Use 1 for mono, 2 for stereo, etc. Default is 1. + * + * @param channelCount number of channels + * @return address of this builder for chaining calls + */ + Builder *setChannelCount(int32_t channelCount) { + mChannelCount = channelCount; + return this; + } + + /** + * Default is 48000. + * + * @param inputRate sample rate of the input stream + * @return address of this builder for chaining calls + */ + Builder *setInputRate(int32_t inputRate) { + mInputRate = inputRate; + return this; + } + + /** + * Default is 48000. + * + * @param outputRate sample rate of the output stream + * @return address of this builder for chaining calls + */ + Builder *setOutputRate(int32_t outputRate) { + mOutputRate = outputRate; + return this; + } + + /** + * Set cutoff frequency relative to the Nyquist rate of the output sample rate. + * Set to 1.0 to match the Nyquist frequency. + * Set lower to reduce aliasing. + * Default is 0.70. + * + * @param normalizedCutoff anti-aliasing filter cutoff + * @return address of this builder for chaining calls + */ + Builder *setNormalizedCutoff(float normalizedCutoff) { + mNormalizedCutoff = normalizedCutoff; + return this; + } + + int32_t getNumTaps() const { + return mNumTaps; + } + + int32_t getChannelCount() const { + return mChannelCount; + } + + int32_t getInputRate() const { + return mInputRate; + } + + int32_t getOutputRate() const { + return mOutputRate; + } + + float getNormalizedCutoff() const { + return mNormalizedCutoff; + } + + protected: + int32_t mChannelCount = 1; + int32_t mNumTaps = 16; + int32_t mInputRate = 48000; + int32_t mOutputRate = 48000; + float mNormalizedCutoff = kDefaultNormalizedCutoff; + }; + + virtual ~MultiChannelResampler() = default; + + /** + * Factory method for making a resampler that is optimal for the given inputs. + * + * @param channelCount number of channels, 2 for stereo + * @param inputRate sample rate of the input stream + * @param outputRate sample rate of the output stream + * @param quality higher quality sounds better but uses more CPU + * @return an optimal resampler + */ + static MultiChannelResampler *make(int32_t channelCount, + int32_t inputRate, + int32_t outputRate, + Quality quality); + + bool isWriteNeeded() const { + return mIntegerPhase >= mDenominator; + } + + /** + * Write a frame containing N samples. + * + * @param frame pointer to the first sample in a frame + */ + void writeNextFrame(const float *frame) { + writeFrame(frame); + advanceWrite(); + } + + /** + * Read a frame containing N samples. + * + * @param frame pointer to the first sample in a frame + */ + void readNextFrame(float *frame) { + readFrame(frame); + advanceRead(); + } + + int getNumTaps() const { + return mNumTaps; + } + + int getChannelCount() const { + return mChannelCount; + } + + static float hammingWindow(float radians, float spread); + + static float sinc(float radians); + +protected: + + explicit MultiChannelResampler(const MultiChannelResampler::Builder &builder); + + /** + * Write a frame containing N samples. + * Call advanceWrite() after calling this. + * @param frame pointer to the first sample in a frame + */ + virtual void writeFrame(const float *frame); + + /** + * Read a frame containing N samples using interpolation. + * Call advanceRead() after calling this. + * @param frame pointer to the first sample in a frame + */ + virtual void readFrame(float *frame) = 0; + + void advanceWrite() { + mIntegerPhase -= mDenominator; + } + + void advanceRead() { + mIntegerPhase += mNumerator; + } + + /** + * Generate the filter coefficients in optimal order. + * @param inputRate sample rate of the input stream + * @param outputRate sample rate of the output stream + * @param numRows number of rows in the array that contain a set of tap coefficients + * @param phaseIncrement how much to increment the phase between rows + * @param normalizedCutoff filter cutoff frequency normalized to Nyquist rate of output + */ + void generateCoefficients(int32_t inputRate, + int32_t outputRate, + int32_t numRows, + double phaseIncrement, + float normalizedCutoff); + + + int32_t getIntegerPhase() { + return mIntegerPhase; + } + + static constexpr int kMaxCoefficients = 8 * 1024; + std::vector mCoefficients; + + const int mNumTaps; + int mCursor = 0; + std::vector mX; // delayed input values for the FIR + std::vector mSingleFrame; // one frame for temporary use + int32_t mIntegerPhase = 0; + int32_t mNumerator = 0; + int32_t mDenominator = 0; + + +private: + +#if MCR_USE_KAISER + KaiserWindow mKaiserWindow; +#else + HyperbolicCosineWindow mCoshWindow; +#endif + + static constexpr float kDefaultNormalizedCutoff = 0.70f; + + const int mChannelCount; +}; + +} /* namespace RESAMPLER_OUTER_NAMESPACE::resampler */ + +#endif //RESAMPLER_MULTICHANNEL_RESAMPLER_H diff --git a/media/libaaudio/src/flowgraph/resampler/PolyphaseResampler.cpp b/media/libaaudio/src/flowgraph/resampler/PolyphaseResampler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e47ee8e2cd8c90a3aa814a8896a567f8c6db946f --- /dev/null +++ b/media/libaaudio/src/flowgraph/resampler/PolyphaseResampler.cpp @@ -0,0 +1,60 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "IntegerRatio.h" +#include "PolyphaseResampler.h" + +using namespace RESAMPLER_OUTER_NAMESPACE::resampler; + +PolyphaseResampler::PolyphaseResampler(const MultiChannelResampler::Builder &builder) + : MultiChannelResampler(builder) + { + assert((getNumTaps() % 4) == 0); // Required for loop unrolling. + + int32_t inputRate = builder.getInputRate(); + int32_t outputRate = builder.getOutputRate(); + + int32_t numRows = mDenominator; + double phaseIncrement = (double) inputRate / (double) outputRate; + generateCoefficients(inputRate, outputRate, + numRows, phaseIncrement, + builder.getNormalizedCutoff()); +} + +void PolyphaseResampler::readFrame(float *frame) { + // Clear accumulator for mixing. + std::fill(mSingleFrame.begin(), mSingleFrame.end(), 0.0); + + // Multiply input times windowed sinc function. + float *coefficients = &mCoefficients[mCoefficientCursor]; + float *xFrame = &mX[static_cast(mCursor) * static_cast(getChannelCount())]; + for (int i = 0; i < mNumTaps; i++) { + float coefficient = *coefficients++; + for (int channel = 0; channel < getChannelCount(); channel++) { + mSingleFrame[channel] += *xFrame++ * coefficient; + } + } + + // Advance and wrap through coefficients. + mCoefficientCursor = (mCoefficientCursor + mNumTaps) % mCoefficients.size(); + + // Copy accumulator to output. + for (int channel = 0; channel < getChannelCount(); channel++) { + frame[channel] = mSingleFrame[channel]; + } +} diff --git a/media/libaaudio/src/flowgraph/resampler/PolyphaseResampler.h b/media/libaaudio/src/flowgraph/resampler/PolyphaseResampler.h new file mode 100644 index 0000000000000000000000000000000000000000..3642fcec49df8ef2f6c0cb7287db084724885a58 --- /dev/null +++ b/media/libaaudio/src/flowgraph/resampler/PolyphaseResampler.h @@ -0,0 +1,53 @@ +/* + * Copyright 2019 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 RESAMPLER_POLYPHASE_RESAMPLER_H +#define RESAMPLER_POLYPHASE_RESAMPLER_H + +#include +#include +#include +#include + +#include "MultiChannelResampler.h" +#include "ResamplerDefinitions.h" + +namespace RESAMPLER_OUTER_NAMESPACE::resampler { +/** + * Resampler that is optimized for a reduced ratio of sample rates. + * All of the coefficients for each possible phase value are pre-calculated. + */ +class PolyphaseResampler : public MultiChannelResampler { +public: + /** + * + * @param builder containing lots of parameters + */ + explicit PolyphaseResampler(const MultiChannelResampler::Builder &builder); + + virtual ~PolyphaseResampler() = default; + + void readFrame(float *frame) override; + +protected: + + int32_t mCoefficientCursor = 0; + +}; + +} /* namespace RESAMPLER_OUTER_NAMESPACE::resampler */ + +#endif //RESAMPLER_POLYPHASE_RESAMPLER_H diff --git a/media/libaaudio/src/flowgraph/resampler/PolyphaseResamplerMono.cpp b/media/libaaudio/src/flowgraph/resampler/PolyphaseResamplerMono.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fdaf13ea6aae24d8ff799dd4a43e9b05dca46901 --- /dev/null +++ b/media/libaaudio/src/flowgraph/resampler/PolyphaseResamplerMono.cpp @@ -0,0 +1,63 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "PolyphaseResamplerMono.h" + +using namespace RESAMPLER_OUTER_NAMESPACE::resampler; + +#define MONO 1 + +PolyphaseResamplerMono::PolyphaseResamplerMono(const MultiChannelResampler::Builder &builder) + : PolyphaseResampler(builder) { + assert(builder.getChannelCount() == MONO); +} + +void PolyphaseResamplerMono::writeFrame(const float *frame) { + // Move cursor before write so that cursor points to last written frame in read. + if (--mCursor < 0) { + mCursor = getNumTaps() - 1; + } + float *dest = &mX[mCursor * MONO]; + const int offset = mNumTaps * MONO; + // Write each channel twice so we avoid having to wrap when running the FIR. + const float sample = frame[0]; + // Put ordered writes together. + dest[0] = sample; + dest[offset] = sample; +} + +void PolyphaseResamplerMono::readFrame(float *frame) { + // Clear accumulator. + float sum = 0.0; + + // Multiply input times precomputed windowed sinc function. + const float *coefficients = &mCoefficients[mCoefficientCursor]; + float *xFrame = &mX[mCursor * MONO]; + const int numLoops = mNumTaps >> 2; // n/4 + for (int i = 0; i < numLoops; i++) { + // Manual loop unrolling, might get converted to SIMD. + sum += *xFrame++ * *coefficients++; + sum += *xFrame++ * *coefficients++; + sum += *xFrame++ * *coefficients++; + sum += *xFrame++ * *coefficients++; + } + + mCoefficientCursor = (mCoefficientCursor + mNumTaps) % mCoefficients.size(); + + // Copy accumulator to output. + frame[0] = sum; +} diff --git a/media/libaaudio/src/flowgraph/resampler/PolyphaseResamplerMono.h b/media/libaaudio/src/flowgraph/resampler/PolyphaseResamplerMono.h new file mode 100644 index 0000000000000000000000000000000000000000..fe020b545340e406c2c3aa8e1b93e2dbfb501b49 --- /dev/null +++ b/media/libaaudio/src/flowgraph/resampler/PolyphaseResamplerMono.h @@ -0,0 +1,41 @@ +/* + * Copyright 2019 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 RESAMPLER_POLYPHASE_RESAMPLER_MONO_H +#define RESAMPLER_POLYPHASE_RESAMPLER_MONO_H + +#include +#include + +#include "PolyphaseResampler.h" +#include "ResamplerDefinitions.h" + +namespace RESAMPLER_OUTER_NAMESPACE::resampler { + +class PolyphaseResamplerMono : public PolyphaseResampler { +public: + explicit PolyphaseResamplerMono(const MultiChannelResampler::Builder &builder); + + virtual ~PolyphaseResamplerMono() = default; + + void writeFrame(const float *frame) override; + + void readFrame(float *frame) override; +}; + +} /* namespace RESAMPLER_OUTER_NAMESPACE::resampler */ + +#endif //RESAMPLER_POLYPHASE_RESAMPLER_MONO_H diff --git a/media/libaaudio/src/flowgraph/resampler/PolyphaseResamplerStereo.cpp b/media/libaaudio/src/flowgraph/resampler/PolyphaseResamplerStereo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b38185186a9adc948ac55ab73233d3fd3da2d909 --- /dev/null +++ b/media/libaaudio/src/flowgraph/resampler/PolyphaseResamplerStereo.cpp @@ -0,0 +1,79 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "PolyphaseResamplerStereo.h" + +using namespace RESAMPLER_OUTER_NAMESPACE::resampler; + +#define STEREO 2 + +PolyphaseResamplerStereo::PolyphaseResamplerStereo(const MultiChannelResampler::Builder &builder) + : PolyphaseResampler(builder) { + assert(builder.getChannelCount() == STEREO); +} + +void PolyphaseResamplerStereo::writeFrame(const float *frame) { + // Move cursor before write so that cursor points to last written frame in read. + if (--mCursor < 0) { + mCursor = getNumTaps() - 1; + } + float *dest = &mX[mCursor * STEREO]; + const int offset = mNumTaps * STEREO; + // Write each channel twice so we avoid having to wrap when running the FIR. + const float left = frame[0]; + const float right = frame[1]; + // Put ordered writes together. + dest[0] = left; + dest[1] = right; + dest[offset] = left; + dest[1 + offset] = right; +} + +void PolyphaseResamplerStereo::readFrame(float *frame) { + // Clear accumulators. + float left = 0.0; + float right = 0.0; + + // Multiply input times precomputed windowed sinc function. + const float *coefficients = &mCoefficients[mCoefficientCursor]; + float *xFrame = &mX[mCursor * STEREO]; + const int numLoops = mNumTaps >> 2; // n/4 + for (int i = 0; i < numLoops; i++) { + // Manual loop unrolling, might get converted to SIMD. + float coefficient = *coefficients++; + left += *xFrame++ * coefficient; + right += *xFrame++ * coefficient; + + coefficient = *coefficients++; // next tap + left += *xFrame++ * coefficient; + right += *xFrame++ * coefficient; + + coefficient = *coefficients++; // next tap + left += *xFrame++ * coefficient; + right += *xFrame++ * coefficient; + + coefficient = *coefficients++; // next tap + left += *xFrame++ * coefficient; + right += *xFrame++ * coefficient; + } + + mCoefficientCursor = (mCoefficientCursor + mNumTaps) % mCoefficients.size(); + + // Copy accumulators to output. + frame[0] = left; + frame[1] = right; +} diff --git a/media/libaaudio/src/flowgraph/resampler/PolyphaseResamplerStereo.h b/media/libaaudio/src/flowgraph/resampler/PolyphaseResamplerStereo.h new file mode 100644 index 0000000000000000000000000000000000000000..ee4cabaad60dbbe52681c956ebee0f0692dc280c --- /dev/null +++ b/media/libaaudio/src/flowgraph/resampler/PolyphaseResamplerStereo.h @@ -0,0 +1,41 @@ +/* + * Copyright 2019 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 RESAMPLER_POLYPHASE_RESAMPLER_STEREO_H +#define RESAMPLER_POLYPHASE_RESAMPLER_STEREO_H + +#include +#include + +#include "PolyphaseResampler.h" +#include "ResamplerDefinitions.h" + +namespace RESAMPLER_OUTER_NAMESPACE::resampler { + +class PolyphaseResamplerStereo : public PolyphaseResampler { +public: + explicit PolyphaseResamplerStereo(const MultiChannelResampler::Builder &builder); + + virtual ~PolyphaseResamplerStereo() = default; + + void writeFrame(const float *frame) override; + + void readFrame(float *frame) override; +}; + +} /* namespace RESAMPLER_OUTER_NAMESPACE::resampler */ + +#endif //RESAMPLER_POLYPHASE_RESAMPLER_STEREO_H diff --git a/media/libaaudio/src/flowgraph/resampler/README.md b/media/libaaudio/src/flowgraph/resampler/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ea319c7602a57d7d8b33f22800c091bd3ca6ffef --- /dev/null +++ b/media/libaaudio/src/flowgraph/resampler/README.md @@ -0,0 +1,101 @@ +# Sample Rate Converter + +This folder contains a sample rate converter, or "resampler". + +The converter is based on a sinc function that has been windowed by a hyperbolic cosine. +We found this had fewer artifacts than the more traditional Kaiser window. + +## Building the Resampler + +It is part of [Oboe](https://github.com/google/oboe) but has no dependencies on Oboe. +So the contents of this folder can be used outside of Oboe. + +To build it for use outside of Oboe: + +1. Copy the "resampler" folder to a folder in your project that is in the include path. +2. Add all of the \*.cpp files in the resampler folder to your project IDE or Makefile. +3. In ResamplerDefinitions.h, define RESAMPLER_OUTER_NAMESPACE with your own project name. Alternatively, use -DRESAMPLER_OUTER_NAMESPACE=mynamespace when compiling to avoid modifying the resampler code. + +## Creating a Resampler + +Include the [main header](MultiChannelResampler.h) for the resampler. + + #include "resampler/MultiChannelResampler.h" + +Here is an example of creating a stereo resampler that will convert from 44100 to 48000 Hz. +Only do this once, when you open your stream. Then use the sample resampler to process multiple buffers. + + MultiChannelResampler *resampler = MultiChannelResampler::make( + 2, // channel count + 44100, // input sampleRate + 48000, // output sampleRate + MultiChannelResampler::Quality::Medium); // conversion quality + +Possible values for quality include { Fastest, Low, Medium, High, Best }. +Higher quality levels will sound better but consume more CPU because they have more taps in the filter. + +## Fractional Frame Counts + +Note that the number of output frames generated for a given number of input frames can vary. + +For example, suppose you are converting from 44100 Hz to 48000 Hz and using an input buffer with 960 frames. If you calculate the number of output frames you get: + + 960 * 48000 * 44100 = 1044.897959... + +You cannot generate a fractional number of frames. So the resampler will sometimes generate 1044 frames and sometimes 1045 frames. On average it will generate 1044.897959 frames. The resampler stores the fraction internally and keeps track of when to consume or generate a frame. + +You can either use a fixed number of input frames or a fixed number of output frames. The other frame count will vary. + +## Calling the Resampler with a fixed number of OUTPUT frames + +In this example, suppose we have a fixed number of output frames and a variable number of input frames. + +Assume you start with these variables and a method that returns the next input frame: + + float *outputBuffer; // multi-channel buffer to be filled + int numOutputFrames; // number of frames of output + +The resampler has a method isWriteNeeded() that tells you whether to write to or read from the resampler. + + int outputFramesLeft = numOutputFrames; + while (outputFramesLeft > 0) { + if(resampler->isWriteNeeded()) { + const float *frame = getNextInputFrame(); // you provide this + resampler->writeNextFrame(frame); + } else { + resampler->readNextFrame(outputBuffer); + outputBuffer += channelCount; + outputFramesLeft--; + } + } + +## Calling the Resampler with a fixed number of INPUT frames + +In this example, suppose we have a fixed number of input frames and a variable number of output frames. + +Assume you start with these variables: + + float *inputBuffer; // multi-channel buffer to be consumed + float *outputBuffer; // multi-channel buffer to be filled + int numInputFrames; // number of frames of input + int numOutputFrames = 0; + int channelCount; // 1 for mono, 2 for stereo + + int inputFramesLeft = numInputFrames; + while (inputFramesLeft > 0) { + if(resampler->isWriteNeeded()) { + resampler->writeNextFrame(inputBuffer); + inputBuffer += channelCount; + inputFramesLeft--; + } else { + resampler->readNextFrame(outputBuffer); + outputBuffer += channelCount; + numOutputFrames++; + } + } + +## Deleting the Resampler + +When you are done, you should delete the Resampler to avoid a memory leak. + + delete resampler; diff --git a/media/libaaudio/src/flowgraph/resampler/ResamplerDefinitions.h b/media/libaaudio/src/flowgraph/resampler/ResamplerDefinitions.h new file mode 100644 index 0000000000000000000000000000000000000000..c6791ec937277099f5397351f680d257ea686836 --- /dev/null +++ b/media/libaaudio/src/flowgraph/resampler/ResamplerDefinitions.h @@ -0,0 +1,27 @@ +/* + * Copyright 2022 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. + */ + +// Set flag RESAMPLER_OUTER_NAMESPACE based on whether compiler flag +// __ANDROID_NDK__ is defined. __ANDROID_NDK__ should be defined in oboe +// but not in android. + +#ifndef RESAMPLER_OUTER_NAMESPACE +#ifdef __ANDROID_NDK__ +#define RESAMPLER_OUTER_NAMESPACE oboe +#else +#define RESAMPLER_OUTER_NAMESPACE aaudio +#endif // __ANDROID_NDK__ +#endif // RESAMPLER_OUTER_NAMESPACE diff --git a/media/libaaudio/src/flowgraph/resampler/SincResampler.cpp b/media/libaaudio/src/flowgraph/resampler/SincResampler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..42d0ca2028ac00996a68e41e0bf92d624f93605d --- /dev/null +++ b/media/libaaudio/src/flowgraph/resampler/SincResampler.cpp @@ -0,0 +1,78 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "SincResampler.h" + +using namespace RESAMPLER_OUTER_NAMESPACE::resampler; + +SincResampler::SincResampler(const MultiChannelResampler::Builder &builder) + : MultiChannelResampler(builder) + , mSingleFrame2(builder.getChannelCount()) { + assert((getNumTaps() % 4) == 0); // Required for loop unrolling. + mNumRows = kMaxCoefficients / getNumTaps(); // no guard row needed + mPhaseScaler = (double) mNumRows / mDenominator; + double phaseIncrement = 1.0 / mNumRows; + generateCoefficients(builder.getInputRate(), + builder.getOutputRate(), + mNumRows, + phaseIncrement, + builder.getNormalizedCutoff()); +} + +void SincResampler::readFrame(float *frame) { + // Clear accumulator for mixing. + std::fill(mSingleFrame.begin(), mSingleFrame.end(), 0.0); + std::fill(mSingleFrame2.begin(), mSingleFrame2.end(), 0.0); + + // Determine indices into coefficients table. + double tablePhase = getIntegerPhase() * mPhaseScaler; + int index1 = static_cast(floor(tablePhase)); + if (index1 >= mNumRows) { // no guard row needed because we wrap the indices + tablePhase -= mNumRows; + index1 -= mNumRows; + } + + int index2 = index1 + 1; + if (index2 >= mNumRows) { // no guard row needed because we wrap the indices + index2 -= mNumRows; + } + + float *coefficients1 = &mCoefficients[static_cast(index1) + * static_cast(getNumTaps())]; + float *coefficients2 = &mCoefficients[static_cast(index2) + * static_cast(getNumTaps())]; + + float *xFrame = &mX[static_cast(mCursor) * static_cast(getChannelCount())]; + for (int i = 0; i < mNumTaps; i++) { + float coefficient1 = *coefficients1++; + float coefficient2 = *coefficients2++; + for (int channel = 0; channel < getChannelCount(); channel++) { + float sample = *xFrame++; + mSingleFrame[channel] += sample * coefficient1; + mSingleFrame2[channel] += sample * coefficient2; + } + } + + // Interpolate and copy to output. + float fraction = tablePhase - index1; + for (int channel = 0; channel < getChannelCount(); channel++) { + float low = mSingleFrame[channel]; + float high = mSingleFrame2[channel]; + frame[channel] = low + (fraction * (high - low)); + } +} diff --git a/media/libaaudio/src/flowgraph/resampler/SincResampler.h b/media/libaaudio/src/flowgraph/resampler/SincResampler.h new file mode 100644 index 0000000000000000000000000000000000000000..05ff0921cf7dbbda6e91308139942b8b8472945c --- /dev/null +++ b/media/libaaudio/src/flowgraph/resampler/SincResampler.h @@ -0,0 +1,50 @@ +/* + * Copyright 2019 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 RESAMPLER_SINC_RESAMPLER_H +#define RESAMPLER_SINC_RESAMPLER_H + +#include +#include +#include + +#include "MultiChannelResampler.h" +#include "ResamplerDefinitions.h" + +namespace RESAMPLER_OUTER_NAMESPACE::resampler { + +/** + * Resampler that can interpolate between coefficients. + * This can be used to support arbitrary ratios. + */ +class SincResampler : public MultiChannelResampler { +public: + explicit SincResampler(const MultiChannelResampler::Builder &builder); + + virtual ~SincResampler() = default; + + void readFrame(float *frame) override; + +protected: + + std::vector mSingleFrame2; // for interpolation + int32_t mNumRows = 0; + double mPhaseScaler = 1.0; +}; + +} /* namespace RESAMPLER_OUTER_NAMESPACE::resampler */ + +#endif //RESAMPLER_SINC_RESAMPLER_H diff --git a/media/libaaudio/src/flowgraph/resampler/SincResamplerStereo.cpp b/media/libaaudio/src/flowgraph/resampler/SincResamplerStereo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..432137ea19458926d672cf8c24c9800b8a866ef4 --- /dev/null +++ b/media/libaaudio/src/flowgraph/resampler/SincResamplerStereo.cpp @@ -0,0 +1,83 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "SincResamplerStereo.h" + +using namespace RESAMPLER_OUTER_NAMESPACE::resampler; + +#define STEREO 2 + +SincResamplerStereo::SincResamplerStereo(const MultiChannelResampler::Builder &builder) + : SincResampler(builder) { + assert(builder.getChannelCount() == STEREO); +} + +void SincResamplerStereo::writeFrame(const float *frame) { + // Move cursor before write so that cursor points to last written frame in read. + if (--mCursor < 0) { + mCursor = getNumTaps() - 1; + } + float *dest = &mX[mCursor * STEREO]; + const int offset = mNumTaps * STEREO; + // Write each channel twice so we avoid having to wrap when running the FIR. + const float left = frame[0]; + const float right = frame[1]; + // Put ordered writes together. + dest[0] = left; + dest[1] = right; + dest[offset] = left; + dest[1 + offset] = right; +} + +// Multiply input times windowed sinc function. +void SincResamplerStereo::readFrame(float *frame) { + // Clear accumulator for mixing. + std::fill(mSingleFrame.begin(), mSingleFrame.end(), 0.0); + std::fill(mSingleFrame2.begin(), mSingleFrame2.end(), 0.0); + + // Determine indices into coefficients table. + double tablePhase = getIntegerPhase() * mPhaseScaler; + int index1 = static_cast(floor(tablePhase)); + float *coefficients1 = &mCoefficients[static_cast(index1) + * static_cast(getNumTaps())]; + int index2 = (index1 + 1); + if (index2 >= mNumRows) { // no guard row needed because we wrap the indices + index2 = 0; + } + float *coefficients2 = &mCoefficients[static_cast(index2) + * static_cast(getNumTaps())]; + float *xFrame = &mX[static_cast(mCursor) * static_cast(getChannelCount())]; + for (int i = 0; i < mNumTaps; i++) { + float coefficient1 = *coefficients1++; + float coefficient2 = *coefficients2++; + for (int channel = 0; channel < getChannelCount(); channel++) { + float sample = *xFrame++; + mSingleFrame[channel] += sample * coefficient1; + mSingleFrame2[channel] += sample * coefficient2; + } + } + + // Interpolate and copy to output. + float fraction = tablePhase - index1; + for (int channel = 0; channel < getChannelCount(); channel++) { + float low = mSingleFrame[channel]; + float high = mSingleFrame2[channel]; + frame[channel] = low + (fraction * (high - low)); + } +} diff --git a/media/libaaudio/src/flowgraph/resampler/SincResamplerStereo.h b/media/libaaudio/src/flowgraph/resampler/SincResamplerStereo.h new file mode 100644 index 0000000000000000000000000000000000000000..d5576d18685ccc144a1840b219b9fe51b61185a6 --- /dev/null +++ b/media/libaaudio/src/flowgraph/resampler/SincResamplerStereo.h @@ -0,0 +1,42 @@ +/* + * Copyright 2019 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 RESAMPLER_SINC_RESAMPLER_STEREO_H +#define RESAMPLER_SINC_RESAMPLER_STEREO_H + +#include +#include + +#include "SincResampler.h" +#include "ResamplerDefinitions.h" + +namespace RESAMPLER_OUTER_NAMESPACE::resampler { + +class SincResamplerStereo : public SincResampler { +public: + explicit SincResamplerStereo(const MultiChannelResampler::Builder &builder); + + virtual ~SincResamplerStereo() = default; + + void writeFrame(const float *frame) override; + + void readFrame(float *frame) override; + +}; + +} /* namespace RESAMPLER_OUTER_NAMESPACE::resampler */ + +#endif //RESAMPLER_SINC_RESAMPLER_STEREO_H diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp index e96e13400fac0d86b94a6f91ee0f03090daf9ff7..dd11169d8ac652f6ed22a7f2dd8567cda68aee82 100644 --- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp +++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp @@ -37,18 +37,6 @@ AudioStreamLegacy::AudioStreamLegacy() : AudioStream() { } -AudioStreamLegacy::~AudioStreamLegacy() { -} - -// Called from AudioTrack.cpp or AudioRecord.cpp -static void AudioStreamLegacy_callback(int event, void* userData, void *info) { - AudioStreamLegacy *streamLegacy = (AudioStreamLegacy *) userData; - streamLegacy->processCallback(event, info); -} - -aaudio_legacy_callback_t AudioStreamLegacy::getLegacyCallback() { - return AudioStreamLegacy_callback; -} aaudio_data_callback_result_t AudioStreamLegacy::callDataCallbackFrames(uint8_t *buffer, int32_t numFrames) { @@ -76,82 +64,140 @@ int32_t AudioStreamLegacy::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes return (int32_t) callDataCallbackFrames(buffer, numFrames); } -void AudioStreamLegacy::processCallbackCommon(aaudio_callback_operation_t opcode, void *info) { - aaudio_data_callback_result_t callbackResult; + +void AudioStreamLegacy::onNewIAudioTrack() { + ALOGD("%s stream disconnected", __func__); + forceDisconnect(); + mCallbackEnabled.store(false); +} + +size_t AudioStreamLegacy::onMoreData(const android::AudioTrack::Buffer& buffer) { // This illegal size can be used to tell AudioRecord or AudioTrack to stop calling us. // This takes advantage of them killing the stream when they see a size out of range. // That is an undocumented behavior. // TODO add to API in AudioRecord and AudioTrack + // TODO(b/216175830) cleanup size re-computation const size_t SIZE_STOP_CALLBACKS = SIZE_MAX; + aaudio_data_callback_result_t callbackResult; + (void) checkForDisconnectRequest(true); - switch (opcode) { - case AAUDIO_CALLBACK_OPERATION_PROCESS_DATA: { - (void) checkForDisconnectRequest(true); - - // Note that this code assumes an AudioTrack::Buffer is the same as - // AudioRecord::Buffer - // TODO define our own AudioBuffer and pass it from the subclasses. - AudioTrack::Buffer *audioBuffer = static_cast(info); - if (getState() == AAUDIO_STREAM_STATE_DISCONNECTED) { - ALOGW("processCallbackCommon() data, stream disconnected"); - // This will kill the stream and prevent it from being restarted. - // That is OK because the stream is disconnected. - audioBuffer->size = SIZE_STOP_CALLBACKS; - } else if (!mCallbackEnabled.load()) { - ALOGW("processCallbackCommon() no data because callback disabled, set size=0"); - // Do NOT use SIZE_STOP_CALLBACKS here because that will kill the stream and - // prevent it from being restarted. This can occur because of a race condition - // caused by Legacy callbacks running after the track is "stopped". - audioBuffer->size = 0; + // Note that this code assumes an AudioTrack::Buffer is the same as + // AudioRecord::Buffer + // TODO define our own AudioBuffer and pass it from the subclasses. + size_t written = buffer.size(); + if (getState() == AAUDIO_STREAM_STATE_DISCONNECTED) { + ALOGW("%s() data, stream disconnected", __func__); + // This will kill the stream and prevent it from being restarted. + // That is OK because the stream is disconnected. + written = SIZE_STOP_CALLBACKS; + } else if (!mCallbackEnabled.load()) { + ALOGW("%s() no data because callback disabled, set size=0", __func__); + // Do NOT use SIZE_STOP_CALLBACKS here because that will kill the stream and + // prevent it from being restarted. This can occur because of a race condition + // caused by Legacy callbacks running after the track is "stopped". + written = 0; + } else { + if (buffer.getFrameCount() == 0) { + ALOGW("%s() data, frameCount is zero", __func__); + return written; + } + + // If the caller specified an exact size then use a block size adapter. + if (mBlockAdapter != nullptr) { + int32_t byteCount = buffer.getFrameCount() * getBytesPerDeviceFrame(); + callbackResult = mBlockAdapter->processVariableBlock( + buffer.data(), byteCount); + } else { + // Call using the AAudio callback interface. + callbackResult = callDataCallbackFrames(buffer.data(), + buffer.getFrameCount()); + } + if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) { + written = buffer.getFrameCount() * getBytesPerDeviceFrame(); + } else { + if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) { + ALOGD("%s() callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__); } else { - if (audioBuffer->frameCount == 0) { - ALOGW("processCallbackCommon() data, frameCount is zero"); - return; - } - - // If the caller specified an exact size then use a block size adapter. - if (mBlockAdapter != nullptr) { - int32_t byteCount = audioBuffer->frameCount * getBytesPerDeviceFrame(); - callbackResult = mBlockAdapter->processVariableBlock( - (uint8_t *) audioBuffer->raw, byteCount); - } else { - // Call using the AAudio callback interface. - callbackResult = callDataCallbackFrames((uint8_t *)audioBuffer->raw, - audioBuffer->frameCount); - } - if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) { - audioBuffer->size = audioBuffer->frameCount * getBytesPerDeviceFrame(); - } else { - if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) { - ALOGD("%s() callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__); - } else { - ALOGW("%s() callback returned invalid result = %d", - __func__, callbackResult); - } - audioBuffer->size = 0; - systemStopInternal(); - // Disable the callback just in case the system keeps trying to call us. - mCallbackEnabled.store(false); - } - - if (updateStateMachine() != AAUDIO_OK) { - forceDisconnect(); - mCallbackEnabled.store(false); - } + ALOGW("%s() callback returned invalid result = %d", + __func__, callbackResult); } + written = 0; + systemStopInternal(); + // Disable the callback just in case the system keeps trying to call us. + mCallbackEnabled.store(false); } - break; - // Stream got rerouted so we disconnect. - case AAUDIO_CALLBACK_OPERATION_DISCONNECTED: - ALOGD("processCallbackCommon() stream disconnected"); + if (updateStateMachine() != AAUDIO_OK) { forceDisconnect(); mCallbackEnabled.store(false); - break; + } + } + return written; +} - default: - break; +// TODO (b/216175830) this method is duplicated in order to ease refactoring which will +// reconsolidate. +size_t AudioStreamLegacy::onMoreData(const android::AudioRecord::Buffer& buffer) { + // This illegal size can be used to tell AudioRecord or AudioTrack to stop calling us. + // This takes advantage of them killing the stream when they see a size out of range. + // That is an undocumented behavior. + // TODO add to API in AudioRecord and AudioTrack + const size_t SIZE_STOP_CALLBACKS = SIZE_MAX; + aaudio_data_callback_result_t callbackResult; + (void) checkForDisconnectRequest(true); + + // Note that this code assumes an AudioTrack::Buffer is the same as + // AudioRecord::Buffer + // TODO define our own AudioBuffer and pass it from the subclasses. + size_t written = buffer.size(); + if (getState() == AAUDIO_STREAM_STATE_DISCONNECTED) { + ALOGW("%s() data, stream disconnected", __func__); + // This will kill the stream and prevent it from being restarted. + // That is OK because the stream is disconnected. + written = SIZE_STOP_CALLBACKS; + } else if (!mCallbackEnabled.load()) { + ALOGW("%s() no data because callback disabled, set size=0", __func__); + // Do NOT use SIZE_STOP_CALLBACKS here because that will kill the stream and + // prevent it from being restarted. This can occur because of a race condition + // caused by Legacy callbacks running after the track is "stopped". + written = 0; + } else { + if (buffer.getFrameCount() == 0) { + ALOGW("%s() data, frameCount is zero", __func__); + return written; + } + + // If the caller specified an exact size then use a block size adapter. + if (mBlockAdapter != nullptr) { + int32_t byteCount = buffer.getFrameCount() * getBytesPerDeviceFrame(); + callbackResult = mBlockAdapter->processVariableBlock( + buffer.data(), byteCount); + } else { + // Call using the AAudio callback interface. + callbackResult = callDataCallbackFrames(buffer.data(), + buffer.getFrameCount()); + } + if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) { + written = buffer.getFrameCount() * getBytesPerDeviceFrame(); + } else { + if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) { + ALOGD("%s() callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__); + } else { + ALOGW("%s() callback returned invalid result = %d", + __func__, callbackResult); + } + written = 0; + systemStopInternal(); + // Disable the callback just in case the system keeps trying to call us. + mCallbackEnabled.store(false); + } + + if (updateStateMachine() != AAUDIO_OK) { + forceDisconnect(); + mCallbackEnabled.store(false); + } } + return written; } aaudio_result_t AudioStreamLegacy::checkForDisconnectRequest(bool errorCallbackEnabled) { diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.h b/media/libaaudio/src/legacy/AudioStreamLegacy.h index 88ef27070bf329b24ff05f9ca0bc02a4a81098a5..53f6e06f440991ecb051962f0ed6faa3e48fa3b9 100644 --- a/media/libaaudio/src/legacy/AudioStreamLegacy.h +++ b/media/libaaudio/src/legacy/AudioStreamLegacy.h @@ -17,8 +17,10 @@ #ifndef LEGACY_AUDIO_STREAM_LEGACY_H #define LEGACY_AUDIO_STREAM_LEGACY_H -#include +#include #include +#include +#include #include @@ -30,8 +32,6 @@ namespace aaudio { -typedef void (*aaudio_legacy_callback_t)(int event, void* user, void *info); - enum { /** * Request that the callback function should fill the data buffer of an output stream, @@ -56,21 +56,18 @@ enum { typedef int32_t aaudio_callback_operation_t; -class AudioStreamLegacy : public AudioStream, public FixedBlockProcessor { +class AudioStreamLegacy : public AudioStream, + public FixedBlockProcessor, + protected android::AudioTrack::IAudioTrackCallback, + protected android::AudioRecord::IAudioRecordCallback { public: AudioStreamLegacy(); - virtual ~AudioStreamLegacy(); + virtual ~AudioStreamLegacy() = default; - aaudio_legacy_callback_t getLegacyCallback(); int32_t callDataCallbackFrames(uint8_t *buffer, int32_t numFrames); - // This is public so it can be called from the C callback function. - // This is called from the AudioTrack/AudioRecord client. - virtual void processCallback(int event, void *info) = 0; - - void processCallbackCommon(aaudio_callback_operation_t opcode, void *info); // Implement FixedBlockProcessor int32_t onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) override; @@ -86,7 +83,12 @@ public: } protected: - + size_t onMoreData(const android::AudioTrack::Buffer& buffer) override; + // TODO (b/216175830) this method is duplicated in order to ease refactoring which will + // reconsolidate. + size_t onMoreData(const android::AudioRecord::Buffer& buffer) override; + void onNewIAudioTrack() override; + void onNewIAudioRecord() override { onNewIAudioTrack(); } aaudio_result_t getBestTimestamp(clockid_t clockId, int64_t *framePosition, int64_t *timeNanoseconds, diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp index fe8fb191d74c055dc8cf565f5c44ed19131e9bdc..1e39e0f6807546e07cca5ba6157f259d7439fb0e 100644 --- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp +++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp @@ -96,41 +96,18 @@ aaudio_result_t AudioStreamRecord::open(const AudioStreamBuilder& builder) setFormat(AUDIO_FORMAT_PCM_FLOAT); } - // Maybe change device format to get a FAST path. - // AudioRecord does not support FAST mode for FLOAT data. - // TODO AudioRecord should allow FLOAT data paths for FAST tracks. - // So IF the user asks for low latency FLOAT - // AND the sampleRate is likely to be compatible with FAST - // THEN request I16 and convert to FLOAT when passing to user. - // Note that hard coding 48000 Hz is not ideal because the sampleRate - // for a FAST path might not be 48000 Hz. - // It normally is but there is a chance that it is not. - // And there is no reliable way to know that in advance. - // Luckily the consequences of a wrong guess are minor. - // We just may not get a FAST track. - // But we wouldn't have anyway without this hack. - constexpr int32_t kMostLikelySampleRateForFast = 48000; - if (getFormat() == AUDIO_FORMAT_PCM_FLOAT - && perfMode == AAUDIO_PERFORMANCE_MODE_LOW_LATENCY - && (audio_channel_count_from_in_mask(channelMask) <= 2) // FAST only for mono and stereo - && (getSampleRate() == kMostLikelySampleRateForFast - || getSampleRate() == AAUDIO_UNSPECIFIED)) { - setDeviceFormat(AUDIO_FORMAT_PCM_16_BIT); - } else { - setDeviceFormat(getFormat()); - } + + setDeviceFormat(getFormat()); // To avoid glitching, let AudioFlinger pick the optimal burst size. uint32_t notificationFrames = 0; // Setup the callback if there is one. - AudioRecord::callback_t callback = nullptr; - void *callbackData = nullptr; + sp callback; AudioRecord::transfer_type streamTransferType = AudioRecord::transfer_type::TRANSFER_SYNC; if (builder.getDataCallbackProc() != nullptr) { streamTransferType = AudioRecord::transfer_type::TRANSFER_CALLBACK; - callback = getLegacyCallback(); - callbackData = this; + callback = sp::fromExisting(this); } mCallbackBufferSize = builder.getFramesPerDataCallback(); @@ -177,7 +154,6 @@ aaudio_result_t AudioStreamRecord::open(const AudioStreamBuilder& builder) channelMask, frameCount, callback, - callbackData, notificationFrames, false /*threadCanCallJava*/, sessionId, @@ -350,23 +326,6 @@ const void * AudioStreamRecord::maybeConvertDeviceData(const void *audioData, in } } -void AudioStreamRecord::processCallback(int event, void *info) { - switch (event) { - case AudioRecord::EVENT_MORE_DATA: - processCallbackCommon(AAUDIO_CALLBACK_OPERATION_PROCESS_DATA, info); - break; - - // Stream got rerouted so we disconnect. - case AudioRecord::EVENT_NEW_IAUDIORECORD: - processCallbackCommon(AAUDIO_CALLBACK_OPERATION_DISCONNECTED, info); - break; - - default: - break; - } - return; -} - aaudio_result_t AudioStreamRecord::requestStart_l() { if (mAudioRecord.get() == nullptr) { @@ -504,7 +463,7 @@ aaudio_result_t AudioStreamRecord::read(void *buffer, return (aaudio_result_t) framesRead; } -aaudio_result_t AudioStreamRecord::setBufferSize(int32_t requestedFrames) +aaudio_result_t AudioStreamRecord::setBufferSize(int32_t /*requestedFrames*/) { return getBufferSize(); } @@ -552,7 +511,7 @@ int64_t AudioStreamRecord::getFramesWritten() { case AAUDIO_STREAM_STATE_STARTED: result = mAudioRecord->getPosition(&position); if (result == OK) { - mFramesWritten.update32(position); + mFramesWritten.update32((int32_t)position); } break; case AAUDIO_STREAM_STATE_STOPPING: diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.h b/media/libaaudio/src/legacy/AudioStreamRecord.h index 692651d5d73fd27943a4e6f3391bdb63771684f5..5ce73f98487ef081757026860a14a82f2d0568b0 100644 --- a/media/libaaudio/src/legacy/AudioStreamRecord.h +++ b/media/libaaudio/src/legacy/AudioStreamRecord.h @@ -65,7 +65,9 @@ public: } // This is public so it can be called from the C callback function. - void processCallback(int event, void *info) override; + void processCallback(int event, void *info); + + void processCallbackRecord(aaudio_callback_operation_t opcode, void *info); int64_t incrementClientFrameCounter(int32_t frames) override { return incrementFramesRead(frames); diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp index 17736fc9d008970e8dca54826cd1cd0bed54d875..6f1dc928f491a353daa58ef645c0e1841fa731fe 100644 --- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp +++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp @@ -103,14 +103,12 @@ aaudio_result_t AudioStreamTrack::open(const AudioStreamBuilder& builder) : getFormat(); // Setup the callback if there is one. - AudioTrack::callback_t callback = nullptr; - void *callbackData = nullptr; + wp callback; // Note that TRANSFER_SYNC does not allow FAST track AudioTrack::transfer_type streamTransferType = AudioTrack::transfer_type::TRANSFER_SYNC; if (builder.getDataCallbackProc() != nullptr) { streamTransferType = AudioTrack::transfer_type::TRANSFER_CALLBACK; - callback = getLegacyCallback(); - callbackData = this; + callback = wp::fromExisting(this); // If the total buffer size is unspecified then base the size on the burst size. if (frameCount == 0 @@ -157,13 +155,12 @@ aaudio_result_t AudioStreamTrack::open(const AudioStreamBuilder& builder) frameCount, flags, callback, - callbackData, notificationFrames, - 0, // DEFAULT sharedBuffer*/, + nullptr, // DEFAULT sharedBuffer*/, false, // DEFAULT threadCanCallJava sessionId, streamTransferType, - NULL, // DEFAULT audio_offload_info_t + nullptr, // DEFAULT audio_offload_info_t AttributionSourceState(), // DEFAULT uid and pid &attributes, // WARNING - If doNotReconnect set true then audio stops after plugging and unplugging @@ -217,7 +214,6 @@ aaudio_result_t AudioStreamTrack::open(const AudioStreamBuilder& builder) mBlockAdapter = nullptr; } - setState(AAUDIO_STREAM_STATE_OPEN); setDeviceId(mAudioTrack->getRoutedDeviceId()); aaudio_session_id_t actualSessionId = @@ -250,6 +246,19 @@ aaudio_result_t AudioStreamTrack::open(const AudioStreamBuilder& builder) "open() perfMode changed from %d to %d", perfMode, actualPerformanceMode); + if (getState() != AAUDIO_STREAM_STATE_UNINITIALIZED) { + ALOGE("%s - Open canceled since state = %d", __func__, getState()); + if (getState() == AAUDIO_STREAM_STATE_DISCONNECTED) + { + ALOGE("%s - Opening while state is disconnected", __func__); + safeReleaseClose(); + return AAUDIO_ERROR_DISCONNECTED; + } + safeReleaseClose(); + return AAUDIO_ERROR_INVALID_STATE; + } + + setState(AAUDIO_STREAM_STATE_OPEN); return AAUDIO_OK; } @@ -281,31 +290,19 @@ void AudioStreamTrack::close_l() { AudioStream::close_l(); } -void AudioStreamTrack::processCallback(int event, void *info) { - - switch (event) { - case AudioTrack::EVENT_MORE_DATA: - processCallbackCommon(AAUDIO_CALLBACK_OPERATION_PROCESS_DATA, info); - break; - - // Stream got rerouted so we disconnect. - case AudioTrack::EVENT_NEW_IAUDIOTRACK: - // request stream disconnect if the restored AudioTrack has properties not matching - // what was requested initially - if (mAudioTrack->channelCount() != getSamplesPerFrame() - || mAudioTrack->format() != getFormat() - || mAudioTrack->getSampleRate() != getSampleRate() - || mAudioTrack->getRoutedDeviceId() != getDeviceId() - || getBufferCapacityFromDevice() != getBufferCapacity() - || getFramesPerBurstFromDevice() != getFramesPerBurst()) { - processCallbackCommon(AAUDIO_CALLBACK_OPERATION_DISCONNECTED, info); - } - break; - default: - break; +void AudioStreamTrack::onNewIAudioTrack() { + // Stream got rerouted so we disconnect. + // request stream disconnect if the restored AudioTrack has properties not matching + // what was requested initially + if (mAudioTrack->channelCount() != getSamplesPerFrame() + || mAudioTrack->format() != getFormat() + || mAudioTrack->getSampleRate() != getSampleRate() + || mAudioTrack->getRoutedDeviceId() != getDeviceId() + || getBufferCapacityFromDevice() != getBufferCapacity() + || getFramesPerBurstFromDevice() != getFramesPerBurst()) { + AudioStreamLegacy::onNewIAudioTrack(); } - return; } aaudio_result_t AudioStreamTrack::requestStart_l() { @@ -511,7 +508,7 @@ int64_t AudioStreamTrack::getFramesRead() { case AAUDIO_STREAM_STATE_PAUSED: result = mAudioTrack->getPosition(&position); if (result == OK) { - mFramesRead.update32(position); + mFramesRead.update32((int32_t)position); } break; default: diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.h b/media/libaaudio/src/legacy/AudioStreamTrack.h index f604871f22c5df5db8eda30b5f2f2e4eff518560..0f4d72b18c628efe342692a225c63fb8b74d85c3 100644 --- a/media/libaaudio/src/legacy/AudioStreamTrack.h +++ b/media/libaaudio/src/legacy/AudioStreamTrack.h @@ -81,9 +81,6 @@ public: aaudio_result_t updateStateMachine() override; - // This is public so it can be called from the C callback function. - void processCallback(int event, void *info) override; - int64_t incrementClientFrameCounter(int32_t frames) override { return incrementFramesWritten(frames); } @@ -100,6 +97,7 @@ protected: int32_t getFramesPerBurstFromDevice() const override; int32_t getBufferCapacityFromDevice() const override; + void onNewIAudioTrack() override; private: diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp index e44cceec22bf55fa5ea8e6bb1d18941fc848cbad..872faca58b1ab1d4354f258a1169b54286dd5766 100644 --- a/media/libaaudio/src/utility/AAudioUtilities.cpp +++ b/media/libaaudio/src/utility/AAudioUtilities.cpp @@ -544,45 +544,6 @@ int32_t AAudioConvert_framesToBytes(int32_t numFrames, return AAUDIO_OK; } -static int32_t AAudioProperty_getMMapProperty(const char *propName, - int32_t defaultValue, - const char * caller) { - int32_t prop = property_get_int32(propName, defaultValue); - switch (prop) { - case AAUDIO_UNSPECIFIED: - case AAUDIO_POLICY_NEVER: - case AAUDIO_POLICY_ALWAYS: - case AAUDIO_POLICY_AUTO: - break; - default: - ALOGE("%s: invalid = %d", caller, prop); - prop = defaultValue; - break; - } - return prop; -} - -int32_t AAudioProperty_getMMapPolicy() { - return AAudioProperty_getMMapProperty(AAUDIO_PROP_MMAP_POLICY, - AAUDIO_UNSPECIFIED, __func__); -} - -int32_t AAudioProperty_getMMapExclusivePolicy() { - return AAudioProperty_getMMapProperty(AAUDIO_PROP_MMAP_EXCLUSIVE_POLICY, - AAUDIO_UNSPECIFIED, __func__); -} - -int32_t AAudioProperty_getMixerBursts() { - const int32_t defaultBursts = 2; // arbitrary, use 2 for double buffered - const int32_t maxBursts = 1024; // arbitrary - int32_t prop = property_get_int32(AAUDIO_PROP_MIXER_BURSTS, defaultBursts); - if (prop < 1 || prop > maxBursts) { - ALOGE("AAudioProperty_getMixerBursts: invalid = %d", prop); - prop = defaultBursts; - } - return prop; -} - int32_t AAudioProperty_getWakeupDelayMicros() { const int32_t minMicros = 0; // arbitrary const int32_t defaultMicros = 200; // arbitrary, based on some observed jitter @@ -599,9 +560,12 @@ int32_t AAudioProperty_getWakeupDelayMicros() { } int32_t AAudioProperty_getMinimumSleepMicros() { - const int32_t minMicros = 20; // arbitrary - const int32_t defaultMicros = 200; // arbitrary - const int32_t maxMicros = 2000; // arbitrary + const int32_t minMicros = 1; // arbitrary + // Higher values can increase latency for moderate workloads. + // Short values can cause the CPU to short cycle if there is a bug in + // calculating the wakeup times. + const int32_t defaultMicros = 100; // arbitrary + const int32_t maxMicros = 200; // arbitrary int32_t prop = property_get_int32(AAUDIO_PROP_MINIMUM_SLEEP_USEC, defaultMicros); if (prop < minMicros) { ALOGW("AAudioProperty_getMinimumSleepMicros: clipped %d to %d", prop, minMicros); @@ -613,18 +577,6 @@ int32_t AAudioProperty_getMinimumSleepMicros() { return prop; } -int32_t AAudioProperty_getHardwareBurstMinMicros() { - const int32_t defaultMicros = 1000; // arbitrary - const int32_t maxMicros = 1000 * 1000; // arbitrary - int32_t prop = property_get_int32(AAUDIO_PROP_HW_BURST_MIN_USEC, defaultMicros); - if (prop < 1 || prop > maxMicros) { - ALOGE("AAudioProperty_getHardwareBurstMinMicros: invalid = %d, use %d", - prop, defaultMicros); - prop = defaultMicros; - } - return prop; -} - static int32_t AAudioProperty_getMMapOffsetMicros(const char *functionName, const char *propertyName) { const int32_t minMicros = -20000; // arbitrary diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h index f24df464b4c387250fb0069a5cdcfd4230ce451e..b59ce1cd87ec1b494ad8cdca5d231e22f7b2a495 100644 --- a/media/libaaudio/src/utility/AAudioUtilities.h +++ b/media/libaaudio/src/utility/AAudioUtilities.h @@ -127,27 +127,6 @@ audio_channel_mask_t AAudio_getChannelMaskForOpen( // Note that this code may be replaced by Settings or by some other system configuration tool. -/** - * Read system property. - * @return AAUDIO_UNSPECIFIED, AAUDIO_POLICY_NEVER or AAUDIO_POLICY_AUTO or AAUDIO_POLICY_ALWAYS - */ -int32_t AAudioProperty_getMMapPolicy(); -#define AAUDIO_PROP_MMAP_POLICY "aaudio.mmap_policy" - -/** - * Read system property. - * @return AAUDIO_UNSPECIFIED, AAUDIO_POLICY_NEVER or AAUDIO_POLICY_AUTO or AAUDIO_POLICY_ALWAYS - */ -int32_t AAudioProperty_getMMapExclusivePolicy(); -#define AAUDIO_PROP_MMAP_EXCLUSIVE_POLICY "aaudio.mmap_exclusive_policy" - -/** - * Read system property. - * @return number of bursts per AAudio service mixer cycle - */ -int32_t AAudioProperty_getMixerBursts(); -#define AAUDIO_PROP_MIXER_BURSTS "aaudio.mixer_bursts" - /** * Read a system property that specifies the number of extra microseconds that a thread * should sleep when waiting for another thread to service a FIFO. This is used @@ -168,19 +147,6 @@ int32_t AAudioProperty_getWakeupDelayMicros(); int32_t AAudioProperty_getMinimumSleepMicros(); #define AAUDIO_PROP_MINIMUM_SLEEP_USEC "aaudio.minimum_sleep_usec" -/** - * Read system property. - * This is handy in case the DMA is bursting too quickly for the CPU to keep up. - * For example, there may be a DMA burst every 100 usec but you only - * want to feed the MMAP buffer every 2000 usec. - * - * This will affect the framesPerBurst for an MMAP stream. - * - * @return minimum number of microseconds for a MMAP HW burst - */ -int32_t AAudioProperty_getHardwareBurstMinMicros(); -#define AAUDIO_PROP_HW_BURST_MIN_USEC "aaudio.hw_burst_min_usec" - /** * Read a system property that specifies an offset that will be added to MMAP timestamps. * This can be used to correct bias in the timestamp. @@ -227,7 +193,7 @@ aaudio_result_t AAudio_isFlushAllowed(aaudio_stream_state_t state); * @return true if f() eventually returns true. */ static inline bool AAudio_tryUntilTrue( - std::function f, int times, int sleepMs) { + const std::function& f, int times, int sleepMs) { static const useconds_t US_PER_MS = 1000; sleepMs = std::max(sleepMs, 0); @@ -299,9 +265,7 @@ private: class Timestamp { public: - Timestamp() - : mPosition(0) - , mNanoseconds(0) {} + Timestamp() = default; Timestamp(int64_t position, int64_t nanoseconds) : mPosition(position) , mNanoseconds(nanoseconds) {} @@ -312,8 +276,8 @@ public: private: // These cannot be const because we need to implement the copy assignment operator. - int64_t mPosition; - int64_t mNanoseconds; + int64_t mPosition{0}; + int64_t mNanoseconds{0}; }; diff --git a/media/libaaudio/src/utility/FixedBlockAdapter.h b/media/libaaudio/src/utility/FixedBlockAdapter.h index 4dc7e68959714c0ad036d8c4371ff405c9eaa08a..290e47360fd8d8f43526a1273c7c070024b58ca0 100644 --- a/media/libaaudio/src/utility/FixedBlockAdapter.h +++ b/media/libaaudio/src/utility/FixedBlockAdapter.h @@ -35,7 +35,7 @@ public: class FixedBlockAdapter { public: - FixedBlockAdapter(FixedBlockProcessor &fixedBlockProcessor) + explicit FixedBlockAdapter(FixedBlockProcessor &fixedBlockProcessor) : mFixedBlockProcessor(fixedBlockProcessor) {} virtual ~FixedBlockAdapter() = default; diff --git a/media/libaaudio/src/utility/FixedBlockReader.h b/media/libaaudio/src/utility/FixedBlockReader.h index 128dd52fddb9df9dd0da0ca4a94ec6da93ee7476..dc824167f8944410fad70faeac1a5ad9f7a84f50 100644 --- a/media/libaaudio/src/utility/FixedBlockReader.h +++ b/media/libaaudio/src/utility/FixedBlockReader.h @@ -30,7 +30,7 @@ class FixedBlockReader : public FixedBlockAdapter { public: - FixedBlockReader(FixedBlockProcessor &fixedBlockProcessor); + explicit FixedBlockReader(FixedBlockProcessor &fixedBlockProcessor); virtual ~FixedBlockReader() = default; diff --git a/media/libaaudio/src/utility/FixedBlockWriter.h b/media/libaaudio/src/utility/FixedBlockWriter.h index f1d917ca081ce60e7a68d97f005db1cc4086c910..3e89b5d0c6012cdc8f940ae59587e2144eccca4f 100644 --- a/media/libaaudio/src/utility/FixedBlockWriter.h +++ b/media/libaaudio/src/utility/FixedBlockWriter.h @@ -28,7 +28,7 @@ class FixedBlockWriter : public FixedBlockAdapter { public: - FixedBlockWriter(FixedBlockProcessor &fixedBlockProcessor); + explicit FixedBlockWriter(FixedBlockProcessor &fixedBlockProcessor); virtual ~FixedBlockWriter() = default; diff --git a/media/libaaudio/src/utility/MonotonicCounter.h b/media/libaaudio/src/utility/MonotonicCounter.h index 63add4e8dd487d82a391d677021008617431b07b..51eb69b00f501c1213a290f36bc00a6d391b8647 100644 --- a/media/libaaudio/src/utility/MonotonicCounter.h +++ b/media/libaaudio/src/utility/MonotonicCounter.h @@ -30,8 +30,8 @@ class MonotonicCounter { public: - MonotonicCounter() {}; - virtual ~MonotonicCounter() {}; + MonotonicCounter() = default; + virtual ~MonotonicCounter() = default; /** * @return current value of the counter @@ -41,7 +41,12 @@ public: } /** - * advance the current value to match the counter + * Advance the current value to match the counter. + * + * Note that it will take several million years for the 64-bit + * counters to wrap around. + * So we do not use __builtin_sub_overflow. + * We want to know if overflow happens because of a bug. */ void catchUpTo(int64_t counter) { if ((counter - mCounter64) > 0) { @@ -74,7 +79,8 @@ public: * @return current value of the 64-bit counter */ int64_t update32(int32_t counter32) { - int32_t delta = counter32 - mCounter32; + int32_t delta; + __builtin_sub_overflow(counter32, mCounter32, &delta); // protect against the mCounter64 going backwards if (delta > 0) { mCounter64 += delta; @@ -108,5 +114,4 @@ private: int32_t mCounter32 = 0; }; - #endif //UTILITY_MONOTONIC_COUNTER_H diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp index 98e97274347987ea7cff8dd918742c16bad30caf..4b459091b8db54b1f6be4dc86abb5352f7ec4948 100644 --- a/media/libaaudio/tests/Android.bp +++ b/media/libaaudio/tests/Android.bp @@ -13,6 +13,11 @@ cc_defaults { "-Wall", "-Werror", ], + + sanitize: { + integer_overflow: true, + misc_undefined: ["bounds"], + }, } cc_test { @@ -48,7 +53,7 @@ cc_test { shared_libs: ["libaaudio_internal"], } -cc_test { +cc_binary { name: "test_timestamps", defaults: ["libaaudio_tests_defaults"], srcs: ["test_timestamps.cpp"], @@ -60,121 +65,71 @@ cc_test { name: "test_open_params", defaults: ["libaaudio_tests_defaults"], srcs: ["test_open_params.cpp"], - shared_libs: [ - "libaaudio", - "libbinder", - "libcutils", - "libutils", - ], + shared_libs: ["libaaudio"], } -cc_test { +cc_binary { name: "test_no_close", defaults: ["libaaudio_tests_defaults"], srcs: ["test_no_close.cpp"], - shared_libs: [ - "libaaudio", - "libbinder", - "libcutils", - "libutils", - ], + shared_libs: ["libaaudio"], } -cc_test { +cc_binary { name: "test_aaudio_recovery", defaults: ["libaaudio_tests_defaults"], srcs: ["test_recovery.cpp"], - shared_libs: [ - "libaaudio", - "libbinder", - "libcutils", - "libutils", - ], + shared_libs: ["libaaudio"], } -cc_test { +cc_binary { name: "test_n_streams", defaults: ["libaaudio_tests_defaults"], srcs: ["test_n_streams.cpp"], - shared_libs: [ - "libaaudio", - "libbinder", - "libcutils", - "libutils", - ], + shared_libs: ["libaaudio"], } -cc_test { +cc_binary { name: "test_bad_disconnect", defaults: ["libaaudio_tests_defaults"], srcs: ["test_bad_disconnect.cpp"], - shared_libs: [ - "libaaudio", - "libbinder", - "libcutils", - "libutils", - ], + shared_libs: ["libaaudio"], } cc_test { name: "test_various", defaults: ["libaaudio_tests_defaults"], srcs: ["test_various.cpp"], - shared_libs: [ - "libaaudio", - "libbinder", - "libcutils", - "libutils", - ], + shared_libs: ["libaaudio"], } cc_test { name: "test_session_id", defaults: ["libaaudio_tests_defaults"], srcs: ["test_session_id.cpp"], - shared_libs: [ - "libaaudio", - "libbinder", - "libcutils", - "libutils", - ], + shared_libs: ["libaaudio"], } -cc_test { +cc_binary { name: "test_aaudio_monkey", defaults: ["libaaudio_tests_defaults"], srcs: ["test_aaudio_monkey.cpp"], header_libs: ["libaaudio_example_utils"], - shared_libs: [ - "libaaudio", - "libbinder", - "libcutils", - "libutils", - ], + shared_libs: ["libaaudio"], } cc_test { name: "test_attributes", defaults: ["libaaudio_tests_defaults"], srcs: ["test_attributes.cpp"], - shared_libs: [ - "libaaudio", - "libbinder", - "libcutils", - "libutils", - ], + shared_libs: ["libaaudio"], } cc_test { name: "test_interference", defaults: ["libaaudio_tests_defaults"], srcs: ["test_interference.cpp"], - shared_libs: [ - "libaaudio", - "libbinder", - "libcutils", - "libutils", - ], + shared_libs: ["libaaudio"], } cc_test { @@ -197,27 +152,29 @@ cc_test { } cc_test { - name: "test_return_stop", + name: "test_monotonic_counter", defaults: ["libaaudio_tests_defaults"], - srcs: ["test_return_stop.cpp"], + srcs: ["test_monotonic_counter.cpp"], shared_libs: [ - "libaaudio", + "libaaudio_internal", "libbinder", "libcutils", "libutils", ], } +cc_binary { + name: "test_return_stop", + defaults: ["libaaudio_tests_defaults"], + srcs: ["test_return_stop.cpp"], + shared_libs: ["libaaudio"], +} + cc_test { name: "test_callback_race", defaults: ["libaaudio_tests_defaults"], srcs: ["test_callback_race.cpp"], - shared_libs: [ - "libaaudio", - "libbinder", - "libcutils", - "libutils", - ], + shared_libs: ["libaaudio"], } cc_test { @@ -238,7 +195,7 @@ cc_test { ], } -cc_test { +cc_binary { name: "test_steal_exclusive", defaults: ["libaaudio_tests_defaults"], srcs: ["test_steal_exclusive.cpp"], @@ -251,15 +208,9 @@ cc_test { ], } - -cc_test { +cc_binary { name: "test_disconnect_race", defaults: ["libaaudio_tests_defaults"], srcs: ["test_disconnect_race.cpp"], - shared_libs: [ - "libaaudio", - "libbinder", - "libcutils", - "libutils", - ], + shared_libs: ["libaaudio"], } diff --git a/media/libaaudio/tests/test_attributes.cpp b/media/libaaudio/tests/test_attributes.cpp index d540866c094138497319905325a7b2dcd57b9abc..b88d5627ef0a3d3937dcdaae966d216fabe0f48c 100644 --- a/media/libaaudio/tests/test_attributes.cpp +++ b/media/libaaudio/tests/test_attributes.cpp @@ -16,6 +16,10 @@ // Test AAudio attributes such as Usage, ContentType and InputPreset. +// TODO Many of these tests are duplicates of CTS tests in +// "test_aaudio_attributes.cpp". That other file is more current. +// So these tests could be deleted. + #include #include @@ -91,7 +95,7 @@ static void checkAttributes(aaudio_performance_mode_t perfMode, aaudio_allowed_capture_policy_t expectedCapturePolicy = (capturePolicy == DONT_SET || capturePolicy == AAUDIO_UNSPECIFIED) ? AAUDIO_ALLOW_CAPTURE_BY_ALL // default - : preset; + : capturePolicy; EXPECT_EQ(expectedCapturePolicy, AAudioStream_getAllowedCapturePolicy(aaudioStream)); bool expectedPrivacyMode = @@ -132,10 +136,7 @@ static const aaudio_usage_t sUsages[] = { AAUDIO_USAGE_ASSISTANCE_SONIFICATION, AAUDIO_USAGE_GAME, AAUDIO_USAGE_ASSISTANT, - AAUDIO_SYSTEM_USAGE_EMERGENCY, - AAUDIO_SYSTEM_USAGE_SAFETY, - AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS, - AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT + // Note that the AAUDIO_SYSTEM_USAGE_* values requires special permission. }; static const aaudio_content_type_t sContentypes[] = { diff --git a/media/libaaudio/tests/test_flowgraph.cpp b/media/libaaudio/tests/test_flowgraph.cpp index d563a7e8da193f404fa6087e4f070523e38b8fca..66b77ebd6db4531761acef1452da0c42390c7ce0 100644 --- a/media/libaaudio/tests/test_flowgraph.cpp +++ b/media/libaaudio/tests/test_flowgraph.cpp @@ -16,6 +16,9 @@ /* * Test FlowGraph + * + * This file also tests a few different conversion techniques because + * sometimes that have caused compiler bugs. */ #include @@ -23,35 +26,161 @@ #include #include "flowgraph/ClipToRange.h" +#include "flowgraph/MonoBlend.h" #include "flowgraph/MonoToMultiConverter.h" #include "flowgraph/SourceFloat.h" #include "flowgraph/RampLinear.h" #include "flowgraph/SinkFloat.h" #include "flowgraph/SinkI16.h" #include "flowgraph/SinkI24.h" +#include "flowgraph/SinkI32.h" #include "flowgraph/SourceI16.h" #include "flowgraph/SourceI24.h" -using namespace flowgraph; +using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph; constexpr int kBytesPerI24Packed = 3; +constexpr int kNumSamples = 8; +constexpr std::array kInputFloat = { + 1.0f, 0.5f, -0.25f, -1.0f, + 0.0f, 53.9f, -87.2f, -1.02f}; + +// Corresponding PCM values as integers. +constexpr std::array kExpectedI16 = { + INT16_MAX, 1 << 14, INT16_MIN / 4, INT16_MIN, + 0, INT16_MAX, INT16_MIN, INT16_MIN}; + +constexpr std::array kExpectedI32 = { + INT32_MAX, 1 << 30, INT32_MIN / 4, INT32_MIN, + 0, INT32_MAX, INT32_MIN, INT32_MIN}; + +// =================================== FLOAT to I16 ============== + +// Simple test that tries to reproduce a Clang compiler bug. +__attribute__((noinline)) +void local_convert_float_to_int16(const float *input, + int16_t *output, + int count) { + for (int i = 0; i < count; i++) { + int32_t n = (int32_t) (*input++ * 32768.0f); + *output++ = std::min(INT16_MAX, std::max(INT16_MIN, n)); // clip + } +} + +TEST(test_flowgraph, local_convert_float_int16) { + std::array output; + + // Do it inline, which will probably work even with the buggy compiler. + // This validates the expected data. + const float *in = kInputFloat.data(); + int16_t *out = output.data(); + output.fill(777); + for (int i = 0; i < kNumSamples; i++) { + int32_t n = (int32_t) (*in++ * 32768.0f); + *out++ = std::min(INT16_MAX, std::max(INT16_MIN, n)); // clip + } + for (int i = 0; i < kNumSamples; i++) { + EXPECT_EQ(kExpectedI16.at(i), output.at(i)) << ", i = " << i; + } + + // Convert audio signal using the function. + output.fill(777); + local_convert_float_to_int16(kInputFloat.data(), output.data(), kNumSamples); + for (int i = 0; i < kNumSamples; i++) { + EXPECT_EQ(kExpectedI16.at(i), output.at(i)) << ", i = " << i; + } +} + TEST(test_flowgraph, module_sinki16) { - static const float input[] = {1.0f, 0.5f, -0.25f, -1.0f, 0.0f, 53.9f, -87.2f}; - static const int16_t expected[] = {32767, 16384, -8192, -32768, 0, 32767, -32768}; - int16_t output[20]; + static constexpr int kNumSamples = 8; + std::array output; // larger than input + SourceFloat sourceFloat{1}; SinkI16 sinkI16{1}; - int numInputFrames = sizeof(input) / sizeof(input[0]); - sourceFloat.setData(input, numInputFrames); + sourceFloat.setData(kInputFloat.data(), kNumSamples); sourceFloat.output.connect(&sinkI16.input); - int numOutputFrames = sizeof(output) / sizeof(int16_t); - int32_t numRead = sinkI16.read(output, numOutputFrames); - ASSERT_EQ(numInputFrames, numRead); + output.fill(777); + int32_t numRead = sinkI16.read(output.data(), output.size()); + ASSERT_EQ(kNumSamples, numRead); for (int i = 0; i < numRead; i++) { - EXPECT_EQ(expected[i], output[i]); + EXPECT_EQ(kExpectedI16.at(i), output.at(i)) << ", i = " << i; + } +} + +// =================================== FLOAT to I32 ============== +// Simple test that tries to reproduce a Clang compiler bug. +__attribute__((noinline)) +static int32_t clamp32FromFloat(float f) +{ + static const float scale = (float)(1UL << 31); + static const float limpos = 1.; + static const float limneg = -1.; + + if (f <= limneg) { + return INT32_MIN; + } else if (f >= limpos) { + return INT32_MAX; + } + f *= scale; + /* integer conversion is through truncation (though int to float is not). + * ensure that we round to nearest, ties away from 0. + */ + return f > 0 ? f + 0.5 : f - 0.5; +} + +void local_convert_float_to_int32(const float *input, + int32_t *output, + int count) { + for (int i = 0; i < count; i++) { + *output++ = clamp32FromFloat(*input++); + } +} + +TEST(test_flowgraph, simple_convert_float_int32) { + std::array output; + + // Do it inline, which will probably work even with a buggy compiler. + // This validates the expected data. + const float *in = kInputFloat.data(); + output.fill(777); + int32_t *out = output.data(); + for (int i = 0; i < kNumSamples; i++) { + int64_t n = (int64_t) (*in++ * 2147483648.0f); + *out++ = (int32_t)std::min((int64_t)INT32_MAX, + std::max((int64_t)INT32_MIN, n)); // clip + } + for (int i = 0; i < kNumSamples; i++) { + EXPECT_EQ(kExpectedI32.at(i), output.at(i)) << ", i = " << i; + } +} + +TEST(test_flowgraph, local_convert_float_int32) { + std::array output; + // Convert audio signal using the function. + output.fill(777); + local_convert_float_to_int32(kInputFloat.data(), output.data(), kNumSamples); + for (int i = 0; i < kNumSamples; i++) { + EXPECT_EQ(kExpectedI32.at(i), output.at(i)) << ", i = " << i; + } +} + +TEST(test_flowgraph, module_sinki32) { + std::array output; // larger than input + + SourceFloat sourceFloat{1}; + SinkI32 sinkI32{1}; + + sourceFloat.setData(kInputFloat.data(), kNumSamples); + sourceFloat.output.connect(&sinkI32.input); + + output.fill(777); + int32_t numRead = sinkI32.read(output.data(), output.size()); + ASSERT_EQ(kNumSamples, numRead); + for (int i = 0; i < numRead; i++) { + EXPECT_EQ(kExpectedI32.at(i), output.at(i)) << ", i = " << i; } } @@ -76,31 +205,40 @@ TEST(test_flowgraph, module_mono_to_stereo) { } TEST(test_flowgraph, module_ramp_linear) { + constexpr int singleNumOutput = 1; constexpr int rampSize = 5; constexpr int numOutput = 100; constexpr float value = 1.0f; - constexpr float target = 100.0f; + constexpr float initialTarget = 10.0f; + constexpr float finalTarget = 100.0f; + constexpr float tolerance = 0.0001f; // arbitrary float output[numOutput] = {}; RampLinear rampLinear{1}; SinkFloat sinkFloat{1}; rampLinear.input.setValue(value); rampLinear.setLengthInFrames(rampSize); - rampLinear.setTarget(target); - rampLinear.forceCurrent(0.0f); - rampLinear.output.connect(&sinkFloat.input); + // Check that the values go to the initial target instantly. + rampLinear.setTarget(initialTarget); + int32_t singleNumRead = sinkFloat.read(output, singleNumOutput); + ASSERT_EQ(singleNumRead, singleNumOutput); + EXPECT_NEAR(value * initialTarget, output[0], tolerance); + + // Now set target and check that the linear ramp works as expected. + rampLinear.setTarget(finalTarget); int32_t numRead = sinkFloat.read(output, numOutput); + const float incrementSize = (finalTarget - initialTarget) / rampSize; ASSERT_EQ(numOutput, numRead); - constexpr float tolerance = 0.0001f; // arbitrary + int i = 0; for (; i < rampSize; i++) { - float expected = i * value * target / rampSize; + float expected = value * (initialTarget + i * incrementSize); EXPECT_NEAR(expected, output[i], tolerance); } for (; i < numOutput; i++) { - float expected = value * target; + float expected = value * finalTarget; EXPECT_NEAR(expected, output[i], tolerance); } } @@ -155,3 +293,29 @@ TEST(test_flowgraph, module_clip_to_range) { EXPECT_NEAR(expected[i], output[i], tolerance); } } + +TEST(test_flowgraph, module_mono_blend) { + // Two channel to two channel with 3 inputs and outputs. + constexpr int numChannels = 2; + constexpr int numFrames = 3; + + static const float input[] = {-0.7, 0.5, -0.25, 1.25, 1000, 2000}; + static const float expected[] = {-0.1, -0.1, 0.5, 0.5, 1500, 1500}; + float output[100]; + SourceFloat sourceFloat{numChannels}; + MonoBlend monoBlend{numChannels}; + SinkFloat sinkFloat{numChannels}; + + sourceFloat.setData(input, numFrames); + + sourceFloat.output.connect(&monoBlend.input); + monoBlend.output.connect(&sinkFloat.input); + + int32_t numRead = sinkFloat.read(output, numFrames); + ASSERT_EQ(numRead, numFrames); + constexpr float tolerance = 0.000001f; // arbitrary + for (int i = 0; i < numRead; i++) { + EXPECT_NEAR(expected[i], output[i], tolerance); + } +} + diff --git a/media/libaaudio/tests/test_monotonic_counter.cpp b/media/libaaudio/tests/test_monotonic_counter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5cbbaf764e7bd62c48353ad77a95449841e4319b --- /dev/null +++ b/media/libaaudio/tests/test_monotonic_counter.cpp @@ -0,0 +1,91 @@ +/* + * Copyright 2022 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. + */ + +/* + * Test MonotonicCounter + */ + +#include + +#include + +#include "utility/MonotonicCounter.h" + +TEST(test_monotonic_counter, builtin_wrap) { + int32_t x = 0x7FFFFFF0; + int32_t y = 0x80000010; + int32_t delta; + // delta = y - x; // This would cause a numeric overflow! + __builtin_sub_overflow(y, x, &delta); + ASSERT_EQ(0x20, delta); +} + +// test updating past some overflow points +TEST(test_monotonic_counter, mono_counter_update32_wrap) { + MonotonicCounter counter; + ASSERT_EQ(0, counter.get()); + + static constexpr uint32_t x = (uint32_t) 0x7FFFFFF0; + counter.update32(x); + ASSERT_EQ((int64_t)0x7FFFFFF0, counter.get()); + + static constexpr uint32_t y = (uint32_t) 0x80000010; + counter.update32(y); + ASSERT_EQ((int64_t)0x80000010, counter.get()); + + counter.update32(0); + ASSERT_EQ((int64_t)0x100000000, counter.get()); +} + +TEST(test_monotonic_counter, mono_counter_roundup) { + MonotonicCounter counter; + static constexpr uint32_t x = 2345; + counter.update32(x); + ASSERT_EQ((int64_t)x, counter.get()); + + counter.roundUp64(100); + ASSERT_EQ((int64_t)2400, counter.get()); +} + +TEST(test_monotonic_counter, mono_counter_catchup) { + MonotonicCounter counter; + counter.update32(7654); + counter.catchUpTo(5000); // already past 5000 so no change + ASSERT_EQ((int64_t)7654, counter.get()); + counter.catchUpTo(9876); // jumps + ASSERT_EQ((int64_t)9876, counter.get()); +} + +TEST(test_monotonic_counter, mono_counter_increment) { + MonotonicCounter counter; + counter.update32(1000); + counter.increment(-234); // will not go backwards + ASSERT_EQ((int64_t)1000, counter.get()); + counter.increment(96); // advances + ASSERT_EQ((int64_t)1096, counter.get()); +} + +TEST(test_monotonic_counter, mono_counter_reset) { + MonotonicCounter counter; + counter.update32(1000); + // Counter is monotonic and should not go backwards. + counter.update32(500); // No change because 32-bit counter is already past 1000. + ASSERT_EQ((int64_t)1000, counter.get()); + + counter.reset32(); + counter.update32(500); + ASSERT_EQ((int64_t)1500, counter.get()); +} diff --git a/media/libaaudio/tests/test_steal_exclusive.cpp b/media/libaaudio/tests/test_steal_exclusive.cpp index 05c560dd602f6ea214cbe9d8cb491310e1002775..ca4f3d6e00edd38167d8eecea5475f3163d00440 100644 --- a/media/libaaudio/tests/test_steal_exclusive.cpp +++ b/media/libaaudio/tests/test_steal_exclusive.cpp @@ -110,7 +110,11 @@ public: mOpenDelayMillis = openDelayMillis; } - void restartStream() { + void setCloseEnabled(bool enabled) { + mCloseEnabled = enabled; + } + + aaudio_result_t restartStream() { int retriesLeft = mMaxRetries; aaudio_result_t result; do { @@ -126,6 +130,7 @@ public: mName.c_str(), AAudio_convertResultToText(result)); } while (retriesLeft-- > 0 && result != AAUDIO_OK); + return result; } aaudio_data_callback_result_t onAudioReady( @@ -189,10 +194,12 @@ public: std::lock_guard lock(mLock); aaudio_result_t result = AAUDIO_OK; if (mStream != nullptr) { - result = AAudioStream_close(mStream); - if (result != AAUDIO_OK) { - printf("AAudioStream_close returned %s\n", - AAudio_convertResultToText(result)); + if (mCloseEnabled) { + result = AAudioStream_close(mStream); + printf("AAudioStream_close() returned %s\n", + AAudio_convertResultToText(result)); + } else { + printf("AAudioStream_close() DISABLED!\n"); } mStream = nullptr; } @@ -232,6 +239,12 @@ public: return AAudioStream_requestStart(mStream); } + aaudio_result_t pause() { + std::lock_guard lock(mLock); + if (mStream == nullptr) return 0; + return AAudioStream_requestPause(mStream); + } + aaudio_result_t stop() { std::lock_guard lock(mLock); if (mStream == nullptr) return 0; @@ -287,6 +300,7 @@ private: std::string mName; int mMaxRetries = 1; int mOpenDelayMillis = 0; + bool mCloseEnabled = true; }; // Callback function that fills the audio output buffer. @@ -319,11 +333,13 @@ static void s_myErrorCallbackProc( } static void s_usage() { - printf("test_steal_exclusive [-i] [-r{maxRetries}] [-d{delay}] -s\n"); + printf("test_steal_exclusive [-i] [-r{maxRetries}] [-d{delay}] [-p{pausedTime}]-s -c{flag}\n"); printf(" -i direction INPUT, otherwise OUTPUT\n"); - printf(" -d delay open by milliseconds, default = 0\n"); - printf(" -r max retries in the error callback, default = 1\n"); + printf(" -d Delay open by milliseconds, default = 0\n"); + printf(" -p Pause first stream then sleep for msec before opening second streams, default = 0\n"); + printf(" -r max Retries in the error callback, default = 1\n"); printf(" -s try to open in SHARED mode\n"); + printf(" -c enable or disabling Closing of the stream with 0/1, default = 1\n"); } int main(int argc, char ** argv) { @@ -334,7 +350,9 @@ int main(int argc, char ** argv) { int errorCount = 0; int maxRetries = 1; int openDelayMillis = 0; + bool closeEnabled = true; aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE; + int pausedMillis = 0; // Make printf print immediately so that debug info is not stuck // in a buffer if we hang or crash. @@ -348,12 +366,18 @@ int main(int argc, char ** argv) { if (arg[0] == '-') { char option = arg[1]; switch (option) { + case 'c': + closeEnabled = atoi(&arg[2]) != 0; + break; case 'd': openDelayMillis = atoi(&arg[2]); break; case 'i': direction = AAUDIO_DIRECTION_INPUT; break; + case 'p': + pausedMillis = atoi(&arg[2]); + break; case 'r': maxRetries = atoi(&arg[2]); break; @@ -376,6 +400,8 @@ int main(int argc, char ** argv) { thief.setOpenDelayMillis(openDelayMillis); victim.setMaxRetries(maxRetries); thief.setMaxRetries(maxRetries); + victim.setCloseEnabled(closeEnabled); + thief.setCloseEnabled(closeEnabled); result = victim.openAudioStream(direction, requestedSharingMode); if (result != AAUDIO_OK) { @@ -414,6 +440,12 @@ int main(int argc, char ** argv) { } } + if (pausedMillis > 0) { + printf("Pausing the VICTIM for %d millis before starting THIEF -----\n", pausedMillis); + victim.pause(); + usleep(pausedMillis * 1000); + } + printf("Trying to start the THIEF stream, which may steal the VICTIM MMAP resource -----\n"); result = thief.openAudioStream(direction, requestedSharingMode); if (result != AAUDIO_OK) { @@ -429,6 +461,25 @@ int main(int argc, char ** argv) { errorCount++; } + if (pausedMillis > 0) { + result = victim.start(); + printf("Restarting VICTIM, AAudioStream_requestStart(VICTIM) returned %d " + ">>>>>>>>>>>>>>>>>>>>>>\n", result); + if (result == AAUDIO_ERROR_DISCONNECTED) { + // The stream is disconnected due to thief steal the resource + printf("VICTIM was disconnected while hanging as the THIEF " + "stole the resource >>>>>>>>>>>>>>>>>>>>>>\n"); + result = victim.restartStream(); + printf("Restarting VICTIM, AAudioStream_requestStart(VICTIM) returned %d " + ">>>>>>>>>>>>>>>>>>>>>>\n", result); + if (result != AAUDIO_OK) { + errorCount++; + } + } else { + errorCount++; + } + } + // Give stream time to advance. usleep(SLEEP_DURATION_MSEC * 1000); @@ -442,7 +493,7 @@ int main(int argc, char ** argv) { } LOGI("Both streams running. Ask user to plug in headset. ===="); - printf("\n====\nPlease PLUG IN A HEADSET now!\n====\n\n"); + printf("\n====\nPlease PLUG IN A HEADSET now! - OPTIONAL\n====\n\n"); if (result == AAUDIO_OK) { const int watchLoops = DUET_DURATION_MSEC / SLEEP_DURATION_MSEC; diff --git a/media/libaudioclient/AidlConversion.cpp b/media/libaudioclient/AidlConversion.cpp index a9ac3d90ef1c303d84d8d268495cb7f3909a021d..11724e0e8330527e6c2a768cddf9a92b1cb25e59 100644 --- a/media/libaudioclient/AidlConversion.cpp +++ b/media/libaudioclient/AidlConversion.cpp @@ -14,6 +14,11 @@ * limitations under the License. */ +#include +#include +#include +#include + #define LOG_TAG "AidlConversion" //#define LOG_NDEBUG 0 #include @@ -21,6 +26,7 @@ #include "media/AidlConversion.h" #include +#include //////////////////////////////////////////////////////////////////////////////////////////////////// // Utilities @@ -28,6 +34,40 @@ namespace android { using base::unexpected; +using media::audio::common::AudioChannelLayout; +using media::audio::common::AudioConfig; +using media::audio::common::AudioConfigBase; +using media::audio::common::AudioContentType; +using media::audio::common::AudioDevice; +using media::audio::common::AudioDeviceAddress; +using media::audio::common::AudioDeviceDescription; +using media::audio::common::AudioDeviceType; +using media::audio::common::AudioEncapsulationMetadataType; +using media::audio::common::AudioEncapsulationMode; +using media::audio::common::AudioEncapsulationType; +using media::audio::common::AudioFormatDescription; +using media::audio::common::AudioFormatType; +using media::audio::common::AudioGain; +using media::audio::common::AudioGainConfig; +using media::audio::common::AudioGainMode; +using media::audio::common::AudioInputFlags; +using media::audio::common::AudioIoFlags; +using media::audio::common::AudioMode; +using media::audio::common::AudioOffloadInfo; +using media::audio::common::AudioOutputFlags; +using media::audio::common::AudioPortDeviceExt; +using media::audio::common::AudioPortExt; +using media::audio::common::AudioPortMixExt; +using media::audio::common::AudioPortMixExtUseCase; +using media::audio::common::AudioProfile; +using media::audio::common::AudioSource; +using media::audio::common::AudioStandard; +using media::audio::common::AudioStreamType; +using media::audio::common::AudioUsage; +using media::audio::common::AudioUuid; +using media::audio::common::ExtraAudioDescriptor; +using media::audio::common::Int; +using media::audio::common::PcmType; namespace { @@ -219,75 +259,7 @@ ConversionResult legacy2aidl_String8_string(const String8& legacy) return std::string(legacy.c_str()); } -// The legacy enum is unnamed. Thus, we use int32_t. -ConversionResult aidl2legacy_AudioPortConfigType_int32_t( - media::AudioPortConfigType aidl) { - switch (aidl) { - case media::AudioPortConfigType::SAMPLE_RATE: - return AUDIO_PORT_CONFIG_SAMPLE_RATE; - case media::AudioPortConfigType::CHANNEL_MASK: - return AUDIO_PORT_CONFIG_CHANNEL_MASK; - case media::AudioPortConfigType::FORMAT: - return AUDIO_PORT_CONFIG_FORMAT; - case media::AudioPortConfigType::GAIN: - return AUDIO_PORT_CONFIG_GAIN; - case media::AudioPortConfigType::FLAGS: - return AUDIO_PORT_CONFIG_FLAGS; - } - return unexpected(BAD_VALUE); -} - -// The legacy enum is unnamed. Thus, we use int32_t. -ConversionResult legacy2aidl_int32_t_AudioPortConfigType( - int32_t legacy) { - switch (legacy) { - case AUDIO_PORT_CONFIG_SAMPLE_RATE: - return media::AudioPortConfigType::SAMPLE_RATE; - case AUDIO_PORT_CONFIG_CHANNEL_MASK: - return media::AudioPortConfigType::CHANNEL_MASK; - case AUDIO_PORT_CONFIG_FORMAT: - return media::AudioPortConfigType::FORMAT; - case AUDIO_PORT_CONFIG_GAIN: - return media::AudioPortConfigType::GAIN; - case AUDIO_PORT_CONFIG_FLAGS: - return media::AudioPortConfigType::FLAGS; - } - return unexpected(BAD_VALUE); -} - -ConversionResult aidl2legacy_int32_t_config_mask(int32_t aidl) { - return convertBitmask( - aidl, aidl2legacy_AudioPortConfigType_int32_t, - // AudioPortConfigType enum is index-based. - indexToEnum_index, - // AUDIO_PORT_CONFIG_* flags are mask-based. - enumToMask_bitmask); -} - -ConversionResult legacy2aidl_config_mask_int32_t(unsigned int legacy) { - return convertBitmask( - legacy, legacy2aidl_int32_t_AudioPortConfigType, - // AUDIO_PORT_CONFIG_* flags are mask-based. - indexToEnum_bitmask, - // AudioPortConfigType enum is index-based. - enumToMask_index); -} - -ConversionResult aidl2legacy_int32_t_audio_channel_mask_t(int32_t aidl) { - // TODO(ytai): should we convert bit-by-bit? - // One problem here is that the representation is both opaque and is different based on the - // context (input vs. output). Can determine based on type and role, as per useInChannelMask(). - return convertReinterpret(aidl); -} - -ConversionResult legacy2aidl_audio_channel_mask_t_int32_t(audio_channel_mask_t legacy) { - // TODO(ytai): should we convert bit-by-bit? - // One problem here is that the representation is both opaque and is different based on the - // context (input vs. output). Can determine based on type and role, as per useInChannelMask(). - return convertReinterpret(legacy); -} - -ConversionResult aidl2legacy_AudioIoConfigEvent_audio_io_config_event( +ConversionResult aidl2legacy_AudioIoConfigEvent_audio_io_config_event_t( media::AudioIoConfigEvent aidl) { switch (aidl) { case media::AudioIoConfigEvent::OUTPUT_REGISTERED: @@ -312,8 +284,8 @@ ConversionResult aidl2legacy_AudioIoConfigEvent_audio_io_ return unexpected(BAD_VALUE); } -ConversionResult legacy2aidl_audio_io_config_event_AudioIoConfigEvent( - audio_io_config_event legacy) { +ConversionResult legacy2aidl_audio_io_config_event_t_AudioIoConfigEvent( + audio_io_config_event_t legacy) { switch (legacy) { case AUDIO_OUTPUT_REGISTERED: return media::AudioIoConfigEvent::OUTPUT_REGISTERED; @@ -393,81 +365,1029 @@ ConversionResult legacy2aidl_audio_port_type_t_AudioPortTy return unexpected(BAD_VALUE); } -ConversionResult aidl2legacy_AudioFormat_audio_format_t( - media::audio::common::AudioFormat aidl) { - // This relies on AudioFormat being kept in sync with audio_format_t. - static_assert(sizeof(media::audio::common::AudioFormat) == sizeof(audio_format_t)); - return static_cast(aidl); +namespace { + +namespace detail { +using AudioChannelBitPair = std::pair; +using AudioChannelBitPairs = std::vector; +using AudioChannelPair = std::pair; +using AudioChannelPairs = std::vector; +using AudioDevicePair = std::pair; +using AudioDevicePairs = std::vector; +using AudioFormatPair = std::pair; +using AudioFormatPairs = std::vector; +} + +const detail::AudioChannelBitPairs& getInAudioChannelBits() { + static const detail::AudioChannelBitPairs pairs = { + { AUDIO_CHANNEL_IN_LEFT, AudioChannelLayout::CHANNEL_FRONT_LEFT }, + { AUDIO_CHANNEL_IN_RIGHT, AudioChannelLayout::CHANNEL_FRONT_RIGHT }, + // AUDIO_CHANNEL_IN_FRONT is at the end + { AUDIO_CHANNEL_IN_BACK, AudioChannelLayout::CHANNEL_BACK_CENTER }, + // AUDIO_CHANNEL_IN_*_PROCESSED not supported + // AUDIO_CHANNEL_IN_PRESSURE not supported + // AUDIO_CHANNEL_IN_*_AXIS not supported + // AUDIO_CHANNEL_IN_VOICE_* not supported + { AUDIO_CHANNEL_IN_BACK_LEFT, AudioChannelLayout::CHANNEL_BACK_LEFT }, + { AUDIO_CHANNEL_IN_BACK_RIGHT, AudioChannelLayout::CHANNEL_BACK_RIGHT }, + { AUDIO_CHANNEL_IN_CENTER, AudioChannelLayout::CHANNEL_FRONT_CENTER }, + { AUDIO_CHANNEL_IN_LOW_FREQUENCY, AudioChannelLayout::CHANNEL_LOW_FREQUENCY }, + { AUDIO_CHANNEL_IN_TOP_LEFT, AudioChannelLayout::CHANNEL_TOP_SIDE_LEFT }, + { AUDIO_CHANNEL_IN_TOP_RIGHT, AudioChannelLayout::CHANNEL_TOP_SIDE_RIGHT }, + // When going from aidl to legacy, IN_CENTER is used + { AUDIO_CHANNEL_IN_FRONT, AudioChannelLayout::CHANNEL_FRONT_CENTER } + }; + return pairs; +} + +const detail::AudioChannelPairs& getInAudioChannelPairs() { + static const detail::AudioChannelPairs pairs = { +#define DEFINE_INPUT_LAYOUT(n) \ + { \ + AUDIO_CHANNEL_IN_##n, \ + AudioChannelLayout::make( \ + AudioChannelLayout::LAYOUT_##n) \ + } + + DEFINE_INPUT_LAYOUT(MONO), + DEFINE_INPUT_LAYOUT(STEREO), + DEFINE_INPUT_LAYOUT(FRONT_BACK), + // AUDIO_CHANNEL_IN_6 not supported + DEFINE_INPUT_LAYOUT(2POINT0POINT2), + DEFINE_INPUT_LAYOUT(2POINT1POINT2), + DEFINE_INPUT_LAYOUT(3POINT0POINT2), + DEFINE_INPUT_LAYOUT(3POINT1POINT2), + DEFINE_INPUT_LAYOUT(5POINT1) +#undef DEFINE_INPUT_LAYOUT + }; + return pairs; +} + +const detail::AudioChannelBitPairs& getOutAudioChannelBits() { + static const detail::AudioChannelBitPairs pairs = { +#define DEFINE_OUTPUT_BITS(n) \ + { AUDIO_CHANNEL_OUT_##n, AudioChannelLayout::CHANNEL_##n } + + DEFINE_OUTPUT_BITS(FRONT_LEFT), + DEFINE_OUTPUT_BITS(FRONT_RIGHT), + DEFINE_OUTPUT_BITS(FRONT_CENTER), + DEFINE_OUTPUT_BITS(LOW_FREQUENCY), + DEFINE_OUTPUT_BITS(BACK_LEFT), + DEFINE_OUTPUT_BITS(BACK_RIGHT), + DEFINE_OUTPUT_BITS(FRONT_LEFT_OF_CENTER), + DEFINE_OUTPUT_BITS(FRONT_RIGHT_OF_CENTER), + DEFINE_OUTPUT_BITS(BACK_CENTER), + DEFINE_OUTPUT_BITS(SIDE_LEFT), + DEFINE_OUTPUT_BITS(SIDE_RIGHT), + DEFINE_OUTPUT_BITS(TOP_CENTER), + DEFINE_OUTPUT_BITS(TOP_FRONT_LEFT), + DEFINE_OUTPUT_BITS(TOP_FRONT_CENTER), + DEFINE_OUTPUT_BITS(TOP_FRONT_RIGHT), + DEFINE_OUTPUT_BITS(TOP_BACK_LEFT), + DEFINE_OUTPUT_BITS(TOP_BACK_CENTER), + DEFINE_OUTPUT_BITS(TOP_BACK_RIGHT), + DEFINE_OUTPUT_BITS(TOP_SIDE_LEFT), + DEFINE_OUTPUT_BITS(TOP_SIDE_RIGHT), + DEFINE_OUTPUT_BITS(BOTTOM_FRONT_LEFT), + DEFINE_OUTPUT_BITS(BOTTOM_FRONT_CENTER), + DEFINE_OUTPUT_BITS(BOTTOM_FRONT_RIGHT), + DEFINE_OUTPUT_BITS(LOW_FREQUENCY_2), + DEFINE_OUTPUT_BITS(FRONT_WIDE_LEFT), + DEFINE_OUTPUT_BITS(FRONT_WIDE_RIGHT), +#undef DEFINE_OUTPUT_BITS + { AUDIO_CHANNEL_OUT_HAPTIC_A, AudioChannelLayout::CHANNEL_HAPTIC_A }, + { AUDIO_CHANNEL_OUT_HAPTIC_B, AudioChannelLayout::CHANNEL_HAPTIC_B } + }; + return pairs; +} + +const detail::AudioChannelPairs& getOutAudioChannelPairs() { + static const detail::AudioChannelPairs pairs = { +#define DEFINE_OUTPUT_LAYOUT(n) \ + { \ + AUDIO_CHANNEL_OUT_##n, \ + AudioChannelLayout::make( \ + AudioChannelLayout::LAYOUT_##n) \ + } + + DEFINE_OUTPUT_LAYOUT(MONO), + DEFINE_OUTPUT_LAYOUT(STEREO), + DEFINE_OUTPUT_LAYOUT(2POINT1), + DEFINE_OUTPUT_LAYOUT(TRI), + DEFINE_OUTPUT_LAYOUT(TRI_BACK), + DEFINE_OUTPUT_LAYOUT(3POINT1), + DEFINE_OUTPUT_LAYOUT(2POINT0POINT2), + DEFINE_OUTPUT_LAYOUT(2POINT1POINT2), + DEFINE_OUTPUT_LAYOUT(3POINT0POINT2), + DEFINE_OUTPUT_LAYOUT(3POINT1POINT2), + DEFINE_OUTPUT_LAYOUT(QUAD), + DEFINE_OUTPUT_LAYOUT(QUAD_SIDE), + DEFINE_OUTPUT_LAYOUT(SURROUND), + DEFINE_OUTPUT_LAYOUT(PENTA), + DEFINE_OUTPUT_LAYOUT(5POINT1), + DEFINE_OUTPUT_LAYOUT(5POINT1_SIDE), + DEFINE_OUTPUT_LAYOUT(5POINT1POINT2), + DEFINE_OUTPUT_LAYOUT(5POINT1POINT4), + DEFINE_OUTPUT_LAYOUT(6POINT1), + DEFINE_OUTPUT_LAYOUT(7POINT1), + DEFINE_OUTPUT_LAYOUT(7POINT1POINT2), + DEFINE_OUTPUT_LAYOUT(7POINT1POINT4), + DEFINE_OUTPUT_LAYOUT(13POINT_360RA), + DEFINE_OUTPUT_LAYOUT(22POINT2), + DEFINE_OUTPUT_LAYOUT(MONO_HAPTIC_A), + DEFINE_OUTPUT_LAYOUT(STEREO_HAPTIC_A), + DEFINE_OUTPUT_LAYOUT(HAPTIC_AB), + DEFINE_OUTPUT_LAYOUT(MONO_HAPTIC_AB), + DEFINE_OUTPUT_LAYOUT(STEREO_HAPTIC_AB) +#undef DEFINE_OUTPUT_LAYOUT + }; + return pairs; +} + +const detail::AudioChannelPairs& getVoiceAudioChannelPairs() { + static const detail::AudioChannelPairs pairs = { +#define DEFINE_VOICE_LAYOUT(n) \ + { \ + AUDIO_CHANNEL_IN_VOICE_##n, \ + AudioChannelLayout::make( \ + AudioChannelLayout::VOICE_##n) \ + } + DEFINE_VOICE_LAYOUT(UPLINK_MONO), + DEFINE_VOICE_LAYOUT(DNLINK_MONO), + DEFINE_VOICE_LAYOUT(CALL_MONO) +#undef DEFINE_VOICE_LAYOUT + }; + return pairs; +} + +AudioDeviceDescription make_AudioDeviceDescription(AudioDeviceType type, + const std::string& connection = "") { + AudioDeviceDescription result; + result.type = type; + result.connection = connection; + return result; +} + +void append_AudioDeviceDescription(detail::AudioDevicePairs& pairs, + audio_devices_t inputType, audio_devices_t outputType, + AudioDeviceType inType, AudioDeviceType outType, + const std::string& connection = "") { + pairs.push_back(std::make_pair(inputType, make_AudioDeviceDescription(inType, connection))); + pairs.push_back(std::make_pair(outputType, make_AudioDeviceDescription(outType, connection))); +} + +const detail::AudioDevicePairs& getAudioDevicePairs() { + static const detail::AudioDevicePairs pairs = []() { + detail::AudioDevicePairs pairs = {{ + { + AUDIO_DEVICE_NONE, AudioDeviceDescription{} + }, + { + AUDIO_DEVICE_OUT_EARPIECE, make_AudioDeviceDescription( + AudioDeviceType::OUT_SPEAKER_EARPIECE) + }, + { + AUDIO_DEVICE_OUT_SPEAKER, make_AudioDeviceDescription( + AudioDeviceType::OUT_SPEAKER) + }, + { + AUDIO_DEVICE_OUT_WIRED_HEADPHONE, make_AudioDeviceDescription( + AudioDeviceType::OUT_HEADPHONE, + AudioDeviceDescription::CONNECTION_ANALOG()) + }, + { + AUDIO_DEVICE_OUT_BLUETOOTH_SCO, make_AudioDeviceDescription( + AudioDeviceType::OUT_DEVICE, + AudioDeviceDescription::CONNECTION_BT_SCO()) + }, + { + AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT, make_AudioDeviceDescription( + AudioDeviceType::OUT_CARKIT, + AudioDeviceDescription::CONNECTION_BT_SCO()) + }, + { + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES, make_AudioDeviceDescription( + AudioDeviceType::OUT_HEADPHONE, + AudioDeviceDescription::CONNECTION_BT_A2DP()) + }, + { + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER, make_AudioDeviceDescription( + AudioDeviceType::OUT_SPEAKER, + AudioDeviceDescription::CONNECTION_BT_A2DP()) + }, + { + AUDIO_DEVICE_OUT_TELEPHONY_TX, make_AudioDeviceDescription( + AudioDeviceType::OUT_TELEPHONY_TX) + }, + { + AUDIO_DEVICE_OUT_AUX_LINE, make_AudioDeviceDescription( + AudioDeviceType::OUT_LINE_AUX) + }, + { + AUDIO_DEVICE_OUT_SPEAKER_SAFE, make_AudioDeviceDescription( + AudioDeviceType::OUT_SPEAKER_SAFE) + }, + { + AUDIO_DEVICE_OUT_HEARING_AID, make_AudioDeviceDescription( + AudioDeviceType::OUT_HEARING_AID, + AudioDeviceDescription::CONNECTION_WIRELESS()) + }, + { + AUDIO_DEVICE_OUT_ECHO_CANCELLER, make_AudioDeviceDescription( + AudioDeviceType::OUT_ECHO_CANCELLER) + }, + { + AUDIO_DEVICE_OUT_BLE_SPEAKER, make_AudioDeviceDescription( + AudioDeviceType::OUT_SPEAKER, + AudioDeviceDescription::CONNECTION_BT_LE()) + }, + { + AUDIO_DEVICE_OUT_BLE_BROADCAST, make_AudioDeviceDescription( + AudioDeviceType::OUT_BROADCAST, + AudioDeviceDescription::CONNECTION_BT_LE()) + }, + // AUDIO_DEVICE_IN_AMBIENT and IN_COMMUNICATION are removed since they were deprecated. + { + AUDIO_DEVICE_IN_BUILTIN_MIC, make_AudioDeviceDescription( + AudioDeviceType::IN_MICROPHONE) + }, + { + AUDIO_DEVICE_IN_BACK_MIC, make_AudioDeviceDescription( + AudioDeviceType::IN_MICROPHONE_BACK) + }, + { + AUDIO_DEVICE_IN_TELEPHONY_RX, make_AudioDeviceDescription( + AudioDeviceType::IN_TELEPHONY_RX) + }, + { + AUDIO_DEVICE_IN_TV_TUNER, make_AudioDeviceDescription( + AudioDeviceType::IN_TV_TUNER) + }, + { + AUDIO_DEVICE_IN_LOOPBACK, make_AudioDeviceDescription( + AudioDeviceType::IN_LOOPBACK) + }, + { + AUDIO_DEVICE_IN_BLUETOOTH_BLE, make_AudioDeviceDescription( + AudioDeviceType::IN_DEVICE, + AudioDeviceDescription::CONNECTION_BT_LE()) + }, + { + AUDIO_DEVICE_IN_ECHO_REFERENCE, make_AudioDeviceDescription( + AudioDeviceType::IN_ECHO_REFERENCE) + } + }}; + append_AudioDeviceDescription(pairs, + AUDIO_DEVICE_IN_DEFAULT, AUDIO_DEVICE_OUT_DEFAULT, + AudioDeviceType::IN_DEFAULT, AudioDeviceType::OUT_DEFAULT); + append_AudioDeviceDescription(pairs, + AUDIO_DEVICE_IN_WIRED_HEADSET, AUDIO_DEVICE_OUT_WIRED_HEADSET, + AudioDeviceType::IN_HEADSET, AudioDeviceType::OUT_HEADSET, + AudioDeviceDescription::CONNECTION_ANALOG()); + append_AudioDeviceDescription(pairs, + AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, + AudioDeviceType::IN_HEADSET, AudioDeviceType::OUT_HEADSET, + AudioDeviceDescription::CONNECTION_BT_SCO()); + append_AudioDeviceDescription(pairs, + AUDIO_DEVICE_IN_HDMI, AUDIO_DEVICE_OUT_HDMI, + AudioDeviceType::IN_DEVICE, AudioDeviceType::OUT_DEVICE, + AudioDeviceDescription::CONNECTION_HDMI()); + append_AudioDeviceDescription(pairs, + AUDIO_DEVICE_IN_REMOTE_SUBMIX, AUDIO_DEVICE_OUT_REMOTE_SUBMIX, + AudioDeviceType::IN_SUBMIX, AudioDeviceType::OUT_SUBMIX); + append_AudioDeviceDescription(pairs, + AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET, AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET, + AudioDeviceType::IN_DOCK, AudioDeviceType::OUT_DOCK, + AudioDeviceDescription::CONNECTION_ANALOG()); + append_AudioDeviceDescription(pairs, + AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET, AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET, + AudioDeviceType::IN_DOCK, AudioDeviceType::OUT_DOCK, + AudioDeviceDescription::CONNECTION_USB()); + append_AudioDeviceDescription(pairs, + AUDIO_DEVICE_IN_USB_ACCESSORY, AUDIO_DEVICE_OUT_USB_ACCESSORY, + AudioDeviceType::IN_ACCESSORY, AudioDeviceType::OUT_ACCESSORY, + AudioDeviceDescription::CONNECTION_USB()); + append_AudioDeviceDescription(pairs, + AUDIO_DEVICE_IN_USB_DEVICE, AUDIO_DEVICE_OUT_USB_DEVICE, + AudioDeviceType::IN_DEVICE, AudioDeviceType::OUT_DEVICE, + AudioDeviceDescription::CONNECTION_USB()); + append_AudioDeviceDescription(pairs, + AUDIO_DEVICE_IN_FM_TUNER, AUDIO_DEVICE_OUT_FM, + AudioDeviceType::IN_FM_TUNER, AudioDeviceType::OUT_FM); + append_AudioDeviceDescription(pairs, + AUDIO_DEVICE_IN_LINE, AUDIO_DEVICE_OUT_LINE, + AudioDeviceType::IN_DEVICE, AudioDeviceType::OUT_DEVICE, + AudioDeviceDescription::CONNECTION_ANALOG()); + append_AudioDeviceDescription(pairs, + AUDIO_DEVICE_IN_SPDIF, AUDIO_DEVICE_OUT_SPDIF, + AudioDeviceType::IN_DEVICE, AudioDeviceType::OUT_DEVICE, + AudioDeviceDescription::CONNECTION_SPDIF()); + append_AudioDeviceDescription(pairs, + AUDIO_DEVICE_IN_BLUETOOTH_A2DP, AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, + AudioDeviceType::IN_DEVICE, AudioDeviceType::OUT_DEVICE, + AudioDeviceDescription::CONNECTION_BT_A2DP()); + append_AudioDeviceDescription(pairs, + AUDIO_DEVICE_IN_IP, AUDIO_DEVICE_OUT_IP, + AudioDeviceType::IN_DEVICE, AudioDeviceType::OUT_DEVICE, + AudioDeviceDescription::CONNECTION_IP_V4()); + append_AudioDeviceDescription(pairs, + AUDIO_DEVICE_IN_BUS, AUDIO_DEVICE_OUT_BUS, + AudioDeviceType::IN_DEVICE, AudioDeviceType::OUT_DEVICE, + AudioDeviceDescription::CONNECTION_BUS()); + append_AudioDeviceDescription(pairs, + AUDIO_DEVICE_IN_PROXY, AUDIO_DEVICE_OUT_PROXY, + AudioDeviceType::IN_AFE_PROXY, AudioDeviceType::OUT_AFE_PROXY); + append_AudioDeviceDescription(pairs, + AUDIO_DEVICE_IN_USB_HEADSET, AUDIO_DEVICE_OUT_USB_HEADSET, + AudioDeviceType::IN_HEADSET, AudioDeviceType::OUT_HEADSET, + AudioDeviceDescription::CONNECTION_USB()); + append_AudioDeviceDescription(pairs, + AUDIO_DEVICE_IN_HDMI_ARC, AUDIO_DEVICE_OUT_HDMI_ARC, + AudioDeviceType::IN_DEVICE, AudioDeviceType::OUT_DEVICE, + AudioDeviceDescription::CONNECTION_HDMI_ARC()); + append_AudioDeviceDescription(pairs, + AUDIO_DEVICE_IN_HDMI_EARC, AUDIO_DEVICE_OUT_HDMI_EARC, + AudioDeviceType::IN_DEVICE, AudioDeviceType::OUT_DEVICE, + AudioDeviceDescription::CONNECTION_HDMI_EARC()); + append_AudioDeviceDescription(pairs, + AUDIO_DEVICE_IN_BLE_HEADSET, AUDIO_DEVICE_OUT_BLE_HEADSET, + AudioDeviceType::IN_HEADSET, AudioDeviceType::OUT_HEADSET, + AudioDeviceDescription::CONNECTION_BT_LE()); + return pairs; + }(); + return pairs; +} + +AudioFormatDescription make_AudioFormatDescription(AudioFormatType type) { + AudioFormatDescription result; + result.type = type; + return result; +} + +AudioFormatDescription make_AudioFormatDescription(PcmType pcm) { + auto result = make_AudioFormatDescription(AudioFormatType::PCM); + result.pcm = pcm; + return result; +} + +AudioFormatDescription make_AudioFormatDescription(const std::string& encoding) { + AudioFormatDescription result; + result.encoding = encoding; + return result; +} + +AudioFormatDescription make_AudioFormatDescription(PcmType transport, + const std::string& encoding) { + auto result = make_AudioFormatDescription(encoding); + result.pcm = transport; + return result; +} + +const detail::AudioFormatPairs& getAudioFormatPairs() { + static const detail::AudioFormatPairs pairs = {{ + { + AUDIO_FORMAT_INVALID, + make_AudioFormatDescription(AudioFormatType::SYS_RESERVED_INVALID) + }, + { + AUDIO_FORMAT_DEFAULT, AudioFormatDescription{} + }, + { + AUDIO_FORMAT_PCM_16_BIT, make_AudioFormatDescription(PcmType::INT_16_BIT) + }, + { + AUDIO_FORMAT_PCM_8_BIT, make_AudioFormatDescription(PcmType::UINT_8_BIT) + }, + { + AUDIO_FORMAT_PCM_32_BIT, make_AudioFormatDescription(PcmType::INT_32_BIT) + }, + { + AUDIO_FORMAT_PCM_8_24_BIT, make_AudioFormatDescription(PcmType::FIXED_Q_8_24) + }, + { + AUDIO_FORMAT_PCM_FLOAT, make_AudioFormatDescription(PcmType::FLOAT_32_BIT) + }, + { + AUDIO_FORMAT_PCM_24_BIT_PACKED, make_AudioFormatDescription(PcmType::INT_24_BIT) + }, + { + AUDIO_FORMAT_MP3, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_MPEG) + }, + { + AUDIO_FORMAT_AMR_NB, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AMR_NB) + }, + { + AUDIO_FORMAT_AMR_WB, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AMR_WB) + }, + { + AUDIO_FORMAT_AAC, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_MP4) + }, + { + AUDIO_FORMAT_AAC_MAIN, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_MAIN) + }, + { + AUDIO_FORMAT_AAC_LC, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_LC) + }, + { + AUDIO_FORMAT_AAC_SSR, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_SSR) + }, + { + AUDIO_FORMAT_AAC_LTP, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_LTP) + }, + { + AUDIO_FORMAT_AAC_HE_V1, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_HE_V1) + }, + { + AUDIO_FORMAT_AAC_SCALABLE, + make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_SCALABLE) + }, + { + AUDIO_FORMAT_AAC_ERLC, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_ERLC) + }, + { + AUDIO_FORMAT_AAC_LD, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_LD) + }, + { + AUDIO_FORMAT_AAC_HE_V2, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_HE_V2) + }, + { + AUDIO_FORMAT_AAC_ELD, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_ELD) + }, + { + AUDIO_FORMAT_AAC_XHE, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_XHE) + }, + // AUDIO_FORMAT_HE_AAC_V1 and HE_AAC_V2 are removed since they were deprecated long time + // ago. + { + AUDIO_FORMAT_VORBIS, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_VORBIS) + }, + { + AUDIO_FORMAT_OPUS, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_OPUS) + }, + { + AUDIO_FORMAT_AC3, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AC3) + }, + { + AUDIO_FORMAT_E_AC3, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_EAC3) + }, + { + AUDIO_FORMAT_E_AC3_JOC, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_EAC3_JOC) + }, + { + AUDIO_FORMAT_DTS, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_DTS) + }, + { + AUDIO_FORMAT_DTS_HD, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_DTS_HD) + }, + // In the future, we would like to represent encapsulated bitstreams as + // nested AudioFormatDescriptions. The legacy 'AUDIO_FORMAT_IEC61937' type doesn't + // specify the format of the encapsulated bitstream. + { + AUDIO_FORMAT_IEC61937, + make_AudioFormatDescription(PcmType::INT_16_BIT, MEDIA_MIMETYPE_AUDIO_IEC61937) + }, + { + AUDIO_FORMAT_DOLBY_TRUEHD, + make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_DOLBY_TRUEHD) + }, + { + AUDIO_FORMAT_EVRC, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_EVRC) + }, + { + AUDIO_FORMAT_EVRCB, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_EVRCB) + }, + { + AUDIO_FORMAT_EVRCWB, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_EVRCWB) + }, + { + AUDIO_FORMAT_EVRCNW, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_EVRCNW) + }, + { + AUDIO_FORMAT_AAC_ADIF, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_ADIF) + }, + { + AUDIO_FORMAT_WMA, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_WMA) + }, + { + // Note: not in the IANA registry. + AUDIO_FORMAT_WMA_PRO, make_AudioFormatDescription("audio/x-ms-wma.pro") + }, + { + AUDIO_FORMAT_AMR_WB_PLUS, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AMR_WB_PLUS) + }, + { + AUDIO_FORMAT_MP2, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II) + }, + { + AUDIO_FORMAT_QCELP, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_QCELP) + }, + { + // Note: not in the IANA registry. + AUDIO_FORMAT_DSD, make_AudioFormatDescription("audio/vnd.sony.dsd") + }, + { + AUDIO_FORMAT_FLAC, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_FLAC) + }, + { + AUDIO_FORMAT_ALAC, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_ALAC) + }, + { + // Note: not in the IANA registry. + AUDIO_FORMAT_APE, make_AudioFormatDescription("audio/x-ape") + }, + { + AUDIO_FORMAT_AAC_ADTS, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_ADTS) + }, + { + AUDIO_FORMAT_AAC_ADTS_MAIN, + make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_ADTS_MAIN) + }, + { + AUDIO_FORMAT_AAC_ADTS_LC, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_ADTS_LC) + }, + { + AUDIO_FORMAT_AAC_ADTS_SSR, + make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_ADTS_SSR) + }, + { + AUDIO_FORMAT_AAC_ADTS_LTP, + make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_ADTS_LTP) + }, + { + AUDIO_FORMAT_AAC_ADTS_HE_V1, + make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_ADTS_HE_V1) + }, + { + AUDIO_FORMAT_AAC_ADTS_SCALABLE, + make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_ADTS_SCALABLE) + }, + { + AUDIO_FORMAT_AAC_ADTS_ERLC, + make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_ADTS_ERLC) + }, + { + AUDIO_FORMAT_AAC_ADTS_LD, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_ADTS_LD) + }, + { + AUDIO_FORMAT_AAC_ADTS_HE_V2, + make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_ADTS_HE_V2) + }, + { + AUDIO_FORMAT_AAC_ADTS_ELD, + make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_ADTS_ELD) + }, + { + AUDIO_FORMAT_AAC_ADTS_XHE, + make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_ADTS_XHE) + }, + { + // Note: not in the IANA registry. "vnd.octel.sbc" is not BT SBC. + AUDIO_FORMAT_SBC, make_AudioFormatDescription("audio/x-sbc") + }, + { + AUDIO_FORMAT_APTX, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_APTX) + }, + { + // Note: not in the IANA registry. + AUDIO_FORMAT_APTX_HD, make_AudioFormatDescription("audio/vnd.qcom.aptx.hd") + }, + { + AUDIO_FORMAT_AC4, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AC4) + }, + { + // Note: not in the IANA registry. + AUDIO_FORMAT_LDAC, make_AudioFormatDescription("audio/vnd.sony.ldac") + }, + { + AUDIO_FORMAT_MAT, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_DOLBY_MAT) + }, + { + // Note: not in the IANA registry. + AUDIO_FORMAT_MAT_1_0, + make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_DOLBY_MAT + std::string(".1.0")) + }, + { + // Note: not in the IANA registry. + AUDIO_FORMAT_MAT_2_0, + make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_DOLBY_MAT + std::string(".2.0")) + }, + { + // Note: not in the IANA registry. + AUDIO_FORMAT_MAT_2_1, + make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_DOLBY_MAT + std::string(".2.1")) + }, + { + AUDIO_FORMAT_AAC_LATM, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC) + }, + { + AUDIO_FORMAT_AAC_LATM_LC, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_LATM_LC) + }, + { + AUDIO_FORMAT_AAC_LATM_HE_V1, + make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_LATM_HE_V1) + }, + { + AUDIO_FORMAT_AAC_LATM_HE_V2, + make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_AAC_LATM_HE_V2) + }, + { + // Note: not in the IANA registry. + AUDIO_FORMAT_CELT, make_AudioFormatDescription("audio/x-celt") + }, + { + // Note: not in the IANA registry. + AUDIO_FORMAT_APTX_ADAPTIVE, make_AudioFormatDescription("audio/vnd.qcom.aptx.adaptive") + }, + { + // Note: not in the IANA registry. + AUDIO_FORMAT_LHDC, make_AudioFormatDescription("audio/vnd.savitech.lhdc") + }, + { + // Note: not in the IANA registry. + AUDIO_FORMAT_LHDC_LL, make_AudioFormatDescription("audio/vnd.savitech.lhdc.ll") + }, + { + // Note: not in the IANA registry. + AUDIO_FORMAT_APTX_TWSP, make_AudioFormatDescription("audio/vnd.qcom.aptx.twsp") + }, + { + // Note: not in the IANA registry. + AUDIO_FORMAT_LC3, make_AudioFormatDescription("audio/x-lc3") + }, + { + AUDIO_FORMAT_MPEGH, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_MPEGH_MHM1) + }, + { + AUDIO_FORMAT_MPEGH_BL_L3, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_MPEGH_BL_L3) + }, + { + AUDIO_FORMAT_MPEGH_BL_L4, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_MPEGH_BL_L4) + }, + { + AUDIO_FORMAT_MPEGH_LC_L3, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_MPEGH_LC_L3) + }, + { + AUDIO_FORMAT_MPEGH_LC_L4, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_MPEGH_LC_L4) + }, + { + AUDIO_FORMAT_IEC60958, + make_AudioFormatDescription(PcmType::INT_24_BIT, MEDIA_MIMETYPE_AUDIO_IEC60958) + }, + { + AUDIO_FORMAT_DTS_UHD, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_DTS_UHD) + }, + { + AUDIO_FORMAT_DRA, make_AudioFormatDescription(MEDIA_MIMETYPE_AUDIO_DRA) + }, + }}; + return pairs; +} + +template +std::unordered_map make_DirectMap(const std::vector>& v) { + std::unordered_map result(v.begin(), v.end()); + LOG_ALWAYS_FATAL_IF(result.size() != v.size(), "Duplicate key elements detected"); + return result; +} + +template +std::unordered_map make_DirectMap( + const std::vector>& v1, const std::vector>& v2) { + std::unordered_map result(v1.begin(), v1.end()); + LOG_ALWAYS_FATAL_IF(result.size() != v1.size(), "Duplicate key elements detected in v1"); + result.insert(v2.begin(), v2.end()); + LOG_ALWAYS_FATAL_IF(result.size() != v1.size() + v2.size(), + "Duplicate key elements detected in v1+v2"); + return result; +} + +template +std::unordered_map make_ReverseMap(const std::vector>& v) { + std::unordered_map result; + std::transform(v.begin(), v.end(), std::inserter(result, result.begin()), + [](const std::pair& p) { + return std::make_pair(p.second, p.first); + }); + LOG_ALWAYS_FATAL_IF(result.size() != v.size(), "Duplicate key elements detected"); + return result; +} + +} // namespace + +audio_channel_mask_t aidl2legacy_AudioChannelLayout_layout_audio_channel_mask_t_bits( + int aidlLayout, bool isInput) { + auto& bitMapping = isInput ? getInAudioChannelBits() : getOutAudioChannelBits(); + const int aidlLayoutInitial = aidlLayout; // for error message + audio_channel_mask_t legacy = AUDIO_CHANNEL_NONE; + for (const auto& bitPair : bitMapping) { + if ((aidlLayout & bitPair.second) == bitPair.second) { + legacy = static_cast(legacy | bitPair.first); + aidlLayout &= ~bitPair.second; + if (aidlLayout == 0) { + return legacy; + } + } + } + ALOGE("%s: aidl layout 0x%x contains bits 0x%x that have no match to legacy %s bits", + __func__, aidlLayoutInitial, aidlLayout, isInput ? "input" : "output"); + return AUDIO_CHANNEL_NONE; +} + +ConversionResult aidl2legacy_AudioChannelLayout_audio_channel_mask_t( + const AudioChannelLayout& aidl, bool isInput) { + using ReverseMap = std::unordered_map; + using Tag = AudioChannelLayout::Tag; + static const ReverseMap mIn = make_ReverseMap(getInAudioChannelPairs()); + static const ReverseMap mOut = make_ReverseMap(getOutAudioChannelPairs()); + static const ReverseMap mVoice = make_ReverseMap(getVoiceAudioChannelPairs()); + + auto convert = [](const AudioChannelLayout& aidl, const ReverseMap& m, + const char* func, const char* type) -> ConversionResult { + if (auto it = m.find(aidl); it != m.end()) { + return it->second; + } else { + ALOGW("%s: no legacy %s audio_channel_mask_t found for %s", func, type, + aidl.toString().c_str()); + return unexpected(BAD_VALUE); + } + }; + + switch (aidl.getTag()) { + case Tag::none: + return AUDIO_CHANNEL_NONE; + case Tag::invalid: + return AUDIO_CHANNEL_INVALID; + case Tag::indexMask: + // Index masks do not have pre-defined values. + if (const int bits = aidl.get(); + __builtin_popcount(bits) != 0 && + __builtin_popcount(bits) <= AUDIO_CHANNEL_COUNT_MAX) { + return audio_channel_mask_from_representation_and_bits( + AUDIO_CHANNEL_REPRESENTATION_INDEX, bits); + } else { + ALOGE("%s: invalid indexMask value 0x%x in %s", + __func__, bits, aidl.toString().c_str()); + return unexpected(BAD_VALUE); + } + case Tag::layoutMask: + // The fast path is to find a direct match for some known layout mask. + if (const auto layoutMatch = convert(aidl, isInput ? mIn : mOut, __func__, + isInput ? "input" : "output"); + layoutMatch.ok()) { + return layoutMatch; + } + // If a match for a predefined layout wasn't found, make a custom one from bits. + if (audio_channel_mask_t bitMask = + aidl2legacy_AudioChannelLayout_layout_audio_channel_mask_t_bits( + aidl.get(), isInput); + bitMask != AUDIO_CHANNEL_NONE) { + return bitMask; + } + return unexpected(BAD_VALUE); + case Tag::voiceMask: + return convert(aidl, mVoice, __func__, "voice"); + } + ALOGE("%s: unexpected tag value %d", __func__, aidl.getTag()); + return unexpected(BAD_VALUE); +} + +int legacy2aidl_audio_channel_mask_t_bits_AudioChannelLayout_layout( + audio_channel_mask_t legacy, bool isInput) { + auto& bitMapping = isInput ? getInAudioChannelBits() : getOutAudioChannelBits(); + const int legacyInitial = legacy; // for error message + int aidlLayout = 0; + for (const auto& bitPair : bitMapping) { + if ((legacy & bitPair.first) == bitPair.first) { + aidlLayout |= bitPair.second; + legacy = static_cast(legacy & ~bitPair.first); + if (legacy == 0) { + return aidlLayout; + } + } + } + ALOGE("%s: legacy %s audio_channel_mask_t 0x%x contains unrecognized bits 0x%x", + __func__, isInput ? "input" : "output", legacyInitial, legacy); + return 0; +} + +ConversionResult legacy2aidl_audio_channel_mask_t_AudioChannelLayout( + audio_channel_mask_t legacy, bool isInput) { + using DirectMap = std::unordered_map; + using Tag = AudioChannelLayout::Tag; + static const DirectMap mInAndVoice = make_DirectMap( + getInAudioChannelPairs(), getVoiceAudioChannelPairs()); + static const DirectMap mOut = make_DirectMap(getOutAudioChannelPairs()); + + auto convert = [](const audio_channel_mask_t legacy, const DirectMap& m, + const char* func, const char* type) -> ConversionResult { + if (auto it = m.find(legacy); it != m.end()) { + return it->second; + } else { + ALOGW("%s: no AudioChannelLayout found for legacy %s audio_channel_mask_t value 0x%x", + func, type, legacy); + return unexpected(BAD_VALUE); + } + }; + + if (legacy == AUDIO_CHANNEL_NONE) { + return AudioChannelLayout{}; + } else if (legacy == AUDIO_CHANNEL_INVALID) { + return AudioChannelLayout::make(0); + } + + const audio_channel_representation_t repr = audio_channel_mask_get_representation(legacy); + if (repr == AUDIO_CHANNEL_REPRESENTATION_INDEX) { + if (audio_channel_mask_is_valid(legacy)) { + const int indexMask = VALUE_OR_RETURN( + convertIntegral(audio_channel_mask_get_bits(legacy))); + return AudioChannelLayout::make(indexMask); + } else { + ALOGE("%s: legacy audio_channel_mask_t value 0x%x is invalid", __func__, legacy); + return unexpected(BAD_VALUE); + } + } else if (repr == AUDIO_CHANNEL_REPRESENTATION_POSITION) { + // The fast path is to find a direct match for some known layout mask. + if (const auto layoutMatch = convert(legacy, isInput ? mInAndVoice : mOut, __func__, + isInput ? "input / voice" : "output"); + layoutMatch.ok()) { + return layoutMatch; + } + // If a match for a predefined layout wasn't found, make a custom one from bits, + // rejecting those with voice channel bits. + if (!isInput || + (legacy & (AUDIO_CHANNEL_IN_VOICE_UPLINK | AUDIO_CHANNEL_IN_VOICE_DNLINK)) == 0) { + if (int bitMaskLayout = + legacy2aidl_audio_channel_mask_t_bits_AudioChannelLayout_layout( + legacy, isInput); + bitMaskLayout != 0) { + return AudioChannelLayout::make(bitMaskLayout); + } + } else { + ALOGE("%s: legacy audio_channel_mask_t value 0x%x contains voice bits", + __func__, legacy); + } + return unexpected(BAD_VALUE); + } + + ALOGE("%s: unknown representation %d in audio_channel_mask_t value 0x%x", + __func__, repr, legacy); + return unexpected(BAD_VALUE); +} + +ConversionResult aidl2legacy_AudioDeviceDescription_audio_devices_t( + const AudioDeviceDescription& aidl) { + static const std::unordered_map m = + make_ReverseMap(getAudioDevicePairs()); + if (auto it = m.find(aidl); it != m.end()) { + return it->second; + } else { + ALOGE("%s: no legacy audio_devices_t found for %s", __func__, aidl.toString().c_str()); + return unexpected(BAD_VALUE); + } +} + +ConversionResult legacy2aidl_audio_devices_t_AudioDeviceDescription( + audio_devices_t legacy) { + static const std::unordered_map m = + make_DirectMap(getAudioDevicePairs()); + if (auto it = m.find(legacy); it != m.end()) { + return it->second; + } else { + ALOGE("%s: no AudioDeviceDescription found for legacy audio_devices_t value 0x%x", + __func__, legacy); + return unexpected(BAD_VALUE); + } +} + +status_t aidl2legacy_AudioDevice_audio_device( + const AudioDevice& aidl, + audio_devices_t* legacyType, char* legacyAddress) { + *legacyType = VALUE_OR_RETURN_STATUS( + aidl2legacy_AudioDeviceDescription_audio_devices_t(aidl.type)); + return aidl2legacy_string( + aidl.address.get(), + legacyAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN); } -ConversionResult legacy2aidl_audio_format_t_AudioFormat( +status_t aidl2legacy_AudioDevice_audio_device( + const AudioDevice& aidl, + audio_devices_t* legacyType, String8* legacyAddress) { + *legacyType = VALUE_OR_RETURN_STATUS( + aidl2legacy_AudioDeviceDescription_audio_devices_t(aidl.type)); + *legacyAddress = VALUE_OR_RETURN_STATUS(aidl2legacy_string_view_String8( + aidl.address.get())); + return OK; +} + +status_t aidl2legacy_AudioDevice_audio_device( + const AudioDevice& aidl, + audio_devices_t* legacyType, std::string* legacyAddress) { + *legacyType = VALUE_OR_RETURN_STATUS( + aidl2legacy_AudioDeviceDescription_audio_devices_t(aidl.type)); + *legacyAddress = aidl.address.get(); + return OK; +} + +ConversionResult legacy2aidl_audio_device_AudioDevice( + audio_devices_t legacyType, const char* legacyAddress) { + AudioDevice aidl; + aidl.type = VALUE_OR_RETURN( + legacy2aidl_audio_devices_t_AudioDeviceDescription(legacyType)); + const std::string aidl_id = VALUE_OR_RETURN( + legacy2aidl_string(legacyAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN)); + aidl.address = AudioDeviceAddress::make(aidl_id); + return aidl; +} + +ConversionResult +legacy2aidl_audio_device_AudioDevice( + audio_devices_t legacyType, const String8& legacyAddress) { + AudioDevice aidl; + aidl.type = VALUE_OR_RETURN( + legacy2aidl_audio_devices_t_AudioDeviceDescription(legacyType)); + const std::string aidl_id = VALUE_OR_RETURN( + legacy2aidl_String8_string(legacyAddress)); + aidl.address = AudioDeviceAddress::make(aidl_id); + return aidl; +} + +ConversionResult aidl2legacy_AudioFormatDescription_audio_format_t( + const AudioFormatDescription& aidl) { + static const std::unordered_map m = + make_ReverseMap(getAudioFormatPairs()); + if (auto it = m.find(aidl); it != m.end()) { + return it->second; + } else { + ALOGE("%s: no legacy audio_format_t found for %s", __func__, aidl.toString().c_str()); + return unexpected(BAD_VALUE); + } +} + +ConversionResult legacy2aidl_audio_format_t_AudioFormatDescription( audio_format_t legacy) { - // This relies on AudioFormat being kept in sync with audio_format_t. - static_assert(sizeof(media::audio::common::AudioFormat) == sizeof(audio_format_t)); - return static_cast(legacy); + static const std::unordered_map m = + make_DirectMap(getAudioFormatPairs()); + if (auto it = m.find(legacy); it != m.end()) { + return it->second; + } else { + ALOGE("%s: no AudioFormatDescription found for legacy audio_format_t value 0x%x", + __func__, legacy); + return unexpected(BAD_VALUE); + } } -ConversionResult aidl2legacy_AudioGainMode_audio_gain_mode_t(media::AudioGainMode aidl) { +ConversionResult aidl2legacy_AudioGainMode_audio_gain_mode_t( + AudioGainMode aidl) { switch (aidl) { - case media::AudioGainMode::JOINT: + case AudioGainMode::JOINT: return AUDIO_GAIN_MODE_JOINT; - case media::AudioGainMode::CHANNELS: + case AudioGainMode::CHANNELS: return AUDIO_GAIN_MODE_CHANNELS; - case media::AudioGainMode::RAMP: + case AudioGainMode::RAMP: return AUDIO_GAIN_MODE_RAMP; } return unexpected(BAD_VALUE); } -ConversionResult legacy2aidl_audio_gain_mode_t_AudioGainMode(audio_gain_mode_t legacy) { +ConversionResult legacy2aidl_audio_gain_mode_t_AudioGainMode( + audio_gain_mode_t legacy) { switch (legacy) { case AUDIO_GAIN_MODE_JOINT: - return media::AudioGainMode::JOINT; + return AudioGainMode::JOINT; case AUDIO_GAIN_MODE_CHANNELS: - return media::AudioGainMode::CHANNELS; + return AudioGainMode::CHANNELS; case AUDIO_GAIN_MODE_RAMP: - return media::AudioGainMode::RAMP; + return AudioGainMode::RAMP; } return unexpected(BAD_VALUE); } ConversionResult aidl2legacy_int32_t_audio_gain_mode_t_mask(int32_t aidl) { - return convertBitmask( + return convertBitmask( aidl, aidl2legacy_AudioGainMode_audio_gain_mode_t, // AudioGainMode is index-based. - indexToEnum_index, + indexToEnum_index, // AUDIO_GAIN_MODE_* constants are mask-based. enumToMask_bitmask); } ConversionResult legacy2aidl_audio_gain_mode_t_int32_t_mask(audio_gain_mode_t legacy) { - return convertBitmask( + return convertBitmask( legacy, legacy2aidl_audio_gain_mode_t_AudioGainMode, // AUDIO_GAIN_MODE_* constants are mask-based. indexToEnum_bitmask, // AudioGainMode is index-based. - enumToMask_index); -} - -ConversionResult aidl2legacy_int32_t_audio_devices_t(int32_t aidl) { - // TODO(ytai): bitfield? - return convertReinterpret(aidl); -} - -ConversionResult legacy2aidl_audio_devices_t_int32_t(audio_devices_t legacy) { - // TODO(ytai): bitfield? - return convertReinterpret(legacy); + enumToMask_index); } ConversionResult aidl2legacy_AudioGainConfig_audio_gain_config( - const media::AudioGainConfig& aidl, media::AudioPortRole role, media::AudioPortType type) { + const AudioGainConfig& aidl, bool isInput) { audio_gain_config legacy; legacy.index = VALUE_OR_RETURN(convertIntegral(aidl.index)); legacy.mode = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_gain_mode_t_mask(aidl.mode)); - legacy.channel_mask = - VALUE_OR_RETURN(aidl2legacy_int32_t_audio_channel_mask_t(aidl.channelMask)); - const bool isInput = VALUE_OR_RETURN(direction(role, type)) == Direction::INPUT; - const bool isJoint = bitmaskIsSet(aidl.mode, media::AudioGainMode::JOINT); + legacy.channel_mask = VALUE_OR_RETURN( + aidl2legacy_AudioChannelLayout_audio_channel_mask_t(aidl.channelMask, isInput)); + const bool isJoint = bitmaskIsSet(aidl.mode, AudioGainMode::JOINT); size_t numValues = isJoint ? 1 : isInput ? audio_channel_count_from_in_mask(legacy.channel_mask) : audio_channel_count_from_out_mask(legacy.channel_mask); @@ -481,14 +1401,13 @@ ConversionResult aidl2legacy_AudioGainConfig_audio_gain_confi return legacy; } -ConversionResult legacy2aidl_audio_gain_config_AudioGainConfig( - const audio_gain_config& legacy, audio_port_role_t role, audio_port_type_t type) { - media::AudioGainConfig aidl; +ConversionResult legacy2aidl_audio_gain_config_AudioGainConfig( + const audio_gain_config& legacy, bool isInput) { + AudioGainConfig aidl; aidl.index = VALUE_OR_RETURN(convertIntegral(legacy.index)); aidl.mode = VALUE_OR_RETURN(legacy2aidl_audio_gain_mode_t_int32_t_mask(legacy.mode)); - aidl.channelMask = - VALUE_OR_RETURN(legacy2aidl_audio_channel_mask_t_int32_t(legacy.channel_mask)); - const bool isInput = VALUE_OR_RETURN(direction(role, type)) == Direction::INPUT; + aidl.channelMask = VALUE_OR_RETURN( + legacy2aidl_audio_channel_mask_t_AudioChannelLayout(legacy.channel_mask, isInput)); const bool isJoint = (legacy.mode & AUDIO_GAIN_MODE_JOINT) != 0; size_t numValues = isJoint ? 1 : isInput ? audio_channel_count_from_in_mask(legacy.channel_mask) @@ -502,141 +1421,141 @@ ConversionResult legacy2aidl_audio_gain_config_AudioGain } ConversionResult aidl2legacy_AudioInputFlags_audio_input_flags_t( - media::AudioInputFlags aidl) { + AudioInputFlags aidl) { switch (aidl) { - case media::AudioInputFlags::FAST: + case AudioInputFlags::FAST: return AUDIO_INPUT_FLAG_FAST; - case media::AudioInputFlags::HW_HOTWORD: + case AudioInputFlags::HW_HOTWORD: return AUDIO_INPUT_FLAG_HW_HOTWORD; - case media::AudioInputFlags::RAW: + case AudioInputFlags::RAW: return AUDIO_INPUT_FLAG_RAW; - case media::AudioInputFlags::SYNC: + case AudioInputFlags::SYNC: return AUDIO_INPUT_FLAG_SYNC; - case media::AudioInputFlags::MMAP_NOIRQ: + case AudioInputFlags::MMAP_NOIRQ: return AUDIO_INPUT_FLAG_MMAP_NOIRQ; - case media::AudioInputFlags::VOIP_TX: + case AudioInputFlags::VOIP_TX: return AUDIO_INPUT_FLAG_VOIP_TX; - case media::AudioInputFlags::HW_AV_SYNC: + case AudioInputFlags::HW_AV_SYNC: return AUDIO_INPUT_FLAG_HW_AV_SYNC; - case media::AudioInputFlags::DIRECT: + case AudioInputFlags::DIRECT: return AUDIO_INPUT_FLAG_DIRECT; - case media::AudioInputFlags::ULTRASOUND: + case AudioInputFlags::ULTRASOUND: return AUDIO_INPUT_FLAG_ULTRASOUND; } return unexpected(BAD_VALUE); } -ConversionResult legacy2aidl_audio_input_flags_t_AudioInputFlags( +ConversionResult legacy2aidl_audio_input_flags_t_AudioInputFlags( audio_input_flags_t legacy) { switch (legacy) { case AUDIO_INPUT_FLAG_NONE: break; // shouldn't get here. must be listed -Werror,-Wswitch case AUDIO_INPUT_FLAG_FAST: - return media::AudioInputFlags::FAST; + return AudioInputFlags::FAST; case AUDIO_INPUT_FLAG_HW_HOTWORD: - return media::AudioInputFlags::HW_HOTWORD; + return AudioInputFlags::HW_HOTWORD; case AUDIO_INPUT_FLAG_RAW: - return media::AudioInputFlags::RAW; + return AudioInputFlags::RAW; case AUDIO_INPUT_FLAG_SYNC: - return media::AudioInputFlags::SYNC; + return AudioInputFlags::SYNC; case AUDIO_INPUT_FLAG_MMAP_NOIRQ: - return media::AudioInputFlags::MMAP_NOIRQ; + return AudioInputFlags::MMAP_NOIRQ; case AUDIO_INPUT_FLAG_VOIP_TX: - return media::AudioInputFlags::VOIP_TX; + return AudioInputFlags::VOIP_TX; case AUDIO_INPUT_FLAG_HW_AV_SYNC: - return media::AudioInputFlags::HW_AV_SYNC; + return AudioInputFlags::HW_AV_SYNC; case AUDIO_INPUT_FLAG_DIRECT: - return media::AudioInputFlags::DIRECT; + return AudioInputFlags::DIRECT; case AUDIO_INPUT_FLAG_ULTRASOUND: - return media::AudioInputFlags::ULTRASOUND; + return AudioInputFlags::ULTRASOUND; } return unexpected(BAD_VALUE); } ConversionResult aidl2legacy_AudioOutputFlags_audio_output_flags_t( - media::AudioOutputFlags aidl) { + AudioOutputFlags aidl) { switch (aidl) { - case media::AudioOutputFlags::DIRECT: + case AudioOutputFlags::DIRECT: return AUDIO_OUTPUT_FLAG_DIRECT; - case media::AudioOutputFlags::PRIMARY: + case AudioOutputFlags::PRIMARY: return AUDIO_OUTPUT_FLAG_PRIMARY; - case media::AudioOutputFlags::FAST: + case AudioOutputFlags::FAST: return AUDIO_OUTPUT_FLAG_FAST; - case media::AudioOutputFlags::DEEP_BUFFER: + case AudioOutputFlags::DEEP_BUFFER: return AUDIO_OUTPUT_FLAG_DEEP_BUFFER; - case media::AudioOutputFlags::COMPRESS_OFFLOAD: + case AudioOutputFlags::COMPRESS_OFFLOAD: return AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD; - case media::AudioOutputFlags::NON_BLOCKING: + case AudioOutputFlags::NON_BLOCKING: return AUDIO_OUTPUT_FLAG_NON_BLOCKING; - case media::AudioOutputFlags::HW_AV_SYNC: + case AudioOutputFlags::HW_AV_SYNC: return AUDIO_OUTPUT_FLAG_HW_AV_SYNC; - case media::AudioOutputFlags::TTS: + case AudioOutputFlags::TTS: return AUDIO_OUTPUT_FLAG_TTS; - case media::AudioOutputFlags::RAW: + case AudioOutputFlags::RAW: return AUDIO_OUTPUT_FLAG_RAW; - case media::AudioOutputFlags::SYNC: + case AudioOutputFlags::SYNC: return AUDIO_OUTPUT_FLAG_SYNC; - case media::AudioOutputFlags::IEC958_NONAUDIO: + case AudioOutputFlags::IEC958_NONAUDIO: return AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO; - case media::AudioOutputFlags::DIRECT_PCM: + case AudioOutputFlags::DIRECT_PCM: return AUDIO_OUTPUT_FLAG_DIRECT_PCM; - case media::AudioOutputFlags::MMAP_NOIRQ: + case AudioOutputFlags::MMAP_NOIRQ: return AUDIO_OUTPUT_FLAG_MMAP_NOIRQ; - case media::AudioOutputFlags::VOIP_RX: + case AudioOutputFlags::VOIP_RX: return AUDIO_OUTPUT_FLAG_VOIP_RX; - case media::AudioOutputFlags::INCALL_MUSIC: + case AudioOutputFlags::INCALL_MUSIC: return AUDIO_OUTPUT_FLAG_INCALL_MUSIC; - case media::AudioOutputFlags::GAPLESS_OFFLOAD: + case AudioOutputFlags::GAPLESS_OFFLOAD: return AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD; - case media::AudioOutputFlags::SPATIALIZER: - return AUDIO_OUTPUT_FLAG_SPATIALIZER; - case media::AudioOutputFlags::ULTRASOUND: + case AudioOutputFlags::ULTRASOUND: return AUDIO_OUTPUT_FLAG_ULTRASOUND; + case AudioOutputFlags::SPATIALIZER: + return AUDIO_OUTPUT_FLAG_SPATIALIZER; } return unexpected(BAD_VALUE); } -ConversionResult legacy2aidl_audio_output_flags_t_AudioOutputFlags( +ConversionResult legacy2aidl_audio_output_flags_t_AudioOutputFlags( audio_output_flags_t legacy) { switch (legacy) { case AUDIO_OUTPUT_FLAG_NONE: break; // shouldn't get here. must be listed -Werror,-Wswitch case AUDIO_OUTPUT_FLAG_DIRECT: - return media::AudioOutputFlags::DIRECT; + return AudioOutputFlags::DIRECT; case AUDIO_OUTPUT_FLAG_PRIMARY: - return media::AudioOutputFlags::PRIMARY; + return AudioOutputFlags::PRIMARY; case AUDIO_OUTPUT_FLAG_FAST: - return media::AudioOutputFlags::FAST; + return AudioOutputFlags::FAST; case AUDIO_OUTPUT_FLAG_DEEP_BUFFER: - return media::AudioOutputFlags::DEEP_BUFFER; + return AudioOutputFlags::DEEP_BUFFER; case AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD: - return media::AudioOutputFlags::COMPRESS_OFFLOAD; + return AudioOutputFlags::COMPRESS_OFFLOAD; case AUDIO_OUTPUT_FLAG_NON_BLOCKING: - return media::AudioOutputFlags::NON_BLOCKING; + return AudioOutputFlags::NON_BLOCKING; case AUDIO_OUTPUT_FLAG_HW_AV_SYNC: - return media::AudioOutputFlags::HW_AV_SYNC; + return AudioOutputFlags::HW_AV_SYNC; case AUDIO_OUTPUT_FLAG_TTS: - return media::AudioOutputFlags::TTS; + return AudioOutputFlags::TTS; case AUDIO_OUTPUT_FLAG_RAW: - return media::AudioOutputFlags::RAW; + return AudioOutputFlags::RAW; case AUDIO_OUTPUT_FLAG_SYNC: - return media::AudioOutputFlags::SYNC; + return AudioOutputFlags::SYNC; case AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO: - return media::AudioOutputFlags::IEC958_NONAUDIO; + return AudioOutputFlags::IEC958_NONAUDIO; case AUDIO_OUTPUT_FLAG_DIRECT_PCM: - return media::AudioOutputFlags::DIRECT_PCM; + return AudioOutputFlags::DIRECT_PCM; case AUDIO_OUTPUT_FLAG_MMAP_NOIRQ: - return media::AudioOutputFlags::MMAP_NOIRQ; + return AudioOutputFlags::MMAP_NOIRQ; case AUDIO_OUTPUT_FLAG_VOIP_RX: - return media::AudioOutputFlags::VOIP_RX; + return AudioOutputFlags::VOIP_RX; case AUDIO_OUTPUT_FLAG_INCALL_MUSIC: - return media::AudioOutputFlags::INCALL_MUSIC; + return AudioOutputFlags::INCALL_MUSIC; case AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD: - return media::AudioOutputFlags::GAPLESS_OFFLOAD; - case AUDIO_OUTPUT_FLAG_SPATIALIZER: - return media::AudioOutputFlags::SPATIALIZER; + return AudioOutputFlags::GAPLESS_OFFLOAD; case AUDIO_OUTPUT_FLAG_ULTRASOUND: - return media::AudioOutputFlags::ULTRASOUND; + return AudioOutputFlags::ULTRASOUND; + case AUDIO_OUTPUT_FLAG_SPATIALIZER: + return AudioOutputFlags::SPATIALIZER; } return unexpected(BAD_VALUE); } @@ -646,9 +1565,9 @@ ConversionResult aidl2legacy_int32_t_audio_input_flags_t_ma using LegacyMask = std::underlying_type_t; LegacyMask converted = VALUE_OR_RETURN( - (convertBitmask( + (convertBitmask( aidl, aidl2legacy_AudioInputFlags_audio_input_flags_t, - indexToEnum_index, + indexToEnum_index, enumToMask_bitmask))); return static_cast(converted); } @@ -658,10 +1577,10 @@ ConversionResult legacy2aidl_audio_input_flags_t_int32_t_mask( using LegacyMask = std::underlying_type_t; LegacyMask legacyMask = static_cast(legacy); - return convertBitmask( + return convertBitmask( legacyMask, legacy2aidl_audio_input_flags_t_AudioInputFlags, indexToEnum_bitmask, - enumToMask_index); + enumToMask_index); } ConversionResult aidl2legacy_int32_t_audio_output_flags_t_mask( @@ -669,9 +1588,9 @@ ConversionResult aidl2legacy_int32_t_audio_output_flags_t_ return convertBitmask( + AudioOutputFlags>( aidl, aidl2legacy_AudioOutputFlags_audio_output_flags_t, - indexToEnum_index, + indexToEnum_index, enumToMask_bitmask); } @@ -680,229 +1599,215 @@ ConversionResult legacy2aidl_audio_output_flags_t_int32_t_mask( using LegacyMask = std::underlying_type_t; LegacyMask legacyMask = static_cast(legacy); - return convertBitmask( + return convertBitmask( legacyMask, legacy2aidl_audio_output_flags_t_AudioOutputFlags, indexToEnum_bitmask, - enumToMask_index); + enumToMask_index); } ConversionResult aidl2legacy_AudioIoFlags_audio_io_flags( - const media::AudioIoFlags& aidl, media::AudioPortRole role, media::AudioPortType type) { + const AudioIoFlags& aidl, bool isInput) { audio_io_flags legacy; - Direction dir = VALUE_OR_RETURN(direction(role, type)); - switch (dir) { - case Direction::INPUT: { - legacy.input = VALUE_OR_RETURN( - aidl2legacy_int32_t_audio_input_flags_t_mask( - VALUE_OR_RETURN(UNION_GET(aidl, input)))); - } - break; - - case Direction::OUTPUT: { - legacy.output = VALUE_OR_RETURN( - aidl2legacy_int32_t_audio_output_flags_t_mask( - VALUE_OR_RETURN(UNION_GET(aidl, output)))); - } - break; + if (isInput) { + legacy.input = VALUE_OR_RETURN( + aidl2legacy_int32_t_audio_input_flags_t_mask( + VALUE_OR_RETURN(UNION_GET(aidl, input)))); + } else { + legacy.output = VALUE_OR_RETURN( + aidl2legacy_int32_t_audio_output_flags_t_mask( + VALUE_OR_RETURN(UNION_GET(aidl, output)))); } - return legacy; } -ConversionResult legacy2aidl_audio_io_flags_AudioIoFlags( - const audio_io_flags& legacy, audio_port_role_t role, audio_port_type_t type) { - media::AudioIoFlags aidl; - - Direction dir = VALUE_OR_RETURN(direction(role, type)); - switch (dir) { - case Direction::INPUT: - UNION_SET(aidl, input, - VALUE_OR_RETURN(legacy2aidl_audio_input_flags_t_int32_t_mask( - legacy.input))); - break; - case Direction::OUTPUT: - UNION_SET(aidl, output, - VALUE_OR_RETURN(legacy2aidl_audio_output_flags_t_int32_t_mask( - legacy.output))); - break; +ConversionResult legacy2aidl_audio_io_flags_AudioIoFlags( + const audio_io_flags& legacy, bool isInput) { + AudioIoFlags aidl; + if (isInput) { + UNION_SET(aidl, input, + VALUE_OR_RETURN(legacy2aidl_audio_input_flags_t_int32_t_mask(legacy.input))); + } else { + UNION_SET(aidl, output, + VALUE_OR_RETURN(legacy2aidl_audio_output_flags_t_int32_t_mask(legacy.output))); } return aidl; } ConversionResult -aidl2legacy_AudioPortConfigDeviceExt_audio_port_config_device_ext( - const media::AudioPortConfigDeviceExt& aidl) { +aidl2legacy_AudioPortDeviceExt_audio_port_config_device_ext( + const AudioPortDeviceExt& aidl, const media::AudioPortDeviceExtSys& aidlDeviceExt) { audio_port_config_device_ext legacy; - legacy.hw_module = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_module_handle_t(aidl.hwModule)); - legacy.type = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_devices_t(aidl.type)); - RETURN_IF_ERROR(aidl2legacy_string(aidl.address, legacy.address, AUDIO_DEVICE_MAX_ADDRESS_LEN)); + legacy.hw_module = VALUE_OR_RETURN( + aidl2legacy_int32_t_audio_module_handle_t(aidlDeviceExt.hwModule)); + RETURN_IF_ERROR(aidl2legacy_AudioDevice_audio_device( + aidl.device, &legacy.type, legacy.address)); return legacy; } -ConversionResult -legacy2aidl_audio_port_config_device_ext_AudioPortConfigDeviceExt( - const audio_port_config_device_ext& legacy) { - media::AudioPortConfigDeviceExt aidl; - aidl.hwModule = VALUE_OR_RETURN(legacy2aidl_audio_module_handle_t_int32_t(legacy.hw_module)); - aidl.type = VALUE_OR_RETURN(legacy2aidl_audio_devices_t_int32_t(legacy.type)); - aidl.address = VALUE_OR_RETURN( - legacy2aidl_string(legacy.address, AUDIO_DEVICE_MAX_ADDRESS_LEN)); - return aidl; +status_t legacy2aidl_audio_port_config_device_ext_AudioPortDeviceExt( + const audio_port_config_device_ext& legacy, + AudioPortDeviceExt* aidl, media::AudioPortDeviceExtSys* aidlDeviceExt) { + aidlDeviceExt->hwModule = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_module_handle_t_int32_t(legacy.hw_module)); + aidl->device = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_device_AudioDevice(legacy.type, legacy.address)); + return OK; } ConversionResult aidl2legacy_AudioStreamType_audio_stream_type_t( - media::AudioStreamType aidl) { + AudioStreamType aidl) { switch (aidl) { - case media::AudioStreamType::DEFAULT: + case AudioStreamType::INVALID: + break; // return error + case AudioStreamType::SYS_RESERVED_DEFAULT: return AUDIO_STREAM_DEFAULT; - case media::AudioStreamType::VOICE_CALL: + case AudioStreamType::VOICE_CALL: return AUDIO_STREAM_VOICE_CALL; - case media::AudioStreamType::SYSTEM: + case AudioStreamType::SYSTEM: return AUDIO_STREAM_SYSTEM; - case media::AudioStreamType::RING: + case AudioStreamType::RING: return AUDIO_STREAM_RING; - case media::AudioStreamType::MUSIC: + case AudioStreamType::MUSIC: return AUDIO_STREAM_MUSIC; - case media::AudioStreamType::ALARM: + case AudioStreamType::ALARM: return AUDIO_STREAM_ALARM; - case media::AudioStreamType::NOTIFICATION: + case AudioStreamType::NOTIFICATION: return AUDIO_STREAM_NOTIFICATION; - case media::AudioStreamType::BLUETOOTH_SCO: + case AudioStreamType::BLUETOOTH_SCO: return AUDIO_STREAM_BLUETOOTH_SCO; - case media::AudioStreamType::ENFORCED_AUDIBLE: + case AudioStreamType::ENFORCED_AUDIBLE: return AUDIO_STREAM_ENFORCED_AUDIBLE; - case media::AudioStreamType::DTMF: + case AudioStreamType::DTMF: return AUDIO_STREAM_DTMF; - case media::AudioStreamType::TTS: + case AudioStreamType::TTS: return AUDIO_STREAM_TTS; - case media::AudioStreamType::ACCESSIBILITY: + case AudioStreamType::ACCESSIBILITY: return AUDIO_STREAM_ACCESSIBILITY; - case media::AudioStreamType::ASSISTANT: + case AudioStreamType::ASSISTANT: return AUDIO_STREAM_ASSISTANT; - case media::AudioStreamType::REROUTING: + case AudioStreamType::SYS_RESERVED_REROUTING: return AUDIO_STREAM_REROUTING; - case media::AudioStreamType::PATCH: + case AudioStreamType::SYS_RESERVED_PATCH: return AUDIO_STREAM_PATCH; - case media::AudioStreamType::CALL_ASSISTANT: + case AudioStreamType::CALL_ASSISTANT: return AUDIO_STREAM_CALL_ASSISTANT; } return unexpected(BAD_VALUE); } -ConversionResult legacy2aidl_audio_stream_type_t_AudioStreamType( +ConversionResult legacy2aidl_audio_stream_type_t_AudioStreamType( audio_stream_type_t legacy) { switch (legacy) { case AUDIO_STREAM_DEFAULT: - return media::AudioStreamType::DEFAULT; + return AudioStreamType::SYS_RESERVED_DEFAULT; case AUDIO_STREAM_VOICE_CALL: - return media::AudioStreamType::VOICE_CALL; + return AudioStreamType::VOICE_CALL; case AUDIO_STREAM_SYSTEM: - return media::AudioStreamType::SYSTEM; + return AudioStreamType::SYSTEM; case AUDIO_STREAM_RING: - return media::AudioStreamType::RING; + return AudioStreamType::RING; case AUDIO_STREAM_MUSIC: - return media::AudioStreamType::MUSIC; + return AudioStreamType::MUSIC; case AUDIO_STREAM_ALARM: - return media::AudioStreamType::ALARM; + return AudioStreamType::ALARM; case AUDIO_STREAM_NOTIFICATION: - return media::AudioStreamType::NOTIFICATION; + return AudioStreamType::NOTIFICATION; case AUDIO_STREAM_BLUETOOTH_SCO: - return media::AudioStreamType::BLUETOOTH_SCO; + return AudioStreamType::BLUETOOTH_SCO; case AUDIO_STREAM_ENFORCED_AUDIBLE: - return media::AudioStreamType::ENFORCED_AUDIBLE; + return AudioStreamType::ENFORCED_AUDIBLE; case AUDIO_STREAM_DTMF: - return media::AudioStreamType::DTMF; + return AudioStreamType::DTMF; case AUDIO_STREAM_TTS: - return media::AudioStreamType::TTS; + return AudioStreamType::TTS; case AUDIO_STREAM_ACCESSIBILITY: - return media::AudioStreamType::ACCESSIBILITY; + return AudioStreamType::ACCESSIBILITY; case AUDIO_STREAM_ASSISTANT: - return media::AudioStreamType::ASSISTANT; + return AudioStreamType::ASSISTANT; case AUDIO_STREAM_REROUTING: - return media::AudioStreamType::REROUTING; + return AudioStreamType::SYS_RESERVED_REROUTING; case AUDIO_STREAM_PATCH: - return media::AudioStreamType::PATCH; + return AudioStreamType::SYS_RESERVED_PATCH; case AUDIO_STREAM_CALL_ASSISTANT: - return media::AudioStreamType::CALL_ASSISTANT; + return AudioStreamType::CALL_ASSISTANT; } return unexpected(BAD_VALUE); } -ConversionResult aidl2legacy_AudioSourceType_audio_source_t( - media::AudioSourceType aidl) { +ConversionResult aidl2legacy_AudioSource_audio_source_t( + AudioSource aidl) { switch (aidl) { - case media::AudioSourceType::INVALID: - // This value does not have an enum + case AudioSource::SYS_RESERVED_INVALID: return AUDIO_SOURCE_INVALID; - case media::AudioSourceType::DEFAULT: + case AudioSource::DEFAULT: return AUDIO_SOURCE_DEFAULT; - case media::AudioSourceType::MIC: + case AudioSource::MIC: return AUDIO_SOURCE_MIC; - case media::AudioSourceType::VOICE_UPLINK: + case AudioSource::VOICE_UPLINK: return AUDIO_SOURCE_VOICE_UPLINK; - case media::AudioSourceType::VOICE_DOWNLINK: + case AudioSource::VOICE_DOWNLINK: return AUDIO_SOURCE_VOICE_DOWNLINK; - case media::AudioSourceType::VOICE_CALL: + case AudioSource::VOICE_CALL: return AUDIO_SOURCE_VOICE_CALL; - case media::AudioSourceType::CAMCORDER: + case AudioSource::CAMCORDER: return AUDIO_SOURCE_CAMCORDER; - case media::AudioSourceType::VOICE_RECOGNITION: + case AudioSource::VOICE_RECOGNITION: return AUDIO_SOURCE_VOICE_RECOGNITION; - case media::AudioSourceType::VOICE_COMMUNICATION: + case AudioSource::VOICE_COMMUNICATION: return AUDIO_SOURCE_VOICE_COMMUNICATION; - case media::AudioSourceType::REMOTE_SUBMIX: + case AudioSource::REMOTE_SUBMIX: return AUDIO_SOURCE_REMOTE_SUBMIX; - case media::AudioSourceType::UNPROCESSED: + case AudioSource::UNPROCESSED: return AUDIO_SOURCE_UNPROCESSED; - case media::AudioSourceType::VOICE_PERFORMANCE: + case AudioSource::VOICE_PERFORMANCE: return AUDIO_SOURCE_VOICE_PERFORMANCE; - case media::AudioSourceType::ECHO_REFERENCE: + case AudioSource::ULTRASOUND: + return AUDIO_SOURCE_ULTRASOUND; + case AudioSource::ECHO_REFERENCE: return AUDIO_SOURCE_ECHO_REFERENCE; - case media::AudioSourceType::FM_TUNER: + case AudioSource::FM_TUNER: return AUDIO_SOURCE_FM_TUNER; - case media::AudioSourceType::HOTWORD: + case AudioSource::HOTWORD: return AUDIO_SOURCE_HOTWORD; - case media::AudioSourceType::ULTRASOUND: - return AUDIO_SOURCE_ULTRASOUND; } return unexpected(BAD_VALUE); } -ConversionResult legacy2aidl_audio_source_t_AudioSourceType( +ConversionResult legacy2aidl_audio_source_t_AudioSource( audio_source_t legacy) { switch (legacy) { case AUDIO_SOURCE_INVALID: - return media::AudioSourceType::INVALID; + return AudioSource::SYS_RESERVED_INVALID; case AUDIO_SOURCE_DEFAULT: - return media::AudioSourceType::DEFAULT; + return AudioSource::DEFAULT; case AUDIO_SOURCE_MIC: - return media::AudioSourceType::MIC; + return AudioSource::MIC; case AUDIO_SOURCE_VOICE_UPLINK: - return media::AudioSourceType::VOICE_UPLINK; + return AudioSource::VOICE_UPLINK; case AUDIO_SOURCE_VOICE_DOWNLINK: - return media::AudioSourceType::VOICE_DOWNLINK; + return AudioSource::VOICE_DOWNLINK; case AUDIO_SOURCE_VOICE_CALL: - return media::AudioSourceType::VOICE_CALL; + return AudioSource::VOICE_CALL; case AUDIO_SOURCE_CAMCORDER: - return media::AudioSourceType::CAMCORDER; + return AudioSource::CAMCORDER; case AUDIO_SOURCE_VOICE_RECOGNITION: - return media::AudioSourceType::VOICE_RECOGNITION; + return AudioSource::VOICE_RECOGNITION; case AUDIO_SOURCE_VOICE_COMMUNICATION: - return media::AudioSourceType::VOICE_COMMUNICATION; + return AudioSource::VOICE_COMMUNICATION; case AUDIO_SOURCE_REMOTE_SUBMIX: - return media::AudioSourceType::REMOTE_SUBMIX; + return AudioSource::REMOTE_SUBMIX; case AUDIO_SOURCE_UNPROCESSED: - return media::AudioSourceType::UNPROCESSED; + return AudioSource::UNPROCESSED; case AUDIO_SOURCE_VOICE_PERFORMANCE: - return media::AudioSourceType::VOICE_PERFORMANCE; + return AudioSource::VOICE_PERFORMANCE; + case AUDIO_SOURCE_ULTRASOUND: + return AudioSource::ULTRASOUND; case AUDIO_SOURCE_ECHO_REFERENCE: - return media::AudioSourceType::ECHO_REFERENCE; + return AudioSource::ECHO_REFERENCE; case AUDIO_SOURCE_FM_TUNER: - return media::AudioSourceType::FM_TUNER; + return AudioSource::FM_TUNER; case AUDIO_SOURCE_HOTWORD: - return media::AudioSourceType::HOTWORD; - case AUDIO_SOURCE_ULTRASOUND: - return media::AudioSourceType::ULTRASOUND; + return AudioSource::HOTWORD; } return unexpected(BAD_VALUE); } @@ -918,8 +1823,8 @@ ConversionResult legacy2aidl_audio_session_t_int32_t(audio_session_t le // This type is unnamed in the original definition, thus we name it here. using audio_port_config_mix_ext_usecase = decltype(audio_port_config_mix_ext::usecase); -ConversionResult aidl2legacy_AudioPortConfigMixExtUseCase( - const media::AudioPortConfigMixExtUseCase& aidl, media::AudioPortRole role) { +ConversionResult aidl2legacy_AudioPortMixExtUseCase( + const AudioPortMixExtUseCase& aidl, media::AudioPortRole role) { audio_port_config_mix_ext_usecase legacy; switch (role) { @@ -936,16 +1841,16 @@ ConversionResult aidl2legacy_AudioPortConfigM case media::AudioPortRole::SINK: // This is not a bug. A SINK role corresponds to the source field. - legacy.source = VALUE_OR_RETURN(aidl2legacy_AudioSourceType_audio_source_t( + legacy.source = VALUE_OR_RETURN(aidl2legacy_AudioSource_audio_source_t( VALUE_OR_RETURN(UNION_GET(aidl, source)))); return legacy; } LOG_ALWAYS_FATAL("Shouldn't get here"); // with -Werror,-Wswitch may compile-time fail } -ConversionResult legacy2aidl_AudioPortConfigMixExtUseCase( +ConversionResult legacy2aidl_AudioPortMixExtUseCase( const audio_port_config_mix_ext_usecase& legacy, audio_port_role_t role) { - media::AudioPortConfigMixExtUseCase aidl; + AudioPortMixExtUseCase aidl; switch (role) { case AUDIO_PORT_ROLE_NONE: @@ -959,52 +1864,53 @@ ConversionResult legacy2aidl_AudioPortConfi case AUDIO_PORT_ROLE_SINK: // This is not a bug. A SINK role corresponds to the source field. UNION_SET(aidl, source, - VALUE_OR_RETURN(legacy2aidl_audio_source_t_AudioSourceType(legacy.source))); + VALUE_OR_RETURN(legacy2aidl_audio_source_t_AudioSource(legacy.source))); return aidl; } LOG_ALWAYS_FATAL("Shouldn't get here"); // with -Werror,-Wswitch may compile-time fail } -ConversionResult aidl2legacy_AudioPortConfigMixExt( - const media::AudioPortConfigMixExt& aidl, media::AudioPortRole role) { +ConversionResult aidl2legacy_AudioPortMixExt( + const AudioPortMixExt& aidl, media::AudioPortRole role, + const media::AudioPortMixExtSys& aidlMixExt) { audio_port_config_mix_ext legacy; - legacy.hw_module = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_module_handle_t(aidl.hwModule)); + legacy.hw_module = VALUE_OR_RETURN( + aidl2legacy_int32_t_audio_module_handle_t(aidlMixExt.hwModule)); legacy.handle = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_io_handle_t(aidl.handle)); - legacy.usecase = VALUE_OR_RETURN(aidl2legacy_AudioPortConfigMixExtUseCase(aidl.usecase, role)); + legacy.usecase = VALUE_OR_RETURN(aidl2legacy_AudioPortMixExtUseCase(aidl.usecase, role)); return legacy; } -ConversionResult legacy2aidl_AudioPortConfigMixExt( - const audio_port_config_mix_ext& legacy, audio_port_role_t role) { - media::AudioPortConfigMixExt aidl; - aidl.hwModule = VALUE_OR_RETURN(legacy2aidl_audio_module_handle_t_int32_t(legacy.hw_module)); - aidl.handle = VALUE_OR_RETURN(legacy2aidl_audio_io_handle_t_int32_t(legacy.handle)); - aidl.usecase = VALUE_OR_RETURN(legacy2aidl_AudioPortConfigMixExtUseCase(legacy.usecase, role)); - return aidl; +status_t legacy2aidl_AudioPortMixExt( + const audio_port_config_mix_ext& legacy, audio_port_role_t role, + AudioPortMixExt* aidl, media::AudioPortMixExtSys* aidlMixExt) { + aidlMixExt->hwModule = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_module_handle_t_int32_t(legacy.hw_module)); + aidl->handle = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(legacy.handle)); + aidl->usecase = VALUE_OR_RETURN_STATUS( + legacy2aidl_AudioPortMixExtUseCase(legacy.usecase, role)); + return OK; } ConversionResult -aidl2legacy_AudioPortConfigSessionExt_audio_port_config_session_ext( - const media::AudioPortConfigSessionExt& aidl) { +aidl2legacy_int32_t_audio_port_config_session_ext(int32_t aidl) { audio_port_config_session_ext legacy; - legacy.session = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_session_t(aidl.session)); + legacy.session = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_session_t(aidl)); return legacy; } -ConversionResult -legacy2aidl_audio_port_config_session_ext_AudioPortConfigSessionExt( +ConversionResult +legacy2aidl_audio_port_config_session_ext_int32_t( const audio_port_config_session_ext& legacy) { - media::AudioPortConfigSessionExt aidl; - aidl.session = VALUE_OR_RETURN(legacy2aidl_audio_session_t_int32_t(legacy.session)); - return aidl; + return legacy2aidl_audio_session_t_int32_t(legacy.session); } // This type is unnamed in the original definition, thus we name it here. using audio_port_config_ext = decltype(audio_port_config::ext); -ConversionResult aidl2legacy_AudioPortConfigExt( - const media::AudioPortConfigExt& aidl, media::AudioPortType type, - media::AudioPortRole role) { +ConversionResult aidl2legacy_AudioPortExt_audio_port_config_ext( + const AudioPortExt& aidl, media::AudioPortType type, + media::AudioPortRole role, const media::AudioPortExtSys& aidlSys) { audio_port_config_ext legacy; switch (type) { case media::AudioPortType::NONE: @@ -1013,16 +1919,19 @@ ConversionResult aidl2legacy_AudioPortConfigExt( return legacy; case media::AudioPortType::DEVICE: legacy.device = VALUE_OR_RETURN( - aidl2legacy_AudioPortConfigDeviceExt_audio_port_config_device_ext( - VALUE_OR_RETURN(UNION_GET(aidl, device)))); + aidl2legacy_AudioPortDeviceExt_audio_port_config_device_ext( + VALUE_OR_RETURN(UNION_GET(aidl, device)), + VALUE_OR_RETURN(UNION_GET(aidlSys, device)))); return legacy; case media::AudioPortType::MIX: legacy.mix = VALUE_OR_RETURN( - aidl2legacy_AudioPortConfigMixExt(VALUE_OR_RETURN(UNION_GET(aidl, mix)), role)); + aidl2legacy_AudioPortMixExt( + VALUE_OR_RETURN(UNION_GET(aidl, mix)), role, + VALUE_OR_RETURN(UNION_GET(aidlSys, mix)))); return legacy; case media::AudioPortType::SESSION: legacy.session = VALUE_OR_RETURN( - aidl2legacy_AudioPortConfigSessionExt_audio_port_config_session_ext( + aidl2legacy_int32_t_audio_port_config_session_ext( VALUE_OR_RETURN(UNION_GET(aidl, session)))); return legacy; @@ -1030,90 +1939,113 @@ ConversionResult aidl2legacy_AudioPortConfigExt( LOG_ALWAYS_FATAL("Shouldn't get here"); // with -Werror,-Wswitch may compile-time fail } -ConversionResult legacy2aidl_AudioPortConfigExt( - const audio_port_config_ext& legacy, audio_port_type_t type, audio_port_role_t role) { - media::AudioPortConfigExt aidl; - +status_t legacy2aidl_AudioPortExt( + const audio_port_config_ext& legacy, audio_port_type_t type, audio_port_role_t role, + AudioPortExt* aidl, media::AudioPortExtSys* aidlSys) { switch (type) { case AUDIO_PORT_TYPE_NONE: - UNION_SET(aidl, unspecified, false); - return aidl; - case AUDIO_PORT_TYPE_DEVICE: - UNION_SET(aidl, device, - VALUE_OR_RETURN( - legacy2aidl_audio_port_config_device_ext_AudioPortConfigDeviceExt( - legacy.device))); - return aidl; - case AUDIO_PORT_TYPE_MIX: - UNION_SET(aidl, mix, - VALUE_OR_RETURN(legacy2aidl_AudioPortConfigMixExt(legacy.mix, role))); - return aidl; + UNION_SET(*aidl, unspecified, false); + UNION_SET(*aidlSys, unspecified, false); + return OK; + case AUDIO_PORT_TYPE_DEVICE: { + AudioPortDeviceExt device; + media::AudioPortDeviceExtSys deviceSys; + RETURN_STATUS_IF_ERROR( + legacy2aidl_audio_port_config_device_ext_AudioPortDeviceExt( + legacy.device, &device, &deviceSys)); + UNION_SET(*aidl, device, device); + UNION_SET(*aidlSys, device, deviceSys); + return OK; + } + case AUDIO_PORT_TYPE_MIX: { + AudioPortMixExt mix; + media::AudioPortMixExtSys mixSys; + RETURN_STATUS_IF_ERROR(legacy2aidl_AudioPortMixExt(legacy.mix, role, &mix, &mixSys)); + UNION_SET(*aidl, mix, mix); + UNION_SET(*aidlSys, mix, mixSys); + return OK; + } case AUDIO_PORT_TYPE_SESSION: - UNION_SET(aidl, session, - VALUE_OR_RETURN( - legacy2aidl_audio_port_config_session_ext_AudioPortConfigSessionExt( - legacy.session))); - return aidl; + UNION_SET(*aidl, session, VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_port_config_session_ext_int32_t(legacy.session))); + UNION_SET(*aidlSys, unspecified, false); + return OK; } LOG_ALWAYS_FATAL("Shouldn't get here"); // with -Werror,-Wswitch may compile-time fail } ConversionResult aidl2legacy_AudioPortConfig_audio_port_config( const media::AudioPortConfig& aidl) { - audio_port_config legacy; - legacy.id = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_port_handle_t(aidl.id)); - legacy.role = VALUE_OR_RETURN(aidl2legacy_AudioPortRole_audio_port_role_t(aidl.role)); - legacy.type = VALUE_OR_RETURN(aidl2legacy_AudioPortType_audio_port_type_t(aidl.type)); - legacy.config_mask = VALUE_OR_RETURN(aidl2legacy_int32_t_config_mask(aidl.configMask)); - if (bitmaskIsSet(aidl.configMask, media::AudioPortConfigType::SAMPLE_RATE)) { - legacy.sample_rate = VALUE_OR_RETURN(convertIntegral(aidl.sampleRate)); - } - if (bitmaskIsSet(aidl.configMask, media::AudioPortConfigType::CHANNEL_MASK)) { + audio_port_config legacy{}; + legacy.id = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_port_handle_t(aidl.hal.id)); + legacy.role = VALUE_OR_RETURN(aidl2legacy_AudioPortRole_audio_port_role_t(aidl.sys.role)); + legacy.type = VALUE_OR_RETURN(aidl2legacy_AudioPortType_audio_port_type_t(aidl.sys.type)); + const bool isInput = + VALUE_OR_RETURN(direction(aidl.sys.role, aidl.sys.type)) == Direction::INPUT; + if (aidl.hal.sampleRate.has_value()) { + legacy.sample_rate = VALUE_OR_RETURN( + convertIntegral(aidl.hal.sampleRate.value().value)); + legacy.config_mask |= AUDIO_PORT_CONFIG_SAMPLE_RATE; + } + if (aidl.hal.channelMask.has_value()) { legacy.channel_mask = - VALUE_OR_RETURN(aidl2legacy_int32_t_audio_channel_mask_t(aidl.channelMask)); - } - if (bitmaskIsSet(aidl.configMask, media::AudioPortConfigType::FORMAT)) { - legacy.format = VALUE_OR_RETURN(aidl2legacy_AudioFormat_audio_format_t(aidl.format)); - } - if (bitmaskIsSet(aidl.configMask, media::AudioPortConfigType::GAIN)) { - legacy.gain = VALUE_OR_RETURN( - aidl2legacy_AudioGainConfig_audio_gain_config(aidl.gain, aidl.role, aidl.type)); - } - if (bitmaskIsSet(aidl.configMask, media::AudioPortConfigType::FLAGS)) { + VALUE_OR_RETURN( + aidl2legacy_AudioChannelLayout_audio_channel_mask_t( + aidl.hal.channelMask.value(), isInput)); + legacy.config_mask |= AUDIO_PORT_CONFIG_CHANNEL_MASK; + } + if (aidl.hal.format.has_value()) { + legacy.format = VALUE_OR_RETURN( + aidl2legacy_AudioFormatDescription_audio_format_t(aidl.hal.format.value())); + legacy.config_mask |= AUDIO_PORT_CONFIG_FORMAT; + } + if (aidl.hal.gain.has_value()) { + legacy.gain = VALUE_OR_RETURN(aidl2legacy_AudioGainConfig_audio_gain_config( + aidl.hal.gain.value(), isInput)); + legacy.config_mask |= AUDIO_PORT_CONFIG_GAIN; + } + if (aidl.hal.flags.has_value()) { legacy.flags = VALUE_OR_RETURN( - aidl2legacy_AudioIoFlags_audio_io_flags(aidl.flags, aidl.role, aidl.type)); + aidl2legacy_AudioIoFlags_audio_io_flags(aidl.hal.flags.value(), isInput)); + legacy.config_mask |= AUDIO_PORT_CONFIG_FLAGS; } - legacy.ext = VALUE_OR_RETURN(aidl2legacy_AudioPortConfigExt(aidl.ext, aidl.type, aidl.role)); + legacy.ext = VALUE_OR_RETURN( + aidl2legacy_AudioPortExt_audio_port_config_ext( + aidl.hal.ext, aidl.sys.type, aidl.sys.role, aidl.sys.ext)); return legacy; } ConversionResult legacy2aidl_audio_port_config_AudioPortConfig( const audio_port_config& legacy) { media::AudioPortConfig aidl; - aidl.id = VALUE_OR_RETURN(legacy2aidl_audio_port_handle_t_int32_t(legacy.id)); - aidl.role = VALUE_OR_RETURN(legacy2aidl_audio_port_role_t_AudioPortRole(legacy.role)); - aidl.type = VALUE_OR_RETURN(legacy2aidl_audio_port_type_t_AudioPortType(legacy.type)); - aidl.configMask = VALUE_OR_RETURN(legacy2aidl_config_mask_int32_t(legacy.config_mask)); + aidl.hal.id = VALUE_OR_RETURN(legacy2aidl_audio_port_handle_t_int32_t(legacy.id)); + aidl.sys.role = VALUE_OR_RETURN(legacy2aidl_audio_port_role_t_AudioPortRole(legacy.role)); + aidl.sys.type = VALUE_OR_RETURN(legacy2aidl_audio_port_type_t_AudioPortType(legacy.type)); + const bool isInput = VALUE_OR_RETURN( + direction(legacy.role, legacy.type)) == Direction::INPUT; if (legacy.config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { - aidl.sampleRate = VALUE_OR_RETURN(convertIntegral(legacy.sample_rate)); + Int aidl_sampleRate; + aidl_sampleRate.value = VALUE_OR_RETURN(convertIntegral(legacy.sample_rate)); + aidl.hal.sampleRate = aidl_sampleRate; } if (legacy.config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { - aidl.channelMask = - VALUE_OR_RETURN(legacy2aidl_audio_channel_mask_t_int32_t(legacy.channel_mask)); + aidl.hal.channelMask = VALUE_OR_RETURN( + legacy2aidl_audio_channel_mask_t_AudioChannelLayout(legacy.channel_mask, isInput)); } if (legacy.config_mask & AUDIO_PORT_CONFIG_FORMAT) { - aidl.format = VALUE_OR_RETURN(legacy2aidl_audio_format_t_AudioFormat(legacy.format)); + aidl.hal.format = VALUE_OR_RETURN( + legacy2aidl_audio_format_t_AudioFormatDescription(legacy.format)); } if (legacy.config_mask & AUDIO_PORT_CONFIG_GAIN) { - aidl.gain = VALUE_OR_RETURN(legacy2aidl_audio_gain_config_AudioGainConfig( - legacy.gain, legacy.role, legacy.type)); + aidl.hal.gain = VALUE_OR_RETURN( + legacy2aidl_audio_gain_config_AudioGainConfig(legacy.gain, isInput)); } if (legacy.config_mask & AUDIO_PORT_CONFIG_FLAGS) { - aidl.flags = VALUE_OR_RETURN( - legacy2aidl_audio_io_flags_AudioIoFlags(legacy.flags, legacy.role, legacy.type)); + aidl.hal.flags = VALUE_OR_RETURN( + legacy2aidl_audio_io_flags_AudioIoFlags(legacy.flags, isInput)); } - aidl.ext = - VALUE_OR_RETURN(legacy2aidl_AudioPortConfigExt(legacy.ext, legacy.type, legacy.role)); + RETURN_IF_ERROR(legacy2aidl_AudioPortExt(legacy.ext, legacy.type, legacy.role, + &aidl.hal.ext, &aidl.sys.ext)); return aidl; } @@ -1164,33 +2096,40 @@ ConversionResult legacy2aidl_audio_patch_AudioPatch( ConversionResult> aidl2legacy_AudioIoDescriptor_AudioIoDescriptor( const media::AudioIoDescriptor& aidl) { - sp legacy(new AudioIoDescriptor()); - legacy->mIoHandle = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_io_handle_t(aidl.ioHandle)); - legacy->mPatch = VALUE_OR_RETURN(aidl2legacy_AudioPatch_audio_patch(aidl.patch)); - legacy->mSamplingRate = VALUE_OR_RETURN(convertIntegral(aidl.samplingRate)); - legacy->mFormat = VALUE_OR_RETURN(aidl2legacy_AudioFormat_audio_format_t(aidl.format)); - legacy->mChannelMask = - VALUE_OR_RETURN(aidl2legacy_int32_t_audio_channel_mask_t(aidl.channelMask)); - legacy->mFrameCount = VALUE_OR_RETURN(convertIntegral(aidl.frameCount)); - legacy->mFrameCountHAL = VALUE_OR_RETURN(convertIntegral(aidl.frameCountHAL)); - legacy->mLatency = VALUE_OR_RETURN(convertIntegral(aidl.latency)); - legacy->mPortId = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_port_handle_t(aidl.portId)); - return legacy; + const audio_io_handle_t io_handle = VALUE_OR_RETURN( + aidl2legacy_int32_t_audio_io_handle_t(aidl.ioHandle)); + const struct audio_patch patch = VALUE_OR_RETURN( + aidl2legacy_AudioPatch_audio_patch(aidl.patch)); + const bool isInput = aidl.isInput; + const uint32_t sampling_rate = VALUE_OR_RETURN(convertIntegral(aidl.samplingRate)); + const audio_format_t format = VALUE_OR_RETURN( + aidl2legacy_AudioFormatDescription_audio_format_t(aidl.format)); + const audio_channel_mask_t channel_mask = VALUE_OR_RETURN( + aidl2legacy_AudioChannelLayout_audio_channel_mask_t(aidl.channelMask, isInput)); + const size_t frame_count = VALUE_OR_RETURN(convertIntegral(aidl.frameCount)); + const size_t frame_count_hal = VALUE_OR_RETURN(convertIntegral(aidl.frameCountHAL)); + const uint32_t latency = VALUE_OR_RETURN(convertIntegral(aidl.latency)); + const audio_port_handle_t port_id = VALUE_OR_RETURN( + aidl2legacy_int32_t_audio_port_handle_t(aidl.portId)); + return sp::make(io_handle, patch, isInput, sampling_rate, format, + channel_mask, frame_count, frame_count_hal, latency, port_id); } ConversionResult legacy2aidl_AudioIoDescriptor_AudioIoDescriptor( const sp& legacy) { media::AudioIoDescriptor aidl; - aidl.ioHandle = VALUE_OR_RETURN(legacy2aidl_audio_io_handle_t_int32_t(legacy->mIoHandle)); - aidl.patch = VALUE_OR_RETURN(legacy2aidl_audio_patch_AudioPatch(legacy->mPatch)); - aidl.samplingRate = VALUE_OR_RETURN(convertIntegral(legacy->mSamplingRate)); - aidl.format = VALUE_OR_RETURN(legacy2aidl_audio_format_t_AudioFormat(legacy->mFormat)); - aidl.channelMask = VALUE_OR_RETURN( - legacy2aidl_audio_channel_mask_t_int32_t(legacy->mChannelMask)); - aidl.frameCount = VALUE_OR_RETURN(convertIntegral(legacy->mFrameCount)); - aidl.frameCountHAL = VALUE_OR_RETURN(convertIntegral(legacy->mFrameCountHAL)); - aidl.latency = VALUE_OR_RETURN(convertIntegral(legacy->mLatency)); - aidl.portId = VALUE_OR_RETURN(legacy2aidl_audio_port_handle_t_int32_t(legacy->mPortId)); + aidl.ioHandle = VALUE_OR_RETURN(legacy2aidl_audio_io_handle_t_int32_t(legacy->getIoHandle())); + aidl.patch = VALUE_OR_RETURN(legacy2aidl_audio_patch_AudioPatch(legacy->getPatch())); + aidl.isInput = legacy->getIsInput(); + aidl.samplingRate = VALUE_OR_RETURN(convertIntegral(legacy->getSamplingRate())); + aidl.format = VALUE_OR_RETURN( + legacy2aidl_audio_format_t_AudioFormatDescription(legacy->getFormat())); + aidl.channelMask = VALUE_OR_RETURN(legacy2aidl_audio_channel_mask_t_AudioChannelLayout( + legacy->getChannelMask(), legacy->getIsInput())); + aidl.frameCount = VALUE_OR_RETURN(convertIntegral(legacy->getFrameCount())); + aidl.frameCountHAL = VALUE_OR_RETURN(convertIntegral(legacy->getFrameCountHAL())); + aidl.latency = VALUE_OR_RETURN(convertIntegral(legacy->getLatency())); + aidl.portId = VALUE_OR_RETURN(legacy2aidl_audio_port_handle_t_int32_t(legacy->getPortId())); return aidl; } @@ -1211,141 +2150,143 @@ ConversionResult legacy2aidl_AudioClient_AudioClient( } ConversionResult -aidl2legacy_AudioContentType_audio_content_type_t(media::AudioContentType aidl) { +aidl2legacy_AudioContentType_audio_content_type_t(AudioContentType aidl) { switch (aidl) { - case media::AudioContentType::UNKNOWN: + case AudioContentType::UNKNOWN: return AUDIO_CONTENT_TYPE_UNKNOWN; - case media::AudioContentType::SPEECH: + case AudioContentType::SPEECH: return AUDIO_CONTENT_TYPE_SPEECH; - case media::AudioContentType::MUSIC: + case AudioContentType::MUSIC: return AUDIO_CONTENT_TYPE_MUSIC; - case media::AudioContentType::MOVIE: + case AudioContentType::MOVIE: return AUDIO_CONTENT_TYPE_MOVIE; - case media::AudioContentType::SONIFICATION: + case AudioContentType::SONIFICATION: return AUDIO_CONTENT_TYPE_SONIFICATION; - case media::AudioContentType::ULTRASOUND: + case AudioContentType::ULTRASOUND: return AUDIO_CONTENT_TYPE_ULTRASOUND; } return unexpected(BAD_VALUE); } -ConversionResult +ConversionResult legacy2aidl_audio_content_type_t_AudioContentType(audio_content_type_t legacy) { switch (legacy) { case AUDIO_CONTENT_TYPE_UNKNOWN: - return media::AudioContentType::UNKNOWN; + return AudioContentType::UNKNOWN; case AUDIO_CONTENT_TYPE_SPEECH: - return media::AudioContentType::SPEECH; + return AudioContentType::SPEECH; case AUDIO_CONTENT_TYPE_MUSIC: - return media::AudioContentType::MUSIC; + return AudioContentType::MUSIC; case AUDIO_CONTENT_TYPE_MOVIE: - return media::AudioContentType::MOVIE; + return AudioContentType::MOVIE; case AUDIO_CONTENT_TYPE_SONIFICATION: - return media::AudioContentType::SONIFICATION; + return AudioContentType::SONIFICATION; case AUDIO_CONTENT_TYPE_ULTRASOUND: - return media::AudioContentType::ULTRASOUND; + return AudioContentType::ULTRASOUND; } return unexpected(BAD_VALUE); } ConversionResult -aidl2legacy_AudioUsage_audio_usage_t(media::AudioUsage aidl) { +aidl2legacy_AudioUsage_audio_usage_t(AudioUsage aidl) { switch (aidl) { - case media::AudioUsage::UNKNOWN: + case AudioUsage::INVALID: + break; // return error + case AudioUsage::UNKNOWN: return AUDIO_USAGE_UNKNOWN; - case media::AudioUsage::MEDIA: + case AudioUsage::MEDIA: return AUDIO_USAGE_MEDIA; - case media::AudioUsage::VOICE_COMMUNICATION: + case AudioUsage::VOICE_COMMUNICATION: return AUDIO_USAGE_VOICE_COMMUNICATION; - case media::AudioUsage::VOICE_COMMUNICATION_SIGNALLING: + case AudioUsage::VOICE_COMMUNICATION_SIGNALLING: return AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING; - case media::AudioUsage::ALARM: + case AudioUsage::ALARM: return AUDIO_USAGE_ALARM; - case media::AudioUsage::NOTIFICATION: + case AudioUsage::NOTIFICATION: return AUDIO_USAGE_NOTIFICATION; - case media::AudioUsage::NOTIFICATION_TELEPHONY_RINGTONE: + case AudioUsage::NOTIFICATION_TELEPHONY_RINGTONE: return AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE; - case media::AudioUsage::NOTIFICATION_COMMUNICATION_REQUEST: + case AudioUsage::SYS_RESERVED_NOTIFICATION_COMMUNICATION_REQUEST: return AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST; - case media::AudioUsage::NOTIFICATION_COMMUNICATION_INSTANT: + case AudioUsage::SYS_RESERVED_NOTIFICATION_COMMUNICATION_INSTANT: return AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT; - case media::AudioUsage::NOTIFICATION_COMMUNICATION_DELAYED: + case AudioUsage::SYS_RESERVED_NOTIFICATION_COMMUNICATION_DELAYED: return AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED; - case media::AudioUsage::NOTIFICATION_EVENT: + case AudioUsage::NOTIFICATION_EVENT: return AUDIO_USAGE_NOTIFICATION_EVENT; - case media::AudioUsage::ASSISTANCE_ACCESSIBILITY: + case AudioUsage::ASSISTANCE_ACCESSIBILITY: return AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY; - case media::AudioUsage::ASSISTANCE_NAVIGATION_GUIDANCE: + case AudioUsage::ASSISTANCE_NAVIGATION_GUIDANCE: return AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; - case media::AudioUsage::ASSISTANCE_SONIFICATION: + case AudioUsage::ASSISTANCE_SONIFICATION: return AUDIO_USAGE_ASSISTANCE_SONIFICATION; - case media::AudioUsage::GAME: + case AudioUsage::GAME: return AUDIO_USAGE_GAME; - case media::AudioUsage::VIRTUAL_SOURCE: + case AudioUsage::VIRTUAL_SOURCE: return AUDIO_USAGE_VIRTUAL_SOURCE; - case media::AudioUsage::ASSISTANT: + case AudioUsage::ASSISTANT: return AUDIO_USAGE_ASSISTANT; - case media::AudioUsage::CALL_ASSISTANT: + case AudioUsage::CALL_ASSISTANT: return AUDIO_USAGE_CALL_ASSISTANT; - case media::AudioUsage::EMERGENCY: + case AudioUsage::EMERGENCY: return AUDIO_USAGE_EMERGENCY; - case media::AudioUsage::SAFETY: + case AudioUsage::SAFETY: return AUDIO_USAGE_SAFETY; - case media::AudioUsage::VEHICLE_STATUS: + case AudioUsage::VEHICLE_STATUS: return AUDIO_USAGE_VEHICLE_STATUS; - case media::AudioUsage::ANNOUNCEMENT: + case AudioUsage::ANNOUNCEMENT: return AUDIO_USAGE_ANNOUNCEMENT; } return unexpected(BAD_VALUE); } -ConversionResult +ConversionResult legacy2aidl_audio_usage_t_AudioUsage(audio_usage_t legacy) { switch (legacy) { case AUDIO_USAGE_UNKNOWN: - return media::AudioUsage::UNKNOWN; + return AudioUsage::UNKNOWN; case AUDIO_USAGE_MEDIA: - return media::AudioUsage::MEDIA; + return AudioUsage::MEDIA; case AUDIO_USAGE_VOICE_COMMUNICATION: - return media::AudioUsage::VOICE_COMMUNICATION; + return AudioUsage::VOICE_COMMUNICATION; case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING: - return media::AudioUsage::VOICE_COMMUNICATION_SIGNALLING; + return AudioUsage::VOICE_COMMUNICATION_SIGNALLING; case AUDIO_USAGE_ALARM: - return media::AudioUsage::ALARM; + return AudioUsage::ALARM; case AUDIO_USAGE_NOTIFICATION: - return media::AudioUsage::NOTIFICATION; + return AudioUsage::NOTIFICATION; case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE: - return media::AudioUsage::NOTIFICATION_TELEPHONY_RINGTONE; + return AudioUsage::NOTIFICATION_TELEPHONY_RINGTONE; case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST: - return media::AudioUsage::NOTIFICATION_COMMUNICATION_REQUEST; + return AudioUsage::SYS_RESERVED_NOTIFICATION_COMMUNICATION_REQUEST; case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT: - return media::AudioUsage::NOTIFICATION_COMMUNICATION_INSTANT; + return AudioUsage::SYS_RESERVED_NOTIFICATION_COMMUNICATION_INSTANT; case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED: - return media::AudioUsage::NOTIFICATION_COMMUNICATION_DELAYED; + return AudioUsage::SYS_RESERVED_NOTIFICATION_COMMUNICATION_DELAYED; case AUDIO_USAGE_NOTIFICATION_EVENT: - return media::AudioUsage::NOTIFICATION_EVENT; + return AudioUsage::NOTIFICATION_EVENT; case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY: - return media::AudioUsage::ASSISTANCE_ACCESSIBILITY; + return AudioUsage::ASSISTANCE_ACCESSIBILITY; case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: - return media::AudioUsage::ASSISTANCE_NAVIGATION_GUIDANCE; + return AudioUsage::ASSISTANCE_NAVIGATION_GUIDANCE; case AUDIO_USAGE_ASSISTANCE_SONIFICATION: - return media::AudioUsage::ASSISTANCE_SONIFICATION; + return AudioUsage::ASSISTANCE_SONIFICATION; case AUDIO_USAGE_GAME: - return media::AudioUsage::GAME; + return AudioUsage::GAME; case AUDIO_USAGE_VIRTUAL_SOURCE: - return media::AudioUsage::VIRTUAL_SOURCE; + return AudioUsage::VIRTUAL_SOURCE; case AUDIO_USAGE_ASSISTANT: - return media::AudioUsage::ASSISTANT; + return AudioUsage::ASSISTANT; case AUDIO_USAGE_CALL_ASSISTANT: - return media::AudioUsage::CALL_ASSISTANT; + return AudioUsage::CALL_ASSISTANT; case AUDIO_USAGE_EMERGENCY: - return media::AudioUsage::EMERGENCY; + return AudioUsage::EMERGENCY; case AUDIO_USAGE_SAFETY: - return media::AudioUsage::SAFETY; + return AudioUsage::SAFETY; case AUDIO_USAGE_VEHICLE_STATUS: - return media::AudioUsage::VEHICLE_STATUS; + return AudioUsage::VEHICLE_STATUS; case AUDIO_USAGE_ANNOUNCEMENT: - return media::AudioUsage::ANNOUNCEMENT; + return AudioUsage::ANNOUNCEMENT; } return unexpected(BAD_VALUE); } @@ -1385,6 +2326,8 @@ aidl2legacy_AudioFlag_audio_flags_mask_t(media::AudioFlag aidl) { return AUDIO_FLAG_CONTENT_SPATIALIZED; case media::AudioFlag::NEVER_SPATIALIZE: return AUDIO_FLAG_NEVER_SPATIALIZE; + case media::AudioFlag::CALL_REDIRECTION: + return AUDIO_FLAG_CALL_REDIRECTION; } return unexpected(BAD_VALUE); } @@ -1426,6 +2369,8 @@ legacy2aidl_audio_flags_mask_t_AudioFlag(audio_flags_mask_t legacy) { return media::AudioFlag::CONTENT_SPATIALIZED; case AUDIO_FLAG_NEVER_SPATIALIZE: return media::AudioFlag::NEVER_SPATIALIZE; + case AUDIO_FLAG_CALL_REDIRECTION: + return media::AudioFlag::CALL_REDIRECTION; } return unexpected(BAD_VALUE); } @@ -1451,7 +2396,7 @@ aidl2legacy_AudioAttributesInternal_audio_attributes_t(const media::AudioAttribu legacy.content_type = VALUE_OR_RETURN( aidl2legacy_AudioContentType_audio_content_type_t(aidl.contentType)); legacy.usage = VALUE_OR_RETURN(aidl2legacy_AudioUsage_audio_usage_t(aidl.usage)); - legacy.source = VALUE_OR_RETURN(aidl2legacy_AudioSourceType_audio_source_t(aidl.source)); + legacy.source = VALUE_OR_RETURN(aidl2legacy_AudioSource_audio_source_t(aidl.source)); legacy.flags = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_flags_mask_t_mask(aidl.flags)); RETURN_IF_ERROR(aidl2legacy_string(aidl.tags, legacy.tags, sizeof(legacy.tags))); return legacy; @@ -1463,51 +2408,51 @@ legacy2aidl_audio_attributes_t_AudioAttributesInternal(const audio_attributes_t& aidl.contentType = VALUE_OR_RETURN( legacy2aidl_audio_content_type_t_AudioContentType(legacy.content_type)); aidl.usage = VALUE_OR_RETURN(legacy2aidl_audio_usage_t_AudioUsage(legacy.usage)); - aidl.source = VALUE_OR_RETURN(legacy2aidl_audio_source_t_AudioSourceType(legacy.source)); + aidl.source = VALUE_OR_RETURN(legacy2aidl_audio_source_t_AudioSource(legacy.source)); aidl.flags = VALUE_OR_RETURN(legacy2aidl_audio_flags_mask_t_int32_t_mask(legacy.flags)); aidl.tags = VALUE_OR_RETURN(legacy2aidl_string(legacy.tags, sizeof(legacy.tags))); return aidl; } ConversionResult -aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t(media::AudioEncapsulationMode aidl) { +aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t(AudioEncapsulationMode aidl) { switch (aidl) { - case media::AudioEncapsulationMode::NONE: + case AudioEncapsulationMode::INVALID: + break; // return error + case AudioEncapsulationMode::NONE: return AUDIO_ENCAPSULATION_MODE_NONE; - case media::AudioEncapsulationMode::ELEMENTARY_STREAM: + case AudioEncapsulationMode::ELEMENTARY_STREAM: return AUDIO_ENCAPSULATION_MODE_ELEMENTARY_STREAM; - case media::AudioEncapsulationMode::HANDLE: + case AudioEncapsulationMode::HANDLE: return AUDIO_ENCAPSULATION_MODE_HANDLE; } return unexpected(BAD_VALUE); } -ConversionResult +ConversionResult legacy2aidl_audio_encapsulation_mode_t_AudioEncapsulationMode(audio_encapsulation_mode_t legacy) { switch (legacy) { case AUDIO_ENCAPSULATION_MODE_NONE: - return media::AudioEncapsulationMode::NONE; + return AudioEncapsulationMode::NONE; case AUDIO_ENCAPSULATION_MODE_ELEMENTARY_STREAM: - return media::AudioEncapsulationMode::ELEMENTARY_STREAM; + return AudioEncapsulationMode::ELEMENTARY_STREAM; case AUDIO_ENCAPSULATION_MODE_HANDLE: - return media::AudioEncapsulationMode::HANDLE; + return AudioEncapsulationMode::HANDLE; } return unexpected(BAD_VALUE); } ConversionResult -aidl2legacy_AudioOffloadInfo_audio_offload_info_t(const media::AudioOffloadInfo& aidl) { - audio_offload_info_t legacy; - legacy.version = VALUE_OR_RETURN(convertIntegral(aidl.version)); - legacy.size = sizeof(audio_offload_info_t); - audio_config_base_t config = VALUE_OR_RETURN( - aidl2legacy_AudioConfigBase_audio_config_base_t(aidl.config)); - legacy.sample_rate = config.sample_rate; - legacy.channel_mask = config.channel_mask; - legacy.format = config.format; +aidl2legacy_AudioOffloadInfo_audio_offload_info_t(const AudioOffloadInfo& aidl) { + audio_offload_info_t legacy = AUDIO_INFO_INITIALIZER; + audio_config_base_t base = VALUE_OR_RETURN( + aidl2legacy_AudioConfigBase_audio_config_base_t(aidl.base, false /*isInput*/)); + legacy.sample_rate = base.sample_rate; + legacy.channel_mask = base.channel_mask; + legacy.format = base.format; legacy.stream_type = VALUE_OR_RETURN( aidl2legacy_AudioStreamType_audio_stream_type_t(aidl.streamType)); - legacy.bit_rate = VALUE_OR_RETURN(convertIntegral(aidl.bitRate)); + legacy.bit_rate = VALUE_OR_RETURN(convertIntegral(aidl.bitRatePerSecond)); legacy.duration_us = VALUE_OR_RETURN(convertIntegral(aidl.durationUs)); legacy.has_video = aidl.hasVideo; legacy.is_streaming = aidl.isStreaming; @@ -1521,21 +2466,20 @@ aidl2legacy_AudioOffloadInfo_audio_offload_info_t(const media::AudioOffloadInfo& return legacy; } -ConversionResult +ConversionResult legacy2aidl_audio_offload_info_t_AudioOffloadInfo(const audio_offload_info_t& legacy) { - media::AudioOffloadInfo aidl; + AudioOffloadInfo aidl; // Version 0.1 fields. if (legacy.size < offsetof(audio_offload_info_t, usage) + sizeof(audio_offload_info_t::usage)) { return unexpected(BAD_VALUE); } - aidl.version = VALUE_OR_RETURN(convertIntegral(legacy.version)); - aidl.config.sampleRate = VALUE_OR_RETURN(convertIntegral(legacy.sample_rate)); - aidl.config.channelMask = VALUE_OR_RETURN( - legacy2aidl_audio_channel_mask_t_int32_t(legacy.channel_mask)); - aidl.config.format = VALUE_OR_RETURN(legacy2aidl_audio_format_t_AudioFormat(legacy.format)); + const audio_config_base_t base = { .sample_rate = legacy.sample_rate, + .channel_mask = legacy.channel_mask, .format = legacy.format }; + aidl.base = VALUE_OR_RETURN(legacy2aidl_audio_config_base_t_AudioConfigBase( + base, false /*isInput*/)); aidl.streamType = VALUE_OR_RETURN( legacy2aidl_audio_stream_type_t_AudioStreamType(legacy.stream_type)); - aidl.bitRate = VALUE_OR_RETURN(convertIntegral(legacy.bit_rate)); + aidl.bitRatePerSecond = VALUE_OR_RETURN(convertIntegral(legacy.bit_rate)); aidl.durationUs = VALUE_OR_RETURN(convertIntegral(legacy.duration_us)); aidl.hasVideo = legacy.has_video; aidl.isStreaming = legacy.is_streaming; @@ -1559,25 +2503,25 @@ legacy2aidl_audio_offload_info_t_AudioOffloadInfo(const audio_offload_info_t& le } ConversionResult -aidl2legacy_AudioConfig_audio_config_t(const media::AudioConfig& aidl) { - audio_config_t legacy; - legacy.sample_rate = VALUE_OR_RETURN(convertIntegral(aidl.sampleRate)); - legacy.channel_mask = VALUE_OR_RETURN( - aidl2legacy_int32_t_audio_channel_mask_t(aidl.channelMask)); - legacy.format = VALUE_OR_RETURN(aidl2legacy_AudioFormat_audio_format_t(aidl.format)); +aidl2legacy_AudioConfig_audio_config_t(const AudioConfig& aidl, bool isInput) { + const audio_config_base_t legacyBase = VALUE_OR_RETURN( + aidl2legacy_AudioConfigBase_audio_config_base_t(aidl.base, isInput)); + audio_config_t legacy = AUDIO_CONFIG_INITIALIZER; + legacy.sample_rate = legacyBase.sample_rate; + legacy.channel_mask = legacyBase.channel_mask; + legacy.format = legacyBase.format; legacy.offload_info = VALUE_OR_RETURN( aidl2legacy_AudioOffloadInfo_audio_offload_info_t(aidl.offloadInfo)); legacy.frame_count = VALUE_OR_RETURN(convertIntegral(aidl.frameCount)); return legacy; } -ConversionResult -legacy2aidl_audio_config_t_AudioConfig(const audio_config_t& legacy) { - media::AudioConfig aidl; - aidl.sampleRate = VALUE_OR_RETURN(convertIntegral(legacy.sample_rate)); - aidl.channelMask = VALUE_OR_RETURN( - legacy2aidl_audio_channel_mask_t_int32_t(legacy.channel_mask)); - aidl.format = VALUE_OR_RETURN(legacy2aidl_audio_format_t_AudioFormat(legacy.format)); +ConversionResult +legacy2aidl_audio_config_t_AudioConfig(const audio_config_t& legacy, bool isInput) { + const audio_config_base_t base = { .sample_rate = legacy.sample_rate, + .channel_mask = legacy.channel_mask, .format = legacy.format }; + AudioConfig aidl; + aidl.base = VALUE_OR_RETURN(legacy2aidl_audio_config_base_t_AudioConfigBase(base, isInput)); aidl.offloadInfo = VALUE_OR_RETURN( legacy2aidl_audio_offload_info_t_AudioOffloadInfo(legacy.offload_info)); aidl.frameCount = VALUE_OR_RETURN(convertIntegral(legacy.frame_count)); @@ -1585,22 +2529,22 @@ legacy2aidl_audio_config_t_AudioConfig(const audio_config_t& legacy) { } ConversionResult -aidl2legacy_AudioConfigBase_audio_config_base_t(const media::AudioConfigBase& aidl) { +aidl2legacy_AudioConfigBase_audio_config_base_t(const AudioConfigBase& aidl, bool isInput) { audio_config_base_t legacy; legacy.sample_rate = VALUE_OR_RETURN(convertIntegral(aidl.sampleRate)); legacy.channel_mask = VALUE_OR_RETURN( - aidl2legacy_int32_t_audio_channel_mask_t(aidl.channelMask)); - legacy.format = VALUE_OR_RETURN(aidl2legacy_AudioFormat_audio_format_t(aidl.format)); + aidl2legacy_AudioChannelLayout_audio_channel_mask_t(aidl.channelMask, isInput)); + legacy.format = VALUE_OR_RETURN(aidl2legacy_AudioFormatDescription_audio_format_t(aidl.format)); return legacy; } -ConversionResult -legacy2aidl_audio_config_base_t_AudioConfigBase(const audio_config_base_t& legacy) { - media::AudioConfigBase aidl; +ConversionResult +legacy2aidl_audio_config_base_t_AudioConfigBase(const audio_config_base_t& legacy, bool isInput) { + AudioConfigBase aidl; aidl.sampleRate = VALUE_OR_RETURN(convertIntegral(legacy.sample_rate)); aidl.channelMask = VALUE_OR_RETURN( - legacy2aidl_audio_channel_mask_t_int32_t(legacy.channel_mask)); - aidl.format = VALUE_OR_RETURN(legacy2aidl_audio_format_t_AudioFormat(legacy.format)); + legacy2aidl_audio_channel_mask_t_AudioChannelLayout(legacy.channel_mask, isInput)); + aidl.format = VALUE_OR_RETURN(legacy2aidl_audio_format_t_AudioFormatDescription(legacy.format)); return aidl; } @@ -1659,7 +2603,7 @@ legacy2aidl_AudioTimestamp_AudioTimestampInternal(const AudioTimestamp& legacy) } ConversionResult -aidl2legacy_AudioUuid_audio_uuid_t(const media::AudioUuid& aidl) { +aidl2legacy_AudioUuid_audio_uuid_t(const AudioUuid& aidl) { audio_uuid_t legacy; legacy.timeLow = VALUE_OR_RETURN(convertReinterpret(aidl.timeLow)); legacy.timeMid = VALUE_OR_RETURN(convertIntegral(aidl.timeMid)); @@ -1672,9 +2616,9 @@ aidl2legacy_AudioUuid_audio_uuid_t(const media::AudioUuid& aidl) { return legacy; } -ConversionResult +ConversionResult legacy2aidl_audio_uuid_t_AudioUuid(const audio_uuid_t& legacy) { - media::AudioUuid aidl; + AudioUuid aidl; aidl.timeLow = VALUE_OR_RETURN(convertReinterpret(legacy.timeLow)); aidl.timeMid = VALUE_OR_RETURN(convertIntegral(legacy.timeMid)); aidl.timeHiAndVersion = VALUE_OR_RETURN(convertIntegral(legacy.timeHiAndVersion)); @@ -1715,28 +2659,28 @@ legacy2aidl_effect_descriptor_t_EffectDescriptor(const effect_descriptor_t& lega ConversionResult aidl2legacy_AudioEncapsulationMetadataType_audio_encapsulation_metadata_type_t( - media::AudioEncapsulationMetadataType aidl) { + AudioEncapsulationMetadataType aidl) { switch (aidl) { - case media::AudioEncapsulationMetadataType::NONE: + case AudioEncapsulationMetadataType::NONE: return AUDIO_ENCAPSULATION_METADATA_TYPE_NONE; - case media::AudioEncapsulationMetadataType::FRAMEWORK_TUNER: + case AudioEncapsulationMetadataType::FRAMEWORK_TUNER: return AUDIO_ENCAPSULATION_METADATA_TYPE_FRAMEWORK_TUNER; - case media::AudioEncapsulationMetadataType::DVB_AD_DESCRIPTOR: + case AudioEncapsulationMetadataType::DVB_AD_DESCRIPTOR: return AUDIO_ENCAPSULATION_METADATA_TYPE_DVB_AD_DESCRIPTOR; } return unexpected(BAD_VALUE); } -ConversionResult +ConversionResult legacy2aidl_audio_encapsulation_metadata_type_t_AudioEncapsulationMetadataType( audio_encapsulation_metadata_type_t legacy) { switch (legacy) { case AUDIO_ENCAPSULATION_METADATA_TYPE_NONE: - return media::AudioEncapsulationMetadataType::NONE; + return AudioEncapsulationMetadataType::NONE; case AUDIO_ENCAPSULATION_METADATA_TYPE_FRAMEWORK_TUNER: - return media::AudioEncapsulationMetadataType::FRAMEWORK_TUNER; + return AudioEncapsulationMetadataType::FRAMEWORK_TUNER; case AUDIO_ENCAPSULATION_METADATA_TYPE_DVB_AD_DESCRIPTOR: - return media::AudioEncapsulationMetadataType::DVB_AD_DESCRIPTOR; + return AudioEncapsulationMetadataType::DVB_AD_DESCRIPTOR; } return unexpected(BAD_VALUE); } @@ -1746,9 +2690,9 @@ aidl2legacy_AudioEncapsulationMode_mask(int32_t aidl) { return convertBitmask( + AudioEncapsulationMode>( aidl, aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t, - indexToEnum_index, + indexToEnum_index, enumToMask_index); } @@ -1756,11 +2700,11 @@ ConversionResult legacy2aidl_AudioEncapsulationMode_mask(uint32_t legacy) { return convertBitmask( legacy, legacy2aidl_audio_encapsulation_mode_t_AudioEncapsulationMode, indexToEnum_index, - enumToMask_index); + enumToMask_index); } ConversionResult @@ -1768,9 +2712,9 @@ aidl2legacy_AudioEncapsulationMetadataType_mask(int32_t aidl) { return convertBitmask( + AudioEncapsulationMetadataType>( aidl, aidl2legacy_AudioEncapsulationMetadataType_audio_encapsulation_metadata_type_t, - indexToEnum_index, + indexToEnum_index, enumToMask_index); } @@ -1778,104 +2722,79 @@ ConversionResult legacy2aidl_AudioEncapsulationMetadataType_mask(uint32_t legacy) { return convertBitmask( legacy, legacy2aidl_audio_encapsulation_metadata_type_t_AudioEncapsulationMetadataType, indexToEnum_index, - enumToMask_index); -} - -ConversionResult -aidl2legacy_AudioMixLatencyClass_audio_mix_latency_class_t( - media::AudioMixLatencyClass aidl) { - switch (aidl) { - case media::AudioMixLatencyClass::LOW: - return AUDIO_LATENCY_LOW; - case media::AudioMixLatencyClass::NORMAL: - return AUDIO_LATENCY_NORMAL; - } - return unexpected(BAD_VALUE); -} - -ConversionResult -legacy2aidl_audio_mix_latency_class_t_AudioMixLatencyClass( - audio_mix_latency_class_t legacy) { - switch (legacy) { - case AUDIO_LATENCY_LOW: - return media::AudioMixLatencyClass::LOW; - case AUDIO_LATENCY_NORMAL: - return media::AudioMixLatencyClass::NORMAL; - } - return unexpected(BAD_VALUE); + enumToMask_index); } ConversionResult -aidl2legacy_AudioPortDeviceExt_audio_port_device_ext(const media::AudioPortDeviceExt& aidl) { +aidl2legacy_AudioPortDeviceExt_audio_port_device_ext( + const AudioPortDeviceExt& aidl, const media::AudioPortDeviceExtSys& aidlSys) { audio_port_device_ext legacy; - legacy.hw_module = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_module_handle_t(aidl.hwModule)); - legacy.type = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_devices_t(aidl.device.type)); - RETURN_IF_ERROR( - aidl2legacy_string(aidl.device.address, legacy.address, sizeof(legacy.address))); + legacy.hw_module = VALUE_OR_RETURN( + aidl2legacy_int32_t_audio_module_handle_t(aidlSys.hwModule)); + RETURN_IF_ERROR(aidl2legacy_AudioDevice_audio_device( + aidl.device, &legacy.type, legacy.address)); legacy.encapsulation_modes = VALUE_OR_RETURN( - aidl2legacy_AudioEncapsulationMode_mask(aidl.encapsulationModes)); + aidl2legacy_AudioEncapsulationMode_mask(aidlSys.encapsulationModes)); legacy.encapsulation_metadata_types = VALUE_OR_RETURN( - aidl2legacy_AudioEncapsulationMetadataType_mask(aidl.encapsulationMetadataTypes)); + aidl2legacy_AudioEncapsulationMetadataType_mask( + aidlSys.encapsulationMetadataTypes)); return legacy; } -ConversionResult -legacy2aidl_audio_port_device_ext_AudioPortDeviceExt(const audio_port_device_ext& legacy) { - media::AudioPortDeviceExt aidl; - aidl.hwModule = VALUE_OR_RETURN(legacy2aidl_audio_module_handle_t_int32_t(legacy.hw_module)); - aidl.device.type = VALUE_OR_RETURN(legacy2aidl_audio_devices_t_int32_t(legacy.type)); - aidl.device.address = VALUE_OR_RETURN( - legacy2aidl_string(legacy.address, sizeof(legacy.address))); - aidl.encapsulationModes = VALUE_OR_RETURN( +status_t legacy2aidl_audio_port_device_ext_AudioPortDeviceExt( + const audio_port_device_ext& legacy, + AudioPortDeviceExt* aidl, media::AudioPortDeviceExtSys* aidlDeviceExt) { + aidlDeviceExt->hwModule = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_module_handle_t_int32_t(legacy.hw_module)); + aidl->device = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_device_AudioDevice(legacy.type, legacy.address)); + aidlDeviceExt->encapsulationModes = VALUE_OR_RETURN_STATUS( legacy2aidl_AudioEncapsulationMode_mask(legacy.encapsulation_modes)); - aidl.encapsulationMetadataTypes = VALUE_OR_RETURN( + aidlDeviceExt->encapsulationMetadataTypes = VALUE_OR_RETURN_STATUS( legacy2aidl_AudioEncapsulationMetadataType_mask(legacy.encapsulation_metadata_types)); - return aidl; + return OK; } ConversionResult -aidl2legacy_AudioPortMixExt_audio_port_mix_ext(const media::AudioPortMixExt& aidl) { - audio_port_mix_ext legacy; - legacy.hw_module = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_module_handle_t(aidl.hwModule)); +aidl2legacy_AudioPortMixExt_audio_port_mix_ext( + const AudioPortMixExt& aidl, const media::AudioPortMixExtSys& aidlSys) { + audio_port_mix_ext legacy{}; + legacy.hw_module = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_module_handle_t(aidlSys.hwModule)); legacy.handle = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_io_handle_t(aidl.handle)); - legacy.latency_class = VALUE_OR_RETURN( - aidl2legacy_AudioMixLatencyClass_audio_mix_latency_class_t(aidl.latencyClass)); return legacy; } -ConversionResult -legacy2aidl_audio_port_mix_ext_AudioPortMixExt(const audio_port_mix_ext& legacy) { - media::AudioPortMixExt aidl; - aidl.hwModule = VALUE_OR_RETURN(legacy2aidl_audio_module_handle_t_int32_t(legacy.hw_module)); - aidl.handle = VALUE_OR_RETURN(legacy2aidl_audio_io_handle_t_int32_t(legacy.handle)); - aidl.latencyClass = VALUE_OR_RETURN( - legacy2aidl_audio_mix_latency_class_t_AudioMixLatencyClass(legacy.latency_class)); - return aidl; +status_t +legacy2aidl_audio_port_mix_ext_AudioPortMixExt(const audio_port_mix_ext& legacy, + AudioPortMixExt* aidl, media::AudioPortMixExtSys* aidlMixExt) { + aidlMixExt->hwModule = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_module_handle_t_int32_t(legacy.hw_module)); + aidl->handle = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(legacy.handle)); + return OK; } ConversionResult -aidl2legacy_AudioPortSessionExt_audio_port_session_ext(const media::AudioPortSessionExt& aidl) { +aidl2legacy_int32_t_audio_port_session_ext(int32_t aidl) { audio_port_session_ext legacy; - legacy.session = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_session_t(aidl.session)); + legacy.session = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_session_t(aidl)); return legacy; } -ConversionResult -legacy2aidl_audio_port_session_ext_AudioPortSessionExt(const audio_port_session_ext& legacy) { - media::AudioPortSessionExt aidl; - aidl.session = VALUE_OR_RETURN(legacy2aidl_audio_session_t_int32_t(legacy.session)); - return aidl; +ConversionResult +legacy2aidl_audio_port_session_ext_int32_t(const audio_port_session_ext& legacy) { + return legacy2aidl_audio_session_t_int32_t(legacy.session); } // This type is unnamed in the original definition, thus we name it here. using audio_port_v7_ext = decltype(audio_port_v7::ext); -ConversionResult aidl2legacy_AudioPortExt( - const media::AudioPortExt& aidl, media::AudioPortType type) { +ConversionResult aidl2legacy_AudioPortExt_audio_port_v7_ext( + const AudioPortExt& aidl, media::AudioPortType type, + const media::AudioPortExtSys& aidlSys) { audio_port_v7_ext legacy; switch (type) { case media::AudioPortType::NONE: @@ -1885,66 +2804,83 @@ ConversionResult aidl2legacy_AudioPortExt( case media::AudioPortType::DEVICE: legacy.device = VALUE_OR_RETURN( aidl2legacy_AudioPortDeviceExt_audio_port_device_ext( - VALUE_OR_RETURN(UNION_GET(aidl, device)))); + VALUE_OR_RETURN(UNION_GET(aidl, device)), + VALUE_OR_RETURN(UNION_GET(aidlSys, device)))); return legacy; case media::AudioPortType::MIX: legacy.mix = VALUE_OR_RETURN( aidl2legacy_AudioPortMixExt_audio_port_mix_ext( - VALUE_OR_RETURN(UNION_GET(aidl, mix)))); + VALUE_OR_RETURN(UNION_GET(aidl, mix)), + VALUE_OR_RETURN(UNION_GET(aidlSys, mix)))); return legacy; case media::AudioPortType::SESSION: - legacy.session = VALUE_OR_RETURN(aidl2legacy_AudioPortSessionExt_audio_port_session_ext( - VALUE_OR_RETURN(UNION_GET(aidl, session)))); + legacy.session = VALUE_OR_RETURN( + aidl2legacy_int32_t_audio_port_session_ext( + VALUE_OR_RETURN(UNION_GET(aidl, session)))); return legacy; } LOG_ALWAYS_FATAL("Shouldn't get here"); // with -Werror,-Wswitch may compile-time fail } -ConversionResult legacy2aidl_AudioPortExt( - const audio_port_v7_ext& legacy, audio_port_type_t type) { - media::AudioPortExt aidl; +status_t legacy2aidl_AudioPortExt( + const audio_port_v7_ext& legacy, audio_port_type_t type, + AudioPortExt* aidl, media::AudioPortExtSys* aidlSys) { switch (type) { case AUDIO_PORT_TYPE_NONE: - UNION_SET(aidl, unspecified, false); - return aidl; - case AUDIO_PORT_TYPE_DEVICE: - UNION_SET(aidl, device, - VALUE_OR_RETURN( - legacy2aidl_audio_port_device_ext_AudioPortDeviceExt(legacy.device))); - return aidl; - case AUDIO_PORT_TYPE_MIX: - UNION_SET(aidl, mix, - VALUE_OR_RETURN(legacy2aidl_audio_port_mix_ext_AudioPortMixExt(legacy.mix))); - return aidl; + UNION_SET(*aidl, unspecified, false); + UNION_SET(*aidlSys, unspecified, false); + return OK; + case AUDIO_PORT_TYPE_DEVICE: { + AudioPortDeviceExt device; + media::AudioPortDeviceExtSys deviceSys; + RETURN_STATUS_IF_ERROR( + legacy2aidl_audio_port_device_ext_AudioPortDeviceExt( + legacy.device, &device, &deviceSys)); + UNION_SET(*aidl, device, device); + UNION_SET(*aidlSys, device, deviceSys); + return OK; + } + case AUDIO_PORT_TYPE_MIX: { + AudioPortMixExt mix; + media::AudioPortMixExtSys mixSys; + RETURN_STATUS_IF_ERROR( + legacy2aidl_audio_port_mix_ext_AudioPortMixExt( + legacy.mix, &mix, &mixSys)); + UNION_SET(*aidl, mix, mix); + UNION_SET(*aidlSys, mix, mixSys); + return OK; + } case AUDIO_PORT_TYPE_SESSION: - UNION_SET(aidl, session, - VALUE_OR_RETURN(legacy2aidl_audio_port_session_ext_AudioPortSessionExt( - legacy.session))); - return aidl; + UNION_SET(*aidl, session, VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_port_session_ext_int32_t(legacy.session))); + UNION_SET(*aidlSys, unspecified, false); + return OK; } LOG_ALWAYS_FATAL("Shouldn't get here"); // with -Werror,-Wswitch may compile-time fail } ConversionResult -aidl2legacy_AudioProfile_audio_profile(const media::AudioProfile& aidl) { +aidl2legacy_AudioProfile_audio_profile(const AudioProfile& aidl, bool isInput) { audio_profile legacy; - legacy.format = VALUE_OR_RETURN(aidl2legacy_AudioFormat_audio_format_t(aidl.format)); + legacy.format = VALUE_OR_RETURN(aidl2legacy_AudioFormatDescription_audio_format_t(aidl.format)); - if (aidl.samplingRates.size() > std::size(legacy.sample_rates)) { + if (aidl.sampleRates.size() > std::size(legacy.sample_rates)) { return unexpected(BAD_VALUE); } RETURN_IF_ERROR( - convertRange(aidl.samplingRates.begin(), aidl.samplingRates.end(), legacy.sample_rates, + convertRange(aidl.sampleRates.begin(), aidl.sampleRates.end(), legacy.sample_rates, convertIntegral)); - legacy.num_sample_rates = aidl.samplingRates.size(); + legacy.num_sample_rates = aidl.sampleRates.size(); if (aidl.channelMasks.size() > std::size(legacy.channel_masks)) { return unexpected(BAD_VALUE); } RETURN_IF_ERROR( convertRange(aidl.channelMasks.begin(), aidl.channelMasks.end(), legacy.channel_masks, - aidl2legacy_int32_t_audio_channel_mask_t)); + [isInput](const AudioChannelLayout& l) { + return aidl2legacy_AudioChannelLayout_audio_channel_mask_t(l, isInput); + })); legacy.num_channel_masks = aidl.channelMasks.size(); legacy.encapsulation_type = VALUE_OR_RETURN( @@ -1952,17 +2888,17 @@ aidl2legacy_AudioProfile_audio_profile(const media::AudioProfile& aidl) { return legacy; } -ConversionResult -legacy2aidl_audio_profile_AudioProfile(const audio_profile& legacy) { - media::AudioProfile aidl; - aidl.format = VALUE_OR_RETURN(legacy2aidl_audio_format_t_AudioFormat(legacy.format)); +ConversionResult +legacy2aidl_audio_profile_AudioProfile(const audio_profile& legacy, bool isInput) { + AudioProfile aidl; + aidl.format = VALUE_OR_RETURN(legacy2aidl_audio_format_t_AudioFormatDescription(legacy.format)); if (legacy.num_sample_rates > std::size(legacy.sample_rates)) { return unexpected(BAD_VALUE); } RETURN_IF_ERROR( convertRange(legacy.sample_rates, legacy.sample_rates + legacy.num_sample_rates, - std::back_inserter(aidl.samplingRates), + std::back_inserter(aidl.sampleRates), convertIntegral)); if (legacy.num_channel_masks > std::size(legacy.channel_masks)) { @@ -1971,7 +2907,9 @@ legacy2aidl_audio_profile_AudioProfile(const audio_profile& legacy) { RETURN_IF_ERROR( convertRange(legacy.channel_masks, legacy.channel_masks + legacy.num_channel_masks, std::back_inserter(aidl.channelMasks), - legacy2aidl_audio_channel_mask_t_int32_t)); + [isInput](audio_channel_mask_t m) { + return legacy2aidl_audio_channel_mask_t_AudioChannelLayout(m, isInput); + })); aidl.encapsulationType = VALUE_OR_RETURN( legacy2aidl_audio_encapsulation_type_t_AudioEncapsulationType( @@ -1980,11 +2918,11 @@ legacy2aidl_audio_profile_AudioProfile(const audio_profile& legacy) { } ConversionResult -aidl2legacy_AudioGain_audio_gain(const media::AudioGain& aidl) { +aidl2legacy_AudioGain_audio_gain(const AudioGain& aidl, bool isInput) { audio_gain legacy; legacy.mode = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_gain_mode_t_mask(aidl.mode)); - legacy.channel_mask = VALUE_OR_RETURN( - aidl2legacy_int32_t_audio_channel_mask_t(aidl.channelMask)); + legacy.channel_mask = VALUE_OR_RETURN(aidl2legacy_AudioChannelLayout_audio_channel_mask_t( + aidl.channelMask, isInput)); legacy.min_value = VALUE_OR_RETURN(convertIntegral(aidl.minValue)); legacy.max_value = VALUE_OR_RETURN(convertIntegral(aidl.maxValue)); legacy.default_value = VALUE_OR_RETURN(convertIntegral(aidl.defaultValue)); @@ -1994,12 +2932,12 @@ aidl2legacy_AudioGain_audio_gain(const media::AudioGain& aidl) { return legacy; } -ConversionResult -legacy2aidl_audio_gain_AudioGain(const audio_gain& legacy) { - media::AudioGain aidl; +ConversionResult +legacy2aidl_audio_gain_AudioGain(const audio_gain& legacy, bool isInput) { + AudioGain aidl; aidl.mode = VALUE_OR_RETURN(legacy2aidl_audio_gain_mode_t_int32_t_mask(legacy.mode)); aidl.channelMask = VALUE_OR_RETURN( - legacy2aidl_audio_channel_mask_t_int32_t(legacy.channel_mask)); + legacy2aidl_audio_channel_mask_t_AudioChannelLayout(legacy.channel_mask, isInput)); aidl.minValue = VALUE_OR_RETURN(convertIntegral(legacy.min_value)); aidl.maxValue = VALUE_OR_RETURN(convertIntegral(legacy.max_value)); aidl.defaultValue = VALUE_OR_RETURN(convertIntegral(legacy.default_value)); @@ -2012,63 +2950,76 @@ legacy2aidl_audio_gain_AudioGain(const audio_gain& legacy) { ConversionResult aidl2legacy_AudioPort_audio_port_v7(const media::AudioPort& aidl) { audio_port_v7 legacy; - legacy.id = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_port_handle_t(aidl.id)); - legacy.role = VALUE_OR_RETURN(aidl2legacy_AudioPortRole_audio_port_role_t(aidl.role)); - legacy.type = VALUE_OR_RETURN(aidl2legacy_AudioPortType_audio_port_type_t(aidl.type)); - RETURN_IF_ERROR(aidl2legacy_string(aidl.name, legacy.name, sizeof(legacy.name))); + legacy.id = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_port_handle_t(aidl.hal.id)); + legacy.role = VALUE_OR_RETURN(aidl2legacy_AudioPortRole_audio_port_role_t(aidl.sys.role)); + legacy.type = VALUE_OR_RETURN(aidl2legacy_AudioPortType_audio_port_type_t(aidl.sys.type)); + RETURN_IF_ERROR(aidl2legacy_string(aidl.hal.name, legacy.name, sizeof(legacy.name))); - if (aidl.profiles.size() > std::size(legacy.audio_profiles)) { + if (aidl.hal.profiles.size() > std::size(legacy.audio_profiles)) { return unexpected(BAD_VALUE); } - RETURN_IF_ERROR(convertRange(aidl.profiles.begin(), aidl.profiles.end(), legacy.audio_profiles, - aidl2legacy_AudioProfile_audio_profile)); - legacy.num_audio_profiles = aidl.profiles.size(); + const bool isInput = + VALUE_OR_RETURN(direction(aidl.sys.role, aidl.sys.type)) == Direction::INPUT; + RETURN_IF_ERROR(convertRange( + aidl.hal.profiles.begin(), aidl.hal.profiles.end(), legacy.audio_profiles, + [isInput](const AudioProfile& p) { + return aidl2legacy_AudioProfile_audio_profile(p, isInput); + })); + legacy.num_audio_profiles = aidl.hal.profiles.size(); - if (aidl.extraAudioDescriptors.size() > std::size(legacy.extra_audio_descriptors)) { + if (aidl.hal.extraAudioDescriptors.size() > std::size(legacy.extra_audio_descriptors)) { return unexpected(BAD_VALUE); } RETURN_IF_ERROR( - convertRange(aidl.extraAudioDescriptors.begin(), aidl.extraAudioDescriptors.end(), - legacy.extra_audio_descriptors, - aidl2legacy_ExtraAudioDescriptor_audio_extra_audio_descriptor)); - legacy.num_extra_audio_descriptors = aidl.extraAudioDescriptors.size(); + convertRange( + aidl.hal.extraAudioDescriptors.begin(), aidl.hal.extraAudioDescriptors.end(), + legacy.extra_audio_descriptors, + aidl2legacy_ExtraAudioDescriptor_audio_extra_audio_descriptor)); + legacy.num_extra_audio_descriptors = aidl.hal.extraAudioDescriptors.size(); - if (aidl.gains.size() > std::size(legacy.gains)) { + if (aidl.hal.gains.size() > std::size(legacy.gains)) { return unexpected(BAD_VALUE); } - RETURN_IF_ERROR(convertRange(aidl.gains.begin(), aidl.gains.end(), legacy.gains, - aidl2legacy_AudioGain_audio_gain)); - legacy.num_gains = aidl.gains.size(); + RETURN_IF_ERROR(convertRange(aidl.hal.gains.begin(), aidl.hal.gains.end(), legacy.gains, + [isInput](const AudioGain& g) { + return aidl2legacy_AudioGain_audio_gain(g, isInput); + })); + legacy.num_gains = aidl.hal.gains.size(); legacy.active_config = VALUE_OR_RETURN( - aidl2legacy_AudioPortConfig_audio_port_config(aidl.activeConfig)); - legacy.ext = VALUE_OR_RETURN(aidl2legacy_AudioPortExt(aidl.ext, aidl.type)); + aidl2legacy_AudioPortConfig_audio_port_config(aidl.sys.activeConfig)); + legacy.ext = VALUE_OR_RETURN( + aidl2legacy_AudioPortExt_audio_port_v7_ext(aidl.hal.ext, aidl.sys.type, aidl.sys.ext)); return legacy; } ConversionResult legacy2aidl_audio_port_v7_AudioPort(const audio_port_v7& legacy) { media::AudioPort aidl; - aidl.id = VALUE_OR_RETURN(legacy2aidl_audio_port_handle_t_int32_t(legacy.id)); - aidl.role = VALUE_OR_RETURN(legacy2aidl_audio_port_role_t_AudioPortRole(legacy.role)); - aidl.type = VALUE_OR_RETURN(legacy2aidl_audio_port_type_t_AudioPortType(legacy.type)); - aidl.name = VALUE_OR_RETURN(legacy2aidl_string(legacy.name, sizeof(legacy.name))); + aidl.hal.id = VALUE_OR_RETURN(legacy2aidl_audio_port_handle_t_int32_t(legacy.id)); + aidl.sys.role = VALUE_OR_RETURN(legacy2aidl_audio_port_role_t_AudioPortRole(legacy.role)); + aidl.sys.type = VALUE_OR_RETURN(legacy2aidl_audio_port_type_t_AudioPortType(legacy.type)); + aidl.hal.name = VALUE_OR_RETURN(legacy2aidl_string(legacy.name, sizeof(legacy.name))); if (legacy.num_audio_profiles > std::size(legacy.audio_profiles)) { return unexpected(BAD_VALUE); } + const bool isInput = VALUE_OR_RETURN(direction(legacy.role, legacy.type)) == Direction::INPUT; RETURN_IF_ERROR( convertRange(legacy.audio_profiles, legacy.audio_profiles + legacy.num_audio_profiles, - std::back_inserter(aidl.profiles), - legacy2aidl_audio_profile_AudioProfile)); + std::back_inserter(aidl.hal.profiles), + [isInput](const audio_profile& p) { + return legacy2aidl_audio_profile_AudioProfile(p, isInput); + })); if (legacy.num_extra_audio_descriptors > std::size(legacy.extra_audio_descriptors)) { return unexpected(BAD_VALUE); } + aidl.sys.profiles.resize(legacy.num_audio_profiles); RETURN_IF_ERROR( convertRange(legacy.extra_audio_descriptors, legacy.extra_audio_descriptors + legacy.num_extra_audio_descriptors, - std::back_inserter(aidl.extraAudioDescriptors), + std::back_inserter(aidl.hal.extraAudioDescriptors), legacy2aidl_audio_extra_audio_descriptor_ExtraAudioDescriptor)); if (legacy.num_gains > std::size(legacy.gains)) { @@ -2076,53 +3027,66 @@ legacy2aidl_audio_port_v7_AudioPort(const audio_port_v7& legacy) { } RETURN_IF_ERROR( convertRange(legacy.gains, legacy.gains + legacy.num_gains, - std::back_inserter(aidl.gains), - legacy2aidl_audio_gain_AudioGain)); + std::back_inserter(aidl.hal.gains), + [isInput](const audio_gain& g) { + return legacy2aidl_audio_gain_AudioGain(g, isInput); + })); + aidl.sys.gains.resize(legacy.num_gains); - aidl.activeConfig = VALUE_OR_RETURN( + aidl.sys.activeConfig = VALUE_OR_RETURN( legacy2aidl_audio_port_config_AudioPortConfig(legacy.active_config)); - aidl.ext = VALUE_OR_RETURN(legacy2aidl_AudioPortExt(legacy.ext, legacy.type)); + aidl.sys.activeConfig.hal.portId = aidl.hal.id; + RETURN_IF_ERROR( + legacy2aidl_AudioPortExt(legacy.ext, legacy.type, &aidl.hal.ext, &aidl.sys.ext)); return aidl; } ConversionResult -aidl2legacy_AudioMode_audio_mode_t(media::AudioMode aidl) { +aidl2legacy_AudioMode_audio_mode_t(AudioMode aidl) { switch (aidl) { - case media::AudioMode::INVALID: + case AudioMode::SYS_RESERVED_INVALID: return AUDIO_MODE_INVALID; - case media::AudioMode::CURRENT: + case AudioMode::SYS_RESERVED_CURRENT: return AUDIO_MODE_CURRENT; - case media::AudioMode::NORMAL: + case AudioMode::NORMAL: return AUDIO_MODE_NORMAL; - case media::AudioMode::RINGTONE: + case AudioMode::RINGTONE: return AUDIO_MODE_RINGTONE; - case media::AudioMode::IN_CALL: + case AudioMode::IN_CALL: return AUDIO_MODE_IN_CALL; - case media::AudioMode::IN_COMMUNICATION: + case AudioMode::IN_COMMUNICATION: return AUDIO_MODE_IN_COMMUNICATION; - case media::AudioMode::CALL_SCREEN: + case AudioMode::CALL_SCREEN: return AUDIO_MODE_CALL_SCREEN; + case AudioMode::SYS_RESERVED_CALL_REDIRECT: + return AUDIO_MODE_CALL_REDIRECT; + case AudioMode::SYS_RESERVED_COMMUNICATION_REDIRECT: + return AUDIO_MODE_COMMUNICATION_REDIRECT; } return unexpected(BAD_VALUE); } -ConversionResult +ConversionResult legacy2aidl_audio_mode_t_AudioMode(audio_mode_t legacy) { switch (legacy) { case AUDIO_MODE_INVALID: - return media::AudioMode::INVALID; + return AudioMode::SYS_RESERVED_INVALID; case AUDIO_MODE_CURRENT: - return media::AudioMode::CURRENT; + return AudioMode::SYS_RESERVED_CURRENT; case AUDIO_MODE_NORMAL: - return media::AudioMode::NORMAL; + return AudioMode::NORMAL; case AUDIO_MODE_RINGTONE: - return media::AudioMode::RINGTONE; + return AudioMode::RINGTONE; case AUDIO_MODE_IN_CALL: - return media::AudioMode::IN_CALL; + return AudioMode::IN_CALL; case AUDIO_MODE_IN_COMMUNICATION: - return media::AudioMode::IN_COMMUNICATION; + return AudioMode::IN_COMMUNICATION; case AUDIO_MODE_CALL_SCREEN: - return media::AudioMode::CALL_SCREEN; + return AudioMode::CALL_SCREEN; + case AUDIO_MODE_CALL_REDIRECT: + return AudioMode::SYS_RESERVED_CALL_REDIRECT; + case AUDIO_MODE_COMMUNICATION_REDIRECT: + return AudioMode::SYS_RESERVED_COMMUNICATION_REDIRECT; case AUDIO_MODE_CNT: break; } @@ -2272,30 +3236,30 @@ legacy2aidl_audio_playback_rate_t_AudioPlaybackRate(const audio_playback_rate_t& } ConversionResult -aidl2legacy_AudioStandard_audio_standard_t(media::AudioStandard aidl) { +aidl2legacy_AudioStandard_audio_standard_t(AudioStandard aidl) { switch (aidl) { - case media::AudioStandard::NONE: + case AudioStandard::NONE: return AUDIO_STANDARD_NONE; - case media::AudioStandard::EDID: + case AudioStandard::EDID: return AUDIO_STANDARD_EDID; } return unexpected(BAD_VALUE); } -ConversionResult +ConversionResult legacy2aidl_audio_standard_t_AudioStandard(audio_standard_t legacy) { switch (legacy) { case AUDIO_STANDARD_NONE: - return media::AudioStandard::NONE; + return AudioStandard::NONE; case AUDIO_STANDARD_EDID: - return media::AudioStandard::EDID; + return AudioStandard::EDID; } return unexpected(BAD_VALUE); } ConversionResult aidl2legacy_ExtraAudioDescriptor_audio_extra_audio_descriptor( - const media::ExtraAudioDescriptor& aidl) { + const ExtraAudioDescriptor& aidl) { audio_extra_audio_descriptor legacy; legacy.standard = VALUE_OR_RETURN(aidl2legacy_AudioStandard_audio_standard_t(aidl.standard)); if (aidl.audioDescriptor.size() > EXTRA_AUDIO_DESCRIPTOR_SIZE) { @@ -2310,10 +3274,10 @@ aidl2legacy_ExtraAudioDescriptor_audio_extra_audio_descriptor( return legacy; } -ConversionResult +ConversionResult legacy2aidl_audio_extra_audio_descriptor_ExtraAudioDescriptor( const audio_extra_audio_descriptor& legacy) { - media::ExtraAudioDescriptor aidl; + ExtraAudioDescriptor aidl; aidl.standard = VALUE_OR_RETURN(legacy2aidl_audio_standard_t_AudioStandard(legacy.standard)); if (legacy.descriptor_length > EXTRA_AUDIO_DESCRIPTOR_SIZE) { return unexpected(BAD_VALUE); @@ -2329,24 +3293,24 @@ legacy2aidl_audio_extra_audio_descriptor_ExtraAudioDescriptor( ConversionResult aidl2legacy_AudioEncapsulationType_audio_encapsulation_type_t( - const media::AudioEncapsulationType& aidl) { + const AudioEncapsulationType& aidl) { switch (aidl) { - case media::AudioEncapsulationType::NONE: + case AudioEncapsulationType::NONE: return AUDIO_ENCAPSULATION_TYPE_NONE; - case media::AudioEncapsulationType::IEC61937: + case AudioEncapsulationType::IEC61937: return AUDIO_ENCAPSULATION_TYPE_IEC61937; } return unexpected(BAD_VALUE); } -ConversionResult +ConversionResult legacy2aidl_audio_encapsulation_type_t_AudioEncapsulationType( const audio_encapsulation_type_t & legacy) { switch (legacy) { case AUDIO_ENCAPSULATION_TYPE_NONE: - return media::AudioEncapsulationType::NONE; + return AudioEncapsulationType::NONE; case AUDIO_ENCAPSULATION_TYPE_IEC61937: - return media::AudioEncapsulationType::IEC61937; + return AudioEncapsulationType::IEC61937; } return unexpected(BAD_VALUE); } @@ -2375,4 +3339,53 @@ legacy2aidl_TrackSecondaryOutputInfoPair_TrackSecondaryOutputInfo( return trackSecondaryOutputInfo; } +ConversionResult +aidl2legacy_AudioDirectMode_audio_direct_mode_t(media::AudioDirectMode aidl) { + switch (aidl) { + case media::AudioDirectMode::NONE: + return AUDIO_DIRECT_NOT_SUPPORTED; + case media::AudioDirectMode::OFFLOAD: + return AUDIO_DIRECT_OFFLOAD_SUPPORTED; + case media::AudioDirectMode::OFFLOAD_GAPLESS: + return AUDIO_DIRECT_OFFLOAD_GAPLESS_SUPPORTED; + case media::AudioDirectMode::BITSTREAM: + return AUDIO_DIRECT_BITSTREAM_SUPPORTED; + } + return unexpected(BAD_VALUE); +} +ConversionResult +legacy2aidl_audio_direct_mode_t_AudioDirectMode(audio_direct_mode_t legacy) { + switch (legacy) { + case AUDIO_DIRECT_NOT_SUPPORTED: + return media::AudioDirectMode::NONE; + case AUDIO_DIRECT_OFFLOAD_SUPPORTED: + return media::AudioDirectMode::OFFLOAD; + case AUDIO_DIRECT_OFFLOAD_GAPLESS_SUPPORTED: + return media::AudioDirectMode::OFFLOAD_GAPLESS; + case AUDIO_DIRECT_BITSTREAM_SUPPORTED: + return media::AudioDirectMode::BITSTREAM; + } + return unexpected(BAD_VALUE); +} + +ConversionResult aidl2legacy_int32_t_audio_direct_mode_t_mask(int32_t aidl) { + using LegacyMask = std::underlying_type_t; + + LegacyMask converted = VALUE_OR_RETURN( + (convertBitmask( + aidl, aidl2legacy_AudioDirectMode_audio_direct_mode_t, + indexToEnum_index, + enumToMask_bitmask))); + return static_cast(converted); +} +ConversionResult legacy2aidl_audio_direct_mode_t_int32_t_mask(audio_direct_mode_t legacy) { + using LegacyMask = std::underlying_type_t; + + LegacyMask legacyMask = static_cast(legacy); + return convertBitmask( + legacyMask, legacy2aidl_audio_direct_mode_t_AudioDirectMode, + indexToEnum_bitmask, + enumToMask_index); +} + } // namespace android diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp index a00cb79e6a12e6220f628b08afdeb3ad5f89de55..69a9c688bfea5d61fd8eb4e9fece20dbee29eff7 100644 --- a/media/libaudioclient/Android.bp +++ b/media/libaudioclient/Android.bp @@ -11,6 +11,10 @@ cc_library_headers { name: "libaudioclient_headers", vendor_available: true, min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.media", + ], host_supported: true, header_libs: [ @@ -51,6 +55,7 @@ cc_library { "PolicyAidlConversion.cpp" ], shared_libs: [ + "android.media.audio.common.types-V1-cpp", "audioclient-types-aidl-cpp", "audioflinger-aidl-cpp", "audiopolicy-aidl-cpp", @@ -71,6 +76,7 @@ cc_library { include_dirs: ["system/media/audio_utils/include"], export_include_dirs: ["include"], export_shared_lib_headers: [ + "android.media.audio.common.types-V1-cpp", "audioclient-types-aidl-cpp", "audioflinger-aidl-cpp", "audiopolicy-aidl-cpp", @@ -111,6 +117,7 @@ cc_library { "TrackPlayerBase.cpp", ], shared_libs: [ + "android.media.audio.common.types-V1-cpp", "audioclient-types-aidl-cpp", "audioflinger-aidl-cpp", "audiopolicy-aidl-cpp", @@ -134,7 +141,6 @@ cc_library { "libprocessgroup", "libshmemcompat", "libutils", - "libvibrator", "framework-permission-aidl-cpp", "packagemanager_aidl-cpp", ], @@ -144,6 +150,7 @@ cc_library { "spatializer-aidl-cpp", "framework-permission-aidl-cpp", "libbinder", + "libmediametrics", ], include_dirs: [ @@ -196,9 +203,11 @@ cc_library_headers { ], header_libs: [ "libbase_headers", + "liberror_headers", ], export_header_lib_headers: [ "libbase_headers", + "liberror_headers", ], apex_available: [ "//apex_available:platform", @@ -229,16 +238,19 @@ cc_library { "libaudioclient_aidl_conversion_util", ], shared_libs: [ + "android.media.audio.common.types-V1-cpp", "audioclient-types-aidl-cpp", "libbase", "libbinder", "liblog", "libshmemcompat", + "libstagefright_foundation", "libutils", "shared-file-region-aidl-cpp", "framework-permission-aidl-cpp", ], export_shared_lib_headers: [ + "android.media.audio.common.types-V1-cpp", "audioclient-types-aidl-cpp", "libbase", "shared-file-region-aidl-cpp", @@ -308,57 +320,32 @@ aidl_interface { srcs: [ "aidl/android/media/AudioAttributesInternal.aidl", "aidl/android/media/AudioClient.aidl", - "aidl/android/media/AudioConfig.aidl", - "aidl/android/media/AudioConfigBase.aidl", - "aidl/android/media/AudioContentType.aidl", - "aidl/android/media/AudioDevice.aidl", + "aidl/android/media/AudioDirectMode.aidl", "aidl/android/media/AudioDualMonoMode.aidl", - "aidl/android/media/AudioEncapsulationMode.aidl", - "aidl/android/media/AudioEncapsulationMetadataType.aidl", - "aidl/android/media/AudioEncapsulationType.aidl", "aidl/android/media/AudioFlag.aidl", - "aidl/android/media/AudioGain.aidl", - "aidl/android/media/AudioGainConfig.aidl", - "aidl/android/media/AudioGainMode.aidl", - "aidl/android/media/AudioInputFlags.aidl", + "aidl/android/media/AudioGainSys.aidl", "aidl/android/media/AudioIoConfigEvent.aidl", "aidl/android/media/AudioIoDescriptor.aidl", - "aidl/android/media/AudioIoFlags.aidl", - "aidl/android/media/AudioMixLatencyClass.aidl", - "aidl/android/media/AudioMode.aidl", - "aidl/android/media/AudioOffloadInfo.aidl", - "aidl/android/media/AudioOutputFlags.aidl", "aidl/android/media/AudioPatch.aidl", "aidl/android/media/AudioPlaybackRate.aidl", "aidl/android/media/AudioPort.aidl", + "aidl/android/media/AudioPortSys.aidl", "aidl/android/media/AudioPortConfig.aidl", - "aidl/android/media/AudioPortConfigType.aidl", - "aidl/android/media/AudioPortConfigDeviceExt.aidl", - "aidl/android/media/AudioPortConfigExt.aidl", - "aidl/android/media/AudioPortConfigMixExt.aidl", - "aidl/android/media/AudioPortConfigMixExtUseCase.aidl", - "aidl/android/media/AudioPortConfigSessionExt.aidl", - "aidl/android/media/AudioPortDeviceExt.aidl", - "aidl/android/media/AudioPortExt.aidl", - "aidl/android/media/AudioPortMixExt.aidl", + "aidl/android/media/AudioPortConfigSys.aidl", + "aidl/android/media/AudioPortDeviceExtSys.aidl", + "aidl/android/media/AudioPortExtSys.aidl", + "aidl/android/media/AudioPortMixExtSys.aidl", "aidl/android/media/AudioPortRole.aidl", - "aidl/android/media/AudioPortSessionExt.aidl", "aidl/android/media/AudioPortType.aidl", - "aidl/android/media/AudioProfile.aidl", - "aidl/android/media/AudioSourceType.aidl", - "aidl/android/media/AudioStandard.aidl", - "aidl/android/media/AudioStreamType.aidl", + "aidl/android/media/AudioProfileSys.aidl", "aidl/android/media/AudioTimestampInternal.aidl", "aidl/android/media/AudioUniqueIdUse.aidl", - "aidl/android/media/AudioUsage.aidl", - "aidl/android/media/AudioUuid.aidl", "aidl/android/media/AudioVibratorInfo.aidl", "aidl/android/media/EffectDescriptor.aidl", - "aidl/android/media/ExtraAudioDescriptor.aidl", "aidl/android/media/TrackSecondaryOutputInfo.aidl", ], imports: [ - "audio_common-aidl", + "android.media.audio.common.types-V1", "framework-permission-aidl", ], backend: { @@ -369,6 +356,9 @@ aidl_interface { "com.android.media", ], }, + java: { + sdk_version: "module_current", + }, }, } aidl_interface { @@ -399,7 +389,7 @@ aidl_interface { "aidl/android/media/SpatializerHeadTrackingMode.aidl", ], imports: [ - "audio_common-aidl", + "android.media.audio.common.types-V1", "audioclient-types-aidl", ], backend: { @@ -410,6 +400,9 @@ aidl_interface { "com.android.media", ], }, + java: { + sdk_version: "module_current", + }, }, } @@ -439,7 +432,7 @@ aidl_interface { "aidl/android/media/IAudioTrackCallback.aidl", ], imports: [ - "audio_common-aidl", + "android.media.audio.common.types-V1", "audioclient-types-aidl", "av-types-aidl", "effect-aidl", @@ -455,6 +448,9 @@ aidl_interface { "com.android.media", ], }, + java: { + sdk_version: "module_current", + }, }, } @@ -468,13 +464,12 @@ aidl_interface { "aidl/android/media/GetInputForAttrResponse.aidl", "aidl/android/media/GetOutputForAttrResponse.aidl", "aidl/android/media/GetSpatializerResponse.aidl", - "aidl/android/media/Int.aidl", "aidl/android/media/RecordClientInfo.aidl", "aidl/android/media/IAudioPolicyService.aidl", "aidl/android/media/IAudioPolicyServiceClient.aidl", ], imports: [ - "audio_common-aidl", + "android.media.audio.common.types-V1", "audioclient-types-aidl", "audiopolicy-types-aidl", "capture_state_listener-aidl", @@ -491,6 +486,9 @@ aidl_interface { "com.android.media", ], }, + java: { + sdk_version: "module_current", + }, }, } @@ -518,5 +516,8 @@ aidl_interface { "com.android.media", ], }, + java: { + sdk_version: "module_current", + }, }, } diff --git a/media/libaudioclient/AudioAttributes.cpp b/media/libaudioclient/AudioAttributes.cpp index 83bf5a7f2fcb75893963f6ce84133b2066c42333..260c06c52116a880a31bcca8196624a14bf8dcd5 100644 --- a/media/libaudioclient/AudioAttributes.cpp +++ b/media/libaudioclient/AudioAttributes.cpp @@ -24,9 +24,6 @@ #include #include -#define RETURN_STATUS_IF_ERROR(x) \ - { auto _tmp = (x); if (_tmp != OK) return _tmp; } - namespace android { status_t AudioAttributes::readFromParcel(const Parcel* parcel) { diff --git a/media/libaudioclient/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp index 8c645c3ecc057df609f2a1072dbb3dd4422fe054..d447f0cb302aa91aefb4876ca551e05c6cf88973 100644 --- a/media/libaudioclient/AudioEffect.cpp +++ b/media/libaudioclient/AudioEffect.cpp @@ -32,16 +32,12 @@ #include #include -#define RETURN_STATUS_IF_ERROR(x) \ - { \ - auto _tmp = (x); \ - if (_tmp != OK) return _tmp; \ - } - namespace android { using aidl_utils::statusTFromBinderStatus; using binder::Status; using media::IAudioPolicyService; +using media::audio::common::AudioSource; +using media::audio::common::AudioUuid; namespace { @@ -65,8 +61,7 @@ AudioEffect::AudioEffect(const android::content::AttributionSourceState& attribu status_t AudioEffect::set(const effect_uuid_t *type, const effect_uuid_t *uuid, int32_t priority, - effect_callback_t cbf, - void* user, + const wp& callback, audio_session_t sessionId, audio_io_handle_t io, const AudioDeviceTypeAddr& device, @@ -77,7 +72,7 @@ status_t AudioEffect::set(const effect_uuid_t *type, sp cblk; int enabled; - ALOGV("set %p mUserData: %p uuid: %p timeLow %08x", this, user, type, type ? type->timeLow : 0); + ALOGV("set %p uuid: %p timeLow %08x", this, type, type ? type->timeLow : 0); if (mIEffect != 0) { ALOGW("Effect already in use"); @@ -100,9 +95,8 @@ status_t AudioEffect::set(const effect_uuid_t *type, } mProbe = probe; mPriority = priority; - mCbf = cbf; - mUserData = user; mSessionId = sessionId; + mCallback = callback; memset(&mDescriptor, 0, sizeof(effect_descriptor_t)); mDescriptor.type = *(type != nullptr ? type : EFFECT_UUID_NULL); @@ -132,6 +126,9 @@ status_t AudioEffect::set(const effect_uuid_t *type, mStatus = audioFlinger->createEffect(request, &response); if (mStatus == OK) { + if (response.alreadyExists) { + mStatus = ALREADY_EXISTS; + } mId = response.id; enabled = response.enabled; iEffect = response.effect; @@ -188,11 +185,60 @@ status_t AudioEffect::set(const effect_uuid_t *type, return mStatus; } +namespace { +class LegacyCallbackWrapper : public AudioEffect::IAudioEffectCallback { + public: + LegacyCallbackWrapper(AudioEffect::legacy_callback_t callback, void* user): + mCallback(callback), mUser(user) {} + private: + void onControlStatusChanged(bool isGranted) override { + mCallback(AudioEffect::EVENT_CONTROL_STATUS_CHANGED, mUser, &isGranted); + } + + void onEnableStatusChanged(bool isEnabled) override { + mCallback(AudioEffect::EVENT_ENABLE_STATUS_CHANGED, mUser, &isEnabled); + } + + void onParameterChanged(std::vector param) override { + mCallback(AudioEffect::EVENT_PARAMETER_CHANGED, mUser, param.data()); + } + + void onError(status_t errorCode) override { + mCallback(AudioEffect::EVENT_ERROR, mUser, &errorCode); + } + + void onFramesProcessed(int32_t framesProcessed) override { + mCallback(AudioEffect::EVENT_FRAMES_PROCESSED, mUser, &framesProcessed); + } + + const AudioEffect::legacy_callback_t mCallback; + void* const mUser; +}; +} // namespace + +status_t AudioEffect::set(const effect_uuid_t *type, + const effect_uuid_t *uuid, + int32_t priority, + legacy_callback_t cbf, + void* user, + audio_session_t sessionId, + audio_io_handle_t io, + const AudioDeviceTypeAddr& device, + bool probe, + bool notifyFramesProcessed) +{ + if (cbf != nullptr) { + mLegacyWrapper = sp::make(cbf, user); + } else if (user != nullptr) { + LOG_ALWAYS_FATAL("%s: User provided without callback", __func__); + } + return set(type, uuid, priority, mLegacyWrapper, sessionId, io, device, probe, + notifyFramesProcessed); +} status_t AudioEffect::set(const char *typeStr, const char *uuidStr, int32_t priority, - effect_callback_t cbf, - void* user, + const wp& callback, audio_session_t sessionId, audio_io_handle_t io, const AudioDeviceTypeAddr& device, @@ -214,11 +260,29 @@ status_t AudioEffect::set(const char *typeStr, pUuid = &uuid; } - return set(pType, pUuid, priority, cbf, user, sessionId, io, + return set(pType, pUuid, priority, callback, sessionId, io, device, probe, notifyFramesProcessed); } - +status_t AudioEffect::set(const char *typeStr, + const char *uuidStr, + int32_t priority, + legacy_callback_t cbf, + void* user, + audio_session_t sessionId, + audio_io_handle_t io, + const AudioDeviceTypeAddr& device, + bool probe, + bool notifyFramesProcessed) +{ + if (cbf != nullptr) { + mLegacyWrapper = sp::make(cbf, user); + } else if (user != nullptr) { + LOG_ALWAYS_FATAL("%s: User provided without callback", __func__); + } + return set(typeStr, uuidStr, priority, mLegacyWrapper, sessionId, io, device, probe, + notifyFramesProcessed); +} AudioEffect::~AudioEffect() { ALOGV("Destructor %p", this); @@ -470,9 +534,9 @@ void AudioEffect::binderDied() { ALOGW("IEffect died"); mStatus = DEAD_OBJECT; - if (mCbf != NULL) { - status_t status = DEAD_OBJECT; - mCbf(EVENT_ERROR, mUserData, &status); + auto cb = mCallback.promote(); + if (cb != nullptr) { + cb->onError(mStatus); } mIEffect.clear(); } @@ -481,8 +545,8 @@ void AudioEffect::binderDied() void AudioEffect::controlStatusChanged(bool controlGranted) { - ALOGV("controlStatusChanged %p control %d callback %p mUserData %p", this, controlGranted, mCbf, - mUserData); + auto cb = mCallback.promote(); + ALOGV("controlStatusChanged %p control %d callback %p", this, controlGranted, cb.get()); if (controlGranted) { if (mStatus == ALREADY_EXISTS) { mStatus = NO_ERROR; @@ -492,18 +556,19 @@ void AudioEffect::controlStatusChanged(bool controlGranted) mStatus = ALREADY_EXISTS; } } - if (mCbf != NULL) { - mCbf(EVENT_CONTROL_STATUS_CHANGED, mUserData, &controlGranted); + if (cb != nullptr) { + cb->onControlStatusChanged(controlGranted); } } void AudioEffect::enableStatusChanged(bool enabled) { - ALOGV("enableStatusChanged %p enabled %d mCbf %p", this, enabled, mCbf); + auto cb = mCallback.promote(); + ALOGV("enableStatusChanged %p enabled %d mCallback %p", this, enabled, cb.get()); if (mStatus == ALREADY_EXISTS) { mEnabled = enabled; - if (mCbf != NULL) { - mCbf(EVENT_ENABLE_STATUS_CHANGED, mUserData, &enabled); + if (cb != nullptr) { + cb->onEnableStatusChanged(enabled); } } } @@ -515,19 +580,20 @@ void AudioEffect::commandExecuted(int32_t cmdCode, if (cmdData.empty() || replyData.empty()) { return; } - - if (mCbf != NULL && cmdCode == EFFECT_CMD_SET_PARAM) { + auto cb = mCallback.promote(); + if (cb != nullptr && cmdCode == EFFECT_CMD_SET_PARAM) { std::vector cmdDataCopy(cmdData); effect_param_t* cmd = reinterpret_cast(cmdDataCopy.data()); cmd->status = *reinterpret_cast(replyData.data()); - mCbf(EVENT_PARAMETER_CHANGED, mUserData, cmd); + cb->onParameterChanged(std::move(cmdDataCopy)); } } void AudioEffect::framesProcessed(int32_t frames) { - if (mCbf != NULL) { - mCbf(EVENT_FRAMES_PROCESSED, mUserData, &frames); + auto cb = mCallback.promote(); + if (cb != nullptr) { + cb->onFramesProcessed(frames); } } @@ -578,7 +644,7 @@ status_t AudioEffect::queryDefaultPreProcessing(audio_session_t audioSession, int32_t audioSessionAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_session_t_int32_t(audioSession)); - media::Int countAidl; + media::audio::common::Int countAidl; countAidl.value = VALUE_OR_RETURN_STATUS(convertIntegral(*count)); std::vector retAidl; RETURN_STATUS_IF_ERROR(statusTFromBinderStatus( @@ -630,12 +696,12 @@ status_t AudioEffect::addSourceDefaultEffect(const char *typeStr, uuid = *EFFECT_UUID_NULL; } - media::AudioUuid typeAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_uuid_t_AudioUuid(type)); - media::AudioUuid uuidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_uuid_t_AudioUuid(uuid)); + AudioUuid typeAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_uuid_t_AudioUuid(type)); + AudioUuid uuidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_uuid_t_AudioUuid(uuid)); std::string opPackageNameAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_String16_string(opPackageName)); - media::AudioSourceType sourceAidl = VALUE_OR_RETURN_STATUS( - legacy2aidl_audio_source_t_AudioSourceType(source)); + AudioSource sourceAidl = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_source_t_AudioSource(source)); int32_t retAidl; RETURN_STATUS_IF_ERROR(statusTFromBinderStatus( aps->addSourceDefaultEffect(typeAidl, opPackageNameAidl, uuidAidl, priority, sourceAidl, @@ -674,11 +740,11 @@ status_t AudioEffect::addStreamDefaultEffect(const char *typeStr, uuid = *EFFECT_UUID_NULL; } - media::AudioUuid typeAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_uuid_t_AudioUuid(type)); - media::AudioUuid uuidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_uuid_t_AudioUuid(uuid)); + AudioUuid typeAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_uuid_t_AudioUuid(type)); + AudioUuid uuidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_uuid_t_AudioUuid(uuid)); std::string opPackageNameAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_String16_string(opPackageName)); - media::AudioUsage usageAidl = VALUE_OR_RETURN_STATUS( + media::audio::common::AudioUsage usageAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_usage_t_AudioUsage(usage)); int32_t retAidl; RETURN_STATUS_IF_ERROR(statusTFromBinderStatus( diff --git a/media/libaudioclient/AudioProductStrategy.cpp b/media/libaudioclient/AudioProductStrategy.cpp index f98027aa1753d85ca426b2005f2a1be9ff770ff3..ecd423a05ba52b3686701625110d4ce2b6b7de42 100644 --- a/media/libaudioclient/AudioProductStrategy.cpp +++ b/media/libaudioclient/AudioProductStrategy.cpp @@ -21,9 +21,6 @@ #include #include -#define RETURN_STATUS_IF_ERROR(x) \ - { auto _tmp = (x); if (_tmp != OK) return _tmp; } - namespace android { status_t AudioProductStrategy::readFromParcel(const Parcel* parcel) { diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp index 22f0295d452e95c48f4f67e59c2e8e9428c4d41f..15203d6912bc9a598eadf36fa066c7547cc9f7ad 100644 --- a/media/libaudioclient/AudioRecord.cpp +++ b/media/libaudioclient/AudioRecord.cpp @@ -20,8 +20,10 @@ #include #include +#include #include +#include #include #include #include @@ -39,6 +41,7 @@ namespace android { +using ::android::base::StringPrintf; using android::content::AttributionSourceState; using aidl_utils::statusTFromBinderStatus; @@ -66,8 +69,9 @@ status_t AudioRecord::getMinFrameCount( // We double the size of input buffer for ping pong use of record buffer. // Assumes audio_is_linear_pcm(format) - if ((*frameCount = (size * 2) / (audio_channel_count_from_in_mask(channelMask) * - audio_bytes_per_sample(format))) == 0) { + const auto sampleSize = audio_channel_count_from_in_mask(channelMask) * + audio_bytes_per_sample(format); + if (sampleSize == 0 || ((*frameCount = (size * 2) / sampleSize) == 0)) { ALOGE("%s(): Unsupported configuration: sampleRate %u, format %#x, channelMask %#x", __func__, sampleRate, format, channelMask); return BAD_VALUE; @@ -142,7 +146,7 @@ AudioRecord::AudioRecord( audio_channel_mask_t channelMask, const AttributionSourceState& client, size_t frameCount, - callback_t cbf, + legacy_callback_t callback, void* user, uint32_t notificationFrames, audio_session_t sessionId, @@ -162,7 +166,39 @@ AudioRecord::AudioRecord( { uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(mClientAttributionSource.uid)); pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mClientAttributionSource.pid)); - (void)set(inputSource, sampleRate, format, channelMask, frameCount, cbf, user, + (void)set(inputSource, sampleRate, format, channelMask, frameCount, callback, user, + notificationFrames, false /*threadCanCallJava*/, sessionId, transferType, flags, + uid, pid, pAttributes, selectedDeviceId, selectedMicDirection, + microphoneFieldDimension); +} + +AudioRecord::AudioRecord( + audio_source_t inputSource, + uint32_t sampleRate, + audio_format_t format, + audio_channel_mask_t channelMask, + const AttributionSourceState& client, + size_t frameCount, + const wp& callback, + uint32_t notificationFrames, + audio_session_t sessionId, + transfer_type transferType, + audio_input_flags_t flags, + const audio_attributes_t* pAttributes, + audio_port_handle_t selectedDeviceId, + audio_microphone_direction_t selectedMicDirection, + float microphoneFieldDimension) + : mActive(false), + mStatus(NO_INIT), + mClientAttributionSource(client), + mSessionId(AUDIO_SESSION_ALLOCATE), + mPreviousPriority(ANDROID_PRIORITY_NORMAL), + mPreviousSchedulingGroup(SP_DEFAULT), + mProxy(nullptr) +{ + uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(mClientAttributionSource.uid)); + pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mClientAttributionSource.pid)); + (void)set(inputSource, sampleRate, format, channelMask, frameCount, callback, notificationFrames, false /*threadCanCallJava*/, sessionId, transferType, flags, uid, pid, pAttributes, selectedDeviceId, selectedMicDirection, microphoneFieldDimension); @@ -219,14 +255,44 @@ void AudioRecord::stopAndJoinCallbacks() { mDeviceCallback.clear(); } } +namespace { +class LegacyCallbackWrapper : public AudioRecord::IAudioRecordCallback { + const AudioRecord::legacy_callback_t mCallback; + void* const mData; + + public: + LegacyCallbackWrapper(AudioRecord::legacy_callback_t callback, void* user) + : mCallback(callback), mData(user) {} + + size_t onMoreData(const AudioRecord::Buffer& buffer) override { + AudioRecord::Buffer copy = buffer; + mCallback(AudioRecord::EVENT_MORE_DATA, mData, ©); + return copy.size(); + } + + void onOverrun() override { mCallback(AudioRecord::EVENT_OVERRUN, mData, nullptr); } + + void onMarker(uint32_t markerPosition) override { + mCallback(AudioRecord::EVENT_MARKER, mData, &markerPosition); + } + + void onNewPos(uint32_t newPos) override { + mCallback(AudioRecord::EVENT_NEW_POS, mData, &newPos); + } + + void onNewIAudioRecord() override { + mCallback(AudioRecord::EVENT_NEW_IAUDIORECORD, mData, nullptr); + } +}; +} // namespace + status_t AudioRecord::set( audio_source_t inputSource, uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, size_t frameCount, - callback_t cbf, - void* user, + const wp& callback, uint32_t notificationFrames, bool threadCanCallJava, audio_session_t sessionId, @@ -241,8 +307,8 @@ status_t AudioRecord::set( int32_t maxSharedAudioHistoryMs) { status_t status = NO_ERROR; - uint32_t channelCount; - + LOG_ALWAYS_FATAL_IF(mInitialized, "%s: should not be called twice", __func__); + mInitialized = true; // Note mPortId is not valid until the track is created, so omit mPortId in ALOG for set. ALOGV("%s(): inputSource %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, " "notificationFrames %u, sessionId %d, transferType %d, flags %#x, attributionSource %s" @@ -273,80 +339,87 @@ status_t AudioRecord::set( mSelectedMicFieldDimension = microphoneFieldDimension; mMaxSharedAudioHistoryMs = maxSharedAudioHistoryMs; - switch (transferType) { + std::string errorMessage; + // Copy the state variables early so they are available for error reporting. + if (pAttributes == nullptr) { + mAttributes = AUDIO_ATTRIBUTES_INITIALIZER; + mAttributes.source = inputSource; + if (inputSource == AUDIO_SOURCE_VOICE_COMMUNICATION + || inputSource == AUDIO_SOURCE_CAMCORDER) { + mAttributes.flags = static_cast( + mAttributes.flags | AUDIO_FLAG_CAPTURE_PRIVATE); + } + } else { + // stream type shouldn't be looked at, this track has audio attributes + memcpy(&mAttributes, pAttributes, sizeof(audio_attributes_t)); + ALOGV("%s: Building AudioRecord with attributes: source=%d flags=0x%x tags=[%s]", + __func__, mAttributes.source, mAttributes.flags, mAttributes.tags); + } + mSampleRate = sampleRate; + if (format == AUDIO_FORMAT_DEFAULT) { + format = AUDIO_FORMAT_PCM_16_BIT; + } + if (!audio_is_linear_pcm(format)) { + // Compressed capture requires direct + flags = (audio_input_flags_t) (flags | AUDIO_INPUT_FLAG_DIRECT); + ALOGI("%s(): Format %#x is not linear pcm. Setting DIRECT, using flags %#x", __func__, + format, flags); + } + mFormat = format; + mChannelMask = channelMask; + mSessionId = sessionId; + ALOGV("%s: mSessionId %d", __func__, mSessionId); + mOrigFlags = mFlags = flags; + + mTransfer = transferType; + switch (mTransfer) { case TRANSFER_DEFAULT: - if (cbf == NULL || threadCanCallJava) { - transferType = TRANSFER_SYNC; + if (callback == nullptr || threadCanCallJava) { + mTransfer = TRANSFER_SYNC; } else { - transferType = TRANSFER_CALLBACK; + mTransfer = TRANSFER_CALLBACK; } break; case TRANSFER_CALLBACK: - if (cbf == NULL) { - ALOGE("%s(): Transfer type TRANSFER_CALLBACK but cbf == NULL", __func__); + if (callback == nullptr) { + errorMessage = StringPrintf( + "%s: Transfer type TRANSFER_CALLBACK but callback == nullptr", __func__); status = BAD_VALUE; - goto exit; + goto error; } break; case TRANSFER_OBTAIN: case TRANSFER_SYNC: break; default: - ALOGE("%s(): Invalid transfer type %d", __func__, transferType); + errorMessage = StringPrintf("%s: Invalid transfer type %d", __func__, mTransfer); status = BAD_VALUE; - goto exit; + goto error; } - mTransfer = transferType; // invariant that mAudioRecord != 0 is true only after set() returns successfully if (mAudioRecord != 0) { - ALOGE("%s(): Track already in use", __func__); + errorMessage = StringPrintf("%s: Track already in use", __func__); status = INVALID_OPERATION; - goto exit; + goto error; } - if (pAttributes == NULL) { - mAttributes = AUDIO_ATTRIBUTES_INITIALIZER; - mAttributes.source = inputSource; - if (inputSource == AUDIO_SOURCE_VOICE_COMMUNICATION - || inputSource == AUDIO_SOURCE_CAMCORDER) { - mAttributes.flags = static_cast( - mAttributes.flags | AUDIO_FLAG_CAPTURE_PRIVATE); - } - } else { - // stream type shouldn't be looked at, this track has audio attributes - memcpy(&mAttributes, pAttributes, sizeof(audio_attributes_t)); - ALOGV("%s(): Building AudioRecord with attributes: source=%d flags=0x%x tags=[%s]", - __func__, mAttributes.source, mAttributes.flags, mAttributes.tags); - } - - mSampleRate = sampleRate; - - // these below should probably come from the audioFlinger too... - if (format == AUDIO_FORMAT_DEFAULT) { - format = AUDIO_FORMAT_PCM_16_BIT; - } - - // validate parameters - // AudioFlinger capture only supports linear PCM - if (!audio_is_valid_format(format) || !audio_is_linear_pcm(format)) { - ALOGE("%s(): Format %#x is not linear pcm", __func__, format); + if (!audio_is_valid_format(mFormat)) { + errorMessage = StringPrintf("%s: Format %#x is not valid", __func__, mFormat); status = BAD_VALUE; - goto exit; + goto error; } - mFormat = format; - if (!audio_is_input_channel(channelMask)) { - ALOGE("%s(): Invalid channel mask %#x", __func__, channelMask); + if (!audio_is_input_channel(mChannelMask)) { + errorMessage = StringPrintf("%s: Invalid channel mask %#x", __func__, mChannelMask); status = BAD_VALUE; - goto exit; + goto error; } - mChannelMask = channelMask; - channelCount = audio_channel_count_from_in_mask(channelMask); - mChannelCount = channelCount; - if (audio_is_linear_pcm(format)) { - mFrameSize = channelCount * audio_bytes_per_sample(format); + mChannelCount = audio_channel_count_from_in_mask(mChannelMask); + + if (audio_is_linear_pcm(mFormat)) { + mFrameSize = mChannelCount * audio_bytes_per_sample(mFormat); } else { mFrameSize = sizeof(uint8_t); } @@ -357,13 +430,8 @@ status_t AudioRecord::set( mNotificationFramesReq = notificationFrames; // mNotificationFramesAct is initialized in createRecord_l - mSessionId = sessionId; - ALOGV("%s(): mSessionId %d", __func__, mSessionId); - - mOrigFlags = mFlags = flags; - mCbf = cbf; - - if (cbf != NULL) { + mCallback = callback; + if (mCallback != nullptr) { mAudioRecordThread = new AudioRecordThread(*this); mAudioRecordThread->run("AudioRecord", ANDROID_PRIORITY_AUDIO); // thread begins in paused state, and will not reference us until start() @@ -383,10 +451,10 @@ status_t AudioRecord::set( mAudioRecordThread->requestExitAndWait(); mAudioRecordThread.clear(); } + // bypass error message to avoid logging twice (createRecord_l logs the error). goto exit; } - mUserData = user; // TODO: add audio hardware input latency here mLatency = (1000LL * mFrameCount) / mSampleRate; mMarkerPosition = 0; @@ -400,14 +468,48 @@ status_t AudioRecord::set( mFramesRead = 0; mFramesReadServerOffset = 0; -exit: - mStatus = status; +error: if (status != NO_ERROR) { mMediaMetrics.markError(status, __FUNCTION__); + ALOGE_IF(!errorMessage.empty(), "%s", errorMessage.c_str()); + reportError(status, AMEDIAMETRICS_PROP_EVENT_VALUE_CREATE, errorMessage.c_str()); } +exit: + mStatus = status; return status; } +status_t AudioRecord::set( + audio_source_t inputSource, + uint32_t sampleRate, + audio_format_t format, + audio_channel_mask_t channelMask, + size_t frameCount, + legacy_callback_t callback, + void* user, + uint32_t notificationFrames, + bool threadCanCallJava, + audio_session_t sessionId, + transfer_type transferType, + audio_input_flags_t flags, + uid_t uid, + pid_t pid, + const audio_attributes_t* pAttributes, + audio_port_handle_t selectedDeviceId, + audio_microphone_direction_t selectedMicDirection, + float microphoneFieldDimension, + int32_t maxSharedAudioHistoryMs) +{ + if (callback != nullptr) { + mLegacyCallbackWrapper = sp::make(callback, user); + } else if (user) { + LOG_ALWAYS_FATAL("Callback data provided without callback pointer!"); + } + return set(inputSource, sampleRate, format, channelMask, frameCount, mLegacyCallbackWrapper, + notificationFrames, threadCanCallJava, sessionId, transferType, flags, uid, pid, + pAttributes, selectedDeviceId, selectedMicDirection, microphoneFieldDimension, + maxSharedAudioHistoryMs); +} // ------------------------------------------------------------------------- status_t AudioRecord::start(AudioSystem::sync_event_t event, audio_session_t triggerSession) @@ -537,12 +639,12 @@ bool AudioRecord::stopped() const status_t AudioRecord::setMarkerPosition(uint32_t marker) { + AutoMutex lock(mLock); // The only purpose of setting marker position is to get a callback - if (mCbf == NULL) { + if (mCallback == nullptr) { return INVALID_OPERATION; } - AutoMutex lock(mLock); mMarkerPosition = marker; mMarkerReached = false; @@ -567,12 +669,12 @@ status_t AudioRecord::getMarkerPosition(uint32_t *marker) const status_t AudioRecord::setPositionUpdatePeriod(uint32_t updatePeriod) { + AutoMutex lock(mLock); // The only purpose of setting position update period is to get a callback - if (mCbf == NULL) { + if (mCallback == nullptr) { return INVALID_OPERATION; } - AutoMutex lock(mLock); mNewPosition = mProxy->getPosition() + updatePeriod; mUpdatePeriod = updatePeriod; @@ -623,6 +725,11 @@ status_t AudioRecord::getTimestamp(ExtendedTimestamp *timestamp) if (status == OK) { timestamp->mPosition[ExtendedTimestamp::LOCATION_CLIENT] = mFramesRead; timestamp->mTimeNs[ExtendedTimestamp::LOCATION_CLIENT] = 0; + if (!audio_is_linear_pcm(mFormat)) { + // Don't do retrograde corrections or server offset if track is + // compressed + return OK; + } // server side frame offset in case AudioRecord has been restored. for (int i = ExtendedTimestamp::LOCATION_SERVER; i < ExtendedTimestamp::LOCATION_MAX; ++i) { @@ -670,6 +777,8 @@ status_t AudioRecord::getTimestamp(ExtendedTimestamp *timestamp) // ---- Explicit Routing --------------------------------------------------- status_t AudioRecord::setInputDevice(audio_port_handle_t deviceId) { AutoMutex lock(mLock); + ALOGV("%s(%d): deviceId=%d mSelectedDeviceId=%d", + __func__, mPortId, deviceId, mSelectedDeviceId); if (mSelectedDeviceId != deviceId) { mSelectedDeviceId = deviceId; if (mStatus == NO_ERROR) { @@ -756,15 +865,16 @@ status_t AudioRecord::createRecord_l(const Modulo &epoch) const sp& audioFlinger = AudioSystem::get_audio_flinger(); IAudioFlinger::CreateRecordInput input; IAudioFlinger::CreateRecordOutput output; - audio_session_t originalSessionId; + [[maybe_unused]] audio_session_t originalSessionId; void *iMemPointer; audio_track_cblk_t* cblk; status_t status; static const int32_t kMaxCreateAttempts = 3; int32_t remainingAttempts = kMaxCreateAttempts; + std::string errorMessage; if (audioFlinger == 0) { - ALOGE("%s(%d): Could not get audioflinger", __func__, mPortId); + errorMessage = StringPrintf("%s(%d): Could not get audioflinger", __func__, mPortId); status = NO_INIT; goto exit; } @@ -830,8 +940,9 @@ status_t AudioRecord::createRecord_l(const Modulo &epoch) break; } if (status != FAILED_TRANSACTION || --remainingAttempts <= 0) { - ALOGE("%s(%d): AudioFlinger could not create record track, status: %d", - __func__, mPortId, status); + errorMessage = StringPrintf( + "%s(%d): AudioFlinger could not create record track, status: %d", + __func__, mPortId, status); goto exit; } // FAILED_TRANSACTION happens under very specific conditions causing a state mismatch @@ -858,9 +969,13 @@ status_t AudioRecord::createRecord_l(const Modulo &epoch) mRoutedDeviceId = output.selectedDeviceId; mSessionId = output.sessionId; mSampleRate = output.sampleRate; + mServerConfig = output.serverConfig; + mServerFrameSize = audio_bytes_per_frame( + audio_channel_count_from_in_mask(mServerConfig.channel_mask), mServerConfig.format); + mServerSampleSize = audio_bytes_per_sample(mServerConfig.format); if (output.cblk == 0) { - ALOGE("%s(%d): Could not get control block", __func__, mPortId); + errorMessage = StringPrintf("%s(%d): Could not get control block", __func__, mPortId); status = NO_INIT; goto exit; } @@ -870,7 +985,8 @@ status_t AudioRecord::createRecord_l(const Modulo &epoch) // issue (e.g. by copying). iMemPointer = output.cblk ->unsecurePointer(); if (iMemPointer == NULL) { - ALOGE("%s(%d): Could not get control block pointer", __func__, mPortId); + errorMessage = StringPrintf( + "%s(%d): Could not get control block pointer", __func__, mPortId); status = NO_INIT; goto exit; } @@ -889,7 +1005,8 @@ status_t AudioRecord::createRecord_l(const Modulo &epoch) // issue (e.g. by copying). buffers = output.buffers->unsecurePointer(); if (buffers == NULL) { - ALOGE("%s(%d): Could not get buffer pointer", __func__, mPortId); + errorMessage = StringPrintf( + "%s(%d): Could not get buffer pointer", __func__, mPortId); status = NO_INIT; goto exit; } @@ -921,6 +1038,10 @@ status_t AudioRecord::createRecord_l(const Modulo &epoch) mNotificationFramesReq, output.notificationFrameCount, output.frameCount); } mNotificationFramesAct = (uint32_t)output.notificationFrameCount; + if (mServerConfig.format != mFormat && mCallback != nullptr) { + mFormatConversionBufRaw = std::make_unique(mNotificationFramesAct * mFrameSize); + mFormatConversionBuffer.raw = mFormatConversionBufRaw.get(); + } //mInput != input includes the case where mInput == AUDIO_IO_HANDLE_NONE for first creation if (mDeviceCallback != 0) { @@ -947,7 +1068,7 @@ status_t AudioRecord::createRecord_l(const Modulo &epoch) } // update proxy - mProxy = new AudioRecordClientProxy(cblk, buffers, mFrameCount, mFrameSize); + mProxy = new AudioRecordClientProxy(cblk, buffers, mFrameCount, mServerFrameSize); mProxy->setEpoch(epoch); mProxy->setMinimum(mNotificationFramesAct); @@ -980,11 +1101,38 @@ status_t AudioRecord::createRecord_l(const Modulo &epoch) .record(); exit: + if (status != NO_ERROR) { + ALOGE_IF(!errorMessage.empty(), "%s", errorMessage.c_str()); + reportError(status, AMEDIAMETRICS_PROP_EVENT_VALUE_CREATE, errorMessage.c_str()); + } + mStatus = status; // sp track destructor will cause releaseOutput() to be called by AudioFlinger return status; } +// Report error associated with the event and some configuration details. +void AudioRecord::reportError(status_t status, const char *event, const char *message) const +{ + if (status == NO_ERROR) return; + // We report error on the native side because some callers do not come + // from Java. + // Ensure these variables are initialized in set(). + mediametrics::LogItem(AMEDIAMETRICS_KEY_AUDIO_RECORD_ERROR) + .set(AMEDIAMETRICS_PROP_EVENT, event) + .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)status) + .set(AMEDIAMETRICS_PROP_STATUSMESSAGE, message) + .set(AMEDIAMETRICS_PROP_ORIGINALFLAGS, toString(mOrigFlags).c_str()) + .set(AMEDIAMETRICS_PROP_SESSIONID, (int32_t)mSessionId) + .set(AMEDIAMETRICS_PROP_SOURCE, toString(mAttributes.source).c_str()) + .set(AMEDIAMETRICS_PROP_SELECTEDDEVICEID, (int32_t)mSelectedDeviceId) + .set(AMEDIAMETRICS_PROP_ENCODING, toString(mFormat).c_str()) + .set(AMEDIAMETRICS_PROP_CHANNELMASK, (int32_t)mChannelMask) + .set(AMEDIAMETRICS_PROP_FRAMECOUNT, (int32_t)mFrameCount) + .set(AMEDIAMETRICS_PROP_SAMPLERATE, (int32_t)mSampleRate) + .record(); +} + status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount, size_t *nonContig) { if (audioBuffer == NULL) { @@ -995,7 +1143,7 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount, size_ } if (mTransfer != TRANSFER_OBTAIN) { audioBuffer->frameCount = 0; - audioBuffer->size = 0; + audioBuffer->mSize = 0; audioBuffer->raw = NULL; if (nonContig != NULL) { *nonContig = 0; @@ -1048,7 +1196,13 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, const struct timespec *r if (status == DEAD_OBJECT) { // re-create track, unless someone else has already done so if (newSequence == oldSequence) { - status = restoreRecord_l("obtainBuffer"); + if (!audio_is_linear_pcm(mFormat)) { + // If compressed capture, don't attempt to restore the track. + // Return a DEAD_OBJECT error and let the caller recreate. + tryCounter = 0; + } else { + status = restoreRecord_l("obtainBuffer"); + } if (status != NO_ERROR) { buffer.mFrameCount = 0; buffer.mRaw = NULL; @@ -1078,7 +1232,7 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, const struct timespec *r } while ((status == DEAD_OBJECT) && (tryCounter-- > 0)); audioBuffer->frameCount = buffer.mFrameCount; - audioBuffer->size = buffer.mFrameCount * mFrameSize; + audioBuffer->mSize = buffer.mFrameCount * mServerFrameSize; audioBuffer->raw = buffer.mRaw; audioBuffer->sequence = oldSequence; if (nonContig != NULL) { @@ -1091,7 +1245,7 @@ void AudioRecord::releaseBuffer(const Buffer* audioBuffer) { // FIXME add error checking on mode, by adding an internal version - size_t stepCount = audioBuffer->size / mFrameSize; + size_t stepCount = audioBuffer->frameCount; if (stepCount == 0) { return; } @@ -1153,8 +1307,9 @@ ssize_t AudioRecord::read(void* buffer, size_t userSize, bool blocking) return ssize_t(err); } - size_t bytesRead = audioBuffer.size; - memcpy(buffer, audioBuffer.i8, bytesRead); + size_t bytesRead = audioBuffer.frameCount * mFrameSize; + memcpy_by_audio_format(buffer, mFormat, audioBuffer.raw, mServerConfig.format, + audioBuffer.mSize / mServerSampleSize); buffer = ((char *) buffer) + bytesRead; userSize -= bytesRead; read += bytesRead; @@ -1173,6 +1328,12 @@ ssize_t AudioRecord::read(void* buffer, size_t userSize, bool blocking) nsecs_t AudioRecord::processAudioBuffer() { mLock.lock(); + const sp callback = mCallback.promote(); + if (!callback) { + mCallback = nullptr; + mLock.unlock(); + return NS_NEVER; + } if (mAwaitBoost) { mAwaitBoost = false; mLock.unlock(); @@ -1248,26 +1409,26 @@ nsecs_t AudioRecord::processAudioBuffer() uint32_t sequence = mSequence; // These fields don't need to be cached, because they are assigned only by set(): - // mTransfer, mCbf, mUserData, mSampleRate, mFrameSize + // mTransfer, mCallback, mUserData, mSampleRate, mFrameSize mLock.unlock(); // perform callbacks while unlocked if (newOverrun) { - mCbf(EVENT_OVERRUN, mUserData, NULL); + callback->onOverrun(); + } if (markerReached) { - mCbf(EVENT_MARKER, mUserData, &markerPosition); + callback->onMarker(markerPosition.value()); } while (newPosCount > 0) { - size_t temp = newPosition.value(); // FIXME size_t != uint32_t - mCbf(EVENT_NEW_POS, mUserData, &temp); + callback->onNewPos(newPosition.value()); newPosition += updatePeriod; newPosCount--; } if (mObservedSequence != sequence) { mObservedSequence = sequence; - mCbf(EVENT_NEW_IAUDIORECORD, mUserData, NULL); + callback->onNewIAudioRecord(); } // if inactive, then don't run me again until re-started @@ -1351,9 +1512,19 @@ nsecs_t AudioRecord::processAudioBuffer() } } - size_t reqSize = audioBuffer.size; - mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer); - size_t readSize = audioBuffer.size; + Buffer* buffer = &audioBuffer; + if (mServerConfig.format != mFormat) { + buffer = &mFormatConversionBuffer; + buffer->frameCount = audioBuffer.frameCount; + buffer->mSize = buffer->frameCount * mFrameSize; + buffer->sequence = audioBuffer.sequence; + memcpy_by_audio_format(buffer->raw, mFormat, audioBuffer.raw, + mServerConfig.format, audioBuffer.size() / mServerSampleSize); + } + + const size_t reqSize = buffer->size(); + const size_t readSize = callback->onMoreData(*buffer); + buffer->mSize = readSize; // Validate on returned size if (ssize_t(readSize) < 0 || readSize > reqSize) { diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index 139d931d10493155738cc53b6aa1458676c8cc51..de8c2985faa88007d79cb4ece57ed9f3d90e9489 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -40,19 +40,25 @@ if (!_tmp.ok()) return aidl_utils::binderStatusFromStatusT(_tmp.error()); \ std::move(_tmp.value()); }) -#define RETURN_STATUS_IF_ERROR(x) \ - { \ - auto _tmp = (x); \ - if (_tmp != OK) return _tmp; \ - } - // ---------------------------------------------------------------------------- namespace android { using aidl_utils::statusTFromBinderStatus; using binder::Status; +using content::AttributionSourceState; using media::IAudioPolicyService; -using android::content::AttributionSourceState; +using media::audio::common::AudioConfig; +using media::audio::common::AudioConfigBase; +using media::audio::common::AudioDevice; +using media::audio::common::AudioDeviceDescription; +using media::audio::common::AudioFormatDescription; +using media::audio::common::AudioMMapPolicyInfo; +using media::audio::common::AudioMMapPolicyType; +using media::audio::common::AudioOffloadInfo; +using media::audio::common::AudioSource; +using media::audio::common::AudioStreamType; +using media::audio::common::AudioUsage; +using media::audio::common::Int; // client singleton for AudioFlinger binder interface Mutex AudioSystem::gLock; @@ -64,6 +70,7 @@ std::set AudioSystem::gAudioErrorCallbacks; dynamic_policy_callback AudioSystem::gDynPolicyCallback = NULL; record_config_callback AudioSystem::gRecordConfigCallback = NULL; routing_callback AudioSystem::gRoutingCallback = NULL; +vol_range_init_req_callback AudioSystem::gVolRangeInitReqCallback = NULL; // Required to be held while calling into gSoundTriggerCaptureStateListener. class CaptureStateListenerImpl; @@ -336,7 +343,7 @@ status_t AudioSystem::getSamplingRate(audio_io_handle_t ioHandle, if (desc == 0) { *samplingRate = af->sampleRate(ioHandle); } else { - *samplingRate = desc->mSamplingRate; + *samplingRate = desc->getSamplingRate(); } if (*samplingRate == 0) { ALOGE("AudioSystem::getSamplingRate failed for ioHandle %d", ioHandle); @@ -371,7 +378,7 @@ status_t AudioSystem::getFrameCount(audio_io_handle_t ioHandle, if (desc == 0) { *frameCount = af->frameCount(ioHandle); } else { - *frameCount = desc->mFrameCount; + *frameCount = desc->getFrameCount(); } if (*frameCount == 0) { ALOGE("AudioSystem::getFrameCount failed for ioHandle %d", ioHandle); @@ -406,7 +413,7 @@ status_t AudioSystem::getLatency(audio_io_handle_t output, if (outputDesc == 0) { *latency = af->latency(output); } else { - *latency = outputDesc->mLatency; + *latency = outputDesc->getLatency(); } ALOGV("getLatency() output %d, latency %d", output, *latency); @@ -494,7 +501,7 @@ status_t AudioSystem::getFrameCountHAL(audio_io_handle_t ioHandle, if (desc == 0) { *frameCount = af->frameCountHAL(ioHandle); } else { - *frameCount = desc->mFrameCountHAL; + *frameCount = desc->getFrameCountHAL(); } if (*frameCount == 0) { ALOGE("AudioSystem::getFrameCountHAL failed for ioHandle %d", ioHandle); @@ -535,15 +542,15 @@ void AudioSystem::AudioFlingerClient::binderDied(const wp& who __unused Status AudioSystem::AudioFlingerClient::ioConfigChanged( media::AudioIoConfigEvent _event, const media::AudioIoDescriptor& _ioDesc) { - audio_io_config_event event = VALUE_OR_RETURN_BINDER_STATUS( - aidl2legacy_AudioIoConfigEvent_audio_io_config_event(_event)); + audio_io_config_event_t event = VALUE_OR_RETURN_BINDER_STATUS( + aidl2legacy_AudioIoConfigEvent_audio_io_config_event_t(_event)); sp ioDesc( VALUE_OR_RETURN_BINDER_STATUS( aidl2legacy_AudioIoDescriptor_AudioIoDescriptor(_ioDesc))); ALOGV("ioConfigChanged() event %d", event); - if (ioDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) return Status::ok(); + if (ioDesc->getIoHandle() == AUDIO_IO_HANDLE_NONE) return Status::ok(); audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE; std::vector> callbacksToCall; @@ -556,93 +563,88 @@ Status AudioSystem::AudioFlingerClient::ioConfigChanged( case AUDIO_OUTPUT_REGISTERED: case AUDIO_INPUT_OPENED: case AUDIO_INPUT_REGISTERED: { - sp oldDesc = getIoDescriptor_l(ioDesc->mIoHandle); + sp oldDesc = getIoDescriptor_l(ioDesc->getIoHandle()); if (oldDesc == 0) { - mIoDescriptors.add(ioDesc->mIoHandle, ioDesc); + mIoDescriptors.add(ioDesc->getIoHandle(), ioDesc); } else { deviceId = oldDesc->getDeviceId(); - mIoDescriptors.replaceValueFor(ioDesc->mIoHandle, ioDesc); + mIoDescriptors.replaceValueFor(ioDesc->getIoHandle(), ioDesc); } if (ioDesc->getDeviceId() != AUDIO_PORT_HANDLE_NONE) { deviceId = ioDesc->getDeviceId(); if (event == AUDIO_OUTPUT_OPENED || event == AUDIO_INPUT_OPENED) { - auto it = mAudioDeviceCallbacks.find(ioDesc->mIoHandle); + auto it = mAudioDeviceCallbacks.find(ioDesc->getIoHandle()); if (it != mAudioDeviceCallbacks.end()) { callbacks = it->second; } } } - ALOGV("ioConfigChanged() new %s %s %d samplingRate %u, format %#x channel mask %#x " - "frameCount %zu deviceId %d", + ALOGV("ioConfigChanged() new %s %s %s", event == AUDIO_OUTPUT_OPENED || event == AUDIO_OUTPUT_REGISTERED ? "output" : "input", event == AUDIO_OUTPUT_OPENED || event == AUDIO_INPUT_OPENED ? "opened" : "registered", - ioDesc->mIoHandle, ioDesc->mSamplingRate, ioDesc->mFormat, - ioDesc->mChannelMask, - ioDesc->mFrameCount, ioDesc->getDeviceId()); + ioDesc->toDebugString().c_str()); } break; case AUDIO_OUTPUT_CLOSED: case AUDIO_INPUT_CLOSED: { - if (getIoDescriptor_l(ioDesc->mIoHandle) == 0) { + if (getIoDescriptor_l(ioDesc->getIoHandle()) == 0) { ALOGW("ioConfigChanged() closing unknown %s %d", - event == AUDIO_OUTPUT_CLOSED ? "output" : "input", ioDesc->mIoHandle); + event == AUDIO_OUTPUT_CLOSED ? "output" : "input", ioDesc->getIoHandle()); break; } ALOGV("ioConfigChanged() %s %d closed", - event == AUDIO_OUTPUT_CLOSED ? "output" : "input", ioDesc->mIoHandle); + event == AUDIO_OUTPUT_CLOSED ? "output" : "input", ioDesc->getIoHandle()); - mIoDescriptors.removeItem(ioDesc->mIoHandle); - mAudioDeviceCallbacks.erase(ioDesc->mIoHandle); + mIoDescriptors.removeItem(ioDesc->getIoHandle()); + mAudioDeviceCallbacks.erase(ioDesc->getIoHandle()); } break; case AUDIO_OUTPUT_CONFIG_CHANGED: case AUDIO_INPUT_CONFIG_CHANGED: { - sp oldDesc = getIoDescriptor_l(ioDesc->mIoHandle); + sp oldDesc = getIoDescriptor_l(ioDesc->getIoHandle()); if (oldDesc == 0) { ALOGW("ioConfigChanged() modifying unknown %s! %d", event == AUDIO_OUTPUT_CONFIG_CHANGED ? "output" : "input", - ioDesc->mIoHandle); + ioDesc->getIoHandle()); break; } deviceId = oldDesc->getDeviceId(); - mIoDescriptors.replaceValueFor(ioDesc->mIoHandle, ioDesc); + mIoDescriptors.replaceValueFor(ioDesc->getIoHandle(), ioDesc); if (deviceId != ioDesc->getDeviceId()) { deviceId = ioDesc->getDeviceId(); - auto it = mAudioDeviceCallbacks.find(ioDesc->mIoHandle); + auto it = mAudioDeviceCallbacks.find(ioDesc->getIoHandle()); if (it != mAudioDeviceCallbacks.end()) { callbacks = it->second; } } - ALOGV("ioConfigChanged() new config for %s %d samplingRate %u, format %#x " - "channel mask %#x frameCount %zu frameCountHAL %zu deviceId %d", + ALOGV("ioConfigChanged() new config for %s %s", event == AUDIO_OUTPUT_CONFIG_CHANGED ? "output" : "input", - ioDesc->mIoHandle, ioDesc->mSamplingRate, ioDesc->mFormat, - ioDesc->mChannelMask, ioDesc->mFrameCount, ioDesc->mFrameCountHAL, - ioDesc->getDeviceId()); + ioDesc->toDebugString().c_str()); } break; case AUDIO_CLIENT_STARTED: { - sp oldDesc = getIoDescriptor_l(ioDesc->mIoHandle); + sp oldDesc = getIoDescriptor_l(ioDesc->getIoHandle()); if (oldDesc == 0) { - ALOGW("ioConfigChanged() start client on unknown io! %d", ioDesc->mIoHandle); + ALOGW("ioConfigChanged() start client on unknown io! %d", + ioDesc->getIoHandle()); break; } ALOGV("ioConfigChanged() AUDIO_CLIENT_STARTED io %d port %d num callbacks %zu", - ioDesc->mIoHandle, ioDesc->mPortId, mAudioDeviceCallbacks.size()); - oldDesc->mPatch = ioDesc->mPatch; - auto it = mAudioDeviceCallbacks.find(ioDesc->mIoHandle); + ioDesc->getIoHandle(), ioDesc->getPortId(), mAudioDeviceCallbacks.size()); + oldDesc->setPatch(ioDesc->getPatch()); + auto it = mAudioDeviceCallbacks.find(ioDesc->getIoHandle()); if (it != mAudioDeviceCallbacks.end()) { auto cbks = it->second; - auto it2 = cbks.find(ioDesc->mPortId); + auto it2 = cbks.find(ioDesc->getPortId()); if (it2 != cbks.end()) { - callbacks.emplace(ioDesc->mPortId, it2->second); + callbacks.emplace(ioDesc->getPortId(), it2->second); deviceId = oldDesc->getDeviceId(); } } @@ -661,8 +663,8 @@ Status AudioSystem::AudioFlingerClient::ioConfigChanged( // Callbacks must be called without mLock held. May lead to dead lock if calling for // example getRoutedDevice that updates the device and tries to acquire mLock. for (auto cb : callbacksToCall) { - // If callbacksToCall is not empty, it implies ioDesc->mIoHandle and deviceId are valid - cb->onAudioDeviceUpdate(ioDesc->mIoHandle, deviceId); + // If callbacksToCall is not empty, it implies ioDesc->getIoHandle() and deviceId are valid + cb->onAudioDeviceUpdate(ioDesc->getIoHandle(), deviceId); } return Status::ok(); @@ -780,6 +782,11 @@ status_t AudioSystem::AudioFlingerClient::removeAudioDeviceCallback( gRoutingCallback = cb; } +/*static*/ void AudioSystem::setVolInitReqCallback(vol_range_init_req_callback cb) { + Mutex::Autolock _l(gLock); + gVolRangeInitReqCallback = cb; +} + // client singleton for AudioPolicyService binder interface // protected by gLockAPS sp AudioSystem::gAudioPolicyService; @@ -825,6 +832,11 @@ const sp AudioSystem::get_audio_policy_service() { return ap; } +void AudioSystem::clearAudioPolicyService() { + Mutex::Autolock _l(gLockAPS); + gAudioPolicyService.clear(); +} + // --------------------------------------------------------------------------- void AudioSystem::onNewAudioModulesAvailable() { @@ -833,35 +845,20 @@ void AudioSystem::onNewAudioModulesAvailable() { aps->onNewAudioModulesAvailable(); } -status_t AudioSystem::setDeviceConnectionState(audio_devices_t device, - audio_policy_dev_state_t state, - const char* device_address, - const char* device_name, +status_t AudioSystem::setDeviceConnectionState(audio_policy_dev_state_t state, + const android::media::audio::common::AudioPort& port, audio_format_t encodedFormat) { const sp& aps = AudioSystem::get_audio_policy_service(); - const char* address = ""; - const char* name = ""; if (aps == 0) return PERMISSION_DENIED; - if (device_address != NULL) { - address = device_address; - } - if (device_name != NULL) { - name = device_name; - } - - media::AudioDevice deviceAidl; - deviceAidl.type = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_devices_t_int32_t(device)); - deviceAidl.address = address; - return statusTFromBinderStatus( aps->setDeviceConnectionState( - deviceAidl, VALUE_OR_RETURN_STATUS( legacy2aidl_audio_policy_dev_state_t_AudioPolicyDeviceState(state)), - name, - VALUE_OR_RETURN_STATUS(legacy2aidl_audio_format_t_AudioFormat(encodedFormat)))); + port, + VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_format_t_AudioFormatDescription(encodedFormat)))); } audio_policy_dev_state_t AudioSystem::getDeviceConnectionState(audio_devices_t device, @@ -870,9 +867,8 @@ audio_policy_dev_state_t AudioSystem::getDeviceConnectionState(audio_devices_t d if (aps == 0) return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; auto result = [&]() -> ConversionResult { - media::AudioDevice deviceAidl; - deviceAidl.type = VALUE_OR_RETURN(legacy2aidl_audio_devices_t_int32_t(device)); - deviceAidl.address = device_address; + AudioDevice deviceAidl = VALUE_OR_RETURN( + legacy2aidl_audio_device_AudioDevice(device, device_address)); media::AudioPolicyDeviceState result; RETURN_IF_ERROR(statusTFromBinderStatus( @@ -900,13 +896,12 @@ status_t AudioSystem::handleDeviceConfigChange(audio_devices_t device, name = device_name; } - media::AudioDevice deviceAidl; - deviceAidl.type = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_devices_t_int32_t(device)); - deviceAidl.address = address; + AudioDevice deviceAidl = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_device_AudioDevice(device, address)); return statusTFromBinderStatus( aps->handleDeviceConfigChange(deviceAidl, name, VALUE_OR_RETURN_STATUS( - legacy2aidl_audio_format_t_AudioFormat(encodedFormat)))); + legacy2aidl_audio_format_t_AudioFormatDescription(encodedFormat)))); } status_t AudioSystem::setPhoneState(audio_mode_t state, uid_t uid) { @@ -955,7 +950,7 @@ audio_io_handle_t AudioSystem::getOutput(audio_stream_type_t stream) { if (aps == 0) return AUDIO_IO_HANDLE_NONE; auto result = [&]() -> ConversionResult { - media::AudioStreamType streamAidl = VALUE_OR_RETURN( + AudioStreamType streamAidl = VALUE_OR_RETURN( legacy2aidl_audio_stream_type_t_AudioStreamType(stream)); int32_t outputAidl; RETURN_IF_ERROR( @@ -975,7 +970,8 @@ status_t AudioSystem::getOutputForAttr(audio_attributes_t* attr, audio_output_flags_t flags, audio_port_handle_t* selectedDeviceId, audio_port_handle_t* portId, - std::vector* secondaryOutputs) { + std::vector* secondaryOutputs, + bool *isSpatialized) { if (attr == nullptr) { ALOGE("%s NULL audio attributes", __func__); return BAD_VALUE; @@ -1003,8 +999,8 @@ status_t AudioSystem::getOutputForAttr(audio_attributes_t* attr, media::AudioAttributesInternal attrAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_attributes_t_AudioAttributesInternal(*attr)); int32_t sessionAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_session_t_int32_t(session)); - media::AudioConfig configAidl = VALUE_OR_RETURN_STATUS( - legacy2aidl_audio_config_t_AudioConfig(*config)); + AudioConfig configAidl = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_config_t_AudioConfig(*config, false /*isInput*/)); int32_t flagsAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_output_flags_t_int32_t_mask(flags)); int32_t selectedDeviceIdAidl = VALUE_OR_RETURN_STATUS( @@ -1028,6 +1024,7 @@ status_t AudioSystem::getOutputForAttr(audio_attributes_t* attr, *portId = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_port_handle_t(responseAidl.portId)); *secondaryOutputs = VALUE_OR_RETURN_STATUS(convertContainer>( responseAidl.secondaryOutputs, aidl2legacy_int32_t_audio_io_handle_t)); + *isSpatialized = responseAidl.isSpatialized; return OK; } @@ -1097,8 +1094,8 @@ status_t AudioSystem::getInputForAttr(const audio_attributes_t* attr, int32_t inputAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(*input)); int32_t riidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_unique_id_t_int32_t(riid)); int32_t sessionAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_session_t_int32_t(session)); - media::AudioConfigBase configAidl = VALUE_OR_RETURN_STATUS( - legacy2aidl_audio_config_base_t_AudioConfigBase(*config)); + AudioConfigBase configAidl = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_config_base_t_AudioConfigBase(*config, true /*isInput*/)); int32_t flagsAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_input_flags_t_int32_t_mask(flags)); int32_t selectedDeviceIdAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_port_handle_t_int32_t(*selectedDeviceId)); @@ -1154,12 +1151,19 @@ status_t AudioSystem::initStreamVolume(audio_stream_type_t stream, const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; - media::AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS( + AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_stream_type_t_AudioStreamType(stream)); int32_t indexMinAidl = VALUE_OR_RETURN_STATUS(convertIntegral(indexMin)); int32_t indexMaxAidl = VALUE_OR_RETURN_STATUS(convertIntegral(indexMax)); - return statusTFromBinderStatus( + status_t status = statusTFromBinderStatus( aps->initStreamVolume(streamAidl, indexMinAidl, indexMaxAidl)); + if (status == DEAD_OBJECT) { + // This is a critical operation since w/o proper stream volumes no audio + // will be heard. Make sure we recover from a failure in any case. + ALOGE("Received DEAD_OBJECT from APS, clearing the client"); + clearAudioPolicyService(); + } + return status; } status_t AudioSystem::setStreamVolumeIndex(audio_stream_type_t stream, @@ -1168,10 +1172,11 @@ status_t AudioSystem::setStreamVolumeIndex(audio_stream_type_t stream, const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; - media::AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS( + AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_stream_type_t_AudioStreamType(stream)); int32_t indexAidl = VALUE_OR_RETURN_STATUS(convertIntegral(index)); - int32_t deviceAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_devices_t_int32_t(device)); + AudioDeviceDescription deviceAidl = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_devices_t_AudioDeviceDescription(device)); return statusTFromBinderStatus( aps->setStreamVolumeIndex(streamAidl, deviceAidl, indexAidl)); } @@ -1182,9 +1187,10 @@ status_t AudioSystem::getStreamVolumeIndex(audio_stream_type_t stream, const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; - media::AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS( + AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_stream_type_t_AudioStreamType(stream)); - int32_t deviceAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_devices_t_int32_t(device)); + AudioDeviceDescription deviceAidl = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_devices_t_AudioDeviceDescription(device)); int32_t indexAidl; RETURN_STATUS_IF_ERROR(statusTFromBinderStatus( aps->getStreamVolumeIndex(streamAidl, deviceAidl, &indexAidl))); @@ -1203,7 +1209,8 @@ status_t AudioSystem::setVolumeIndexForAttributes(const audio_attributes_t& attr media::AudioAttributesInternal attrAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_attributes_t_AudioAttributesInternal(attr)); int32_t indexAidl = VALUE_OR_RETURN_STATUS(convertIntegral(index)); - int32_t deviceAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_devices_t_int32_t(device)); + AudioDeviceDescription deviceAidl = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_devices_t_AudioDeviceDescription(device)); return statusTFromBinderStatus( aps->setVolumeIndexForAttributes(attrAidl, deviceAidl, indexAidl)); } @@ -1216,7 +1223,8 @@ status_t AudioSystem::getVolumeIndexForAttributes(const audio_attributes_t& attr media::AudioAttributesInternal attrAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_attributes_t_AudioAttributesInternal(attr)); - int32_t deviceAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_devices_t_int32_t(device)); + AudioDeviceDescription deviceAidl = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_devices_t_AudioDeviceDescription(device)); int32_t indexAidl; RETURN_STATUS_IF_ERROR(statusTFromBinderStatus( aps->getVolumeIndexForAttributes(attrAidl, deviceAidl, &indexAidl))); @@ -1255,7 +1263,7 @@ product_strategy_t AudioSystem::getStrategyForStream(audio_stream_type_t stream) if (aps == 0) return PRODUCT_STRATEGY_NONE; auto result = [&]() -> ConversionResult { - media::AudioStreamType streamAidl = VALUE_OR_RETURN( + AudioStreamType streamAidl = VALUE_OR_RETURN( legacy2aidl_audio_stream_type_t_AudioStreamType(stream)); int32_t resultAidl; RETURN_IF_ERROR(statusTFromBinderStatus( @@ -1265,23 +1273,9 @@ product_strategy_t AudioSystem::getStrategyForStream(audio_stream_type_t stream) return result.value_or(PRODUCT_STRATEGY_NONE); } -audio_devices_t AudioSystem::getDevicesForStream(audio_stream_type_t stream) { - const sp& aps = AudioSystem::get_audio_policy_service(); - if (aps == 0) return AUDIO_DEVICE_NONE; - - auto result = [&]() -> ConversionResult { - media::AudioStreamType streamAidl = VALUE_OR_RETURN( - legacy2aidl_audio_stream_type_t_AudioStreamType(stream)); - int32_t resultAidl; - RETURN_IF_ERROR(statusTFromBinderStatus( - aps->getDevicesForStream(streamAidl, &resultAidl))); - return aidl2legacy_int32_t_audio_devices_t(resultAidl); - }(); - return result.value_or(AUDIO_DEVICE_NONE); -} - status_t AudioSystem::getDevicesForAttributes(const AudioAttributes& aa, - AudioDeviceTypeAddrVector* devices) { + AudioDeviceTypeAddrVector* devices, + bool forVolume) { if (devices == nullptr) { return BAD_VALUE; } @@ -1290,9 +1284,9 @@ status_t AudioSystem::getDevicesForAttributes(const AudioAttributes& aa, media::AudioAttributesEx aaAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_AudioAttributes_AudioAttributesEx(aa)); - std::vector retAidl; + std::vector retAidl; RETURN_STATUS_IF_ERROR( - statusTFromBinderStatus(aps->getDevicesForAttributes(aaAidl, &retAidl))); + statusTFromBinderStatus(aps->getDevicesForAttributes(aaAidl, forVolume, &retAidl))); *devices = VALUE_OR_RETURN_STATUS( convertContainer( retAidl, @@ -1368,7 +1362,7 @@ status_t AudioSystem::isStreamActive(audio_stream_type_t stream, bool* state, ui if (aps == 0) return PERMISSION_DENIED; if (state == NULL) return BAD_VALUE; - media::AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS( + AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_stream_type_t_AudioStreamType(stream)); int32_t inPastMsAidl = VALUE_OR_RETURN_STATUS(convertIntegral(inPastMs)); RETURN_STATUS_IF_ERROR(statusTFromBinderStatus( @@ -1382,7 +1376,7 @@ status_t AudioSystem::isStreamActiveRemotely(audio_stream_type_t stream, bool* s if (aps == 0) return PERMISSION_DENIED; if (state == NULL) return BAD_VALUE; - media::AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS( + AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_stream_type_t_AudioStreamType(stream)); int32_t inPastMsAidl = VALUE_OR_RETURN_STATUS(convertIntegral(inPastMs)); RETURN_STATUS_IF_ERROR(statusTFromBinderStatus( @@ -1395,8 +1389,8 @@ status_t AudioSystem::isSourceActive(audio_source_t stream, bool* state) { if (aps == 0) return PERMISSION_DENIED; if (state == NULL) return BAD_VALUE; - media::AudioSourceType streamAidl = VALUE_OR_RETURN_STATUS( - legacy2aidl_audio_source_t_AudioSourceType(stream)); + AudioSource streamAidl = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_source_t_AudioSource(stream)); RETURN_STATUS_IF_ERROR(statusTFromBinderStatus( aps->isSourceActive(streamAidl, state))); return OK; @@ -1430,19 +1424,16 @@ void AudioSystem::clearAudioConfigCache() { } gAudioFlinger.clear(); } - { - Mutex::Autolock _l(gLockAPS); - gAudioPolicyService.clear(); - } + clearAudioPolicyService(); } status_t AudioSystem::setSupportedSystemUsages(const std::vector& systemUsages) { const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == nullptr) return PERMISSION_DENIED; - std::vector systemUsagesAidl = VALUE_OR_RETURN_STATUS( - convertContainer>(systemUsages, - legacy2aidl_audio_usage_t_AudioUsage)); + std::vector systemUsagesAidl = VALUE_OR_RETURN_STATUS( + convertContainer>(systemUsages, + legacy2aidl_audio_usage_t_AudioUsage)); return statusTFromBinderStatus(aps->setSupportedSystemUsages(systemUsagesAidl)); } @@ -1462,7 +1453,7 @@ audio_offload_mode_t AudioSystem::getOffloadSupport(const audio_offload_info_t& if (aps == 0) return AUDIO_OFFLOAD_NOT_SUPPORTED; auto result = [&]() -> ConversionResult { - media::AudioOffloadInfo infoAidl = VALUE_OR_RETURN( + AudioOffloadInfo infoAidl = VALUE_OR_RETURN( legacy2aidl_audio_offload_info_t_AudioOffloadInfo(info)); media::AudioOffloadMode retAidl; RETURN_IF_ERROR( @@ -1490,7 +1481,7 @@ status_t AudioSystem::listAudioPorts(audio_port_role_t role, legacy2aidl_audio_port_role_t_AudioPortRole(role)); media::AudioPortType typeAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_port_type_t_AudioPortType(type)); - media::Int numPortsAidl; + Int numPortsAidl; numPortsAidl.value = VALUE_OR_RETURN_STATUS(convertIntegral(*num_ports)); std::vector portsAidl; int32_t generationAidl; @@ -1508,13 +1499,12 @@ status_t AudioSystem::getAudioPort(struct audio_port_v7* port) { if (port == nullptr) { return BAD_VALUE; } - const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; - media::AudioPort portAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_port_v7_AudioPort(*port)); + media::AudioPort portAidl; RETURN_STATUS_IF_ERROR( - statusTFromBinderStatus(aps->getAudioPort(portAidl, &portAidl))); + statusTFromBinderStatus(aps->getAudioPort(port->id, &portAidl))); *port = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioPort_audio_port_v7(portAidl)); return OK; } @@ -1557,7 +1547,7 @@ status_t AudioSystem::listAudioPatches(unsigned int* num_patches, if (aps == 0) return PERMISSION_DENIED; - media::Int numPatchesAidl; + Int numPatchesAidl; numPatchesAidl.value = VALUE_OR_RETURN_STATUS(convertIntegral(*num_patches)); std::vector patchesAidl; int32_t generationAidl; @@ -1696,7 +1686,8 @@ status_t AudioSystem::acquireSoundTriggerSession(audio_session_t* session, statusTFromBinderStatus(aps->acquireSoundTriggerSession(&retAidl))); *session = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_session_t(retAidl.session)); *ioHandle = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_io_handle_t(retAidl.ioHandle)); - *device = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_devices_t(retAidl.device)); + *device = VALUE_OR_RETURN_STATUS( + aidl2legacy_AudioDeviceDescription_audio_devices_t(retAidl.device)); return OK; } @@ -1713,7 +1704,7 @@ audio_mode_t AudioSystem::getPhoneState() { if (aps == 0) return AUDIO_MODE_INVALID; auto result = [&]() -> ConversionResult { - media::AudioMode retAidl; + media::audio::common::AudioMode retAidl; RETURN_IF_ERROR(statusTFromBinderStatus(aps->getPhoneState(&retAidl))); return aidl2legacy_AudioMode_audio_mode_t(retAidl); }(); @@ -1738,8 +1729,8 @@ status_t AudioSystem::setUidDeviceAffinities(uid_t uid, const AudioDeviceTypeAdd if (aps == 0) return PERMISSION_DENIED; int32_t uidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(uid)); - std::vector devicesAidl = VALUE_OR_RETURN_STATUS( - convertContainer>(devices, + std::vector devicesAidl = VALUE_OR_RETURN_STATUS( + convertContainer>(devices, legacy2aidl_AudioDeviceTypeAddress)); return statusTFromBinderStatus(aps->setUidDeviceAffinities(uidAidl, devicesAidl)); } @@ -1758,9 +1749,9 @@ status_t AudioSystem::setUserIdDeviceAffinities(int userId, if (aps == 0) return PERMISSION_DENIED; int32_t userIdAidl = VALUE_OR_RETURN_STATUS(convertReinterpret(userId)); - std::vector devicesAidl = VALUE_OR_RETURN_STATUS( - convertContainer>(devices, - legacy2aidl_AudioDeviceTypeAddress)); + std::vector devicesAidl = VALUE_OR_RETURN_STATUS( + convertContainer>(devices, + legacy2aidl_AudioDeviceTypeAddress)); return statusTFromBinderStatus( aps->setUserIdDeviceAffinities(userIdAidl, devicesAidl)); } @@ -1833,10 +1824,11 @@ AudioSystem::getStreamVolumeDB(audio_stream_type_t stream, int index, audio_devi if (aps == 0) return NAN; auto result = [&]() -> ConversionResult { - media::AudioStreamType streamAidl = VALUE_OR_RETURN( + AudioStreamType streamAidl = VALUE_OR_RETURN( legacy2aidl_audio_stream_type_t_AudioStreamType(stream)); int32_t indexAidl = VALUE_OR_RETURN(convertIntegral(index)); - int32_t deviceAidl = VALUE_OR_RETURN(legacy2aidl_audio_devices_t_int32_t(device)); + AudioDeviceDescription deviceAidl = VALUE_OR_RETURN( + legacy2aidl_audio_devices_t_AudioDeviceDescription(device)); float retAidl; RETURN_IF_ERROR(statusTFromBinderStatus( aps->getStreamVolumeDB(streamAidl, indexAidl, deviceAidl, &retAidl))); @@ -1868,10 +1860,10 @@ status_t AudioSystem::getSurroundFormats(unsigned int* numSurroundFormats, const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; - media::Int numSurroundFormatsAidl; + Int numSurroundFormatsAidl; numSurroundFormatsAidl.value = VALUE_OR_RETURN_STATUS(convertIntegral(*numSurroundFormats)); - std::vector surroundFormatsAidl; + std::vector surroundFormatsAidl; std::vector surroundFormatsEnabledAidl; RETURN_STATUS_IF_ERROR(statusTFromBinderStatus( aps->getSurroundFormats(&numSurroundFormatsAidl, &surroundFormatsAidl, @@ -1881,7 +1873,7 @@ status_t AudioSystem::getSurroundFormats(unsigned int* numSurroundFormats, convertIntegral(numSurroundFormatsAidl.value)); RETURN_STATUS_IF_ERROR( convertRange(surroundFormatsAidl.begin(), surroundFormatsAidl.end(), surroundFormats, - aidl2legacy_AudioFormat_audio_format_t)); + aidl2legacy_AudioFormatDescription_audio_format_t)); std::copy(surroundFormatsEnabledAidl.begin(), surroundFormatsEnabledAidl.end(), surroundFormatsEnabled); return OK; @@ -1895,10 +1887,10 @@ status_t AudioSystem::getReportedSurroundFormats(unsigned int* numSurroundFormat const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; - media::Int numSurroundFormatsAidl; + Int numSurroundFormatsAidl; numSurroundFormatsAidl.value = VALUE_OR_RETURN_STATUS(convertIntegral(*numSurroundFormats)); - std::vector surroundFormatsAidl; + std::vector surroundFormatsAidl; RETURN_STATUS_IF_ERROR(statusTFromBinderStatus( aps->getReportedSurroundFormats(&numSurroundFormatsAidl, &surroundFormatsAidl))); @@ -1906,7 +1898,7 @@ status_t AudioSystem::getReportedSurroundFormats(unsigned int* numSurroundFormat convertIntegral(numSurroundFormatsAidl.value)); RETURN_STATUS_IF_ERROR( convertRange(surroundFormatsAidl.begin(), surroundFormatsAidl.end(), surroundFormats, - aidl2legacy_AudioFormat_audio_format_t)); + aidl2legacy_AudioFormatDescription_audio_format_t)); return OK; } @@ -1914,26 +1906,28 @@ status_t AudioSystem::setSurroundFormatEnabled(audio_format_t audioFormat, bool const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; - media::audio::common::AudioFormat audioFormatAidl = VALUE_OR_RETURN_STATUS( - legacy2aidl_audio_format_t_AudioFormat(audioFormat)); + AudioFormatDescription audioFormatAidl = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_format_t_AudioFormatDescription(audioFormat)); return statusTFromBinderStatus( aps->setSurroundFormatEnabled(audioFormatAidl, enabled)); } -status_t AudioSystem::setAssistantUid(uid_t uid) { +status_t AudioSystem::setAssistantServicesUids(const std::vector& uids) { const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; - int32_t uidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(uid)); - return statusTFromBinderStatus(aps->setAssistantUid(uidAidl)); + std::vector uidsAidl = VALUE_OR_RETURN_STATUS( + convertContainer>(uids, legacy2aidl_uid_t_int32_t)); + return statusTFromBinderStatus(aps->setAssistantServicesUids(uidsAidl)); } -status_t AudioSystem::setHotwordDetectionServiceUid(uid_t uid) { +status_t AudioSystem::setActiveAssistantServicesUids(const std::vector& activeUids) { const sp& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; - int32_t uidAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_uid_t_int32_t(uid)); - return statusTFromBinderStatus(aps->setHotwordDetectionServiceUid(uidAidl)); + std::vector activeUidsAidl = VALUE_OR_RETURN_STATUS( + convertContainer>(activeUids, legacy2aidl_uid_t_int32_t)); + return statusTFromBinderStatus(aps->setActiveAssistantServicesUids(activeUidsAidl)); } status_t AudioSystem::setA11yServicesUids(const std::vector& uids) { @@ -1966,6 +1960,19 @@ bool AudioSystem::isHapticPlaybackSupported() { return result.value_or(false); } +bool AudioSystem::isUltrasoundSupported() { + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) return false; + + auto result = [&]() -> ConversionResult { + bool retVal; + RETURN_IF_ERROR( + statusTFromBinderStatus(aps->isUltrasoundSupported(&retVal))); + return retVal; + }(); + return result.value_or(false); +} + status_t AudioSystem::getHwOffloadFormatsSupportedForBluetoothMedia( audio_devices_t device, std::vector* formats) { if (formats == nullptr) { @@ -1976,14 +1983,15 @@ status_t AudioSystem::getHwOffloadFormatsSupportedForBluetoothMedia( & aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return PERMISSION_DENIED; - std::vector formatsAidl; - int32_t deviceAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_devices_t_int32_t(device)); - + std::vector formatsAidl; + AudioDeviceDescription deviceAidl = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_devices_t_AudioDeviceDescription(device)); RETURN_STATUS_IF_ERROR(statusTFromBinderStatus( aps->getHwOffloadFormatsSupportedForBluetoothMedia(deviceAidl, &formatsAidl))); *formats = VALUE_OR_RETURN_STATUS( - convertContainer>(formatsAidl, - aidl2legacy_AudioFormat_audio_format_t)); + convertContainer>( + formatsAidl, + aidl2legacy_AudioFormatDescription_audio_format_t)); return OK; } @@ -2122,9 +2130,9 @@ status_t AudioSystem::setDevicesRoleForStrategy(product_strategy_t strategy, int32_t strategyAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_product_strategy_t_int32_t(strategy)); media::DeviceRole roleAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_device_role_t_DeviceRole(role)); - std::vector devicesAidl = VALUE_OR_RETURN_STATUS( - convertContainer>(devices, - legacy2aidl_AudioDeviceTypeAddress)); + std::vector devicesAidl = VALUE_OR_RETURN_STATUS( + convertContainer>(devices, + legacy2aidl_AudioDeviceTypeAddress)); return statusTFromBinderStatus( aps->setDevicesRoleForStrategy(strategyAidl, roleAidl, devicesAidl)); } @@ -2150,7 +2158,7 @@ status_t AudioSystem::getDevicesForRoleAndStrategy(product_strategy_t strategy, } int32_t strategyAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_product_strategy_t_int32_t(strategy)); media::DeviceRole roleAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_device_role_t_DeviceRole(role)); - std::vector devicesAidl; + std::vector devicesAidl; RETURN_STATUS_IF_ERROR(statusTFromBinderStatus( aps->getDevicesForRoleAndStrategy(strategyAidl, roleAidl, &devicesAidl))); devices = VALUE_OR_RETURN_STATUS( @@ -2167,12 +2175,12 @@ status_t AudioSystem::setDevicesRoleForCapturePreset(audio_source_t audioSource, return PERMISSION_DENIED; } - media::AudioSourceType audioSourceAidl = VALUE_OR_RETURN_STATUS( - legacy2aidl_audio_source_t_AudioSourceType(audioSource)); + AudioSource audioSourceAidl = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_source_t_AudioSource(audioSource)); media::DeviceRole roleAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_device_role_t_DeviceRole(role)); - std::vector devicesAidl = VALUE_OR_RETURN_STATUS( - convertContainer>(devices, - legacy2aidl_AudioDeviceTypeAddress)); + std::vector devicesAidl = VALUE_OR_RETURN_STATUS( + convertContainer>(devices, + legacy2aidl_AudioDeviceTypeAddress)); return statusTFromBinderStatus( aps->setDevicesRoleForCapturePreset(audioSourceAidl, roleAidl, devicesAidl)); } @@ -2184,12 +2192,12 @@ status_t AudioSystem::addDevicesRoleForCapturePreset(audio_source_t audioSource, if (aps == 0) { return PERMISSION_DENIED; } - media::AudioSourceType audioSourceAidl = VALUE_OR_RETURN_STATUS( - legacy2aidl_audio_source_t_AudioSourceType(audioSource)); + AudioSource audioSourceAidl = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_source_t_AudioSource(audioSource)); media::DeviceRole roleAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_device_role_t_DeviceRole(role)); - std::vector devicesAidl = VALUE_OR_RETURN_STATUS( - convertContainer>(devices, - legacy2aidl_AudioDeviceTypeAddress)); + std::vector devicesAidl = VALUE_OR_RETURN_STATUS( + convertContainer>(devices, + legacy2aidl_AudioDeviceTypeAddress)); return statusTFromBinderStatus( aps->addDevicesRoleForCapturePreset(audioSourceAidl, roleAidl, devicesAidl)); } @@ -2200,12 +2208,12 @@ status_t AudioSystem::removeDevicesRoleForCapturePreset( if (aps == 0) { return PERMISSION_DENIED; } - media::AudioSourceType audioSourceAidl = VALUE_OR_RETURN_STATUS( - legacy2aidl_audio_source_t_AudioSourceType(audioSource)); + AudioSource audioSourceAidl = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_source_t_AudioSource(audioSource)); media::DeviceRole roleAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_device_role_t_DeviceRole(role)); - std::vector devicesAidl = VALUE_OR_RETURN_STATUS( - convertContainer>(devices, - legacy2aidl_AudioDeviceTypeAddress)); + std::vector devicesAidl = VALUE_OR_RETURN_STATUS( + convertContainer>(devices, + legacy2aidl_AudioDeviceTypeAddress)); return statusTFromBinderStatus( aps->removeDevicesRoleForCapturePreset(audioSourceAidl, roleAidl, devicesAidl)); } @@ -2216,8 +2224,8 @@ status_t AudioSystem::clearDevicesRoleForCapturePreset(audio_source_t audioSourc if (aps == 0) { return PERMISSION_DENIED; } - media::AudioSourceType audioSourceAidl = VALUE_OR_RETURN_STATUS( - legacy2aidl_audio_source_t_AudioSourceType(audioSource)); + AudioSource audioSourceAidl = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_source_t_AudioSource(audioSource)); media::DeviceRole roleAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_device_role_t_DeviceRole(role)); return statusTFromBinderStatus( aps->clearDevicesRoleForCapturePreset(audioSourceAidl, roleAidl)); @@ -2230,10 +2238,10 @@ status_t AudioSystem::getDevicesForRoleAndCapturePreset(audio_source_t audioSour if (aps == 0) { return PERMISSION_DENIED; } - media::AudioSourceType audioSourceAidl = VALUE_OR_RETURN_STATUS( - legacy2aidl_audio_source_t_AudioSourceType(audioSource)); + AudioSource audioSourceAidl = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_source_t_AudioSource(audioSource)); media::DeviceRole roleAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_device_role_t_DeviceRole(role)); - std::vector devicesAidl; + std::vector devicesAidl; RETURN_STATUS_IF_ERROR(statusTFromBinderStatus( aps->getDevicesForRoleAndCapturePreset(audioSourceAidl, roleAidl, &devicesAidl))); devices = VALUE_OR_RETURN_STATUS( @@ -2272,16 +2280,63 @@ status_t AudioSystem::canBeSpatialized(const audio_attributes_t *attr, std::optional attrAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_attributes_t_AudioAttributesInternal(attributes)); - std::optional configAidl = VALUE_OR_RETURN_STATUS( - legacy2aidl_audio_config_t_AudioConfig(configuration)); - std::vector devicesAidl = VALUE_OR_RETURN_STATUS( - convertContainer>(devices, - legacy2aidl_AudioDeviceTypeAddress)); + std::optional configAidl = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_config_t_AudioConfig(configuration, false /*isInput*/)); + std::vector devicesAidl = VALUE_OR_RETURN_STATUS( + convertContainer>(devices, + legacy2aidl_AudioDeviceTypeAddress)); RETURN_STATUS_IF_ERROR(statusTFromBinderStatus( aps->canBeSpatialized(attrAidl, configAidl, devicesAidl, canBeSpatialized))); return OK; } +status_t AudioSystem::getDirectPlaybackSupport(const audio_attributes_t *attr, + const audio_config_t *config, + audio_direct_mode_t* directMode) { + if (attr == nullptr || config == nullptr || directMode == nullptr) { + return BAD_VALUE; + } + + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) { + return PERMISSION_DENIED; + } + + media::AudioAttributesInternal attrAidl = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_attributes_t_AudioAttributesInternal(*attr)); + AudioConfig configAidl = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_config_t_AudioConfig(*config, false /*isInput*/)); + + media::AudioDirectMode retAidl; + RETURN_STATUS_IF_ERROR(statusTFromBinderStatus( + aps->getDirectPlaybackSupport(attrAidl, configAidl, &retAidl))); + *directMode = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_direct_mode_t_mask( + static_cast(retAidl))); + return NO_ERROR; +} + +status_t AudioSystem::getDirectProfilesForAttributes(const audio_attributes_t* attr, + std::vector* audioProfiles) { + if (attr == nullptr) { + return BAD_VALUE; + } + + const sp& aps = AudioSystem::get_audio_policy_service(); + if (aps == 0) { + return PERMISSION_DENIED; + } + + media::AudioAttributesInternal attrAidl = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_attributes_t_AudioAttributesInternal(*attr)); + + std::vector audioProfilesAidl; + RETURN_STATUS_IF_ERROR(statusTFromBinderStatus( + aps->getDirectProfilesForAttributes(attrAidl, &audioProfilesAidl))); + *audioProfiles = VALUE_OR_RETURN_STATUS(convertContainer>( + audioProfilesAidl, aidl2legacy_AudioProfile_audio_profile, false /*isInput*/)); + + return NO_ERROR; +} class CaptureStateListenerImpl : public media::BnCaptureStateListener, public IBinder::DeathRecipient { @@ -2347,6 +2402,31 @@ status_t AudioSystem::setVibratorInfos( return af->setVibratorInfos(vibratorInfos); } +status_t AudioSystem::getMmapPolicyInfo( + AudioMMapPolicyType policyType, std::vector *policyInfos) { + const sp& af = AudioSystem::get_audio_flinger(); + if (af == nullptr) { + return PERMISSION_DENIED; + } + return af->getMmapPolicyInfos(policyType, policyInfos); +} + +int32_t AudioSystem::getAAudioMixerBurstCount() { + const sp& af = AudioSystem::get_audio_flinger(); + if (af == nullptr) { + return PERMISSION_DENIED; + } + return af->getAAudioMixerBurstCount(); +} + +int32_t AudioSystem::getAAudioHardwareBurstMinUsec() { + const sp& af = AudioSystem::get_audio_flinger(); + if (af == nullptr) { + return PERMISSION_DENIED; + } + return af->getAAudioHardwareBurstMinUsec(); +} + // --------------------------------------------------------------------------- int AudioSystem::AudioPolicyServiceClient::addAudioPortCallback( @@ -2458,12 +2538,12 @@ Status AudioSystem::AudioPolicyServiceClient::onDynamicPolicyMixStateUpdate( Status AudioSystem::AudioPolicyServiceClient::onRecordingConfigurationUpdate( int32_t event, const media::RecordClientInfo& clientInfo, - const media::AudioConfigBase& clientConfig, + const AudioConfigBase& clientConfig, const std::vector& clientEffects, - const media::AudioConfigBase& deviceConfig, + const AudioConfigBase& deviceConfig, const std::vector& effects, int32_t patchHandle, - media::AudioSourceType source) { + AudioSource source) { record_config_callback cb = NULL; { Mutex::Autolock _l(AudioSystem::gLock); @@ -2475,13 +2555,13 @@ Status AudioSystem::AudioPolicyServiceClient::onRecordingConfigurationUpdate( record_client_info_t clientInfoLegacy = VALUE_OR_RETURN_BINDER_STATUS( aidl2legacy_RecordClientInfo_record_client_info_t(clientInfo)); audio_config_base_t clientConfigLegacy = VALUE_OR_RETURN_BINDER_STATUS( - aidl2legacy_AudioConfigBase_audio_config_base_t(clientConfig)); + aidl2legacy_AudioConfigBase_audio_config_base_t(clientConfig, true /*isInput*/)); std::vector clientEffectsLegacy = VALUE_OR_RETURN_BINDER_STATUS( convertContainer>( clientEffects, aidl2legacy_EffectDescriptor_effect_descriptor_t)); audio_config_base_t deviceConfigLegacy = VALUE_OR_RETURN_BINDER_STATUS( - aidl2legacy_AudioConfigBase_audio_config_base_t(deviceConfig)); + aidl2legacy_AudioConfigBase_audio_config_base_t(deviceConfig, true /*isInput*/)); std::vector effectsLegacy = VALUE_OR_RETURN_BINDER_STATUS( convertContainer>( effects, @@ -2489,7 +2569,7 @@ Status AudioSystem::AudioPolicyServiceClient::onRecordingConfigurationUpdate( audio_patch_handle_t patchHandleLegacy = VALUE_OR_RETURN_BINDER_STATUS( aidl2legacy_int32_t_audio_patch_handle_t(patchHandle)); audio_source_t sourceLegacy = VALUE_OR_RETURN_BINDER_STATUS( - aidl2legacy_AudioSourceType_audio_source_t(source)); + aidl2legacy_AudioSource_audio_source_t(source)); cb(eventLegacy, &clientInfoLegacy, &clientConfigLegacy, clientEffectsLegacy, &deviceConfigLegacy, effectsLegacy, patchHandleLegacy, sourceLegacy); } @@ -2509,6 +2589,19 @@ Status AudioSystem::AudioPolicyServiceClient::onRoutingUpdated() { return Status::ok(); } +Status AudioSystem::AudioPolicyServiceClient::onVolumeRangeInitRequest() { + vol_range_init_req_callback cb = NULL; + { + Mutex::Autolock _l(AudioSystem::gLock); + cb = gVolRangeInitReqCallback; + } + + if (cb != NULL) { + cb(); + } + return Status::ok(); +} + void AudioSystem::AudioPolicyServiceClient::binderDied(const wp& who __unused) { { Mutex::Autolock _l(mLock); @@ -2519,10 +2612,7 @@ void AudioSystem::AudioPolicyServiceClient::binderDied(const wp& who __ mAudioVolumeGroupCallback[i]->onServiceDied(); } } - { - Mutex::Autolock _l(gLockAPS); - AudioSystem::gAudioPolicyService.clear(); - } + AudioSystem::clearAudioPolicyService(); ALOGW("AudioPolicyService server died!"); } @@ -2533,7 +2623,7 @@ aidl2legacy_RecordClientInfo_record_client_info_t(const media::RecordClientInfo& legacy.riid = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_unique_id_t(aidl.riid)); legacy.uid = VALUE_OR_RETURN(aidl2legacy_int32_t_uid_t(aidl.uid)); legacy.session = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_session_t(aidl.session)); - legacy.source = VALUE_OR_RETURN(aidl2legacy_AudioSourceType_audio_source_t(aidl.source)); + legacy.source = VALUE_OR_RETURN(aidl2legacy_AudioSource_audio_source_t(aidl.source)); legacy.port_id = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_port_handle_t(aidl.portId)); legacy.silenced = aidl.silenced; return legacy; @@ -2545,7 +2635,7 @@ legacy2aidl_record_client_info_t_RecordClientInfo(const record_client_info_t& le aidl.riid = VALUE_OR_RETURN(legacy2aidl_audio_unique_id_t_int32_t(legacy.riid)); aidl.uid = VALUE_OR_RETURN(legacy2aidl_uid_t_int32_t(legacy.uid)); aidl.session = VALUE_OR_RETURN(legacy2aidl_audio_session_t_int32_t(legacy.session)); - aidl.source = VALUE_OR_RETURN(legacy2aidl_audio_source_t_AudioSourceType(legacy.source)); + aidl.source = VALUE_OR_RETURN(legacy2aidl_audio_source_t_AudioSource(legacy.source)); aidl.portId = VALUE_OR_RETURN(legacy2aidl_audio_port_handle_t_int32_t(legacy.port_id)); aidl.silenced = legacy.silenced; return aidl; diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp index 70984b150e8bc265624642ed440fe5ba09bd6ecf..6ab8339eaeb7b73ad2bebe7dcd98bef5b658d66b 100644 --- a/media/libaudioclient/AudioTrack.cpp +++ b/media/libaudioclient/AudioTrack.cpp @@ -173,8 +173,8 @@ bool AudioTrack::isDirectOutputSupported(const audio_config_base_t& config, if (aps == 0) return false; auto result = [&]() -> ConversionResult { - media::AudioConfigBase configAidl = VALUE_OR_RETURN( - legacy2aidl_audio_config_base_t_AudioConfigBase(config)); + media::audio::common::AudioConfigBase configAidl = VALUE_OR_RETURN( + legacy2aidl_audio_config_base_t_AudioConfigBase(config, false /*isInput*/)); media::AudioAttributesInternal attributesAidl = VALUE_OR_RETURN( legacy2aidl_audio_attributes_t_AudioAttributesInternal(attributes)); bool retAidl; @@ -258,7 +258,82 @@ AudioTrack::AudioTrack( audio_channel_mask_t channelMask, size_t frameCount, audio_output_flags_t flags, - callback_t cbf, + const wp & callback, + int32_t notificationFrames, + audio_session_t sessionId, + transfer_type transferType, + const audio_offload_info_t *offloadInfo, + const AttributionSourceState& attributionSource, + const audio_attributes_t* pAttributes, + bool doNotReconnect, + float maxRequiredSpeed, + audio_port_handle_t selectedDeviceId) + : mStatus(NO_INIT), + mState(STATE_STOPPED), + mPreviousPriority(ANDROID_PRIORITY_NORMAL), + mPreviousSchedulingGroup(SP_DEFAULT), + mPausedPosition(0), + mAudioTrackCallback(new AudioTrackCallback()) +{ + mAttributes = AUDIO_ATTRIBUTES_INITIALIZER; + + // make_unique does not aggregate init until c++20 + mSetParams = std::unique_ptr{ + new SetParams{streamType, sampleRate, format, channelMask, frameCount, flags, callback, + notificationFrames, 0 /*sharedBuffer*/, false /*threadCanCallJava*/, + sessionId, transferType, offloadInfo, attributionSource, pAttributes, + doNotReconnect, maxRequiredSpeed, selectedDeviceId}}; +} + +namespace { + class LegacyCallbackWrapper : public AudioTrack::IAudioTrackCallback { + const AudioTrack::legacy_callback_t mCallback; + void * const mData; + public: + LegacyCallbackWrapper(AudioTrack::legacy_callback_t callback, void* user) + : mCallback(callback), mData(user) {} + size_t onMoreData(const AudioTrack::Buffer & buffer) override { + AudioTrack::Buffer copy = buffer; + mCallback(AudioTrack::EVENT_MORE_DATA, mData, static_cast(©)); + return copy.size(); + } + void onUnderrun() override { + mCallback(AudioTrack::EVENT_UNDERRUN, mData, nullptr); + } + void onLoopEnd(int32_t loopsRemaining) override { + mCallback(AudioTrack::EVENT_LOOP_END, mData, &loopsRemaining); + } + void onMarker(uint32_t markerPosition) override { + mCallback(AudioTrack::EVENT_MARKER, mData, &markerPosition); + } + void onNewPos(uint32_t newPos) override { + mCallback(AudioTrack::EVENT_NEW_POS, mData, &newPos); + } + void onBufferEnd() override { + mCallback(AudioTrack::EVENT_BUFFER_END, mData, nullptr); + } + void onNewIAudioTrack() override { + mCallback(AudioTrack::EVENT_NEW_IAUDIOTRACK, mData, nullptr); + } + void onStreamEnd() override { + mCallback(AudioTrack::EVENT_STREAM_END, mData, nullptr); + } + size_t onCanWriteMoreData(const AudioTrack::Buffer & buffer) override { + AudioTrack::Buffer copy = buffer; + mCallback(AudioTrack::EVENT_CAN_WRITE_MORE_DATA, mData, static_cast(©)); + return copy.size(); + } + }; +} + +AudioTrack::AudioTrack( + audio_stream_type_t streamType, + uint32_t sampleRate, + audio_format_t format, + audio_channel_mask_t channelMask, + size_t frameCount, + audio_output_flags_t flags, + legacy_callback_t callback, void* user, int32_t notificationFrames, audio_session_t sessionId, @@ -277,11 +352,49 @@ AudioTrack::AudioTrack( mAudioTrackCallback(new AudioTrackCallback()) { mAttributes = AUDIO_ATTRIBUTES_INITIALIZER; + if (callback != nullptr) { + mLegacyCallbackWrapper = sp::make(callback, user); + } else if (user) { + LOG_ALWAYS_FATAL("Callback data provided without callback pointer!"); + } + mSetParams = std::unique_ptr{new SetParams{ + streamType, sampleRate, format, channelMask, frameCount, flags, mLegacyCallbackWrapper, + notificationFrames, 0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId, + transferType, offloadInfo, attributionSource, pAttributes, doNotReconnect, + maxRequiredSpeed, selectedDeviceId}}; +} + +AudioTrack::AudioTrack( + audio_stream_type_t streamType, + uint32_t sampleRate, + audio_format_t format, + audio_channel_mask_t channelMask, + const sp& sharedBuffer, + audio_output_flags_t flags, + const wp& callback, + int32_t notificationFrames, + audio_session_t sessionId, + transfer_type transferType, + const audio_offload_info_t *offloadInfo, + const AttributionSourceState& attributionSource, + const audio_attributes_t* pAttributes, + bool doNotReconnect, + float maxRequiredSpeed) + : mStatus(NO_INIT), + mState(STATE_STOPPED), + mPreviousPriority(ANDROID_PRIORITY_NORMAL), + mPreviousSchedulingGroup(SP_DEFAULT), + mPausedPosition(0), + mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE), + mAudioTrackCallback(new AudioTrackCallback()) +{ + mAttributes = AUDIO_ATTRIBUTES_INITIALIZER; - (void)set(streamType, sampleRate, format, channelMask, - frameCount, flags, cbf, user, notificationFrames, - 0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId, transferType, offloadInfo, - attributionSource, pAttributes, doNotReconnect, maxRequiredSpeed, selectedDeviceId); + mSetParams = std::unique_ptr{ + new SetParams{streamType, sampleRate, format, channelMask, 0 /*frameCount*/, flags, + callback, notificationFrames, sharedBuffer, false /*threadCanCallJava*/, + sessionId, transferType, offloadInfo, attributionSource, pAttributes, + doNotReconnect, maxRequiredSpeed, AUDIO_PORT_HANDLE_NONE}}; } AudioTrack::AudioTrack( @@ -291,7 +404,7 @@ AudioTrack::AudioTrack( audio_channel_mask_t channelMask, const sp& sharedBuffer, audio_output_flags_t flags, - callback_t cbf, + legacy_callback_t callback, void* user, int32_t notificationFrames, audio_session_t sessionId, @@ -310,11 +423,23 @@ AudioTrack::AudioTrack( mAudioTrackCallback(new AudioTrackCallback()) { mAttributes = AUDIO_ATTRIBUTES_INITIALIZER; + if (callback) { + mLegacyCallbackWrapper = sp::make(callback, user); + } else if (user) { + LOG_ALWAYS_FATAL("Callback data provided without callback pointer!"); + } + mSetParams = std::unique_ptr{new SetParams{ + streamType, sampleRate, format, channelMask, 0 /*frameCount*/, flags, + mLegacyCallbackWrapper, notificationFrames, sharedBuffer, false /*threadCanCallJava*/, + sessionId, transferType, offloadInfo, attributionSource, pAttributes, doNotReconnect, + maxRequiredSpeed, AUDIO_PORT_HANDLE_NONE}}; +} - (void)set(streamType, sampleRate, format, channelMask, - 0 /*frameCount*/, flags, cbf, user, notificationFrames, - sharedBuffer, false /*threadCanCallJava*/, sessionId, transferType, offloadInfo, - attributionSource, pAttributes, doNotReconnect, maxRequiredSpeed); +void AudioTrack::onFirstRef() { + if (mSetParams) { + set(*mSetParams); + mSetParams.reset(); + } } AudioTrack::~AudioTrack() @@ -379,8 +504,38 @@ status_t AudioTrack::set( audio_channel_mask_t channelMask, size_t frameCount, audio_output_flags_t flags, - callback_t cbf, - void* user, + legacy_callback_t callback, + void * user, + int32_t notificationFrames, + const sp& sharedBuffer, + bool threadCanCallJava, + audio_session_t sessionId, + transfer_type transferType, + const audio_offload_info_t *offloadInfo, + const AttributionSourceState& attributionSource, + const audio_attributes_t* pAttributes, + bool doNotReconnect, + float maxRequiredSpeed, + audio_port_handle_t selectedDeviceId) +{ + if (callback) { + mLegacyCallbackWrapper = sp::make(callback, user); + } else if (user) { + LOG_ALWAYS_FATAL("Callback data provided without callback pointer!"); + } + return set(streamType, sampleRate,format, channelMask, frameCount, flags, + mLegacyCallbackWrapper, notificationFrames, sharedBuffer, threadCanCallJava, + sessionId, transferType, offloadInfo, attributionSource, pAttributes, + doNotReconnect, maxRequiredSpeed, selectedDeviceId); +} +status_t AudioTrack::set( + audio_stream_type_t streamType, + uint32_t sampleRate, + audio_format_t format, + audio_channel_mask_t channelMask, + size_t frameCount, + audio_output_flags_t flags, + const wp& callback, int32_t notificationFrames, const sp& sharedBuffer, bool threadCanCallJava, @@ -393,6 +548,8 @@ status_t AudioTrack::set( float maxRequiredSpeed, audio_port_handle_t selectedDeviceId) { + LOG_ALWAYS_FATAL_IF(mInitialized, "%s: should not be called twice", __func__); + mInitialized = true; status_t status; uint32_t channelCount; pid_t callingPid; @@ -400,7 +557,6 @@ status_t AudioTrack::set( uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(attributionSource.uid)); pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(attributionSource.pid)); std::string errorMessage; - // Note mPortId is not valid until the track is created, so omit mPortId in ALOG for set. ALOGV("%s(): streamType %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, " "flags #%x, notificationFrames %d, sessionId %d, transferType %d, uid %d, pid %d", @@ -462,7 +618,7 @@ status_t AudioTrack::set( case TRANSFER_DEFAULT: if (sharedBuffer != 0) { transferType = TRANSFER_SHARED; - } else if (cbf == NULL || threadCanCallJava) { + } else if (callback == nullptr|| threadCanCallJava) { transferType = TRANSFER_SYNC; } else { transferType = TRANSFER_CALLBACK; @@ -470,9 +626,9 @@ status_t AudioTrack::set( break; case TRANSFER_CALLBACK: case TRANSFER_SYNC_NOTIF_CALLBACK: - if (cbf == NULL || sharedBuffer != 0) { + if (callback == nullptr || sharedBuffer != 0) { errorMessage = StringPrintf( - "%s: Transfer type %s but cbf == NULL || sharedBuffer != 0", + "%s: Transfer type %s but callback == nullptr || sharedBuffer != 0", convertTransferToText(transferType), __func__); status = BAD_VALUE; goto error; @@ -625,10 +781,10 @@ status_t AudioTrack::set( mClientAttributionSource.pid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(callingPid)); } mAuxEffectId = 0; - mCbf = cbf; + mCallback = callback; - if (cbf != NULL) { - mAudioTrackThread = new AudioTrackThread(*this); + if (callback != nullptr) { + mAudioTrackThread = sp::make(*this); mAudioTrackThread->run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/); // thread begins in paused state, and will not reference us until start() } @@ -648,7 +804,6 @@ status_t AudioTrack::set( goto exit; } - mUserData = user; mLoopCount = 0; mLoopStart = 0; mLoopEnd = 0; @@ -698,7 +853,7 @@ status_t AudioTrack::set( uint32_t channelMask, size_t frameCount, audio_output_flags_t flags, - callback_t cbf, + legacy_callback_t callback, void* user, int32_t notificationFrames, const sp& sharedBuffer, @@ -717,11 +872,15 @@ status_t AudioTrack::set( attributionSource.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(uid)); attributionSource.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(pid)); attributionSource.token = sp::make(); - return set(streamType, sampleRate, format, - static_cast(channelMask), - frameCount, flags, cbf, user, notificationFrames, sharedBuffer, - threadCanCallJava, sessionId, transferType, offloadInfo, attributionSource, - pAttributes, doNotReconnect, maxRequiredSpeed, selectedDeviceId); + if (callback) { + mLegacyCallbackWrapper = sp::make(callback, user); + } else if (user) { + LOG_ALWAYS_FATAL("Callback data provided without callback pointer!"); + } + return set(streamType, sampleRate, format, static_cast(channelMask), + frameCount, flags, mLegacyCallbackWrapper, notificationFrames, sharedBuffer, + threadCanCallJava, sessionId, transferType, offloadInfo, attributionSource, + pAttributes, doNotReconnect, maxRequiredSpeed, selectedDeviceId); } // ------------------------------------------------------------------------- @@ -1354,10 +1513,6 @@ ssize_t AudioTrack::setBufferSizeInFrames(size_t bufferSizeInFrames) if (mOutput == AUDIO_IO_HANDLE_NONE || mProxy.get() == 0) { return NO_INIT; } - // Reject if timed track or compressed audio. - if (!audio_is_linear_pcm(mFormat)) { - return INVALID_OPERATION; - } ssize_t originalBufferSize = mProxy->getBufferSizeInFrames(); ssize_t finalBufferSize = mProxy->setBufferSizeInFrames((uint32_t) bufferSizeInFrames); @@ -1450,12 +1605,12 @@ void AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount) status_t AudioTrack::setMarkerPosition(uint32_t marker) { + AutoMutex lock(mLock); // The only purpose of setting marker position is to get a callback - if (mCbf == NULL || isOffloadedOrDirect()) { + if (!mCallback.promote() || isOffloadedOrDirect_l()) { return INVALID_OPERATION; } - AutoMutex lock(mLock); mMarkerPosition = marker; mMarkerReached = false; @@ -1483,12 +1638,12 @@ status_t AudioTrack::getMarkerPosition(uint32_t *marker) const status_t AudioTrack::setPositionUpdatePeriod(uint32_t updatePeriod) { + AutoMutex lock(mLock); // The only purpose of setting position update period is to get a callback - if (mCbf == NULL || isOffloadedOrDirect()) { + if (!mCallback.promote() || isOffloadedOrDirect_l()) { return INVALID_OPERATION; } - AutoMutex lock(mLock); mNewPosition = updateAndGetPosition_l() + updatePeriod; mUpdatePeriod = updatePeriod; @@ -1634,6 +1789,8 @@ audio_io_handle_t AudioTrack::getOutput() const status_t AudioTrack::setOutputDevice(audio_port_handle_t deviceId) { AutoMutex lock(mLock); + ALOGV("%s(%d): deviceId=%d mSelectedDeviceId=%d", + __func__, mPortId, deviceId, mSelectedDeviceId); if (mSelectedDeviceId != deviceId) { mSelectedDeviceId = deviceId; if (mStatus == NO_ERROR) { @@ -1881,7 +2038,7 @@ status_t AudioTrack::createTrack_l() mAwaitBoost = true; } } else { - ALOGD("%s(%d): AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %zu -> %zu", + ALOGV("%s(%d): AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %zu -> %zu", __func__, mPortId, mReqFrameCount, mFrameCount); } } @@ -2068,7 +2225,7 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount, size_t } if (mTransfer != TRANSFER_OBTAIN) { audioBuffer->frameCount = 0; - audioBuffer->size = 0; + audioBuffer->mSize = 0; audioBuffer->raw = NULL; if (nonContig != NULL) { *nonContig = 0; @@ -2160,7 +2317,7 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *re } while (((status == DEAD_OBJECT) || (status == NOT_ENOUGH_DATA)) && (tryCounter-- > 0)); audioBuffer->frameCount = buffer.mFrameCount; - audioBuffer->size = buffer.mFrameCount * mFrameSize; + audioBuffer->mSize = buffer.mFrameCount * mFrameSize; audioBuffer->raw = buffer.mRaw; audioBuffer->sequence = oldSequence; if (nonContig != NULL) { @@ -2176,7 +2333,7 @@ void AudioTrack::releaseBuffer(const Buffer* audioBuffer) return; } - size_t stepCount = audioBuffer->size / mFrameSize; + size_t stepCount = audioBuffer->mSize / mFrameSize; if (stepCount == 0) { return; } @@ -2256,8 +2413,8 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking) return ssize_t(err); } - size_t toWrite = audioBuffer.size; - memcpy(audioBuffer.i8, buffer, toWrite); + size_t toWrite = audioBuffer.size(); + memcpy(audioBuffer.raw, buffer, toWrite); buffer = ((const char *) buffer) + toWrite; userSize -= toWrite; written += toWrite; @@ -2287,10 +2444,15 @@ nsecs_t AudioTrack::processAudioBuffer() { // Currently the AudioTrack thread is not created if there are no callbacks. // Would it ever make sense to run the thread, even without callbacks? - // If so, then replace this by checks at each use for mCbf != NULL. + // If so, then replace this by checks at each use for mCallback != NULL. LOG_ALWAYS_FATAL_IF(mCblk == NULL); - mLock.lock(); + sp callback = mCallback.promote(); + if (!callback) { + mCallback = nullptr; + mLock.unlock(); + return NS_NEVER; + } if (mAwaitBoost) { mAwaitBoost = false; mLock.unlock(); @@ -2388,7 +2550,7 @@ nsecs_t AudioTrack::processAudioBuffer() sp proxy = mProxy; // Determine the number of new loop callback(s) that will be needed, while locked. - int loopCountNotifications = 0; + uint32_t loopCountNotifications = 0; uint32_t loopPeriod = 0; // time in frames for next EVENT_LOOP_END or EVENT_BUFFER_END if (mLoopCount > 0) { @@ -2410,7 +2572,7 @@ nsecs_t AudioTrack::processAudioBuffer() } // These fields don't need to be cached, because they are assigned only by set(): - // mTransfer, mCbf, mUserData, mFormat, mFrameSize, mFlags + // mTransfer, mCallback, mUserData, mFormat, mFrameSize, mFlags // mFlags is also assigned by createTrack_l(), but not the bit we care about. mLock.unlock(); @@ -2435,7 +2597,7 @@ nsecs_t AudioTrack::processAudioBuffer() if (status != DEAD_OBJECT) { // for DEAD_OBJECT, we do not send a EVENT_STREAM_END after stop(); // instead, the application should handle the EVENT_NEW_IAUDIOTRACK. - mCbf(EVENT_STREAM_END, mUserData, NULL); + callback->onStreamEnd(); } { AutoMutex lock(mLock); @@ -2458,28 +2620,27 @@ nsecs_t AudioTrack::processAudioBuffer() // perform callbacks while unlocked if (newUnderrun) { - mCbf(EVENT_UNDERRUN, mUserData, NULL); + callback->onUnderrun(); } while (loopCountNotifications > 0) { - mCbf(EVENT_LOOP_END, mUserData, NULL); --loopCountNotifications; + callback->onLoopEnd(mLoopCount > 0 ? loopCountNotifications + mLoopCountNotified : -1); } if (flags & CBLK_BUFFER_END) { - mCbf(EVENT_BUFFER_END, mUserData, NULL); + callback->onBufferEnd(); } if (markerReached) { - mCbf(EVENT_MARKER, mUserData, &markerPosition); + callback->onMarker(markerPosition.value()); } while (newPosCount > 0) { - size_t temp = newPosition.value(); // FIXME size_t != uint32_t - mCbf(EVENT_NEW_POS, mUserData, &temp); + callback->onNewPos(newPosition.value()); newPosition += updatePeriod; newPosCount--; } if (mObservedSequence != sequence) { mObservedSequence = sequence; - mCbf(EVENT_NEW_IAUDIOTRACK, mUserData, NULL); + callback->onNewIAudioTrack(); // for offloaded tracks, just wait for the upper layers to recreate the track if (isOffloadedOrDirect()) { return NS_INACTIVE; @@ -2611,16 +2772,15 @@ nsecs_t AudioTrack::processAudioBuffer() } } - size_t reqSize = audioBuffer.size; + size_t reqSize = audioBuffer.size(); if (mTransfer == TRANSFER_SYNC_NOTIF_CALLBACK) { // when notifying client it can write more data, pass the total size that can be // written in the next write() call, since it's not passed through the callback - audioBuffer.size += nonContig; + audioBuffer.mSize += nonContig; } - mCbf(mTransfer == TRANSFER_CALLBACK ? EVENT_MORE_DATA : EVENT_CAN_WRITE_MORE_DATA, - mUserData, &audioBuffer); - size_t writtenSize = audioBuffer.size; - + const size_t writtenSize = (mTransfer == TRANSFER_CALLBACK) + ? callback->onMoreData(audioBuffer) + : callback->onCanWriteMoreData(audioBuffer); // Validate on returned size if (ssize_t(writtenSize) < 0 || writtenSize > reqSize) { ALOGE("%s(%d): EVENT_MORE_DATA requested %zu bytes but callback returned %zd bytes", @@ -2680,6 +2840,9 @@ nsecs_t AudioTrack::processAudioBuffer() return ns; } + // releaseBuffer reads from audioBuffer.size + audioBuffer.mSize = writtenSize; + size_t releasedFrames = writtenSize / mFrameSize; audioBuffer.frameCount = releasedFrames; mRemainingFrames -= releasedFrames; diff --git a/media/libaudioclient/AudioVolumeGroup.cpp b/media/libaudioclient/AudioVolumeGroup.cpp index 361f7b834d76acaa39b8f7182582bc218ad5beff..ab95246150e0b4f96d9e82c1e3c52baa32fb4178 100644 --- a/media/libaudioclient/AudioVolumeGroup.cpp +++ b/media/libaudioclient/AudioVolumeGroup.cpp @@ -26,11 +26,10 @@ #include #include -#define RETURN_STATUS_IF_ERROR(x) \ - { auto _tmp = (x); if (_tmp != OK) return _tmp; } - namespace android { +using media::audio::common::AudioStreamType; + status_t AudioVolumeGroup::readFromParcel(const Parcel *parcel) { media::AudioVolumeGroup aidl; @@ -55,7 +54,7 @@ legacy2aidl_AudioVolumeGroup(const AudioVolumeGroup& legacy) { legacy.getAudioAttributes(), legacy2aidl_audio_attributes_t_AudioAttributesInternal)); aidl.streams = VALUE_OR_RETURN( - convertContainer>(legacy.getStreamTypes(), + convertContainer>(legacy.getStreamTypes(), legacy2aidl_audio_stream_type_t_AudioStreamType)); return aidl; } diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp index 504e4f874646f9e1fd10d5f9e0927d6c57095689..292d92f475c29f61b5e4d3a87b1f309bc23341df 100644 --- a/media/libaudioclient/IAudioFlinger.cpp +++ b/media/libaudioclient/IAudioFlinger.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "IAudioFlinger" //#define LOG_NDEBUG 0 + #include #include @@ -30,6 +31,13 @@ namespace android { using aidl_utils::statusTFromBinderStatus; using binder::Status; +using media::audio::common::AudioChannelLayout; +using media::audio::common::AudioFormatDescription; +using media::audio::common::AudioMMapPolicyInfo; +using media::audio::common::AudioMMapPolicyType; +using media::audio::common::AudioMode; +using media::audio::common::AudioStreamType; +using media::audio::common::AudioUuid; #define MAX_ITEMS_PER_LIST 1024 @@ -40,12 +48,6 @@ using binder::Status; std::move(_tmp.value()); \ }) -#define RETURN_STATUS_IF_ERROR(x) \ - { \ - auto _tmp = (x); \ - if (_tmp != OK) return _tmp; \ - } - #define RETURN_BINDER_IF_ERROR(x) \ { \ auto _tmp = (x); \ @@ -55,7 +57,9 @@ using binder::Status; ConversionResult IAudioFlinger::CreateTrackInput::toAidl() const { media::CreateTrackRequest aidl; aidl.attr = VALUE_OR_RETURN(legacy2aidl_audio_attributes_t_AudioAttributesInternal(attr)); - aidl.config = VALUE_OR_RETURN(legacy2aidl_audio_config_t_AudioConfig(config)); + // Do not be mislead by 'Input'--this is an input to 'createTrack', which creates output tracks. + aidl.config = VALUE_OR_RETURN(legacy2aidl_audio_config_t_AudioConfig( + config, false /*isInput*/)); aidl.clientInfo = VALUE_OR_RETURN(legacy2aidl_AudioClient_AudioClient(clientInfo)); aidl.sharedBuffer = VALUE_OR_RETURN(legacy2aidl_NullableIMemory_SharedFileRegion(sharedBuffer)); aidl.notificationsPerBuffer = VALUE_OR_RETURN(convertIntegral(notificationsPerBuffer)); @@ -74,7 +78,9 @@ ConversionResult IAudioFlinger::CreateTrackInput::fromAidl(const media::CreateTrackRequest& aidl) { IAudioFlinger::CreateTrackInput legacy; legacy.attr = VALUE_OR_RETURN(aidl2legacy_AudioAttributesInternal_audio_attributes_t(aidl.attr)); - legacy.config = VALUE_OR_RETURN(aidl2legacy_AudioConfig_audio_config_t(aidl.config)); + // Do not be mislead by 'Input'--this is an input to 'createTrack', which creates output tracks. + legacy.config = VALUE_OR_RETURN( + aidl2legacy_AudioConfig_audio_config_t(aidl.config, false /*isInput*/)); legacy.clientInfo = VALUE_OR_RETURN(aidl2legacy_AudioClient_AudioClient(aidl.clientInfo)); legacy.sharedBuffer = VALUE_OR_RETURN(aidl2legacy_NullableSharedFileRegion_IMemory(aidl.sharedBuffer)); legacy.notificationsPerBuffer = VALUE_OR_RETURN( @@ -139,7 +145,8 @@ ConversionResult IAudioFlinger::CreateRecordInput::toAidl() const { media::CreateRecordRequest aidl; aidl.attr = VALUE_OR_RETURN(legacy2aidl_audio_attributes_t_AudioAttributesInternal(attr)); - aidl.config = VALUE_OR_RETURN(legacy2aidl_audio_config_base_t_AudioConfigBase(config)); + aidl.config = VALUE_OR_RETURN( + legacy2aidl_audio_config_base_t_AudioConfigBase(config, true /*isInput*/)); aidl.clientInfo = VALUE_OR_RETURN(legacy2aidl_AudioClient_AudioClient(clientInfo)); aidl.riid = VALUE_OR_RETURN(legacy2aidl_audio_unique_id_t_int32_t(riid)); aidl.maxSharedAudioHistoryMs = VALUE_OR_RETURN( @@ -159,7 +166,8 @@ IAudioFlinger::CreateRecordInput::fromAidl( IAudioFlinger::CreateRecordInput legacy; legacy.attr = VALUE_OR_RETURN( aidl2legacy_AudioAttributesInternal_audio_attributes_t(aidl.attr)); - legacy.config = VALUE_OR_RETURN(aidl2legacy_AudioConfigBase_audio_config_base_t(aidl.config)); + legacy.config = VALUE_OR_RETURN( + aidl2legacy_AudioConfigBase_audio_config_base_t(aidl.config, true /*isInput*/)); legacy.clientInfo = VALUE_OR_RETURN(aidl2legacy_AudioClient_AudioClient(aidl.clientInfo)); legacy.riid = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_unique_id_t(aidl.riid)); legacy.maxSharedAudioHistoryMs = VALUE_OR_RETURN( @@ -189,6 +197,8 @@ IAudioFlinger::CreateRecordOutput::toAidl() const { aidl.buffers = VALUE_OR_RETURN(legacy2aidl_NullableIMemory_SharedFileRegion(buffers)); aidl.portId = VALUE_OR_RETURN(legacy2aidl_audio_port_handle_t_int32_t(portId)); aidl.audioRecord = audioRecord; + aidl.serverConfig = VALUE_OR_RETURN( + legacy2aidl_audio_config_base_t_AudioConfigBase(serverConfig, true /*isInput*/)); return aidl; } @@ -209,6 +219,8 @@ IAudioFlinger::CreateRecordOutput::fromAidl( legacy.buffers = VALUE_OR_RETURN(aidl2legacy_NullableSharedFileRegion_IMemory(aidl.buffers)); legacy.portId = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_port_handle_t(aidl.portId)); legacy.audioRecord = aidl.audioRecord; + legacy.serverConfig = VALUE_OR_RETURN( + aidl2legacy_AudioConfigBase_audio_config_base_t(aidl.serverConfig, true /*isInput*/)); return legacy; } @@ -242,9 +254,9 @@ uint32_t AudioFlingerClientAdapter::sampleRate(audio_io_handle_t ioHandle) const audio_format_t AudioFlingerClientAdapter::format(audio_io_handle_t output) const { auto result = [&]() -> ConversionResult { int32_t outputAidl = VALUE_OR_RETURN(legacy2aidl_audio_io_handle_t_int32_t(output)); - media::audio::common::AudioFormat aidlRet; + AudioFormatDescription aidlRet; RETURN_IF_ERROR(statusTFromBinderStatus(mDelegate->format(outputAidl, &aidlRet))); - return aidl2legacy_AudioFormat_audio_format_t(aidlRet); + return aidl2legacy_AudioFormatDescription_audio_format_t(aidlRet); }(); return result.value_or(AUDIO_FORMAT_INVALID); } @@ -309,14 +321,14 @@ status_t AudioFlingerClientAdapter::getMasterBalance(float* balance) const{ status_t AudioFlingerClientAdapter::setStreamVolume(audio_stream_type_t stream, float value, audio_io_handle_t output) { - media::AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS( + AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_stream_type_t_AudioStreamType(stream)); int32_t outputAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(output)); return statusTFromBinderStatus(mDelegate->setStreamVolume(streamAidl, value, outputAidl)); } status_t AudioFlingerClientAdapter::setStreamMute(audio_stream_type_t stream, bool muted) { - media::AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS( + AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_stream_type_t_AudioStreamType(stream)); return statusTFromBinderStatus(mDelegate->setStreamMute(streamAidl, muted)); } @@ -324,7 +336,7 @@ status_t AudioFlingerClientAdapter::setStreamMute(audio_stream_type_t stream, bo float AudioFlingerClientAdapter::streamVolume(audio_stream_type_t stream, audio_io_handle_t output) const { auto result = [&]() -> ConversionResult { - media::AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS( + AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_stream_type_t_AudioStreamType(stream)); int32_t outputAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(output)); float aidlRet; @@ -338,7 +350,7 @@ float AudioFlingerClientAdapter::streamVolume(audio_stream_type_t stream, bool AudioFlingerClientAdapter::streamMute(audio_stream_type_t stream) const { auto result = [&]() -> ConversionResult { - media::AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS( + AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_stream_type_t_AudioStreamType(stream)); bool aidlRet; RETURN_IF_ERROR(statusTFromBinderStatus( @@ -350,7 +362,7 @@ bool AudioFlingerClientAdapter::streamMute(audio_stream_type_t stream) const { } status_t AudioFlingerClientAdapter::setMode(audio_mode_t mode) { - media::AudioMode modeAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_mode_t_AudioMode(mode)); + AudioMode modeAidl = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_mode_t_AudioMode(mode)); return statusTFromBinderStatus(mDelegate->setMode(modeAidl)); } @@ -410,10 +422,10 @@ size_t AudioFlingerClientAdapter::getInputBufferSize(uint32_t sampleRate, audio_ audio_channel_mask_t channelMask) const { auto result = [&]() -> ConversionResult { int32_t sampleRateAidl = VALUE_OR_RETURN(convertIntegral(sampleRate)); - media::audio::common::AudioFormat formatAidl = VALUE_OR_RETURN( - legacy2aidl_audio_format_t_AudioFormat(format)); - int32_t channelMaskAidl = VALUE_OR_RETURN( - legacy2aidl_audio_channel_mask_t_int32_t(channelMask)); + AudioFormatDescription formatAidl = VALUE_OR_RETURN( + legacy2aidl_audio_format_t_AudioFormatDescription(format)); + AudioChannelLayout channelMaskAidl = VALUE_OR_RETURN( + legacy2aidl_audio_channel_mask_t_AudioChannelLayout(channelMask, true /*isInput*/)); int64_t aidlRet; RETURN_IF_ERROR(statusTFromBinderStatus( mDelegate->getInputBufferSize(sampleRateAidl, formatAidl, channelMaskAidl, @@ -469,7 +481,7 @@ status_t AudioFlingerClientAdapter::closeInput(audio_io_handle_t input) { } status_t AudioFlingerClientAdapter::invalidateStream(audio_stream_type_t stream) { - media::AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS( + AudioStreamType streamAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_stream_type_t_AudioStreamType(stream)); return statusTFromBinderStatus(mDelegate->invalidateStream(streamAidl)); } @@ -568,9 +580,9 @@ status_t AudioFlingerClientAdapter::getEffectDescriptor(const effect_uuid_t* pEf const effect_uuid_t* pTypeUUID, uint32_t preferredTypeFlag, effect_descriptor_t* pDescriptor) const { - media::AudioUuid effectUuidAidl = VALUE_OR_RETURN_STATUS( + AudioUuid effectUuidAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_uuid_t_AudioUuid(*pEffectUUID)); - media::AudioUuid typeUuidAidl = VALUE_OR_RETURN_STATUS( + AudioUuid typeUuidAidl = VALUE_OR_RETURN_STATUS( legacy2aidl_audio_uuid_t_AudioUuid(*pTypeUUID)); int32_t preferredTypeFlagAidl = VALUE_OR_RETURN_STATUS( convertReinterpret(preferredTypeFlag)); @@ -765,6 +777,32 @@ status_t AudioFlingerClientAdapter::updateSecondaryOutputs( return statusTFromBinderStatus(mDelegate->updateSecondaryOutputs(trackSecondaryOutputInfos)); } +status_t AudioFlingerClientAdapter::getMmapPolicyInfos( + AudioMMapPolicyType policyType, std::vector *policyInfos) { + return statusTFromBinderStatus(mDelegate->getMmapPolicyInfos(policyType, policyInfos)); +} + +int32_t AudioFlingerClientAdapter::getAAudioMixerBurstCount() { + auto result = [&]() -> ConversionResult { + int32_t aidlRet; + RETURN_IF_ERROR(statusTFromBinderStatus(mDelegate->getAAudioMixerBurstCount(&aidlRet))); + return convertIntegral(aidlRet); + }(); + // Failure is ignored. + return result.value_or(0); +} + +int32_t AudioFlingerClientAdapter::getAAudioHardwareBurstMinUsec() { + auto result = [&]() -> ConversionResult { + int32_t aidlRet; + RETURN_IF_ERROR(statusTFromBinderStatus( + mDelegate->getAAudioHardwareBurstMinUsec(&aidlRet))); + return convertIntegral(aidlRet); + }(); + // Failure is ignored. + return result.value_or(0); +} + status_t AudioFlingerClientAdapter::setDeviceConnectedState( const struct audio_port_v7 *port, bool connected) { media::AudioPort aidlPort = VALUE_OR_RETURN_STATUS( @@ -816,11 +854,11 @@ Status AudioFlingerServerAdapter::sampleRate(int32_t ioHandle, int32_t* _aidl_re } Status AudioFlingerServerAdapter::format(int32_t output, - media::audio::common::AudioFormat* _aidl_return) { + AudioFormatDescription* _aidl_return) { audio_io_handle_t outputLegacy = VALUE_OR_RETURN_BINDER( aidl2legacy_int32_t_audio_io_handle_t(output)); *_aidl_return = VALUE_OR_RETURN_BINDER( - legacy2aidl_audio_format_t_AudioFormat(mDelegate->format(outputLegacy))); + legacy2aidl_audio_format_t_AudioFormatDescription(mDelegate->format(outputLegacy))); return Status::ok(); } @@ -866,7 +904,7 @@ Status AudioFlingerServerAdapter::getMasterBalance(float* _aidl_return) { return Status::fromStatusT(mDelegate->getMasterBalance(_aidl_return)); } -Status AudioFlingerServerAdapter::setStreamVolume(media::AudioStreamType stream, float value, +Status AudioFlingerServerAdapter::setStreamVolume(AudioStreamType stream, float value, int32_t output) { audio_stream_type_t streamLegacy = VALUE_OR_RETURN_BINDER( aidl2legacy_AudioStreamType_audio_stream_type_t(stream)); @@ -875,13 +913,13 @@ Status AudioFlingerServerAdapter::setStreamVolume(media::AudioStreamType stream, return Status::fromStatusT(mDelegate->setStreamVolume(streamLegacy, value, outputLegacy)); } -Status AudioFlingerServerAdapter::setStreamMute(media::AudioStreamType stream, bool muted) { +Status AudioFlingerServerAdapter::setStreamMute(AudioStreamType stream, bool muted) { audio_stream_type_t streamLegacy = VALUE_OR_RETURN_BINDER( aidl2legacy_AudioStreamType_audio_stream_type_t(stream)); return Status::fromStatusT(mDelegate->setStreamMute(streamLegacy, muted)); } -Status AudioFlingerServerAdapter::streamVolume(media::AudioStreamType stream, int32_t output, +Status AudioFlingerServerAdapter::streamVolume(AudioStreamType stream, int32_t output, float* _aidl_return) { audio_stream_type_t streamLegacy = VALUE_OR_RETURN_BINDER( aidl2legacy_AudioStreamType_audio_stream_type_t(stream)); @@ -891,14 +929,14 @@ Status AudioFlingerServerAdapter::streamVolume(media::AudioStreamType stream, in return Status::ok(); } -Status AudioFlingerServerAdapter::streamMute(media::AudioStreamType stream, bool* _aidl_return) { +Status AudioFlingerServerAdapter::streamMute(AudioStreamType stream, bool* _aidl_return) { audio_stream_type_t streamLegacy = VALUE_OR_RETURN_BINDER( aidl2legacy_AudioStreamType_audio_stream_type_t(stream)); *_aidl_return = mDelegate->streamMute(streamLegacy); return Status::ok(); } -Status AudioFlingerServerAdapter::setMode(media::AudioMode mode) { +Status AudioFlingerServerAdapter::setMode(AudioMode mode) { audio_mode_t modeLegacy = VALUE_OR_RETURN_BINDER(aidl2legacy_AudioMode_audio_mode_t(mode)); return Status::fromStatusT(mDelegate->setMode(modeLegacy)); } @@ -944,13 +982,14 @@ Status AudioFlingerServerAdapter::registerClient(const sp(sampleRate)); audio_format_t formatLegacy = VALUE_OR_RETURN_BINDER( - aidl2legacy_AudioFormat_audio_format_t(format)); + aidl2legacy_AudioFormatDescription_audio_format_t(format)); audio_channel_mask_t channelMaskLegacy = VALUE_OR_RETURN_BINDER( - aidl2legacy_int32_t_audio_channel_mask_t(channelMask)); + aidl2legacy_AudioChannelLayout_audio_channel_mask_t(channelMask, true /*isInput*/)); size_t size = mDelegate->getInputBufferSize(sampleRateLegacy, formatLegacy, channelMaskLegacy); *_aidl_return = VALUE_OR_RETURN_BINDER(convertIntegral(size)); return Status::ok(); @@ -1001,7 +1040,7 @@ Status AudioFlingerServerAdapter::closeInput(int32_t input) { return Status::fromStatusT(mDelegate->closeInput(inputLegacy)); } -Status AudioFlingerServerAdapter::invalidateStream(media::AudioStreamType stream) { +Status AudioFlingerServerAdapter::invalidateStream(AudioStreamType stream) { audio_stream_type_t streamLegacy = VALUE_OR_RETURN_BINDER( aidl2legacy_AudioStreamType_audio_stream_type_t(stream)); return Status::fromStatusT(mDelegate->invalidateStream(streamLegacy)); @@ -1076,8 +1115,8 @@ AudioFlingerServerAdapter::queryEffect(int32_t index, media::EffectDescriptor* _ return Status::ok(); } -Status AudioFlingerServerAdapter::getEffectDescriptor(const media::AudioUuid& effectUUID, - const media::AudioUuid& typeUUID, +Status AudioFlingerServerAdapter::getEffectDescriptor(const AudioUuid& effectUUID, + const AudioUuid& typeUUID, int32_t preferredTypeFlag, media::EffectDescriptor* _aidl_return) { effect_uuid_t effectUuidLegacy = VALUE_OR_RETURN_BINDER( @@ -1242,6 +1281,23 @@ Status AudioFlingerServerAdapter::updateSecondaryOutputs( return Status::fromStatusT(mDelegate->updateSecondaryOutputs(trackSecondaryOutputs)); } +Status AudioFlingerServerAdapter::getMmapPolicyInfos( + AudioMMapPolicyType policyType, std::vector *_aidl_return) { + return Status::fromStatusT(mDelegate->getMmapPolicyInfos(policyType, _aidl_return)); +} + +Status AudioFlingerServerAdapter::getAAudioMixerBurstCount(int32_t* _aidl_return) { + *_aidl_return = VALUE_OR_RETURN_BINDER( + convertIntegral(mDelegate->getAAudioMixerBurstCount())); + return Status::ok(); +} + +Status AudioFlingerServerAdapter::getAAudioHardwareBurstMinUsec(int32_t* _aidl_return) { + *_aidl_return = VALUE_OR_RETURN_BINDER( + convertIntegral(mDelegate->getAAudioHardwareBurstMinUsec())); + return Status::ok(); +} + Status AudioFlingerServerAdapter::setDeviceConnectedState( const media::AudioPort& port, bool connected) { audio_port_v7 portLegacy = VALUE_OR_RETURN_BINDER(aidl2legacy_AudioPort_audio_port_v7(port)); diff --git a/media/libaudioclient/PolicyAidlConversion.cpp b/media/libaudioclient/PolicyAidlConversion.cpp index dcfde8bab3c0aa57925c9f2a668493fdffc83590..520f09ceeab2fe1373187f9a2b5c9edbacff9e18 100644 --- a/media/libaudioclient/PolicyAidlConversion.cpp +++ b/media/libaudioclient/PolicyAidlConversion.cpp @@ -25,6 +25,7 @@ namespace android { using base::unexpected; +using media::audio::common::AudioDeviceAddress; ConversionResult aidl2legacy_AudioMixType_uint32_t(media::AudioMixType aidl) { @@ -142,7 +143,7 @@ aidl2legacy_AudioMixMatchCriterionValue( case media::AudioMixMatchCriterionValue::source: legacy.mSource = VALUE_OR_RETURN( - aidl2legacy_AudioSourceType_audio_source_t(UNION_GET(aidl, source).value())); + aidl2legacy_AudioSource_audio_source_t(UNION_GET(aidl, source).value())); *rule |= RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET; return legacy; @@ -174,7 +175,7 @@ legacy2aidl_AudioMixMatchCriterionValue( case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET: UNION_SET(aidl, source, - VALUE_OR_RETURN(legacy2aidl_audio_source_t_AudioSourceType(legacy.mSource))); + VALUE_OR_RETURN(legacy2aidl_audio_source_t_AudioSource(legacy.mSource))); break; case RULE_MATCH_UID: @@ -222,11 +223,14 @@ aidl2legacy_AudioMix(const media::AudioMix& aidl) { std::back_inserter(legacy.mCriteria), aidl2legacy_AudioMixMatchCriterion)); legacy.mMixType = VALUE_OR_RETURN(aidl2legacy_AudioMixType_uint32_t(aidl.mixType)); - legacy.mFormat = VALUE_OR_RETURN(aidl2legacy_AudioConfig_audio_config_t(aidl.format)); + // See 'convertAudioMixToNative' in 'android_media_AudioSystem.cpp' -- only + // an output mask is expected here. + legacy.mFormat = VALUE_OR_RETURN(aidl2legacy_AudioConfig_audio_config_t( + aidl.format, false /*isInput*/)); legacy.mRouteFlags = VALUE_OR_RETURN( aidl2legacy_AudioMixRouteFlag_uint32_t_mask(aidl.routeFlags)); - legacy.mDeviceType = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_devices_t(aidl.device.type)); - legacy.mDeviceAddress = VALUE_OR_RETURN(aidl2legacy_string_view_String8(aidl.device.address)); + RETURN_IF_ERROR(aidl2legacy_AudioDevice_audio_device( + aidl.device, &legacy.mDeviceType, &legacy.mDeviceAddress)); legacy.mCbFlags = VALUE_OR_RETURN(aidl2legacy_AudioMixCallbackFlag_uint32_t_mask(aidl.cbFlags)); legacy.mAllowPrivilegedMediaPlaybackCapture = aidl.allowPrivilegedMediaPlaybackCapture; legacy.mVoiceCommunicationCaptureAllowed = aidl.voiceCommunicationCaptureAllowed; @@ -241,11 +245,15 @@ legacy2aidl_AudioMix(const AudioMix& legacy) { legacy.mCriteria, legacy2aidl_AudioMixMatchCriterion)); aidl.mixType = VALUE_OR_RETURN(legacy2aidl_uint32_t_AudioMixType(legacy.mMixType)); - aidl.format = VALUE_OR_RETURN(legacy2aidl_audio_config_t_AudioConfig(legacy.mFormat)); + // See 'convertAudioMixToNative' in 'android_media_AudioSystem.cpp' -- only + // an output mask is expected here. + aidl.format = VALUE_OR_RETURN(legacy2aidl_audio_config_t_AudioConfig( + legacy.mFormat, false /*isInput*/)); aidl.routeFlags = VALUE_OR_RETURN( legacy2aidl_uint32_t_AudioMixRouteFlag_mask(legacy.mRouteFlags)); - aidl.device.type = VALUE_OR_RETURN(legacy2aidl_audio_devices_t_int32_t(legacy.mDeviceType)); - aidl.device.address = VALUE_OR_RETURN(legacy2aidl_String8_string(legacy.mDeviceAddress)); + aidl.device = VALUE_OR_RETURN( + legacy2aidl_audio_device_AudioDevice( + legacy.mDeviceType, legacy.mDeviceAddress)); aidl.cbFlags = VALUE_OR_RETURN(legacy2aidl_uint32_t_AudioMixCallbackFlag_mask(legacy.mCbFlags)); aidl.allowPrivilegedMediaPlaybackCapture = legacy.mAllowPrivilegedMediaPlaybackCapture; aidl.voiceCommunicationCaptureAllowed = legacy.mVoiceCommunicationCaptureAllowed; diff --git a/media/libaudioclient/TEST_MAPPING b/media/libaudioclient/TEST_MAPPING new file mode 100644 index 0000000000000000000000000000000000000000..3751f80d4900ce8a438a5639617d90b735097103 --- /dev/null +++ b/media/libaudioclient/TEST_MAPPING @@ -0,0 +1,15 @@ +{ + "presubmit": [ + { + "name": "audio_aidl_conversion_tests" + }, + { + "name": "CtsNativeMediaAAudioTestCases", + "options" : [ + { + "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic.*" + } + ] + } + ] +} diff --git a/media/libaudioclient/ToneGenerator.cpp b/media/libaudioclient/ToneGenerator.cpp index e5e8496753b14a36e4960c5ac0e7b7c285b477b3..f968a4bc37971638d66decdf9d80a4102a0b44ea 100644 --- a/media/libaudioclient/ToneGenerator.cpp +++ b/media/libaudioclient/ToneGenerator.cpp @@ -17,6 +17,7 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "ToneGenerator" +#include #include #include @@ -866,6 +867,11 @@ const ToneGenerator::ToneDescriptor ToneGenerator::sToneDescriptors[] = { { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, .repeatCnt = ToneGenerator::TONEGEN_INF, .repeatSegment = 0 }, // TONE_TW_RINGTONE + { .segments = { { .duration = 200, .waveFreq = { 400, 0 }, 0, 0 }, + { .duration = 3000, .waveFreq = { 0 }, 0, 0 }, + { .duration = 0 , .waveFreq = { 0 }, 0, 0}}, + .repeatCnt = 3, + .repeatSegment = 0 }, // TONE_NZ_CALL_WAITING }; // Used by ToneGenerator::getToneForRegion() to convert user specified supervisory tone type @@ -960,6 +966,16 @@ const unsigned char /*tone_type*/ ToneGenerator::sToneMappingTable[NUM_REGIONS-1 TONE_SUP_ERROR, // TONE_SUP_ERROR TONE_SUP_CALL_WAITING, // TONE_SUP_CALL_WAITING TONE_TW_RINGTONE // TONE_SUP_RINGTONE + }, + { // NEW ZEALAND + TONE_JAPAN_DIAL, // TONE_SUP_DIAL + TONE_JAPAN_BUSY, // TONE_SUP_BUSY + TONE_SUP_CONGESTION, // TONE_SUP_CONGESTION + TONE_SUP_RADIO_ACK, // TONE_SUP_RADIO_ACK + TONE_SUP_RADIO_NOTAVAIL, // TONE_SUP_RADIO_NOTAVAIL + TONE_SUP_ERROR, // TONE_SUP_ERROR + TONE_NZ_CALL_WAITING, // TONE_SUP_CALL_WAITING + TONE_GB_RINGTONE // TONE_SUP_RINGTONE } }; @@ -977,7 +993,7 @@ const unsigned char /*tone_type*/ ToneGenerator::sToneMappingTable[NUM_REGIONS-1 // Method: ToneGenerator::ToneGenerator() // // Description: Constructor. Initializes the tone sequencer, intantiates required sine wave -// generators, instantiates output audio track. +// generators, does not initialize output audio track. // // Input: // streamType: Type of stream used for tone playback @@ -1037,10 +1053,29 @@ ToneGenerator::ToneGenerator(audio_stream_type_t streamType, float volume, bool mRegion = INDIA; } else if (strstr(value, "tw") != NULL) { mRegion = TAIWAN; + } else if (strstr(value, "nz") != NULL) { + mRegion = NZ; } else { mRegion = CEPT; } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: ToneGenerator::onFirstRef() +// +// Description: Called upon first RefBase reference. Initializes audio track +// with weak pointer to self as the registered callback. +// Input: +// none +// +// Output: +// none +// +//////////////////////////////////////////////////////////////////////////////// + +void ToneGenerator::onFirstRef() { if (initAudioTrack()) { ALOGV("ToneGenerator INIT OK, time: %d", (unsigned int)(systemTime()/1000000)); } else { @@ -1048,9 +1083,6 @@ ToneGenerator::ToneGenerator(audio_stream_type_t streamType, float volume, bool } } - - - //////////////////////////////////////////////////////////////////////////////// // // Method: ToneGenerator::~ToneGenerator() @@ -1215,7 +1247,8 @@ void ToneGenerator::stopTone() { sec = sec * 1000 + nsec / 1000000; // duration in milliseconds mMaxSmp = (unsigned int)(((int64_t)sec * mSamplingRate) / 1000); } - ALOGV("stopTone() forcing mMaxSmp to %d, total for far %d", mMaxSmp, mTotalSmp); + ALOGV("stopTone() forcing mMaxSmp to %d, total for far %" PRIu64, mMaxSmp, + mTotalSmp); } else { mState = TONE_STOPPING; } @@ -1282,8 +1315,7 @@ bool ToneGenerator::initAudioTrack() { AUDIO_CHANNEL_OUT_MONO, frameCount, AUDIO_OUTPUT_FLAG_FAST, - audioCallback, - this, // user + wp::fromExisting(this), 0, // notificationFrames 0, // sharedBuffer mThreadCanCallJava, @@ -1308,50 +1340,47 @@ bool ToneGenerator::initAudioTrack() { //////////////////////////////////////////////////////////////////////////////// // -// Method: ToneGenerator::audioCallback() +// Method: ToneGenerator::onMoreData() // // Description: AudioTrack callback implementation. Generates a block of // PCM samples // and manages tone generator sequencer: tones pulses, tone duration... // // Input: -// user reference (pointer to our ToneGenerator) -// info audio buffer descriptor +// buffer An buffer object containing a pointer which we will fill with +// buffer.size() bytes. // // Output: -// returned value: always true. +// The number of bytes we successfully wrote. // //////////////////////////////////////////////////////////////////////////////// -void ToneGenerator::audioCallback(int event, void* user, void *info) { - - if (event != AudioTrack::EVENT_MORE_DATA) return; - - AudioTrack::Buffer *buffer = static_cast(info); - ToneGenerator *lpToneGen = static_cast(user); - int16_t *lpOut = buffer->i16; - unsigned int lNumSmp = buffer->size/sizeof(int16_t); - const ToneDescriptor *lpToneDesc = lpToneGen->mpToneDesc; - - if (buffer->size == 0) return; +size_t ToneGenerator::onMoreData(const AudioTrack::Buffer& buffer) { + int16_t *lpOut = reinterpret_cast(buffer.data()); + uint32_t lNumSmp = (buffer.size() / sizeof(int16_t) < UINT32_MAX) ? + buffer.size() / sizeof(int16_t) : UINT32_MAX; + if (buffer.size() == 0) return 0; + // We will write to the entire buffer unless we are stopped, then we return + // 0 at loop end + size_t bytesWritten = lNumSmp * sizeof(int16_t); // Clear output buffer: WaveGenerator accumulates into lpOut buffer - memset(lpOut, 0, buffer->size); + memset(lpOut, 0, buffer.size()); while (lNumSmp) { - unsigned int lReqSmp = lNumSmp < lpToneGen->mProcessSize*2 ? lNumSmp : lpToneGen->mProcessSize; + unsigned int lReqSmp = lNumSmp < mProcessSize*2 ? lNumSmp : mProcessSize; unsigned int lGenSmp; unsigned int lWaveCmd = WaveGenerator::WAVEGEN_CONT; bool lSignal = false; - lpToneGen->mLock.lock(); + mLock.lock(); // Update pcm frame count and end time (current time at the end of this process) - lpToneGen->mTotalSmp += lReqSmp; + mTotalSmp += lReqSmp; // Update tone gen state machine and select wave gen command - switch (lpToneGen->mState) { + switch (mState) { case TONE_PLAYING: lWaveCmd = WaveGenerator::WAVEGEN_CONT; break; @@ -1365,7 +1394,7 @@ void ToneGenerator::audioCallback(int event, void* user, void *info) { ALOGV("Stop/restart Cbk"); lWaveCmd = WaveGenerator::WAVEGEN_STOP; - lpToneGen->mNextSegSmp = TONEGEN_INF; // forced to skip state machine management below + mNextSegSmp = TONEGEN_INF; // forced to skip state machine management below break; case TONE_STOPPED: ALOGV("Stopped Cbk"); @@ -1376,20 +1405,20 @@ void ToneGenerator::audioCallback(int event, void* user, void *info) { } // Exit if tone sequence is over - if (lpToneDesc->segments[lpToneGen->mCurSegment].duration == 0 || - lpToneGen->mTotalSmp > lpToneGen->mMaxSmp) { - if (lpToneGen->mState == TONE_PLAYING) { - lpToneGen->mState = TONE_STOPPING; + if (mpToneDesc->segments[mCurSegment].duration == 0 || + mTotalSmp > mMaxSmp) { + if (mState == TONE_PLAYING) { + mState = TONE_STOPPING; } - if (lpToneDesc->segments[lpToneGen->mCurSegment].duration == 0) { + if (mpToneDesc->segments[mCurSegment].duration == 0) { goto audioCallback_EndLoop; } // fade out before stopping if maximum duration reached lWaveCmd = WaveGenerator::WAVEGEN_STOP; - lpToneGen->mNextSegSmp = TONEGEN_INF; // forced to skip state machine management below + mNextSegSmp = TONEGEN_INF; // forced to skip state machine management below } - if (lpToneGen->mTotalSmp > lpToneGen->mNextSegSmp) { + if (mTotalSmp > mNextSegSmp && mNextSegSmp != TONEGEN_INF) { // Time to go to next sequence segment ALOGV("End Segment, time: %d", (unsigned int)(systemTime()/1000000)); @@ -1397,61 +1426,61 @@ void ToneGenerator::audioCallback(int event, void* user, void *info) { lGenSmp = lReqSmp; // If segment, ON -> OFF transition : ramp volume down - if (lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[0] != 0) { + if (mpToneDesc->segments[mCurSegment].waveFreq[0] != 0) { lWaveCmd = WaveGenerator::WAVEGEN_STOP; unsigned int lFreqIdx = 0; - uint16_t lFrequency = lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[lFreqIdx]; + uint16_t lFrequency = mpToneDesc->segments[mCurSegment].waveFreq[lFreqIdx]; while (lFrequency != 0) { - WaveGenerator *lpWaveGen = lpToneGen->mWaveGens.valueFor(lFrequency); + WaveGenerator *lpWaveGen = mWaveGens.valueFor(lFrequency); lpWaveGen->getSamples(lpOut, lGenSmp, lWaveCmd); - lFrequency = lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[++lFreqIdx]; + lFrequency = mpToneDesc->segments[mCurSegment].waveFreq[++lFreqIdx]; } ALOGV("ON->OFF, lGenSmp: %d, lReqSmp: %d", lGenSmp, lReqSmp); } // check if we need to loop and loop for the reqd times - if (lpToneDesc->segments[lpToneGen->mCurSegment].loopCnt) { - if (lpToneGen->mLoopCounter < lpToneDesc->segments[lpToneGen->mCurSegment].loopCnt) { + if (mpToneDesc->segments[mCurSegment].loopCnt) { + if (mLoopCounter < mpToneDesc->segments[mCurSegment].loopCnt) { ALOGV ("in if loop loopCnt(%d) loopctr(%d), CurSeg(%d)", - lpToneDesc->segments[lpToneGen->mCurSegment].loopCnt, - lpToneGen->mLoopCounter, - lpToneGen->mCurSegment); - lpToneGen->mCurSegment = lpToneDesc->segments[lpToneGen->mCurSegment].loopIndx; - ++lpToneGen->mLoopCounter; + mpToneDesc->segments[mCurSegment].loopCnt, + mLoopCounter, + mCurSegment); + mCurSegment = mpToneDesc->segments[mCurSegment].loopIndx; + ++mLoopCounter; } else { // completed loop. go to next segment - lpToneGen->mLoopCounter = 0; - lpToneGen->mCurSegment++; + mLoopCounter = 0; + mCurSegment++; ALOGV ("in else loop loopCnt(%d) loopctr(%d), CurSeg(%d)", - lpToneDesc->segments[lpToneGen->mCurSegment].loopCnt, - lpToneGen->mLoopCounter, - lpToneGen->mCurSegment); + mpToneDesc->segments[mCurSegment].loopCnt, + mLoopCounter, + mCurSegment); } } else { - lpToneGen->mCurSegment++; + mCurSegment++; ALOGV ("Goto next seg loopCnt(%d) loopctr(%d), CurSeg(%d)", - lpToneDesc->segments[lpToneGen->mCurSegment].loopCnt, - lpToneGen->mLoopCounter, - lpToneGen->mCurSegment); + mpToneDesc->segments[mCurSegment].loopCnt, + mLoopCounter, + mCurSegment); } // Handle loop if last segment reached - if (lpToneDesc->segments[lpToneGen->mCurSegment].duration == 0) { - ALOGV("Last Seg: %d", lpToneGen->mCurSegment); + if (mpToneDesc->segments[mCurSegment].duration == 0) { + ALOGV("Last Seg: %d", mCurSegment); // Pre increment loop count and restart if total count not reached. Stop sequence otherwise - if (++lpToneGen->mCurCount <= lpToneDesc->repeatCnt) { - ALOGV("Repeating Count: %d", lpToneGen->mCurCount); + if (++mCurCount <= mpToneDesc->repeatCnt) { + ALOGV("Repeating Count: %d", mCurCount); - lpToneGen->mCurSegment = lpToneDesc->repeatSegment; - if (lpToneDesc->segments[lpToneDesc->repeatSegment].waveFreq[0] != 0) { + mCurSegment = mpToneDesc->repeatSegment; + if (mpToneDesc->segments[mpToneDesc->repeatSegment].waveFreq[0] != 0) { lWaveCmd = WaveGenerator::WAVEGEN_START; } - ALOGV("New segment %d, Next Time: %lld", lpToneGen->mCurSegment, - ((long long)(lpToneGen->mNextSegSmp)*1000)/lpToneGen->mSamplingRate); + ALOGV("New segment %d, Next Time: %lld", mCurSegment, + ((long long)(mNextSegSmp)*1000)/mSamplingRate); } else { @@ -1459,10 +1488,10 @@ void ToneGenerator::audioCallback(int event, void* user, void *info) { ALOGV("End repeat, time: %d", (unsigned int)(systemTime()/1000000)); } } else { - ALOGV("New segment %d, Next Time: %lld", lpToneGen->mCurSegment, - ((long long)(lpToneGen->mNextSegSmp)*1000)/lpToneGen->mSamplingRate); + ALOGV("New segment %d, Next Time: %lld", mCurSegment, + ((long long)(mNextSegSmp)*1000)/mSamplingRate); - if (lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[0] != 0) { + if (mpToneDesc->segments[mCurSegment].waveFreq[0] != 0) { // If next segment is not silent, OFF -> ON transition : reset wave generator lWaveCmd = WaveGenerator::WAVEGEN_START; @@ -1472,13 +1501,16 @@ void ToneGenerator::audioCallback(int event, void* user, void *info) { } } - // Update next segment transition position. No harm to do it also for last segment as lpToneGen->mNextSegSmp won't be used any more - lpToneGen->mNextSegSmp - += (lpToneDesc->segments[lpToneGen->mCurSegment].duration * lpToneGen->mSamplingRate) / 1000; + // Update next segment transition position. No harm to do it also for last segment as + // mNextSegSmp won't be used any more. + // Handle 32 bit wraparound gracefully. + const uint64_t res = static_cast(mNextSegSmp) + + (mpToneDesc->segments[mCurSegment].duration * mSamplingRate) / 1000; + mNextSegSmp = static_cast(std::min(TONEGEN_INF, res)); } else { // Inside a segment keep tone ON or OFF - if (lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[0] == 0) { + if (mpToneDesc->segments[mCurSegment].waveFreq[0] == 0) { lGenSmp = 0; // If odd segment, tone is currently OFF } else { lGenSmp = lReqSmp; // If event segment, tone is currently ON @@ -1488,12 +1520,12 @@ void ToneGenerator::audioCallback(int event, void* user, void *info) { if (lGenSmp) { // If samples must be generated, call all active wave generators and acumulate waves in lpOut unsigned int lFreqIdx = 0; - uint16_t lFrequency = lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[lFreqIdx]; + uint16_t lFrequency = mpToneDesc->segments[mCurSegment].waveFreq[lFreqIdx]; while (lFrequency != 0) { - WaveGenerator *lpWaveGen = lpToneGen->mWaveGens.valueFor(lFrequency); + WaveGenerator *lpWaveGen = mWaveGens.valueFor(lFrequency); lpWaveGen->getSamples(lpOut, lGenSmp, lWaveCmd); - lFrequency = lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[++lFreqIdx]; + lFrequency = mpToneDesc->segments[mCurSegment].waveFreq[++lFreqIdx]; } } @@ -1501,21 +1533,19 @@ void ToneGenerator::audioCallback(int event, void* user, void *info) { lpOut += lReqSmp; audioCallback_EndLoop: - - switch (lpToneGen->mState) { + switch (mState) { case TONE_RESTARTING: ALOGV("Cbk restarting track"); - if (lpToneGen->prepareWave()) { - lpToneGen->mState = TONE_STARTING; - if (clock_gettime(CLOCK_MONOTONIC, &lpToneGen->mStartTime) != 0) { - lpToneGen->mStartTime.tv_sec = 0; + if (prepareWave()) { + mState = TONE_STARTING; + if (clock_gettime(CLOCK_MONOTONIC, &mStartTime) != 0) { + mStartTime.tv_sec = 0; } - // must reload lpToneDesc as prepareWave() may change mpToneDesc - lpToneDesc = lpToneGen->mpToneDesc; + // must reload mpToneDesc as prepareWave() may change mpToneDesc } else { ALOGW("Cbk restarting prepareWave() failed"); - lpToneGen->mState = TONE_IDLE; - lpToneGen->mpAudioTrack->stop(); + mState = TONE_IDLE; + mpAudioTrack->stop(); // Force loop exit lNumSmp = 0; } @@ -1523,22 +1553,22 @@ audioCallback_EndLoop: break; case TONE_STOPPING: ALOGV("Cbk Stopping"); - lpToneGen->mState = TONE_STOPPED; + mState = TONE_STOPPED; // Force loop exit lNumSmp = 0; break; case TONE_STOPPED: - lpToneGen->mState = TONE_INIT; + mState = TONE_INIT; ALOGV("Cbk Stopped track"); - lpToneGen->mpAudioTrack->stop(); + mpAudioTrack->stop(); // Force loop exit lNumSmp = 0; - buffer->size = 0; + bytesWritten = 0; lSignal = true; break; case TONE_STARTING: ALOGV("Cbk starting track"); - lpToneGen->mState = TONE_PLAYING; + mState = TONE_PLAYING; lSignal = true; break; case TONE_PLAYING: @@ -1546,14 +1576,15 @@ audioCallback_EndLoop: default: // Force loop exit lNumSmp = 0; - buffer->size = 0; + bytesWritten = 0; break; } if (lSignal) - lpToneGen->mWaitCbkCond.broadcast(); - lpToneGen->mLock.unlock(); + mWaitCbkCond.broadcast(); + mLock.unlock(); } + return bytesWritten; } diff --git a/media/libaudioclient/TrackPlayerBase.cpp b/media/libaudioclient/TrackPlayerBase.cpp index 188f321aa3dcf78a7981e5238f9d4b3b0c3f5e9c..4fc1c445c5e2a9bae6b517ee535434f213e9f8e3 100644 --- a/media/libaudioclient/TrackPlayerBase.cpp +++ b/media/libaudioclient/TrackPlayerBase.cpp @@ -33,11 +33,14 @@ TrackPlayerBase::~TrackPlayerBase() { doDestroy(); } -void TrackPlayerBase::init(AudioTrack* pat, player_type_t playerType, audio_usage_t usage, - audio_session_t sessionId) { +void TrackPlayerBase::init(const sp& pat, + const sp& callback, + player_type_t playerType, audio_usage_t usage, + audio_session_t sessionId) { PlayerBase::init(playerType, usage, sessionId); mAudioTrack = pat; if (mAudioTrack != 0) { + mCallbackHandle = callback; mSelfAudioDeviceCallback = new SelfAudioDeviceCallback(*this); mAudioTrack->addAudioDeviceCallback(mSelfAudioDeviceCallback); mAudioTrack->setPlayerIId(mPIId); // set in PlayerBase::init(). diff --git a/media/libaudioclient/aidl/android/media/AudioAttributesEx.aidl b/media/libaudioclient/aidl/android/media/AudioAttributesEx.aidl index 04a02c721b252d9f58c5b0228f2bdc3c5cae8cd0..335866f566bdc3dd559cc684f39802e2ea97ce44 100644 --- a/media/libaudioclient/aidl/android/media/AudioAttributesEx.aidl +++ b/media/libaudioclient/aidl/android/media/AudioAttributesEx.aidl @@ -17,7 +17,7 @@ package android.media; import android.media.AudioAttributesInternal; -import android.media.AudioStreamType; +import android.media.audio.common.AudioStreamType; /** * This is the equivalent of the android::AudioAttributes C++ type. diff --git a/media/libaudioclient/aidl/android/media/AudioAttributesInternal.aidl b/media/libaudioclient/aidl/android/media/AudioAttributesInternal.aidl index 699df0adad73a5c5acb395a1bc15174d90e73c78..2e7420633d62395abab1ea60ea0dcdfd8931965e 100644 --- a/media/libaudioclient/aidl/android/media/AudioAttributesInternal.aidl +++ b/media/libaudioclient/aidl/android/media/AudioAttributesInternal.aidl @@ -16,9 +16,9 @@ package android.media; -import android.media.AudioContentType; -import android.media.AudioSourceType; -import android.media.AudioUsage; +import android.media.audio.common.AudioContentType; +import android.media.audio.common.AudioSource; +import android.media.audio.common.AudioUsage; /** * The "Internal" suffix of this type name is to disambiguate it from the @@ -28,7 +28,7 @@ import android.media.AudioUsage; parcelable AudioAttributesInternal { AudioContentType contentType; AudioUsage usage; - AudioSourceType source; + AudioSource source; // Bitmask, indexed by AudioFlag. int flags; @utf8InCpp String tags; /* UTF8 */ diff --git a/media/libaudioclient/aidl/android/media/AudioConfig.aidl b/media/libaudioclient/aidl/android/media/AudioConfig.aidl deleted file mode 100644 index 8dc97d3260a2bef2d5294157a7ddcba46deb2cf9..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioConfig.aidl +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ - -package android.media; - -import android.media.AudioOffloadInfo; -import android.media.audio.common.AudioFormat; - -/** - * {@hide} - */ -parcelable AudioConfig { - int sampleRate; - /** - * Interpreted as audio_channel_mask_t. - * TODO(ytai): Create a designated type. - */ - int channelMask; - AudioFormat format; - AudioOffloadInfo offloadInfo; - long frameCount; -} diff --git a/media/libaudioclient/aidl/android/media/AudioContentType.aidl b/media/libaudioclient/aidl/android/media/AudioContentType.aidl deleted file mode 100644 index a7d3277184a653ee796a5852a67a908b2fd0079b..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioContentType.aidl +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ -package android.media; - -@Backing(type="int") -enum AudioContentType { - UNKNOWN = 0, - SPEECH = 1, - MUSIC = 2, - MOVIE = 3, - SONIFICATION = 4, - ULTRASOUND = 1997, -} diff --git a/media/libaudioclient/aidl/android/media/AudioStandard.aidl b/media/libaudioclient/aidl/android/media/AudioDirectMode.aidl similarity index 84% rename from media/libaudioclient/aidl/android/media/AudioStandard.aidl rename to media/libaudioclient/aidl/android/media/AudioDirectMode.aidl index e131d0d0dfdf6b74572bf1a556fb7b491f4186d3..0da47219b8d54fbf0312900c3c5a5c30c314e4e9 100644 --- a/media/libaudioclient/aidl/android/media/AudioStandard.aidl +++ b/media/libaudioclient/aidl/android/media/AudioDirectMode.aidl @@ -15,13 +15,10 @@ */ package android.media; -/** - * The audio standard that describe audio playback/capture capabilites. - * - * {@hide} - */ @Backing(type="int") -enum AudioStandard { +enum AudioDirectMode { NONE = 0, - EDID = 1, + OFFLOAD = 1, + OFFLOAD_GAPLESS = 2, + BITSTREAM = 4, } diff --git a/media/libaudioclient/aidl/android/media/AudioEncapsulationMetadataType.aidl b/media/libaudioclient/aidl/android/media/AudioEncapsulationMetadataType.aidl deleted file mode 100644 index b03adfe382acbc1cd8825aa24de470e5268894a4..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioEncapsulationMetadataType.aidl +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ -package android.media; - -/** - * {@hide} - */ -@Backing(type="int") -enum AudioEncapsulationMetadataType { - NONE = 0, - FRAMEWORK_TUNER = 1, - DVB_AD_DESCRIPTOR = 2, -} diff --git a/media/libaudioclient/aidl/android/media/AudioEncapsulationMode.aidl b/media/libaudioclient/aidl/android/media/AudioEncapsulationMode.aidl deleted file mode 100644 index 9e04e82eacdd4cdacb82deba70fd7becad197630..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioEncapsulationMode.aidl +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ -package android.media; - -/** - * {@hide} - */ -@Backing(type="int") -enum AudioEncapsulationMode { - NONE = 0, - ELEMENTARY_STREAM = 1, - HANDLE = 2, -} diff --git a/media/libaudioclient/aidl/android/media/AudioFlag.aidl b/media/libaudioclient/aidl/android/media/AudioFlag.aidl index 91361fbcc5ae4857eaf974a0352fd93bc11d5020..acf4e6d9e6a7b63f7a3350a9e2390ed257280e5f 100644 --- a/media/libaudioclient/aidl/android/media/AudioFlag.aidl +++ b/media/libaudioclient/aidl/android/media/AudioFlag.aidl @@ -36,4 +36,5 @@ enum AudioFlag { CAPTURE_PRIVATE = 13, CONTENT_SPATIALIZED = 14, NEVER_SPATIALIZE = 15, + CALL_REDIRECTION = 16, } diff --git a/media/libaudioclient/aidl/android/media/AudioGain.aidl b/media/libaudioclient/aidl/android/media/AudioGain.aidl deleted file mode 100644 index 048b295bc031783396c779dd2204d8920f20d148..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioGain.aidl +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ - -package android.media; - -/** - * {@hide} - */ -parcelable AudioGain { - int index; - boolean useInChannelMask; - boolean useForVolume; - /** Bitmask, indexed by AudioGainMode. */ - int mode; - /** Interpreted as audio_channel_mask_t. */ - int channelMask; - int minValue; - int maxValue; - int defaultValue; - int stepValue; - int minRampMs; - int maxRampMs; -} diff --git a/media/libaudioclient/aidl/android/media/AudioGainConfig.aidl b/media/libaudioclient/aidl/android/media/AudioGainConfig.aidl deleted file mode 100644 index b93c2dc4acc25dc98e61ee394511226ba1af7e80..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioGainConfig.aidl +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ - -package android.media; - -/** - * {@hide} - */ -parcelable AudioGainConfig { - /** Index of the corresponding audio_gain in the audio_port gains[] table. */ - int index; - - /** Mode requested for this command. Bitfield indexed by AudioGainMode. */ - int mode; - - /** - * Channels which gain value follows. N/A in joint mode. - * Interpreted as audio_channel_mask_t. - */ - int channelMask; - - /** - * Gain values in millibels. - * For each channel ordered from LSb to MSb in channel mask. The number of values is 1 in joint - * mode, otherwise equals the number of bits implied by channelMask. - */ - int[] values; - - /** Ramp duration in ms. */ - int rampDurationMs; -} diff --git a/media/libaudioclient/aidl/android/media/AudioGainMode.aidl b/media/libaudioclient/aidl/android/media/AudioGainMode.aidl deleted file mode 100644 index e1b9f0b65dd086058707cd908bd36b615fa47f77..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioGainMode.aidl +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ -package android.media; - -/** - * {@hide} - */ -@Backing(type="int") -enum AudioGainMode { - JOINT = 0, - CHANNELS = 1, - RAMP = 2, -} diff --git a/media/libaudioclient/aidl/android/media/AudioDevice.aidl b/media/libaudioclient/aidl/android/media/AudioGainSys.aidl similarity index 82% rename from media/libaudioclient/aidl/android/media/AudioDevice.aidl rename to media/libaudioclient/aidl/android/media/AudioGainSys.aidl index b2006970b32a94f1512d44f928f7482b3f7b6bf1..426f4ed2b9781247d141ce901b2adff96600c753 100644 --- a/media/libaudioclient/aidl/android/media/AudioDevice.aidl +++ b/media/libaudioclient/aidl/android/media/AudioGainSys.aidl @@ -17,10 +17,11 @@ package android.media; /** + * Provides additional runtime information for AudioGain, used by the framework. + * * {@hide} */ -parcelable AudioDevice { - /** Interpreted as audio_devices_t. */ - int type; - @utf8InCpp String address; +parcelable AudioGainSys { + int index; + boolean isInput; } diff --git a/media/libaudioclient/aidl/android/media/AudioInputFlags.aidl b/media/libaudioclient/aidl/android/media/AudioInputFlags.aidl deleted file mode 100644 index d79769c8038c9a055d09e799077c598aae5abaf6..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioInputFlags.aidl +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ -package android.media; - -/** - * {@hide} - */ -@Backing(type="int") -enum AudioInputFlags { - FAST = 0, - HW_HOTWORD = 1, - RAW = 2, - SYNC = 3, - MMAP_NOIRQ = 4, - VOIP_TX = 5, - HW_AV_SYNC = 6, - DIRECT = 7, - ULTRASOUND = 8, -} diff --git a/media/libaudioclient/aidl/android/media/AudioIoDescriptor.aidl b/media/libaudioclient/aidl/android/media/AudioIoDescriptor.aidl index 876ef9b43ca92877006b88573b130858a4b8aff0..b01f90276052d145ed9e3ddeea6098c5088b5d69 100644 --- a/media/libaudioclient/aidl/android/media/AudioIoDescriptor.aidl +++ b/media/libaudioclient/aidl/android/media/AudioIoDescriptor.aidl @@ -17,7 +17,8 @@ package android.media; import android.media.AudioPatch; -import android.media.audio.common.AudioFormat; +import android.media.audio.common.AudioChannelLayout; +import android.media.audio.common.AudioFormatDescription; /** * {@hide} @@ -26,10 +27,10 @@ parcelable AudioIoDescriptor { /** Interpreted as audio_io_handle_t. */ int ioHandle; AudioPatch patch; + boolean isInput; int samplingRate; - AudioFormat format; - /** Interpreted as audio_channel_mask_t. */ - int channelMask; + AudioFormatDescription format; + AudioChannelLayout channelMask; long frameCount; long frameCountHAL; /** Only valid for output. */ diff --git a/media/libaudioclient/aidl/android/media/AudioMix.aidl b/media/libaudioclient/aidl/android/media/AudioMix.aidl index 7473372a139c7458472a0f9814b45409b4395236..88b04501a9410eea45a802d47f2966fb2070e153 100644 --- a/media/libaudioclient/aidl/android/media/AudioMix.aidl +++ b/media/libaudioclient/aidl/android/media/AudioMix.aidl @@ -16,12 +16,12 @@ package android.media; -import android.media.AudioConfig; -import android.media.AudioDevice; import android.media.AudioMixCallbackFlag; import android.media.AudioMixMatchCriterion; import android.media.AudioMixRouteFlag; import android.media.AudioMixType; +import android.media.audio.common.AudioConfig; +import android.media.audio.common.AudioDevice; /** * {@hide} diff --git a/media/libaudioclient/aidl/android/media/AudioMixLatencyClass.aidl b/media/libaudioclient/aidl/android/media/AudioMixLatencyClass.aidl deleted file mode 100644 index d70b3643760375d0fbd544133794b02fe863e510..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioMixLatencyClass.aidl +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ -package android.media; - -/** - * {@hide} - */ -@Backing(type="int") -enum AudioMixLatencyClass { - LOW = 0, - NORMAL = 1, -} diff --git a/media/libaudioclient/aidl/android/media/AudioMixMatchCriterionValue.aidl b/media/libaudioclient/aidl/android/media/AudioMixMatchCriterionValue.aidl index e26a9e19be4adc3c117115cd679f01cfe4c64057..921a93aa3884e6966e30241054b9274531290cb3 100644 --- a/media/libaudioclient/aidl/android/media/AudioMixMatchCriterionValue.aidl +++ b/media/libaudioclient/aidl/android/media/AudioMixMatchCriterionValue.aidl @@ -16,15 +16,15 @@ package android.media; -import android.media.AudioSourceType; -import android.media.AudioUsage; +import android.media.audio.common.AudioSource; +import android.media.audio.common.AudioUsage; /** * {@hide} */ union AudioMixMatchCriterionValue { AudioUsage usage = AudioUsage.UNKNOWN; - AudioSourceType source; + AudioSource source; /** Interpreted as uid_t. */ int uid; int userId; diff --git a/media/libaudioclient/aidl/android/media/AudioMode.aidl b/media/libaudioclient/aidl/android/media/AudioMode.aidl deleted file mode 100644 index 7067dd3b09f96bca04de8864940f5ea5bd7c63de..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioMode.aidl +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ -package android.media; - -/** - * {@hide} - */ -@Backing(type="int") -enum AudioMode { - INVALID = -2, - CURRENT = -1, - NORMAL = 0, - RINGTONE = 1, - IN_CALL = 2, - IN_COMMUNICATION = 3, - CALL_SCREEN = 4, -} diff --git a/media/libaudioclient/aidl/android/media/AudioOffloadInfo.aidl b/media/libaudioclient/aidl/android/media/AudioOffloadInfo.aidl deleted file mode 100644 index c86b3f04b3a4a0853351eae21d02ae19ae5187a7..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioOffloadInfo.aidl +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ - -package android.media; - -import android.media.AudioConfigBase; -import android.media.AudioEncapsulationMode; -import android.media.AudioStreamType; -import android.media.AudioUsage; -import android.media.audio.common.AudioFormat; - -/** - * {@hide} - */ -parcelable AudioOffloadInfo { - /** Version of the info structure. Interpreted as a uint16_t version constant. */ - int version; - /** Audio configuration. */ - AudioConfigBase config; - /** Stream type. */ - AudioStreamType streamType; - /** Bit rate in bits per second. */ - int bitRate; - /** Duration in microseconds, -1 if unknown. */ - long durationUs; - /** true if stream is tied to a video stream. */ - boolean hasVideo; - /** true if streaming, false if local playback. */ - boolean isStreaming; - int bitWidth; - /** Offload fragment size. */ - int offloadBufferSize; - AudioUsage usage; - AudioEncapsulationMode encapsulationMode; - /** Content id from tuner HAL (0 if none). */ - int contentId; - /** Sync id from tuner HAL (0 if none). */ - int syncId; -} diff --git a/media/libaudioclient/aidl/android/media/AudioOutputFlags.aidl b/media/libaudioclient/aidl/android/media/AudioOutputFlags.aidl deleted file mode 100644 index f49b24cd38f321d20beb69e73ba77a365634c609..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioOutputFlags.aidl +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ -package android.media; - -/** - * {@hide} - */ -@Backing(type="int") -enum AudioOutputFlags { - DIRECT = 0, - PRIMARY = 1, - FAST = 2, - DEEP_BUFFER = 3, - COMPRESS_OFFLOAD = 4, - NON_BLOCKING = 5, - HW_AV_SYNC = 6, - TTS = 7, - RAW = 8, - SYNC = 9, - IEC958_NONAUDIO = 10, - DIRECT_PCM = 11, - MMAP_NOIRQ = 12, - VOIP_RX = 13, - INCALL_MUSIC = 14, - GAPLESS_OFFLOAD = 15, - SPATIALIZER = 16, - ULTRASOUND = 17, -} diff --git a/media/libaudioclient/aidl/android/media/AudioPort.aidl b/media/libaudioclient/aidl/android/media/AudioPort.aidl index bf0e5b792c7ef24e7e3417ab7511b5aaf6af566f..ff177c0ac17b480945a4674423971c4a03b9e6b5 100644 --- a/media/libaudioclient/aidl/android/media/AudioPort.aidl +++ b/media/libaudioclient/aidl/android/media/AudioPort.aidl @@ -16,35 +16,13 @@ package android.media; -import android.media.AudioGain; -import android.media.AudioPortConfig; -import android.media.AudioPortExt; -import android.media.AudioPortRole; -import android.media.AudioPortType; -import android.media.AudioProfile; -import android.media.ExtraAudioDescriptor; +import android.media.AudioPortSys; +import android.media.audio.common.AudioPort; /** * {@hide} */ parcelable AudioPort { - /** Port unique ID. Interpreted as audio_port_handle_t. */ - int id; - /** Sink or source. */ - AudioPortRole role; - /** Device, mix ... */ - AudioPortType type; - @utf8InCpp String name; - /** AudioProfiles supported by this port (format, Rates, Channels). */ - AudioProfile[] profiles; - /** - * ExtraAudioDescriptors supported by this port. The format is not unrecognized to the - * platform. The audio capability is described by a hardware descriptor. - */ - ExtraAudioDescriptor[] extraAudioDescriptors; - /** Gain controllers. */ - AudioGain[] gains; - /** Current audio port configuration. */ - AudioPortConfig activeConfig; - AudioPortExt ext; + AudioPort hal; + AudioPortSys sys; } diff --git a/media/libaudioclient/aidl/android/media/AudioPortConfig.aidl b/media/libaudioclient/aidl/android/media/AudioPortConfig.aidl index 2dd30a450561014808490e3e16819a90e7cb5521..3a4ca31e367366134b134292e73fb4d9a9a8aa12 100644 --- a/media/libaudioclient/aidl/android/media/AudioPortConfig.aidl +++ b/media/libaudioclient/aidl/android/media/AudioPortConfig.aidl @@ -16,44 +16,13 @@ package android.media; -import android.media.AudioGainConfig; -import android.media.AudioIoFlags; -import android.media.AudioPortConfigExt; -import android.media.AudioPortConfigType; -import android.media.AudioPortRole; -import android.media.AudioPortType; -import android.media.audio.common.AudioFormat; +import android.media.AudioPortConfigSys; +import android.media.audio.common.AudioPortConfig; /** * {@hide} */ parcelable AudioPortConfig { - /** - * Port unique ID. - * Interpreted as audio_port_handle_t. - */ - int id; - /** Sink or source. */ - AudioPortRole role; - /** Device, mix ... */ - AudioPortType type; - /** Bitmask, indexed by AudioPortConfigType. */ - int configMask; - /** Sampling rate in Hz. */ - int sampleRate; - /** - * Channel mask, if applicable. - * Interpreted as audio_channel_mask_t. - * TODO: bitmask? - */ - int channelMask; - /** - * Format, if applicable. - */ - AudioFormat format; - /** Gain to apply, if applicable. */ - AudioGainConfig gain; - /** Framework only: HW_AV_SYNC, DIRECT, ... */ - AudioIoFlags flags; - AudioPortConfigExt ext; + AudioPortConfig hal; + AudioPortConfigSys sys; } diff --git a/media/libaudioclient/aidl/android/media/AudioPortConfigDeviceExt.aidl b/media/libaudioclient/aidl/android/media/AudioPortConfigDeviceExt.aidl deleted file mode 100644 index a99aa9bb3d2da37508bb9511be8e3ca17bb4c900..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioPortConfigDeviceExt.aidl +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ - -package android.media; - -/** - * {@hide} - */ -parcelable AudioPortConfigDeviceExt { - /** - * Module the device is attached to. - * Interpreted as audio_module_handle_t. - */ - int hwModule; - /** - * Device type (e.g AUDIO_DEVICE_OUT_SPEAKER). - * Interpreted as audio_devices_t. - * TODO: Convert to a standalone AIDL representation. - */ - int type; - /** Device address. "" if N/A. */ - @utf8InCpp String address; -} diff --git a/media/libaudioclient/aidl/android/media/AudioPortConfigExt.aidl b/media/libaudioclient/aidl/android/media/AudioPortConfigExt.aidl deleted file mode 100644 index 5d635b6d09b760b82ac83af8f369201111c7371c..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioPortConfigExt.aidl +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ - -package android.media; - -import android.media.AudioPortConfigDeviceExt; -import android.media.AudioPortConfigMixExt; -import android.media.AudioPortConfigSessionExt; - -/** - * {@hide} - */ -union AudioPortConfigExt { - /** - * This represents an empty union. Value is ignored. - * TODO(ytai): replace with the canonical representation for an empty union, as soon as it is - * established. - */ - boolean unspecified; - /** Device specific info. */ - AudioPortConfigDeviceExt device; - /** Mix specific info. */ - AudioPortConfigMixExt mix; - /** Session specific info. */ - AudioPortConfigSessionExt session; -} diff --git a/media/libaudioclient/aidl/android/media/AudioPortConfigMixExtUseCase.aidl b/media/libaudioclient/aidl/android/media/AudioPortConfigMixExtUseCase.aidl deleted file mode 100644 index c61f044ecf7bb943ef2e953ecc4bc324c6361431..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioPortConfigMixExtUseCase.aidl +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ - -package android.media; - -import android.media.AudioSourceType; -import android.media.AudioStreamType; - -/** - * {@hide} - */ -union AudioPortConfigMixExtUseCase { - /** - * This to be set if the containing config has the AudioPortRole::NONE role. - * This represents an empty value (value is ignored). - * TODO(ytai): replace with the canonical representation for an empty union, as soon as it is - * established. - */ - boolean unspecified; - /** This to be set if the containing config has the AudioPortRole::SOURCE role. */ - AudioStreamType stream; - /** This to be set if the containing config has the AudioPortRole::SINK role. */ - AudioSourceType source; -} diff --git a/media/libaudioclient/aidl/android/media/AudioEncapsulationType.aidl b/media/libaudioclient/aidl/android/media/AudioPortConfigSys.aidl similarity index 70% rename from media/libaudioclient/aidl/android/media/AudioEncapsulationType.aidl rename to media/libaudioclient/aidl/android/media/AudioPortConfigSys.aidl index b08a604c3c2dfb6edac01dcfbe839c953e23ff08..8692848c7f68587057a9e2c7be33e567b34124b5 100644 --- a/media/libaudioclient/aidl/android/media/AudioEncapsulationType.aidl +++ b/media/libaudioclient/aidl/android/media/AudioPortConfigSys.aidl @@ -16,14 +16,17 @@ package android.media; +import android.media.AudioPortExtSys; +import android.media.AudioPortRole; +import android.media.AudioPortType; + /** - * Audio encapsulation type is used to describe if the audio data should be sent with a particular - * encapsulation type or not. - * * {@hide} */ -@Backing(type="int") -enum AudioEncapsulationType { - NONE = 0, - IEC61937 = 1, -} \ No newline at end of file +parcelable AudioPortConfigSys { + /** Sink or source. */ + AudioPortRole role; + /** Device, mix ... */ + AudioPortType type; + AudioPortExtSys ext; +} diff --git a/media/libaudioclient/aidl/android/media/AudioPortConfigType.aidl b/media/libaudioclient/aidl/android/media/AudioPortConfigType.aidl deleted file mode 100644 index 6e22b8d4fab1d27c51e7410d1789d4e448ffb5bb..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioPortConfigType.aidl +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ -package android.media; - -/** - * {@hide} - */ -@Backing(type="int") -enum AudioPortConfigType { - SAMPLE_RATE = 0, - CHANNEL_MASK = 1, - FORMAT = 2, - GAIN = 3, - FLAGS = 4, -} diff --git a/media/libaudioclient/aidl/android/media/AudioPortDeviceExt.aidl b/media/libaudioclient/aidl/android/media/AudioPortDeviceExtSys.aidl similarity index 86% rename from media/libaudioclient/aidl/android/media/AudioPortDeviceExt.aidl rename to media/libaudioclient/aidl/android/media/AudioPortDeviceExtSys.aidl index b758f23cd3796d084a3b63d3402d03177243ac69..0f5a9b67f696cbafd1db094103a0aea5584871dd 100644 --- a/media/libaudioclient/aidl/android/media/AudioPortDeviceExt.aidl +++ b/media/libaudioclient/aidl/android/media/AudioPortDeviceExtSys.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2021 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. @@ -16,15 +16,12 @@ package android.media; -import android.media.AudioDevice; - /** * {@hide} */ -parcelable AudioPortDeviceExt { +parcelable AudioPortDeviceExtSys { /** Module the device is attached to. Interpreted as audio_module_handle_t. */ int hwModule; - AudioDevice device; /** Bitmask, indexed by AudioEncapsulationMode. */ int encapsulationModes; /** Bitmask, indexed by AudioEncapsulationMetadataType. */ diff --git a/media/libaudioclient/aidl/android/media/AudioPortExt.aidl b/media/libaudioclient/aidl/android/media/AudioPortExt.aidl deleted file mode 100644 index 453784be66936727fb9dacee1109f42622b0d2cf..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioPortExt.aidl +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ - -package android.media; - -import android.media.AudioPortDeviceExt; -import android.media.AudioPortMixExt; -import android.media.AudioPortSessionExt; - -/** - * {@hide} - */ -union AudioPortExt { - /** - * This represents an empty union. Value is ignored. - * TODO(ytai): replace with the canonical representation for an empty union, as soon as it is - * established. - */ - boolean unspecified; - /** Device specific info. */ - AudioPortDeviceExt device; - /** Mix specific info. */ - AudioPortMixExt mix; - /** Session specific info. */ - AudioPortSessionExt session; -} diff --git a/media/libaudioclient/aidl/android/media/AudioPortExtSys.aidl b/media/libaudioclient/aidl/android/media/AudioPortExtSys.aidl new file mode 100644 index 0000000000000000000000000000000000000000..2cdf4f68ca67933e41d3fda3f87c4f6ec051919f --- /dev/null +++ b/media/libaudioclient/aidl/android/media/AudioPortExtSys.aidl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2021 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. + */ + +package android.media; + +import android.media.AudioPortDeviceExtSys; +import android.media.AudioPortMixExtSys; + +/** + * {@hide} + */ +union AudioPortExtSys { + /** + * This represents an empty union. Value is ignored. + */ + boolean unspecified; + /** System-only parameters when the port is an audio device. */ + AudioPortDeviceExtSys device; + /** System-only parameters when the port is an audio mix. */ + AudioPortMixExtSys mix; +} diff --git a/media/libaudioclient/aidl/android/media/AudioPortMixExt.aidl b/media/libaudioclient/aidl/android/media/AudioPortMixExt.aidl deleted file mode 100644 index 62cdb8ef2b9b887ed6de07d536eeceb46c73853b..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioPortMixExt.aidl +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ - -package android.media; - -import android.media.AudioMixLatencyClass; - -/** - * {@hide} - */ -parcelable AudioPortMixExt { - /** Module the stream is attached to. Interpreted as audio_module_handle_t. */ - int hwModule; - /** I/O handle of the input/output stream. Interpreted as audio_io_handle_t. */ - int handle; - /** Latency class */ - AudioMixLatencyClass latencyClass; -} diff --git a/media/libaudioclient/aidl/android/media/Int.aidl b/media/libaudioclient/aidl/android/media/AudioPortMixExtSys.aidl similarity index 79% rename from media/libaudioclient/aidl/android/media/Int.aidl rename to media/libaudioclient/aidl/android/media/AudioPortMixExtSys.aidl index 24f4d62a117a15cff608012bc8f1ac7b507b164e..599988586ad6627675cc6695b6c86f867366d0c6 100644 --- a/media/libaudioclient/aidl/android/media/Int.aidl +++ b/media/libaudioclient/aidl/android/media/AudioPortMixExtSys.aidl @@ -17,11 +17,9 @@ package android.media; /** - * This is a simple wrapper around an 'int', putting it in a parcelable, so it can be used as an - * inout parameter, be made @nullable, etc. - * * {@hide} */ -parcelable Int { - int value; +parcelable AudioPortMixExtSys { + /** Module the stream is attached to. Interpreted as audio_module_handle_t. */ + int hwModule; } diff --git a/media/libaudioclient/aidl/android/media/AudioPortSys.aidl b/media/libaudioclient/aidl/android/media/AudioPortSys.aidl new file mode 100644 index 0000000000000000000000000000000000000000..f3b5c19ba53f4a5abc9e9e89f6bd12365a7d8289 --- /dev/null +++ b/media/libaudioclient/aidl/android/media/AudioPortSys.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2021 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. + */ + +package android.media; + +import android.media.AudioGainSys; +import android.media.AudioPortConfig; +import android.media.AudioPortExtSys; +import android.media.AudioPortRole; +import android.media.AudioPortType; +import android.media.AudioProfileSys; + +/** + * {@hide} + */ +parcelable AudioPortSys { + /** Sink or source. */ + AudioPortRole role; + /** Device, mix ... */ + AudioPortType type; + /** System-only parameters for each AudioProfile from 'port.profiles'. */ + AudioProfileSys[] profiles; + /** System-only parameters for each AudioGain from 'port.gains'. */ + AudioGainSys[] gains; + /** Current audio port configuration. */ + AudioPortConfig activeConfig; + /** System-only extra parameters for 'port.ext'. */ + AudioPortExtSys ext; +} diff --git a/media/libaudioclient/aidl/android/media/AudioProfile.aidl b/media/libaudioclient/aidl/android/media/AudioProfile.aidl deleted file mode 100644 index afb288f09510c0e5ab41fca8ad8454001f91c1cc..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioProfile.aidl +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ - -package android.media; - -import android.media.AudioEncapsulationType; -import android.media.audio.common.AudioFormat; - -/** - * {@hide} - */ -parcelable AudioProfile { - @utf8InCpp String name; - /** The format for an audio profile should only be set when initialized. */ - AudioFormat format; - /** Interpreted as audio_channel_mask_t. */ - int[] channelMasks; - int[] samplingRates; - boolean isDynamicFormat; - boolean isDynamicChannels; - boolean isDynamicRate; - AudioEncapsulationType encapsulationType; -} diff --git a/media/libaudioclient/aidl/android/media/AudioIoFlags.aidl b/media/libaudioclient/aidl/android/media/AudioProfileSys.aidl similarity index 69% rename from media/libaudioclient/aidl/android/media/AudioIoFlags.aidl rename to media/libaudioclient/aidl/android/media/AudioProfileSys.aidl index f9b25bfb1f33866c87f90478e256f9983e5a2ddb..329c9d56b684d982fabfb49074b69055314782e6 100644 --- a/media/libaudioclient/aidl/android/media/AudioIoFlags.aidl +++ b/media/libaudioclient/aidl/android/media/AudioProfileSys.aidl @@ -17,11 +17,14 @@ package android.media; /** + * Provides indication whether the parameters of the AudioProfiles in the + * AudioPort are dynamic. Each instance of AudioProfileSys corresponds + * to an instance of AudioProfile. + * * {@hide} */ -union AudioIoFlags { - /** Bitmask indexed by AudioInputFlags. */ - int input; - /** Bitmask indexed by AudioOutputFlags. */ - int output; +parcelable AudioProfileSys { + boolean isDynamicFormat; + boolean isDynamicChannels; + boolean isDynamicRate; } diff --git a/media/libaudioclient/aidl/android/media/AudioSourceType.aidl b/media/libaudioclient/aidl/android/media/AudioSourceType.aidl deleted file mode 100644 index 2006e6ce770aae0dfc35e834d618815d7bcb3428..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioSourceType.aidl +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ -package android.media; - -/** - * {@hide} - */ -@Backing(type="int") -enum AudioSourceType { - INVALID = -1, - DEFAULT = 0, - MIC = 1, - VOICE_UPLINK = 2, - VOICE_DOWNLINK = 3, - VOICE_CALL = 4, - CAMCORDER = 5, - VOICE_RECOGNITION = 6, - VOICE_COMMUNICATION = 7, - REMOTE_SUBMIX = 8, - UNPROCESSED = 9, - VOICE_PERFORMANCE = 10, - ECHO_REFERENCE = 1997, - FM_TUNER = 1998, - /** - * A low-priority, preemptible audio source for for background software - * hotword detection. Same tuning as VOICE_RECOGNITION. - * Used only internally by the framework. - */ - HOTWORD = 1999, - ULTRASOUND = 2000, -} diff --git a/media/libaudioclient/aidl/android/media/AudioStreamType.aidl b/media/libaudioclient/aidl/android/media/AudioStreamType.aidl deleted file mode 100644 index d7778828e5ad3c80f114806f9c169d9c6148b626..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioStreamType.aidl +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ -package android.media; - -/** - * {@hide} - */ -@Backing(type="int") -enum AudioStreamType { - DEFAULT = -1, - VOICE_CALL = 0, - SYSTEM = 1, - RING = 2, - MUSIC = 3, - ALARM = 4, - NOTIFICATION = 5, - BLUETOOTH_SCO = 6, - ENFORCED_AUDIBLE = 7, - DTMF = 8, - TTS = 9, - ACCESSIBILITY = 10, - ASSISTANT = 11, - /** For dynamic policy output mixes. Only used by the audio policy */ - REROUTING = 12, - /** For audio flinger tracks volume. Only used by the audioflinger */ - PATCH = 13, - /** stream for corresponding to AUDIO_USAGE_CALL_ASSISTANT */ - CALL_ASSISTANT = 14, -} diff --git a/media/libaudioclient/aidl/android/media/AudioUsage.aidl b/media/libaudioclient/aidl/android/media/AudioUsage.aidl deleted file mode 100644 index 66c5c30e14adcb8b70fec57b303b37ef53beba0b..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioUsage.aidl +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ -package android.media; - -/** - * {@hide} - */ -@Backing(type="int") -enum AudioUsage { - UNKNOWN = 0, - MEDIA = 1, - VOICE_COMMUNICATION = 2, - VOICE_COMMUNICATION_SIGNALLING = 3, - ALARM = 4, - NOTIFICATION = 5, - NOTIFICATION_TELEPHONY_RINGTONE = 6, - NOTIFICATION_COMMUNICATION_REQUEST = 7, - NOTIFICATION_COMMUNICATION_INSTANT = 8, - NOTIFICATION_COMMUNICATION_DELAYED = 9, - NOTIFICATION_EVENT = 10, - ASSISTANCE_ACCESSIBILITY = 11, - ASSISTANCE_NAVIGATION_GUIDANCE = 12, - ASSISTANCE_SONIFICATION = 13, - GAME = 14, - VIRTUAL_SOURCE = 15, - ASSISTANT = 16, - CALL_ASSISTANT = 17, - EMERGENCY = 1000, - SAFETY = 1001, - VEHICLE_STATUS = 1002, - ANNOUNCEMENT = 1003, -} diff --git a/media/libaudioclient/aidl/android/media/AudioUuid.aidl b/media/libaudioclient/aidl/android/media/AudioUuid.aidl deleted file mode 100644 index bba9039ac48c18a6569c9f38f7264c742c6383b6..0000000000000000000000000000000000000000 --- a/media/libaudioclient/aidl/android/media/AudioUuid.aidl +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ -package android.media; - -/** - * {@hide} - */ -parcelable AudioUuid { - int timeLow; - int timeMid; - int timeHiAndVersion; - int clockSeq; - byte[] node; // Length = 6 -} diff --git a/media/libaudioclient/aidl/android/media/AudioVolumeGroup.aidl b/media/libaudioclient/aidl/android/media/AudioVolumeGroup.aidl index 3a29a084ddca08a49bb41cfe1cf49789b21c503b..b95a1d3d334bc87259041335a3a5005962c1ecab 100644 --- a/media/libaudioclient/aidl/android/media/AudioVolumeGroup.aidl +++ b/media/libaudioclient/aidl/android/media/AudioVolumeGroup.aidl @@ -17,7 +17,7 @@ package android.media; import android.media.AudioAttributesInternal; -import android.media.AudioStreamType; +import android.media.audio.common.AudioStreamType; /** * {@hide} diff --git a/media/libaudioclient/aidl/android/media/CreateEffectRequest.aidl b/media/libaudioclient/aidl/android/media/CreateEffectRequest.aidl index 35a56eba266ced56c5914a1830e4c563ec0eece0..bcca04aa6c1a99f64ed5905fab864afe7bca6aec 100644 --- a/media/libaudioclient/aidl/android/media/CreateEffectRequest.aidl +++ b/media/libaudioclient/aidl/android/media/CreateEffectRequest.aidl @@ -16,10 +16,10 @@ package android.media; -import android.media.AudioDevice; +import android.content.AttributionSourceState; import android.media.EffectDescriptor; import android.media.IEffectClient; -import android.content.AttributionSourceState; +import android.media.audio.common.AudioDevice; /** * Input arguments of the createEffect() method. diff --git a/media/libaudioclient/aidl/android/media/CreateEffectResponse.aidl b/media/libaudioclient/aidl/android/media/CreateEffectResponse.aidl index 0aa640ae0df64811e8fc176f99b5f625af01d916..e2755dddc2c51bfd94a416b54d2feaab63ed130c 100644 --- a/media/libaudioclient/aidl/android/media/CreateEffectResponse.aidl +++ b/media/libaudioclient/aidl/android/media/CreateEffectResponse.aidl @@ -29,4 +29,5 @@ parcelable CreateEffectResponse { boolean enabled; @nullable IEffect effect; EffectDescriptor desc; + boolean alreadyExists; } diff --git a/media/libaudioclient/aidl/android/media/CreateRecordRequest.aidl b/media/libaudioclient/aidl/android/media/CreateRecordRequest.aidl index 7e3c24018f7954708b36dbfca1464be10cd75aed..b938a3eda77e83589e4120d9c28a60d60af6cc4b 100644 --- a/media/libaudioclient/aidl/android/media/CreateRecordRequest.aidl +++ b/media/libaudioclient/aidl/android/media/CreateRecordRequest.aidl @@ -18,7 +18,7 @@ package android.media; import android.media.AudioAttributesInternal; import android.media.AudioClient; -import android.media.AudioConfigBase; +import android.media.audio.common.AudioConfigBase; /** * CreateRecordRequest contains all input arguments sent by AudioRecord to AudioFlinger diff --git a/media/libaudioclient/aidl/android/media/CreateRecordResponse.aidl b/media/libaudioclient/aidl/android/media/CreateRecordResponse.aidl index d78b3fcd2ba787bceb6bdebfe0a775b0bda119ce..7d159d05067d24226c27419525677976a65fe19d 100644 --- a/media/libaudioclient/aidl/android/media/CreateRecordResponse.aidl +++ b/media/libaudioclient/aidl/android/media/CreateRecordResponse.aidl @@ -18,6 +18,7 @@ package android.media; import android.media.IAudioRecord; import android.media.SharedFileRegion; +import android.media.audio.common.AudioConfigBase; /** * CreateRecordResponse contains all output arguments returned by AudioFlinger to AudioRecord @@ -43,4 +44,5 @@ parcelable CreateRecordResponse { int portId; /** The newly created record. */ @nullable IAudioRecord audioRecord; + AudioConfigBase serverConfig; } diff --git a/media/libaudioclient/aidl/android/media/CreateTrackRequest.aidl b/media/libaudioclient/aidl/android/media/CreateTrackRequest.aidl index 014b3cace5a6c8d995264dc13c844e854a3ef5eb..212221e51c4f7b7dbeca5fe8064c0536b1c458a7 100644 --- a/media/libaudioclient/aidl/android/media/CreateTrackRequest.aidl +++ b/media/libaudioclient/aidl/android/media/CreateTrackRequest.aidl @@ -18,9 +18,9 @@ package android.media; import android.media.AudioAttributesInternal; import android.media.AudioClient; -import android.media.AudioConfig; import android.media.IAudioTrackCallback; import android.media.SharedFileRegion; +import android.media.audio.common.AudioConfig; /** * CreateTrackInput contains all input arguments sent by AudioTrack to AudioFlinger diff --git a/media/libaudioclient/aidl/android/media/CreateTrackResponse.aidl b/media/libaudioclient/aidl/android/media/CreateTrackResponse.aidl index 40473fa0e6e03db9247c8c3d48c81ccff7724415..da6f4548d63ddf10ac48c08b1359685fdcfbd56b 100644 --- a/media/libaudioclient/aidl/android/media/CreateTrackResponse.aidl +++ b/media/libaudioclient/aidl/android/media/CreateTrackResponse.aidl @@ -16,7 +16,7 @@ package android.media; -import android.media.AudioStreamType; +import android.media.audio.common.AudioStreamType; import android.media.IAudioTrack; /** diff --git a/media/libaudioclient/aidl/android/media/EffectDescriptor.aidl b/media/libaudioclient/aidl/android/media/EffectDescriptor.aidl index 35a3d7467f5925abe506d74d5466e10e38e22fae..e5b515806f1822990437b8c1bc0b1f238783997c 100644 --- a/media/libaudioclient/aidl/android/media/EffectDescriptor.aidl +++ b/media/libaudioclient/aidl/android/media/EffectDescriptor.aidl @@ -16,7 +16,7 @@ package android.media; -import android.media.AudioUuid; +import android.media.audio.common.AudioUuid; /** * {@hide} diff --git a/media/libaudioclient/aidl/android/media/GetOutputForAttrResponse.aidl b/media/libaudioclient/aidl/android/media/GetOutputForAttrResponse.aidl index 164fb9df302b79502d7b8843436c362928190bc3..f1848b60f52f671d45558cd0ce4751c0fca50065 100644 --- a/media/libaudioclient/aidl/android/media/GetOutputForAttrResponse.aidl +++ b/media/libaudioclient/aidl/android/media/GetOutputForAttrResponse.aidl @@ -16,7 +16,7 @@ package android.media; -import android.media.AudioStreamType; +import android.media.audio.common.AudioStreamType; /** * {@hide} @@ -31,4 +31,6 @@ parcelable GetOutputForAttrResponse { int portId; /** Interpreted as audio_io_handle_t[]. */ int[] secondaryOutputs; + /** True if the track is connected to a spatializer mixer and actually spatialized */ + boolean isSpatialized; } diff --git a/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl b/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl index 5cdde5d1e4019e14036087b37ef328abb1806ac3..10da0288737f532e1bc37d1f4e7b44ace4963f65 100644 --- a/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl +++ b/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl @@ -16,13 +16,10 @@ package android.media; -import android.media.AudioMode; import android.media.AudioPatch; import android.media.AudioPort; import android.media.AudioPortConfig; -import android.media.AudioStreamType; import android.media.AudioUniqueIdUse; -import android.media.AudioUuid; import android.media.AudioVibratorInfo; import android.media.CreateEffectRequest; import android.media.CreateEffectResponse; @@ -41,7 +38,13 @@ import android.media.IAudioTrack; import android.media.MicrophoneInfoData; import android.media.RenderPosition; import android.media.TrackSecondaryOutputInfo; -import android.media.audio.common.AudioFormat; +import android.media.audio.common.AudioChannelLayout; +import android.media.audio.common.AudioFormatDescription; +import android.media.audio.common.AudioMMapPolicyInfo; +import android.media.audio.common.AudioMMapPolicyType; +import android.media.audio.common.AudioMode; +import android.media.audio.common.AudioStreamType; +import android.media.audio.common.AudioUuid; /** * {@hide} @@ -62,7 +65,7 @@ interface IAudioFlingerService { */ int sampleRate(int /* audio_io_handle_t */ ioHandle); - AudioFormat format(int /* audio_io_handle_t */ output); + AudioFormatDescription format(int /* audio_io_handle_t */ output); long frameCount(int /* audio_io_handle_t */ ioHandle); @@ -115,8 +118,8 @@ interface IAudioFlingerService { // Retrieve the audio recording buffer size in bytes. // FIXME This API assumes a route, and so should be deprecated. long getInputBufferSize(int sampleRate, - AudioFormat format, - int /* audio_channel_mask_t */ channelMask); + in AudioFormatDescription format, + in AudioChannelLayout channelMask); OpenOutputResponse openOutput(in OpenOutputRequest request); int /* audio_io_handle_t */ openDuplicateOutput(int /* audio_io_handle_t */ output1, @@ -217,5 +220,16 @@ interface IAudioFlingerService { void updateSecondaryOutputs( in TrackSecondaryOutputInfo[] trackSecondaryOutputInfos); + AudioMMapPolicyInfo[] getMmapPolicyInfos(AudioMMapPolicyType policyType); + + int getAAudioMixerBurstCount(); + + int getAAudioHardwareBurstMinUsec(); + void setDeviceConnectedState(in AudioPort devicePort, boolean connected); + + // When adding a new method, please review and update + // IAudioFlinger.h AudioFlingerServerAdapter::Delegate::TransactionCode + // AudioFlinger.cpp AudioFlinger::onTransactWrapper() + // AudioFlinger.cpp IAUDIOFLINGER_BINDER_METHOD_MACRO_LIST } diff --git a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl index 6140a64660ee27552c2561fdd3aa263b1f2e5254..8ac89a847e751c543593835a3aac2e98230e4cd7 100644 --- a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl +++ b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl @@ -18,16 +18,10 @@ package android.media; import android.content.AttributionSourceState; -import android.media.audio.common.AudioFormat; - import android.media.AudioAttributesEx; import android.media.AudioAttributesInternal; -import android.media.AudioConfig; -import android.media.AudioConfigBase; -import android.media.AudioDevice; +import android.media.AudioDirectMode; import android.media.AudioMix; -import android.media.AudioMode; -import android.media.AudioOffloadInfo; import android.media.AudioOffloadMode; import android.media.AudioPatch; import android.media.AudioPolicyDeviceState; @@ -38,10 +32,6 @@ import android.media.AudioPortConfig; import android.media.AudioPortRole; import android.media.AudioPortType; import android.media.AudioProductStrategy; -import android.media.AudioSourceType; -import android.media.AudioStreamType; -import android.media.AudioUsage; -import android.media.AudioUuid; import android.media.AudioVolumeGroup; import android.media.DeviceRole; import android.media.EffectDescriptor; @@ -51,8 +41,21 @@ import android.media.GetSpatializerResponse; import android.media.IAudioPolicyServiceClient; import android.media.ICaptureStateListener; import android.media.INativeSpatializerCallback; -import android.media.Int; import android.media.SoundTriggerSession; +import android.media.audio.common.AudioConfig; +import android.media.audio.common.AudioConfigBase; +import android.media.audio.common.AudioDevice; +import android.media.audio.common.AudioDeviceDescription; +import android.media.audio.common.AudioFormatDescription; +import android.media.audio.common.AudioMode; +import android.media.audio.common.AudioProfile; +import android.media.audio.common.AudioOffloadInfo; +import android.media.audio.common.AudioPort; +import android.media.audio.common.AudioSource; +import android.media.audio.common.AudioStreamType; +import android.media.audio.common.AudioUsage; +import android.media.audio.common.AudioUuid; +import android.media.audio.common.Int; /** * IAudioPolicyService interface (see AudioPolicyInterface for method descriptions). @@ -62,16 +65,15 @@ import android.media.SoundTriggerSession; interface IAudioPolicyService { oneway void onNewAudioModulesAvailable(); - void setDeviceConnectionState(in AudioDevice device, - in AudioPolicyDeviceState state, - @utf8InCpp String deviceName, - in AudioFormat encodedFormat); + void setDeviceConnectionState(in AudioPolicyDeviceState state, + in android.media.audio.common.AudioPort port, + in AudioFormatDescription encodedFormat); AudioPolicyDeviceState getDeviceConnectionState(in AudioDevice device); void handleDeviceConfigChange(in AudioDevice device, @utf8InCpp String deviceName, - in AudioFormat encodedFormat); + in AudioFormatDescription encodedFormat); void setPhoneState(AudioMode state, int /* uid_t */ uid); @@ -116,18 +118,18 @@ interface IAudioPolicyService { int indexMax); void setStreamVolumeIndex(AudioStreamType stream, - int /* audio_devices_t */ device, + in AudioDeviceDescription device, int index); int getStreamVolumeIndex(AudioStreamType stream, - int /* audio_devices_t */ device); + in AudioDeviceDescription device); void setVolumeIndexForAttributes(in AudioAttributesInternal attr, - int /* audio_devices_t */ device, + in AudioDeviceDescription device, int index); int getVolumeIndexForAttributes(in AudioAttributesInternal attr, - int /* audio_devices_t */ device); + in AudioDeviceDescription device); int getMaxVolumeIndexForAttributes(in AudioAttributesInternal attr); @@ -135,9 +137,7 @@ interface IAudioPolicyService { int /* product_strategy_t */ getStrategyForStream(AudioStreamType stream); - int /* bitmask of audio_devices_t */ getDevicesForStream(AudioStreamType stream); - - AudioDevice[] getDevicesForAttributes(in AudioAttributesEx attr); + AudioDevice[] getDevicesForAttributes(in AudioAttributesEx attr, boolean forVolume); int /* audio_io_handle_t */ getOutputForEffect(in EffectDescriptor desc); @@ -157,7 +157,7 @@ interface IAudioPolicyService { boolean isStreamActiveRemotely(AudioStreamType stream, int inPastMs); - boolean isSourceActive(AudioSourceType source); + boolean isSourceActive(AudioSource source); /** * On input, count represents the maximum length of the returned array. @@ -172,7 +172,7 @@ interface IAudioPolicyService { @utf8InCpp String opPackageName, in AudioUuid uuid, int priority, - AudioSourceType source); + AudioSource source); int /* audio_unique_id_t */ addStreamDefaultEffect(in AudioUuid type, @utf8InCpp String opPackageName, @@ -214,8 +214,8 @@ interface IAudioPolicyService { inout Int count, out AudioPort[] ports); - /** Get attributes for a given audio port. */ - AudioPort getAudioPort(in AudioPort port); + /** Get attributes for the audio port with the given id (AudioPort.hal.id field). */ + AudioPort getAudioPort(int /* audio_port_handle_t */ portId); /** * Create an audio patch between several source and sink ports. @@ -270,7 +270,7 @@ interface IAudioPolicyService { boolean getMasterMono(); - float getStreamVolumeDB(AudioStreamType stream, int index, int /* audio_devices_t */ device); + float getStreamVolumeDB(AudioStreamType stream, int index, in AudioDeviceDescription device); /** * Populates supported surround formats and their enabled state in formats and formatsEnabled. @@ -281,7 +281,7 @@ interface IAudioPolicyService { * number of elements without actually retrieving them. */ void getSurroundFormats(inout Int count, - out AudioFormat[] formats, + out AudioFormatDescription[] formats, out boolean[] formatsEnabled); /** @@ -293,15 +293,16 @@ interface IAudioPolicyService { * number of elements without actually retrieving them. */ void getReportedSurroundFormats(inout Int count, - out AudioFormat[] formats); + out AudioFormatDescription[] formats); - AudioFormat[] getHwOffloadFormatsSupportedForBluetoothMedia(int /* audio_devices_t */ device); + AudioFormatDescription[] getHwOffloadFormatsSupportedForBluetoothMedia( + in AudioDeviceDescription device); - void setSurroundFormatEnabled(AudioFormat audioFormat, boolean enabled); + void setSurroundFormatEnabled(in AudioFormatDescription audioFormat, boolean enabled); - void setAssistantUid(int /* uid_t */ uid); + void setAssistantServicesUids(in int[] /* uid_t[] */ uids); - void setHotwordDetectionServiceUid(int /* uid_t */ uid); + void setActiveAssistantServicesUids(in int[] /* uid_t[] */ activeUids); void setA11yServicesUids(in int[] /* uid_t[] */ uids); @@ -309,6 +310,8 @@ interface IAudioPolicyService { boolean isHapticPlaybackSupported(); + boolean isUltrasoundSupported(); + AudioProductStrategy[] listAudioProductStrategies(); int /* product_strategy_t */ getProductStrategyFromAudioAttributes(in AudioAttributesEx aa, boolean fallbackOnDefault); @@ -331,22 +334,22 @@ interface IAudioPolicyService { AudioDevice[] getDevicesForRoleAndStrategy(int /* product_strategy_t */ strategy, DeviceRole role); - void setDevicesRoleForCapturePreset(AudioSourceType audioSource, + void setDevicesRoleForCapturePreset(AudioSource audioSource, DeviceRole role, in AudioDevice[] devices); - void addDevicesRoleForCapturePreset(AudioSourceType audioSource, + void addDevicesRoleForCapturePreset(AudioSource audioSource, DeviceRole role, in AudioDevice[] devices); - void removeDevicesRoleForCapturePreset(AudioSourceType audioSource, + void removeDevicesRoleForCapturePreset(AudioSource audioSource, DeviceRole role, in AudioDevice[] devices); - void clearDevicesRoleForCapturePreset(AudioSourceType audioSource, + void clearDevicesRoleForCapturePreset(AudioSource audioSource, DeviceRole role); - AudioDevice[] getDevicesForRoleAndCapturePreset(AudioSourceType audioSource, + AudioDevice[] getDevicesForRoleAndCapturePreset(AudioSource audioSource, DeviceRole role); boolean registerSoundTriggerCaptureStateListener(ICaptureStateListener listener); @@ -375,4 +378,20 @@ interface IAudioPolicyService { boolean canBeSpatialized(in @nullable AudioAttributesInternal attr, in @nullable AudioConfig config, in AudioDevice[] devices); + + /** + * Query how the direct playback is currently supported on the device. + */ + AudioDirectMode getDirectPlaybackSupport(in AudioAttributesInternal attr, + in AudioConfig config); + + /** + * Query audio profiles available for direct playback on the current output device(s) + * for the specified audio attributes. + */ + AudioProfile[] getDirectProfilesForAttributes(in AudioAttributesInternal attr); + + // When adding a new method, please review and update + // AudioPolicyService.cpp AudioPolicyService::onTransact() + // AudioPolicyService.cpp IAUDIOPOLICYSERVICE_BINDER_METHOD_MACRO_LIST } diff --git a/media/libaudioclient/aidl/android/media/IAudioPolicyServiceClient.aidl b/media/libaudioclient/aidl/android/media/IAudioPolicyServiceClient.aidl index a7782b86fd7485675970fbaea3a108c0a56b53f9..c0cdd96dd510804e24de5bcb23d4769782ad2816 100644 --- a/media/libaudioclient/aidl/android/media/IAudioPolicyServiceClient.aidl +++ b/media/libaudioclient/aidl/android/media/IAudioPolicyServiceClient.aidl @@ -16,10 +16,10 @@ package android.media; -import android.media.AudioConfigBase; -import android.media.AudioSourceType; import android.media.EffectDescriptor; import android.media.RecordClientInfo; +import android.media.audio.common.AudioConfigBase; +import android.media.audio.common.AudioSource; /** * {@hide} @@ -43,7 +43,10 @@ oneway interface IAudioPolicyServiceClient { in AudioConfigBase deviceConfig, in EffectDescriptor[] effects, int /* audio_patch_handle_t */ patchHandle, - AudioSourceType source); + AudioSource source); /** Notifies a change of audio routing */ void onRoutingUpdated(); + /** Notifies a request for volume index ranges to be reset after they were observed as invalid + */ + void onVolumeRangeInitRequest(); } diff --git a/media/libaudioclient/aidl/android/media/IEffect.aidl b/media/libaudioclient/aidl/android/media/IEffect.aidl index 813cd5c4fa164cfb7ba542c069546121ef99d3ec..6ec0405b8e0a20c5c25bee4a309368d4aeaa41ce 100644 --- a/media/libaudioclient/aidl/android/media/IEffect.aidl +++ b/media/libaudioclient/aidl/android/media/IEffect.aidl @@ -62,4 +62,8 @@ interface IEffect { * TODO(ytai): Explain how this should be used exactly. */ SharedFileRegion getCblk(); + + // When adding a new method, please review and update + // Effects.cpp AudioFlinger::EffectHandle::onTransact() + // Effects.cpp IEFFECT_BINDER_METHOD_MACRO_LIST } diff --git a/media/libaudioclient/aidl/android/media/ISpatializer.aidl b/media/libaudioclient/aidl/android/media/ISpatializer.aidl index b871238c9d201a7e257a54db60a7f3b1a683bcce..a61ad58eeb501568ec33e76e945c7b42b082349f 100644 --- a/media/libaudioclient/aidl/android/media/ISpatializer.aidl +++ b/media/libaudioclient/aidl/android/media/ISpatializer.aidl @@ -57,8 +57,10 @@ interface ISpatializer { boolean isHeadTrackingSupported(); /** Reports the list of supported head tracking modes (see SpatializerHeadTrackingMode.aidl). - * The list can be empty if the spatializer implementation does not support head tracking or if - * no head tracking sensor is registered (see setHeadSensor() and setScreenSensor()). + * The list always contains SpatializerHeadTrackingMode.DISABLED and can include other modes + * if the spatializer effect implementation supports head tracking. + * The result does not depend on currently connected sensors but reflects the capabilities + * when sensors are available. */ SpatializerHeadTrackingMode[] getSupportedHeadTrackingModes(); diff --git a/media/libaudioclient/aidl/android/media/OpenInputRequest.aidl b/media/libaudioclient/aidl/android/media/OpenInputRequest.aidl index 2e55526b03f560cb02acc23885dc8cb24fb78751..75ff8e926c16bc852b9553c305b7df5bfd078d56 100644 --- a/media/libaudioclient/aidl/android/media/OpenInputRequest.aidl +++ b/media/libaudioclient/aidl/android/media/OpenInputRequest.aidl @@ -16,9 +16,9 @@ package android.media; -import android.media.AudioConfig; -import android.media.AudioDevice; -import android.media.AudioSourceType; +import android.media.audio.common.AudioConfig; +import android.media.audio.common.AudioDevice; +import android.media.audio.common.AudioSource; /** * {@hide} @@ -30,7 +30,7 @@ parcelable OpenInputRequest { int input; AudioConfig config; AudioDevice device; - AudioSourceType source; + AudioSource source; /** Bitmask, indexed by AudioInputFlag. */ int flags; } diff --git a/media/libaudioclient/aidl/android/media/OpenInputResponse.aidl b/media/libaudioclient/aidl/android/media/OpenInputResponse.aidl index b613ba53eb64b71c7c58b8f12e6546de9819cc63..41bc38a147e631a617327315d15dc557078e0c84 100644 --- a/media/libaudioclient/aidl/android/media/OpenInputResponse.aidl +++ b/media/libaudioclient/aidl/android/media/OpenInputResponse.aidl @@ -16,8 +16,8 @@ package android.media; -import android.media.AudioConfig; -import android.media.AudioDevice; +import android.media.audio.common.AudioConfig; +import android.media.audio.common.AudioDevice; /** * {@hide} diff --git a/media/libaudioclient/aidl/android/media/OpenOutputRequest.aidl b/media/libaudioclient/aidl/android/media/OpenOutputRequest.aidl index 1541948fec39d2a3036e94586ed755a7c007dceb..90e7ea6d23a5c848fe2fba49459f49f3af3c13fd 100644 --- a/media/libaudioclient/aidl/android/media/OpenOutputRequest.aidl +++ b/media/libaudioclient/aidl/android/media/OpenOutputRequest.aidl @@ -16,9 +16,9 @@ package android.media; -import android.media.AudioConfig; -import android.media.AudioConfigBase; import android.media.AudioPort; +import android.media.audio.common.AudioConfig; +import android.media.audio.common.AudioConfigBase; /** * {@hide} diff --git a/media/libaudioclient/aidl/android/media/OpenOutputResponse.aidl b/media/libaudioclient/aidl/android/media/OpenOutputResponse.aidl index a051969ca684caac154c482b46ce7ac497ad195c..451a0bf70e972c5b3f041c27776eafcfba434518 100644 --- a/media/libaudioclient/aidl/android/media/OpenOutputResponse.aidl +++ b/media/libaudioclient/aidl/android/media/OpenOutputResponse.aidl @@ -16,7 +16,7 @@ package android.media; -import android.media.AudioConfig; +import android.media.audio.common.AudioConfig; /** * {@hide} diff --git a/media/libaudioclient/aidl/android/media/RecordClientInfo.aidl b/media/libaudioclient/aidl/android/media/RecordClientInfo.aidl index 328046072f335f886fb9c4c3c8f4b361f02f77ab..7dad58d6132f85b856e944bf0f21388f9a366ccd 100644 --- a/media/libaudioclient/aidl/android/media/RecordClientInfo.aidl +++ b/media/libaudioclient/aidl/android/media/RecordClientInfo.aidl @@ -16,7 +16,7 @@ package android.media; -import android.media.AudioSourceType; +import android.media.audio.common.AudioSource; /** * {@hide} @@ -28,7 +28,7 @@ parcelable RecordClientInfo { int uid; /** Interpreted as audio_session_t. */ int session; - AudioSourceType source; + AudioSource source; /** Interpreted as audio_port_handle_t. */ int portId; boolean silenced; diff --git a/media/libaudioclient/aidl/android/media/SoundTriggerSession.aidl b/media/libaudioclient/aidl/android/media/SoundTriggerSession.aidl index a829e593754bd9abc4ca0247045510f3abb538d0..4b540a91241b21ca29084a2b82ed2acdd3418dde 100644 --- a/media/libaudioclient/aidl/android/media/SoundTriggerSession.aidl +++ b/media/libaudioclient/aidl/android/media/SoundTriggerSession.aidl @@ -16,6 +16,8 @@ package android.media; +import android.media.audio.common.AudioDeviceDescription; + /** * {@hide} */ @@ -24,6 +26,6 @@ parcelable SoundTriggerSession { int session; /** Interpreted as audio_io_handle_t. */ int ioHandle; - /** Interpreted as audio_devices_t. */ - int device; + /** Device type. */ + AudioDeviceDescription device; } diff --git a/media/libaudioclient/aidl/android/media/SpatializationMode.aidl b/media/libaudioclient/aidl/android/media/SpatializationMode.aidl index 5d8fd9347f492ffcd75e711aa7f6f17746e25c64..eaaff372d6939df1e82585b605bfd2be99147c0e 100644 --- a/media/libaudioclient/aidl/android/media/SpatializationMode.aidl +++ b/media/libaudioclient/aidl/android/media/SpatializationMode.aidl @@ -24,7 +24,7 @@ package android.media; @Backing(type="byte") enum SpatializationMode { /** The spatializer supports binaural mode (over headphones type devices). */ - SPATIALIZATER_BINAURAL = 0, + SPATIALIZER_BINAURAL = 0, /** The spatializer supports transaural mode (over speaker type devices). */ - SPATIALIZATER_TRANSAURAL = 1, + SPATIALIZER_TRANSAURAL = 1, } diff --git a/media/libaudioclient/fuzzer/Android.bp b/media/libaudioclient/fuzzer/Android.bp index b290aa82e8da648f3533998aab4277088a5afbe6..969e3e677d53ae84116442415c662f6c43ed17e3 100644 --- a/media/libaudioclient/fuzzer/Android.bp +++ b/media/libaudioclient/fuzzer/Android.bp @@ -46,6 +46,7 @@ cc_fuzz { ], shared_libs: [ "android.hardware.audio.common-util", + "android.media.audio.common.types-V1-cpp", "audioclient-types-aidl-cpp", "audioflinger-aidl-cpp", "audiopolicy-aidl-cpp", diff --git a/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp b/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp index bd9e158cfa682fd6175c48e45ed432309aa875b7..036e72ec4dfd25b170c3885b46654acf6cec0461 100644 --- a/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp +++ b/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp @@ -58,7 +58,8 @@ constexpr audio_unique_id_use_t kUniqueIds[] = { constexpr audio_mode_t kModes[] = { AUDIO_MODE_INVALID, AUDIO_MODE_CURRENT, AUDIO_MODE_NORMAL, AUDIO_MODE_RINGTONE, - AUDIO_MODE_IN_CALL, AUDIO_MODE_IN_COMMUNICATION, AUDIO_MODE_CALL_SCREEN}; + AUDIO_MODE_IN_CALL, AUDIO_MODE_IN_COMMUNICATION, AUDIO_MODE_CALL_SCREEN, + AUDIO_MODE_CALL_REDIRECT, AUDIO_MODE_COMMUNICATION_REDIRECT}; constexpr audio_session_t kSessionId[] = {AUDIO_SESSION_NONE, AUDIO_SESSION_OUTPUT_STAGE, AUDIO_SESSION_DEVICE}; @@ -231,7 +232,7 @@ void AudioFlingerFuzzer::invokeAudioTrack() { attributionSource.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(getpid())); attributionSource.token = sp::make(); track->set(AUDIO_STREAM_DEFAULT, sampleRate, format, channelMask, frameCount, flags, nullptr, - nullptr, notificationFrames, sharedBuffer, false, sessionId, + notificationFrames, sharedBuffer, false, sessionId, ((fast && sharedBuffer == 0) || offload) ? AudioTrack::TRANSFER_CALLBACK : AudioTrack::TRANSFER_DEFAULT, offload ? &offloadInfo : nullptr, attributionSource, &attributes, false, 1.0f, @@ -314,7 +315,7 @@ void AudioFlingerFuzzer::invokeAudioRecord() { attributionSource.packageName = std::string(mFdp.ConsumeRandomLengthString().c_str()); attributionSource.token = sp::make(); sp record = new AudioRecord(attributionSource); - record->set(AUDIO_SOURCE_DEFAULT, sampleRate, format, channelMask, frameCount, nullptr, nullptr, + record->set(AUDIO_SOURCE_DEFAULT, sampleRate, format, channelMask, frameCount, nullptr, notificationFrames, false, sessionId, fast ? AudioRecord::TRANSFER_CALLBACK : AudioRecord::TRANSFER_DEFAULT, flags, getuid(), getpid(), &attributes, AUDIO_PORT_HANDLE_NONE); @@ -354,7 +355,7 @@ void AudioFlingerFuzzer::invokeAudioRecord() { audioBuffer.frameCount = static_cast(mFdp.ConsumeIntegral()); record->obtainBuffer(&audioBuffer, waitCount, &nonContig); bool blocking = false; - record->read(audioBuffer.raw, audioBuffer.size, blocking); + record->read(audioBuffer.data(), audioBuffer.size(), blocking); record->getInputFramesLost(); record->getFlags(); @@ -601,9 +602,10 @@ status_t AudioFlingerFuzzer::invokeAudioInputDevice() { media::OpenInputRequest request{}; request.module = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_module_handle_t_int32_t(module)); request.input = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_io_handle_t_int32_t(input)); - request.config = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_config_t_AudioConfig(config)); + request.config = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_config_t_AudioConfig(config, true /*isInput*/)); request.device = VALUE_OR_RETURN_STATUS(legacy2aidl_AudioDeviceTypeAddress(deviceTypeAddr)); - request.source = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_source_t_AudioSourceType(source)); + request.source = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_source_t_AudioSource(source)); request.flags = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_input_flags_t_int32_t_mask(flags)); media::OpenInputResponse response{}; @@ -658,9 +660,10 @@ status_t AudioFlingerFuzzer::invokeAudioOutputDevice() { media::OpenOutputResponse response{}; request.module = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_module_handle_t_int32_t(module)); - request.halConfig = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_config_t_AudioConfig(config)); - request.mixerConfig = - VALUE_OR_RETURN_STATUS(legacy2aidl_audio_config_base_t_AudioConfigBase(mixerConfig)); + request.halConfig = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_config_t_AudioConfig(config, false /*isInput*/)); + request.mixerConfig = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_config_base_t_AudioConfigBase(mixerConfig, false /*isInput*/)); request.device = VALUE_OR_RETURN_STATUS(legacy2aidl_DeviceDescriptorBase(device)); request.flags = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_output_flags_t_int32_t_mask(flags)); diff --git a/media/libaudioclient/include/media/AidlConversion.h b/media/libaudioclient/include/media/AidlConversion.h index 4ec69c71ae24695e877b1db02a10c4d9ff61da9b..e769303fc13c156b263ecdd3ad93e6ce1f2b87b6 100644 --- a/media/libaudioclient/include/media/AidlConversion.h +++ b/media/libaudioclient/include/media/AidlConversion.h @@ -23,34 +23,43 @@ #include #include -#include -#include +#include #include -#include -#include -#include #include -#include -#include -#include #include #include -#include -#include -#include #include #include -#include -#include -#include -#include -#include -#include +#include +#include #include #include #include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -86,19 +95,9 @@ ConversionResult legacy2aidl_audio_unique_id_t_int32_t(audio_unique_id_ ConversionResult aidl2legacy_int32_t_audio_hw_sync_t(int32_t aidl); ConversionResult legacy2aidl_audio_hw_sync_t_int32_t(audio_hw_sync_t legacy); -// The legacy enum is unnamed. Thus, we use int32_t. -ConversionResult aidl2legacy_AudioPortConfigType_int32_t( - media::AudioPortConfigType aidl); -// The legacy enum is unnamed. Thus, we use int32_t. -ConversionResult legacy2aidl_int32_t_AudioPortConfigType( - int32_t legacy); - ConversionResult aidl2legacy_int32_t_config_mask(int32_t aidl); ConversionResult legacy2aidl_config_mask_int32_t(unsigned int legacy); -ConversionResult aidl2legacy_int32_t_audio_channel_mask_t(int32_t aidl); -ConversionResult legacy2aidl_audio_channel_mask_t_int32_t(audio_channel_mask_t legacy); - ConversionResult aidl2legacy_int32_t_pid_t(int32_t aidl); ConversionResult legacy2aidl_pid_t_int32_t(pid_t legacy); @@ -116,10 +115,10 @@ aidl2legacy_optional_string_view_optional_String16(std::optional> legacy2aidl_optional_String16_optional_string(std::optional legacy); -ConversionResult aidl2legacy_AudioIoConfigEvent_audio_io_config_event( +ConversionResult aidl2legacy_AudioIoConfigEvent_audio_io_config_event_t( media::AudioIoConfigEvent aidl); -ConversionResult legacy2aidl_audio_io_config_event_AudioIoConfigEvent( - audio_io_config_event legacy); +ConversionResult legacy2aidl_audio_io_config_event_t_AudioIoConfigEvent( + audio_io_config_event_t legacy); ConversionResult aidl2legacy_AudioPortRole_audio_port_role_t( media::AudioPortRole aidl); @@ -131,36 +130,59 @@ ConversionResult aidl2legacy_AudioPortType_audio_port_type_t( ConversionResult legacy2aidl_audio_port_type_t_AudioPortType( audio_port_type_t legacy); -ConversionResult aidl2legacy_AudioFormat_audio_format_t( - media::audio::common::AudioFormat aidl); -ConversionResult legacy2aidl_audio_format_t_AudioFormat( - audio_format_t legacy); +ConversionResult aidl2legacy_AudioChannelLayout_audio_channel_mask_t( + const media::audio::common::AudioChannelLayout& aidl, bool isInput); +ConversionResult +legacy2aidl_audio_channel_mask_t_AudioChannelLayout(audio_channel_mask_t legacy, bool isInput); + +ConversionResult aidl2legacy_AudioDeviceDescription_audio_devices_t( + const media::audio::common::AudioDeviceDescription& aidl); +ConversionResult +legacy2aidl_audio_devices_t_AudioDeviceDescription(audio_devices_t legacy); + +status_t aidl2legacy_AudioDevice_audio_device( + const media::audio::common::AudioDevice& aidl, + audio_devices_t* legacyType, char* legacyAddress); +status_t aidl2legacy_AudioDevice_audio_device( + const media::audio::common::AudioDevice& aidl, + audio_devices_t* legacyType, String8* legacyAddress); +status_t aidl2legacy_AudioDevice_audio_device( + const media::audio::common::AudioDevice& aidl, + audio_devices_t* legacyType, std::string* legacyAddress); +ConversionResult +legacy2aidl_audio_device_AudioDevice( + audio_devices_t legacyType, const char* legacyAddress); +ConversionResult +legacy2aidl_audio_device_AudioDevice( + audio_devices_t legacyType, const String8& legacyAddress); + +ConversionResult aidl2legacy_AudioFormatDescription_audio_format_t( + const media::audio::common::AudioFormatDescription& aidl); +ConversionResult +legacy2aidl_audio_format_t_AudioFormatDescription(audio_format_t legacy); ConversionResult -aidl2legacy_AudioGainMode_audio_gain_mode_t(media::AudioGainMode aidl); -ConversionResult +aidl2legacy_AudioGainMode_audio_gain_mode_t(media::audio::common::AudioGainMode aidl); +ConversionResult legacy2aidl_audio_gain_mode_t_AudioGainMode(audio_gain_mode_t legacy); ConversionResult aidl2legacy_int32_t_audio_gain_mode_t_mask(int32_t aidl); ConversionResult legacy2aidl_audio_gain_mode_t_int32_t_mask(audio_gain_mode_t legacy); -ConversionResult aidl2legacy_int32_t_audio_devices_t(int32_t aidl); -ConversionResult legacy2aidl_audio_devices_t_int32_t(audio_devices_t legacy); - ConversionResult aidl2legacy_AudioGainConfig_audio_gain_config( - const media::AudioGainConfig& aidl, media::AudioPortRole role, media::AudioPortType type); -ConversionResult legacy2aidl_audio_gain_config_AudioGainConfig( - const audio_gain_config& legacy, audio_port_role_t role, audio_port_type_t type); + const media::audio::common::AudioGainConfig& aidl, bool isInput); +ConversionResult +legacy2aidl_audio_gain_config_AudioGainConfig(const audio_gain_config& legacy, bool isInput); -ConversionResult aidl2legacy_AudioInputFlags_audio_input_flags_t( - media::AudioInputFlags aidl); -ConversionResult legacy2aidl_audio_input_flags_t_AudioInputFlags( - audio_input_flags_t legacy); +ConversionResult +aidl2legacy_AudioInputFlags_audio_input_flags_t(media::audio::common::AudioInputFlags aidl); +ConversionResult +legacy2aidl_audio_input_flags_t_AudioInputFlags(audio_input_flags_t legacy); -ConversionResult aidl2legacy_AudioOutputFlags_audio_output_flags_t( - media::AudioOutputFlags aidl); -ConversionResult legacy2aidl_audio_output_flags_t_AudioOutputFlags( - audio_output_flags_t legacy); +ConversionResult +aidl2legacy_AudioOutputFlags_audio_output_flags_t(media::audio::common::AudioOutputFlags aidl); +ConversionResult +legacy2aidl_audio_output_flags_t_AudioOutputFlags(audio_output_flags_t legacy); ConversionResult aidl2legacy_int32_t_audio_input_flags_t_mask( int32_t aidl); @@ -173,40 +195,43 @@ ConversionResult legacy2aidl_audio_output_flags_t_int32_t_mask( audio_output_flags_t legacy); ConversionResult aidl2legacy_AudioIoFlags_audio_io_flags( - const media::AudioIoFlags& aidl, media::AudioPortRole role, media::AudioPortType type); -ConversionResult legacy2aidl_audio_io_flags_AudioIoFlags( - const audio_io_flags& legacy, audio_port_role_t role, audio_port_type_t type); + const media::audio::common::AudioIoFlags& aidl, bool isInput); +ConversionResult legacy2aidl_audio_io_flags_AudioIoFlags( + const audio_io_flags& legacy, bool isInput); ConversionResult -aidl2legacy_AudioPortConfigDeviceExt_audio_port_config_device_ext( - const media::AudioPortConfigDeviceExt& aidl); -ConversionResult -legacy2aidl_audio_port_config_device_ext_AudioPortConfigDeviceExt( - const audio_port_config_device_ext& legacy); +aidl2legacy_AudioPortDeviceExt_audio_port_config_device_ext( + const media::audio::common::AudioPortDeviceExt& aidl, + const media::AudioPortDeviceExtSys& aidlDeviceExt); +status_t legacy2aidl_audio_port_config_device_ext_AudioPortDeviceExt( + const audio_port_config_device_ext& legacy, + media::audio::common::AudioPortDeviceExt* aidl, + media::AudioPortDeviceExtSys* aidlDeviceExt); ConversionResult aidl2legacy_AudioStreamType_audio_stream_type_t( - media::AudioStreamType aidl); -ConversionResult legacy2aidl_audio_stream_type_t_AudioStreamType( - audio_stream_type_t legacy); - -ConversionResult aidl2legacy_AudioSourceType_audio_source_t( - media::AudioSourceType aidl); -ConversionResult legacy2aidl_audio_source_t_AudioSourceType( + media::audio::common::AudioStreamType aidl); +ConversionResult +legacy2aidl_audio_stream_type_t_AudioStreamType(audio_stream_type_t legacy); + +ConversionResult aidl2legacy_AudioSource_audio_source_t( + media::audio::common::AudioSource aidl); +ConversionResult + legacy2aidl_audio_source_t_AudioSource( audio_source_t legacy); ConversionResult aidl2legacy_int32_t_audio_session_t(int32_t aidl); ConversionResult legacy2aidl_audio_session_t_int32_t(audio_session_t legacy); -ConversionResult aidl2legacy_AudioPortConfigMixExt( - const media::AudioPortConfigMixExt& aidl, media::AudioPortRole role); -ConversionResult legacy2aidl_AudioPortConfigMixExt( - const audio_port_config_mix_ext& legacy, audio_port_role_t role); +ConversionResult aidl2legacy_AudioPortMixExt( + const media::audio::common::AudioPortMixExt& aidl, media::AudioPortRole role, + const media::AudioPortMixExtSys& aidlMixExt); +status_t legacy2aidl_AudioPortMixExt( + const audio_port_config_mix_ext& legacy, audio_port_role_t role, + media::audio::common::AudioPortMixExt* aidl, media::AudioPortMixExtSys* aidlMixExt); ConversionResult -aidl2legacy_AudioPortConfigSessionExt_audio_port_config_session_ext( - const media::AudioPortConfigSessionExt& aidl); -ConversionResult -legacy2aidl_audio_port_config_session_ext_AudioPortConfigSessionExt( +aidl2legacy_int32_t_audio_port_config_session_ext(int32_t aidl); +ConversionResult legacy2aidl_audio_port_config_session_ext_AudioPortConfigSessionExt( const audio_port_config_session_ext& legacy); ConversionResult aidl2legacy_AudioPortConfig_audio_port_config( @@ -221,7 +246,6 @@ ConversionResult legacy2aidl_audio_patch_AudioPatch( ConversionResult> aidl2legacy_AudioIoDescriptor_AudioIoDescriptor( const media::AudioIoDescriptor& aidl); - ConversionResult legacy2aidl_AudioIoDescriptor_AudioIoDescriptor( const sp& legacy); @@ -231,13 +255,14 @@ ConversionResult legacy2aidl_AudioClient_AudioClient( const AudioClient& legacy); ConversionResult -aidl2legacy_AudioContentType_audio_content_type_t(media::AudioContentType aidl); -ConversionResult +aidl2legacy_AudioContentType_audio_content_type_t( + media::audio::common::AudioContentType aidl); +ConversionResult legacy2aidl_audio_content_type_t_AudioContentType(audio_content_type_t legacy); ConversionResult -aidl2legacy_AudioUsage_audio_usage_t(media::AudioUsage aidl); -ConversionResult +aidl2legacy_AudioUsage_audio_usage_t(media::audio::common::AudioUsage aidl); +ConversionResult legacy2aidl_audio_usage_t_AudioUsage(audio_usage_t legacy); ConversionResult @@ -256,24 +281,27 @@ ConversionResult legacy2aidl_audio_attributes_t_AudioAttributesInternal(const audio_attributes_t& legacy); ConversionResult -aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t(media::AudioEncapsulationMode aidl); -ConversionResult +aidl2legacy_AudioEncapsulationMode_audio_encapsulation_mode_t( + media::audio::common::AudioEncapsulationMode aidl); +ConversionResult legacy2aidl_audio_encapsulation_mode_t_AudioEncapsulationMode(audio_encapsulation_mode_t legacy); ConversionResult -aidl2legacy_AudioOffloadInfo_audio_offload_info_t(const media::AudioOffloadInfo& aidl); -ConversionResult +aidl2legacy_AudioOffloadInfo_audio_offload_info_t( + const media::audio::common::AudioOffloadInfo& aidl); +ConversionResult legacy2aidl_audio_offload_info_t_AudioOffloadInfo(const audio_offload_info_t& legacy); ConversionResult -aidl2legacy_AudioConfig_audio_config_t(const media::AudioConfig& aidl); -ConversionResult -legacy2aidl_audio_config_t_AudioConfig(const audio_config_t& legacy); +aidl2legacy_AudioConfig_audio_config_t(const media::audio::common::AudioConfig& aidl, bool isInput); +ConversionResult +legacy2aidl_audio_config_t_AudioConfig(const audio_config_t& legacy, bool isInput); ConversionResult -aidl2legacy_AudioConfigBase_audio_config_base_t(const media::AudioConfigBase& aidl); -ConversionResult -legacy2aidl_audio_config_base_t_AudioConfigBase(const audio_config_base_t& legacy); +aidl2legacy_AudioConfigBase_audio_config_base_t( + const media::audio::common::AudioConfigBase& aidl, bool isInput); +ConversionResult +legacy2aidl_audio_config_base_t_AudioConfigBase(const audio_config_base_t& legacy, bool isInput); ConversionResult> aidl2legacy_SharedFileRegion_IMemory(const media::SharedFileRegion& aidl); @@ -291,8 +319,8 @@ ConversionResult legacy2aidl_AudioTimestamp_AudioTimestampInternal(const AudioTimestamp& legacy); ConversionResult -aidl2legacy_AudioUuid_audio_uuid_t(const media::AudioUuid& aidl); -ConversionResult +aidl2legacy_AudioUuid_audio_uuid_t(const media::audio::common::AudioUuid& aidl); +ConversionResult legacy2aidl_audio_uuid_t_AudioUuid(const audio_uuid_t& legacy); ConversionResult @@ -302,8 +330,8 @@ legacy2aidl_effect_descriptor_t_EffectDescriptor(const effect_descriptor_t& lega ConversionResult aidl2legacy_AudioEncapsulationMetadataType_audio_encapsulation_metadata_type_t( - media::AudioEncapsulationMetadataType aidl); -ConversionResult + media::audio::common::AudioEncapsulationMetadataType aidl); +ConversionResult legacy2aidl_audio_encapsulation_metadata_type_t_AudioEncapsulationMetadataType( audio_encapsulation_metadata_type_t legacy); @@ -317,37 +345,39 @@ aidl2legacy_AudioEncapsulationMetadataType_mask(int32_t aidl); ConversionResult legacy2aidl_AudioEncapsulationMetadataType_mask(uint32_t legacy); -ConversionResult -aidl2legacy_AudioMixLatencyClass_audio_mix_latency_class_t( - media::AudioMixLatencyClass aidl); -ConversionResult -legacy2aidl_audio_mix_latency_class_t_AudioMixLatencyClass( - audio_mix_latency_class_t legacy); - ConversionResult -aidl2legacy_AudioPortDeviceExt_audio_port_device_ext(const media::AudioPortDeviceExt& aidl); -ConversionResult -legacy2aidl_audio_port_device_ext_AudioPortDeviceExt(const audio_port_device_ext& legacy); +aidl2legacy_AudioPortDeviceExt_audio_port_device_ext( + const media::audio::common::AudioPortDeviceExt& aidl, + const media::AudioPortDeviceExtSys& aidlDeviceExt); +status_t legacy2aidl_audio_port_device_ext_AudioPortDeviceExt( + const audio_port_device_ext& legacy, + media::audio::common::AudioPortDeviceExt* aidl, + media::AudioPortDeviceExtSys* aidlDeviceExt); ConversionResult -aidl2legacy_AudioPortMixExt_audio_port_mix_ext(const media::AudioPortMixExt& aidl); -ConversionResult -legacy2aidl_audio_port_mix_ext_AudioPortMixExt(const audio_port_mix_ext& legacy); +aidl2legacy_AudioPortMixExt_audio_port_mix_ext( + const media::audio::common::AudioPortMixExt& aidl, + const media::AudioPortMixExtSys& aidlMixExt); +status_t legacy2aidl_audio_port_mix_ext_AudioPortMixExt( + const audio_port_mix_ext& legacy, + media::audio::common::AudioPortMixExt* aidl, + media::AudioPortMixExtSys* aidlMixExt); ConversionResult -aidl2legacy_AudioPortSessionExt_audio_port_session_ext(const media::AudioPortSessionExt& aidl); -ConversionResult -legacy2aidl_audio_port_session_ext_AudioPortSessionExt(const audio_port_session_ext& legacy); +aidl2legacy_int32_t_audio_port_session_ext(int32_t aidl); +ConversionResult +legacy2aidl_audio_port_session_ext_int32_t(const audio_port_session_ext& legacy); ConversionResult -aidl2legacy_AudioProfile_audio_profile(const media::AudioProfile& aidl); -ConversionResult -legacy2aidl_audio_profile_AudioProfile(const audio_profile& legacy); +aidl2legacy_AudioProfile_audio_profile( + const media::audio::common::AudioProfile& aidl, bool isInput); +ConversionResult +legacy2aidl_audio_profile_AudioProfile(const audio_profile& legacy, bool isInput); ConversionResult -aidl2legacy_AudioGain_audio_gain(const media::AudioGain& aidl); -ConversionResult -legacy2aidl_audio_gain_AudioGain(const audio_gain& legacy); +aidl2legacy_AudioGain_audio_gain(const media::audio::common::AudioGain& aidl, bool isInput); +ConversionResult +legacy2aidl_audio_gain_AudioGain(const audio_gain& legacy, bool isInput); ConversionResult aidl2legacy_AudioPort_audio_port_v7(const media::AudioPort& aidl); @@ -355,8 +385,8 @@ ConversionResult legacy2aidl_audio_port_v7_AudioPort(const audio_port_v7& legacy); ConversionResult -aidl2legacy_AudioMode_audio_mode_t(media::AudioMode aidl); -ConversionResult +aidl2legacy_AudioMode_audio_mode_t(media::audio::common::AudioMode aidl); +ConversionResult legacy2aidl_audio_mode_t_AudioMode(audio_mode_t legacy); ConversionResult @@ -390,21 +420,21 @@ ConversionResult legacy2aidl_audio_playback_rate_t_AudioPlaybackRate(const audio_playback_rate_t& legacy); ConversionResult -aidl2legacy_AudioStandard_audio_standard_t(media::AudioStandard aidl); -ConversionResult +aidl2legacy_AudioStandard_audio_standard_t(media::audio::common::AudioStandard aidl); +ConversionResult legacy2aidl_audio_standard_t_AudioStandard(audio_standard_t legacy); ConversionResult aidl2legacy_ExtraAudioDescriptor_audio_extra_audio_descriptor( - const media::ExtraAudioDescriptor& aidl); -ConversionResult + const media::audio::common::ExtraAudioDescriptor& aidl); +ConversionResult legacy2aidl_audio_extra_audio_descriptor_ExtraAudioDescriptor( const audio_extra_audio_descriptor& legacy); ConversionResult aidl2legacy_AudioEncapsulationType_audio_encapsulation_type_t( - const media::AudioEncapsulationType& aidl); -ConversionResult + const media::audio::common::AudioEncapsulationType& aidl); +ConversionResult legacy2aidl_audio_encapsulation_type_t_AudioEncapsulationType( const audio_encapsulation_type_t & legacy); @@ -416,5 +446,13 @@ ConversionResult legacy2aidl_TrackSecondaryOutputInfoPair_TrackSecondaryOutputInfo( const TrackSecondaryOutputInfoPair& legacy); +ConversionResult +aidl2legacy_AudioDirectMode_audio_direct_mode_t(media::AudioDirectMode aidl); +ConversionResult +legacy2aidl_audio_direct_mode_t_AudioDirectMode(audio_direct_mode_t legacy); + +ConversionResult aidl2legacy_int32_t_audio_direct_mode_t_mask(int32_t aidl); +ConversionResult legacy2aidl_audio_direct_mode_t_int32_t_mask(audio_direct_mode_t legacy); + } // namespace android diff --git a/media/libaudioclient/include/media/AidlConversionUtil.h b/media/libaudioclient/include/media/AidlConversionUtil.h index c1a2be33fc059185644ff0c43aadc82499846932..8817c359befa1718f7aa635dd0e04d11f5683579 100644 --- a/media/libaudioclient/include/media/AidlConversionUtil.h +++ b/media/libaudioclient/include/media/AidlConversionUtil.h @@ -20,42 +20,14 @@ #include #include -#include +#include #include +#include namespace android { template -using ConversionResult = base::expected; - -// Convenience macros for working with ConversionResult, useful for writing converted for aggregate -// types. - -#define VALUE_OR_RETURN(result) \ - ({ \ - auto _tmp = (result); \ - if (!_tmp.ok()) return base::unexpected(_tmp.error()); \ - std::move(_tmp.value()); \ - }) - -#define RETURN_IF_ERROR(result) \ - if (status_t _tmp = (result); _tmp != OK) return base::unexpected(_tmp); - -#define VALUE_OR_RETURN_STATUS(x) \ - ({ \ - auto _tmp = (x); \ - if (!_tmp.ok()) return _tmp.error(); \ - std::move(_tmp.value()); \ - }) - -#define VALUE_OR_FATAL(result) \ - ({ \ - auto _tmp = (result); \ - LOG_ALWAYS_FATAL_IF(!_tmp.ok(), \ - "Function: %s Line: %d Failed result (%d)",\ - __FUNCTION__, __LINE__, _tmp.error()); \ - std::move(_tmp.value()); \ - }) +using ConversionResult = error::Result; /** * A generic template to safely cast between integral types, respecting limits of the destination @@ -105,6 +77,26 @@ status_t convertRange(InputIterator start, return OK; } +/** + * A generic template that helps convert containers of convertible types, using iterators. + * Uses a limit as maximum conversion items. + */ +template +status_t convertRangeWithLimit(InputIterator start, + InputIterator end, + OutputIterator out, + const Func& itemConversion, + const size_t limit) { + InputIterator last = end; + if (end - start > limit) { + last = start + limit; + } + for (InputIterator iter = start; (iter != last); ++iter, ++out) { + *out = VALUE_OR_RETURN_STATUS(itemConversion(*iter)); + } + return OK; +} + /** * A generic template that helps convert containers of convertible types. */ @@ -119,6 +111,62 @@ convertContainer(const InputContainer& input, const Func& itemConversion) { return output; } +/** + * A generic template that helps convert containers of convertible types + * using an item conversion function with an additional parameter. + */ +template +ConversionResult +convertContainer(const InputContainer& input, const Func& itemConversion, const Parameter& param) { + OutputContainer output; + auto ins = std::inserter(output, output.begin()); + for (const auto& item : input) { + *ins = VALUE_OR_RETURN(itemConversion(item, param)); + } + return output; +} + +/** + * A generic template that helps to "zip" two input containers of the same size + * into a single vector of converted types. The conversion function must + * thus accept two arguments. + */ +template +ConversionResult +convertContainers(const InputContainer1& input1, const InputContainer2& input2, + const Func& itemConversion) { + auto iter2 = input2.begin(); + OutputContainer output; + auto ins = std::inserter(output, output.begin()); + for (const auto& item1 : input1) { + RETURN_IF_ERROR(iter2 != input2.end() ? OK : BAD_VALUE); + *ins = VALUE_OR_RETURN(itemConversion(item1, *iter2++)); + } + return output; +} + +/** + * A generic template that helps to "unzip" a per-element conversion into + * a pair of elements into a pair of containers. The conversion function + * must emit a pair of elements. + */ +template +ConversionResult> +convertContainerSplit(const InputContainer& input, const Func& itemConversion) { + OutputContainer1 output1; + OutputContainer2 output2; + auto ins1 = std::inserter(output1, output1.begin()); + auto ins2 = std::inserter(output2, output2.begin()); + for (const auto& item : input) { + auto out_pair = VALUE_OR_RETURN(itemConversion(item)); + *ins1 = out_pair.first; + *ins2 = out_pair.second; + } + return std::make_pair(output1, output2); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// // The code below establishes: // IntegralTypeOf, which works for either integral types (in which case it evaluates to T), or @@ -222,6 +270,29 @@ ConversionResult> unionGetField(const T& u) { namespace aidl_utils { +/** + * Return true if the value is valid for the AIDL enumeration. + */ +template +bool isValidEnum(T value) { + constexpr android::enum_range er{}; + return std::find(er.begin(), er.end(), value) != er.end(); +} + +// T is a "container" of enum binder types with a toString(). +template +std::string enumsToString(const T& t) { + std::string s; + for (const auto item : t) { + if (s.empty()) { + s = toString(item); + } else { + s.append("|").append(toString(item)); + } + } + return s; +} + /** * Return the equivalent Android status_t from a binder exception code. * diff --git a/media/libaudioclient/include/media/AudioCommonTypes.h b/media/libaudioclient/include/media/AudioCommonTypes.h index 5f0c5909fe7e004f502bc9dcfc26dea93ef7a3e7..862a0f9ca399db8d06a2f46032f12d829c385e2f 100644 --- a/media/libaudioclient/include/media/AudioCommonTypes.h +++ b/media/libaudioclient/include/media/AudioCommonTypes.h @@ -17,9 +17,75 @@ #pragma once +#include + +#include +#include +#include +#include #include #include -#include + +namespace { +// see boost::hash_combine +#if defined(__clang__) +__attribute__((no_sanitize("unsigned-integer-overflow"))) +#endif +static size_t hash_combine(size_t seed, size_t v) { + return std::hash{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); +} +} + +namespace std { + +// Note: when extending the types hashed below we need to account for the +// possibility of processing types belonging to different versions of the type, +// e.g. a HAL may be using a previous version of the AIDL interface. + +template<> struct hash +{ + std::size_t operator()( + const android::media::audio::common::AudioChannelLayout& acl) const noexcept { + using Tag = android::media::audio::common::AudioChannelLayout::Tag; + const size_t seed = std::hash{}(acl.getTag()); + switch (acl.getTag()) { + case Tag::none: + return hash_combine(seed, std::hash{}(acl.get())); + case Tag::invalid: + return hash_combine(seed, std::hash{}(acl.get())); + case Tag::indexMask: + return hash_combine(seed, std::hash{}(acl.get())); + case Tag::layoutMask: + return hash_combine(seed, std::hash{}(acl.get())); + case Tag::voiceMask: + return hash_combine(seed, std::hash{}(acl.get())); + } + return seed; + } +}; + +template<> struct hash +{ + std::size_t operator()( + const android::media::audio::common::AudioDeviceDescription& add) const noexcept { + return hash_combine( + std::hash{}(add.type), + std::hash{}(add.connection)); + } +}; + +template<> struct hash +{ + std::size_t operator()( + const android::media::audio::common::AudioFormatDescription& afd) const noexcept { + return hash_combine( + std::hash{}(afd.type), + hash_combine( + std::hash{}(afd.pcm), + std::hash{}(afd.encoding))); + } +}; +} // namespace std namespace android { @@ -81,4 +147,3 @@ enum volume_group_t : uint32_t; static const volume_group_t VOLUME_GROUP_NONE = static_cast(-1); } // namespace android - diff --git a/media/libaudioclient/include/media/AudioEffect.h b/media/libaudioclient/include/media/AudioEffect.h index 02ff43ffec587675f9155b8d6279035349441039..ca35543720d990959e1fda5c4e171815230c3241 100644 --- a/media/libaudioclient/include/media/AudioEffect.h +++ b/media/libaudioclient/include/media/AudioEffect.h @@ -40,7 +40,7 @@ struct effect_param_cblk_t; // ---------------------------------------------------------------------------- -class AudioEffect : public RefBase +class AudioEffect : public virtual RefBase { public: @@ -278,7 +278,7 @@ public: static status_t removeStreamDefaultEffect(audio_unique_id_t id); /* - * Events used by callback function (effect_callback_t). + * Events used by callback function (legacy_callback_t). */ enum event_type { EVENT_CONTROL_STATUS_CHANGED = 0, @@ -288,6 +288,47 @@ public: EVENT_FRAMES_PROCESSED = 4, }; + class IAudioEffectCallback : public virtual RefBase { + friend AudioEffect; + protected: + /* The event is received when an application loses or + * gains the control of the effect engine. Loss of control happens + * if another application requests the use of the engine by creating an AudioEffect for + * the same effect type but with a higher priority. Control is returned when the + * application having the control deletes its AudioEffect object. + * @param isGranted: True if control has been granted. False if stolen. + */ + virtual void onControlStatusChanged([[maybe_unused]] bool isGranted) {} + + /* The event is received by all applications not having the + * control of the effect engine when the effect is enabled or disabled. + * @param isEnabled: True if enabled. False if disabled. + */ + virtual void onEnableStatusChanged([[maybe_unused]] bool isEnabled) {} + + /* The event is received by all applications not having the + * control of the effect engine when an effect parameter is changed. + * @param param: A vector containing the raw bytes of a effect_param_type containing + * raw data representing a param type, value pair. + */ + // TODO pass an AIDL parcel instead of effect_param_type + virtual void onParameterChanged([[maybe_unused]] std::vector param) {} + + /* The event is received when the binder connection to the mediaserver + * is no longer valid. Typically the server has been killed. + * @param errorCode: A code representing the type of error. + */ + virtual void onError([[maybe_unused]] status_t errorCode) {} + + + /* The event is received when the audio server has processed a block of + * data. + * @param framesProcessed: The number of frames the audio server has + * processed. + */ + virtual void onFramesProcessed([[maybe_unused]] int32_t framesProcessed) {} + }; + /* Callback function notifying client application of a change in effect engine state or * configuration. * An effect engine can be shared by several applications but only one has the control @@ -316,7 +357,7 @@ public: * - EVENT_ERROR: status_t indicating the error (DEAD_OBJECT when media server dies). */ - typedef void (*effect_callback_t)(int32_t event, void* user, void *info); + typedef void (*legacy_callback_t)(int32_t event, void* user, void *info); /* Constructor. @@ -361,7 +402,7 @@ public: * priority: requested priority for effect control: the priority level corresponds to the * value of priority parameter: negative values indicate lower priorities, positive values * higher priorities, 0 being the normal priority. - * cbf: optional callback function (see effect_callback_t) + * cbf: optional callback function (see legacy_callback_t) * user: pointer to context for use by the callback receiver. * sessionId: audio session this effect is associated to. * If equal to AUDIO_SESSION_OUTPUT_MIX, the effect will be global to @@ -384,10 +425,20 @@ public: * - NO_INIT: audio flinger or audio hardware not initialized */ status_t set(const effect_uuid_t *type, - const effect_uuid_t *uuid = NULL, + const effect_uuid_t *uuid = nullptr, int32_t priority = 0, - effect_callback_t cbf = NULL, - void* user = NULL, + const wp& callback = nullptr, + audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX, + audio_io_handle_t io = AUDIO_IO_HANDLE_NONE, + const AudioDeviceTypeAddr& device = {}, + bool probe = false, + bool notifyFramesProcessed = false); + + status_t set(const effect_uuid_t *type, + const effect_uuid_t *uuid, + int32_t priority, + legacy_callback_t cbf, + void* user, audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX, audio_io_handle_t io = AUDIO_IO_HANDLE_NONE, const AudioDeviceTypeAddr& device = {}, @@ -397,10 +448,21 @@ public: * Same as above but with type and uuid specified by character strings. */ status_t set(const char *typeStr, - const char *uuidStr = NULL, + const char *uuidStr = nullptr, int32_t priority = 0, - effect_callback_t cbf = NULL, - void* user = NULL, + const wp& callback = nullptr, + audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX, + audio_io_handle_t io = AUDIO_IO_HANDLE_NONE, + const AudioDeviceTypeAddr& device = {}, + bool probe = false, + bool notifyFramesProcessed = false); + + + status_t set(const char *typeStr, + const char *uuidStr, + int32_t priority, + legacy_callback_t cbf, + void* user, audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX, audio_io_handle_t io = AUDIO_IO_HANDLE_NONE, const AudioDeviceTypeAddr& device = {}, @@ -536,18 +598,19 @@ public: protected: android::content::AttributionSourceState mClientAttributionSource; // source for app op checks. - bool mEnabled = false; // enable state - audio_session_t mSessionId = AUDIO_SESSION_OUTPUT_MIX; // audio session ID - int32_t mPriority = 0; // priority for effect control - status_t mStatus = NO_INIT; // effect status - bool mProbe = false; // effect created in probe mode: all commands - // are no ops because mIEffect is NULL - effect_callback_t mCbf = nullptr; // callback function for status, control and - // parameter changes notifications - void* mUserData = nullptr;// client context for callback function - effect_descriptor_t mDescriptor = {}; // effect descriptor - int32_t mId = -1; // system wide unique effect engine instance ID - Mutex mLock; // Mutex for mEnabled access + bool mEnabled = false; // enable state + audio_session_t mSessionId = AUDIO_SESSION_OUTPUT_MIX; // audio session ID + int32_t mPriority = 0; // priority for effect control + status_t mStatus = NO_INIT; // effect status + bool mProbe = false; // effect created in probe mode: all commands + // are no ops because mIEffect is nullptr + + wp mCallback = nullptr; // callback interface for status, control and + // parameter changes notifications + sp mLegacyWrapper = nullptr; + effect_descriptor_t mDescriptor = {}; // effect descriptor + int32_t mId = -1; // system wide unique effect engine instance ID + Mutex mLock; // Mutex for mEnabled access // IEffectClient diff --git a/media/libaudioclient/include/media/AudioIoDescriptor.h b/media/libaudioclient/include/media/AudioIoDescriptor.h index 981d33a39aeee22238219d72428172e7b529baa0..405ec7d440fe2e92b51285da2032d308e040256d 100644 --- a/media/libaudioclient/include/media/AudioIoDescriptor.h +++ b/media/libaudioclient/include/media/AudioIoDescriptor.h @@ -17,9 +17,15 @@ #ifndef ANDROID_AUDIO_IO_DESCRIPTOR_H #define ANDROID_AUDIO_IO_DESCRIPTOR_H +#include +#include + +#include +#include + namespace android { -enum audio_io_config_event { +enum audio_io_config_event_t { AUDIO_OUTPUT_REGISTERED, AUDIO_OUTPUT_OPENED, AUDIO_OUTPUT_CLOSED, @@ -33,41 +39,70 @@ enum audio_io_config_event { // audio input/output descriptor used to cache output configurations in client process to avoid // frequent calls through IAudioFlinger -class AudioIoDescriptor : public RefBase { +class AudioIoDescriptor : public virtual RefBase { public: - AudioIoDescriptor() : - mIoHandle(AUDIO_IO_HANDLE_NONE), - mSamplingRate(0), mFormat(AUDIO_FORMAT_DEFAULT), mChannelMask(AUDIO_CHANNEL_NONE), - mFrameCount(0), mFrameCountHAL(0), mLatency(0), mPortId(AUDIO_PORT_HANDLE_NONE) - { - memset(&mPatch, 0, sizeof(struct audio_patch)); - } + AudioIoDescriptor() = default; + // For AUDIO_{INPUT|OUTPUT}_CLOSED events. + AudioIoDescriptor(audio_io_handle_t ioHandle) : mIoHandle(ioHandle) {} + // For AUDIO_CLIENT_STARTED events. + AudioIoDescriptor( + audio_io_handle_t ioHandle, const audio_patch& patch, audio_port_handle_t portId) : + mIoHandle(ioHandle), mPatch(patch), mPortId(portId) {} + // For everything else. + AudioIoDescriptor( + audio_io_handle_t ioHandle, const audio_patch& patch, bool isInput, + uint32_t samplingRate, audio_format_t format, audio_channel_mask_t channelMask, + size_t frameCount, size_t frameCountHal, uint32_t latency = 0, + audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE) : + mIoHandle(ioHandle), mPatch(patch), mIsInput(isInput), + mSamplingRate(samplingRate), mFormat(format), mChannelMask(channelMask), + mFrameCount(frameCount), mFrameCountHAL(frameCountHal), mLatency(latency), + mPortId(portId) {} - virtual ~AudioIoDescriptor() {} - - audio_port_handle_t getDeviceId() { + audio_io_handle_t getIoHandle() const { return mIoHandle; } + const audio_patch& getPatch() const { return mPatch; } + bool getIsInput() const { return mIsInput; } + uint32_t getSamplingRate() const { return mSamplingRate; } + audio_format_t getFormat() const { return mFormat; } + audio_channel_mask_t getChannelMask() const { return mChannelMask; } + size_t getFrameCount() const { return mFrameCount; } + size_t getFrameCountHAL() const { return mFrameCountHAL; } + uint32_t getLatency() const { return mLatency; } + audio_port_handle_t getPortId() const { return mPortId; } + audio_port_handle_t getDeviceId() const { if (mPatch.num_sources != 0 && mPatch.num_sinks != 0) { - if (mPatch.sources[0].type == AUDIO_PORT_TYPE_MIX) { - // this is an output mix - // FIXME: the API only returns the first device in case of multiple device selection - return mPatch.sinks[0].id; - } else { - // this is an input mix - return mPatch.sources[0].id; - } + // FIXME: the API only returns the first device in case of multiple device selection + return mIsInput ? mPatch.sources[0].id : mPatch.sinks[0].id; } return AUDIO_PORT_HANDLE_NONE; } + void setPatch(const audio_patch& patch) { mPatch = patch; } + + std::string toDebugString() const { + std::ostringstream ss; + ss << mIoHandle << ", samplingRate " << mSamplingRate << ", " + << audio_format_to_string(mFormat) << ", " + << (audio_channel_mask_get_representation(mChannelMask) == + AUDIO_CHANNEL_REPRESENTATION_INDEX ? + audio_channel_index_mask_to_string(mChannelMask) : + (mIsInput ? audio_channel_in_mask_to_string(mChannelMask) : + audio_channel_out_mask_to_string(mChannelMask))) + << ", frameCount " << mFrameCount << ", frameCountHAL " << mFrameCountHAL + << ", deviceId " << getDeviceId(); + return ss.str(); + } - audio_io_handle_t mIoHandle; - struct audio_patch mPatch; - uint32_t mSamplingRate; - audio_format_t mFormat; - audio_channel_mask_t mChannelMask; - size_t mFrameCount; - size_t mFrameCountHAL; - uint32_t mLatency; // only valid for output - audio_port_handle_t mPortId; // valid for event AUDIO_CLIENT_STARTED + private: + const audio_io_handle_t mIoHandle = AUDIO_IO_HANDLE_NONE; + struct audio_patch mPatch = {}; + const bool mIsInput = false; + const uint32_t mSamplingRate = 0; + const audio_format_t mFormat = AUDIO_FORMAT_DEFAULT; + const audio_channel_mask_t mChannelMask = AUDIO_CHANNEL_NONE; + const size_t mFrameCount = 0; + const size_t mFrameCountHAL = 0; + const uint32_t mLatency = 0; + const audio_port_handle_t mPortId = AUDIO_PORT_HANDLE_NONE; }; diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h index f17ee3a5077dfb79900c1ad0e81d4353caebebc0..cb05dd9952a14cc74846851d6227c0c6d209b696 100644 --- a/media/libaudioclient/include/media/AudioRecord.h +++ b/media/libaudioclient/include/media/AudioRecord.h @@ -40,14 +40,13 @@ namespace android { struct audio_track_cblk_t; class AudioRecordClientProxy; - // ---------------------------------------------------------------------------- class AudioRecord : public AudioSystem::AudioDeviceCallback { public: - /* Events used by AudioRecord callback function (callback_t). + /* Events used by AudioRecord callback function (legacy_callback_t). * Keep in sync with frameworks/base/media/java/android/media/AudioRecord.java NATIVE_EVENT_*. */ enum event_type { @@ -65,20 +64,26 @@ public: }; /* Client should declare a Buffer and pass address to obtainBuffer() - * and releaseBuffer(). See also callback_t for EVENT_MORE_DATA. + * and releaseBuffer(). See also legacy_callback_t for EVENT_MORE_DATA. */ class Buffer { + friend AudioRecord; public: - // FIXME use m prefix + size_t size() const { return mSize; } + size_t getFrameCount() const { return frameCount; } + uint8_t* data() const { return ui8; } + // Leaving public for now to assist refactoring. This class will + // be replaced. size_t frameCount; // number of sample frames corresponding to size; // on input to obtainBuffer() it is the number of frames desired // on output from obtainBuffer() it is the number of available // frames to be read // on input to releaseBuffer() it is currently ignored - size_t size; // input/output in bytes == frameCount * frameSize + private: + size_t mSize; // input/output in bytes == frameCount * frameSize // on input to obtainBuffer() it is ignored // on output from obtainBuffer() it is the number of available // bytes to be read, which is frameCount * frameSize @@ -90,7 +95,7 @@ public: union { void* raw; int16_t* i16; // signed 16-bit - int8_t* i8; // unsigned 8-bit, offset by 0x80 + uint8_t* ui8; // unsigned 8-bit, offset by 0x80 // input to obtainBuffer(): unused, output: pointer to buffer }; @@ -117,7 +122,28 @@ public: * - EVENT_NEW_IAUDIORECORD: unused. */ - typedef void (*callback_t)(int event, void* user, void *info); + typedef void (*legacy_callback_t)(int event, void* user, void *info); + + class IAudioRecordCallback : public virtual RefBase { + friend AudioRecord; + protected: + // Request for client to read newly available data. + // Used for TRANSFER_CALLBACK mode. + // Parameters: + // - buffer : Buffer to read from + // Returns: + // - Number of bytes actually consumed. + virtual size_t onMoreData([[maybe_unused]] const AudioRecord::Buffer& buffer) { return 0; } + // A buffer overrun occurred. + virtual void onOverrun() {} + // Record head is at the specified marker (see setMarkerPosition()). + virtual void onMarker([[maybe_unused]] uint32_t markerPosition) {} + // Record head is at a new position (see setPositionUpdatePeriod()). + virtual void onNewPos([[maybe_unused]] uint32_t newPos) {} + // IAudioRecord was recreated due to re-routing, server invalidation or + // server crash. + virtual void onNewIAudioRecord() {} + }; /* Returns the minimum frame count required for the successful creation of * an AudioRecord object. @@ -182,20 +208,37 @@ public: * pAttributes: If not NULL, supersedes inputSource for use case selection. * threadCanCallJava: Not present in parameter list, and so is fixed at false. */ - AudioRecord(audio_source_t inputSource, uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, const android::content::AttributionSourceState& client, size_t frameCount = 0, - callback_t cbf = NULL, - void* user = NULL, + const wp &callback = nullptr, + uint32_t notificationFrames = 0, + audio_session_t sessionId = AUDIO_SESSION_ALLOCATE, + transfer_type transferType = TRANSFER_DEFAULT, + audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE, + const audio_attributes_t* pAttributes = nullptr, + audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE, + audio_microphone_direction_t + selectedMicDirection = MIC_DIRECTION_UNSPECIFIED, + float selectedMicFieldDimension = MIC_FIELD_DIMENSION_DEFAULT); + + + AudioRecord(audio_source_t inputSource, + uint32_t sampleRate, + audio_format_t format, + audio_channel_mask_t channelMask, + const android::content::AttributionSourceState& client, + size_t frameCount, + legacy_callback_t callback, + void* user, uint32_t notificationFrames = 0, audio_session_t sessionId = AUDIO_SESSION_ALLOCATE, transfer_type transferType = TRANSFER_DEFAULT, audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE, - const audio_attributes_t* pAttributes = NULL, + const audio_attributes_t* pAttributes = nullptr, audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE, audio_microphone_direction_t selectedMicDirection = MIC_DIRECTION_UNSPECIFIED, @@ -223,13 +266,12 @@ public: * * threadCanCallJava: Whether callbacks are made from an attached thread and thus can call JNI. */ - status_t set(audio_source_t inputSource, + status_t set(audio_source_t inputSource, uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, size_t frameCount = 0, - callback_t cbf = NULL, - void* user = NULL, + const wp &callback = nullptr, uint32_t notificationFrames = 0, bool threadCanCallJava = false, audio_session_t sessionId = AUDIO_SESSION_ALLOCATE, @@ -237,7 +279,28 @@ public: audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE, uid_t uid = AUDIO_UID_INVALID, pid_t pid = -1, - const audio_attributes_t* pAttributes = NULL, + const audio_attributes_t* pAttributes = nullptr, + audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE, + audio_microphone_direction_t + selectedMicDirection = MIC_DIRECTION_UNSPECIFIED, + float selectedMicFieldDimension = MIC_FIELD_DIMENSION_DEFAULT, + int32_t maxSharedAudioHistoryMs = 0); + + status_t set(audio_source_t inputSource, + uint32_t sampleRate, + audio_format_t format, + audio_channel_mask_t channelMask, + size_t frameCount, + legacy_callback_t callback, + void* user, + uint32_t notificationFrames = 0, + bool threadCanCallJava = false, + audio_session_t sessionId = AUDIO_SESSION_ALLOCATE, + transfer_type transferType = TRANSFER_DEFAULT, + audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE, + uid_t uid = AUDIO_UID_INVALID, + pid_t pid = -1, + const audio_attributes_t* pAttributes = nullptr, audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE, audio_microphone_direction_t selectedMicDirection = MIC_DIRECTION_UNSPECIFIED, @@ -673,9 +736,11 @@ private: bool mActive; // for client callback handler - callback_t mCbf; // callback handler for events, or NULL - void* mUserData; + wp mCallback; + sp mLegacyCallbackWrapper; + + bool mInitialized = false; // Protect against double set // for notification APIs uint32_t mNotificationFramesReq; // requested number of frames between each // notification callback @@ -760,6 +825,13 @@ private: bool mTimestampRetrogradePositionReported = false; // reduce log spam bool mTimestampRetrogradeTimeReported = false; // reduce log spam + // Format conversion. Maybe needed for adding fast tracks whose format is different from server. + audio_config_base_t mServerConfig; + size_t mServerFrameSize; + size_t mServerSampleSize; + std::unique_ptr mFormatConversionBufRaw; + Buffer mFormatConversionBuffer; + private: class DeathNotifier : public IBinder::DeathRecipient { public: @@ -824,6 +896,8 @@ private: MediaMetrics mMediaMetrics; std::string mMetricsId; // GUARDED_BY(mLock), could change in createRecord_l(). std::string mCallerName; // for example "aaudio" + + void reportError(status_t status, const char *event, const char *message) const; }; }; // namespace android diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index 4d85f7a4b3b2b1bf2ef7e12020ba831f5f803d36..360b83dd8ca57a8c28078d803d794e6ab90528b5 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -19,26 +19,31 @@ #include +#include +#include + +#include #include #include #include #include #include -#include +#include +#include +#include #include +#include #include #include #include #include #include #include -#include #include #include #include #include #include -#include using android::content::AttributionSourceState; @@ -72,6 +77,7 @@ typedef void (*record_config_callback)(int event, audio_patch_handle_t patchHandle, audio_source_t source); typedef void (*routing_callback)(); +typedef void (*vol_range_init_req_callback)(); class IAudioFlinger; class String8; @@ -149,6 +155,7 @@ public: static void setDynPolicyCallback(dynamic_policy_callback cb); static void setRecordConfigCallback(record_config_callback); static void setRoutingCallback(routing_callback cb); + static void setVolInitReqCallback(vol_range_init_req_callback cb); // Sets the binder to use for accessing the AudioFlinger service. This enables the system server // to grant specific isolated processes access to the audio system. Currently used only for the @@ -259,8 +266,8 @@ public: // IAudioPolicyService interface (see AudioPolicyInterface for method descriptions) // static void onNewAudioModulesAvailable(); - static status_t setDeviceConnectionState(audio_devices_t device, audio_policy_dev_state_t state, - const char *device_address, const char *device_name, + static status_t setDeviceConnectionState(audio_policy_dev_state_t state, + const android::media::audio::common::AudioPort& port, audio_format_t encodedFormat); static audio_policy_dev_state_t getDeviceConnectionState(audio_devices_t device, const char *device_address); @@ -281,7 +288,8 @@ public: audio_output_flags_t flags, audio_port_handle_t *selectedDeviceId, audio_port_handle_t *portId, - std::vector *secondaryOutputs); + std::vector *secondaryOutputs, + bool *isSpatialized); static status_t startOutput(audio_port_handle_t portId); static status_t stopOutput(audio_port_handle_t portId); static void releaseOutput(audio_port_handle_t portId); @@ -323,9 +331,9 @@ public: static status_t getMinVolumeIndexForAttributes(const audio_attributes_t &attr, int &index); static product_strategy_t getStrategyForStream(audio_stream_type_t stream); - static audio_devices_t getDevicesForStream(audio_stream_type_t stream); static status_t getDevicesForAttributes(const AudioAttributes &aa, - AudioDeviceTypeAddrVector *devices); + AudioDeviceTypeAddrVector *devices, + bool forVolume); static audio_io_handle_t getOutputForEffect(const effect_descriptor_t *desc); static status_t registerEffect(const effect_descriptor_t *desc, @@ -342,6 +350,7 @@ public: static void clearAudioConfigCache(); static const sp get_audio_policy_service(); + static void clearAudioPolicyService(); // helpers for android.media.AudioManager.getProperty(), see description there for meaning static uint32_t getPrimaryOutputSamplingRate(); @@ -369,7 +378,8 @@ public: struct audio_port_v7 *ports, unsigned int *generation); - /* Get attributes for a given audio port */ + /* Get attributes for a given audio port. On input, the port + * only needs the 'id' field to be filled in. */ static status_t getAudioPort(struct audio_port_v7 *port); /* Create an audio patch between several source and sink ports */ @@ -433,13 +443,16 @@ public: audio_format_t *surroundFormats); static status_t setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled); - static status_t setAssistantUid(uid_t uid); - static status_t setHotwordDetectionServiceUid(uid_t uid); + static status_t setAssistantServicesUids(const std::vector& uids); + static status_t setActiveAssistantServicesUids(const std::vector& activeUids); + static status_t setA11yServicesUids(const std::vector& uids); static status_t setCurrentImeUid(uid_t uid); static bool isHapticPlaybackSupported(); + static bool isUltrasoundSupported(); + static status_t listAudioProductStrategies(AudioProductStrategyVector &strategies); static status_t getProductStrategyFromAudioAttributes( const AudioAttributes &aa, product_strategy_t &productStrategy, @@ -532,9 +545,32 @@ public: const AudioDeviceTypeAddrVector &devices, bool *canBeSpatialized); + /** + * Query how the direct playback is currently supported on the device. + * @param attr audio attributes describing the playback use case + * @param config audio configuration for the playback + * @param directMode out: a set of flags describing how the direct playback is currently + * supported on the device + * @return NO_ERROR in case of success, DEAD_OBJECT, NO_INIT, BAD_VALUE, PERMISSION_DENIED + * in case of error. + */ + static status_t getDirectPlaybackSupport(const audio_attributes_t *attr, + const audio_config_t *config, + audio_direct_mode_t *directMode); + + + /** + * Query which direct audio profiles are available for the specified audio attributes. + * @param attr audio attributes describing the playback use case + * @param audioProfiles out: a vector of audio profiles + * @return NO_ERROR in case of success, DEAD_OBJECT, NO_INIT, BAD_VALUE, PERMISSION_DENIED + * in case of error. + */ + static status_t getDirectProfilesForAttributes(const audio_attributes_t* attr, + std::vector* audioProfiles); // A listener for capture state changes. - class CaptureStateListener : public RefBase { + class CaptureStateListener : public virtual RefBase { public: // Called whenever capture state changes. virtual void onStateChanged(bool active) = 0; @@ -559,7 +595,7 @@ public: // ---------------------------------------------------------------------------- - class AudioVolumeGroupCallback : public RefBase + class AudioVolumeGroupCallback : public virtual RefBase { public: @@ -574,7 +610,7 @@ public: static status_t addAudioVolumeGroupCallback(const sp& callback); static status_t removeAudioVolumeGroupCallback(const sp& callback); - class AudioPortCallback : public RefBase + class AudioPortCallback : public virtual RefBase { public: @@ -590,7 +626,7 @@ public: static status_t addAudioPortCallback(const sp& callback); static status_t removeAudioPortCallback(const sp& callback); - class AudioDeviceCallback : public RefBase + class AudioDeviceCallback : public virtual RefBase { public: @@ -612,6 +648,14 @@ public: static status_t setVibratorInfos(const std::vector& vibratorInfos); + static status_t getMmapPolicyInfo( + media::audio::common::AudioMMapPolicyType policyType, + std::vector *policyInfos); + + static int32_t getAAudioMixerBurstCount(); + + static int32_t getAAudioHardwareBurstMinUsec(); + private: class AudioFlingerClient: public IBinder::DeathRecipient, public media::BnAudioFlingerClient @@ -688,13 +732,14 @@ private: binder::Status onRecordingConfigurationUpdate( int32_t event, const media::RecordClientInfo& clientInfo, - const media::AudioConfigBase& clientConfig, + const media::audio::common::AudioConfigBase& clientConfig, const std::vector& clientEffects, - const media::AudioConfigBase& deviceConfig, + const media::audio::common::AudioConfigBase& deviceConfig, const std::vector& effects, int32_t patchHandle, - media::AudioSourceType source) override; + media::audio::common::AudioSource source) override; binder::Status onRoutingUpdated(); + binder::Status onVolumeRangeInitRequest(); private: Mutex mLock; @@ -722,6 +767,7 @@ private: static dynamic_policy_callback gDynPolicyCallback; static record_config_callback gRecordConfigCallback; static routing_callback gRoutingCallback; + static vol_range_init_req_callback gVolRangeInitReqCallback; static size_t gInBuffSize; // previous parameters for recording buffer size queries diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h index 285a28aa03c6c3ed0f43867130363338692f9ac0..9f540e61af5a47275d58bff9cbc214bd342aab1f 100644 --- a/media/libaudioclient/include/media/AudioTrack.h +++ b/media/libaudioclient/include/media/AudioTrack.h @@ -95,34 +95,36 @@ public: class Buffer { + friend AudioTrack; public: - // FIXME use m prefix + size_t size() const { return mSize; } + size_t getFrameCount() const { return frameCount; } + uint8_t * data() const { return ui8; } + // Leaving public for now to ease refactoring. This class will be + // replaced size_t frameCount; // number of sample frames corresponding to size; // on input to obtainBuffer() it is the number of frames desired, // on output from obtainBuffer() it is the number of available // [empty slots for] frames to be filled // on input to releaseBuffer() it is currently ignored - - size_t size; // input/output in bytes == frameCount * frameSize + private: + size_t mSize; // input/output in bytes == frameCount * frameSize // on input to obtainBuffer() it is ignored // on output from obtainBuffer() it is the number of available // [empty slots for] bytes to be filled, // which is frameCount * frameSize // on input to releaseBuffer() it is the number of bytes to // release - // FIXME This is redundant with respect to frameCount. Consider - // removing size and making frameCount the primary field. union { void* raw; int16_t* i16; // signed 16-bit - int8_t* i8; // unsigned 8-bit, offset by 0x80 + uint8_t* ui8; // unsigned 8-bit, offset by 0x80 }; // input to obtainBuffer(): unused, output: pointer to buffer uint32_t sequence; // IAudioTrack instance sequence number, as of obtainBuffer(). // It is set by obtainBuffer() and confirmed by releaseBuffer(). // Not "user-serviceable". - // TODO Consider sp instead, or in addition to this. }; /* As a convenience, if a callback is supplied, a handler thread @@ -146,7 +148,79 @@ public: * - EVENT_NEW_TIMESTAMP: pointer to const AudioTimestamp. */ - typedef void (*callback_t)(int event, void* user, void *info); + typedef void (*legacy_callback_t)(int event, void* user, void* info); + class IAudioTrackCallback : public virtual RefBase { + friend AudioTrack; + protected: + /* Request to write more data to buffer. + * This event only occurs for TRANSFER_CALLBACK. + * If this event is delivered but the callback handler does not want to write more data, + * the handler must ignore the event by returning zero. + * This might occur, for example, if the application is waiting for source data or is at + * the end of stream. + * For data filling, it is preferred that the callback does not block and instead returns + * a short count of the amount of data actually delivered. + * Parameters: + * - buffer: Buffer to fill + * Returns: + * Amount of data actually written in bytes. + */ + virtual size_t onMoreData([[maybe_unused]] const AudioTrack::Buffer& buffer) { return 0; } + + // Buffer underrun occurred. This will not occur for static tracks. + virtual void onUnderrun() {} + + /* Sample loop end was reached; playback restarted from loop start if loop count was not 0 + * for a static track. + * Parameters: + * - loopsRemaining: Number of loops remaining to be played. -1 if infinite looping. + */ + virtual void onLoopEnd([[maybe_unused]] int32_t loopsRemaining) {} + + /* Playback head is at the specified marker (See setMarkerPosition()). + * Parameters: + * - onMarker: Marker position in frames + */ + virtual void onMarker([[maybe_unused]] uint32_t markerPosition) {} + + /* Playback head is at a new position (See setPositionUpdatePeriod()). + * Parameters: + * - newPos: New position in frames + */ + virtual void onNewPos([[maybe_unused]] uint32_t newPos) {} + + // Playback has completed for a static track. + virtual void onBufferEnd() {} + + // IAudioTrack was re-created, either due to re-routing and voluntary invalidation + // by mediaserver, or mediaserver crash. + virtual void onNewIAudioTrack() {} + + // Sent after all the buffers queued in AF and HW are played back (after stop is called) + // for an offloaded track. + virtual void onStreamEnd() {} + + /* Delivered periodically and when there's a significant change + * in the mapping from frame position to presentation time. + * See AudioTimestamp for the information included with event. + * TODO not yet implemented. + * Parameters: + * - timestamp: New frame position and presentation time mapping. + */ + virtual void onNewTimestamp([[maybe_unused]] AudioTimestamp timestamp) {} + + /* Notification that more data can be given by write() + * This event only occurs for TRANSFER_SYNC_NOTIF_CALLBACK. + * Similar to onMoreData(), return the number of frames actually written + * Parameters: + * - buffer: Buffer to fill + * Returns: + * Amount of data actually written in bytes. + */ + virtual size_t onCanWriteMoreData([[maybe_unused]] const AudioTrack::Buffer& buffer) { + return 0; + } + }; /* Returns the minimum frame count required for the successful creation of * an AudioTrack object. @@ -257,15 +331,34 @@ public: audio_channel_mask_t channelMask, size_t frameCount = 0, audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, - callback_t cbf = NULL, - void* user = NULL, + const wp& callback = nullptr, + int32_t notificationFrames = 0, + audio_session_t sessionId = AUDIO_SESSION_ALLOCATE, + transfer_type transferType = TRANSFER_DEFAULT, + const audio_offload_info_t *offloadInfo = nullptr, + const AttributionSourceState& attributionSource = + AttributionSourceState(), + const audio_attributes_t* pAttributes = nullptr, + bool doNotReconnect = false, + float maxRequiredSpeed = 1.0f, + audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE); + + + AudioTrack( audio_stream_type_t streamType, + uint32_t sampleRate, + audio_format_t format, + audio_channel_mask_t channelMask, + size_t frameCount, + audio_output_flags_t flags, + legacy_callback_t cbf, + void* user = nullptr, int32_t notificationFrames = 0, audio_session_t sessionId = AUDIO_SESSION_ALLOCATE, transfer_type transferType = TRANSFER_DEFAULT, - const audio_offload_info_t *offloadInfo = NULL, + const audio_offload_info_t *offloadInfo = nullptr, const AttributionSourceState& attributionSource = AttributionSourceState(), - const audio_attributes_t* pAttributes = NULL, + const audio_attributes_t* pAttributes = nullptr, bool doNotReconnect = false, float maxRequiredSpeed = 1.0f, audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE); @@ -281,22 +374,39 @@ public: * It is recommended to pass a callback function to be notified of playback end by an * EVENT_UNDERRUN event. */ - AudioTrack( audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, const sp& sharedBuffer, audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, - callback_t cbf = NULL, - void* user = NULL, + const wp& callback = nullptr, int32_t notificationFrames = 0, audio_session_t sessionId = AUDIO_SESSION_ALLOCATE, transfer_type transferType = TRANSFER_DEFAULT, - const audio_offload_info_t *offloadInfo = NULL, + const audio_offload_info_t *offloadInfo = nullptr, const AttributionSourceState& attributionSource = AttributionSourceState(), - const audio_attributes_t* pAttributes = NULL, + const audio_attributes_t* pAttributes = nullptr, + bool doNotReconnect = false, + float maxRequiredSpeed = 1.0f); + + + AudioTrack( audio_stream_type_t streamType, + uint32_t sampleRate, + audio_format_t format, + audio_channel_mask_t channelMask, + const sp& sharedBuffer, + audio_output_flags_t flags, + legacy_callback_t cbf, + void* user = nullptr, + int32_t notificationFrames = 0, + audio_session_t sessionId = AUDIO_SESSION_ALLOCATE, + transfer_type transferType = TRANSFER_DEFAULT, + const audio_offload_info_t *offloadInfo = nullptr, + const AttributionSourceState& attributionSource = + AttributionSourceState(), + const audio_attributes_t* pAttributes = nullptr, bool doNotReconnect = false, float maxRequiredSpeed = 1.0f); @@ -334,20 +444,73 @@ public: audio_channel_mask_t channelMask, size_t frameCount = 0, audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, - callback_t cbf = NULL, - void* user = NULL, + const wp& callback = nullptr, int32_t notificationFrames = 0, const sp& sharedBuffer = 0, bool threadCanCallJava = false, audio_session_t sessionId = AUDIO_SESSION_ALLOCATE, transfer_type transferType = TRANSFER_DEFAULT, - const audio_offload_info_t *offloadInfo = NULL, + const audio_offload_info_t *offloadInfo = nullptr, const AttributionSourceState& attributionSource = AttributionSourceState(), - const audio_attributes_t* pAttributes = NULL, + const audio_attributes_t* pAttributes = nullptr, bool doNotReconnect = false, float maxRequiredSpeed = 1.0f, audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE); + + struct SetParams { + audio_stream_type_t streamType; + uint32_t sampleRate; + audio_format_t format; + audio_channel_mask_t channelMask; + size_t frameCount; + audio_output_flags_t flags; + wp callback; + int32_t notificationFrames; + sp sharedBuffer; + bool threadCanCallJava; + audio_session_t sessionId; + transfer_type transferType; + // TODO don't take pointers here + const audio_offload_info_t *offloadInfo; + AttributionSourceState attributionSource; + const audio_attributes_t* pAttributes; + bool doNotReconnect; + float maxRequiredSpeed; + audio_port_handle_t selectedDeviceId; + }; + private: + // Note: Consumes parameters + void set(SetParams& s) { + (void)set(s.streamType, s.sampleRate, s.format, s.channelMask, s.frameCount, + s.flags, std::move(s.callback), s.notificationFrames, + std::move(s.sharedBuffer), s.threadCanCallJava, s.sessionId, + s.transferType, s.offloadInfo, std::move(s.attributionSource), + s.pAttributes, s.doNotReconnect, s.maxRequiredSpeed, s.selectedDeviceId); + } + void onFirstRef() override; + public: + status_t set(audio_stream_type_t streamType, + uint32_t sampleRate, + audio_format_t format, + audio_channel_mask_t channelMask, + size_t frameCount, + audio_output_flags_t flags, + legacy_callback_t callback, + void * user = nullptr, + int32_t notificationFrames = 0, + const sp& sharedBuffer = 0, + bool threadCanCallJava = false, + audio_session_t sessionId = AUDIO_SESSION_ALLOCATE, + transfer_type transferType = TRANSFER_DEFAULT, + const audio_offload_info_t *offloadInfo = nullptr, + const AttributionSourceState& attributionSource = + AttributionSourceState(), + const audio_attributes_t* pAttributes = nullptr, + bool doNotReconnect = false, + float maxRequiredSpeed = 1.0f, + audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE); + // FIXME(b/169889714): Vendor code depends on the old method signature at link time status_t set(audio_stream_type_t streamType, uint32_t sampleRate, @@ -355,17 +518,17 @@ public: uint32_t channelMask, size_t frameCount = 0, audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, - callback_t cbf = NULL, - void* user = NULL, + legacy_callback_t cbf = nullptr, + void* user = nullptr, int32_t notificationFrames = 0, const sp& sharedBuffer = 0, bool threadCanCallJava = false, audio_session_t sessionId = AUDIO_SESSION_ALLOCATE, transfer_type transferType = TRANSFER_DEFAULT, - const audio_offload_info_t *offloadInfo = NULL, + const audio_offload_info_t *offloadInfo = nullptr, uid_t uid = AUDIO_UID_INVALID, pid_t pid = -1, - const audio_attributes_t* pAttributes = NULL, + const audio_attributes_t* pAttributes = nullptr, bool doNotReconnect = false, float maxRequiredSpeed = 1.0f, audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE); @@ -429,8 +592,7 @@ public: * less than or equal to the getBufferCapacityInFrames(). * It may also be adjusted slightly for internal reasons. * - * Return the final size or a negative error if the track is unitialized - * or does not support variable sizes. + * Return the final size or a negative value (NO_INIT) if the track is uninitialized. */ ssize_t setBufferSizeInFrames(size_t size); @@ -1215,11 +1377,13 @@ public: } // for client callback handler - callback_t mCbf; // callback handler for events, or NULL - void* mUserData; - + wp mCallback; // callback handler for events, or NULL + sp mLegacyCallbackWrapper; // wrapper for legacy callback interface // for notification APIs + std::unique_ptr mSetParams; // Temporary copy of ctor params to allow for + // deferred set after first reference. + bool mInitialized = false; // Set after track is initialized // next 2 fields are const after constructor or set() uint32_t mNotificationFramesReq; // requested number of frames between each // notification callback, diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h index 6d4ab8efccd19003606210961f5db4c53aae3784..3c3715d7fb246ab086b48a399530703d9320a325 100644 --- a/media/libaudioclient/include/media/IAudioFlinger.h +++ b/media/libaudioclient/include/media/IAudioFlinger.h @@ -37,10 +37,12 @@ #include #include +#include #include #include #include -#include +#include +#include #include "android/media/CreateEffectRequest.h" #include "android/media/CreateEffectResponse.h" #include "android/media/CreateRecordRequest.h" @@ -63,7 +65,7 @@ namespace android { // ---------------------------------------------------------------------------- -class IAudioFlinger : public RefBase { +class IAudioFlinger : public virtual RefBase { public: static constexpr char DEFAULT_SERVICE_NAME[] = "media.audio_flinger"; @@ -166,6 +168,7 @@ public: sp buffers; audio_port_handle_t portId; sp audioRecord; + audio_config_base_t serverConfig; ConversionResult toAidl() const; static ConversionResult @@ -348,6 +351,14 @@ public: virtual status_t updateSecondaryOutputs( const TrackSecondaryOutputsMap& trackSecondaryOutputs) = 0; + virtual status_t getMmapPolicyInfos( + media::audio::common::AudioMMapPolicyType policyType, + std::vector *policyInfos) = 0; + + virtual int32_t getAAudioMixerBurstCount() = 0; + + virtual int32_t getAAudioHardwareBurstMinUsec() = 0; + virtual status_t setDeviceConnectedState(const struct audio_port_v7 *port, bool connected) = 0; }; @@ -445,6 +456,11 @@ public: status_t setVibratorInfos(const std::vector& vibratorInfos) override; status_t updateSecondaryOutputs( const TrackSecondaryOutputsMap& trackSecondaryOutputs) override; + status_t getMmapPolicyInfos( + media::audio::common::AudioMMapPolicyType policyType, + std::vector *policyInfos) override; + int32_t getAAudioMixerBurstCount() override; + int32_t getAAudioHardwareBurstMinUsec() override; status_t setDeviceConnectedState(const struct audio_port_v7 *port, bool connected) override; private: @@ -466,9 +482,9 @@ public: * Legacy server should implement this interface in order to be wrapped. */ class Delegate : public IAudioFlinger { - protected: friend class AudioFlingerServerAdapter; - + public: + // expose the TransactionCode enum for TimeCheck purposes. enum class TransactionCode { CREATE_TRACK = media::BnAudioFlingerService::TRANSACTION_createTrack, CREATE_RECORD = media::BnAudioFlingerService::TRANSACTION_createRecord, @@ -531,9 +547,13 @@ public: SET_AUDIO_HAL_PIDS = media::BnAudioFlingerService::TRANSACTION_setAudioHalPids, SET_VIBRATOR_INFOS = media::BnAudioFlingerService::TRANSACTION_setVibratorInfos, UPDATE_SECONDARY_OUTPUTS = media::BnAudioFlingerService::TRANSACTION_updateSecondaryOutputs, + GET_MMAP_POLICY_INFOS = media::BnAudioFlingerService::TRANSACTION_getMmapPolicyInfos, + GET_AAUDIO_MIXER_BURST_COUNT = media::BnAudioFlingerService::TRANSACTION_getAAudioMixerBurstCount, + GET_AAUDIO_HARDWARE_BURST_MIN_USEC = media::BnAudioFlingerService::TRANSACTION_getAAudioHardwareBurstMinUsec, SET_DEVICE_CONNECTED_STATE = media::BnAudioFlingerService::TRANSACTION_setDeviceConnectedState, }; + protected: /** * And optional hook, called on every transaction, allowing additional operations to be * performed before/after the unparceling ofthe data and dispatching to the respective @@ -573,7 +593,8 @@ public: Status createRecord(const media::CreateRecordRequest& request, media::CreateRecordResponse* _aidl_return) override; Status sampleRate(int32_t ioHandle, int32_t* _aidl_return) override; - Status format(int32_t output, media::audio::common::AudioFormat* _aidl_return) override; + Status format(int32_t output, + media::audio::common::AudioFormatDescription* _aidl_return) override; Status frameCount(int32_t ioHandle, int64_t* _aidl_return) override; Status latency(int32_t output, int32_t* _aidl_return) override; Status setMasterVolume(float value) override; @@ -582,12 +603,13 @@ public: Status masterMute(bool* _aidl_return) override; Status setMasterBalance(float balance) override; Status getMasterBalance(float* _aidl_return) override; - Status setStreamVolume(media::AudioStreamType stream, float value, int32_t output) override; - Status setStreamMute(media::AudioStreamType stream, bool muted) override; - Status - streamVolume(media::AudioStreamType stream, int32_t output, float* _aidl_return) override; - Status streamMute(media::AudioStreamType stream, bool* _aidl_return) override; - Status setMode(media::AudioMode mode) override; + Status setStreamVolume(media::audio::common::AudioStreamType stream, + float value, int32_t output) override; + Status setStreamMute(media::audio::common::AudioStreamType stream, bool muted) override; + Status streamVolume(media::audio::common::AudioStreamType stream, + int32_t output, float* _aidl_return) override; + Status streamMute(media::audio::common::AudioStreamType stream, bool* _aidl_return) override; + Status setMode(media::audio::common::AudioMode mode) override; Status setMicMute(bool state) override; Status getMicMute(bool* _aidl_return) override; Status setRecordSilenced(int32_t portId, bool silenced) override; @@ -595,8 +617,10 @@ public: Status getParameters(int32_t ioHandle, const std::string& keys, std::string* _aidl_return) override; Status registerClient(const sp& client) override; - Status getInputBufferSize(int32_t sampleRate, media::audio::common::AudioFormat format, - int32_t channelMask, int64_t* _aidl_return) override; + Status getInputBufferSize(int32_t sampleRate, + const media::audio::common::AudioFormatDescription& format, + const media::audio::common::AudioChannelLayout& channelMask, + int64_t* _aidl_return) override; Status openOutput(const media::OpenOutputRequest& request, media::OpenOutputResponse* _aidl_return) override; Status openDuplicateOutput(int32_t output1, int32_t output2, int32_t* _aidl_return) override; @@ -606,7 +630,7 @@ public: Status openInput(const media::OpenInputRequest& request, media::OpenInputResponse* _aidl_return) override; Status closeInput(int32_t input) override; - Status invalidateStream(media::AudioStreamType stream) override; + Status invalidateStream(media::audio::common::AudioStreamType stream) override; Status setVoiceVolume(float volume) override; Status getRenderPosition(int32_t output, media::RenderPosition* _aidl_return) override; Status getInputFramesLost(int32_t ioHandle, int32_t* _aidl_return) override; @@ -615,7 +639,8 @@ public: Status releaseAudioSessionId(int32_t audioSession, int32_t pid) override; Status queryNumberEffects(int32_t* _aidl_return) override; Status queryEffect(int32_t index, media::EffectDescriptor* _aidl_return) override; - Status getEffectDescriptor(const media::AudioUuid& effectUUID, const media::AudioUuid& typeUUID, + Status getEffectDescriptor(const media::audio::common::AudioUuid& effectUUID, + const media::audio::common::AudioUuid& typeUUID, int32_t preferredTypeFlag, media::EffectDescriptor* _aidl_return) override; Status createEffect(const media::CreateEffectRequest& request, @@ -641,6 +666,11 @@ public: Status setVibratorInfos(const std::vector& vibratorInfos) override; Status updateSecondaryOutputs( const std::vector& trackSecondaryOutputInfos) override; + Status getMmapPolicyInfos( + media::audio::common::AudioMMapPolicyType policyType, + std::vector *_aidl_return) override; + Status getAAudioMixerBurstCount(int32_t* _aidl_return) override; + Status getAAudioHardwareBurstMinUsec(int32_t* _aidl_return) override; Status setDeviceConnectedState(const media::AudioPort& port, bool connected) override; private: diff --git a/media/libaudioclient/include/media/PolicyAidlConversion.h b/media/libaudioclient/include/media/PolicyAidlConversion.h index 2cfa43821fcdb90f74ec5a98a4633ee29727f02f..54e778e7d1d0825905a80729599259867c71710b 100644 --- a/media/libaudioclient/include/media/PolicyAidlConversion.h +++ b/media/libaudioclient/include/media/PolicyAidlConversion.h @@ -23,10 +23,8 @@ #include #include -#include #include #include -#include #include #include #include diff --git a/media/libaudioclient/include/media/ToneGenerator.h b/media/libaudioclient/include/media/ToneGenerator.h index a575616188a2eff9fa109267b6ecadbc1d8a2aa0..46e95011c99a7074e0868fb09287f3041af7d3cb 100644 --- a/media/libaudioclient/include/media/ToneGenerator.h +++ b/media/libaudioclient/include/media/ToneGenerator.h @@ -28,7 +28,7 @@ namespace android { -class ToneGenerator { +class ToneGenerator : public AudioTrack::IAudioTrackCallback { public: // List of all available tones @@ -156,6 +156,9 @@ public: ToneGenerator(audio_stream_type_t streamType, float volume, bool threadCanCallJava = false, std::string opPackageName = {}); + + void onFirstRef() override; + ~ToneGenerator(); bool startTone(tone_type toneType, int durationMs = -1); @@ -222,7 +225,11 @@ private: TONE_INDIA_CONGESTION, // Congestion tone: 400 Hz, 250ms ON, 250ms OFF... TONE_INDIA_CALL_WAITING, // Call waiting tone: 400 Hz, tone repeated in a 0.2s on, 0.1s off, 0.2s on, 7.5s off pattern. TONE_INDIA_RINGTONE, // Ring tone: 400 Hz tone modulated with 25Hz, 0.4 on 0.2 off 0.4 on 2..0 off + // TAIWAN supervisory tones TONE_TW_RINGTONE, // Ring Tone: 440 Hz + 480 Hz repeated with pattern 1s on, 3s off. + // NEW ZEALAND supervisory tones + TONE_NZ_CALL_WAITING, // Call waiting tone: 400 Hz, 0.2s ON, 3s OFF, + // 0.2s ON, 3s OFF, 0.2s ON, 3s OFF, 0.2s ON NUM_ALTERNATE_TONES }; @@ -236,6 +243,7 @@ private: IRELAND, INDIA, TAIWAN, + NZ, CEPT, NUM_REGIONS }; @@ -284,11 +292,10 @@ private: static const ToneDescriptor sToneDescriptors[]; bool mThreadCanCallJava; - unsigned int mTotalSmp; // Total number of audio samples played (gives current time) + uint64_t mTotalSmp; // Total number of audio samples played (gives current time) + // Since these types are 32 bit, we may have issues with aborting on + // overflow now that we have integer overflow sanitization enabled globally. unsigned int mNextSegSmp; // Position of next segment transition expressed in samples - // NOTE: because mTotalSmp, mNextSegSmp are stored on 32 bit, current design will operate properly - // only if tone duration is less than about 27 Hours(@44100Hz sampling rate). If this time is exceeded, - // no crash will occur but tone sequence will show a glitch. unsigned int mMaxSmp; // Maximum number of audio samples played (maximun tone duration) int mDurationMs; // Maximum tone duration in ms @@ -311,6 +318,7 @@ private: unsigned int mProcessSize; // Size of audio blocks generated at a time by audioCallback() (in PCM frames). struct timespec mStartTime; // tone start time: needed to guaranty actual tone duration + size_t onMoreData(const AudioTrack::Buffer& buffer) override; bool initAudioTrack(); static void audioCallback(int event, void* user, void *info); bool prepareWave(); diff --git a/media/libaudioclient/include/media/TrackPlayerBase.h b/media/libaudioclient/include/media/TrackPlayerBase.h index 80124b8ca8377929da20564f5ea52fffd1bd2991..fe88116d508d14e7de03d5d409642f77c661e8a7 100644 --- a/media/libaudioclient/include/media/TrackPlayerBase.h +++ b/media/libaudioclient/include/media/TrackPlayerBase.h @@ -28,8 +28,8 @@ public: explicit TrackPlayerBase(); virtual ~TrackPlayerBase(); - void init(AudioTrack* pat, player_type_t playerType, audio_usage_t usage, - audio_session_t sessionId); + void init(const sp& pat, const sp& callback, + player_type_t playerType, audio_usage_t usage, audio_session_t sessionId); virtual void destroy(); //IPlayer implementation @@ -66,8 +66,8 @@ private: // volume coming from the player volume API float mPlayerVolumeL, mPlayerVolumeR; - - sp mSelfAudioDeviceCallback; + sp mCallbackHandle; + sp mSelfAudioDeviceCallback; }; } // namespace android diff --git a/media/libaudioclient/tests/Android.bp b/media/libaudioclient/tests/Android.bp index def7ca6bfcc8ac5c831cbf579f24e5805bb1206f..891293e6cfffa593ccf98e1e0f37e4a0b4e926c3 100644 --- a/media/libaudioclient/tests/Android.bp +++ b/media/libaudioclient/tests/Android.bp @@ -9,10 +9,35 @@ package { cc_defaults { name: "libaudioclient_tests_defaults", + test_suites: ["device-tests"], cflags: [ "-Wall", "-Werror", ], + sanitize: { + misc_undefined: [ + "unsigned-integer-overflow", + "signed-integer-overflow", + ], + }, +} + +cc_test { + name: "audio_aidl_conversion_tests", + defaults: ["libaudioclient_tests_defaults"], + srcs: ["audio_aidl_legacy_conversion_tests.cpp"], + shared_libs: [ + "libbinder", + "libcutils", + "liblog", + "libutils", + ], + static_libs: [ + "android.media.audio.common.types-V1-cpp", + "audioclient-types-aidl-cpp", + "libaudioclient_aidl_conversion", + "libstagefright_foundation", + ], } cc_test { @@ -30,8 +55,10 @@ cc_test { cc_test { name: "test_create_audiotrack", defaults: ["libaudioclient_tests_defaults"], - srcs: ["test_create_audiotrack.cpp", - "test_create_utils.cpp"], + srcs: [ + "test_create_audiotrack.cpp", + "test_create_utils.cpp", + ], header_libs: [ "libmedia_headers", "libmediametrics_headers", @@ -49,8 +76,10 @@ cc_test { cc_test { name: "test_create_audiorecord", defaults: ["libaudioclient_tests_defaults"], - srcs: ["test_create_audiorecord.cpp", - "test_create_utils.cpp"], + srcs: [ + "test_create_audiorecord.cpp", + "test_create_utils.cpp", + ], header_libs: [ "libmedia_headers", "libmediametrics_headers", diff --git a/media/libaudioclient/tests/audio_aidl_legacy_conversion_tests.cpp b/media/libaudioclient/tests/audio_aidl_legacy_conversion_tests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..997f62a19a4a1f29e6974957f59a66ebd4ea603c --- /dev/null +++ b/media/libaudioclient/tests/audio_aidl_legacy_conversion_tests.cpp @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +using namespace android; +using namespace android::aidl_utils; + +using media::audio::common::AudioChannelLayout; +using media::audio::common::AudioDeviceDescription; +using media::audio::common::AudioDeviceType; +using media::audio::common::AudioFormatDescription; +using media::audio::common::AudioFormatType; +using media::audio::common::PcmType; + +namespace { + +template size_t hash(const T& t) { + return std::hash{}(t); +} + +AudioChannelLayout make_ACL_None() { + return AudioChannelLayout{}; +} + +AudioChannelLayout make_ACL_Invalid() { + return AudioChannelLayout::make(0); +} + +AudioChannelLayout make_ACL_Stereo() { + return AudioChannelLayout::make( + AudioChannelLayout::LAYOUT_STEREO); +} + +AudioChannelLayout make_ACL_LayoutArbitrary() { + return AudioChannelLayout::make( + // Use channels that exist both for input and output, + // but doesn't form a known layout mask. + AudioChannelLayout::CHANNEL_FRONT_LEFT | + AudioChannelLayout::CHANNEL_FRONT_RIGHT | + AudioChannelLayout::CHANNEL_TOP_SIDE_LEFT | + AudioChannelLayout::CHANNEL_TOP_SIDE_RIGHT); +} + +AudioChannelLayout make_ACL_ChannelIndex2() { + return AudioChannelLayout::make( + AudioChannelLayout::INDEX_MASK_2); +} + +AudioChannelLayout make_ACL_ChannelIndexArbitrary() { + // Use channels 1 and 3. + return AudioChannelLayout::make(5); +} + +AudioChannelLayout make_ACL_VoiceCall() { + return AudioChannelLayout::make( + AudioChannelLayout::VOICE_CALL_MONO); +} + +AudioDeviceDescription make_AudioDeviceDescription(AudioDeviceType type, + const std::string& connection = "") { + AudioDeviceDescription result; + result.type = type; + result.connection = connection; + return result; +} + +AudioDeviceDescription make_ADD_None() { + return AudioDeviceDescription{}; +} + +AudioDeviceDescription make_ADD_DefaultIn() { + return make_AudioDeviceDescription(AudioDeviceType::IN_DEFAULT); +} + +AudioDeviceDescription make_ADD_DefaultOut() { + return make_AudioDeviceDescription(AudioDeviceType::OUT_DEFAULT); +} + +AudioDeviceDescription make_ADD_WiredHeadset() { + return make_AudioDeviceDescription(AudioDeviceType::OUT_HEADSET, + AudioDeviceDescription::CONNECTION_ANALOG()); +} + +AudioDeviceDescription make_ADD_BtScoHeadset() { + return make_AudioDeviceDescription(AudioDeviceType::OUT_HEADSET, + AudioDeviceDescription::CONNECTION_BT_SCO()); +} + +AudioFormatDescription make_AudioFormatDescription(AudioFormatType type) { + AudioFormatDescription result; + result.type = type; + return result; +} + +AudioFormatDescription make_AudioFormatDescription(PcmType pcm) { + auto result = make_AudioFormatDescription(AudioFormatType::PCM); + result.pcm = pcm; + return result; +} + +AudioFormatDescription make_AudioFormatDescription(const std::string& encoding) { + AudioFormatDescription result; + result.encoding = encoding; + return result; +} + +AudioFormatDescription make_AudioFormatDescription(PcmType transport, + const std::string& encoding) { + auto result = make_AudioFormatDescription(encoding); + result.pcm = transport; + return result; +} + +AudioFormatDescription make_AFD_Default() { + return AudioFormatDescription{}; +} + +AudioFormatDescription make_AFD_Invalid() { + return make_AudioFormatDescription(AudioFormatType::SYS_RESERVED_INVALID); +} + +AudioFormatDescription make_AFD_Pcm16Bit() { + return make_AudioFormatDescription(PcmType::INT_16_BIT); +} + +AudioFormatDescription make_AFD_Bitstream() { + return make_AudioFormatDescription("example"); +} + +AudioFormatDescription make_AFD_Encap() { + return make_AudioFormatDescription(PcmType::INT_16_BIT, "example.encap"); +} + +AudioFormatDescription make_AFD_Encap_with_Enc() { + auto afd = make_AFD_Encap(); + afd.encoding += "+example"; + return afd; +} + +} // namespace + +// Verify that two independently constructed ADDs/AFDs have the same hash. +// This ensures that regardless of whether the ADD/AFD instance originates +// from, it can be correctly compared to other ADD/AFD instance. Thus, +// for example, a 16-bit integer format description provided by HAL +// is identical to the same format description constructed by the framework. +class HashIdentityTest : public ::testing::Test { + public: + template void verifyHashIdentity(const std::vector>& valueGens) { + for (size_t i = 0; i < valueGens.size(); ++i) { + for (size_t j = 0; j < valueGens.size(); ++j) { + if (i == j) { + EXPECT_EQ(hash(valueGens[i]()), hash(valueGens[i]())) << i; + } else { + EXPECT_NE(hash(valueGens[i]()), hash(valueGens[j]())) << i << ", " << j; + } + } + } + } +}; + +TEST_F(HashIdentityTest, AudioChannelLayoutHashIdentity) { + verifyHashIdentity({ + make_ACL_None, make_ACL_Invalid, make_ACL_Stereo, + make_ACL_LayoutArbitrary, make_ACL_ChannelIndex2, + make_ACL_ChannelIndexArbitrary, make_ACL_VoiceCall}); +} + +TEST_F(HashIdentityTest, AudioDeviceDescriptionHashIdentity) { + verifyHashIdentity({ + make_ADD_None, make_ADD_DefaultIn, make_ADD_DefaultOut, make_ADD_WiredHeadset, + make_ADD_BtScoHeadset}); +} + +TEST_F(HashIdentityTest, AudioFormatDescriptionHashIdentity) { + verifyHashIdentity({ + make_AFD_Default, make_AFD_Invalid, make_AFD_Pcm16Bit, make_AFD_Bitstream, + make_AFD_Encap, make_AFD_Encap_with_Enc}); +} + +using ChannelLayoutParam = std::tuple; +class AudioChannelLayoutRoundTripTest : + public testing::TestWithParam {}; +TEST_P(AudioChannelLayoutRoundTripTest, Aidl2Legacy2Aidl) { + const auto initial = std::get<0>(GetParam()); + const bool isInput = std::get<1>(GetParam()); + auto conv = aidl2legacy_AudioChannelLayout_audio_channel_mask_t(initial, isInput); + ASSERT_TRUE(conv.ok()); + auto convBack = legacy2aidl_audio_channel_mask_t_AudioChannelLayout(conv.value(), isInput); + ASSERT_TRUE(convBack.ok()); + EXPECT_EQ(initial, convBack.value()); +} +INSTANTIATE_TEST_SUITE_P(AudioChannelLayoutRoundTrip, + AudioChannelLayoutRoundTripTest, + testing::Combine( + testing::Values(AudioChannelLayout{}, make_ACL_Invalid(), make_ACL_Stereo(), + make_ACL_LayoutArbitrary(), make_ACL_ChannelIndex2(), + make_ACL_ChannelIndexArbitrary()), + testing::Values(false, true))); +INSTANTIATE_TEST_SUITE_P(AudioChannelVoiceRoundTrip, + AudioChannelLayoutRoundTripTest, + // In legacy constants the voice call is only defined for input. + testing::Combine(testing::Values(make_ACL_VoiceCall()), testing::Values(true))); + +using ChannelLayoutEdgeCaseParam = std::tuple; +class AudioChannelLayoutEdgeCaseTest : + public testing::TestWithParam {}; +TEST_P(AudioChannelLayoutEdgeCaseTest, Legacy2Aidl) { + const audio_channel_mask_t legacy = static_cast(std::get<0>(GetParam())); + const bool isInput = std::get<1>(GetParam()); + const bool isValid = std::get<2>(GetParam()); + auto conv = legacy2aidl_audio_channel_mask_t_AudioChannelLayout(legacy, isInput); + EXPECT_EQ(isValid, conv.ok()); +} +INSTANTIATE_TEST_SUITE_P(AudioChannelLayoutEdgeCase, + AudioChannelLayoutEdgeCaseTest, + testing::Values( + // Valid legacy input masks. + std::make_tuple(AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO, true, true), + std::make_tuple(AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO, true, true), + std::make_tuple(AUDIO_CHANNEL_IN_VOICE_CALL_MONO, true, true), + // Valid legacy output masks. + std::make_tuple( + // This has the same numerical representation as Mask 'A' below + AUDIO_CHANNEL_OUT_FRONT_CENTER | AUDIO_CHANNEL_OUT_LOW_FREQUENCY | + AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT, false, true), + std::make_tuple( + // This has the same numerical representation as Mask 'B' below + AUDIO_CHANNEL_OUT_FRONT_CENTER | AUDIO_CHANNEL_OUT_LOW_FREQUENCY | + AUDIO_CHANNEL_OUT_TOP_BACK_LEFT, false, true), + // Invalid legacy input masks. + std::make_tuple(AUDIO_CHANNEL_IN_6, true, false), + std::make_tuple( + AUDIO_CHANNEL_IN_6 | AUDIO_CHANNEL_IN_FRONT_PROCESSED, true, false), + std::make_tuple( + AUDIO_CHANNEL_IN_PRESSURE | AUDIO_CHANNEL_IN_X_AXIS | + AUDIO_CHANNEL_IN_Y_AXIS | AUDIO_CHANNEL_IN_Z_AXIS, true, false), + std::make_tuple( // Mask 'A' + AUDIO_CHANNEL_IN_STEREO | AUDIO_CHANNEL_IN_VOICE_UPLINK, true, false), + std::make_tuple( // Mask 'B' + AUDIO_CHANNEL_IN_STEREO | AUDIO_CHANNEL_IN_VOICE_DNLINK, true, false))); + +class AudioDeviceDescriptionRoundTripTest : + public testing::TestWithParam {}; +TEST_P(AudioDeviceDescriptionRoundTripTest, Aidl2Legacy2Aidl) { + const auto initial = GetParam(); + auto conv = aidl2legacy_AudioDeviceDescription_audio_devices_t(initial); + ASSERT_TRUE(conv.ok()); + auto convBack = legacy2aidl_audio_devices_t_AudioDeviceDescription(conv.value()); + ASSERT_TRUE(convBack.ok()); + EXPECT_EQ(initial, convBack.value()); +} +INSTANTIATE_TEST_SUITE_P(AudioDeviceDescriptionRoundTrip, + AudioDeviceDescriptionRoundTripTest, + testing::Values(AudioDeviceDescription{}, make_ADD_DefaultIn(), + make_ADD_DefaultOut(), make_ADD_WiredHeadset(), make_ADD_BtScoHeadset())); + +class AudioFormatDescriptionRoundTripTest : + public testing::TestWithParam {}; +TEST_P(AudioFormatDescriptionRoundTripTest, Aidl2Legacy2Aidl) { + const auto initial = GetParam(); + auto conv = aidl2legacy_AudioFormatDescription_audio_format_t(initial); + ASSERT_TRUE(conv.ok()); + auto convBack = legacy2aidl_audio_format_t_AudioFormatDescription(conv.value()); + ASSERT_TRUE(convBack.ok()); + EXPECT_EQ(initial, convBack.value()); +} +INSTANTIATE_TEST_SUITE_P(AudioFormatDescriptionRoundTrip, + AudioFormatDescriptionRoundTripTest, + testing::Values(make_AFD_Invalid(), AudioFormatDescription{}, make_AFD_Pcm16Bit())); diff --git a/media/libaudioclient/tests/test_create_audiorecord.cpp b/media/libaudioclient/tests/test_create_audiorecord.cpp index 1cbcb71225e62d2ea722f9cd31b2ec1f614c9572..2e0883b58032251fe4728db5070fe7ea0f750195 100644 --- a/media/libaudioclient/tests/test_create_audiorecord.cpp +++ b/media/libaudioclient/tests/test_create_audiorecord.cpp @@ -98,14 +98,14 @@ int testRecord(FILE *inputFile, int outputFileFd) attributes.source = inputSource; sp record = new AudioRecord(attributionSource); + const auto emptyCallback = sp::make(); record->set(AUDIO_SOURCE_DEFAULT, sampleRate, format, channelMask, frameCount, - fast ? callback : nullptr, - nullptr, + fast ? emptyCallback : nullptr, notificationFrames, false, sessionId, diff --git a/media/libaudioclient/tests/test_create_audiotrack.cpp b/media/libaudioclient/tests/test_create_audiotrack.cpp index cf9b925ad93f397c590da531098ba1f161c110ae..e7231d310f3a35280c5237f77422247dfc26154c 100644 --- a/media/libaudioclient/tests/test_create_audiotrack.cpp +++ b/media/libaudioclient/tests/test_create_audiotrack.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -108,17 +109,15 @@ int testTrack(FILE *inputFile, int outputFileFd) memset(&attributes, 0, sizeof(attributes)); attributes.content_type = contentType; attributes.usage = usage; - sp track = new AudioTrack(); - + const auto emptyCallback = sp::make(); track->set(AUDIO_STREAM_DEFAULT, sampleRate, format, channelMask, frameCount, flags, - (fast || offload) ? callback : nullptr, - nullptr, + (fast || offload) ? emptyCallback : nullptr, notificationFrames, sharedBuffer, false, @@ -126,8 +125,7 @@ int testTrack(FILE *inputFile, int outputFileFd) ((fast && sharedBuffer == 0) || offload) ? AudioTrack::TRANSFER_CALLBACK : AudioTrack::TRANSFER_DEFAULT, offload ? &offloadInfo : nullptr, - getuid(), - getpid(), + AttributionSourceState(), &attributes, false, 1.0f, diff --git a/media/libaudioclient/tests/test_create_utils.cpp b/media/libaudioclient/tests/test_create_utils.cpp index 8aa1f13c33ab0aaca95f59695a7cff876782c56c..caf52277c505148669bf833ec85a8bdaeeeb9680 100644 --- a/media/libaudioclient/tests/test_create_utils.cpp +++ b/media/libaudioclient/tests/test_create_utils.cpp @@ -68,10 +68,6 @@ bool checkVersion(FILE *inputFile, const char *version) return true; } -void callback(int event __unused, void* user __unused, void *info __unused) -{ -} - int main(int argc, char **argv, test_func_t testFunc) { FILE *inputFile = nullptr; diff --git a/media/libaudioclient/tests/test_create_utils.h b/media/libaudioclient/tests/test_create_utils.h index 2ad646e89b258ff49377592992966044d22ae0c0..9a6f9fa364b5cee8e4988fa5d80e152ca05cf79f 100644 --- a/media/libaudioclient/tests/test_create_utils.h +++ b/media/libaudioclient/tests/test_create_utils.h @@ -31,7 +31,6 @@ int readLine(FILE *inputFile, char *line, int size); bool checkVersion(FILE *inputFile, const char *version); -void callback(int event, void* user, void *info); typedef int (*test_func_t)(FILE *inputFile, int outputFileFd); diff --git a/media/libaudiofoundation/Android.bp b/media/libaudiofoundation/Android.bp index 3bef55b3abc26ed3b686ebe2526672dc263a2aed..159f898c101279af8e1af00f48edd89c65414f51 100644 --- a/media/libaudiofoundation/Android.bp +++ b/media/libaudiofoundation/Android.bp @@ -11,6 +11,10 @@ cc_library_headers { name: "libaudiofoundation_headers", vendor_available: true, min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.media", + ], export_include_dirs: ["include"], header_libs: [ @@ -24,9 +28,11 @@ cc_library_headers { "libmedia_helper_headers", ], static_libs: [ + "android.media.audio.common.types-V1-cpp", "audioclient-types-aidl-cpp", ], export_static_lib_headers: [ + "android.media.audio.common.types-V1-cpp", "audioclient-types-aidl-cpp", ], host_supported: true, @@ -52,6 +58,7 @@ cc_library { ], shared_libs: [ + "android.media.audio.common.types-V1-cpp", "audioclient-types-aidl-cpp", "libaudioclient_aidl_conversion", "libaudioutils", @@ -63,6 +70,7 @@ cc_library { ], export_shared_lib_headers: [ + "android.media.audio.common.types-V1-cpp", "audioclient-types-aidl-cpp", "libaudioclient_aidl_conversion", ], diff --git a/media/libaudiofoundation/AudioContainers.cpp b/media/libaudiofoundation/AudioContainers.cpp index 3df9378496508b75995282a3b124d3bd6317ec65..0a8188fe5a1fd51ac0dd0b577e6567e4e7b34dd5 100644 --- a/media/libaudiofoundation/AudioContainers.cpp +++ b/media/libaudiofoundation/AudioContainers.cpp @@ -70,48 +70,46 @@ const DeviceTypeSet& getAudioDeviceOutAllBleSet() { return audioDeviceOutAllBleSet; } -bool deviceTypesToString(const DeviceTypeSet &deviceTypes, std::string &str) { +const DeviceTypeSet& getAudioDeviceOutLeAudioUnicastSet() { + static const DeviceTypeSet audioDeviceOutLeAudioUnicastSet = DeviceTypeSet( + std::begin(AUDIO_DEVICE_OUT_BLE_UNICAST_ARRAY), + std::end(AUDIO_DEVICE_OUT_BLE_UNICAST_ARRAY)); + return audioDeviceOutLeAudioUnicastSet; +} + +std::string deviceTypesToString(const DeviceTypeSet &deviceTypes) { if (deviceTypes.empty()) { - str = "Empty device types"; - return true; + return "Empty device types"; } - bool ret = true; - for (auto it = deviceTypes.begin(); it != deviceTypes.end();) { - std::string deviceTypeStr; - ret = audio_is_output_device(*it) ? - OutputDeviceConverter::toString(*it, deviceTypeStr) : - InputDeviceConverter::toString(*it, deviceTypeStr); - if (!ret) { - break; + std::stringstream ss; + for (auto it = deviceTypes.begin(); it != deviceTypes.end(); ++it) { + if (it != deviceTypes.begin()) { + ss << ", "; } - str.append(deviceTypeStr); - if (++it != deviceTypes.end()) { - str.append(" , "); + const char* strType = audio_device_to_string(*it); + if (strlen(strType) != 0) { + ss << strType; + } else { + ss << "unknown type:0x" << std::hex << *it; } } - if (!ret) { - str = "Unknown values"; - } - return ret; + return ss.str(); +} + +bool deviceTypesToString(const DeviceTypeSet &deviceTypes, std::string &str) { + str = deviceTypesToString(deviceTypes); + return true; } std::string dumpDeviceTypes(const DeviceTypeSet &deviceTypes) { - std::string ret; - for (auto it = deviceTypes.begin(); it != deviceTypes.end();) { - std::stringstream ss; - ss << "0x" << std::hex << (*it); - ret.append(ss.str()); - if (++it != deviceTypes.end()) { - ret.append(" , "); + std::stringstream ss; + for (auto it = deviceTypes.begin(); it != deviceTypes.end(); ++it) { + if (it != deviceTypes.begin()) { + ss << ", "; } + ss << "0x" << std::hex << (*it); } - return ret; -} - -std::string toString(const DeviceTypeSet& deviceTypes) { - std::string ret; - deviceTypesToString(deviceTypes, ret); - return ret; + return ss.str(); } } // namespace android diff --git a/media/libaudiofoundation/AudioDeviceTypeAddr.cpp b/media/libaudiofoundation/AudioDeviceTypeAddr.cpp index c5d7da80d08c54365b4d3daa8e6d2a48fba5e5c5..4a7e956f42a0e8e4e488564f90a4a138cd82c84d 100644 --- a/media/libaudiofoundation/AudioDeviceTypeAddr.cpp +++ b/media/libaudiofoundation/AudioDeviceTypeAddr.cpp @@ -25,6 +25,9 @@ namespace android { +using media::audio::common::AudioDevice; +using media::audio::common::AudioDeviceAddress; + namespace { static const std::string SUPPRESSED = "SUPPRESSED"; @@ -97,10 +100,13 @@ void AudioDeviceTypeAddr::reset() { std::string AudioDeviceTypeAddr::toString(bool includeSensitiveInfo) const { std::stringstream sstream; - sstream << "type:0x" << std::hex << mType; + sstream << audio_device_to_string(mType); + if (sstream.str().empty()) { + sstream << "unknown type:0x" << std::hex << mType; + } // IP and MAC address are sensitive information. The sensitive information will be suppressed // is `includeSensitiveInfo` is false. - sstream << ",@:" + sstream << ", @:" << (!includeSensitiveInfo && mIsAddressSensitive ? SUPPRESSED : mAddress); return sstream.str(); } @@ -157,17 +163,16 @@ std::string dumpAudioDeviceTypeAddrVector(const AudioDeviceTypeAddrVector& devic } ConversionResult -aidl2legacy_AudioDeviceTypeAddress(const media::AudioDevice& aidl) { - audio_devices_t type = VALUE_OR_RETURN(aidl2legacy_int32_t_audio_devices_t(aidl.type)); - return AudioDeviceTypeAddr(type, aidl.address); +aidl2legacy_AudioDeviceTypeAddress(const AudioDevice& aidl) { + audio_devices_t type; + std::string address; + RETURN_IF_ERROR(aidl2legacy_AudioDevice_audio_device(aidl, &type, &address)); + return AudioDeviceTypeAddr(type, address); } -ConversionResult +ConversionResult legacy2aidl_AudioDeviceTypeAddress(const AudioDeviceTypeAddr& legacy) { - media::AudioDevice aidl; - aidl.type = VALUE_OR_RETURN(legacy2aidl_audio_devices_t_int32_t(legacy.mType)); - aidl.address = legacy.getAddress(); - return aidl; + return legacy2aidl_audio_device_AudioDevice(legacy.mType, legacy.getAddress()); } } // namespace android diff --git a/media/libaudiofoundation/AudioGain.cpp b/media/libaudiofoundation/AudioGain.cpp index 1dee938fc27084c3afe2cbd1414dcc65ecaf9ab7..47e0edb344e216da1574860517f2d719aef84666 100644 --- a/media/libaudiofoundation/AudioGain.cpp +++ b/media/libaudiofoundation/AudioGain.cpp @@ -24,22 +24,18 @@ #define ALOGVV(a...) do { } while(0) #endif +#include + #include #include #include #include -#include - namespace android { -AudioGain::AudioGain(int index, bool useInChannelMask) -{ - mIndex = index; - mUseInChannelMask = useInChannelMask; - memset(&mGain, 0, sizeof(struct audio_gain)); -} +AudioGain::AudioGain(int index, bool isInput) + : mIndex(index), mIsInput(isInput) {} void AudioGain::getDefaultConfig(struct audio_gain_config *config) { @@ -49,12 +45,9 @@ void AudioGain::getDefaultConfig(struct audio_gain_config *config) if ((mGain.mode & AUDIO_GAIN_MODE_JOINT) == AUDIO_GAIN_MODE_JOINT) { config->values[0] = mGain.default_value; } else { - uint32_t numValues; - if (mUseInChannelMask) { - numValues = audio_channel_count_from_in_mask(mGain.channel_mask); - } else { - numValues = audio_channel_count_from_out_mask(mGain.channel_mask); - } + const uint32_t numValues = mIsInput ? + audio_channel_count_from_in_mask(mGain.channel_mask) : + audio_channel_count_from_out_mask(mGain.channel_mask); for (size_t i = 0; i < numValues; i++) { config->values[i] = mGain.default_value; } @@ -78,12 +71,9 @@ status_t AudioGain::checkConfig(const struct audio_gain_config *config) if ((config->channel_mask & ~mGain.channel_mask) != 0) { return BAD_VALUE; } - uint32_t numValues; - if (mUseInChannelMask) { - numValues = audio_channel_count_from_in_mask(config->channel_mask); - } else { - numValues = audio_channel_count_from_out_mask(config->channel_mask); - } + const uint32_t numValues = mIsInput ? + audio_channel_count_from_in_mask(config->channel_mask) : + audio_channel_count_from_out_mask(config->channel_mask); for (size_t i = 0; i < numValues; i++) { if ((config->values[i] < mGain.min_value) || (config->values[i] > mGain.max_value)) { @@ -116,7 +106,7 @@ void AudioGain::dump(std::string *dst, int spaces, int index) const bool AudioGain::equals(const sp& other) const { return other != nullptr && - mUseInChannelMask == other->mUseInChannelMask && + mIsInput == other->mIsInput && mUseForVolume == other->mUseForVolume && // Compare audio gain mGain.mode == other->mGain.mode && @@ -129,51 +119,24 @@ bool AudioGain::equals(const sp& other) const mGain.max_ramp_ms == other->mGain.max_ramp_ms; } -status_t AudioGain::writeToParcel(android::Parcel *parcel) const { - media::AudioGain parcelable; - return writeToParcelable(&parcelable) - ?: parcelable.writeToParcel(parcel); -} - -status_t AudioGain::writeToParcelable(media::AudioGain* parcelable) const { - parcelable->index = VALUE_OR_RETURN_STATUS(convertIntegral(mIndex)); - parcelable->useInChannelMask = mUseInChannelMask; - parcelable->useForVolume = mUseForVolume; - parcelable->mode = VALUE_OR_RETURN_STATUS( - legacy2aidl_audio_gain_mode_t_int32_t_mask(mGain.mode)); - parcelable->channelMask = VALUE_OR_RETURN_STATUS( - legacy2aidl_audio_channel_mask_t_int32_t(mGain.channel_mask)); - parcelable->minValue = VALUE_OR_RETURN_STATUS(convertIntegral(mGain.min_value)); - parcelable->maxValue = VALUE_OR_RETURN_STATUS(convertIntegral(mGain.max_value)); - parcelable->defaultValue = VALUE_OR_RETURN_STATUS( - convertIntegral(mGain.default_value)); - parcelable->stepValue = VALUE_OR_RETURN_STATUS(convertIntegral(mGain.step_value)); - parcelable->minRampMs = VALUE_OR_RETURN_STATUS(convertIntegral(mGain.min_ramp_ms)); - parcelable->maxRampMs = VALUE_OR_RETURN_STATUS(convertIntegral(mGain.max_ramp_ms)); - return OK; -} - -status_t AudioGain::readFromParcel(const android::Parcel *parcel) { - media::AudioGain parcelable; - return parcelable.readFromParcel(parcel) - ?: readFromParcelable(parcelable); +ConversionResult AudioGain::toParcelable() const { + media::audio::common::AudioGain aidl = VALUE_OR_RETURN( + legacy2aidl_audio_gain_AudioGain(mGain, mIsInput)); + aidl.useForVolume = mUseForVolume; + media::AudioGainSys aidlSys; + aidlSys.index = VALUE_OR_RETURN(convertIntegral(mIndex)); + aidlSys.isInput = mIsInput; + return std::make_pair(aidl, aidlSys); } -status_t AudioGain::readFromParcelable(const media::AudioGain& parcelable) { - mIndex = VALUE_OR_RETURN_STATUS(convertIntegral(parcelable.index)); - mUseInChannelMask = parcelable.useInChannelMask; - mUseForVolume = parcelable.useForVolume; - mGain.mode = VALUE_OR_RETURN_STATUS( - aidl2legacy_int32_t_audio_gain_mode_t_mask(parcelable.mode)); - mGain.channel_mask = VALUE_OR_RETURN_STATUS( - aidl2legacy_int32_t_audio_channel_mask_t(parcelable.channelMask)); - mGain.min_value = VALUE_OR_RETURN_STATUS(convertIntegral(parcelable.minValue)); - mGain.max_value = VALUE_OR_RETURN_STATUS(convertIntegral(parcelable.maxValue)); - mGain.default_value = VALUE_OR_RETURN_STATUS(convertIntegral(parcelable.defaultValue)); - mGain.step_value = VALUE_OR_RETURN_STATUS(convertIntegral(parcelable.stepValue)); - mGain.min_ramp_ms = VALUE_OR_RETURN_STATUS(convertIntegral(parcelable.minRampMs)); - mGain.max_ramp_ms = VALUE_OR_RETURN_STATUS(convertIntegral(parcelable.maxRampMs)); - return OK; +ConversionResult> AudioGain::fromParcelable(const AudioGain::Aidl& aidl) { + const media::audio::common::AudioGain& hal = aidl.first; + const media::AudioGainSys& sys = aidl.second; + auto index = VALUE_OR_RETURN(convertIntegral(sys.index)); + sp legacy = sp::make(index, sys.isInput); + legacy->mGain = VALUE_OR_RETURN(aidl2legacy_AudioGain_audio_gain(hal, sys.isInput)); + legacy->mUseForVolume = hal.useForVolume; + return legacy; } bool AudioGains::equals(const AudioGains &other) const @@ -184,59 +147,30 @@ bool AudioGains::equals(const AudioGains &other) const }); } -status_t AudioGains::writeToParcel(android::Parcel *parcel) const { - status_t status = NO_ERROR; - if ((status = parcel->writeVectorSize(*this)) != NO_ERROR) return status; - for (const auto &audioGain : *this) { - if ((status = parcel->writeParcelable(*audioGain)) != NO_ERROR) { - break; - } - } - return status; -} - -status_t AudioGains::readFromParcel(const android::Parcel *parcel) { - status_t status = NO_ERROR; - this->clear(); - if ((status = parcel->resizeOutVector(this)) != NO_ERROR) return status; - for (size_t i = 0; i < this->size(); i++) { - this->at(i) = new AudioGain(0, false); - if ((status = parcel->readParcelable(this->at(i).get())) != NO_ERROR) { - this->clear(); - break; - } - } - return status; -} - ConversionResult> -aidl2legacy_AudioGain(const media::AudioGain& aidl) { - sp legacy = new AudioGain(0, false); - status_t status = legacy->readFromParcelable(aidl); - if (status != OK) { - return base::unexpected(status); - } - return legacy; +aidl2legacy_AudioGain(const AudioGain::Aidl& aidl) { + return AudioGain::fromParcelable(aidl); } -ConversionResult +ConversionResult legacy2aidl_AudioGain(const sp& legacy) { - media::AudioGain aidl; - status_t status = legacy->writeToParcelable(&aidl); - if (status != OK) { - return base::unexpected(status); - } - return aidl; + return legacy->toParcelable(); } ConversionResult -aidl2legacy_AudioGains(const std::vector& aidl) { - return convertContainer(aidl, aidl2legacy_AudioGain); +aidl2legacy_AudioGains(const AudioGains::Aidl& aidl) { + return convertContainers(aidl.first, aidl.second, + [](const media::audio::common::AudioGain& g, + const media::AudioGainSys& gs) { + return aidl2legacy_AudioGain(std::make_pair(g, gs)); + }); } -ConversionResult> +ConversionResult legacy2aidl_AudioGains(const AudioGains& legacy) { - return convertContainer>(legacy, legacy2aidl_AudioGain); + return convertContainerSplit< + std::vector, + std::vector>(legacy, legacy2aidl_AudioGain); } } // namespace android diff --git a/media/libaudiofoundation/AudioPort.cpp b/media/libaudiofoundation/AudioPort.cpp index fafabd902238041a1e56ea278d132bfe688e2b3d..451332396e68d8c4c65d18cd9bc669057308c00f 100644 --- a/media/libaudiofoundation/AudioPort.cpp +++ b/media/libaudiofoundation/AudioPort.cpp @@ -18,13 +18,28 @@ #include #include -#include #include #include #include namespace android { +void AudioPort::setFlags(uint32_t flags) +{ + // force direct flag if offload flag is set: offloading implies a direct output stream + // and all common behaviors are driven by checking only the direct flag + // this should normally be set appropriately in the policy configuration file + if (mRole == AUDIO_PORT_ROLE_SOURCE && + (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) { + flags |= AUDIO_OUTPUT_FLAG_DIRECT; + } + if (useInputChannelMask()) { + mFlags.input = static_cast(flags); + } else { + mFlags.output = static_cast(flags); + } +} + void AudioPort::importAudioPort(const sp& port, bool force __unused) { for (const auto& profileToImport : port->mProfiles) { @@ -147,9 +162,16 @@ void AudioPort::toAudioPort(struct audio_port_v7 *port) const { } } -void AudioPort::dump(std::string *dst, int spaces, bool verbose) const { +void AudioPort::dump(std::string *dst, int spaces, const char* extraInfo, bool verbose) const { if (!mName.empty()) { - dst->append(base::StringPrintf("%*s- name: %s\n", spaces, "", mName.c_str())); + dst->append(base::StringPrintf("\"%s\"%s", mName.c_str(), + extraInfo != nullptr ? "; " : "")); + } + if (extraInfo != nullptr) { + dst->append(base::StringPrintf("%s", extraInfo)); + } + if (!mName.empty() || extraInfo != nullptr) { + dst->append("\n"); } if (verbose) { std::string profilesStr; @@ -196,39 +218,59 @@ bool AudioPort::equals(const sp &other) const mType == other->getType() && mRole == other->getRole() && mProfiles.equals(other->getAudioProfiles()) && + getFlags() == other->getFlags() && mExtraAudioDescriptors == other->getExtraAudioDescriptors(); } -status_t AudioPort::writeToParcel(Parcel *parcel) const -{ - media::AudioPort parcelable; - return writeToParcelable(&parcelable) - ?: parcelable.writeToParcel(parcel); -} - status_t AudioPort::writeToParcelable(media::AudioPort* parcelable) const { - parcelable->name = mName; - parcelable->type = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_port_type_t_AudioPortType(mType)); - parcelable->role = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_port_role_t_AudioPortRole(mRole)); - parcelable->profiles = VALUE_OR_RETURN_STATUS(legacy2aidl_AudioProfileVector(mProfiles)); - parcelable->extraAudioDescriptors = mExtraAudioDescriptors; - parcelable->gains = VALUE_OR_RETURN_STATUS(legacy2aidl_AudioGains(mGains)); + parcelable->hal.name = mName; + parcelable->sys.type = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_port_type_t_AudioPortType(mType)); + parcelable->sys.role = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_port_role_t_AudioPortRole(mRole)); + auto aidlProfiles = VALUE_OR_RETURN_STATUS( + legacy2aidl_AudioProfileVector(mProfiles, useInputChannelMask())); + parcelable->hal.profiles = aidlProfiles.first; + parcelable->sys.profiles = aidlProfiles.second; + parcelable->hal.flags = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_io_flags_AudioIoFlags(mFlags, useInputChannelMask())); + parcelable->hal.extraAudioDescriptors = mExtraAudioDescriptors; + auto aidlGains = VALUE_OR_RETURN_STATUS(legacy2aidl_AudioGains(mGains)); + parcelable->hal.gains = aidlGains.first; + parcelable->sys.gains = aidlGains.second; + if (mType == AUDIO_PORT_TYPE_MIX) { + media::audio::common::AudioPortMixExt mixExt{}; + mixExt.maxOpenStreamCount = maxOpenCount; + mixExt.maxActiveStreamCount = maxActiveCount; + mixExt.recommendedMuteDurationMs = recommendedMuteDurationMs; + parcelable->hal.ext = media::audio::common::AudioPortExt::make< + media::audio::common::AudioPortExt::mix>(mixExt); + } return OK; } -status_t AudioPort::readFromParcel(const Parcel *parcel) { - media::AudioPort parcelable; - return parcelable.readFromParcel(parcel) - ?: readFromParcelable(parcelable); -} - status_t AudioPort::readFromParcelable(const media::AudioPort& parcelable) { - mName = parcelable.name; - mType = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioPortType_audio_port_type_t(parcelable.type)); - mRole = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioPortRole_audio_port_role_t(parcelable.role)); - mProfiles = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioProfileVector(parcelable.profiles)); - mExtraAudioDescriptors = parcelable.extraAudioDescriptors; - mGains = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioGains(parcelable.gains)); + mName = parcelable.hal.name; + mType = VALUE_OR_RETURN_STATUS( + aidl2legacy_AudioPortType_audio_port_type_t(parcelable.sys.type)); + mRole = VALUE_OR_RETURN_STATUS( + aidl2legacy_AudioPortRole_audio_port_role_t(parcelable.sys.role)); + mProfiles = VALUE_OR_RETURN_STATUS( + aidl2legacy_AudioProfileVector( + std::make_pair(parcelable.hal.profiles, parcelable.sys.profiles), + useInputChannelMask())); + mFlags = VALUE_OR_RETURN_STATUS( + aidl2legacy_AudioIoFlags_audio_io_flags(parcelable.hal.flags, useInputChannelMask())); + mExtraAudioDescriptors = parcelable.hal.extraAudioDescriptors; + mGains = VALUE_OR_RETURN_STATUS( + aidl2legacy_AudioGains(std::make_pair(parcelable.hal.gains, parcelable.sys.gains))); + if (mType == AUDIO_PORT_TYPE_MIX) { + const media::audio::common::AudioPortMixExt& mixExt = + parcelable.hal.ext.get(); + maxOpenCount = mixExt.maxOpenStreamCount; + maxActiveCount = mixExt.maxActiveStreamCount; + recommendedMuteDurationMs = mixExt.recommendedMuteDurationMs; + } return OK; } @@ -250,6 +292,9 @@ status_t AudioPortConfig::applyAudioPortConfig( if (config->config_mask & AUDIO_PORT_CONFIG_GAIN) { mGain = config->gain; } + if (config->config_mask & AUDIO_PORT_CONFIG_FLAGS) { + mFlags = config->flags; + } return NO_ERROR; } @@ -303,6 +348,9 @@ void AudioPortConfig::toAudioPortConfig( } else { dstConfig->config_mask &= ~AUDIO_PORT_CONFIG_GAIN; } + + updateField(mFlags, &audio_port_config::flags, + dstConfig, srcConfig, AUDIO_PORT_CONFIG_FLAGS, { AUDIO_INPUT_FLAG_NONE }); } bool AudioPortConfig::hasGainController(bool canUseForVolume) const @@ -315,12 +363,14 @@ bool AudioPortConfig::hasGainController(bool canUseForVolume) const : audioport->getGains().size() > 0; } -bool AudioPortConfig::equals(const sp &other) const +bool AudioPortConfig::equals(const sp &other, bool isInput) const { return other != nullptr && mSamplingRate == other->getSamplingRate() && mFormat == other->getFormat() && mChannelMask == other->getChannelMask() && + (isInput ? mFlags.input == other->getFlags().input : + mFlags.output == other->getFlags().output )&& // Compare audio gain config mGain.index == other->mGain.index && mGain.mode == other->mGain.mode && @@ -330,54 +380,47 @@ bool AudioPortConfig::equals(const sp &other) const mGain.ramp_duration_ms == other->mGain.ramp_duration_ms; } -status_t AudioPortConfig::writeToParcel(Parcel *parcel) const { - media::AudioPortConfig parcelable; - return writeToParcelable(&parcelable) - ?: parcelable.writeToParcel(parcel); -} - -status_t AudioPortConfig::writeToParcelable(media::AudioPortConfig* parcelable) const { - parcelable->sampleRate = VALUE_OR_RETURN_STATUS(convertIntegral(mSamplingRate)); - parcelable->format = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_format_t_AudioFormat(mFormat)); +status_t AudioPortConfig::writeToParcelable( + media::audio::common::AudioPortConfig* parcelable, bool isInput) const { + media::audio::common::Int aidl_sampleRate; + aidl_sampleRate.value = VALUE_OR_RETURN_STATUS(convertIntegral(mSamplingRate)); + parcelable->sampleRate = aidl_sampleRate; + parcelable->format = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_format_t_AudioFormatDescription(mFormat)); parcelable->channelMask = VALUE_OR_RETURN_STATUS( - legacy2aidl_audio_channel_mask_t_int32_t(mChannelMask)); + legacy2aidl_audio_channel_mask_t_AudioChannelLayout(mChannelMask, isInput)); parcelable->id = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_port_handle_t_int32_t(mId)); - parcelable->gain.index = VALUE_OR_RETURN_STATUS(convertIntegral(mGain.index)); - parcelable->gain.mode = VALUE_OR_RETURN_STATUS( - legacy2aidl_audio_gain_mode_t_int32_t_mask(mGain.mode)); - parcelable->gain.channelMask = VALUE_OR_RETURN_STATUS( - legacy2aidl_audio_channel_mask_t_int32_t(mGain.channel_mask)); - parcelable->gain.rampDurationMs = VALUE_OR_RETURN_STATUS( - convertIntegral(mGain.ramp_duration_ms)); - parcelable->gain.values = VALUE_OR_RETURN_STATUS(convertContainer>( - mGain.values, convertIntegral)); + media::audio::common::AudioGainConfig aidl_gain = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_gain_config_AudioGainConfig(mGain, isInput)); + parcelable->gain = aidl_gain; + parcelable->flags = VALUE_OR_RETURN_STATUS( + legacy2aidl_audio_io_flags_AudioIoFlags(mFlags, isInput)); return OK; } -status_t AudioPortConfig::readFromParcel(const Parcel *parcel) { - media::AudioPortConfig parcelable; - return parcelable.readFromParcel(parcel) - ?: readFromParcelable(parcelable); -} - -status_t AudioPortConfig::readFromParcelable(const media::AudioPortConfig& parcelable) { - mSamplingRate = VALUE_OR_RETURN_STATUS(convertIntegral(parcelable.sampleRate)); - mFormat = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioFormat_audio_format_t(parcelable.format)); - mChannelMask = VALUE_OR_RETURN_STATUS( - aidl2legacy_int32_t_audio_channel_mask_t(parcelable.channelMask)); +status_t AudioPortConfig::readFromParcelable( + const media::audio::common::AudioPortConfig& parcelable, bool isInput) { + if (parcelable.sampleRate.has_value()) { + mSamplingRate = VALUE_OR_RETURN_STATUS( + convertIntegral(parcelable.sampleRate.value().value)); + } + if (parcelable.format.has_value()) { + mFormat = VALUE_OR_RETURN_STATUS( + aidl2legacy_AudioFormatDescription_audio_format_t(parcelable.format.value())); + } + if (parcelable.channelMask.has_value()) { + mChannelMask = VALUE_OR_RETURN_STATUS( + aidl2legacy_AudioChannelLayout_audio_channel_mask_t( + parcelable.channelMask.value(), isInput)); + } mId = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_port_handle_t(parcelable.id)); - mGain.index = VALUE_OR_RETURN_STATUS(convertIntegral(parcelable.gain.index)); - mGain.mode = VALUE_OR_RETURN_STATUS( - aidl2legacy_int32_t_audio_gain_mode_t_mask(parcelable.gain.mode)); - mGain.channel_mask = VALUE_OR_RETURN_STATUS( - aidl2legacy_int32_t_audio_channel_mask_t(parcelable.gain.channelMask)); - mGain.ramp_duration_ms = VALUE_OR_RETURN_STATUS( - convertIntegral(parcelable.gain.rampDurationMs)); - if (parcelable.gain.values.size() > std::size(mGain.values)) { - return BAD_VALUE; + if (parcelable.gain.has_value()) { + mGain = VALUE_OR_RETURN_STATUS( + aidl2legacy_AudioGainConfig_audio_gain_config(parcelable.gain.value(), isInput)); } - for (size_t i = 0; i < parcelable.gain.values.size(); ++i) { - mGain.values[i] = VALUE_OR_RETURN_STATUS(convertIntegral(parcelable.gain.values[i])); + if (parcelable.flags.has_value()) { + mFlags = VALUE_OR_RETURN_STATUS( + aidl2legacy_AudioIoFlags_audio_io_flags(parcelable.flags.value(), isInput)); } return OK; } diff --git a/media/libaudiofoundation/AudioProfile.cpp b/media/libaudiofoundation/AudioProfile.cpp index 8ac3f734cb18e4188b67cadd4b624bfa5ad007b9..2170cd88f02656e6fabf73e615d42a25b41ffc02 100644 --- a/media/libaudiofoundation/AudioProfile.cpp +++ b/media/libaudiofoundation/AudioProfile.cpp @@ -27,6 +27,8 @@ namespace android { +using media::audio::common::AudioChannelLayout; + bool operator == (const AudioProfile &left, const AudioProfile &right) { return (left.getFormat() == right.getFormat()) && @@ -97,18 +99,14 @@ void AudioProfile::clear() void AudioProfile::dump(std::string *dst, int spaces) const { - dst->append(base::StringPrintf("%s%s%s\n", mIsDynamicFormat ? "[dynamic format]" : "", + dst->append(base::StringPrintf("\"%s\"; ", mName.c_str())); + dst->append(base::StringPrintf("%s%s%s%s", mIsDynamicFormat ? "[dynamic format]" : "", mIsDynamicChannels ? "[dynamic channels]" : "", - mIsDynamicRate ? "[dynamic rates]" : "")); - if (mName.length() != 0) { - dst->append(base::StringPrintf("%*s- name: %s\n", spaces, "", mName.c_str())); - } - std::string formatLiteral; - if (FormatConverter::toString(mFormat, formatLiteral)) { - dst->append(base::StringPrintf("%*s- format: %s\n", spaces, "", formatLiteral.c_str())); - } + mIsDynamicRate ? "[dynamic rates]" : "", isDynamic() ? "; " : "")); + dst->append(base::StringPrintf("%s (0x%x)\n", audio_format_to_string(mFormat), mFormat)); + if (!mSamplingRates.empty()) { - dst->append(base::StringPrintf("%*s- sampling rates:", spaces, "")); + dst->append(base::StringPrintf("%*ssampling rates: ", spaces, "")); for (auto it = mSamplingRates.begin(); it != mSamplingRates.end();) { dst->append(base::StringPrintf("%d", *it)); dst->append(++it == mSamplingRates.end() ? "" : ", "); @@ -117,7 +115,7 @@ void AudioProfile::dump(std::string *dst, int spaces) const } if (!mChannelMasks.empty()) { - dst->append(base::StringPrintf("%*s- channel masks:", spaces, "")); + dst->append(base::StringPrintf("%*schannel masks: ", spaces, "")); for (auto it = mChannelMasks.begin(); it != mChannelMasks.end();) { dst->append(base::StringPrintf("0x%04x", *it)); dst->append(++it == mChannelMasks.end() ? "" : ", "); @@ -126,19 +124,20 @@ void AudioProfile::dump(std::string *dst, int spaces) const } dst->append(base::StringPrintf( - "%*s- encapsulation type: %#x\n", spaces, "", mEncapsulationType)); + "%*s%s\n", spaces, "", audio_encapsulation_type_to_string(mEncapsulationType))); } -bool AudioProfile::equals(const sp& other) const +bool AudioProfile::equals(const sp& other, bool ignoreDynamicFlags) const { return other != nullptr && mName.compare(other->mName) == 0 && mFormat == other->getFormat() && mChannelMasks == other->getChannels() && mSamplingRates == other->getSampleRates() && - mIsDynamicFormat == other->isDynamicFormat() && - mIsDynamicChannels == other->isDynamicChannels() && - mIsDynamicRate == other->isDynamicRate() && + (ignoreDynamicFlags || + (mIsDynamicFormat == other->isDynamicFormat() && + mIsDynamicChannels == other->isDynamicChannels() && + mIsDynamicRate == other->isDynamicRate())) && mEncapsulationType == other->getEncapsulationType(); } @@ -154,67 +153,88 @@ AudioProfile& AudioProfile::operator=(const AudioProfile& other) { return *this; } -status_t AudioProfile::writeToParcel(Parcel *parcel) const { - media::AudioProfile parcelable = VALUE_OR_RETURN_STATUS(toParcelable()); - return parcelable.writeToParcel(parcel); - } +ConversionResult +AudioProfile::toParcelable(bool isInput) const { + media::audio::common::AudioProfile parcelable = VALUE_OR_RETURN(toCommonParcelable(isInput)); + media::AudioProfileSys parcelableSys; + parcelableSys.isDynamicFormat = mIsDynamicFormat; + parcelableSys.isDynamicChannels = mIsDynamicChannels; + parcelableSys.isDynamicRate = mIsDynamicRate; + return std::make_pair(parcelable, parcelableSys); +} + +ConversionResult> AudioProfile::fromParcelable( + const AudioProfile::Aidl& aidl, bool isInput) { + sp legacy = VALUE_OR_RETURN(fromCommonParcelable(aidl.first, isInput)); + const auto& parcelableSys = aidl.second; + legacy->mIsDynamicFormat = parcelableSys.isDynamicFormat; + legacy->mIsDynamicChannels = parcelableSys.isDynamicChannels; + legacy->mIsDynamicRate = parcelableSys.isDynamicRate; + return legacy; +} -ConversionResult -AudioProfile::toParcelable() const { - media::AudioProfile parcelable; +ConversionResult +AudioProfile::toCommonParcelable(bool isInput) const { + media::audio::common::AudioProfile parcelable; parcelable.name = mName; - parcelable.format = VALUE_OR_RETURN(legacy2aidl_audio_format_t_AudioFormat(mFormat)); + parcelable.format = VALUE_OR_RETURN( + legacy2aidl_audio_format_t_AudioFormatDescription(mFormat)); + // Note: legacy 'audio_profile' imposes a limit on the number of + // channel masks and sampling rates. That's why it's not used here + // and conversions are performed directly on the fields instead + // of using 'legacy2aidl_audio_profile_AudioProfile' from AidlConversion. parcelable.channelMasks = VALUE_OR_RETURN( - convertContainer>(mChannelMasks, - legacy2aidl_audio_channel_mask_t_int32_t)); - parcelable.samplingRates = VALUE_OR_RETURN( + convertContainer>( + mChannelMasks, + [isInput](audio_channel_mask_t m) { + return legacy2aidl_audio_channel_mask_t_AudioChannelLayout(m, isInput); + })); + parcelable.sampleRates = VALUE_OR_RETURN( convertContainer>(mSamplingRates, convertIntegral)); - parcelable.isDynamicFormat = mIsDynamicFormat; - parcelable.isDynamicChannels = mIsDynamicChannels; - parcelable.isDynamicRate = mIsDynamicRate; parcelable.encapsulationType = VALUE_OR_RETURN( legacy2aidl_audio_encapsulation_type_t_AudioEncapsulationType(mEncapsulationType)); return parcelable; } -status_t AudioProfile::readFromParcel(const Parcel *parcel) { - media::AudioProfile parcelable; - if (status_t status = parcelable.readFromParcel(parcel); status != OK) { - return status; - } - *this = *VALUE_OR_RETURN_STATUS(fromParcelable(parcelable)); - return OK; -} - -ConversionResult> -AudioProfile::fromParcelable(const media::AudioProfile& parcelable) { +ConversionResult> AudioProfile::fromCommonParcelable( + const media::audio::common::AudioProfile& aidl, bool isInput) { sp legacy = new AudioProfile(); - legacy->mName = parcelable.name; - legacy->mFormat = VALUE_OR_RETURN(aidl2legacy_AudioFormat_audio_format_t(parcelable.format)); + legacy->mName = aidl.name; + legacy->mFormat = VALUE_OR_RETURN( + aidl2legacy_AudioFormatDescription_audio_format_t(aidl.format)); legacy->mChannelMasks = VALUE_OR_RETURN( - convertContainer(parcelable.channelMasks, - aidl2legacy_int32_t_audio_channel_mask_t)); + convertContainer(aidl.channelMasks, + [isInput](const AudioChannelLayout& l) { + return aidl2legacy_AudioChannelLayout_audio_channel_mask_t(l, isInput); + })); legacy->mSamplingRates = VALUE_OR_RETURN( - convertContainer(parcelable.samplingRates, + convertContainer(aidl.sampleRates, convertIntegral)); - legacy->mIsDynamicFormat = parcelable.isDynamicFormat; - legacy->mIsDynamicChannels = parcelable.isDynamicChannels; - legacy->mIsDynamicRate = parcelable.isDynamicRate; legacy->mEncapsulationType = VALUE_OR_RETURN( aidl2legacy_AudioEncapsulationType_audio_encapsulation_type_t( - parcelable.encapsulationType)); + aidl.encapsulationType)); return legacy; } ConversionResult> -aidl2legacy_AudioProfile(const media::AudioProfile& aidl) { - return AudioProfile::fromParcelable(aidl); +aidl2legacy_AudioProfile(const AudioProfile::Aidl& aidl, bool isInput) { + return AudioProfile::fromParcelable(aidl, isInput); } -ConversionResult -legacy2aidl_AudioProfile(const sp& legacy) { - return legacy->toParcelable(); +ConversionResult +legacy2aidl_AudioProfile(const sp& legacy, bool isInput) { + return legacy->toParcelable(isInput); +} + +ConversionResult> +aidl2legacy_AudioProfile_common(const media::audio::common::AudioProfile& aidl, bool isInput) { + return AudioProfile::fromCommonParcelable(aidl, isInput); +} + +ConversionResult +legacy2aidl_AudioProfile_common(const sp& legacy, bool isInput) { + return legacy->toCommonParcelable(isInput); } ssize_t AudioProfileVector::add(const sp &profile) @@ -307,10 +327,10 @@ bool AudioProfileVector::hasDynamicRateFor(audio_format_t format) const return false; } -bool AudioProfileVector::contains(const sp& profile) const +bool AudioProfileVector::contains(const sp& profile, bool ignoreDynamicFlags) const { for (const auto& audioProfile : *this) { - if (audioProfile->equals(profile)) { + if (audioProfile->equals(profile, ignoreDynamicFlags)) { return true; } } @@ -319,42 +339,16 @@ bool AudioProfileVector::contains(const sp& profile) const void AudioProfileVector::dump(std::string *dst, int spaces) const { - dst->append(base::StringPrintf("%*s- Profiles:\n", spaces, "")); + dst->append(base::StringPrintf("%*s- Profiles (%zu):\n", spaces - 2, "", size())); for (size_t i = 0; i < size(); i++) { - dst->append(base::StringPrintf("%*sProfile %zu:", spaces + 4, "", i)); + const std::string prefix = base::StringPrintf("%*s %zu. ", spaces, "", i + 1); + dst->append(prefix); std::string profileStr; - at(i)->dump(&profileStr, spaces + 8); + at(i)->dump(&profileStr, prefix.size()); dst->append(profileStr); } } -status_t AudioProfileVector::writeToParcel(Parcel *parcel) const -{ - status_t status = NO_ERROR; - if ((status = parcel->writeVectorSize(*this)) != NO_ERROR) return status; - for (const auto &audioProfile : *this) { - if ((status = parcel->writeParcelable(*audioProfile)) != NO_ERROR) { - break; - } - } - return status; -} - -status_t AudioProfileVector::readFromParcel(const Parcel *parcel) -{ - status_t status = NO_ERROR; - this->clear(); - if ((status = parcel->resizeOutVector(this)) != NO_ERROR) return status; - for (size_t i = 0; i < this->size(); ++i) { - this->at(i) = new AudioProfile(AUDIO_FORMAT_DEFAULT, AUDIO_CHANNEL_NONE, 0 /*sampleRate*/); - if ((status = parcel->readParcelable(this->at(i).get())) != NO_ERROR) { - this->clear(); - break; - } - } - return status; -} - bool AudioProfileVector::equals(const AudioProfileVector& other) const { return std::equal(begin(), end(), other.begin(), other.end(), @@ -363,14 +357,31 @@ bool AudioProfileVector::equals(const AudioProfileVector& other) const }); } +void AudioProfileVector::addAllValidProfiles(const AudioProfileVector& audioProfiles) { + for (const auto& audioProfile : audioProfiles) { + if (audioProfile->isValid() && !contains(audioProfile, true /*ignoreDynamicFlags*/)) { + add(audioProfile); + } + } +} + ConversionResult -aidl2legacy_AudioProfileVector(const std::vector& aidl) { - return convertContainer(aidl, aidl2legacy_AudioProfile); +aidl2legacy_AudioProfileVector(const AudioProfileVector::Aidl& aidl, bool isInput) { + return convertContainers(aidl.first, aidl.second, + [isInput](const media::audio::common::AudioProfile& p, + const media::AudioProfileSys& ps) { + return aidl2legacy_AudioProfile(std::make_pair(p, ps), isInput); + }); } -ConversionResult> -legacy2aidl_AudioProfileVector(const AudioProfileVector& legacy) { - return convertContainer>(legacy, legacy2aidl_AudioProfile); +ConversionResult +legacy2aidl_AudioProfileVector(const AudioProfileVector& legacy, bool isInput) { + return convertContainerSplit< + std::vector, + std::vector>(legacy, + [isInput](const sp& p) { + return legacy2aidl_AudioProfile(p, isInput); + }); } AudioProfileVector intersectAudioProfiles(const AudioProfileVector& profiles1, diff --git a/media/libaudiofoundation/DeviceDescriptorBase.cpp b/media/libaudiofoundation/DeviceDescriptorBase.cpp index 5cfea816a22be9b9f1892bbee63e67c0c806d955..5ffbffcc12a78c8340bd040bfe8128552633ad29 100644 --- a/media/libaudiofoundation/DeviceDescriptorBase.cpp +++ b/media/libaudiofoundation/DeviceDescriptorBase.cpp @@ -30,16 +30,20 @@ DeviceDescriptorBase::DeviceDescriptorBase(audio_devices_t type) : { } -DeviceDescriptorBase::DeviceDescriptorBase(audio_devices_t type, const std::string& address) : - DeviceDescriptorBase(AudioDeviceTypeAddr(type, address)) +DeviceDescriptorBase::DeviceDescriptorBase( + audio_devices_t type, const std::string& address, + const FormatVector &encodedFormats) : + DeviceDescriptorBase(AudioDeviceTypeAddr(type, address), encodedFormats) { } -DeviceDescriptorBase::DeviceDescriptorBase(const AudioDeviceTypeAddr &deviceTypeAddr) : +DeviceDescriptorBase::DeviceDescriptorBase( + const AudioDeviceTypeAddr &deviceTypeAddr, const FormatVector &encodedFormats) : AudioPort("", AUDIO_PORT_TYPE_DEVICE, audio_is_output_device(deviceTypeAddr.mType) ? AUDIO_PORT_ROLE_SINK : AUDIO_PORT_ROLE_SOURCE), - mDeviceTypeAddr(deviceTypeAddr) + mDeviceTypeAddr(deviceTypeAddr), + mEncodedFormats(encodedFormats) { if (mDeviceTypeAddr.address().empty() && audio_is_remote_submix_device(mDeviceTypeAddr.mType)) { mDeviceTypeAddr.setAddress("0"); @@ -106,32 +110,23 @@ status_t DeviceDescriptorBase::setEncapsulationMetadataTypes(uint32_t encapsulat return NO_ERROR; } -void DeviceDescriptorBase::dump(std::string *dst, int spaces, int index, +void DeviceDescriptorBase::dump(std::string *dst, int spaces, const char* extraInfo, bool verbose) const { - dst->append(base::StringPrintf("%*sDevice %d:\n", spaces, "", index + 1)); if (mId != 0) { - dst->append(base::StringPrintf("%*s- id: %2d\n", spaces, "", mId)); + dst->append(base::StringPrintf("Port ID: %d; ", mId)); } - if (extraInfo != nullptr) { - dst->append(extraInfo); + dst->append(base::StringPrintf("%s; ", extraInfo)); } - - dst->append(base::StringPrintf("%*s- type: %-48s\n", - spaces, "", ::android::toString(mDeviceTypeAddr.mType).c_str())); + dst->append(base::StringPrintf("{%s}\n", + mDeviceTypeAddr.toString(true /*includeSensitiveInfo*/).c_str())); dst->append(base::StringPrintf( - "%*s- supported encapsulation modes: %u\n", spaces, "", mEncapsulationModes)); - dst->append(base::StringPrintf( - "%*s- supported encapsulation metadata types: %u\n", - spaces, "", mEncapsulationMetadataTypes)); + "%*sEncapsulation modes: %u, metadata types: %u\n", spaces, "", + mEncapsulationModes, mEncapsulationMetadataTypes)); - if (mDeviceTypeAddr.address().size() != 0) { - dst->append(base::StringPrintf( - "%*s- address: %-32s\n", spaces, "", mDeviceTypeAddr.getAddress())); - } - AudioPort::dump(dst, spaces, verbose); + AudioPort::dump(dst, spaces, nullptr, verbose); } std::string DeviceDescriptorBase::toString(bool includeSensitiveInfo) const @@ -148,60 +143,83 @@ void DeviceDescriptorBase::log() const AudioPort::log(" "); } +template +bool checkEqual(const T& f1, const T& f2) +{ + std::set s1(f1.begin(), f1.end()); + std::set s2(f2.begin(), f2.end()); + return s1 == s2; +} + bool DeviceDescriptorBase::equals(const sp &other) const { return other != nullptr && static_cast(this)->equals(other) && - static_cast(this)->equals(other) && - mDeviceTypeAddr.equals(other->mDeviceTypeAddr); + static_cast(this)->equals(other, useInputChannelMask()) && + mDeviceTypeAddr.equals(other->mDeviceTypeAddr) && + checkEqual(mEncodedFormats, other->mEncodedFormats); } - -status_t DeviceDescriptorBase::writeToParcel(Parcel *parcel) const +bool DeviceDescriptorBase::supportsFormat(audio_format_t format) { - media::AudioPort parcelable; - return writeToParcelable(&parcelable) - ?: parcelable.writeToParcel(parcel); + if (mEncodedFormats.empty()) { + return true; + } + + for (const auto& devFormat : mEncodedFormats) { + if (devFormat == format) { + return true; + } + } + return false; } status_t DeviceDescriptorBase::writeToParcelable(media::AudioPort* parcelable) const { AudioPort::writeToParcelable(parcelable); - AudioPortConfig::writeToParcelable(&parcelable->activeConfig); - parcelable->id = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_port_handle_t_int32_t(mId)); - - media::AudioPortDeviceExt ext; - ext.device = VALUE_OR_RETURN_STATUS(legacy2aidl_AudioDeviceTypeAddress(mDeviceTypeAddr)); - ext.encapsulationModes = VALUE_OR_RETURN_STATUS( + AudioPortConfig::writeToParcelable(&parcelable->sys.activeConfig.hal, useInputChannelMask()); + parcelable->hal.id = VALUE_OR_RETURN_STATUS(legacy2aidl_audio_port_handle_t_int32_t(mId)); + parcelable->sys.activeConfig.hal.portId = parcelable->hal.id; + + media::audio::common::AudioPortDeviceExt deviceExt; + deviceExt.device = VALUE_OR_RETURN_STATUS( + legacy2aidl_AudioDeviceTypeAddress(mDeviceTypeAddr)); + deviceExt.encodedFormats = VALUE_OR_RETURN_STATUS( + convertContainer>( + mEncodedFormats, legacy2aidl_audio_format_t_AudioFormatDescription)); + UNION_SET(parcelable->hal.ext, device, deviceExt); + media::AudioPortDeviceExtSys deviceSys; + deviceSys.encapsulationModes = VALUE_OR_RETURN_STATUS( legacy2aidl_AudioEncapsulationMode_mask(mEncapsulationModes)); - ext.encapsulationMetadataTypes = VALUE_OR_RETURN_STATUS( + deviceSys.encapsulationMetadataTypes = VALUE_OR_RETURN_STATUS( legacy2aidl_AudioEncapsulationMetadataType_mask(mEncapsulationMetadataTypes)); - UNION_SET(parcelable->ext, device, std::move(ext)); + UNION_SET(parcelable->sys.ext, device, deviceSys); return OK; } -status_t DeviceDescriptorBase::readFromParcel(const Parcel *parcel) { - media::AudioPort parcelable; - return parcelable.readFromParcel(parcel) - ?: readFromParcelable(parcelable); -} - status_t DeviceDescriptorBase::readFromParcelable(const media::AudioPort& parcelable) { - if (parcelable.type != media::AudioPortType::DEVICE) { + if (parcelable.sys.type != media::AudioPortType::DEVICE) { return BAD_VALUE; } status_t status = AudioPort::readFromParcelable(parcelable) - ?: AudioPortConfig::readFromParcelable(parcelable.activeConfig); + ?: AudioPortConfig::readFromParcelable( + parcelable.sys.activeConfig.hal, useInputChannelMask()); if (status != OK) { return status; } - media::AudioPortDeviceExt ext = VALUE_OR_RETURN_STATUS(UNION_GET(parcelable.ext, device)); + media::audio::common::AudioPortDeviceExt deviceExt = VALUE_OR_RETURN_STATUS( + UNION_GET(parcelable.hal.ext, device)); mDeviceTypeAddr = VALUE_OR_RETURN_STATUS( - aidl2legacy_AudioDeviceTypeAddress(ext.device)); + aidl2legacy_AudioDeviceTypeAddress(deviceExt.device)); + mEncodedFormats = VALUE_OR_RETURN_STATUS( + convertContainer(deviceExt.encodedFormats, + aidl2legacy_AudioFormatDescription_audio_format_t)); + media::AudioPortDeviceExtSys deviceSys = VALUE_OR_RETURN_STATUS( + UNION_GET(parcelable.sys.ext, device)); mEncapsulationModes = VALUE_OR_RETURN_STATUS( - aidl2legacy_AudioEncapsulationMode_mask(ext.encapsulationModes)); + aidl2legacy_AudioEncapsulationMode_mask(deviceSys.encapsulationModes)); mEncapsulationMetadataTypes = VALUE_OR_RETURN_STATUS( - aidl2legacy_AudioEncapsulationMetadataType_mask(ext.encapsulationMetadataTypes)); + aidl2legacy_AudioEncapsulationMetadataType_mask(deviceSys.encapsulationMetadataTypes)); return OK; } diff --git a/media/libaudiofoundation/TEST_MAPPING b/media/libaudiofoundation/TEST_MAPPING index f6d249a1a3083806245a91e47284bb9e3b1fa7c5..efe843710fe609d9efe64fd3747de62988873df0 100644 --- a/media/libaudiofoundation/TEST_MAPPING +++ b/media/libaudiofoundation/TEST_MAPPING @@ -1,7 +1,15 @@ { "presubmit": [ { - "name": "audiofoundation_parcelable_test" + "name": "audiofoundation_parcelable_test" + }, + { + "name": "CtsNativeMediaAAudioTestCases", + "options" : [ + { + "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic.*" + } + ] } ] } diff --git a/media/libaudiofoundation/include/media/AudioContainers.h b/media/libaudiofoundation/include/media/AudioContainers.h index 60b42fb8a44033a8a522caa6745d16ef31805cdb..b6e6c84ad20157a9a038b58f51d8f3266dd56ba1 100644 --- a/media/libaudiofoundation/include/media/AudioContainers.h +++ b/media/libaudiofoundation/include/media/AudioContainers.h @@ -41,6 +41,7 @@ const DeviceTypeSet& getAudioDeviceOutAllUsbSet(); const DeviceTypeSet& getAudioDeviceInAllSet(); const DeviceTypeSet& getAudioDeviceInAllUsbSet(); const DeviceTypeSet& getAudioDeviceOutAllBleSet(); +const DeviceTypeSet& getAudioDeviceOutLeAudioUnicastSet(); template static std::vector Intersection(const std::set& a, const std::set& b) { @@ -111,25 +112,7 @@ static inline audio_devices_t deviceTypesToBitMask(const DeviceTypeSet& deviceTy return types; } -// FIXME: This is temporary helper function. Remove this when getting rid of all -// bit mask usages of audio device types. -static inline DeviceTypeSet deviceTypesFromBitMask(audio_devices_t types) { - DeviceTypeSet deviceTypes; - if ((types & AUDIO_DEVICE_BIT_IN) == 0) { - for (auto deviceType : AUDIO_DEVICE_OUT_ALL_ARRAY) { - if ((types & deviceType) == deviceType) { - deviceTypes.insert(deviceType); - } - } - } else { - for (auto deviceType : AUDIO_DEVICE_IN_ALL_ARRAY) { - if ((types & deviceType) == deviceType) { - deviceTypes.insert(deviceType); - } - } - } - return deviceTypes; -} +std::string deviceTypesToString(const DeviceTypeSet& deviceTypes); bool deviceTypesToString(const DeviceTypeSet& deviceTypes, std::string &str); @@ -138,7 +121,9 @@ std::string dumpDeviceTypes(const DeviceTypeSet& deviceTypes); /** * Return human readable string for device types. */ -std::string toString(const DeviceTypeSet& deviceTypes); +inline std::string toString(const DeviceTypeSet& deviceTypes) { + return deviceTypesToString(deviceTypes); +} } // namespace android diff --git a/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h b/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h index 8edcc58ca531889935a1b3d469af79f29dde699b..11aa222a7941c24d6568fdafffc548eb9046d653 100644 --- a/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h +++ b/media/libaudiofoundation/include/media/AudioDeviceTypeAddr.h @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include #include @@ -32,6 +32,7 @@ namespace android { class AudioDeviceTypeAddr : public Parcelable { public: AudioDeviceTypeAddr() = default; + AudioDeviceTypeAddr(const AudioDeviceTypeAddr&) = default; AudioDeviceTypeAddr(audio_devices_t type, const std::string& address); @@ -88,8 +89,8 @@ std::string dumpAudioDeviceTypeAddrVector(const AudioDeviceTypeAddrVector& devic // Conversion routines, according to AidlConversion.h conventions. ConversionResult -aidl2legacy_AudioDeviceTypeAddress(const media::AudioDevice& aidl); -ConversionResult +aidl2legacy_AudioDeviceTypeAddress(const media::audio::common::AudioDevice& aidl); +ConversionResult legacy2aidl_AudioDeviceTypeAddress(const AudioDeviceTypeAddr& legacy); } // namespace android diff --git a/media/libaudiofoundation/include/media/AudioGain.h b/media/libaudiofoundation/include/media/AudioGain.h index a06b686363ae9bc0f4a0988c120147873937f0a7..10088f28f022833aa856a2e9b2b5e94712a746fd 100644 --- a/media/libaudiofoundation/include/media/AudioGain.h +++ b/media/libaudiofoundation/include/media/AudioGain.h @@ -16,23 +16,23 @@ #pragma once -#include -#include -#include +#include +#include +#include + +#include #include #include #include #include -#include -#include namespace android { -class AudioGain: public RefBase, public Parcelable +class AudioGain: public RefBase { public: - AudioGain(int index, bool useInChannelMask); - virtual ~AudioGain() {} + AudioGain(int index, bool isInput); + virtual ~AudioGain() = default; void setMode(audio_gain_mode_t mode) { mGain.mode = mode; } const audio_gain_mode_t &getMode() const { return mGain.mode; } @@ -71,26 +71,24 @@ public: bool equals(const sp& other) const; - status_t writeToParcel(Parcel* parcel) const override; - status_t readFromParcel(const Parcel* parcel) override; - - status_t writeToParcelable(media::AudioGain* parcelable) const; - status_t readFromParcelable(const media::AudioGain& parcelable); + using Aidl = std::pair; + ConversionResult toParcelable() const; + static ConversionResult> fromParcelable(const Aidl& aidl); private: int mIndex; - struct audio_gain mGain; - bool mUseInChannelMask; + bool mIsInput; + struct audio_gain mGain = {}; bool mUseForVolume = false; }; // Conversion routines, according to AidlConversion.h conventions. ConversionResult> -aidl2legacy_AudioGain(const media::AudioGain& aidl); -ConversionResult +aidl2legacy_AudioGain(const AudioGain::Aidl& aidl); +ConversionResult legacy2aidl_AudioGain(const sp& legacy); -class AudioGains : public std::vector >, public Parcelable +class AudioGains : public std::vector> { public: bool canUseForVolume() const @@ -103,7 +101,7 @@ public: return false; } - int32_t add(const sp gain) + int32_t add(const sp& gain) { push_back(gain); return 0; @@ -111,14 +109,15 @@ public: bool equals(const AudioGains& other) const; - status_t writeToParcel(Parcel* parcel) const override; - status_t readFromParcel(const Parcel* parcel) override; + using Aidl = std::pair< + std::vector, + std::vector>; }; // Conversion routines, according to AidlConversion.h conventions. ConversionResult -aidl2legacy_AudioGains(const std::vector& aidl); -ConversionResult> +aidl2legacy_AudioGains(const AudioGains::Aidl& aidl); +ConversionResult legacy2aidl_AudioGains(const AudioGains& legacy); } // namespace android diff --git a/media/libaudiofoundation/include/media/AudioPort.h b/media/libaudiofoundation/include/media/AudioPort.h index 1cee1c95b2e9a8f7a84ed639425810d95ae14257..d6a098fff0a33873f66c95318551b4af5851926f 100644 --- a/media/libaudiofoundation/include/media/AudioPort.h +++ b/media/libaudiofoundation/include/media/AudioPort.h @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include #include @@ -33,7 +33,7 @@ namespace android { -class AudioPort : public virtual RefBase, public virtual Parcelable +class AudioPort : public virtual RefBase { public: AudioPort(const std::string& name, audio_port_type_t type, audio_port_role_t role) : @@ -47,6 +47,9 @@ public: audio_port_type_t getType() const { return mType; } audio_port_role_t getRole() const { return mRole; } + virtual void setFlags(uint32_t flags); + uint32_t getFlags() const { return useInputChannelMask() ? mFlags.input : mFlags.output; } + void setGains(const AudioGains &gains) { mGains = gains; } const AudioGains &getGains() const { return mGains; } @@ -69,10 +72,10 @@ public: AudioProfileVector &getAudioProfiles() { return mProfiles; } void setExtraAudioDescriptors( - const std::vector extraAudioDescriptors) { + const std::vector extraAudioDescriptors) { mExtraAudioDescriptors = extraAudioDescriptors; } - std::vector &getExtraAudioDescriptors() { + std::vector &getExtraAudioDescriptors() { return mExtraAudioDescriptors; } @@ -93,19 +96,47 @@ public: ((mType == AUDIO_PORT_TYPE_MIX) && (mRole == AUDIO_PORT_ROLE_SINK)); } - void dump(std::string *dst, int spaces, bool verbose = true) const; + bool isDirectOutput() const + { + return (mType == AUDIO_PORT_TYPE_MIX) && (mRole == AUDIO_PORT_ROLE_SOURCE) && + ((mFlags.output & AUDIO_OUTPUT_FLAG_DIRECT) != 0); + } + + bool isMmap() const + { + return (mType == AUDIO_PORT_TYPE_MIX) + && (((mRole == AUDIO_PORT_ROLE_SOURCE) && + ((mFlags.output & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) != 0)) + || ((mRole == AUDIO_PORT_ROLE_SINK) && + ((mFlags.input & AUDIO_INPUT_FLAG_MMAP_NOIRQ) != 0))); + } + + void dump(std::string *dst, int spaces, + const char* extraInfo = nullptr, bool verbose = true) const; void log(const char* indent) const; bool equals(const sp& other) const; - status_t writeToParcel(Parcel* parcel) const override; - status_t readFromParcel(const Parcel* parcel) override; - status_t writeToParcelable(media::AudioPort* parcelable) const; status_t readFromParcelable(const media::AudioPort& parcelable); AudioGains mGains; // gain controllers + // Maximum number of input or output streams that can be simultaneously + // opened for this profile. By convention 0 means no limit. To respect + // legacy behavior, initialized to 1 for output profiles and 0 for input + // profiles + // FIXME: IOProfile code used the same value for both cases. + uint32_t maxOpenCount = 1; + // Maximum number of input or output streams that can be simultaneously + // active for this profile. By convention 0 means no limit. To respect + // legacy behavior, initialized to 0 for output profiles and 1 for input + // profiles + // FIXME: IOProfile code used the same value for both cases. + uint32_t maxActiveCount = 1; + // Mute duration while changing device on this output profile. + uint32_t recommendedMuteDurationMs = 0; + protected: std::string mName; audio_port_type_t mType; @@ -114,7 +145,8 @@ protected: // Audio capabilities that are defined by hardware descriptors when the format is unrecognized // by the platform, e.g. short audio descriptor in EDID for HDMI. - std::vector mExtraAudioDescriptors; + std::vector mExtraAudioDescriptors; + union audio_io_flags mFlags = { .output = AUDIO_OUTPUT_FLAG_NONE }; private: template ::value || std::is_same::value, int> = 0> @@ -130,7 +162,7 @@ private: }; -class AudioPortConfig : public virtual RefBase, public virtual Parcelable +class AudioPortConfig : public virtual RefBase { public: virtual ~AudioPortConfig() = default; @@ -147,15 +179,16 @@ public: audio_format_t getFormat() const { return mFormat; } audio_channel_mask_t getChannelMask() const { return mChannelMask; } audio_port_handle_t getId() const { return mId; } + audio_io_flags getFlags() const { return mFlags; } bool hasGainController(bool canUseForVolume = false) const; - bool equals(const sp& other) const; + bool equals(const sp& other, bool isInput) const; - status_t writeToParcel(Parcel* parcel) const override; - status_t readFromParcel(const Parcel* parcel) override; - status_t writeToParcelable(media::AudioPortConfig* parcelable) const; - status_t readFromParcelable(const media::AudioPortConfig& parcelable); + status_t writeToParcelable( + media::audio::common::AudioPortConfig* parcelable, bool isInput) const; + status_t readFromParcelable( + const media::audio::common::AudioPortConfig& parcelable, bool isInput); protected: unsigned int mSamplingRate = 0u; @@ -163,6 +196,7 @@ protected: audio_channel_mask_t mChannelMask = AUDIO_CHANNEL_NONE; audio_port_handle_t mId = AUDIO_PORT_HANDLE_NONE; struct audio_gain_config mGain = { .index = -1 }; + union audio_io_flags mFlags = { AUDIO_INPUT_FLAG_NONE }; }; } // namespace android diff --git a/media/libaudiofoundation/include/media/AudioProfile.h b/media/libaudiofoundation/include/media/AudioProfile.h index 6a36e78bd54ea307480c9b977777f1e6bec3d0da..79dfd129e31652be901f0ec3aec664c12573f8bc 100644 --- a/media/libaudiofoundation/include/media/AudioProfile.h +++ b/media/libaudiofoundation/include/media/AudioProfile.h @@ -17,11 +17,10 @@ #pragma once #include +#include #include -#include -#include -#include +#include #include #include #include @@ -29,7 +28,7 @@ namespace android { -class AudioProfile final : public RefBase, public Parcelable +class AudioProfile final : public RefBase { public: static sp createFullDynamic(audio_format_t dynamicFormat = AUDIO_FORMAT_DEFAULT); @@ -70,7 +69,7 @@ public: void setDynamicFormat(bool dynamic) { mIsDynamicFormat = dynamic; } bool isDynamicFormat() const { return mIsDynamicFormat; } - bool isDynamic() { return mIsDynamicFormat || mIsDynamicChannels || mIsDynamicRate; } + bool isDynamic() const { return mIsDynamicFormat || mIsDynamicChannels || mIsDynamicRate; } audio_encapsulation_type_t getEncapsulationType() const { return mEncapsulationType; } void setEncapsulationType(audio_encapsulation_type_t encapsulationType) { @@ -79,13 +78,17 @@ public: void dump(std::string *dst, int spaces) const; - bool equals(const sp& other) const; + bool equals(const sp& other, bool ignoreDynamicFlags = false) const; - status_t writeToParcel(Parcel* parcel) const override; - status_t readFromParcel(const Parcel* parcel) override; + using Aidl = std::pair; + ConversionResult toParcelable(bool isInput) const; + static ConversionResult> fromParcelable( + const Aidl& aidl, bool isInput); - ConversionResult toParcelable() const; - static ConversionResult> fromParcelable(const media::AudioProfile& parcelable); + ConversionResult + toCommonParcelable(bool isInput) const; + static ConversionResult> fromCommonParcelable( + const media::audio::common::AudioProfile& aidl, bool isInput); private: @@ -106,11 +109,16 @@ private: // Conversion routines, according to AidlConversion.h conventions. ConversionResult> -aidl2legacy_AudioProfile(const media::AudioProfile& aidl); -ConversionResult -legacy2aidl_AudioProfile(const sp& legacy); +aidl2legacy_AudioProfile(const AudioProfile::Aidl& aidl, bool isInput); +ConversionResult +legacy2aidl_AudioProfile(const sp& legacy, bool isInput); -class AudioProfileVector : public std::vector>, public Parcelable +ConversionResult> +aidl2legacy_AudioProfile_common(const media::audio::common::AudioProfile& aidl, bool isInput); +ConversionResult +legacy2aidl_AudioProfile_common(const sp& legacy, bool isInput); + +class AudioProfileVector : public std::vector> { public: virtual ~AudioProfileVector() = default; @@ -131,23 +139,25 @@ public: bool hasDynamicProfile() const; bool hasDynamicRateFor(audio_format_t format) const; - bool contains(const sp& profile) const; + bool contains(const sp& profile, bool ignoreDynamicFlags = false) const; virtual void dump(std::string *dst, int spaces) const; bool equals(const AudioProfileVector& other) const; + void addAllValidProfiles(const AudioProfileVector& audioProfiles); - status_t writeToParcel(Parcel* parcel) const override; - status_t readFromParcel(const Parcel* parcel) override; + using Aidl = std::pair< + std::vector, + std::vector>; }; bool operator == (const AudioProfile &left, const AudioProfile &right); // Conversion routines, according to AidlConversion.h conventions. ConversionResult -aidl2legacy_AudioProfileVector(const std::vector& aidl); -ConversionResult> -legacy2aidl_AudioProfileVector(const AudioProfileVector& legacy); +aidl2legacy_AudioProfileVector(const AudioProfileVector::Aidl& aidl, bool isInput); +ConversionResult +legacy2aidl_AudioProfileVector(const AudioProfileVector& legacy, bool isInput); AudioProfileVector intersectAudioProfiles(const AudioProfileVector& profiles1, const AudioProfileVector& profiles2); diff --git a/media/libaudiofoundation/include/media/DeviceDescriptorBase.h b/media/libaudiofoundation/include/media/DeviceDescriptorBase.h index 140ce360e87304305efbce635efaef83ff9ac196..1f0c768cd8e7d57bc99b51b5353fbc4dd86309ac 100644 --- a/media/libaudiofoundation/include/media/DeviceDescriptorBase.h +++ b/media/libaudiofoundation/include/media/DeviceDescriptorBase.h @@ -36,16 +36,21 @@ class DeviceDescriptorBase : public AudioPort, public AudioPortConfig public: // Note that empty name refers by convention to a generic device. explicit DeviceDescriptorBase(audio_devices_t type); - DeviceDescriptorBase(audio_devices_t type, const std::string& address); - explicit DeviceDescriptorBase(const AudioDeviceTypeAddr& deviceTypeAddr); + DeviceDescriptorBase(audio_devices_t type, const std::string& address, + const FormatVector &encodedFormats = FormatVector{}); + DeviceDescriptorBase(const AudioDeviceTypeAddr& deviceTypeAddr, + const FormatVector &encodedFormats = FormatVector{}); - virtual ~DeviceDescriptorBase() {} + virtual ~DeviceDescriptorBase() = default; audio_devices_t type() const { return mDeviceTypeAddr.mType; } const std::string& address() const { return mDeviceTypeAddr.address(); } void setAddress(const std::string &address); const AudioDeviceTypeAddr& getDeviceTypeAddr() const { return mDeviceTypeAddr; } + const FormatVector& encodedFormats() const { return mEncodedFormats; } + bool supportsFormat(audio_format_t format); + // AudioPortConfig virtual sp getAudioPort() const { return static_cast(const_cast(this)); @@ -60,7 +65,7 @@ public: status_t setEncapsulationModes(uint32_t encapsulationModes); status_t setEncapsulationMetadataTypes(uint32_t encapsulationMetadataTypes); - void dump(std::string *dst, int spaces, int index, + void dump(std::string *dst, int spaces, const char* extraInfo = nullptr, bool verbose = true) const; void log() const; @@ -74,14 +79,12 @@ public: bool equals(const sp& other) const; - status_t writeToParcel(Parcel* parcel) const override; - status_t readFromParcel(const Parcel* parcel) override; - status_t writeToParcelable(media::AudioPort* parcelable) const; status_t readFromParcelable(const media::AudioPort& parcelable); protected: AudioDeviceTypeAddr mDeviceTypeAddr; + FormatVector mEncodedFormats; uint32_t mEncapsulationModes = 0; uint32_t mEncapsulationMetadataTypes = 0; private: diff --git a/media/libaudiofoundation/tests/Android.bp b/media/libaudiofoundation/tests/Android.bp index bb9a5f2ac4d5772780a07a397376a8d55a48db07..3f1fbea86dec9c1d04865a28d797284309d5f1fd 100644 --- a/media/libaudiofoundation/tests/Android.bp +++ b/media/libaudiofoundation/tests/Android.bp @@ -11,12 +11,20 @@ cc_test { name: "audiofoundation_parcelable_test", shared_libs: [ - "libaudiofoundation", + "libbase", "libbinder", "liblog", "libutils", ], + static_libs: [ + "android.media.audio.common.types-V1-cpp", + "audioclient-types-aidl-cpp", + "libaudioclient_aidl_conversion", + "libaudiofoundation", + "libstagefright_foundation", + ], + header_libs: [ "libaudio_system_headers", ], diff --git a/media/libaudiofoundation/tests/audiofoundation_parcelable_test.cpp b/media/libaudiofoundation/tests/audiofoundation_parcelable_test.cpp index 068b5d86be47c587a410f75e0a13cead1207643f..50d8dc8529d61215084d1ff67b5c7ba09fa04b29 100644 --- a/media/libaudiofoundation/tests/audiofoundation_parcelable_test.cpp +++ b/media/libaudiofoundation/tests/audiofoundation_parcelable_test.cpp @@ -53,7 +53,7 @@ public: AudioGains getAudioGainsForTest() { AudioGains audioGains; - sp audioGain = new AudioGain(0 /*index*/, false /*useInChannelMask*/); + sp audioGain = new AudioGain(0 /*index*/, false /*isInput*/); audioGain->setMode(AUDIO_GAIN_MODE_JOINT); audioGain->setChannelMask(AUDIO_CHANNEL_OUT_STEREO); audioGain->setMinValueInMb(-3200); @@ -75,57 +75,74 @@ AudioProfileVector getAudioProfileVectorForTest() { return audioProfiles; } -TEST(AudioFoundationParcelableTest, ParcelingAudioGain) { - Parcel data; - AudioGains audioGains = getAudioGainsForTest(); - - ASSERT_EQ(data.writeParcelable(audioGains), NO_ERROR); - data.setDataPosition(0); - AudioGains audioGainsFromParcel; - ASSERT_EQ(data.readParcelable(&audioGainsFromParcel), NO_ERROR); - ASSERT_TRUE(audioGainsFromParcel.equals(audioGains)); +TEST(AudioFoundationParcelableTest, ParcelingAudioProfile) { + sp profile = getAudioProfileVectorForTest()[0]; + auto conv = legacy2aidl_AudioProfile(profile, false /*isInput*/); + ASSERT_TRUE(conv.ok()); + auto convBack = aidl2legacy_AudioProfile(conv.value(), false /*isInput*/); + ASSERT_TRUE(convBack.ok()); + ASSERT_TRUE(profile->equals(convBack.value())); } TEST(AudioFoundationParcelableTest, ParcelingAudioProfileVector) { - Parcel data; - AudioProfileVector audioProfiles = getAudioProfileVectorForTest(); + AudioProfileVector profiles = getAudioProfileVectorForTest(); + auto conv = legacy2aidl_AudioProfileVector(profiles, false /*isInput*/); + ASSERT_TRUE(conv.ok()); + auto convBack = aidl2legacy_AudioProfileVector(conv.value(), false /*isInput*/); + ASSERT_TRUE(convBack.ok()); + ASSERT_TRUE(profiles.equals(convBack.value())); +} - ASSERT_EQ(data.writeParcelable(audioProfiles), NO_ERROR); - data.setDataPosition(0); - AudioProfileVector audioProfilesFromParcel; - ASSERT_EQ(data.readParcelable(&audioProfilesFromParcel), NO_ERROR); - ASSERT_TRUE(audioProfilesFromParcel.equals(audioProfiles)); +TEST(AudioFoundationParcelableTest, ParcelingAudioGain) { + sp audioGain = getAudioGainsForTest()[0]; + auto conv = legacy2aidl_AudioGain(audioGain); + ASSERT_TRUE(conv.ok()); + auto convBack = aidl2legacy_AudioGain(conv.value()); + ASSERT_TRUE(convBack.ok()); + ASSERT_TRUE(audioGain->equals(convBack.value())); +} + +TEST(AudioFoundationParcelableTest, ParcelingAudioGains) { + AudioGains audioGains = getAudioGainsForTest(); + auto conv = legacy2aidl_AudioGains(audioGains); + ASSERT_TRUE(conv.ok()); + auto convBack = aidl2legacy_AudioGains(conv.value()); + ASSERT_TRUE(convBack.ok()); + ASSERT_TRUE(audioGains.equals(convBack.value())); } TEST(AudioFoundationParcelableTest, ParcelingAudioPort) { - Parcel data; sp audioPort = new AudioPort( "AudioPortName", AUDIO_PORT_TYPE_DEVICE, AUDIO_PORT_ROLE_SINK); audioPort->setGains(getAudioGainsForTest()); audioPort->setAudioProfiles(getAudioProfileVectorForTest()); - ASSERT_EQ(data.writeParcelable(*audioPort), NO_ERROR); - data.setDataPosition(0); + media::AudioPort parcelable; + ASSERT_EQ(NO_ERROR, audioPort->writeToParcelable(&parcelable)); sp audioPortFromParcel = new AudioPort( "", AUDIO_PORT_TYPE_NONE, AUDIO_PORT_ROLE_NONE); - ASSERT_EQ(data.readParcelable(audioPortFromParcel.get()), NO_ERROR); + ASSERT_EQ(NO_ERROR, audioPortFromParcel->readFromParcelable(parcelable)); ASSERT_TRUE(audioPortFromParcel->equals(audioPort)); } TEST(AudioFoundationParcelableTest, ParcelingAudioPortConfig) { + const bool isInput = false; Parcel data; sp audioPortConfig = new AudioPortConfigTestStub(); audioPortConfig->applyAudioPortConfig(&TEST_AUDIO_PORT_CONFIG); - - ASSERT_EQ(data.writeParcelable(*audioPortConfig), NO_ERROR); + media::audio::common::AudioPortConfig parcelable{}; + ASSERT_EQ(NO_ERROR, audioPortConfig->writeToParcelable(&parcelable, isInput)); + ASSERT_EQ(NO_ERROR, data.writeParcelable(parcelable)); data.setDataPosition(0); + media::audio::common::AudioPortConfig parcelableFromParcel{}; + ASSERT_EQ(NO_ERROR, data.readParcelable(&parcelableFromParcel)); sp audioPortConfigFromParcel = new AudioPortConfigTestStub(); - ASSERT_EQ(data.readParcelable(audioPortConfigFromParcel.get()), NO_ERROR); - ASSERT_TRUE(audioPortConfigFromParcel->equals(audioPortConfig)); + ASSERT_EQ(NO_ERROR, audioPortConfigFromParcel->readFromParcelable( + parcelableFromParcel, isInput)); + ASSERT_TRUE(audioPortConfigFromParcel->equals(audioPortConfig, isInput)); } TEST(AudioFoundationParcelableTest, ParcelingDeviceDescriptorBase) { - Parcel data; sp desc = new DeviceDescriptorBase(AUDIO_DEVICE_OUT_SPEAKER); desc->setGains(getAudioGainsForTest()); desc->setAudioProfiles(getAudioProfileVectorForTest()); @@ -135,10 +152,10 @@ TEST(AudioFoundationParcelableTest, ParcelingDeviceDescriptorBase) { ASSERT_EQ(desc->setEncapsulationMetadataTypes( AUDIO_ENCAPSULATION_METADATA_TYPE_ALL_POSITION_BITS), NO_ERROR); - ASSERT_EQ(data.writeParcelable(*desc), NO_ERROR); - data.setDataPosition(0); + media::AudioPort parcelable; + ASSERT_EQ(NO_ERROR, desc->writeToParcelable(&parcelable)); sp descFromParcel = new DeviceDescriptorBase(AUDIO_DEVICE_NONE); - ASSERT_EQ(data.readParcelable(descFromParcel.get()), NO_ERROR); + ASSERT_EQ(NO_ERROR, descFromParcel->readFromParcelable(parcelable)); ASSERT_TRUE(descFromParcel->equals(desc)); } diff --git a/media/libaudiohal/Android.bp b/media/libaudiohal/Android.bp index b32c735f78430823c2e13aadad5d6b1ab126c2bf..5f63e8de04916060a7fb9c9b3d0369c219f19d99 100644 --- a/media/libaudiohal/Android.bp +++ b/media/libaudiohal/Android.bp @@ -31,6 +31,7 @@ cc_library_shared { ], shared_libs: [ + "audioclient-types-aidl-cpp", "libdl", "libhidlbase", "liblog", diff --git a/media/libaudiohal/DevicesFactoryHalInterface.cpp b/media/libaudiohal/DevicesFactoryHalInterface.cpp index 325a54763c715de4be361b2d32e8b5bec25086b8..5ad26fcebd1e5d39b70b673cd0127997d6f9925f 100644 --- a/media/libaudiohal/DevicesFactoryHalInterface.cpp +++ b/media/libaudiohal/DevicesFactoryHalInterface.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include + #include #include @@ -21,8 +23,10 @@ namespace android { // static sp DevicesFactoryHalInterface::create() { + using namespace std::string_literals; return createPreferredImpl( - "android.hardware.audio", "IDevicesFactory"); + std::make_pair("android.hardware.audio"s, "IDevicesFactory"s), + std::make_pair("android.hardware.audio.effect"s, "IEffectsFactory"s)); } } // namespace android diff --git a/media/libaudiohal/EffectsFactoryHalInterface.cpp b/media/libaudiohal/EffectsFactoryHalInterface.cpp index bc3b4c1c4a6818dd9e3f358c4fc494ab7bbd94b3..8a28f6402356c5539a9a87604448cd911b2b8333 100644 --- a/media/libaudiohal/EffectsFactoryHalInterface.cpp +++ b/media/libaudiohal/EffectsFactoryHalInterface.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include + #include #include @@ -21,8 +23,10 @@ namespace android { // static sp EffectsFactoryHalInterface::create() { + using namespace std::string_literals; return createPreferredImpl( - "android.hardware.audio.effect", "IEffectsFactory"); + std::make_pair("android.hardware.audio.effect"s, "IEffectsFactory"s), + std::make_pair("android.hardware.audio"s, "IDevicesFactory"s)); } // static diff --git a/media/libaudiohal/FactoryHalHidl.cpp b/media/libaudiohal/FactoryHalHidl.cpp index 804edcc9aed5dbe2a71e812456ef808b3db01aae..590fec5443ad5969018158d79eabfbdd00a58324 100644 --- a/media/libaudiohal/FactoryHalHidl.cpp +++ b/media/libaudiohal/FactoryHalHidl.cpp @@ -16,6 +16,10 @@ #define LOG_TAG "FactoryHalHidl" +#include +#include +#include + #include #include @@ -28,15 +32,16 @@ namespace android::detail { namespace { -/** Supported HAL versions, in order of preference. +/** Supported HAL versions, from most recent to least recent. */ -const char* sAudioHALVersions[] = { - "7.1", - "7.0", - "6.0", - "5.0", - "4.0", - nullptr +#define CONC_VERSION(maj, min) #maj "." #min +#define DECLARE_VERSION(maj, min) std::make_pair(std::make_pair(maj, min), CONC_VERSION(maj, min)) +static constexpr std::array, const char*>, 5> sAudioHALVersions = { + DECLARE_VERSION(7, 1), + DECLARE_VERSION(7, 0), + DECLARE_VERSION(6, 0), + DECLARE_VERSION(5, 0), + DECLARE_VERSION(4, 0) }; bool createHalService(const std::string& version, const std::string& interface, @@ -94,11 +99,22 @@ bool hasHalService(const std::string& package, const std::string& version, } // namespace -void* createPreferredImpl(const std::string& package, const std::string& interface) { - for (auto version = detail::sAudioHALVersions; *version != nullptr; ++version) { - void* rawInterface = nullptr; - if (hasHalService(package, *version, interface) - && createHalService(*version, interface, &rawInterface)) { +void* createPreferredImpl(const InterfaceName& iface, const InterfaceName& siblingIface) { + auto findMostRecentVersion = [](const InterfaceName& iface) { + return std::find_if(detail::sAudioHALVersions.begin(), detail::sAudioHALVersions.end(), + [&](const auto& v) { return hasHalService(iface.first, v.second, iface.second); }); + }; + auto ifaceVersionIt = findMostRecentVersion(iface); + auto siblingVersionIt = findMostRecentVersion(siblingIface); + if (ifaceVersionIt != detail::sAudioHALVersions.end() && + siblingVersionIt != detail::sAudioHALVersions.end() && + // same major version + ifaceVersionIt->first.first == siblingVersionIt->first.first) { + std::string libraryVersion = + ifaceVersionIt->first >= siblingVersionIt->first ? + ifaceVersionIt->second : siblingVersionIt->second; + void* rawInterface; + if (createHalService(libraryVersion, iface.second, &rawInterface)) { return rawInterface; } } diff --git a/media/libaudiohal/TEST_MAPPING b/media/libaudiohal/TEST_MAPPING new file mode 100644 index 0000000000000000000000000000000000000000..3de5a9fa85ee91fcf87cb4eae6150daaecb2ec6d --- /dev/null +++ b/media/libaudiohal/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "CtsNativeMediaAAudioTestCases", + "options" : [ + { + "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic.*" + } + ] + } + ] +} diff --git a/media/libaudiohal/impl/Android.bp b/media/libaudiohal/impl/Android.bp index e9c8723c770f29c74fc291603ca1f3ca8e4621d0..d30883a95c30b66a08adf8042362b87274c9c103 100644 --- a/media/libaudiohal/impl/Android.bp +++ b/media/libaudiohal/impl/Android.bp @@ -10,12 +10,9 @@ package { filegroup { name: "audio_core_hal_client_sources", srcs: [ - "DeviceHalLocal.cpp", - "DevicesFactoryHalHybrid.cpp", - "DevicesFactoryHalLocal.cpp", + "CoreConversionHelperHidl.cpp", "DeviceHalHidl.cpp", "DevicesFactoryHalHidl.cpp", - "StreamHalLocal.cpp", "StreamHalHidl.cpp", ], } @@ -24,6 +21,7 @@ filegroup { name: "audio_effect_hal_client_sources", srcs: [ "EffectBufferHalHidl.cpp", + "EffectConversionHelperHidl.cpp", "EffectHalHidl.cpp", "EffectsFactoryHalHidl.cpp", ], @@ -32,10 +30,6 @@ filegroup { cc_defaults { name: "libaudiohal_default", - srcs: [ - "ConversionHelperHidl.cpp", - ], - cflags: [ "-Wall", "-Wextra", @@ -61,6 +55,7 @@ cc_defaults { "libmedia_helper", "libmediautils", "libutils", + "audioclient-types-aidl-cpp", ], header_libs: [ "android.hardware.audio.common.util@all-versions", @@ -79,6 +74,7 @@ cc_library_shared { srcs: [ ":audio_core_hal_client_sources", ":audio_effect_hal_client_sources", + "EffectsFactoryHalHidlEntry.cpp", ], shared_libs: [ "android.hardware.audio.common@4.0", @@ -101,6 +97,7 @@ cc_library_shared { srcs: [ ":audio_core_hal_client_sources", ":audio_effect_hal_client_sources", + "EffectsFactoryHalHidlEntry.cpp", ], shared_libs: [ "android.hardware.audio.common@5.0", @@ -123,6 +120,7 @@ cc_library_shared { srcs: [ ":audio_core_hal_client_sources", ":audio_effect_hal_client_sources", + "EffectsFactoryHalHidlEntry.cpp", ], shared_libs: [ "android.hardware.audio.common@6.0", @@ -139,20 +137,41 @@ cc_library_shared { ] } +cc_library_static { + name: "libaudiohal.effect@7.0", + defaults: ["libaudiohal_default"], + srcs: [ + ":audio_effect_hal_client_sources", + ], + static_libs: [ + "android.hardware.audio.common@7.0", + "android.hardware.audio.common@7.0-util", + "android.hardware.audio.effect@7.0", + "android.hardware.audio.effect@7.0-util", + ], + cflags: [ + "-DMAJOR_VERSION=7", + "-DMINOR_VERSION=0", + "-include common/all-versions/VersionMacro.h", + ] +} + cc_library_shared { name: "libaudiohal@7.0", defaults: ["libaudiohal_default"], srcs: [ ":audio_core_hal_client_sources", - ":audio_effect_hal_client_sources", + "EffectsFactoryHalHidlEntry.cpp", ], - shared_libs: [ + static_libs: [ "android.hardware.audio.common@7.0", + "android.hardware.audio.common@7.0-enums", "android.hardware.audio.common@7.0-util", "android.hardware.audio.effect@7.0", "android.hardware.audio.effect@7.0-util", "android.hardware.audio@7.0", "android.hardware.audio@7.0-util", + "libaudiohal.effect@7.0", ], cflags: [ "-DMAJOR_VERSION=7", @@ -166,13 +185,19 @@ cc_library_shared { defaults: ["libaudiohal_default"], srcs: [ ":audio_core_hal_client_sources", + "EffectsFactoryHalHidlEntry.cpp", ], - shared_libs: [ + static_libs: [ "android.hardware.audio.common@7.0", + "android.hardware.audio.common@7.0-util", + "android.hardware.audio.common@7.1-enums", "android.hardware.audio.common@7.1-util", + "android.hardware.audio.effect@7.0", + "android.hardware.audio.effect@7.0-util", "android.hardware.audio@7.0", "android.hardware.audio@7.1", "android.hardware.audio@7.1-util", + "libaudiohal.effect@7.0", ], cflags: [ "-DMAJOR_VERSION=7", diff --git a/media/libaudiohal/impl/ConversionHelperHidl.h b/media/libaudiohal/impl/ConversionHelperHidl.h index 9368551ef8d485a94445147791e9c5cdf741c57a..6e2c8312cd151dce3d12acf43b3fdf8187f1a80d 100644 --- a/media/libaudiohal/impl/ConversionHelperHidl.h +++ b/media/libaudiohal/impl/ConversionHelperHidl.h @@ -17,33 +17,25 @@ #ifndef ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H #define ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H -#include PATH(android/hardware/audio/CORE_TYPES_FILE_VERSION/types.h) +#include + #include #include -#include -#include -#include - -using ::android::hardware::audio::CORE_TYPES_CPP_VERSION::ParameterValue; -using CoreResult = ::android::hardware::audio::CORE_TYPES_CPP_VERSION::Result; - -using ::android::hardware::Return; -using ::android::hardware::hidl_string; -using ::android::hardware::hidl_vec; namespace android { +template class ConversionHelperHidl { protected: - static status_t keysFromHal(const String8& keys, hidl_vec *hidlKeys); - static status_t parametersFromHal(const String8& kvPairs, hidl_vec *hidlParams); - static void parametersToHal(const hidl_vec& parameters, String8 *values); - static void argsFromHal(const Vector& args, hidl_vec *hidlArgs); + using HalResultConverter = std::function; + const std::string mClassName; - ConversionHelperHidl(const char* className); + ConversionHelperHidl(std::string_view className, HalResultConverter resultConv) + : mClassName(className), mResultConverter(resultConv) {} template - status_t processReturn(const char* funcName, const Return& ret, T *retval) { + status_t processReturn(const char* funcName, + const ::android::hardware::Return& ret, T *retval) { if (ret.isOk()) { // This way it also works for enum class to unscoped enum conversion. *retval = static_cast(static_cast(ret)); @@ -53,35 +45,40 @@ class ConversionHelperHidl { } template - status_t processReturn(const char* funcName, const Return& ret) { + status_t processReturn(const char* funcName, const ::android::hardware::Return& ret) { if (!ret.isOk()) { emitError(funcName, ret.description().c_str()); } return ret.isOk() ? OK : FAILED_TRANSACTION; } - status_t processReturn(const char* funcName, const Return& ret) { + status_t processReturn(const char* funcName, + const ::android::hardware::Return& ret) { if (!ret.isOk()) { emitError(funcName, ret.description().c_str()); } - return ret.isOk() ? analyzeResult(ret) : FAILED_TRANSACTION; + return ret.isOk() ? mResultConverter(ret) : FAILED_TRANSACTION; } template status_t processReturn( - const char* funcName, const Return& ret, CoreResult retval) { + const char* funcName, const ::android::hardware::Return& ret, HalResult retval) { if (!ret.isOk()) { emitError(funcName, ret.description().c_str()); } - return ret.isOk() ? analyzeResult(retval) : FAILED_TRANSACTION; + return ret.isOk() ? mResultConverter(retval) : FAILED_TRANSACTION; } - private: - const char* mClassName; + const std::string& getClassName() const { + return mClassName; + } - static status_t analyzeResult(const CoreResult& result); + private: + HalResultConverter mResultConverter; - void emitError(const char* funcName, const char* description); + void emitError(const char* funcName, const char* description) { + ALOGE("%s %p %s: %s (from rpc)", mClassName.c_str(), this, funcName, description); + } }; } // namespace android diff --git a/media/libaudiohal/impl/ConversionHelperHidl.cpp b/media/libaudiohal/impl/CoreConversionHelperHidl.cpp similarity index 87% rename from media/libaudiohal/impl/ConversionHelperHidl.cpp rename to media/libaudiohal/impl/CoreConversionHelperHidl.cpp index 1d34814f02009077c79667f1e5960fc3f6f5a837..2ac8a42e88202b4c1a621fc774480c116aec2a73 100644 --- a/media/libaudiohal/impl/ConversionHelperHidl.cpp +++ b/media/libaudiohal/impl/CoreConversionHelperHidl.cpp @@ -21,7 +21,7 @@ #include #include -#include "ConversionHelperHidl.h" +#include "CoreConversionHelperHidl.h" namespace android { @@ -29,7 +29,8 @@ using namespace ::android::hardware::audio::common::COMMON_TYPES_CPP_VERSION; using namespace ::android::hardware::audio::CORE_TYPES_CPP_VERSION; // static -status_t ConversionHelperHidl::keysFromHal(const String8& keys, hidl_vec *hidlKeys) { +status_t CoreConversionHelperHidl::keysFromHal( + const String8& keys, hidl_vec *hidlKeys) { AudioParameter halKeys(keys); if (halKeys.size() == 0) return BAD_VALUE; hidlKeys->resize(halKeys.size()); @@ -74,7 +75,7 @@ status_t ConversionHelperHidl::keysFromHal(const String8& keys, hidl_vec *hidlParams) { AudioParameter params(kvPairs); if (params.size() == 0) return BAD_VALUE; @@ -90,7 +91,7 @@ status_t ConversionHelperHidl::parametersFromHal( } // static -void ConversionHelperHidl::parametersToHal( +void CoreConversionHelperHidl::parametersToHal( const hidl_vec& parameters, String8 *values) { AudioParameter params; for (size_t i = 0; i < parameters.size(); ++i) { @@ -99,12 +100,11 @@ void ConversionHelperHidl::parametersToHal( values->setTo(params.toString()); } -ConversionHelperHidl::ConversionHelperHidl(const char* className) - : mClassName(className) { -} +CoreConversionHelperHidl::CoreConversionHelperHidl(std::string_view className) + : ConversionHelperHidl(className, analyzeResult) {} // static -void ConversionHelperHidl::argsFromHal( +void CoreConversionHelperHidl::argsFromHal( const Vector& args, hidl_vec *hidlArgs) { hidlArgs->resize(args.size()); for (size_t i = 0; i < args.size(); ++i) { @@ -113,19 +113,15 @@ void ConversionHelperHidl::argsFromHal( } // static -status_t ConversionHelperHidl::analyzeResult(const Result& result) { +status_t CoreConversionHelperHidl::analyzeResult(const CoreResult& result) { switch (result) { case Result::OK: return OK; case Result::INVALID_ARGUMENTS: return BAD_VALUE; case Result::INVALID_STATE: return NOT_ENOUGH_DATA; case Result::NOT_INITIALIZED: return NO_INIT; case Result::NOT_SUPPORTED: return INVALID_OPERATION; - default: return NO_INIT; } -} - -void ConversionHelperHidl::emitError(const char* funcName, const char* description) { - ALOGE("%s %p %s: %s (from rpc)", mClassName, this, funcName, description); + return NO_INIT; } } // namespace android diff --git a/media/libaudiohal/impl/CoreConversionHelperHidl.h b/media/libaudiohal/impl/CoreConversionHelperHidl.h new file mode 100644 index 0000000000000000000000000000000000000000..a4d76f35f71937e84dea1ea986ca4da2874cb682 --- /dev/null +++ b/media/libaudiohal/impl/CoreConversionHelperHidl.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2022 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_HARDWARE_CORE_CONVERSION_HELPER_HIDL_H +#define ANDROID_HARDWARE_CORE_CONVERSION_HELPER_HIDL_H + +#include "ConversionHelperHidl.h" + +#include PATH(android/hardware/audio/CORE_TYPES_FILE_VERSION/types.h) +#include +#include +#include + +using ::android::hardware::audio::CORE_TYPES_CPP_VERSION::ParameterValue; +using CoreResult = ::android::hardware::audio::CORE_TYPES_CPP_VERSION::Result; + +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; + +namespace android { + +class CoreConversionHelperHidl : public ConversionHelperHidl { + protected: + static status_t keysFromHal(const String8& keys, hidl_vec *hidlKeys); + static status_t parametersFromHal(const String8& kvPairs, hidl_vec *hidlParams); + static void parametersToHal(const hidl_vec& parameters, String8 *values); + static void argsFromHal(const Vector& args, hidl_vec *hidlArgs); + + CoreConversionHelperHidl(std::string_view className); + + private: + static status_t analyzeResult(const CoreResult& result); +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_CORE_CONVERSION_HELPER_HIDL_H diff --git a/media/libaudiohal/impl/DeviceHalHidl.cpp b/media/libaudiohal/impl/DeviceHalHidl.cpp index ca2286e54c89f9b3528cb64a87355963e98602e0..0cdf621c7a26974d54dfbdf9709550ffdce27a8a 100644 --- a/media/libaudiohal/impl/DeviceHalHidl.cpp +++ b/media/libaudiohal/impl/DeviceHalHidl.cpp @@ -20,8 +20,10 @@ //#define LOG_NDEBUG 0 #include +#include #include #include +#include #include #include PATH(android/hardware/audio/FILE_VERSION/IPrimaryDevice.h) @@ -44,13 +46,16 @@ namespace android { using namespace ::android::hardware::audio::common::COMMON_TYPES_CPP_VERSION; using namespace ::android::hardware::audio::CORE_TYPES_CPP_VERSION; +#define TIME_CHECK() auto timeCheck = \ + mediautils::makeTimeCheckStatsForClassMethod(getClassName(), __func__) + DeviceHalHidl::DeviceHalHidl(const sp<::android::hardware::audio::CPP_VERSION::IDevice>& device) - : ConversionHelperHidl("Device"), mDevice(device) { + : CoreConversionHelperHidl("DeviceHalHidl"), mDevice(device) { } DeviceHalHidl::DeviceHalHidl( const sp<::android::hardware::audio::CPP_VERSION::IPrimaryDevice>& device) - : ConversionHelperHidl("Device"), + : CoreConversionHelperHidl("DeviceHalHidl"), #if MAJOR_VERSION <= 6 || (MAJOR_VERSION == 7 && MINOR_VERSION == 0) mDevice(device), #endif @@ -83,22 +88,26 @@ status_t DeviceHalHidl::getSupportedDevices(uint32_t*) { } status_t DeviceHalHidl::initCheck() { + TIME_CHECK(); if (mDevice == 0) return NO_INIT; return processReturn("initCheck", mDevice->initCheck()); } status_t DeviceHalHidl::setVoiceVolume(float volume) { + TIME_CHECK(); if (mDevice == 0) return NO_INIT; if (mPrimaryDevice == 0) return INVALID_OPERATION; return processReturn("setVoiceVolume", mPrimaryDevice->setVoiceVolume(volume)); } status_t DeviceHalHidl::setMasterVolume(float volume) { + TIME_CHECK(); if (mDevice == 0) return NO_INIT; return processReturn("setMasterVolume", mDevice->setMasterVolume(volume)); } status_t DeviceHalHidl::getMasterVolume(float *volume) { + TIME_CHECK(); if (mDevice == 0) return NO_INIT; Result retval; Return ret = mDevice->getMasterVolume( @@ -112,17 +121,20 @@ status_t DeviceHalHidl::getMasterVolume(float *volume) { } status_t DeviceHalHidl::setMode(audio_mode_t mode) { + TIME_CHECK(); if (mDevice == 0) return NO_INIT; if (mPrimaryDevice == 0) return INVALID_OPERATION; return processReturn("setMode", mPrimaryDevice->setMode(AudioMode(mode))); } status_t DeviceHalHidl::setMicMute(bool state) { + TIME_CHECK(); if (mDevice == 0) return NO_INIT; return processReturn("setMicMute", mDevice->setMicMute(state)); } status_t DeviceHalHidl::getMicMute(bool *state) { + TIME_CHECK(); if (mDevice == 0) return NO_INIT; Result retval; Return ret = mDevice->getMicMute( @@ -136,11 +148,13 @@ status_t DeviceHalHidl::getMicMute(bool *state) { } status_t DeviceHalHidl::setMasterMute(bool state) { + TIME_CHECK(); if (mDevice == 0) return NO_INIT; return processReturn("setMasterMute", mDevice->setMasterMute(state)); } status_t DeviceHalHidl::getMasterMute(bool *state) { + TIME_CHECK(); if (mDevice == 0) return NO_INIT; Result retval; Return ret = mDevice->getMasterMute( @@ -154,6 +168,7 @@ status_t DeviceHalHidl::getMasterMute(bool *state) { } status_t DeviceHalHidl::setParameters(const String8& kvPairs) { + TIME_CHECK(); if (mDevice == 0) return NO_INIT; hidl_vec hidlParams; status_t status = parametersFromHal(kvPairs, &hidlParams); @@ -164,6 +179,7 @@ status_t DeviceHalHidl::setParameters(const String8& kvPairs) { } status_t DeviceHalHidl::getParameters(const String8& keys, String8 *values) { + TIME_CHECK(); values->clear(); if (mDevice == 0) return NO_INIT; hidl_vec hidlKeys; @@ -184,6 +200,7 @@ status_t DeviceHalHidl::getParameters(const String8& keys, String8 *values) { status_t DeviceHalHidl::getInputBufferSize( const struct audio_config *config, size_t *size) { + TIME_CHECK(); if (mDevice == 0) return NO_INIT; AudioConfig hidlConfig; HidlUtils::audioConfigFromHal(*config, true /*isInput*/, &hidlConfig); @@ -206,6 +223,7 @@ status_t DeviceHalHidl::openOutputStream( struct audio_config *config, const char *address, sp *outStream) { + TIME_CHECK(); if (mDevice == 0) return NO_INIT; DeviceAddress hidlDevice; if (status_t status = CoreUtils::deviceAddressFromHal(deviceType, address, &hidlDevice); @@ -217,6 +235,16 @@ status_t DeviceHalHidl::openOutputStream( status != OK) { return status; } + +#if !(MAJOR_VERSION == 7 && MINOR_VERSION == 1) + //TODO: b/193496180 use spatializer flag at audio HAL when available + if ((flags & AUDIO_OUTPUT_FLAG_SPATIALIZER) != 0) { + flags = (audio_output_flags_t)(flags & ~AUDIO_OUTPUT_FLAG_SPATIALIZER); + flags = (audio_output_flags_t) + (flags | AUDIO_OUTPUT_FLAG_FAST | AUDIO_OUTPUT_FLAG_DEEP_BUFFER); + } +#endif + CoreUtils::AudioOutputFlags hidlFlags; if (status_t status = CoreUtils::audioOutputFlagsFromHal(flags, &hidlFlags); status != OK) { return status; @@ -252,6 +280,7 @@ status_t DeviceHalHidl::openInputStream( audio_devices_t outputDevice, const char *outputDeviceAddress, sp *inStream) { + TIME_CHECK(); if (mDevice == 0) return NO_INIT; DeviceAddress hidlDevice; if (status_t status = CoreUtils::deviceAddressFromHal(devices, address, &hidlDevice); @@ -315,6 +344,7 @@ status_t DeviceHalHidl::openInputStream( } status_t DeviceHalHidl::supportsAudioPatches(bool *supportsPatches) { + TIME_CHECK(); if (mDevice == 0) return NO_INIT; return processReturn("supportsAudioPatches", mDevice->supportsAudioPatches(), supportsPatches); } @@ -325,6 +355,7 @@ status_t DeviceHalHidl::createAudioPatch( unsigned int num_sinks, const struct audio_port_config *sinks, audio_patch_handle_t *patch) { + TIME_CHECK(); if (mDevice == 0) return NO_INIT; if (patch == nullptr) return BAD_VALUE; @@ -370,6 +401,7 @@ status_t DeviceHalHidl::createAudioPatch( } status_t DeviceHalHidl::releaseAudioPatch(audio_patch_handle_t patch) { + TIME_CHECK(); if (mDevice == 0) return NO_INIT; return processReturn("releaseAudioPatch", mDevice->releaseAudioPatch(patch)); } @@ -392,10 +424,12 @@ status_t DeviceHalHidl::getAudioPortImpl(HalPort *port) { } status_t DeviceHalHidl::getAudioPort(struct audio_port *port) { + TIME_CHECK(); return getAudioPortImpl(port); } status_t DeviceHalHidl::getAudioPort(struct audio_port_v7 *port) { + TIME_CHECK(); #if MAJOR_VERSION >= 7 return getAudioPortImpl(port); #else @@ -416,6 +450,7 @@ status_t DeviceHalHidl::getAudioPort(struct audio_port_v7 *port) { } status_t DeviceHalHidl::setAudioPortConfig(const struct audio_port_config *config) { + TIME_CHECK(); if (mDevice == 0) return NO_INIT; AudioPortConfig hidlConfig; HidlUtils::audioPortConfigFromHal(*config, &hidlConfig); @@ -430,6 +465,7 @@ status_t DeviceHalHidl::getMicrophones( } #elif MAJOR_VERSION >= 4 status_t DeviceHalHidl::getMicrophones(std::vector *microphonesInfo) { + TIME_CHECK(); if (mDevice == 0) return NO_INIT; Result retval; Return ret = mDevice->getMicrophones( @@ -450,6 +486,7 @@ status_t DeviceHalHidl::getMicrophones(std::vector *micro #if MAJOR_VERSION >= 6 status_t DeviceHalHidl::addDeviceEffect( audio_port_handle_t device, sp effect) { + TIME_CHECK(); if (mDevice == 0) return NO_INIT; return processReturn("addDeviceEffect", mDevice->addDeviceEffect( static_cast(device), effect->effectId())); @@ -464,6 +501,7 @@ status_t DeviceHalHidl::addDeviceEffect( #if MAJOR_VERSION >= 6 status_t DeviceHalHidl::removeDeviceEffect( audio_port_handle_t device, sp effect) { + TIME_CHECK(); if (mDevice == 0) return NO_INIT; return processReturn("removeDeviceEffect", mDevice->removeDeviceEffect( static_cast(device), effect->effectId())); @@ -476,6 +514,7 @@ status_t DeviceHalHidl::removeDeviceEffect( #endif status_t DeviceHalHidl::setConnectedState(const struct audio_port_v7 *port, bool connected) { + TIME_CHECK(); if (mDevice == 0) return NO_INIT; #if MAJOR_VERSION == 7 && MINOR_VERSION == 1 if (supportsSetConnectedState7_1) { @@ -501,7 +540,21 @@ status_t DeviceHalHidl::setConnectedState(const struct audio_port_v7 *port, bool return processReturn("setConnectedState", mDevice->setConnectedState(hidlAddress, connected)); } +error::Result DeviceHalHidl::getHwAvSync() { + TIME_CHECK(); + if (mDevice == 0) return NO_INIT; + audio_hw_sync_t value; + Result result; + Return ret = mDevice->getHwAvSync([&value, &result](Result r, audio_hw_sync_t v) { + value = v; + result = r; + }); + RETURN_IF_ERROR(processReturn("getHwAvSync", ret, result)); + return value; +} + status_t DeviceHalHidl::dump(int fd, const Vector& args) { + TIME_CHECK(); if (mDevice == 0) return NO_INIT; native_handle_t* hidlHandle = native_handle_create(1, 0); hidlHandle->data[0] = fd; diff --git a/media/libaudiohal/impl/DeviceHalHidl.h b/media/libaudiohal/impl/DeviceHalHidl.h index fb0be5afbc11bfe5d3a147fcfbfade16f77446a4..f6519b6800be499105a599a9e0621c04bd9e5617 100644 --- a/media/libaudiohal/impl/DeviceHalHidl.h +++ b/media/libaudiohal/impl/DeviceHalHidl.h @@ -22,11 +22,11 @@ #include #include -#include "ConversionHelperHidl.h" +#include "CoreConversionHelperHidl.h" namespace android { -class DeviceHalHidl : public DeviceHalInterface, public ConversionHelperHidl +class DeviceHalHidl : public DeviceHalInterface, public CoreConversionHelperHidl { public: // Sets the value of 'devices' to a bitmask of 1 or more values of audio_devices_t. @@ -115,8 +115,27 @@ class DeviceHalHidl : public DeviceHalInterface, public ConversionHelperHidl status_t addDeviceEffect(audio_port_handle_t device, sp effect) override; status_t removeDeviceEffect(audio_port_handle_t device, sp effect) override; + status_t getMmapPolicyInfos( + media::audio::common::AudioMMapPolicyType policyType __unused, + std::vector *policyInfos __unused) override { + // TODO: Implement the HAL query when moving to AIDL HAL. + return INVALID_OPERATION; + } + + int32_t getAAudioMixerBurstCount() override { + // TODO: Implement the HAL query when moving to AIDL HAL. + return INVALID_OPERATION; + } + + int32_t getAAudioHardwareBurstMinUsec() override { + // TODO: Implement the HAL query when moving to AIDL HAL. + return INVALID_OPERATION; + } + status_t setConnectedState(const struct audio_port_v7 *port, bool connected) override; + error::Result getHwAvSync() override; + status_t dump(int fd, const Vector& args) override; private: diff --git a/media/libaudiohal/impl/DeviceHalLocal.cpp b/media/libaudiohal/impl/DeviceHalLocal.cpp deleted file mode 100644 index e473e41ad7b49414ac973603ee99f091b09231b6..0000000000000000000000000000000000000000 --- a/media/libaudiohal/impl/DeviceHalLocal.cpp +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright (C) 2016 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 "DeviceHalLocal" -//#define LOG_NDEBUG 0 - -#include -#include - -#include "DeviceHalLocal.h" -#include "StreamHalLocal.h" - -namespace android { - -DeviceHalLocal::DeviceHalLocal(audio_hw_device_t *dev) - : mDev(dev) { -} - -DeviceHalLocal::~DeviceHalLocal() { - int status = audio_hw_device_close(mDev); - ALOGW_IF(status, "Error closing audio hw device %p: %s", mDev, strerror(-status)); - mDev = 0; -} - -status_t DeviceHalLocal::getSupportedDevices(uint32_t *devices) { - if (mDev->get_supported_devices == NULL) return INVALID_OPERATION; - *devices = mDev->get_supported_devices(mDev); - return OK; -} - -status_t DeviceHalLocal::initCheck() { - return mDev->init_check(mDev); -} - -status_t DeviceHalLocal::setVoiceVolume(float volume) { - return mDev->set_voice_volume(mDev, volume); -} - -status_t DeviceHalLocal::setMasterVolume(float volume) { - if (mDev->set_master_volume == NULL) return INVALID_OPERATION; - return mDev->set_master_volume(mDev, volume); -} - -status_t DeviceHalLocal::getMasterVolume(float *volume) { - if (mDev->get_master_volume == NULL) return INVALID_OPERATION; - return mDev->get_master_volume(mDev, volume); -} - -status_t DeviceHalLocal::setMode(audio_mode_t mode) { - return mDev->set_mode(mDev, mode); -} - -status_t DeviceHalLocal::setMicMute(bool state) { - return mDev->set_mic_mute(mDev, state); -} - -status_t DeviceHalLocal::getMicMute(bool *state) { - return mDev->get_mic_mute(mDev, state); -} - -status_t DeviceHalLocal::setMasterMute(bool state) { - if (mDev->set_master_mute == NULL) return INVALID_OPERATION; - return mDev->set_master_mute(mDev, state); -} - -status_t DeviceHalLocal::getMasterMute(bool *state) { - if (mDev->get_master_mute == NULL) return INVALID_OPERATION; - return mDev->get_master_mute(mDev, state); -} - -status_t DeviceHalLocal::setParameters(const String8& kvPairs) { - return mDev->set_parameters(mDev, kvPairs.string()); -} - -status_t DeviceHalLocal::getParameters(const String8& keys, String8 *values) { - char *halValues = mDev->get_parameters(mDev, keys.string()); - if (halValues != NULL) { - values->setTo(halValues); - free(halValues); - } else { - values->clear(); - } - return OK; -} - -status_t DeviceHalLocal::getInputBufferSize( - const struct audio_config *config, size_t *size) { - *size = mDev->get_input_buffer_size(mDev, config); - return OK; -} - -status_t DeviceHalLocal::openOutputStream( - audio_io_handle_t handle, - audio_devices_t deviceType, - audio_output_flags_t flags, - struct audio_config *config, - const char *address, - sp *outStream) { - audio_stream_out_t *halStream; - ALOGV("open_output_stream handle: %d devices: %x flags: %#x" - "srate: %d format %#x channels %x address %s", - handle, deviceType, flags, - config->sample_rate, config->format, config->channel_mask, - address); - int openResut = mDev->open_output_stream( - mDev, handle, deviceType, flags, config, &halStream, address); - if (openResut == OK) { - *outStream = new StreamOutHalLocal(halStream, this); - } - ALOGV("open_output_stream status %d stream %p", openResut, halStream); - return openResut; -} - -status_t DeviceHalLocal::openInputStream( - audio_io_handle_t handle, - audio_devices_t devices, - struct audio_config *config, - audio_input_flags_t flags, - const char *address, - audio_source_t source, - audio_devices_t /*outputDevice*/, - const char */*outputDeviceAddress*/, - sp *inStream) { - audio_stream_in_t *halStream; - ALOGV("open_input_stream handle: %d devices: %x flags: %#x " - "srate: %d format %#x channels %x address %s source %d", - handle, devices, flags, - config->sample_rate, config->format, config->channel_mask, - address, source); - int openResult = mDev->open_input_stream( - mDev, handle, devices, config, &halStream, flags, address, source); - if (openResult == OK) { - *inStream = new StreamInHalLocal(halStream, this); - } - ALOGV("open_input_stream status %d stream %p", openResult, inStream); - return openResult; -} - -status_t DeviceHalLocal::supportsAudioPatches(bool *supportsPatches) { - *supportsPatches = version() >= AUDIO_DEVICE_API_VERSION_3_0; - return OK; -} - -status_t DeviceHalLocal::createAudioPatch( - unsigned int num_sources, - const struct audio_port_config *sources, - unsigned int num_sinks, - const struct audio_port_config *sinks, - audio_patch_handle_t *patch) { - if (version() >= AUDIO_DEVICE_API_VERSION_3_0) { - return mDev->create_audio_patch( - mDev, num_sources, sources, num_sinks, sinks, patch); - } else { - return INVALID_OPERATION; - } -} - -status_t DeviceHalLocal::releaseAudioPatch(audio_patch_handle_t patch) { - if (version() >= AUDIO_DEVICE_API_VERSION_3_0) { - return mDev->release_audio_patch(mDev, patch); - } else { - return INVALID_OPERATION; - } -} - -status_t DeviceHalLocal::getAudioPort(struct audio_port *port) { - return mDev->get_audio_port(mDev, port); -} - -status_t DeviceHalLocal::getAudioPort(struct audio_port_v7 *port) { -#if MAJOR_VERSION >= 7 - if (version() >= AUDIO_DEVICE_API_VERSION_3_2) { - // get_audio_port_v7 is mandatory if legacy HAL support this API version. - return mDev->get_audio_port_v7(mDev, port); - } -#endif - struct audio_port audioPort = {}; - audio_populate_audio_port(port, &audioPort); - status_t status = getAudioPort(&audioPort); - if (status == NO_ERROR) { - audio_populate_audio_port_v7(&audioPort, port); - } - return status; -} - -status_t DeviceHalLocal::setAudioPortConfig(const struct audio_port_config *config) { - if (version() >= AUDIO_DEVICE_API_VERSION_3_0) - return mDev->set_audio_port_config(mDev, config); - else - return INVALID_OPERATION; -} - -#if MAJOR_VERSION == 2 -status_t DeviceHalLocal::getMicrophones( - std::vector *microphones __unused) { - return INVALID_OPERATION; -} -#elif MAJOR_VERSION >= 4 -status_t DeviceHalLocal::getMicrophones(std::vector *microphones) { - if (mDev->get_microphones == NULL) return INVALID_OPERATION; - size_t actual_mics = AUDIO_MICROPHONE_MAX_COUNT; - audio_microphone_characteristic_t mic_array[AUDIO_MICROPHONE_MAX_COUNT]; - status_t status = mDev->get_microphones(mDev, &mic_array[0], &actual_mics); - for (size_t i = 0; i < actual_mics; i++) { - media::MicrophoneInfo microphoneInfo = media::MicrophoneInfo(mic_array[i]); - microphones->push_back(microphoneInfo); - } - return status; -} -#endif - -// Local HAL implementation does not support effects -status_t DeviceHalLocal::addDeviceEffect( - audio_port_handle_t device __unused, sp effect __unused) { - return INVALID_OPERATION; -} - -status_t DeviceHalLocal::removeDeviceEffect( - audio_port_handle_t device __unused, sp effect __unused) { - return INVALID_OPERATION; -} - -status_t DeviceHalLocal::setConnectedState(const struct audio_port_v7 *port, bool connected) { - AudioParameter param(String8(port->ext.device.address)); - const String8 key(connected ? - AudioParameter::keyDeviceConnect : AudioParameter::keyDeviceDisconnect); - param.addInt(key, port->ext.device.type); - return setParameters(param.toString()); -} - -status_t DeviceHalLocal::dump(int fd, const Vector& /* args */) { - return mDev->dump(mDev, fd); -} - -void DeviceHalLocal::closeOutputStream(struct audio_stream_out *stream_out) { - mDev->close_output_stream(mDev, stream_out); -} - -void DeviceHalLocal::closeInputStream(struct audio_stream_in *stream_in) { - mDev->close_input_stream(mDev, stream_in); -} - -} // namespace android diff --git a/media/libaudiohal/impl/DeviceHalLocal.h b/media/libaudiohal/impl/DeviceHalLocal.h deleted file mode 100644 index 79db930b4f0493cf04debb4c4484da1f49d8e5d8..0000000000000000000000000000000000000000 --- a/media/libaudiohal/impl/DeviceHalLocal.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2016 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_HARDWARE_DEVICE_HAL_LOCAL_H -#define ANDROID_HARDWARE_DEVICE_HAL_LOCAL_H - -#include -#include - -namespace android { - -class DeviceHalLocal : public DeviceHalInterface -{ - public: - // Sets the value of 'devices' to a bitmask of 1 or more values of audio_devices_t. - virtual status_t getSupportedDevices(uint32_t *devices); - - // Check to see if the audio hardware interface has been initialized. - virtual status_t initCheck(); - - // Set the audio volume of a voice call. Range is between 0.0 and 1.0. - virtual status_t setVoiceVolume(float volume); - - // Set the audio volume for all audio activities other than voice call. - virtual status_t setMasterVolume(float volume); - - // Get the current master volume value for the HAL. - virtual status_t getMasterVolume(float *volume); - - // Called when the audio mode changes. - virtual status_t setMode(audio_mode_t mode); - - // Muting control. - virtual status_t setMicMute(bool state); - virtual status_t getMicMute(bool *state); - virtual status_t setMasterMute(bool state); - virtual status_t getMasterMute(bool *state); - - // Set global audio parameters. - virtual status_t setParameters(const String8& kvPairs); - - // Get global audio parameters. - virtual status_t getParameters(const String8& keys, String8 *values); - - // Returns audio input buffer size according to parameters passed. - virtual status_t getInputBufferSize(const struct audio_config *config, - size_t *size); - - // Creates and opens the audio hardware output stream. The stream is closed - // by releasing all references to the returned object. - virtual status_t openOutputStream( - audio_io_handle_t handle, - audio_devices_t devices, - audio_output_flags_t flags, - struct audio_config *config, - const char *address, - sp *outStream); - - // Creates and opens the audio hardware input stream. The stream is closed - // by releasing all references to the returned object. - virtual status_t openInputStream( - audio_io_handle_t handle, - audio_devices_t devices, - struct audio_config *config, - audio_input_flags_t flags, - const char *address, - audio_source_t source, - audio_devices_t outputDevice, - const char *outputDeviceAddress, - sp *inStream); - - // Returns whether createAudioPatch and releaseAudioPatch operations are supported. - virtual status_t supportsAudioPatches(bool *supportsPatches); - - // Creates an audio patch between several source and sink ports. - virtual status_t createAudioPatch( - unsigned int num_sources, - const struct audio_port_config *sources, - unsigned int num_sinks, - const struct audio_port_config *sinks, - audio_patch_handle_t *patch); - - // Releases an audio patch. - virtual status_t releaseAudioPatch(audio_patch_handle_t patch); - - // Fills the list of supported attributes for a given audio port. - virtual status_t getAudioPort(struct audio_port *port); - - // Fills the list of supported attributes for a given audio port. - virtual status_t getAudioPort(struct audio_port_v7 *port); - - // Set audio port configuration. - virtual status_t setAudioPortConfig(const struct audio_port_config *config); - - // List microphones - virtual status_t getMicrophones(std::vector *microphones); - - status_t addDeviceEffect(audio_port_handle_t device, sp effect) override; - status_t removeDeviceEffect(audio_port_handle_t device, sp effect) override; - - status_t setConnectedState(const struct audio_port_v7 *port, bool connected) override; - - status_t dump(int fd, const Vector& args) override; - - void closeOutputStream(struct audio_stream_out *stream_out); - void closeInputStream(struct audio_stream_in *stream_in); - - uint32_t version() const { return mDev->common.version; } - - private: - audio_hw_device_t *mDev; - - friend class DevicesFactoryHalLocal; - - // Can not be constructed directly by clients. - explicit DeviceHalLocal(audio_hw_device_t *dev); - - // The destructor automatically closes the device. - virtual ~DeviceHalLocal(); -}; - -} // namespace android - -#endif // ANDROID_HARDWARE_DEVICE_HAL_LOCAL_H diff --git a/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp b/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp index f4757297a67c36ffdd9f6909ad2e8edb20bca100..4069a6bdbdfec3b72b605a269ddde65e33e54b36 100644 --- a/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp +++ b/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp @@ -26,7 +26,6 @@ #include #include -#include "ConversionHelperHidl.h" #include "DeviceHalHidl.h" #include "DevicesFactoryHalHidl.h" @@ -227,4 +226,10 @@ std::vector> return mDeviceFactories; } +// Main entry-point to the shared library. +extern "C" __attribute__((visibility("default"))) void* createIDevicesFactory() { + auto service = hardware::audio::CPP_VERSION::IDevicesFactory::getService(); + return service ? new DevicesFactoryHalHidl(service) : nullptr; +} + } // namespace android diff --git a/media/libaudiohal/impl/DevicesFactoryHalHidl.h b/media/libaudiohal/impl/DevicesFactoryHalHidl.h index fd8dbc493f473d20945d2fb69a920b3c6e83c2e2..ffd229decc904b952507b2d569444f0893761518 100644 --- a/media/libaudiohal/impl/DevicesFactoryHalHidl.h +++ b/media/libaudiohal/impl/DevicesFactoryHalHidl.h @@ -45,6 +45,8 @@ class DevicesFactoryHalHidl : public DevicesFactoryHalInterface status_t setCallbackOnce(sp callback) override; + float getHalVersion() const override { return MAJOR_VERSION + (float)MINOR_VERSION / 10; } + private: friend class ServiceNotificationListener; void addDeviceFactory(sp factory, bool needToNotify); diff --git a/media/libaudiohal/impl/DevicesFactoryHalHybrid.cpp b/media/libaudiohal/impl/DevicesFactoryHalHybrid.cpp deleted file mode 100644 index d684c271a39daf7a9b1cbc7177212ae6003efb41..0000000000000000000000000000000000000000 --- a/media/libaudiohal/impl/DevicesFactoryHalHybrid.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2017 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 "DevicesFactoryHalHybrid" -//#define LOG_NDEBUG 0 - -#include "DevicesFactoryHalHidl.h" -#include "DevicesFactoryHalHybrid.h" -#include "DevicesFactoryHalLocal.h" - -namespace android { - -DevicesFactoryHalHybrid::DevicesFactoryHalHybrid(sp hidlFactory) - : mLocalFactory(new DevicesFactoryHalLocal()), - mHidlFactory(new DevicesFactoryHalHidl(hidlFactory)) { -} - -status_t DevicesFactoryHalHybrid::openDevice(const char *name, sp *device) { - if (mHidlFactory != 0 && strcmp(AUDIO_HARDWARE_MODULE_ID_A2DP, name) != 0 && - strcmp(AUDIO_HARDWARE_MODULE_ID_HEARING_AID, name) != 0) { - return mHidlFactory->openDevice(name, device); - } - return mLocalFactory->openDevice(name, device); -} - -status_t DevicesFactoryHalHybrid::getHalPids(std::vector *pids) { - if (mHidlFactory != 0) { - return mHidlFactory->getHalPids(pids); - } - return INVALID_OPERATION; -} - -status_t DevicesFactoryHalHybrid::setCallbackOnce(sp callback) { - if (mHidlFactory) { - return mHidlFactory->setCallbackOnce(callback); - } - return INVALID_OPERATION; -} - -extern "C" __attribute__((visibility("default"))) void* createIDevicesFactory() { - auto service = hardware::audio::CPP_VERSION::IDevicesFactory::getService(); - return service ? new DevicesFactoryHalHybrid(service) : nullptr; -} - -} // namespace android diff --git a/media/libaudiohal/impl/DevicesFactoryHalHybrid.h b/media/libaudiohal/impl/DevicesFactoryHalHybrid.h deleted file mode 100644 index 6b2b845353092cd6c7280677000ef3edf2684a90..0000000000000000000000000000000000000000 --- a/media/libaudiohal/impl/DevicesFactoryHalHybrid.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2017 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_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H -#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H - -#include PATH(android/hardware/audio/FILE_VERSION/IDevicesFactory.h) -#include -#include -#include - -using ::android::hardware::audio::CPP_VERSION::IDevicesFactory; - -namespace android { - -class DevicesFactoryHalHybrid : public DevicesFactoryHalInterface -{ - public: - DevicesFactoryHalHybrid(sp hidlFactory); - - // Opens a device with the specified name. To close the device, it is - // necessary to release references to the returned object. - virtual status_t openDevice(const char *name, sp *device); - - status_t getHalPids(std::vector *pids) override; - - status_t setCallbackOnce(sp callback) override; - - private: - sp mLocalFactory; - sp mHidlFactory; -}; - -} // namespace android - -#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H diff --git a/media/libaudiohal/impl/DevicesFactoryHalLocal.cpp b/media/libaudiohal/impl/DevicesFactoryHalLocal.cpp deleted file mode 100644 index 13a9acd9e91c8d76f6efc924d8106e1529831923..0000000000000000000000000000000000000000 --- a/media/libaudiohal/impl/DevicesFactoryHalLocal.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2016 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 "DevicesFactoryHalLocal" -//#define LOG_NDEBUG 0 - -#include - -#include -#include - -#include "DeviceHalLocal.h" -#include "DevicesFactoryHalLocal.h" - -namespace android { - -static status_t load_audio_interface(const char *if_name, audio_hw_device_t **dev) -{ - const hw_module_t *mod; - int rc; - - rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod); - if (rc) { - ALOGE("%s couldn't load audio hw module %s.%s (%s)", __func__, - AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc)); - goto out; - } - rc = audio_hw_device_open(mod, dev); - if (rc) { - ALOGE("%s couldn't open audio hw device in %s.%s (%s)", __func__, - AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc)); - goto out; - } - if ((*dev)->common.version < AUDIO_DEVICE_API_VERSION_MIN) { - ALOGE("%s wrong audio hw device version %04x", __func__, (*dev)->common.version); - rc = BAD_VALUE; - audio_hw_device_close(*dev); - goto out; - } - return OK; - -out: - *dev = NULL; - return rc; -} - -status_t DevicesFactoryHalLocal::openDevice(const char *name, sp *device) { - audio_hw_device_t *dev; - status_t rc = load_audio_interface(name, &dev); - if (rc == OK) { - *device = new DeviceHalLocal(dev); - } - return rc; -} - -} // namespace android diff --git a/media/libaudiohal/impl/DevicesFactoryHalLocal.h b/media/libaudiohal/impl/DevicesFactoryHalLocal.h deleted file mode 100644 index eacf109ecc22107c356129bce0110325cf2f4467..0000000000000000000000000000000000000000 --- a/media/libaudiohal/impl/DevicesFactoryHalLocal.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2016 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_HARDWARE_DEVICES_FACTORY_HAL_LOCAL_H -#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_LOCAL_H - -#include -#include -#include - -#include "DeviceHalLocal.h" - -namespace android { - -class DevicesFactoryHalLocal : public DevicesFactoryHalInterface -{ - public: - // Opens a device with the specified name. To close the device, it is - // necessary to release references to the returned object. - virtual status_t openDevice(const char *name, sp *device); - - status_t getHalPids(std::vector *pids __unused) override { - return INVALID_OPERATION; - } - - status_t setCallbackOnce(sp callback __unused) override { - return INVALID_OPERATION; - } - - private: - friend class DevicesFactoryHalHybrid; - - // Can not be constructed directly by clients. - DevicesFactoryHalLocal() {} - - virtual ~DevicesFactoryHalLocal() {} -}; - -} // namespace android - -#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_LOCAL_H diff --git a/media/libaudiohal/impl/EffectBufferHalHidl.cpp b/media/libaudiohal/impl/EffectBufferHalHidl.cpp index 65297afd50ca1704f592196eafda9897d9d7136c..9d5f72e0e10d11d5f84d80254f2e4f061e79cfde 100644 --- a/media/libaudiohal/impl/EffectBufferHalHidl.cpp +++ b/media/libaudiohal/impl/EffectBufferHalHidl.cpp @@ -23,7 +23,6 @@ #include #include -#include "ConversionHelperHidl.h" #include "EffectBufferHalHidl.h" using ::android::hardware::Return; diff --git a/media/libaudiohal/impl/EffectConversionHelperHidl.cpp b/media/libaudiohal/impl/EffectConversionHelperHidl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9e4f79c9c448c965a08acfc36767a46e34bc3f3d --- /dev/null +++ b/media/libaudiohal/impl/EffectConversionHelperHidl.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 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 "HalHidl" +#include + +#include "EffectConversionHelperHidl.h" + +namespace android { + +EffectConversionHelperHidl::EffectConversionHelperHidl(std::string_view className) + : ConversionHelperHidl(className, analyzeResult) { +} + +// static +status_t EffectConversionHelperHidl::analyzeResult(const EffectResult& result) { + switch (result) { + case EffectResult::OK: return OK; + case EffectResult::INVALID_ARGUMENTS: return BAD_VALUE; + case EffectResult::INVALID_STATE: return NOT_ENOUGH_DATA; + case EffectResult::NOT_INITIALIZED: return NO_INIT; + case EffectResult::NOT_SUPPORTED: return INVALID_OPERATION; + case EffectResult::RESULT_TOO_BIG: return NO_MEMORY; + } + return NO_INIT; +} + +} // namespace android diff --git a/media/libaudiohal/impl/EffectConversionHelperHidl.h b/media/libaudiohal/impl/EffectConversionHelperHidl.h new file mode 100644 index 0000000000000000000000000000000000000000..4371d1204f399ec4ab76621d03443da408888749 --- /dev/null +++ b/media/libaudiohal/impl/EffectConversionHelperHidl.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 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_HARDWARE_EFFECT_CONVERSION_HELPER_HIDL_H +#define ANDROID_HARDWARE_EFFECT_CONVERSION_HELPER_HIDL_H + +#include "ConversionHelperHidl.h" + +#include PATH(android/hardware/audio/effect/FILE_VERSION/types.h) + +using EffectResult = ::android::hardware::audio::effect::CPP_VERSION::Result; + +namespace android { + +class EffectConversionHelperHidl : public ConversionHelperHidl { + protected: + static status_t analyzeResult(const EffectResult& result); + + EffectConversionHelperHidl(std::string_view className); +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_EFFECT_CONVERSION_HELPER_HIDL_H diff --git a/media/libaudiohal/impl/EffectHalHidl.cpp b/media/libaudiohal/impl/EffectHalHidl.cpp index 1bb1e5f52df893b22fb4b054c5cf4d7257dbf58d..fdfe225e90aaa9c87252ce124aaeac948b8ffe54 100644 --- a/media/libaudiohal/impl/EffectHalHidl.cpp +++ b/media/libaudiohal/impl/EffectHalHidl.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -40,8 +41,12 @@ namespace effect { using namespace ::android::hardware::audio::common::CPP_VERSION; using namespace ::android::hardware::audio::effect::CPP_VERSION; +#define TIME_CHECK() auto timeCheck = \ + mediautils::makeTimeCheckStatsForClassMethod(getClassName(), __func__) + EffectHalHidl::EffectHalHidl(const sp& effect, uint64_t effectId) - : mEffect(effect), mEffectId(effectId), mBuffersChanged(true), mEfGroup(nullptr) { + : EffectConversionHelperHidl("EffectHalHidl"), + mEffect(effect), mEffectId(effectId), mBuffersChanged(true), mEfGroup(nullptr) { effect_descriptor_t halDescriptor{}; if (EffectHalHidl::getDescriptor(&halDescriptor) == NO_ERROR) { mIsInput = (halDescriptor.flags & EFFECT_FLAG_TYPE_PRE_PROC) == EFFECT_FLAG_TYPE_PRE_PROC; @@ -59,20 +64,9 @@ EffectHalHidl::~EffectHalHidl() { } } -// static -status_t EffectHalHidl::analyzeResult(const Result& result) { - switch (result) { - case Result::OK: return OK; - case Result::INVALID_ARGUMENTS: return BAD_VALUE; - case Result::INVALID_STATE: return NOT_ENOUGH_DATA; - case Result::NOT_INITIALIZED: return NO_INIT; - case Result::NOT_SUPPORTED: return INVALID_OPERATION; - case Result::RESULT_TOO_BIG: return NO_MEMORY; - default: return NO_INIT; - } -} - status_t EffectHalHidl::setInBuffer(const sp& buffer) { + TIME_CHECK(); + if (!mBuffersChanged) { if (buffer.get() == nullptr || mInBuffer.get() == nullptr) { mBuffersChanged = buffer.get() != mInBuffer.get(); @@ -85,6 +79,8 @@ status_t EffectHalHidl::setInBuffer(const sp& buffer) } status_t EffectHalHidl::setOutBuffer(const sp& buffer) { + TIME_CHECK(); + if (!mBuffersChanged) { if (buffer.get() == nullptr || mOutBuffer.get() == nullptr) { mBuffersChanged = buffer.get() != mOutBuffer.get(); @@ -97,10 +93,14 @@ status_t EffectHalHidl::setOutBuffer(const sp& buffer) } status_t EffectHalHidl::process() { + TIME_CHECK(); + return processImpl(static_cast(MessageQueueFlagBits::REQUEST_PROCESS)); } status_t EffectHalHidl::processReverse() { + TIME_CHECK(); + return processImpl(static_cast(MessageQueueFlagBits::REQUEST_PROCESS_REVERSE)); } @@ -183,6 +183,8 @@ status_t EffectHalHidl::setProcessBuffers() { status_t EffectHalHidl::command(uint32_t cmdCode, uint32_t cmdSize, void *pCmdData, uint32_t *replySize, void *pReplyData) { + TIME_CHECK(); + if (mEffect == 0) return NO_INIT; // Special cases. @@ -214,6 +216,8 @@ status_t EffectHalHidl::command(uint32_t cmdCode, uint32_t cmdSize, void *pCmdDa } status_t EffectHalHidl::getDescriptor(effect_descriptor_t *pDescriptor) { + TIME_CHECK(); + if (mEffect == 0) return NO_INIT; Result retval = Result::NOT_INITIALIZED; Return ret = mEffect->getDescriptor( @@ -227,12 +231,16 @@ status_t EffectHalHidl::getDescriptor(effect_descriptor_t *pDescriptor) { } status_t EffectHalHidl::close() { + TIME_CHECK(); + if (mEffect == 0) return NO_INIT; Return ret = mEffect->close(); return ret.isOk() ? analyzeResult(ret) : FAILED_TRANSACTION; } status_t EffectHalHidl::dump(int fd) { + TIME_CHECK(); + if (mEffect == 0) return NO_INIT; native_handle_t* hidlHandle = native_handle_create(1, 0); hidlHandle->data[0] = fd; diff --git a/media/libaudiohal/impl/EffectHalHidl.h b/media/libaudiohal/impl/EffectHalHidl.h index 07745dbdb0ef066bff5eeed368eb05e6fd7b2e4d..e1397688e440b5c9b8e26242d91b87af514d6c50 100644 --- a/media/libaudiohal/impl/EffectHalHidl.h +++ b/media/libaudiohal/impl/EffectHalHidl.h @@ -23,6 +23,8 @@ #include #include +#include "EffectConversionHelperHidl.h" + using ::android::hardware::EventFlag; using ::android::hardware::MessageQueue; @@ -31,7 +33,7 @@ namespace effect { using namespace ::android::hardware::audio::effect::CPP_VERSION; -class EffectHalHidl : public EffectHalInterface +class EffectHalHidl : public EffectHalInterface, public EffectConversionHelperHidl { public: // Set the input buffer. @@ -77,8 +79,6 @@ class EffectHalHidl : public EffectHalInterface EventFlag* mEfGroup; bool mIsInput = false; - static status_t analyzeResult(const Result& result); - // Can not be constructed directly by clients. EffectHalHidl(const sp& effect, uint64_t effectId); diff --git a/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp b/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp index 90954b20ec4c13ba58fc8dc3aecd9c663b9045d1..d7217fc645a676a2dc92c2ede5d583fea465ba46 100644 --- a/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp +++ b/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp @@ -21,8 +21,9 @@ #include #include +#include -#include "ConversionHelperHidl.h" +#include "EffectConversionHelperHidl.h" #include "EffectBufferHalHidl.h" #include "EffectHalHidl.h" #include "EffectsFactoryHalHidl.h" @@ -38,7 +39,7 @@ using namespace ::android::hardware::audio::common::CPP_VERSION; using namespace ::android::hardware::audio::effect::CPP_VERSION; EffectsFactoryHalHidl::EffectsFactoryHalHidl(sp effectsFactory) - : ConversionHelperHidl("EffectsFactory") { + : EffectConversionHelperHidl("EffectsFactory") { ALOG_ASSERT(effectsFactory != nullptr, "Provided IEffectsFactory service is NULL"); mEffectsFactory = effectsFactory; } @@ -205,7 +206,10 @@ status_t EffectsFactoryHalHidl::mirrorBuffer(void* external, size_t size, } // namespace effect -extern "C" __attribute__((visibility("default"))) void* createIEffectsFactory() { +// When a shared library is built from a static library, even explicit +// exports from a static library are optimized out unless actually used by +// the shared library. See EffectsFactoryHalHidlEntry.cpp. +extern "C" void* createIEffectsFactoryImpl() { auto service = hardware::audio::effect::CPP_VERSION::IEffectsFactory::getService(); return service ? new effect::EffectsFactoryHalHidl(service) : nullptr; } diff --git a/media/libaudiohal/impl/EffectsFactoryHalHidl.h b/media/libaudiohal/impl/EffectsFactoryHalHidl.h index 74911335a638593044c8fef4ed3ab9cf399cb4ef..e1882e170dfd3806a01e4138015bf01ccb6deaa5 100644 --- a/media/libaudiohal/impl/EffectsFactoryHalHidl.h +++ b/media/libaudiohal/impl/EffectsFactoryHalHidl.h @@ -20,16 +20,15 @@ #include PATH(android/hardware/audio/effect/FILE_VERSION/IEffectsFactory.h) #include -#include "ConversionHelperHidl.h" +#include "EffectConversionHelperHidl.h" namespace android { namespace effect { using ::android::hardware::hidl_vec; -using ::android::ConversionHelperHidl; using namespace ::android::hardware::audio::effect::CPP_VERSION; -class EffectsFactoryHalHidl : public EffectsFactoryHalInterface, public ConversionHelperHidl +class EffectsFactoryHalHidl : public EffectsFactoryHalInterface, public EffectConversionHelperHidl { public: EffectsFactoryHalHidl(sp effectsFactory); diff --git a/media/libaudioclient/aidl/android/media/AudioPortSessionExt.aidl b/media/libaudiohal/impl/EffectsFactoryHalHidlEntry.cpp similarity index 71% rename from media/libaudioclient/aidl/android/media/AudioPortSessionExt.aidl rename to media/libaudiohal/impl/EffectsFactoryHalHidlEntry.cpp index dbca168988ffe7c9c4a578678593fb140f5c862e..2c6f2c6d55094269ed78052f487a3e5b9706c24b 100644 --- a/media/libaudioclient/aidl/android/media/AudioPortSessionExt.aidl +++ b/media/libaudiohal/impl/EffectsFactoryHalHidlEntry.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2022 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. @@ -14,12 +14,8 @@ * limitations under the License. */ -package android.media; +extern "C" void* createIEffectsFactoryImpl(); -/** - * {@hide} - */ -parcelable AudioPortSessionExt { - /** Audio session. Interpreted as audio_session_t. */ - int session; +extern "C" __attribute__((visibility("default"))) void* createIEffectsFactory() { + return createIEffectsFactoryImpl(); } diff --git a/media/libaudiohal/impl/StreamHalHidl.cpp b/media/libaudiohal/impl/StreamHalHidl.cpp index 6916ca19171830add38cff1cbb0c26dcc45564ec..021ec51e504814574b06dd3fca9357c33634f85e 100644 --- a/media/libaudiohal/impl/StreamHalHidl.cpp +++ b/media/libaudiohal/impl/StreamHalHidl.cpp @@ -20,7 +20,9 @@ #include #include #include +#include #include +#include #include #include PATH(android/hardware/audio/CORE_TYPES_FILE_VERSION/IStreamOutCallback.h) @@ -44,8 +46,11 @@ using ReadCommand = ::android::hardware::audio::CORE_TYPES_CPP_VERSION::IStreamI using namespace ::android::hardware::audio::common::COMMON_TYPES_CPP_VERSION; using namespace ::android::hardware::audio::CORE_TYPES_CPP_VERSION; -StreamHalHidl::StreamHalHidl(IStream *stream) - : ConversionHelperHidl("Stream"), +#define TIME_CHECK() auto TimeCheck = \ + mediautils::makeTimeCheckStatsForClassMethod(getClassName(), __func__) + +StreamHalHidl::StreamHalHidl(std::string_view className, IStream *stream) + : CoreConversionHelperHidl(className), mStream(stream), mHalThreadPriority(HAL_THREAD_PRIORITY_DEFAULT), mCachedBufferSize(0){ @@ -66,6 +71,7 @@ StreamHalHidl::~StreamHalHidl() { } status_t StreamHalHidl::getBufferSize(size_t *size) { + TIME_CHECK(); if (!mStream) return NO_INIT; status_t status = processReturn("getBufferSize", mStream->getBufferSize(), size); if (status == OK) { @@ -75,6 +81,7 @@ status_t StreamHalHidl::getBufferSize(size_t *size) { } status_t StreamHalHidl::getAudioProperties(audio_config_base_t *configBase) { + TIME_CHECK(); *configBase = AUDIO_CONFIG_BASE_INITIALIZER; if (!mStream) return NO_INIT; #if MAJOR_VERSION <= 6 @@ -104,6 +111,7 @@ status_t StreamHalHidl::getAudioProperties(audio_config_base_t *configBase) { } status_t StreamHalHidl::setParameters(const String8& kvPairs) { + TIME_CHECK(); if (!mStream) return NO_INIT; hidl_vec hidlParams; status_t status = parametersFromHal(kvPairs, &hidlParams); @@ -113,6 +121,7 @@ status_t StreamHalHidl::setParameters(const String8& kvPairs) { } status_t StreamHalHidl::getParameters(const String8& keys, String8 *values) { + TIME_CHECK(); values->clear(); if (!mStream) return NO_INIT; hidl_vec hidlKeys; @@ -133,21 +142,25 @@ status_t StreamHalHidl::getParameters(const String8& keys, String8 *values) { } status_t StreamHalHidl::addEffect(sp effect) { + TIME_CHECK(); if (!mStream) return NO_INIT; return processReturn("addEffect", mStream->addEffect(effect->effectId())); } status_t StreamHalHidl::removeEffect(sp effect) { + TIME_CHECK(); if (!mStream) return NO_INIT; return processReturn("removeEffect", mStream->removeEffect(effect->effectId())); } status_t StreamHalHidl::standby() { + TIME_CHECK(); if (!mStream) return NO_INIT; return processReturn("standby", mStream->standby()); } status_t StreamHalHidl::dump(int fd, const Vector& args) { + TIME_CHECK(); if (!mStream) return NO_INIT; native_handle_t* hidlHandle = native_handle_create(1, 0); hidlHandle->data[0] = fd; @@ -172,17 +185,20 @@ status_t StreamHalHidl::dump(int fd, const Vector& args) { } status_t StreamHalHidl::start() { + TIME_CHECK(); if (!mStream) return NO_INIT; return processReturn("start", mStream->start()); } status_t StreamHalHidl::stop() { + TIME_CHECK(); if (!mStream) return NO_INIT; return processReturn("stop", mStream->stop()); } status_t StreamHalHidl::createMmapBuffer(int32_t minSizeFrames, struct audio_mmap_buffer_info *info) { + TIME_CHECK(); Result retval; Return ret = mStream->createMmapBuffer( minSizeFrames, @@ -215,6 +231,7 @@ status_t StreamHalHidl::createMmapBuffer(int32_t minSizeFrames, } status_t StreamHalHidl::getMmapPosition(struct audio_mmap_position *position) { + TIME_CHECK(); Result retval; Return ret = mStream->getMmapPosition( [&](Result r, const MmapPosition& hidlPosition) { @@ -243,7 +260,7 @@ status_t StreamHalHidl::getCachedBufferSize(size_t *size) { status_t StreamHalHidl::getHalPid(pid_t *pid) { using ::android::hidl::base::V1_0::DebugInfo; using ::android::hidl::manager::V1_0::IServiceManager; - + TIME_CHECK(); DebugInfo debugInfo; auto ret = mStream->getDebugInfo([&] (const auto &info) { debugInfo = info; @@ -271,6 +288,34 @@ bool StreamHalHidl::requestHalThreadPriority(pid_t threadPid, pid_t threadId) { return err == 0; } +status_t StreamHalHidl::legacyCreateAudioPatch(const struct audio_port_config& port, + std::optional source, + audio_devices_t type) { + TIME_CHECK(); + LOG_ALWAYS_FATAL_IF(port.type != AUDIO_PORT_TYPE_DEVICE, "port type must be device"); + unique_malloced_ptr address; + if (strcmp(port.ext.device.address, "") != 0) { + // FIXME: we only support address on first sink with HAL version < 3.0 + address.reset( + audio_device_address_to_parameter(port.ext.device.type, port.ext.device.address)); + } else { + address.reset((char*)calloc(1, 1)); + } + AudioParameter param = AudioParameter(String8(address.get())); + param.addInt(String8(AudioParameter::keyRouting), (int)type); + if (source.has_value()) { + param.addInt(String8(AudioParameter::keyInputSource), (int)source.value()); + } + return setParameters(param.toString()); +} + +status_t StreamHalHidl::legacyReleaseAudioPatch() { + TIME_CHECK(); + AudioParameter param; + param.addInt(String8(AudioParameter::keyRouting), 0); + return setParameters(param.toString()); +} + namespace { /* Notes on callback ownership. @@ -325,7 +370,8 @@ struct StreamOutCallback : public IStreamOutCallback { StreamOutHalHidl::StreamOutHalHidl( const sp<::android::hardware::audio::CPP_VERSION::IStreamOut>& stream) - : StreamHalHidl(stream.get()), mStream(stream), mWriterClient(0), mEfGroup(nullptr) { + : StreamHalHidl("StreamOutHalHidl", stream.get()) + , mStream(stream), mWriterClient(0), mEfGroup(nullptr) { } StreamOutHalHidl::~StreamOutHalHidl() { @@ -349,11 +395,13 @@ StreamOutHalHidl::~StreamOutHalHidl() { } status_t StreamOutHalHidl::getFrameSize(size_t *size) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; return processReturn("getFrameSize", mStream->getFrameSize(), size); } status_t StreamOutHalHidl::getLatency(uint32_t *latency) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; if (mWriterClient == gettid() && mCommandMQ) { return callWriterThread( @@ -367,12 +415,14 @@ status_t StreamOutHalHidl::getLatency(uint32_t *latency) { } status_t StreamOutHalHidl::setVolume(float left, float right) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; return processReturn("setVolume", mStream->setVolume(left, right)); } #if MAJOR_VERSION == 2 status_t StreamOutHalHidl::selectPresentation(int presentationId, int programId) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; std::vector parameters; String8 halParameters; @@ -383,6 +433,7 @@ status_t StreamOutHalHidl::selectPresentation(int presentationId, int programId) } #elif MAJOR_VERSION >= 4 status_t StreamOutHalHidl::selectPresentation(int presentationId, int programId) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; return processReturn("selectPresentation", mStream->selectPresentation(presentationId, programId)); @@ -390,6 +441,7 @@ status_t StreamOutHalHidl::selectPresentation(int presentationId, int programId) #endif status_t StreamOutHalHidl::write(const void *buffer, size_t bytes, size_t *written) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; *written = 0; @@ -535,6 +587,7 @@ status_t StreamOutHalHidl::prepareForWriting(size_t bufferSize) { } status_t StreamOutHalHidl::getRenderPosition(uint32_t *dspFrames) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; Result retval; Return ret = mStream->getRenderPosition( @@ -548,6 +601,7 @@ status_t StreamOutHalHidl::getRenderPosition(uint32_t *dspFrames) { } status_t StreamOutHalHidl::getNextWriteTimestamp(int64_t *timestamp) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; Result retval; Return ret = mStream->getNextWriteTimestamp( @@ -561,6 +615,7 @@ status_t StreamOutHalHidl::getNextWriteTimestamp(int64_t *timestamp) { } status_t StreamOutHalHidl::setCallback(wp callback) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; status_t status = processReturn( "setCallback", mStream->setCallback(new StreamOutCallback(this))); @@ -571,6 +626,7 @@ status_t StreamOutHalHidl::setCallback(wp callbac } status_t StreamOutHalHidl::supportsPauseAndResume(bool *supportsPause, bool *supportsResume) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; Return ret = mStream->supportsPauseAndResume( [&](bool p, bool r) { @@ -581,32 +637,38 @@ status_t StreamOutHalHidl::supportsPauseAndResume(bool *supportsPause, bool *sup } status_t StreamOutHalHidl::pause() { + TIME_CHECK(); if (mStream == 0) return NO_INIT; return processReturn("pause", mStream->pause()); } status_t StreamOutHalHidl::resume() { + TIME_CHECK(); if (mStream == 0) return NO_INIT; return processReturn("pause", mStream->resume()); } status_t StreamOutHalHidl::supportsDrain(bool *supportsDrain) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; return processReturn("supportsDrain", mStream->supportsDrain(), supportsDrain); } status_t StreamOutHalHidl::drain(bool earlyNotify) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; return processReturn( "drain", mStream->drain(earlyNotify ? AudioDrain::EARLY_NOTIFY : AudioDrain::ALL)); } status_t StreamOutHalHidl::flush() { + TIME_CHECK(); if (mStream == 0) return NO_INIT; return processReturn("pause", mStream->flush()); } status_t StreamOutHalHidl::getPresentationPosition(uint64_t *frames, struct timespec *timestamp) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; if (mWriterClient == gettid() && mCommandMQ) { return callWriterThread( @@ -640,6 +702,7 @@ status_t StreamOutHalHidl::updateSourceMetadata( #elif MAJOR_VERSION >= 4 status_t StreamOutHalHidl::updateSourceMetadata( const StreamOutHalInterface::SourceMetadata& sourceMetadata) { + TIME_CHECK(); #if MAJOR_VERSION == 4 ::android::hardware::audio::CORE_TYPES_CPP_VERSION::SourceMetadata hidlMetadata; #else @@ -690,6 +753,7 @@ status_t StreamOutHalHidl::setEventCallback( #else status_t StreamOutHalHidl::getDualMonoMode(audio_dual_mono_mode_t* mode) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; Result retval; Return ret = mStream->getDualMonoMode( @@ -703,12 +767,14 @@ status_t StreamOutHalHidl::getDualMonoMode(audio_dual_mono_mode_t* mode) { } status_t StreamOutHalHidl::setDualMonoMode(audio_dual_mono_mode_t mode) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; return processReturn( "setDualMonoMode", mStream->setDualMonoMode(static_cast(mode))); } status_t StreamOutHalHidl::getAudioDescriptionMixLevel(float* leveldB) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; Result retval; Return ret = mStream->getAudioDescriptionMixLevel( @@ -722,12 +788,14 @@ status_t StreamOutHalHidl::getAudioDescriptionMixLevel(float* leveldB) { } status_t StreamOutHalHidl::setAudioDescriptionMixLevel(float leveldB) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; return processReturn( "setAudioDescriptionMixLevel", mStream->setAudioDescriptionMixLevel(leveldB)); } status_t StreamOutHalHidl::getPlaybackRateParameters(audio_playback_rate_t* playbackRate) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; Result retval; Return ret = mStream->getPlaybackRateParameters( @@ -748,6 +816,7 @@ status_t StreamOutHalHidl::getPlaybackRateParameters(audio_playback_rate_t* play } status_t StreamOutHalHidl::setPlaybackRateParameters(const audio_playback_rate_t& playbackRate) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; return processReturn( "setPlaybackRateParameters", mStream->setPlaybackRateParameters( @@ -782,6 +851,7 @@ struct StreamOutEventCallback : public IStreamOutEventCallback { status_t StreamOutHalHidl::setEventCallback( const sp& callback) { + TIME_CHECK(); if (mStream == nullptr) return NO_INIT; mEventCallback = callback; status_t status = processReturn( @@ -796,12 +866,14 @@ status_t StreamOutHalHidl::setEventCallback( using hardware::audio::V7_1::LatencyMode; status_t StreamOutHalHidl::setLatencyMode(audio_latency_mode_t mode) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; return processReturn( "setLatencyMode", mStream->setLatencyMode(static_cast(mode))); }; status_t StreamOutHalHidl::getRecommendedLatencyModes(std::vector *modes) { + TIME_CHECK(); if (!mStream) return NO_INIT; Result retval; Return ret = mStream->getRecommendedLatencyModes( @@ -842,6 +914,7 @@ struct StreamOutLatencyModeCallback : public IStreamOutLatencyModeCallback { status_t StreamOutHalHidl::setLatencyModeCallback( const sp& callback) { + TIME_CHECK(); if (mStream == nullptr) return NO_INIT; mLatencyModeCallback = callback; @@ -905,10 +978,16 @@ void StreamOutHalHidl::onRecommendedLatencyModeChanged( callback->onRecommendedLatencyModeChanged(modes); } +status_t StreamOutHalHidl::exit() { + // FIXME this is using hard-coded strings but in the future, this functionality will be + // converted to use audio HAL extensions required to support tunneling + return setParameters(String8("exiting=1")); +} StreamInHalHidl::StreamInHalHidl( const sp<::android::hardware::audio::CORE_TYPES_CPP_VERSION::IStreamIn>& stream) - : StreamHalHidl(stream.get()), mStream(stream), mReaderClient(0), mEfGroup(nullptr) { + : StreamHalHidl("StreamInHalHidl", stream.get()) + , mStream(stream), mReaderClient(0), mEfGroup(nullptr) { } StreamInHalHidl::~StreamInHalHidl() { @@ -921,16 +1000,19 @@ StreamInHalHidl::~StreamInHalHidl() { } status_t StreamInHalHidl::getFrameSize(size_t *size) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; return processReturn("getFrameSize", mStream->getFrameSize(), size); } status_t StreamInHalHidl::setGain(float gain) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; return processReturn("setGain", mStream->setGain(gain)); } status_t StreamInHalHidl::read(void *buffer, size_t bytes, size_t *read) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; *read = 0; @@ -1058,11 +1140,13 @@ status_t StreamInHalHidl::prepareForReading(size_t bufferSize) { } status_t StreamInHalHidl::getInputFramesLost(uint32_t *framesLost) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; return processReturn("getInputFramesLost", mStream->getInputFramesLost(), framesLost); } status_t StreamInHalHidl::getCapturePosition(int64_t *frames, int64_t *time) { + TIME_CHECK(); if (mStream == 0) return NO_INIT; if (mReaderClient == gettid() && mCommandMQ) { ReadParameters params; @@ -1102,6 +1186,7 @@ status_t StreamInHalHidl::updateSinkMetadata( #elif MAJOR_VERSION >= 4 status_t StreamInHalHidl::getActiveMicrophones( std::vector *microphonesInfo) { + TIME_CHECK(); if (!mStream) return NO_INIT; Result retval; Return ret = mStream->getActiveMicrophones( @@ -1120,6 +1205,7 @@ status_t StreamInHalHidl::getActiveMicrophones( status_t StreamInHalHidl::updateSinkMetadata(const StreamInHalInterface::SinkMetadata& sinkMetadata) { + TIME_CHECK(); #if MAJOR_VERSION == 4 ::android::hardware::audio::CORE_TYPES_CPP_VERSION::SinkMetadata hidlMetadata; #else @@ -1147,12 +1233,14 @@ status_t StreamInHalHidl::setPreferredMicrophoneFieldDimension(float zoom __unus } #else status_t StreamInHalHidl::setPreferredMicrophoneDirection(audio_microphone_direction_t direction) { + TIME_CHECK(); if (!mStream) return NO_INIT; return processReturn("setPreferredMicrophoneDirection", mStream->setMicrophoneDirection(static_cast(direction))); } status_t StreamInHalHidl::setPreferredMicrophoneFieldDimension(float zoom) { + TIME_CHECK(); if (!mStream) return NO_INIT; return processReturn("setPreferredMicrophoneFieldDimension", mStream->setMicrophoneFieldDimension(zoom)); diff --git a/media/libaudiohal/impl/StreamHalHidl.h b/media/libaudiohal/impl/StreamHalHidl.h index 44bf60abbeccfc024476eb420981a3c3cfd20a44..54fbefef258cdd36ce045bc569a3b674bf7fb0c8 100644 --- a/media/libaudiohal/impl/StreamHalHidl.h +++ b/media/libaudiohal/impl/StreamHalHidl.h @@ -28,7 +28,7 @@ #include #include -#include "ConversionHelperHidl.h" +#include "CoreConversionHelperHidl.h" #include "StreamPowerLog.h" using ::android::hardware::audio::CORE_TYPES_CPP_VERSION::IStream; @@ -45,7 +45,7 @@ namespace android { class DeviceHalHidl; -class StreamHalHidl : public virtual StreamHalInterface, public ConversionHelperHidl +class StreamHalHidl : public virtual StreamHalInterface, public CoreConversionHelperHidl { public: // Return size of input/output buffer in bytes for this stream - eg. 4800. @@ -89,9 +89,15 @@ class StreamHalHidl : public virtual StreamHalInterface, public ConversionHelper // (must match the priority of the audioflinger's thread that calls 'read' / 'write') virtual status_t setHalThreadPriority(int priority); + status_t legacyCreateAudioPatch(const struct audio_port_config& port, + std::optional source, + audio_devices_t type) override; + + status_t legacyReleaseAudioPatch() override; + protected: // Subclasses can not be constructed directly by clients. - explicit StreamHalHidl(IStream *stream); + StreamHalHidl(std::string_view className, IStream *stream); ~StreamHalHidl() override; @@ -197,6 +203,8 @@ class StreamOutHalHidl : public StreamOutHalInterface, public StreamHalHidl { void onRecommendedLatencyModeChanged(const std::vector& modes); + status_t exit() override; + private: friend class DeviceHalHidl; typedef MessageQueue CommandMQ; diff --git a/media/libaudiohal/impl/StreamHalLocal.cpp b/media/libaudiohal/impl/StreamHalLocal.cpp deleted file mode 100644 index 477f5104ea223d67753637099e0c804c7c0950f9..0000000000000000000000000000000000000000 --- a/media/libaudiohal/impl/StreamHalLocal.cpp +++ /dev/null @@ -1,520 +0,0 @@ -/* - * Copyright (C) 2016 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 "StreamHalLocal" -//#define LOG_NDEBUG 0 - -#include -#include -#include -#include - -#include "DeviceHalLocal.h" -#include "ParameterUtils.h" -#include "StreamHalLocal.h" - -namespace android { - -StreamHalLocal::StreamHalLocal(audio_stream_t *stream, sp device) - : mDevice(device), - mStream(stream) { - // Instrument audio signal power logging. - // Note: This assumes channel mask, format, and sample rate do not change after creation. - if (mStream != nullptr /* && mStreamPowerLog.isUserDebugOrEngBuild() */) { - mStreamPowerLog.init(mStream->get_sample_rate(mStream), - mStream->get_channels(mStream), - mStream->get_format(mStream)); - } -} - -StreamHalLocal::~StreamHalLocal() { - mStream = 0; - mDevice.clear(); -} - -status_t StreamHalLocal::getBufferSize(size_t *size) { - *size = mStream->get_buffer_size(mStream); - return OK; -} - -status_t StreamHalLocal::getAudioProperties(audio_config_base_t *configBase) { - configBase->sample_rate = mStream->get_sample_rate(mStream); - configBase->channel_mask = mStream->get_channels(mStream); - configBase->format = mStream->get_format(mStream); - return OK; -} - -status_t StreamHalLocal::setParameters(const String8& kvPairs) { - return mStream->set_parameters(mStream, kvPairs.string()); -} - -status_t StreamHalLocal::getParameters(const String8& keys, String8 *values) { - char *halValues = mStream->get_parameters(mStream, keys.string()); - if (halValues != NULL) { - values->setTo(halValues); - free(halValues); - } else { - values->clear(); - } - return OK; -} - -status_t StreamHalLocal::addEffect(sp) { - LOG_ALWAYS_FATAL("Local streams can not have effects"); - return INVALID_OPERATION; -} - -status_t StreamHalLocal::removeEffect(sp) { - LOG_ALWAYS_FATAL("Local streams can not have effects"); - return INVALID_OPERATION; -} - -status_t StreamHalLocal::standby() { - return mStream->standby(mStream); -} - -status_t StreamHalLocal::dump(int fd, const Vector& args) { - (void) args; - status_t status = mStream->dump(mStream, fd); - mStreamPowerLog.dump(fd); - return status; -} - -status_t StreamHalLocal::setHalThreadPriority(int) { - // Don't need to do anything as local hal is executed by audioflinger directly - // on the same thread. - return OK; -} - -StreamOutHalLocal::StreamOutHalLocal(audio_stream_out_t *stream, sp device) - : StreamHalLocal(&stream->common, device), mStream(stream) { -} - -StreamOutHalLocal::~StreamOutHalLocal() { - mCallback.clear(); - mDevice->closeOutputStream(mStream); - mStream = 0; -} - -status_t StreamOutHalLocal::getFrameSize(size_t *size) { - *size = audio_stream_out_frame_size(mStream); - return OK; -} - -status_t StreamOutHalLocal::getLatency(uint32_t *latency) { - *latency = mStream->get_latency(mStream); - return OK; -} - -status_t StreamOutHalLocal::setVolume(float left, float right) { - if (mStream->set_volume == NULL) return INVALID_OPERATION; - return mStream->set_volume(mStream, left, right); -} - -status_t StreamOutHalLocal::selectPresentation(int presentationId, int programId) { - AudioParameter param; - param.addInt(String8(AudioParameter::keyPresentationId), presentationId); - param.addInt(String8(AudioParameter::keyProgramId), programId); - return setParameters(param.toString()); -} - -status_t StreamOutHalLocal::write(const void *buffer, size_t bytes, size_t *written) { - ssize_t writeResult = mStream->write(mStream, buffer, bytes); - if (writeResult > 0) { - *written = writeResult; - mStreamPowerLog.log(buffer, *written); - return OK; - } else { - *written = 0; - return writeResult; - } -} - -status_t StreamOutHalLocal::getRenderPosition(uint32_t *dspFrames) { - return mStream->get_render_position(mStream, dspFrames); -} - -status_t StreamOutHalLocal::getNextWriteTimestamp(int64_t *timestamp) { - if (mStream->get_next_write_timestamp == NULL) return INVALID_OPERATION; - return mStream->get_next_write_timestamp(mStream, timestamp); -} - -status_t StreamOutHalLocal::setCallback(wp callback) { - if (mStream->set_callback == NULL) return INVALID_OPERATION; - status_t result = mStream->set_callback(mStream, StreamOutHalLocal::asyncCallback, this); - if (result == OK) { - mCallback = callback; - } - return result; -} - -// static -int StreamOutHalLocal::asyncCallback(stream_callback_event_t event, void*, void *cookie) { - // We act as if we gave a wp to HAL. This way we should handle - // correctly the case when the callback is invoked while StreamOutHalLocal's destructor is - // already running, because the destructor is invoked after the refcount has been atomically - // decremented. - wp weakSelf(static_cast(cookie)); - sp self = weakSelf.promote(); - if (self == 0) return 0; - sp callback = self->mCallback.promote(); - if (callback == 0) return 0; - ALOGV("asyncCallback() event %d", event); - switch (event) { - case STREAM_CBK_EVENT_WRITE_READY: - callback->onWriteReady(); - break; - case STREAM_CBK_EVENT_DRAIN_READY: - callback->onDrainReady(); - break; - case STREAM_CBK_EVENT_ERROR: - callback->onError(); - break; - default: - ALOGW("asyncCallback() unknown event %d", event); - break; - } - return 0; -} - -status_t StreamOutHalLocal::supportsPauseAndResume(bool *supportsPause, bool *supportsResume) { - *supportsPause = mStream->pause != NULL; - *supportsResume = mStream->resume != NULL; - return OK; -} - -status_t StreamOutHalLocal::pause() { - if (mStream->pause == NULL) return INVALID_OPERATION; - return mStream->pause(mStream); -} - -status_t StreamOutHalLocal::resume() { - if (mStream->resume == NULL) return INVALID_OPERATION; - return mStream->resume(mStream); -} - -status_t StreamOutHalLocal::supportsDrain(bool *supportsDrain) { - *supportsDrain = mStream->drain != NULL; - return OK; -} - -status_t StreamOutHalLocal::drain(bool earlyNotify) { - if (mStream->drain == NULL) return INVALID_OPERATION; - return mStream->drain(mStream, earlyNotify ? AUDIO_DRAIN_EARLY_NOTIFY : AUDIO_DRAIN_ALL); -} - -status_t StreamOutHalLocal::flush() { - if (mStream->flush == NULL) return INVALID_OPERATION; - return mStream->flush(mStream); -} - -status_t StreamOutHalLocal::getPresentationPosition(uint64_t *frames, struct timespec *timestamp) { - if (mStream->get_presentation_position == NULL) return INVALID_OPERATION; - return mStream->get_presentation_position(mStream, frames, timestamp); -} - -void StreamOutHalLocal::doUpdateSourceMetadata(const SourceMetadata& sourceMetadata) { - std::vector halTracks; - halTracks.reserve(sourceMetadata.tracks.size()); - for (auto& metadata : sourceMetadata.tracks) { - playback_track_metadata halTrackMetadata; - playback_track_metadata_from_v7(&halTrackMetadata, &metadata); - halTracks.push_back(halTrackMetadata); - } - const source_metadata_t halMetadata = { - .track_count = halTracks.size(), - .tracks = halTracks.data(), - }; - mStream->update_source_metadata(mStream, &halMetadata); -} - -#if MAJOR_VERSION >= 7 -void StreamOutHalLocal::doUpdateSourceMetadataV7(const SourceMetadata& sourceMetadata) { - const source_metadata_v7_t metadata { - .track_count = sourceMetadata.tracks.size(), - // const cast is fine as it is in a const structure - .tracks = const_cast(sourceMetadata.tracks.data()), - }; - mStream->update_source_metadata_v7(mStream, &metadata); -} -#endif - -status_t StreamOutHalLocal::updateSourceMetadata(const SourceMetadata& sourceMetadata) { -#if MAJOR_VERSION < 7 - if (mStream->update_source_metadata == nullptr) { - return INVALID_OPERATION; - } - doUpdateSourceMetadata(sourceMetadata); -#else - if (mDevice->version() < AUDIO_DEVICE_API_VERSION_3_2) { - if (mStream->update_source_metadata == nullptr) { - return INVALID_OPERATION; - } - doUpdateSourceMetadata(sourceMetadata); - } else { - if (mStream->update_source_metadata_v7 == nullptr) { - return INVALID_OPERATION; - } - doUpdateSourceMetadataV7(sourceMetadata); - } -#endif - return OK; -} - - -status_t StreamOutHalLocal::start() { - if (mStream->start == NULL) return INVALID_OPERATION; - return mStream->start(mStream); -} - -status_t StreamOutHalLocal::stop() { - if (mStream->stop == NULL) return INVALID_OPERATION; - return mStream->stop(mStream); -} - -status_t StreamOutHalLocal::createMmapBuffer(int32_t minSizeFrames, - struct audio_mmap_buffer_info *info) { - if (mStream->create_mmap_buffer == NULL) return INVALID_OPERATION; - return mStream->create_mmap_buffer(mStream, minSizeFrames, info); -} - -status_t StreamOutHalLocal::getMmapPosition(struct audio_mmap_position *position) { - if (mStream->get_mmap_position == NULL) return INVALID_OPERATION; - return mStream->get_mmap_position(mStream, position); -} - -status_t StreamOutHalLocal::getDualMonoMode(audio_dual_mono_mode_t* mode) { - if (mStream->get_dual_mono_mode == nullptr) return INVALID_OPERATION; - return mStream->get_dual_mono_mode(mStream, mode); -} - -status_t StreamOutHalLocal::setDualMonoMode(audio_dual_mono_mode_t mode) { - if (mStream->set_dual_mono_mode == nullptr) return INVALID_OPERATION; - return mStream->set_dual_mono_mode(mStream, mode); -} - -status_t StreamOutHalLocal::getAudioDescriptionMixLevel(float* leveldB) { - if (mStream->get_audio_description_mix_level == nullptr) return INVALID_OPERATION; - return mStream->get_audio_description_mix_level(mStream, leveldB); -} - -status_t StreamOutHalLocal::setAudioDescriptionMixLevel(float leveldB) { - if (mStream->set_audio_description_mix_level == nullptr) return INVALID_OPERATION; - return mStream->set_audio_description_mix_level(mStream, leveldB); -} - -status_t StreamOutHalLocal::getPlaybackRateParameters(audio_playback_rate_t* playbackRate) { - if (mStream->get_playback_rate_parameters == nullptr) return INVALID_OPERATION; - return mStream->get_playback_rate_parameters(mStream, playbackRate); -} - -status_t StreamOutHalLocal::setPlaybackRateParameters(const audio_playback_rate_t& playbackRate) { - if (mStream->set_playback_rate_parameters == nullptr) return INVALID_OPERATION; - return mStream->set_playback_rate_parameters(mStream, &playbackRate); -} - -status_t StreamOutHalLocal::setEventCallback( - const sp& callback) { - if (mStream->set_event_callback == nullptr) { - return INVALID_OPERATION; - } - stream_event_callback_t asyncCallback = - callback == nullptr ? nullptr : StreamOutHalLocal::asyncEventCallback; - status_t result = mStream->set_event_callback(mStream, asyncCallback, this); - if (result == OK) { - mEventCallback = callback; - } - return result; -} - -// static -int StreamOutHalLocal::asyncEventCallback( - stream_event_callback_type_t event, void *param, void *cookie) { - // We act as if we gave a wp to HAL. This way we should handle - // correctly the case when the callback is invoked while StreamOutHalLocal's destructor is - // already running, because the destructor is invoked after the refcount has been atomically - // decremented. - wp weakSelf(static_cast(cookie)); - sp self = weakSelf.promote(); - if (self == nullptr) return 0; - sp callback = self->mEventCallback.promote(); - if (callback.get() == nullptr) return 0; - switch (event) { - case STREAM_EVENT_CBK_TYPE_CODEC_FORMAT_CHANGED: - // void* param is the byte string buffer from byte_string_from_audio_metadata(). - // As the byte string buffer may have embedded zeroes, we cannot use strlen() - callback->onCodecFormatChanged(std::basic_string( - (const uint8_t*)param, - audio_utils::metadata::dataByteStringLen((const uint8_t*)param))); - break; - default: - ALOGW("%s unknown event %d", __func__, event); - break; - } - return 0; -} - -StreamInHalLocal::StreamInHalLocal(audio_stream_in_t *stream, sp device) - : StreamHalLocal(&stream->common, device), mStream(stream) { -} - -StreamInHalLocal::~StreamInHalLocal() { - mDevice->closeInputStream(mStream); - mStream = 0; -} - -status_t StreamInHalLocal::getFrameSize(size_t *size) { - *size = audio_stream_in_frame_size(mStream); - return OK; -} - -status_t StreamInHalLocal::setGain(float gain) { - return mStream->set_gain(mStream, gain); -} - -status_t StreamInHalLocal::read(void *buffer, size_t bytes, size_t *read) { - ssize_t readResult = mStream->read(mStream, buffer, bytes); - if (readResult > 0) { - *read = readResult; - mStreamPowerLog.log( buffer, *read); - return OK; - } else { - *read = 0; - return readResult; - } -} - -status_t StreamInHalLocal::getInputFramesLost(uint32_t *framesLost) { - *framesLost = mStream->get_input_frames_lost(mStream); - return OK; -} - -status_t StreamInHalLocal::getCapturePosition(int64_t *frames, int64_t *time) { - if (mStream->get_capture_position == NULL) return INVALID_OPERATION; - return mStream->get_capture_position(mStream, frames, time); -} - -void StreamInHalLocal::doUpdateSinkMetadata(const SinkMetadata& sinkMetadata) { - std::vector halTracks; - halTracks.reserve(sinkMetadata.tracks.size()); - for (auto& metadata : sinkMetadata.tracks) { - record_track_metadata halTrackMetadata; - record_track_metadata_from_v7(&halTrackMetadata, &metadata); - halTracks.push_back(halTrackMetadata); - } - const sink_metadata_t halMetadata = { - .track_count = halTracks.size(), - .tracks = halTracks.data(), - }; - mStream->update_sink_metadata(mStream, &halMetadata); -} - -#if MAJOR_VERSION >= 7 -void StreamInHalLocal::doUpdateSinkMetadataV7(const SinkMetadata& sinkMetadata) { - const sink_metadata_v7_t halMetadata { - .track_count = sinkMetadata.tracks.size(), - // const cast is fine as it is in a const structure - .tracks = const_cast(sinkMetadata.tracks.data()), - }; - mStream->update_sink_metadata_v7(mStream, &halMetadata); -} -#endif - -status_t StreamInHalLocal::updateSinkMetadata(const SinkMetadata& sinkMetadata) { -#if MAJOR_VERSION < 7 - if (mStream->update_sink_metadata == nullptr) { - return INVALID_OPERATION; // not supported by the HAL - } - doUpdateSinkMetadata(sinkMetadata); -#else - if (mDevice->version() < AUDIO_DEVICE_API_VERSION_3_2) { - if (mStream->update_sink_metadata == nullptr) { - return INVALID_OPERATION; // not supported by the HAL - } - doUpdateSinkMetadata(sinkMetadata); - } else { - if (mStream->update_sink_metadata_v7 == nullptr) { - return INVALID_OPERATION; // not supported by the HAL - } - doUpdateSinkMetadataV7(sinkMetadata); - } -#endif - return OK; -} - -status_t StreamInHalLocal::start() { - if (mStream->start == NULL) return INVALID_OPERATION; - return mStream->start(mStream); -} - -status_t StreamInHalLocal::stop() { - if (mStream->stop == NULL) return INVALID_OPERATION; - return mStream->stop(mStream); -} - -status_t StreamInHalLocal::createMmapBuffer(int32_t minSizeFrames, - struct audio_mmap_buffer_info *info) { - if (mStream->create_mmap_buffer == NULL) return INVALID_OPERATION; - return mStream->create_mmap_buffer(mStream, minSizeFrames, info); -} - -status_t StreamInHalLocal::getMmapPosition(struct audio_mmap_position *position) { - if (mStream->get_mmap_position == NULL) return INVALID_OPERATION; - return mStream->get_mmap_position(mStream, position); -} - -#if MAJOR_VERSION == 2 -status_t StreamInHalLocal::getActiveMicrophones( - std::vector *microphones __unused) { - return INVALID_OPERATION; -} -#elif MAJOR_VERSION >= 4 -status_t StreamInHalLocal::getActiveMicrophones(std::vector *microphones) { - if (mStream->get_active_microphones == NULL) return INVALID_OPERATION; - size_t actual_mics = AUDIO_MICROPHONE_MAX_COUNT; - audio_microphone_characteristic_t mic_array[AUDIO_MICROPHONE_MAX_COUNT]; - status_t status = mStream->get_active_microphones(mStream, &mic_array[0], &actual_mics); - for (size_t i = 0; i < actual_mics; i++) { - media::MicrophoneInfo microphoneInfo = media::MicrophoneInfo(mic_array[i]); - microphones->push_back(microphoneInfo); - } - return status; -} -#endif - -#if MAJOR_VERSION < 5 -status_t StreamInHalLocal::setPreferredMicrophoneDirection( - audio_microphone_direction_t direction __unused) { - return INVALID_OPERATION; -} - -status_t StreamInHalLocal::setPreferredMicrophoneFieldDimension(float zoom __unused) { - return INVALID_OPERATION; -} -#else -status_t StreamInHalLocal::setPreferredMicrophoneDirection(audio_microphone_direction_t direction) { - if (mStream->set_microphone_direction == NULL) return INVALID_OPERATION; - return mStream->set_microphone_direction(mStream, direction); -} - -status_t StreamInHalLocal::setPreferredMicrophoneFieldDimension(float zoom) { - if (mStream->set_microphone_field_dimension == NULL) return INVALID_OPERATION; - return mStream->set_microphone_field_dimension(mStream, zoom); - -} -#endif - -} // namespace android diff --git a/media/libaudiohal/impl/StreamHalLocal.h b/media/libaudiohal/impl/StreamHalLocal.h deleted file mode 100644 index 770137f5af23b3f6455ca67eab29b6f97a31a487..0000000000000000000000000000000000000000 --- a/media/libaudiohal/impl/StreamHalLocal.h +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright (C) 2016 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_HARDWARE_STREAM_HAL_LOCAL_H -#define ANDROID_HARDWARE_STREAM_HAL_LOCAL_H - -#include -#include "StreamPowerLog.h" - -namespace android { - -class DeviceHalLocal; - -class StreamHalLocal : public virtual StreamHalInterface -{ - public: - // Return size of input/output buffer in bytes for this stream - eg. 4800. - virtual status_t getBufferSize(size_t *size); - - // Return the base configuration of the stream: - // - channel mask; - // - format - e.g. AUDIO_FORMAT_PCM_16_BIT; - // - sampling rate in Hz - eg. 44100. - virtual status_t getAudioProperties(audio_config_base_t *configBase); - - // Set audio stream parameters. - virtual status_t setParameters(const String8& kvPairs); - - // Get audio stream parameters. - virtual status_t getParameters(const String8& keys, String8 *values); - - // Add or remove the effect on the stream. - virtual status_t addEffect(sp effect); - virtual status_t removeEffect(sp effect); - - // Put the audio hardware input/output into standby mode. - virtual status_t standby(); - - virtual status_t dump(int fd, const Vector& args) override; - - // Start a stream operating in mmap mode. - virtual status_t start() = 0; - - // Stop a stream operating in mmap mode. - virtual status_t stop() = 0; - - // Retrieve information on the data buffer in mmap mode. - virtual status_t createMmapBuffer(int32_t minSizeFrames, - struct audio_mmap_buffer_info *info) = 0; - - // Get current read/write position in the mmap buffer - virtual status_t getMmapPosition(struct audio_mmap_position *position) = 0; - - // Set the priority of the thread that interacts with the HAL - // (must match the priority of the audioflinger's thread that calls 'read' / 'write') - virtual status_t setHalThreadPriority(int priority); - - protected: - // Subclasses can not be constructed directly by clients. - StreamHalLocal(audio_stream_t *stream, sp device); - - // The destructor automatically closes the stream. - virtual ~StreamHalLocal(); - - sp mDevice; - - // mStreamPowerLog is used for audio signal power logging. - StreamPowerLog mStreamPowerLog; - - private: - audio_stream_t *mStream; -}; - -class StreamOutHalLocal : public StreamOutHalInterface, public StreamHalLocal { - public: - // Return the frame size (number of bytes per sample) of a stream. - virtual status_t getFrameSize(size_t *size); - - // Return the audio hardware driver estimated latency in milliseconds. - virtual status_t getLatency(uint32_t *latency); - - // Use this method in situations where audio mixing is done in the hardware. - virtual status_t setVolume(float left, float right); - - // Selects the audio presentation (if available). - virtual status_t selectPresentation(int presentationId, int programId); - - // Write audio buffer to driver. - virtual status_t write(const void *buffer, size_t bytes, size_t *written); - - // Return the number of audio frames written by the audio dsp to DAC since - // the output has exited standby. - virtual status_t getRenderPosition(uint32_t *dspFrames); - - // Get the local time at which the next write to the audio driver will be presented. - virtual status_t getNextWriteTimestamp(int64_t *timestamp); - - // Set the callback for notifying completion of non-blocking write and drain. - virtual status_t setCallback(wp callback); - - // Returns whether pause and resume operations are supported. - virtual status_t supportsPauseAndResume(bool *supportsPause, bool *supportsResume); - - // Notifies to the audio driver to resume playback following a pause. - virtual status_t pause(); - - // Notifies to the audio driver to resume playback following a pause. - virtual status_t resume(); - - // Returns whether drain operation is supported. - virtual status_t supportsDrain(bool *supportsDrain); - - // Requests notification when data buffered by the driver/hardware has been played. - virtual status_t drain(bool earlyNotify); - - // Notifies to the audio driver to flush the queued data. - virtual status_t flush(); - - // Return a recent count of the number of audio frames presented to an external observer. - virtual status_t getPresentationPosition(uint64_t *frames, struct timespec *timestamp); - - // Start a stream operating in mmap mode. - virtual status_t start(); - - // Stop a stream operating in mmap mode. - virtual status_t stop(); - - // Retrieve information on the data buffer in mmap mode. - virtual status_t createMmapBuffer(int32_t minSizeFrames, - struct audio_mmap_buffer_info *info); - - // Get current read/write position in the mmap buffer - virtual status_t getMmapPosition(struct audio_mmap_position *position); - - // Called when the metadata of the stream's source has been changed. - status_t updateSourceMetadata(const SourceMetadata& sourceMetadata) override; - - // Returns the Dual Mono mode presentation setting. - status_t getDualMonoMode(audio_dual_mono_mode_t* mode) override; - - // Sets the Dual Mono mode presentation on the output device. - status_t setDualMonoMode(audio_dual_mono_mode_t mode) override; - - // Returns the Audio Description Mix level in dB. - status_t getAudioDescriptionMixLevel(float* leveldB) override; - - // Sets the Audio Description Mix level in dB. - status_t setAudioDescriptionMixLevel(float leveldB) override; - - // Retrieves current playback rate parameters. - status_t getPlaybackRateParameters(audio_playback_rate_t* playbackRate) override; - - // Sets the playback rate parameters that control playback behavior. - status_t setPlaybackRateParameters(const audio_playback_rate_t& playbackRate) override; - - status_t setEventCallback(const sp& callback) override; - - status_t setLatencyMode(audio_latency_mode_t mode __unused) override { - return INVALID_OPERATION; - } - status_t getRecommendedLatencyModes( - std::vector *modes __unused) override { - return INVALID_OPERATION; - } - status_t setLatencyModeCallback( - const sp& callback __unused) override { - return INVALID_OPERATION; - } - - private: - audio_stream_out_t *mStream; - wp mCallback; - wp mEventCallback; - - friend class DeviceHalLocal; - - // Can not be constructed directly by clients. - StreamOutHalLocal(audio_stream_out_t *stream, sp device); - - virtual ~StreamOutHalLocal(); - - static int asyncCallback(stream_callback_event_t event, void *param, void *cookie); - - static int asyncEventCallback(stream_event_callback_type_t event, void *param, void *cookie); - - void doUpdateSourceMetadataV7(const SourceMetadata& sourceMetadata); - void doUpdateSourceMetadata(const SourceMetadata& sourceMetadata); -}; - -class StreamInHalLocal : public StreamInHalInterface, public StreamHalLocal { - public: - // Return the frame size (number of bytes per sample) of a stream. - virtual status_t getFrameSize(size_t *size); - - // Set the input gain for the audio driver. - virtual status_t setGain(float gain); - - // Read audio buffer in from driver. - virtual status_t read(void *buffer, size_t bytes, size_t *read); - - // Return the amount of input frames lost in the audio driver. - virtual status_t getInputFramesLost(uint32_t *framesLost); - - // Return a recent count of the number of audio frames received and - // the clock time associated with that frame count. - virtual status_t getCapturePosition(int64_t *frames, int64_t *time); - - // Start a stream operating in mmap mode. - virtual status_t start(); - - // Stop a stream operating in mmap mode. - virtual status_t stop(); - - // Retrieve information on the data buffer in mmap mode. - virtual status_t createMmapBuffer(int32_t minSizeFrames, - struct audio_mmap_buffer_info *info); - - // Get current read/write position in the mmap buffer - virtual status_t getMmapPosition(struct audio_mmap_position *position); - - // Get active microphones - virtual status_t getActiveMicrophones(std::vector *microphones); - - // Sets microphone direction (for processing) - virtual status_t setPreferredMicrophoneDirection(audio_microphone_direction_t direction); - - // Sets microphone zoom (for processing) - virtual status_t setPreferredMicrophoneFieldDimension(float zoom); - - // Called when the metadata of the stream's sink has been changed. - status_t updateSinkMetadata(const SinkMetadata& sinkMetadata) override; - - private: - audio_stream_in_t *mStream; - - friend class DeviceHalLocal; - - // Can not be constructed directly by clients. - StreamInHalLocal(audio_stream_in_t *stream, sp device); - - virtual ~StreamInHalLocal(); - - void doUpdateSinkMetadata(const SinkMetadata& sinkMetadata); - void doUpdateSinkMetadataV7(const SinkMetadata& sinkMetadata); -}; - -} // namespace android - -#endif // ANDROID_HARDWARE_STREAM_HAL_LOCAL_H diff --git a/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h b/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h index f0a0b2957566f307ad4ddd2e0bac3674339f907b..d27ad4c621205cc507a9cf6d88a89a3ab27d83c0 100644 --- a/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h +++ b/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h @@ -17,6 +17,9 @@ #ifndef ANDROID_HARDWARE_DEVICE_HAL_INTERFACE_H #define ANDROID_HARDWARE_DEVICE_HAL_INTERFACE_H +#include +#include +#include #include #include #include @@ -120,9 +123,17 @@ class DeviceHalInterface : public RefBase virtual status_t removeDeviceEffect( audio_port_handle_t device, sp effect) = 0; + virtual status_t getMmapPolicyInfos( + media::audio::common::AudioMMapPolicyType policyType, + std::vector *policyInfos) = 0; + virtual int32_t getAAudioMixerBurstCount() = 0; + virtual int32_t getAAudioHardwareBurstMinUsec() = 0; + // Update the connection status of an external device. virtual status_t setConnectedState(const struct audio_port_v7 *port, bool connected) = 0; + virtual error::Result getHwAvSync() = 0; + virtual status_t dump(int fd, const Vector& args) = 0; protected: diff --git a/media/libaudiohal/include/media/audiohal/DevicesFactoryHalInterface.h b/media/libaudiohal/include/media/audiohal/DevicesFactoryHalInterface.h index 5091558ce4f1d89fa05869ff1204d4a5291bb4db..17010e680083750c9817eed6fc559e4f44676161 100644 --- a/media/libaudiohal/include/media/audiohal/DevicesFactoryHalInterface.h +++ b/media/libaudiohal/include/media/audiohal/DevicesFactoryHalInterface.h @@ -43,6 +43,8 @@ class DevicesFactoryHalInterface : public RefBase // The callback can be only set once. virtual status_t setCallbackOnce(sp callback) = 0; + virtual float getHalVersion() const = 0; + static sp create(); protected: diff --git a/media/libaudiohal/include/media/audiohal/FactoryHalHidl.h b/media/libaudiohal/include/media/audiohal/FactoryHalHidl.h index d353ed0d8a9b0769443eb659866b4293cf97f0f2..866dd3e0e6476a0a92d9594147755a022636755f 100644 --- a/media/libaudiohal/include/media/audiohal/FactoryHalHidl.h +++ b/media/libaudiohal/include/media/audiohal/FactoryHalHidl.h @@ -18,21 +18,42 @@ #define ANDROID_HARDWARE_FACTORY_HAL_HIDL_H #include +#include #include namespace android { +// The pair of the interface's package name and the interface name, +// e.g. <"android.hardware.audio", "IDevicesFactory">. +// Splitting is used for easier construction of versioned names (FQNs). +using InterfaceName = std::pair; + namespace detail { -void* createPreferredImpl(const std::string& package, const std::string& interface); +void* createPreferredImpl(const InterfaceName& iface, const InterfaceName& siblingIface); } // namespace detail -/** @Return the preferred available implementation or nullptr if none are available. */ +/** + * Create a client for the "preferred" (most recent) implementation of an interface. + * by loading the appropriate version of the shared library containing the implementation. + * + * In the audio HAL, there are two families of interfaces: core and effects. Both are + * packed into the same shared library for memory efficiency. Since the core and the effects + * interface can have different minor versions on the device, in order to avoid loading multiple + * shared libraries the loader function considers which interface among two has the most + * recent version. Thus, a pair of interface names must be passed in. + * + * @param iface the interface that needs to be created. + * @param siblingIface the interface which occupies the same shared library. + * @return the preferred available implementation or nullptr if none are available. + */ template -static sp createPreferredImpl(const std::string& package, const std::string& interface) { - return sp{static_cast(detail::createPreferredImpl(package, interface))}; +static sp createPreferredImpl( + const InterfaceName& iface, const InterfaceName& siblingIface) { + return sp{ + static_cast(detail::createPreferredImpl(iface, siblingIface))}; } } // namespace android diff --git a/media/libaudiohal/include/media/audiohal/StreamHalInterface.h b/media/libaudiohal/include/media/audiohal/StreamHalInterface.h index e12fe77296cce44dc762fa869b868e2cf31aab03..1d52b7dc50f4f7f00e3500a152403859418a7bc9 100644 --- a/media/libaudiohal/include/media/audiohal/StreamHalInterface.h +++ b/media/libaudiohal/include/media/audiohal/StreamHalInterface.h @@ -89,6 +89,12 @@ class StreamHalInterface : public virtual RefBase // (must match the priority of the audioflinger's thread that calls 'read' / 'write') virtual status_t setHalThreadPriority(int priority) = 0; + virtual status_t legacyCreateAudioPatch(const struct audio_port_config& port, + std::optional source, + audio_devices_t type) = 0; + + virtual status_t legacyReleaseAudioPatch() = 0; + protected: // Subclasses can not be constructed directly by clients. StreamHalInterface() {} @@ -242,6 +248,11 @@ class StreamOutHalInterface : public virtual StreamHalInterface { virtual status_t setLatencyModeCallback( const sp& callback) = 0; + /** + * Signal the end of audio output, interrupting an ongoing 'write' operation. + */ + virtual status_t exit() = 0; + protected: virtual ~StreamOutHalInterface() {} }; diff --git a/media/libaudioprocessing/TEST_MAPPING b/media/libaudioprocessing/TEST_MAPPING new file mode 100644 index 0000000000000000000000000000000000000000..3de5a9fa85ee91fcf87cb4eae6150daaecb2ec6d --- /dev/null +++ b/media/libaudioprocessing/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "CtsNativeMediaAAudioTestCases", + "options" : [ + { + "include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic.*" + } + ] + } + ] +} diff --git a/media/libeffects/preprocessing/PreProcessing.cpp b/media/libeffects/preprocessing/PreProcessing.cpp index 19a8b2f9b960f3b0c971e75aabfcb52d4f13e009..61a2bf571b79622e0a57d89c69d7de6207f5e3f6 100644 --- a/media/libeffects/preprocessing/PreProcessing.cpp +++ b/media/libeffects/preprocessing/PreProcessing.cpp @@ -150,6 +150,7 @@ const channel_config_t sDualMicConfigs[CHANNEL_CFG_CNT] = { bool sHasAuxChannels[PREPROC_NUM_EFFECTS] = { false, // PREPROC_AGC + false, // PREPROC_AGC2 true, // PREPROC_AEC true, // PREPROC_NS }; diff --git a/media/libeffects/testlibs/Android.bp b/media/libeffects/testlibs/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..5ba56bb191980a13bf3e7d9fa37d39683ad89c6b --- /dev/null +++ b/media/libeffects/testlibs/Android.bp @@ -0,0 +1,77 @@ +// Test Reverb library +package { + default_applicable_licenses: [ + "frameworks_av_media_libeffects_testlibs_license", + ], +} + +license { + name: "frameworks_av_media_libeffects_testlibs_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + +cc_library { + name: "libreverbtest", + host_supported: true, + vendor: true, + srcs: [ + "EffectReverb.c", + "EffectsMath.c", + ], + + shared_libs: [ + "libcutils", + "liblog", + ], + + relative_install_path: "soundfx", + + cflags: [ + "-fvisibility=hidden", + "-Wall", + "-Werror", + "-Wno-address-of-packed-member", + ], + + header_libs: [ + "libaudioeffects", + ], +} + +cc_library { + name: "libequalizertest", + host_supported: true, + vendor: true, + srcs: [ + "AudioBiquadFilter.cpp", + "AudioCoefInterpolator.cpp", + "AudioEqualizer.cpp", + "AudioPeakingFilter.cpp", + "AudioShelvingFilter.cpp", + "EffectEqualizer.cpp", + "EffectsMath.c", + ], + + shared_libs: [ + "libcutils", + "liblog", + ], + + relative_install_path: "soundfx", + + cflags: [ + "-fvisibility=hidden", + "-Wall", + "-Werror", + ], + + header_libs: [ + "libaudioeffects", + ], +} diff --git a/media/libeffects/testlibs/Android.mk_ b/media/libeffects/testlibs/Android.mk_ deleted file mode 100644 index 14c373fb9bd2b7d0a3913a4946faa990a85a9561..0000000000000000000000000000000000000000 --- a/media/libeffects/testlibs/Android.mk_ +++ /dev/null @@ -1,55 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -# Test Reverb library -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - EffectReverb.c.arm \ - EffectsMath.c.arm - -LOCAL_CFLAGS := -O2 - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl - -LOCAL_MODULE_RELATIVE_PATH := soundfx -LOCAL_MODULE := libreverbtest - -LOCAL_C_INCLUDES := \ - $(call include-path-for, audio-effects) \ - $(call include-path-for, graphics corecg) - -LOCAL_MODULE_TAGS := optional - -include $(BUILD_SHARED_LIBRARY) - -# Test Equalizer library -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - EffectsMath.c.arm \ - EffectEqualizer.cpp \ - AudioBiquadFilter.cpp.arm \ - AudioCoefInterpolator.cpp.arm \ - AudioPeakingFilter.cpp.arm \ - AudioShelvingFilter.cpp.arm \ - AudioEqualizer.cpp.arm - -LOCAL_CFLAGS := -O2 - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libdl - -LOCAL_MODULE_RELATIVE_PATH := soundfx -LOCAL_MODULE := libequalizertest - -LOCAL_C_INCLUDES := \ - $(call include-path-for, graphics corecg) \ - $(call include-path-for, audio-effects) - -LOCAL_MODULE_TAGS := optional - -include $(BUILD_SHARED_LIBRARY) - diff --git a/media/libeffects/testlibs/AudioEqualizer.cpp b/media/libeffects/testlibs/AudioEqualizer.cpp index 4f3a30877c53d4e7ffc8b2b3b59131237c738077..141750b6015b2ae531f0f2caddf0f658f6eae19b 100644 --- a/media/libeffects/testlibs/AudioEqualizer.cpp +++ b/media/libeffects/testlibs/AudioEqualizer.cpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include "AudioEqualizer.h" #include "AudioPeakingFilter.h" diff --git a/media/libeffects/testlibs/AudioPeakingFilter.cpp b/media/libeffects/testlibs/AudioPeakingFilter.cpp index 99323acd6b498b2f0f0bf034c5825fe628b2054b..4257eca94a5c5e46b10a7dc0caef88038726fb26 100644 --- a/media/libeffects/testlibs/AudioPeakingFilter.cpp +++ b/media/libeffects/testlibs/AudioPeakingFilter.cpp @@ -87,9 +87,9 @@ void AudioPeakingFilter::setBandwidth(uint32_t cents) { void AudioPeakingFilter::commit(bool immediate) { audio_coef_t coefs[5]; int intCoord[3] = { - mFrequency >> FREQ_PRECISION_BITS, + (int)(mFrequency >> FREQ_PRECISION_BITS), mGain >> GAIN_PRECISION_BITS, - mBandwidth >> BANDWIDTH_PRECISION_BITS + (int)(mBandwidth >> BANDWIDTH_PRECISION_BITS) }; uint32_t fracCoord[3] = { mFrequency << (32 - FREQ_PRECISION_BITS), diff --git a/media/libeffects/testlibs/AudioShelvingFilter.cpp b/media/libeffects/testlibs/AudioShelvingFilter.cpp index e031287fc7c2f96ecb2f64979a0ae2d8d68eb7db..ad43c5ae1543f9f20aad1bd806d47fb4acbc4813 100644 --- a/media/libeffects/testlibs/AudioShelvingFilter.cpp +++ b/media/libeffects/testlibs/AudioShelvingFilter.cpp @@ -89,8 +89,8 @@ void AudioShelvingFilter::setGain(int32_t millibel) { void AudioShelvingFilter::commit(bool immediate) { audio_coef_t coefs[5]; int intCoord[2] = { - mFrequency >> FREQ_PRECISION_BITS, - mGain >> GAIN_PRECISION_BITS + (int)(mFrequency >> FREQ_PRECISION_BITS), + (int)(mGain >> GAIN_PRECISION_BITS) }; uint32_t fracCoord[2] = { mFrequency << (32 - FREQ_PRECISION_BITS), diff --git a/media/libeffects/testlibs/EffectEqualizer.cpp b/media/libeffects/testlibs/EffectEqualizer.cpp index db4d009a69caad8c084628f2af70f56f3e136526..72b530df697ab837f03ae30a6213497b79192a86 100644 --- a/media/libeffects/testlibs/EffectEqualizer.cpp +++ b/media/libeffects/testlibs/EffectEqualizer.cpp @@ -131,7 +131,8 @@ extern "C" int EffectCreate(const effect_uuid_t *uuid, int32_t ioId, effect_handle_t *pHandle) { int ret; - int i; + (void)sessionId; + (void)ioId; ALOGV("EffectLibCreateEffect start"); @@ -160,7 +161,7 @@ extern "C" int EffectCreate(const effect_uuid_t *uuid, pContext->state = EQUALIZER_STATE_INITIALIZED; ALOGV("EffectLibCreateEffect %p, size %d", - pContext, AudioEqualizer::GetInstanceSize(kNumBands)+sizeof(EqualizerContext)); + pContext, (int)(AudioEqualizer::GetInstanceSize(kNumBands)+sizeof(EqualizerContext))); return 0; @@ -294,7 +295,6 @@ void Equalizer_getConfig(EqualizerContext *pContext, effect_config_t *pConfig) int Equalizer_init(EqualizerContext *pContext) { - int status; ALOGV("Equalizer_init start"); @@ -630,7 +630,6 @@ extern "C" int Equalizer_command(effect_handle_t self, uint32_t cmdCode, uint32_ void *pCmdData, uint32_t *replySize, void *pReplyData) { android::EqualizerContext * pContext = (android::EqualizerContext *) self; - int retsize; if (pContext == NULL || pContext->state == EQUALIZER_STATE_UNINITIALIZED) { return -EINVAL; @@ -750,13 +749,13 @@ const struct effect_interface_s gEqualizerInterface = { NULL }; - +__attribute__ ((visibility ("default"))) audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { - tag : AUDIO_EFFECT_LIBRARY_TAG, - version : EFFECT_LIBRARY_API_VERSION, - name : "Test Equalizer Library", - implementor : "The Android Open Source Project", - create_effect : android::EffectCreate, - release_effect : android::EffectRelease, - get_descriptor : android::EffectGetDescriptor, + .tag = AUDIO_EFFECT_LIBRARY_TAG, + .version = EFFECT_LIBRARY_API_VERSION, + .name = "Test Equalizer Library", + .implementor = "The Android Open Source Project", + .create_effect = android::EffectCreate, + .release_effect = android::EffectRelease, + .get_descriptor = android::EffectGetDescriptor, }; diff --git a/media/libeffects/testlibs/EffectReverb.c b/media/libeffects/testlibs/EffectReverb.c index fce9bedff993384b9ea653ff87c757c15dfa9eda..efba4f451e66092e3298cea25744a7d9b296ff7a 100644 --- a/media/libeffects/testlibs/EffectReverb.c +++ b/media/libeffects/testlibs/EffectReverb.c @@ -107,6 +107,8 @@ int EffectCreate(const effect_uuid_t *uuid, const effect_descriptor_t *desc; int aux = 0; int preset = 0; + (void)sessionId; + (void)ioId; ALOGV("EffectLibCreateEffect start"); @@ -149,7 +151,7 @@ int EffectCreate(const effect_uuid_t *uuid, module->context.mState = REVERB_STATE_INITIALIZED; - ALOGV("EffectLibCreateEffect %p ,size %d", module, sizeof(reverb_module_t)); + ALOGV("EffectLibCreateEffect %p ,size %zu", module, sizeof(reverb_module_t)); return 0; } @@ -283,7 +285,6 @@ static int Reverb_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSi void *pCmdData, uint32_t *replySize, void *pReplyData) { reverb_module_t *pRvbModule = (reverb_module_t *) self; reverb_object_t *pReverb; - int retsize; if (pRvbModule == NULL || pRvbModule->context.mState == REVERB_STATE_UNINITIALIZED) { @@ -758,7 +759,6 @@ int Reverb_getParameter(reverb_object_t *pReverb, int32_t param, uint32_t *pSize int32_t *pValue32; int16_t *pValue16; t_reverb_settings *pProperties; - int32_t i; int32_t temp; int32_t temp2; uint32_t size; @@ -1654,7 +1654,6 @@ static int Reverb(reverb_object_t *pReverb, int nNumSamplesToAdd, int32_t nApOut; int32_t j; - int32_t nEarlyOut; int32_t tempValue; @@ -2203,6 +2202,7 @@ static int ReverbReadInPresets(reverb_object_t *pReverb) { return 0; } +__attribute__ ((visibility ("default"))) audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { .tag = AUDIO_EFFECT_LIBRARY_TAG, .version = EFFECT_LIBRARY_API_VERSION, diff --git a/media/libeffects/testlibs/EffectReverb.h b/media/libeffects/testlibs/EffectReverb.h index 756c5eaf4c22ff0f7a5a50aa1dbef65ae1da4a76..8f405d488e281cd3b91443c1fa488a39cf672c0d 100644 --- a/media/libeffects/testlibs/EffectReverb.h +++ b/media/libeffects/testlibs/EffectReverb.h @@ -443,7 +443,4 @@ static int ReverbReadInPresets(reverb_object_t* pReverbData); */ static int ReverbUpdateRoom(reverb_object_t* pReverbData, bool fullUpdate); - -static int ReverbComputeConstants(reverb_object_t *pReverbData, uint32_t samplingRate); - #endif /*ANDROID_EFFECTREVERB_H_*/ diff --git a/media/liberror/Android.bp b/media/liberror/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..f54d3541ba71a9f0cb6d5827cd21118a0374c688 --- /dev/null +++ b/media/liberror/Android.bp @@ -0,0 +1,67 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_av_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_av_license"], +} + +cc_library_headers { + name: "libexpectedutils_headers", + host_supported: true, + vendor_available: true, + min_sdk_version: "29", + export_include_dirs: [ + "include", + ], + header_libs: [ + "libbase_headers", + "libutils_headers", + ], + export_header_lib_headers: [ + "libbase_headers", + "libutils_headers", + ], + apex_available: [ + "//apex_available:platform", + "com.android.bluetooth", + "com.android.media", + "com.android.media.swcodec", + ], +} + +cc_test_host { + name: "libexpectedutils_test", + srcs: [ + "expected_utils_test.cpp", + ], + shared_libs: [ + "liblog", + ], + header_libs: [ + "libexpectedutils_headers", + ], +} + +cc_library_headers { + name: "liberror_headers", + host_supported: true, + vendor_available: true, + min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.bluetooth", + "com.android.media", + "com.android.media.swcodec", + ], + export_include_dirs: [ + "include", + ], + header_libs: [ + "libexpectedutils_headers", + ], + export_header_lib_headers: [ + "libexpectedutils_headers", + ], +} diff --git a/media/liberror/expected_utils_test.cpp b/media/liberror/expected_utils_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..252210a88d070aafa3771619de5c854287f67320 --- /dev/null +++ b/media/liberror/expected_utils_test.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#define LOG_TAG "Result-test" + +namespace android { +namespace foo { + +class Value { + public: + explicit Value(int i) : mInt(i) {} + Value(const Value&) = delete; + Value(Value&&) = default; + + operator int() const { return mInt; } + + private: + const int mInt; +}; + +class Status { + public: + explicit Status(int i) : mInt(i) {} + Status(const Status&) = delete; + Status(Status&&) = default; + + operator int() const { return mInt; } + + private: + const int mInt; +}; + +bool errorIsOk(const Status& e) { + return e == 0; +} + +std::string errorToString(const Status& e) { + std::ostringstream str; + str << e; + return str.str(); +} + +using Result = base::expected; + +} // namespace foo + +namespace { + +using foo::Result; +using foo::Status; +using foo::Value; + +TEST(Result, ValueOrReturnSuccess) { + Result result = []() -> Result { + Value intermediate = VALUE_OR_RETURN(Result(Value(3))); + return Value(intermediate + 1); + }(); + ASSERT_TRUE(result.ok()); + EXPECT_EQ(4, result.value()); +} + +TEST(Result, ValueOrReturnFailure) { + Result result = []() -> Result { + Value intermediate = VALUE_OR_RETURN(Result(base::unexpected(Status(2)))); + return Value(intermediate + 1); + }(); + ASSERT_FALSE(result.ok()); + EXPECT_EQ(2, result.error()); +} + +TEST(Result, ValueOrReturnStatusSuccess) { + Status status = []() -> Status { + Value intermediate = VALUE_OR_RETURN_STATUS(Result(Value(3))); + (void) intermediate; + return Status(0); + }(); + EXPECT_EQ(0, status); +} + +TEST(Result, ValueOrReturnStatusFailure) { + Status status = []() -> Status { + Value intermediate = VALUE_OR_RETURN_STATUS(Result(base::unexpected(Status(1)))); + (void) intermediate; + return Status(0); + }(); + EXPECT_EQ(1, status); +} + +TEST(Result, ReturnIfErrorSuccess) { + Result result = []() -> Result { + RETURN_IF_ERROR(Status(0)); + return Value(5); + }(); + ASSERT_TRUE(result.ok()); + EXPECT_EQ(5, result.value()); +} + +TEST(Result, ReturnIfErrorFailure) { + Result result = []() -> Result { + RETURN_IF_ERROR(Status(4)); + return Value(5); + }(); + ASSERT_FALSE(result.ok()); + EXPECT_EQ(4, result.error()); +} + +TEST(Result, ReturnStatusIfErrorSuccess) { + Status status = []() -> Status { + RETURN_STATUS_IF_ERROR(Status(0)); + return Status(7); + }(); + EXPECT_EQ(7, status); +} + +TEST(Result, ReturnStatusIfErrorFailure) { + Status status = []() -> Status { + RETURN_STATUS_IF_ERROR(Status(3)); + return Status(0); + }(); + EXPECT_EQ(3, status); +} + +TEST(Result, ValueOrFatalSuccess) { + Value value = VALUE_OR_FATAL(Result(Value(7))); + EXPECT_EQ(7, value); +} + +TEST(Result, ValueOrFatalFailure) { + EXPECT_DEATH(VALUE_OR_FATAL(Result(base::unexpected(Status(3)))), ""); +} + +TEST(Result, FatalIfErrorSuccess) { + FATAL_IF_ERROR(Status(0)); +} + +TEST(Result, FatalIfErrorFailure) { + EXPECT_DEATH(FATAL_IF_ERROR(Status(3)), ""); +} + +} // namespace +} // namespace android diff --git a/media/liberror/include/error/Result.h b/media/liberror/include/error/Result.h new file mode 100644 index 0000000000000000000000000000000000000000..620e6d04cb9811494b77ef20ce54bbfc1d2f0e10 --- /dev/null +++ b/media/liberror/include/error/Result.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 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. + */ +#pragma once + +#include +#include + +namespace android { +namespace error { + +/** + * A convenience short-hand for base::expected, where the error type is a status_t. + */ +template +using Result = base::expected; + +} // namespace error +} // namespace android + +// Below are the implementations of errorIsOk and errorToString for status_t . +// This allows status_t to be used in conjunction with the expected_utils.h macros. +// Unfortuantely, since status_t is merely a typedef for int rather than a unique type, we have to +// overload these methods for any int, and do so in the global namespace for ADL to work. + +inline bool errorIsOk(int status) { + return status == android::OK; +} + +inline std::string errorToString(int status) { + return android::statusToString(status); +} diff --git a/media/liberror/include/error/expected_utils.h b/media/liberror/include/error/expected_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..ddc85175dd4e3a4703dec761fa64135fdf1718e4 --- /dev/null +++ b/media/liberror/include/error/expected_utils.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2021 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. + */ +#pragma once + +#include + +#include +#include + +/** + * Useful macros for working with status codes and base::expected. + * + * These macros facilitate various kinds of strategies for reduction of error-handling-related + * boilerplate. They can be can be classified by the following criteria: + * - Whether the argument is a standalone status code vs. base::expected (status or value). In the + * latter case, the macro will evaluate to the contained value in the case of success. + * - Whether to FATAL or return in response to an error. + * - In the latter case, whether the enclosing function returns a status code or a base::expected. + * + * The table below summarizes which macro serves which case, based on those criteria: + * +--------------------+------------------+------------------------------------------------------+ + * | Error response | FATAL | Early return | + * | | +---------------------------+--------------------------+ + * | Expression type | | Function returns expected | Function returns status | + * +--------------------+------------------+---------------------------+--------------------------+ + * | status code | FATAL_IF_ERROR() | RETURN_IF_ERROR() | RETURN_STATUS_IF_ERROR() | + * +--------------------+------------------+---------------------------+--------------------------+ + * | expected | VALUE_OR_FATAL() | VALUE_OR_RETURN() | VALUE_OR_RETURN_STATUS() | + * +--------------------+------------------+---------------------------+--------------------------+ + * + * All macros expect that: + * - The error type and value value type are movable. + * - The macro argument can be assigned to a variable using `auto x = (exp)`. + * - The expression errorIsOk(e) for the error type evaluatea to a bool which is true iff the + * status is considered success. + * - The expression errorToString(e) for a given error type evaluated to a std::string containing a + * human-readable version of the status. + */ + +#define VALUE_OR_RETURN(exp) \ + ({ \ + auto _tmp = (exp); \ + if (!_tmp.ok()) return ::android::base::unexpected(std::move(_tmp.error())); \ + std::move(_tmp.value()); \ + }) + +#define VALUE_OR_RETURN_STATUS(exp) \ + ({ \ + auto _tmp = (exp); \ + if (!_tmp.ok()) return std::move(_tmp.error()); \ + std::move(_tmp.value()); \ + }) + +#define VALUE_OR_FATAL(exp) \ + ({ \ + auto _tmp = (exp); \ + LOG_ALWAYS_FATAL_IF(!_tmp.ok(), "Function: %s Line: %d Failed result (%s)", __FUNCTION__, \ + __LINE__, errorToString(_tmp.error()).c_str()); \ + std::move(_tmp.value()); \ + }) + +#define RETURN_IF_ERROR(exp) \ + if (auto _tmp = (exp); !errorIsOk(_tmp)) return ::android::base::unexpected(std::move(_tmp)); + +#define RETURN_STATUS_IF_ERROR(exp) \ + if (auto _tmp = (exp); !errorIsOk(_tmp)) return _tmp; + +#define FATAL_IF_ERROR(exp) \ + { \ + auto _tmp = (exp); \ + LOG_ALWAYS_FATAL_IF(!errorIsOk(_tmp), "Function: %s Line: %d Failed result: (%s)", \ + __FUNCTION__, __LINE__, errorToString(_tmp).c_str()); \ + } diff --git a/media/libheadtracking/Android.bp b/media/libheadtracking/Android.bp index 63b769e5389e5194912385da784d86c1c2d05c0c..9d63f9b4788fa9685a67ac6adf53da31e94c27ed 100644 --- a/media/libheadtracking/Android.bp +++ b/media/libheadtracking/Android.bp @@ -14,10 +14,12 @@ cc_library { "HeadTrackingProcessor.cpp", "ModeSelector.cpp", "Pose.cpp", + "PoseBias.cpp", "PoseDriftCompensator.cpp", "PoseRateLimiter.cpp", "QuaternionUtil.cpp", "ScreenHeadFusion.cpp", + "StillnessDetector.cpp", "Twist.cpp", ], export_include_dirs: [ @@ -44,6 +46,7 @@ cc_library { ], export_shared_lib_headers: [ "libheadtracking", + "libsensor", ], } @@ -66,10 +69,12 @@ cc_test_host { "HeadTrackingProcessor-test.cpp", "ModeSelector-test.cpp", "Pose-test.cpp", + "PoseBias-test.cpp", "PoseDriftCompensator-test.cpp", "PoseRateLimiter-test.cpp", "QuaternionUtil-test.cpp", "ScreenHeadFusion-test.cpp", + "StillnessDetector-test.cpp", "Twist-test.cpp", ], shared_libs: [ diff --git a/media/libheadtracking/HeadTrackingProcessor.cpp b/media/libheadtracking/HeadTrackingProcessor.cpp index 47f7cf0bf3c67abf61bc4cad3b309567b3c60cf0..71fae8a3bc7e92eb88e572c22fab08599cd2603b 100644 --- a/media/libheadtracking/HeadTrackingProcessor.cpp +++ b/media/libheadtracking/HeadTrackingProcessor.cpp @@ -17,9 +17,10 @@ #include "media/HeadTrackingProcessor.h" #include "ModeSelector.h" -#include "PoseDriftCompensator.h" +#include "PoseBias.h" #include "QuaternionUtil.h" #include "ScreenHeadFusion.h" +#include "StillnessDetector.h" namespace android { namespace media { @@ -32,13 +33,17 @@ class HeadTrackingProcessorImpl : public HeadTrackingProcessor { public: HeadTrackingProcessorImpl(const Options& options, HeadTrackingMode initialMode) : mOptions(options), - mHeadPoseDriftCompensator(PoseDriftCompensator::Options{ - .translationalDriftTimeConstant = options.translationalDriftTimeConstant, - .rotationalDriftTimeConstant = options.rotationalDriftTimeConstant, + mHeadStillnessDetector(StillnessDetector::Options{ + .defaultValue = false, + .windowDuration = options.autoRecenterWindowDuration, + .translationalThreshold = options.autoRecenterTranslationalThreshold, + .rotationalThreshold = options.autoRecenterRotationalThreshold, }), - mScreenPoseDriftCompensator(PoseDriftCompensator::Options{ - .translationalDriftTimeConstant = options.translationalDriftTimeConstant, - .rotationalDriftTimeConstant = options.rotationalDriftTimeConstant, + mScreenStillnessDetector(StillnessDetector::Options{ + .defaultValue = true, + .windowDuration = options.screenStillnessWindowDuration, + .translationalThreshold = options.screenStillnessTranslationalThreshold, + .rotationalThreshold = options.screenStillnessRotationalThreshold, }), mModeSelector(ModeSelector::Options{.freshnessTimeout = options.freshnessTimeout}, initialMode), @@ -52,7 +57,8 @@ class HeadTrackingProcessorImpl : public HeadTrackingProcessor { const Twist3f& headTwist) override { Pose3f predictedWorldToHead = worldToHead * integrate(headTwist, mOptions.predictionDuration); - mHeadPoseDriftCompensator.setInput(timestamp, predictedWorldToHead); + mHeadPoseBias.setInput(predictedWorldToHead); + mHeadStillnessDetector.setInput(timestamp, predictedWorldToHead); mWorldToHeadTimestamp = timestamp; } @@ -63,8 +69,9 @@ class HeadTrackingProcessorImpl : public HeadTrackingProcessor { mPhysicalToLogicalAngle = mPendingPhysicalToLogicalAngle; } - mScreenPoseDriftCompensator.setInput( - timestamp, worldToScreen * Pose3f(rotateY(-mPhysicalToLogicalAngle))); + Pose3f worldToLogicalScreen = worldToScreen * Pose3f(rotateY(-mPhysicalToLogicalAngle)); + mScreenPoseBias.setInput(worldToLogicalScreen); + mScreenStillnessDetector.setInput(timestamp, worldToLogicalScreen); mWorldToScreenTimestamp = timestamp; } @@ -77,18 +84,32 @@ class HeadTrackingProcessorImpl : public HeadTrackingProcessor { } void calculate(int64_t timestamp) override { - if (mWorldToHeadTimestamp.has_value()) { - const Pose3f worldToHead = mHeadPoseDriftCompensator.getOutput(); - mScreenHeadFusion.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead); - mModeSelector.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead); - } - + // Handle the screen first, since it might trigger a recentering of the head. if (mWorldToScreenTimestamp.has_value()) { - const Pose3f worldToLogicalScreen = mScreenPoseDriftCompensator.getOutput(); + const Pose3f worldToLogicalScreen = mScreenPoseBias.getOutput(); + bool screenStable = mScreenStillnessDetector.calculate(timestamp); + mModeSelector.setScreenStable(mWorldToScreenTimestamp.value(), screenStable); + // Whenever the screen is unstable, recenter the head pose. + if (!screenStable) { + recenter(true, false); + } mScreenHeadFusion.setWorldToScreenPose(mWorldToScreenTimestamp.value(), worldToLogicalScreen); } + // Handle head. + if (mWorldToHeadTimestamp.has_value()) { + Pose3f worldToHead = mHeadPoseBias.getOutput(); + // Auto-recenter. + if (mHeadStillnessDetector.calculate(timestamp)) { + recenter(true, false); + worldToHead = mHeadPoseBias.getOutput(); + } + + mScreenHeadFusion.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead); + mModeSelector.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead); + } + auto maybeScreenToHead = mScreenHeadFusion.calculate(); if (maybeScreenToHead.has_value()) { mModeSelector.setScreenToHeadPose(maybeScreenToHead->timestamp, @@ -113,10 +134,12 @@ class HeadTrackingProcessorImpl : public HeadTrackingProcessor { void recenter(bool recenterHead, bool recenterScreen) override { if (recenterHead) { - mHeadPoseDriftCompensator.recenter(); + mHeadPoseBias.recenter(); + mHeadStillnessDetector.reset(); } if (recenterScreen) { - mScreenPoseDriftCompensator.recenter(); + mScreenPoseBias.recenter(); + mScreenStillnessDetector.reset(); } // If a sensor being recentered is included in the current mode, apply rate limiting to @@ -138,8 +161,10 @@ class HeadTrackingProcessorImpl : public HeadTrackingProcessor { std::optional mWorldToHeadTimestamp; std::optional mWorldToScreenTimestamp; Pose3f mHeadToStagePose; - PoseDriftCompensator mHeadPoseDriftCompensator; - PoseDriftCompensator mScreenPoseDriftCompensator; + PoseBias mHeadPoseBias; + PoseBias mScreenPoseBias; + StillnessDetector mHeadStillnessDetector; + StillnessDetector mScreenStillnessDetector; ScreenHeadFusion mScreenHeadFusion; ModeSelector mModeSelector; PoseRateLimiter mRateLimiter; diff --git a/media/libheadtracking/ModeSelector-test.cpp b/media/libheadtracking/ModeSelector-test.cpp index 6247d84f77a8c5582e9a3507b350a62bcea915d5..a136e6bf18538ee239612f249fb239a45912eb73 100644 --- a/media/libheadtracking/ModeSelector-test.cpp +++ b/media/libheadtracking/ModeSelector-test.cpp @@ -44,6 +44,7 @@ TEST(ModeSelector, InitialWorldRelative) { ModeSelector selector(options, HeadTrackingMode::WORLD_RELATIVE); selector.setWorldToHeadPose(0, worldToHead); + selector.setScreenStable(0, true); selector.calculate(0); EXPECT_EQ(HeadTrackingMode::WORLD_RELATIVE, selector.getActualMode()); EXPECT_EQ(selector.getHeadToStagePose(), worldToHead.inverse()); @@ -69,15 +70,31 @@ TEST(ModeSelector, WorldRelative) { ModeSelector selector(options); selector.setScreenToStagePose(screenToStage); - selector.setDesiredMode(HeadTrackingMode::WORLD_RELATIVE); selector.setWorldToHeadPose(0, worldToHead); + selector.setScreenStable(0, true); selector.calculate(0); EXPECT_EQ(HeadTrackingMode::WORLD_RELATIVE, selector.getActualMode()); EXPECT_EQ(selector.getHeadToStagePose(), worldToHead.inverse() * screenToStage); } -TEST(ModeSelector, WorldRelativeStale) { +TEST(ModeSelector, WorldRelativeUnstable) { + const Pose3f worldToHead({1, 2, 3}, Quaternionf::UnitRandom()); + const Pose3f screenToStage({4, 5, 6}, Quaternionf::UnitRandom()); + + ModeSelector::Options options{.freshnessTimeout = 100}; + ModeSelector selector(options); + + selector.setScreenToStagePose(screenToStage); + selector.setDesiredMode(HeadTrackingMode::WORLD_RELATIVE); + selector.setWorldToHeadPose(0, worldToHead); + selector.setScreenStable(0, false); + selector.calculate(10); + EXPECT_EQ(HeadTrackingMode::STATIC, selector.getActualMode()); + EXPECT_EQ(selector.getHeadToStagePose(), screenToStage); +} + +TEST(ModeSelector, WorldRelativeStableStale) { const Pose3f worldToHead({1, 2, 3}, Quaternionf::UnitRandom()); const Pose3f screenToStage({4, 5, 6}, Quaternionf::UnitRandom()); @@ -85,7 +102,22 @@ TEST(ModeSelector, WorldRelativeStale) { ModeSelector selector(options); selector.setScreenToStagePose(screenToStage); + selector.setDesiredMode(HeadTrackingMode::WORLD_RELATIVE); + selector.setWorldToHeadPose(100, worldToHead); + selector.setScreenStable(0, true); + selector.calculate(101); + EXPECT_EQ(HeadTrackingMode::STATIC, selector.getActualMode()); + EXPECT_EQ(selector.getHeadToStagePose(), screenToStage); +} +TEST(ModeSelector, WorldRelativeStale) { + const Pose3f worldToHead({1, 2, 3}, Quaternionf::UnitRandom()); + const Pose3f screenToStage({4, 5, 6}, Quaternionf::UnitRandom()); + + ModeSelector::Options options{.freshnessTimeout = 100}; + ModeSelector selector(options); + + selector.setScreenToStagePose(screenToStage); selector.setDesiredMode(HeadTrackingMode::WORLD_RELATIVE); selector.setWorldToHeadPose(0, worldToHead); selector.calculate(101); @@ -101,7 +133,6 @@ TEST(ModeSelector, ScreenRelative) { ModeSelector selector(options); selector.setScreenToStagePose(screenToStage); - selector.setDesiredMode(HeadTrackingMode::SCREEN_RELATIVE); selector.setScreenToHeadPose(0, screenToHead); selector.calculate(0); @@ -118,10 +149,10 @@ TEST(ModeSelector, ScreenRelativeStaleToWorldRelative) { ModeSelector selector(options); selector.setScreenToStagePose(screenToStage); - selector.setDesiredMode(HeadTrackingMode::SCREEN_RELATIVE); selector.setScreenToHeadPose(0, screenToHead); selector.setWorldToHeadPose(50, worldToHead); + selector.setScreenStable(50, true); selector.calculate(101); EXPECT_EQ(HeadTrackingMode::WORLD_RELATIVE, selector.getActualMode()); EXPECT_EQ(selector.getHeadToStagePose(), worldToHead.inverse() * screenToStage); @@ -139,6 +170,7 @@ TEST(ModeSelector, ScreenRelativeInvalidToWorldRelative) { selector.setDesiredMode(HeadTrackingMode::SCREEN_RELATIVE); selector.setScreenToHeadPose(50, std::nullopt); selector.setWorldToHeadPose(50, worldToHead); + selector.setScreenStable(50, true); selector.calculate(101); EXPECT_EQ(HeadTrackingMode::WORLD_RELATIVE, selector.getActualMode()); EXPECT_EQ(selector.getHeadToStagePose(), worldToHead.inverse() * screenToStage); diff --git a/media/libheadtracking/ModeSelector.cpp b/media/libheadtracking/ModeSelector.cpp index 16e1712a1374b0a7e0a225d5a6a882b0d051dd9a..cb3a27fd5c103c1ddd5abe5ac31ba0611e4cf780 100644 --- a/media/libheadtracking/ModeSelector.cpp +++ b/media/libheadtracking/ModeSelector.cpp @@ -41,11 +41,18 @@ void ModeSelector::setWorldToHeadPose(int64_t timestamp, const Pose3f& worldToHe mWorldToHeadTimestamp = timestamp; } +void ModeSelector::setScreenStable(int64_t timestamp, bool stable) { + mScreenStable = stable; + mScreenStableTimestamp = timestamp; +} + void ModeSelector::calculateActualMode(int64_t timestamp) { bool isValidScreenToHead = mScreenToHead.has_value() && timestamp - mScreenToHeadTimestamp < mOptions.freshnessTimeout; bool isValidWorldToHead = mWorldToHead.has_value() && timestamp - mWorldToHeadTimestamp < mOptions.freshnessTimeout; + bool isValidScreenStable = mScreenStable.has_value() && + timestamp - mScreenStableTimestamp < mOptions.freshnessTimeout; HeadTrackingMode mode = mDesiredMode; @@ -58,7 +65,7 @@ void ModeSelector::calculateActualMode(int64_t timestamp) { // Optional downgrade from world-relative to static. if (mode == HeadTrackingMode::WORLD_RELATIVE) { - if (!isValidWorldToHead) { + if (!isValidWorldToHead || !isValidScreenStable || !mScreenStable.value()) { mode = HeadTrackingMode::STATIC; } } diff --git a/media/libheadtracking/ModeSelector.h b/media/libheadtracking/ModeSelector.h index 17a5142d0989c78f7cd4699620b7ff7d3871599d..e5370409ea4099917310f3cf5b86b39ea6dbd7bd 100644 --- a/media/libheadtracking/ModeSelector.h +++ b/media/libheadtracking/ModeSelector.h @@ -56,6 +56,7 @@ namespace media { * from screen-relative to world-relative. * - When we cannot get a fresh estimate of the world-to-head pose, we will fall back from * world-relative to static. + * - In world-relative mode, if the screen is unstable, we will fall back to static. * * All the timestamps used here are of arbitrary units and origin. They just need to be consistent * between all the calls and with the Options provided for determining freshness and rate limiting. @@ -91,6 +92,12 @@ class ModeSelector { */ void setWorldToHeadPose(int64_t timestamp, const Pose3f& worldToHead); + /** + * Set whether the screen is considered stable. + * The timestamp needs to reflect how fresh the sample is. + */ + void setScreenStable(int64_t timestamp, bool stable); + /** * Process all the previous inputs and update the outputs. */ @@ -116,6 +123,8 @@ class ModeSelector { int64_t mScreenToHeadTimestamp; std::optional mWorldToHead; int64_t mWorldToHeadTimestamp; + std::optional mScreenStable; + int64_t mScreenStableTimestamp; HeadTrackingMode mActualMode; Pose3f mHeadToStage; diff --git a/media/libheadtracking/Pose.cpp b/media/libheadtracking/Pose.cpp index 47241cef9d0d022d49ee0f656c9b2f813aa45012..ae39512473e8971a07f41011125353b0d672a5fb 100644 --- a/media/libheadtracking/Pose.cpp +++ b/media/libheadtracking/Pose.cpp @@ -43,7 +43,7 @@ std::tuple moveWithRateLimit(const Pose3f& from, const Pose3f& to, return {to, false}; } // Always rate limit if t is 0 (required to avoid division by 0). - if (t == 0) { + if (t == 0 || maxTranslationalVelocity == 0 || maxRotationalVelocity == 0) { return {from, true}; } diff --git a/media/libheadtracking/PoseBias-test.cpp b/media/libheadtracking/PoseBias-test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9f42a2ca0ebbe1060095242455c33df594de55d8 --- /dev/null +++ b/media/libheadtracking/PoseBias-test.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "PoseBias.h" +#include "QuaternionUtil.h" +#include "TestUtil.h" + +namespace android { +namespace media { +namespace { + +using Eigen::Quaternionf; +using Eigen::Vector3f; + +TEST(PoseBias, Initial) { + PoseBias bias; + EXPECT_EQ(bias.getOutput(), Pose3f()); +} + +TEST(PoseBias, Basic) { + Pose3f pose1({1, 2, 3}, Quaternionf::UnitRandom()); + Pose3f pose2({4, 5, 6}, Quaternionf::UnitRandom()); + + PoseBias bias; + bias.setInput(pose1); + EXPECT_EQ(pose1, bias.getOutput()); + bias.recenter(); + EXPECT_EQ(bias.getOutput(), Pose3f()); + bias.setInput(pose2); + EXPECT_EQ(bias.getOutput(), pose1.inverse() * pose2); + bias.recenter(); + EXPECT_EQ(bias.getOutput(), Pose3f()); +} + +} // namespace +} // namespace media +} // namespace android diff --git a/media/libheadtracking/PoseBias.cpp b/media/libheadtracking/PoseBias.cpp new file mode 100644 index 0000000000000000000000000000000000000000..33afca6453ecd69d315814f09054d094f5c82431 --- /dev/null +++ b/media/libheadtracking/PoseBias.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"){} + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PoseBias.h" + +namespace android { +namespace media { + +void PoseBias::setInput(const Pose3f& input) { + mLastWorldToInput = input; +} + +void PoseBias::recenter() { + mBiasToWorld = mLastWorldToInput.inverse(); +} + +Pose3f PoseBias::getOutput() const { + return mBiasToWorld * mLastWorldToInput; +} + +} // namespace media +} // namespace android diff --git a/media/libheadtracking/PoseBias.h b/media/libheadtracking/PoseBias.h new file mode 100644 index 0000000000000000000000000000000000000000..9acb49dee416aebd19b4c1deb5c0dc32d2909933 --- /dev/null +++ b/media/libheadtracking/PoseBias.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 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. + */ +#pragma once + +#include "media/Pose.h" + +namespace android { +namespace media { + +/** + * Biasing for a stream of poses. + * + * This filter takes a stream of poses and at any time during the stream, can change the frame of + * reference for the stream to be that of the last pose received, via the recenter() operation. + * + * Typical usage: + * PoseBias bias; + * + * bias.setInput(...); + * output = bias.getOutput(); + * bias.setInput(...); + * output = bias.getOutput(); + * bias.setInput(...); + * output = bias.getOutput(); + * bias.recenter(); // Reference frame is now equal to the last input. + * output = bias.getOutput(); // This is now the identity pose. + * + * There doesn't need to be a 1:1 correspondence between setInput() and getOutput() calls. + * The initial bias point is identity. + * + * This implementation is thread-compatible, but not thread-safe. + */ +class PoseBias { + public: + void setInput(const Pose3f& input); + + void recenter(); + + Pose3f getOutput() const; + + private: + Pose3f mLastWorldToInput; + Pose3f mBiasToWorld; +}; + +} // namespace media +} // namespace android diff --git a/media/libheadtracking/PoseProcessingGraph.png b/media/libheadtracking/PoseProcessingGraph.png index 03630680776ce94c60a75d74f51035064a84e86c..325b667f2fc095ebff547bf36e4e582e1a4b23a6 100644 Binary files a/media/libheadtracking/PoseProcessingGraph.png and b/media/libheadtracking/PoseProcessingGraph.png differ diff --git a/media/libheadtracking/README.md b/media/libheadtracking/README.md index 3d5b71a1951d8b6d1c25a81c800dfe20e69d6261..44f7bb2a502f9140e5cf94f25ffd459021e01a02 100644 --- a/media/libheadtracking/README.md +++ b/media/libheadtracking/README.md @@ -115,11 +115,9 @@ have a positive Z. #### World It is sometimes convenient to use an intermediate frame when dealing with -head-to-screen transforms. The “world” frame is an arbitrary frame of reference -in the physical world, relative to which we can measure the head pose and screen -pose. In (very common) cases when we can’t establish such an absolute frame, we -can take each measurement relative to a separate, arbitrary frame and high-pass -the result. +head-to-screen transforms. The “world” frame is a frame of reference in the +physical world, relative to which we can measure the head pose and screen pose. +It is arbitrary, but expected to be stable (fixed). ## Processing Description @@ -133,15 +131,10 @@ the outputs. The Predictor block gets pose + twist (pose derivative) and extrapolates to obtain a predicted head pose (w/ given latency). -### Drift / Bias Compensator +### Bias -The Drift / Bias Compensator blocks serve two purposes: - -- Compensate for floating reference axes by applying a high-pass filter, which - slowly pulls the pose toward identity. -- Establish the reference frame for the poses by having the ability to set the - current pose as the reference for future poses (recentering). Effectively, - this is resetting the filter state to identity. +The Bias blocks establish the reference frame for the poses by having the +ability to set the current pose as the reference for future poses (recentering). ### Orientation Compensation @@ -157,6 +150,14 @@ and estimates the pose of the head relative to the screen. Optionally, this module may indicate that the user is likely not in front of the screen via the “valid” output. +### Stillness Detector + +The stillness detector blocks detect when their incoming pose stream has been +stable for a given amount of time (allowing for a configurable amount of error). +When the head is considered still, we would trigger a recenter operation +(“auto-recentering”) and when the screen is considered not still, the mode +selector would use this information to force static mode. + ### Mode Selector The Mode Selector block aggregates the various sources of pose information into @@ -168,7 +169,8 @@ The actual mode may diverge from the desired mode. It is determined as follows: - If the desired mode is static, the actual mode is static. - If the desired mode is world-relative: - - If head poses are fresh, the actual mode is world-relative. + - If head and screen poses are fresh and the screen is stable (stillness + detector output is true), the actual mode is world-relative. - Otherwise the actual mode is static. - If the desired mode is screen-relative: - If head and screen poses are fresh and the ‘valid’ signal is asserted, the diff --git a/media/libheadtracking/SensorPoseProvider.cpp b/media/libheadtracking/SensorPoseProvider.cpp index ec5e1ec6ab909dc00d3f0e82946020bfded74467..8ebaf6edb25e8a11f1a75d6dc33297bb0440667a 100644 --- a/media/libheadtracking/SensorPoseProvider.cpp +++ b/media/libheadtracking/SensorPoseProvider.cpp @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -133,14 +132,14 @@ class SensorPoseProviderImpl : public SensorPoseProvider { { std::lock_guard lock(mMutex); - mEnabledSensorFormats.emplace(sensor, format); + mEnabledSensorsExtra.emplace(sensor, SensorExtra{ .format = format }); } // Enable the sensor. if (mQueue->enableSensor(sensor, samplingPeriod.count(), 0, 0)) { ALOGE("Failed to enable sensor"); std::lock_guard lock(mMutex); - mEnabledSensorFormats.erase(sensor); + mEnabledSensorsExtra.erase(sensor); return false; } @@ -151,14 +150,14 @@ class SensorPoseProviderImpl : public SensorPoseProvider { void stopSensor(int handle) override { mEnabledSensors.erase(handle); std::lock_guard lock(mMutex); - mEnabledSensorFormats.erase(handle); + mEnabledSensorsExtra.erase(handle); } private: enum DataFormat { kUnknown, kQuaternion, - kRotationVectorsAndFlags, + kRotationVectorsAndDiscontinuityCount, }; struct PoseEvent { @@ -167,13 +166,18 @@ class SensorPoseProviderImpl : public SensorPoseProvider { bool isNewReference; }; + struct SensorExtra { + DataFormat format; + std::optional discontinuityCount; + }; + sp mLooper; Listener* const mListener; SensorManager* const mSensorManager; std::thread mThread; std::mutex mMutex; std::map mEnabledSensors; - std::map mEnabledSensorFormats GUARDED_BY(mMutex); + std::map mEnabledSensorsExtra GUARDED_BY(mMutex); sp mQueue; // We must do some of the initialization operations on the worker thread, because the API relies @@ -248,17 +252,16 @@ class SensorPoseProviderImpl : public SensorPoseProvider { } void handleEvent(const ASensorEvent& event) { - DataFormat format; + PoseEvent value; { std::lock_guard lock(mMutex); - auto iter = mEnabledSensorFormats.find(event.sensor); - if (iter == mEnabledSensorFormats.end()) { + auto iter = mEnabledSensorsExtra.find(event.sensor); + if (iter == mEnabledSensorsExtra.end()) { // This can happen if we have any pending events shortly after stopping. return; } - format = iter->second; + value = parseEvent(event, iter->second.format, &iter->second.discontinuityCount); } - auto value = parseEvent(event, format); mListener->onPose(event.timestamp, event.sensor, value.pose, value.twist, value.isNewReference); } @@ -274,14 +277,14 @@ class SensorPoseProviderImpl : public SensorPoseProvider { return DataFormat::kQuaternion; } - if (sensor->getStringType() == "com.google.hardware.sensor.hid_dynamic.headtracker") { - return DataFormat::kRotationVectorsAndFlags; + if (sensor->getType() == ASENSOR_TYPE_HEAD_TRACKER) { + return DataFormat::kRotationVectorsAndDiscontinuityCount; } return DataFormat::kUnknown; } - std::optional getSensorByHandle(int32_t handle) { + std::optional getSensorByHandle(int32_t handle) override { const Sensor* const* list; ssize_t size; @@ -313,8 +316,8 @@ class SensorPoseProviderImpl : public SensorPoseProvider { return std::nullopt; } - static PoseEvent parseEvent(const ASensorEvent& event, DataFormat format) { - // TODO(ytai): Add more types. + static PoseEvent parseEvent(const ASensorEvent& event, DataFormat format, + std::optional* discontinutyCount) { switch (format) { case DataFormat::kQuaternion: { Eigen::Quaternionf quat(event.data[3], event.data[0], event.data[1], event.data[2]); @@ -323,19 +326,19 @@ class SensorPoseProviderImpl : public SensorPoseProvider { return PoseEvent{Pose3f(quat), std::optional(), false}; } - case DataFormat::kRotationVectorsAndFlags: { - // Custom sensor, assumed to contain: - // 3 floats representing orientation as a rotation vector (in rad). - // 3 floats representing angular velocity as a rotation vector (in rad/s). - // 1 uint32_t of flags, where: - // - LSb is '1' iff the given sample is the first one in a new frame of reference. - // - The rest of the bits are reserved for future use. - Eigen::Vector3f rotation = {event.data[0], event.data[1], event.data[2]}; - Eigen::Vector3f twist = {event.data[3], event.data[4], event.data[5]}; + case DataFormat::kRotationVectorsAndDiscontinuityCount: { + Eigen::Vector3f rotation = {event.head_tracker.rx, event.head_tracker.ry, + event.head_tracker.rz}; + Eigen::Vector3f twist = {event.head_tracker.vx, event.head_tracker.vy, + event.head_tracker.vz}; Eigen::Quaternionf quat = rotationVectorToQuaternion(rotation); - uint32_t flags = *reinterpret_cast(&event.data[6]); + bool isNewReference = + !discontinutyCount->has_value() || + discontinutyCount->value() != event.head_tracker.discontinuity_count; + *discontinutyCount = event.head_tracker.discontinuity_count; + return PoseEvent{Pose3f(quat), Twist3f(Eigen::Vector3f::Zero(), twist), - (flags & (1 << 0)) != 0}; + isNewReference}; } default: diff --git a/media/libheadtracking/StillnessDetector-test.cpp b/media/libheadtracking/StillnessDetector-test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b6cd4793883118ccf0247b72e6563819dc20f8bf --- /dev/null +++ b/media/libheadtracking/StillnessDetector-test.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "QuaternionUtil.h" +#include "StillnessDetector.h" +#include "TestUtil.h" + +namespace android { +namespace media { +namespace { + +using Eigen::Quaternionf; +using Eigen::Vector3f; +using Options = StillnessDetector::Options; + +class StillnessDetectorTest : public testing::TestWithParam { + public: + void SetUp() override { mDefaultValue = GetParam(); } + + protected: + bool mDefaultValue; +}; + +TEST_P(StillnessDetectorTest, Still) { + StillnessDetector detector(Options{.defaultValue = mDefaultValue, + .windowDuration = 1000, + .translationalThreshold = 1, + .rotationalThreshold = 0.05}); + + const Pose3f baseline(Vector3f{1, 2, 3}, Quaternionf::UnitRandom()); + const Pose3f withinThreshold = + baseline * Pose3f(Vector3f(0.3, -0.3, 0), rotateX(0.01) * rotateY(-0.01)); + + EXPECT_EQ(mDefaultValue, detector.calculate(0)); + detector.setInput(0, baseline); + EXPECT_EQ(mDefaultValue, detector.calculate(0)); + detector.setInput(300, withinThreshold); + EXPECT_EQ(mDefaultValue, detector.calculate(300)); + detector.setInput(600, baseline); + EXPECT_EQ(mDefaultValue, detector.calculate(600)); + detector.setInput(999, withinThreshold); + EXPECT_EQ(mDefaultValue, detector.calculate(999)); + detector.setInput(1000, baseline); + EXPECT_TRUE(detector.calculate(1000)); +} + +TEST_P(StillnessDetectorTest, ZeroDuration) { + StillnessDetector detector(Options{.defaultValue = mDefaultValue, .windowDuration = 0}); + EXPECT_TRUE(detector.calculate(0)); + EXPECT_TRUE(detector.calculate(1000)); +} + +TEST_P(StillnessDetectorTest, NotStillTranslation) { + StillnessDetector detector(Options{.defaultValue = mDefaultValue, + .windowDuration = 1000, + .translationalThreshold = 1, + .rotationalThreshold = 0.05}); + + const Pose3f baseline(Vector3f{1, 2, 3}, Quaternionf::UnitRandom()); + const Pose3f withinThreshold = + baseline * Pose3f(Vector3f(0.3, -0.3, 0), rotateX(0.01) * rotateY(-0.01)); + const Pose3f outsideThreshold = baseline * Pose3f(Vector3f(1, 1, 0)); + + EXPECT_EQ(mDefaultValue, detector.calculate(0)); + detector.setInput(0, baseline); + EXPECT_EQ(mDefaultValue, detector.calculate(0)); + detector.setInput(300, outsideThreshold); + EXPECT_EQ(mDefaultValue, detector.calculate(300)); + detector.setInput(600, baseline); + EXPECT_EQ(mDefaultValue, detector.calculate(600)); + detector.setInput(1299, withinThreshold); + EXPECT_FALSE(detector.calculate(1299)); + detector.setInput(1300, baseline); + EXPECT_TRUE(detector.calculate(1300)); +} + +TEST_P(StillnessDetectorTest, NotStillRotation) { + StillnessDetector detector(Options{.defaultValue = mDefaultValue, + .windowDuration = 1000, + .translationalThreshold = 1, + .rotationalThreshold = 0.05}); + + const Pose3f baseline(Vector3f{1, 2, 3}, Quaternionf::UnitRandom()); + const Pose3f withinThreshold = + baseline * Pose3f(Vector3f(0.3, -0.3, 0), rotateX(0.03) * rotateY(-0.03)); + const Pose3f outsideThreshold = baseline * Pose3f(rotateZ(0.06)); + + EXPECT_EQ(mDefaultValue, detector.calculate(0)); + detector.setInput(0, baseline); + EXPECT_EQ(mDefaultValue, detector.calculate(0)); + detector.setInput(300, outsideThreshold); + EXPECT_EQ(mDefaultValue, detector.calculate(300)); + detector.setInput(600, baseline); + EXPECT_EQ(mDefaultValue, detector.calculate(600)); + detector.setInput(1299, withinThreshold); + EXPECT_FALSE(detector.calculate(1299)); + detector.setInput(1300, baseline); + EXPECT_TRUE(detector.calculate(1300)); +} + +TEST_P(StillnessDetectorTest, Suppression) { + StillnessDetector detector(Options{.defaultValue = mDefaultValue, + .windowDuration = 1000, + .translationalThreshold = 1, + .rotationalThreshold = 0.05}); + + const Pose3f baseline(Vector3f{1, 2, 3}, Quaternionf::UnitRandom()); + const Pose3f outsideThreshold = baseline * Pose3f(Vector3f(1.1, 0, 0)); + const Pose3f middlePoint = baseline * Pose3f(Vector3f(0.55, 0, 0)); + + detector.setInput(0, baseline); + detector.setInput(1000, baseline); + EXPECT_TRUE(detector.calculate(1000)); + detector.setInput(1100, outsideThreshold); + EXPECT_FALSE(detector.calculate(1100)); + detector.setInput(1500, middlePoint); + EXPECT_FALSE(detector.calculate(1500)); + EXPECT_FALSE(detector.calculate(1999)); + EXPECT_TRUE(detector.calculate(2000)); +} + +TEST_P(StillnessDetectorTest, Reset) { + StillnessDetector detector(Options{.defaultValue = mDefaultValue, + .windowDuration = 1000, + .translationalThreshold = 1, + .rotationalThreshold = 0.05}); + + const Pose3f baseline(Vector3f{1, 2, 3}, Quaternionf::UnitRandom()); + const Pose3f withinThreshold = + baseline * Pose3f(Vector3f(0.3, -0.3, 0), rotateX(0.01) * rotateY(-0.01)); + EXPECT_EQ(mDefaultValue, detector.calculate(0)); + detector.setInput(300, baseline); + EXPECT_EQ(mDefaultValue, detector.calculate(300)); + detector.reset(); + detector.setInput(600, baseline); + EXPECT_EQ(mDefaultValue, detector.calculate(600)); + detector.setInput(900, withinThreshold); + EXPECT_EQ(mDefaultValue, detector.calculate(900)); + detector.setInput(1200, baseline); + EXPECT_EQ(mDefaultValue, detector.calculate(1200)); + detector.setInput(1599, withinThreshold); + EXPECT_EQ(mDefaultValue, detector.calculate(1599)); + detector.setInput(1600, baseline); + EXPECT_TRUE(detector.calculate(1600)); +} + +INSTANTIATE_TEST_SUITE_P(StillnessDetectorTestParametrized, StillnessDetectorTest, + testing::Values(false, true)); + +} // namespace +} // namespace media +} // namespace android diff --git a/media/libheadtracking/StillnessDetector.cpp b/media/libheadtracking/StillnessDetector.cpp new file mode 100644 index 0000000000000000000000000000000000000000..be7c89340b4d2d2b4eee89c3708bc1fe813aba50 --- /dev/null +++ b/media/libheadtracking/StillnessDetector.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StillnessDetector.h" + +namespace android { +namespace media { + +StillnessDetector::StillnessDetector(const Options& options) + : mOptions(options), mCosHalfRotationalThreshold(cos(mOptions.rotationalThreshold / 2)) {} + +void StillnessDetector::reset() { + mFifo.clear(); + mWindowFull = false; + mSuppressionDeadline.reset(); +} + +void StillnessDetector::setInput(int64_t timestamp, const Pose3f& input) { + mFifo.push_back(TimestampedPose{timestamp, input}); + discardOld(timestamp); +} + +bool StillnessDetector::calculate(int64_t timestamp) { + discardOld(timestamp); + + // Check whether all the poses in the queue are in the proximity of the new one. We want to do + // this before checking the overriding conditions below, in order to update the suppression + // deadline correctly. We always go from end to start, to find the most recent pose that + // violated stillness and update the suppression deadline if it has not been set or if the new + // one ends after the current one. + bool moved = false; + + if (!mFifo.empty()) { + for (auto iter = mFifo.rbegin() + 1; iter != mFifo.rend(); ++iter) { + const auto& event = *iter; + if (!areNear(event.pose, mFifo.back().pose)) { + // Enable suppression for the duration of the window. + int64_t deadline = event.timestamp + mOptions.windowDuration; + if (!mSuppressionDeadline.has_value() || mSuppressionDeadline.value() < deadline) { + mSuppressionDeadline = deadline; + } + moved = true; + break; + } + } + } + + // If the window has not been full, return the default value. + if (!mWindowFull) { + return mOptions.defaultValue; + } + + // Force "in motion" while the suppression deadline is active. + if (mSuppressionDeadline.has_value()) { + return false; + } + + return !moved; +} + +void StillnessDetector::discardOld(int64_t timestamp) { + // Handle the special case of the window duration being zero (always considered full). + if (mOptions.windowDuration == 0) { + mFifo.clear(); + mWindowFull = true; + } + + // Remove any events from the queue that are older than the window. If there were any such + // events we consider the window full. + const int64_t windowStart = timestamp - mOptions.windowDuration; + while (!mFifo.empty() && mFifo.front().timestamp <= windowStart) { + mWindowFull = true; + mFifo.pop_front(); + } + + // Expire the suppression deadline. + if (mSuppressionDeadline.has_value() && mSuppressionDeadline <= timestamp) { + mSuppressionDeadline.reset(); + } +} + +bool StillnessDetector::areNear(const Pose3f& pose1, const Pose3f& pose2) const { + // Check translation. We use the L1 norm to reduce computational load on expense of accuracy. + // The L1 norm is an upper bound for the actual (L2) norm, so this approach will err on the side + // of "not near". + if ((pose1.translation() - pose2.translation()).lpNorm<1>() > mOptions.translationalThreshold) { + return false; + } + + // Check orientation. + // The angle x between the quaternions is greater than that threshold iff + // cos(x/2) < cos(threshold/2). + // cos(x/2) can be efficiently calculated as the dot product of both quaternions. + if (pose1.rotation().dot(pose2.rotation()) < mCosHalfRotationalThreshold) { + return false; + } + + return true; +} + +} // namespace media +} // namespace android diff --git a/media/libheadtracking/StillnessDetector.h b/media/libheadtracking/StillnessDetector.h new file mode 100644 index 0000000000000000000000000000000000000000..ee4b2d87219f561b375a947d9633f8495f8f7b03 --- /dev/null +++ b/media/libheadtracking/StillnessDetector.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2021 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. + */ +#pragma once + +#include + +#include + +namespace android { +namespace media { + +/** + * Given a stream of poses, determines if the pose is stable ("still"). + * Stillness is defined as all poses in the recent history ("window") being near the most recent + * sample. + * + * Typical usage: + * + * StillnessDetector detector(StilnessDetector::Options{...}); + * + * while (...) { + * detector.setInput(timestamp, pose); + * bool still = detector.calculate(timestamp); + * } + * + * The detection is not considered reliable until a sufficient number of samples has been provided + * for an initial fill-up of the window. During that time, the detector will return whatever default + * value has been configured. + * The reset() method can be used to empty the window again and get back to this initial state. + * In the special case of the window size being 0, the state will always be considered "still". + */ +class StillnessDetector { + public: + /** + * Configuration options for the detector. + */ + struct Options { + /** + * During the initial fill of the window, should we consider the state still? + */ + bool defaultValue; + /** + * How long is the window, in ticks. The special value of 0 indicates that the stream is + * always considered still. + */ + int64_t windowDuration; + /** + * How much of a translational deviation from the target (in meters) is considered motion. + * This is an approximate quantity - the actual threshold might be a little different as we + * trade-off accuracy with computational efficiency. + */ + float translationalThreshold; + /** + * How much of a rotational deviation from the target (in radians) is considered motion. + * This is an approximate quantity - the actual threshold might be a little different as we + * trade-off accuracy with computational efficiency. + */ + float rotationalThreshold; + }; + + /** Ctor. */ + explicit StillnessDetector(const Options& options); + + /** Clear the window. */ + void reset(); + /** Push a new sample. */ + void setInput(int64_t timestamp, const Pose3f& input); + /** Calculate whether the stream is still at the given timestamp. */ + bool calculate(int64_t timestamp); + + private: + struct TimestampedPose { + int64_t timestamp; + Pose3f pose; + }; + + const Options mOptions; + // Precalculated cos(mOptions.rotationalThreshold / 2) + const float mCosHalfRotationalThreshold; + std::deque mFifo; + bool mWindowFull = false; + // As soon as motion is detected, this will be set for the time of detection + window duration, + // and during this time we will always consider outselves in motion without checking. This is + // used for hyteresis purposes, since because of the approximate method we use for determining + // stillness, we may toggle back and forth at a rate faster than the window side. + std::optional mSuppressionDeadline; + + bool areNear(const Pose3f& pose1, const Pose3f& pose2) const; + void discardOld(int64_t timestamp); +}; + +} // namespace media +} // namespace android diff --git a/media/libheadtracking/include/media/HeadTrackingProcessor.h b/media/libheadtracking/include/media/HeadTrackingProcessor.h index 9fea273611dfd13bc2d49ac3e46337ab2e697b8e..1744be35b97ef291065907e7ff0918e8a537641f 100644 --- a/media/libheadtracking/include/media/HeadTrackingProcessor.h +++ b/media/libheadtracking/include/media/HeadTrackingProcessor.h @@ -38,10 +38,14 @@ class HeadTrackingProcessor { struct Options { float maxTranslationalVelocity = std::numeric_limits::infinity(); float maxRotationalVelocity = std::numeric_limits::infinity(); - float translationalDriftTimeConstant = std::numeric_limits::infinity(); - float rotationalDriftTimeConstant = std::numeric_limits::infinity(); int64_t freshnessTimeout = std::numeric_limits::max(); float predictionDuration = 0; + int64_t autoRecenterWindowDuration = std::numeric_limits::max(); + float autoRecenterTranslationalThreshold = std::numeric_limits::infinity(); + float autoRecenterRotationalThreshold = std::numeric_limits::infinity(); + int64_t screenStillnessWindowDuration = 0; + float screenStillnessTranslationalThreshold = std::numeric_limits::infinity(); + float screenStillnessRotationalThreshold = std::numeric_limits::infinity(); }; /** Sets the desired head-tracking mode. */ diff --git a/media/libheadtracking/include/media/SensorPoseProvider.h b/media/libheadtracking/include/media/SensorPoseProvider.h index d2a6b771e7c3b492b21f79f0791e14cce464c581..0f42074f5172474589e3660ed9671a911bc85b13 100644 --- a/media/libheadtracking/include/media/SensorPoseProvider.h +++ b/media/libheadtracking/include/media/SensorPoseProvider.h @@ -20,6 +20,7 @@ #include #include +#include #include "Pose.h" #include "Twist.h" @@ -91,6 +92,14 @@ class SensorPoseProvider { * @param handle The sensor handle, as provided to startSensor(). */ virtual void stopSensor(int32_t handle) = 0; + + /** + * Returns the sensor or nullopt if it does not exist. + * + * The Sensor object has const methods that can be used to + * discover properties of the sensor. + */ + virtual std::optional getSensorByHandle(int32_t handle) = 0; }; } // namespace media diff --git a/media/libheadtracking/include/media/Twist.h b/media/libheadtracking/include/media/Twist.h index e2fc203e90bc32d4425ec443bb497c8c715092ce..291cea364daa0beaff5326bbb57df28bac9d954d 100644 --- a/media/libheadtracking/include/media/Twist.h +++ b/media/libheadtracking/include/media/Twist.h @@ -56,6 +56,16 @@ class Twist3f { mRotationalVelocity.isApprox(other.mRotationalVelocity, prec); } + template + Twist3f operator*(const T& s) const { + return Twist3f(mTranslationalVelocity * s, mRotationalVelocity * s); + } + + template + Twist3f operator/(const T& s) const { + return Twist3f(mTranslationalVelocity / s, mRotationalVelocity / s); + } + private: Eigen::Vector3f mTranslationalVelocity; Eigen::Vector3f mRotationalVelocity; diff --git a/media/libheif/Android.bp b/media/libheif/Android.bp index 6a3427e6e12ffc678811b20a2473a6ac5c7a650f..55ba61ac3f820bc6af30feb9f0323024988dda1e 100644 --- a/media/libheif/Android.bp +++ b/media/libheif/Android.bp @@ -26,7 +26,5 @@ cc_library_shared { "-Wall", ], - include_dirs: [], - export_include_dirs: ["include"], } diff --git a/media/libheif/HeifDecoderImpl.cpp b/media/libheif/HeifDecoderImpl.cpp index 50f1bf2b58a8b00b0cd9ec3f448412258029e014..1b8656d12b3b5708045af4faaed2ca0ddcf34345 100644 --- a/media/libheif/HeifDecoderImpl.cpp +++ b/media/libheif/HeifDecoderImpl.cpp @@ -47,6 +47,7 @@ void initFrameInfo(HeifFrameInfo *info, const VideoFrame *videoFrame) { info->mRotationAngle = videoFrame->mRotationAngle; info->mBytesPerPixel = videoFrame->mBytesPerPixel; info->mDurationUs = videoFrame->mDurationUs; + info->mBitDepth = videoFrame->mBitDepth; if (videoFrame->mIccSize > 0) { info->mIccData.assign( videoFrame->getFlattenedIccData(), @@ -377,13 +378,14 @@ bool HeifDecoderImpl::reinit(HeifFrameInfo* frameInfo) { // issue (e.g. by copying). VideoFrame* videoFrame = static_cast(sharedMem->unsecurePointer()); - ALOGV("Image dimension %dx%d, display %dx%d, angle %d, iccSize %d", + ALOGV("Image dimension %dx%d, display %dx%d, angle %d, iccSize %d, bitDepth %d", videoFrame->mWidth, videoFrame->mHeight, videoFrame->mDisplayWidth, videoFrame->mDisplayHeight, videoFrame->mRotationAngle, - videoFrame->mIccSize); + videoFrame->mIccSize, + videoFrame->mBitDepth); initFrameInfo(&mImageInfo, videoFrame); @@ -492,6 +494,11 @@ bool HeifDecoderImpl::setOutputColor(HeifColorFormat heifColor) { mOutputColor = HAL_PIXEL_FORMAT_BGRA_8888; break; } + case kHeifColorFormat_RGBA_1010102: + { + mOutputColor = HAL_PIXEL_FORMAT_RGBA_1010102; + break; + } default: ALOGE("Unsupported output color format %d", heifColor); return false; @@ -724,4 +731,13 @@ size_t HeifDecoderImpl::skipScanlines(size_t count) { return (mCurScanline > oldScanline) ? (mCurScanline - oldScanline) : 0; } +uint32_t HeifDecoderImpl::getColorDepth() { + HeifFrameInfo* info = &mImageInfo; + if (info != nullptr) { + return mImageInfo.mBitDepth; + } + + return 0; +} + } // namespace android diff --git a/media/libheif/HeifDecoderImpl.h b/media/libheif/HeifDecoderImpl.h index 2b9c710282950a5f9ba88bc80f6575849ef301d7..86a8628b16feb647004f7f24c87718b65befe59b 100644 --- a/media/libheif/HeifDecoderImpl.h +++ b/media/libheif/HeifDecoderImpl.h @@ -54,6 +54,8 @@ public: size_t skipScanlines(size_t count) override; + uint32_t getColorDepth() override; + private: struct DecodeThread; diff --git a/media/libheif/include/HeifDecoderAPI.h b/media/libheif/include/HeifDecoderAPI.h index 90736721c3df2cb82a870548bb79df156a030686..dc124860c3ef0a8491ec94f2045f0a1d823bfbc5 100644 --- a/media/libheif/include/HeifDecoderAPI.h +++ b/media/libheif/include/HeifDecoderAPI.h @@ -23,9 +23,10 @@ * The output color pixel format of heif decoder. */ typedef enum { - kHeifColorFormat_RGB565 = 0, - kHeifColorFormat_RGBA_8888 = 1, - kHeifColorFormat_BGRA_8888 = 2, + kHeifColorFormat_RGB565 = 0, + kHeifColorFormat_RGBA_8888 = 1, + kHeifColorFormat_BGRA_8888 = 2, + kHeifColorFormat_RGBA_1010102 = 3, } HeifColorFormat; /* @@ -45,7 +46,8 @@ struct HeifFrameInfo { uint32_t mHeight; int32_t mRotationAngle; // Rotation angle, clockwise, should be multiple of 90 uint32_t mBytesPerPixel; // Number of bytes for one pixel - int64_t mDurationUs; // Duration of the frame in us + int64_t mDurationUs; // Duration of the frame in us + uint32_t mBitDepth; // Number of bits for each of the R/G/B channels std::vector mIccData; // ICC data array }; @@ -161,6 +163,11 @@ struct HeifDecoder { */ virtual size_t skipScanlines(size_t count) = 0; + /* + * Returns color depth in bits for each of the R/G/B channels. + */ + virtual uint32_t getColorDepth() = 0; + private: HeifDecoder(const HeifFrameInfo&) = delete; HeifDecoder& operator=(const HeifFrameInfo&) = delete; diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp index 9c1b563f6e9843988352d506da7fb207bb567272..2dd5784c3fbb08cfb9940718995e86abfc50d1f8 100644 --- a/media/libmedia/Android.bp +++ b/media/libmedia/Android.bp @@ -19,6 +19,10 @@ cc_library_headers { name: "libmedia_headers", vendor_available: true, min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + ], export_include_dirs: ["include"], header_libs: [ @@ -214,6 +218,11 @@ cc_library_static { name: "libmedia_midiiowrapper", min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.media", + ], + srcs: ["MidiIoWrapper.cpp"], @@ -347,6 +356,7 @@ cc_library { shared_libs: [ "android.hidl.token@1.0-utils", + "android.media.audio.common.types-V1-cpp", "audioclient-types-aidl-cpp", "av-types-aidl-cpp", "liblog", @@ -440,6 +450,6 @@ cc_library_static { apex_available: [ "//apex_available:platform", - "com.android.media" + "com.android.media", ], } diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp index c89c023069d7372e538f4e792309130622dcd6c5..c9f361e075e86b3e236cf2b97341722ba4f178bb 100644 --- a/media/libmedia/IMediaPlayer.cpp +++ b/media/libmedia/IMediaPlayer.cpp @@ -17,7 +17,6 @@ #include #include -#include #include #include diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp index 67d33fad5b0c5b12f73318eafde5e356c8652b2a..85768bda1f3844c92026f2e5bc5a0e39e241a2d3 100644 --- a/media/libmedia/MediaProfiles.cpp +++ b/media/libmedia/MediaProfiles.cpp @@ -20,12 +20,14 @@ #define LOG_TAG "MediaProfiles" #include +#include #include #include #include #include #include #include +#include #include #include @@ -86,7 +88,24 @@ const MediaProfiles::NameToTagMap MediaProfiles::sVideoEncoderNameMap[] = { {"h263", VIDEO_ENCODER_H263}, {"h264", VIDEO_ENCODER_H264}, {"m4v", VIDEO_ENCODER_MPEG_4_SP}, - {"hevc", VIDEO_ENCODER_HEVC} + {"vp8", VIDEO_ENCODER_VP8}, + {"hevc", VIDEO_ENCODER_HEVC}, + {"vp9", VIDEO_ENCODER_VP9}, + {"dolbyvision", VIDEO_ENCODER_DOLBY_VISION}, +}; + +const MediaProfiles::NameToTagMap MediaProfiles::sChromaSubsamplingNameMap[] = { + {"yuv 4:2:0", CHROMA_SUBSAMPLING_YUV_420}, + {"yuv 4:2:2", CHROMA_SUBSAMPLING_YUV_422}, + {"yuv 4:4:4", CHROMA_SUBSAMPLING_YUV_444}, +}; + +const MediaProfiles::NameToTagMap MediaProfiles::sHdrFormatNameMap[] = { + {"sdr", HDR_FORMAT_NONE}, + {"hlg", HDR_FORMAT_HLG}, + {"hdr10", HDR_FORMAT_HDR10}, + {"hdr10+", HDR_FORMAT_HDR10PLUS}, + {"dolbyvision", HDR_FORMAT_DOLBY_VISION}, }; const MediaProfiles::NameToTagMap MediaProfiles::sAudioEncoderNameMap[] = { @@ -164,12 +183,18 @@ const MediaProfiles::NameToTagMap MediaProfiles::sCamcorderQualityNameMap[] = { MediaProfiles::logVideoCodec(const MediaProfiles::VideoCodec& codec UNUSED) { ALOGV("video codec:"); - ALOGV("codec = %d", codec.mCodec); + ALOGV("codec = %d (%s)", codec.mCodec, + findNameForTag(sVideoEncoderNameMap, NELEM(sVideoEncoderNameMap), codec.mCodec)); ALOGV("bit rate: %d", codec.mBitRate); ALOGV("frame width: %d", codec.mFrameWidth); ALOGV("frame height: %d", codec.mFrameHeight); ALOGV("frame rate: %d", codec.mFrameRate); ALOGV("profile: %d", codec.mProfile); + ALOGV("chroma: %s", findNameForTag(sChromaSubsamplingNameMap, NELEM(sChromaSubsamplingNameMap), + codec.mChromaSubsampling)); + ALOGV("bit depth: %d", codec.mBitDepth); + ALOGV("hdr format: %s", findNameForTag(sHdrFormatNameMap, NELEM(sHdrFormatNameMap), + codec.mHdrFormat)); } /*static*/ void @@ -232,6 +257,155 @@ MediaProfiles::findTagForName(const MediaProfiles::NameToTagMap *map, size_t nMa return tag; } +/*static*/ const char * +MediaProfiles::findNameForTag( + const MediaProfiles::NameToTagMap *map, size_t nMappings, int tag, const char *def_) +{ + for (size_t i = 0; i < nMappings; ++i) { + if (map[i].tag == tag) { + return map[i].name; + } + } + return def_; +} + +/*static*/ bool +MediaProfiles::detectAdvancedVideoProfile( + video_encoder codec, int profile, + chroma_subsampling *chroma, int *bitDepth, hdr_format *hdr) +{ + // default values + *chroma = CHROMA_SUBSAMPLING_YUV_420; + *bitDepth = 8; + *hdr = HDR_FORMAT_NONE; + + switch (codec) { + case VIDEO_ENCODER_H263: + case VIDEO_ENCODER_MPEG_4_SP: + case VIDEO_ENCODER_VP8: + // these are always 4:2:0 SDR 8-bit + return true; + + case VIDEO_ENCODER_H264: + switch (profile) { + case AVCProfileBaseline: + case AVCProfileConstrainedBaseline: + case AVCProfileMain: + case AVCProfileExtended: + case AVCProfileHigh: + case AVCProfileConstrainedHigh: + return true; + case AVCProfileHigh10: + // returning false here as this could be an HLG stream + *bitDepth = 10; + return false; + case AVCProfileHigh422: + *chroma = CHROMA_SUBSAMPLING_YUV_422; + // returning false here as bit-depth could be 8 or 10 + return false; + case AVCProfileHigh444: + *chroma = CHROMA_SUBSAMPLING_YUV_444; + // returning false here as bit-depth could be 8 or 10 + return false; + default: + return false; + } + // flow does not get here + + case VIDEO_ENCODER_HEVC: + switch (profile) { + case HEVCProfileMain: + return true; + case HEVCProfileMain10: + *bitDepth = 10; + // returning false here as this could be an HLG stream + return false; + case HEVCProfileMain10HDR10: + *bitDepth = 10; + *hdr = HDR_FORMAT_HDR10; + return true; + case HEVCProfileMain10HDR10Plus: + *bitDepth = 10; + *hdr = HDR_FORMAT_HDR10PLUS; + return true; + default: + return false; + } + // flow does not get here + + case VIDEO_ENCODER_VP9: + switch (profile) { + case VP9Profile0: + return true; + case VP9Profile2: + // this is always 10-bit on Android */ + *bitDepth = 10; + // returning false here as this could be an HLG stream + return false; + case VP9Profile2HDR: + // this is always 10-bit on Android */ + *bitDepth = 10; + *hdr = HDR_FORMAT_HDR10; + return true; + case VP9Profile2HDR10Plus: + *bitDepth = 10; + *hdr = HDR_FORMAT_HDR10PLUS; + return true; + default: + return false; + } + // flow does not get here + + case VIDEO_ENCODER_DOLBY_VISION: + { + // for Dolby Vision codec we always assume 10-bit DV + *bitDepth = 10; + *hdr = HDR_FORMAT_DOLBY_VISION; + + switch (profile) { + case DolbyVisionProfileDvheDer /* profile 2 deprecated */: + case DolbyVisionProfileDvheDen /* profile 3 deprecated */: + case DolbyVisionProfileDvavPer /* profile 0 deprecated */: + case DolbyVisionProfileDvavPen /* profile 1 deprecated */: + case DolbyVisionProfileDvheDtr /* dvhe.04 */: + case DolbyVisionProfileDvheStn /* dvhe.05 */: + case DolbyVisionProfileDvheDth /* profile 6 deprecated */: + case DolbyVisionProfileDvheDtb /* dvhe.07 */: + case DolbyVisionProfileDvheSt /* dvhe.08 */: + case DolbyVisionProfileDvavSe /* dvav.09 */: + case DolbyVisionProfileDvav110 /* dvav1.10 */: + return true; + default: + return false; + } + // flow does not get here + } + + case VIDEO_ENCODER_AV1: + switch (profile) { + case AV1ProfileMain10: + *bitDepth = 10; + // returning false here as this could be an HLG stream + return false; + case AV1ProfileMain10HDR10: + *bitDepth = 10; + *hdr = HDR_FORMAT_HDR10; + return true; + case AV1ProfileMain10HDR10Plus: + *bitDepth = 10; + *hdr = HDR_FORMAT_HDR10PLUS; + return true; + default: + return false; + } + // flow does not get here + + default: + return false; + } + // flow does not get here +} + /*static*/ void MediaProfiles::createVideoCodec(const char **atts, size_t natts, MediaProfiles *profiles) { @@ -250,13 +424,56 @@ MediaProfiles::createVideoCodec(const char **atts, size_t natts, MediaProfiles * } int profile = -1; + chroma_subsampling chroma = CHROMA_SUBSAMPLING_YUV_420; + int bitDepth = 8; + hdr_format hdr = HDR_FORMAT_NONE; + if (codec == VIDEO_ENCODER_DOLBY_VISION) { + bitDepth = 10; + hdr = HDR_FORMAT_DOLBY_VISION; + } + if (natts >= 12 && !strcmp("profile", atts[10])) { profile = atoi(atts[11]); + if (!detectAdvancedVideoProfile( + (video_encoder)codec, profile, &chroma, &bitDepth, &hdr)) { + // if not detected read values from the attributes + for (size_t ix = 12; natts >= ix + 2; ix += 2) { + if (!strcmp("chroma", atts[ix])) { + int chromaTag = findTagForName(sChromaSubsamplingNameMap, + NELEM(sChromaSubsamplingNameMap), atts[ix + 1]); + if (chromaTag == -1) { + ALOGE("MediaProfiles::createVideoCodec invalid chroma %s", atts[ix + 1]); + return; + } else { + chroma = (chroma_subsampling)chromaTag; + } + } else if (!strcmp("bitDepth", atts[ix])) { + bitDepth = atoi(atts[ix + 1]); + if (bitDepth < 8 || bitDepth > 16) { + ALOGE("MediaProfiles::createVideoCodec invalid bidDepth %s", atts[ix + 1]); + return; + } + } else if (!strcmp("hdr", atts[ix])) { + int hdrTag = findTagForName(sHdrFormatNameMap, + NELEM(sHdrFormatNameMap), atts[ix + 1]); + if (hdrTag == -1) { + ALOGE("MediaProfiles::createVideoCodec invalid hdr %s", atts[ix + 1]); + return; + } else { + hdr = (hdr_format)hdrTag; + } + } else { + // ignoring here. TODO: rewrite this whole file to ignore invalid attrs + ALOGD("MediaProfiles::createVideoCodec ignoring invalid attr %s", atts[ix]); + } + } + } } - VideoCodec videoCodec { + VideoCodec videoCodec{ static_cast(codec), - atoi(atts[3]), atoi(atts[5]), atoi(atts[7]), atoi(atts[9]), profile }; + atoi(atts[3]) /* bitRate */, atoi(atts[5]) /* width */, atoi(atts[7]) /* height */, + atoi(atts[9]) /* frameRate */, profile, chroma, bitDepth, hdr }; logVideoCodec(videoCodec); size_t nCamcorderProfiles; diff --git a/media/libmedia/MediaResource.cpp b/media/libmedia/MediaResource.cpp index ec52a49aea760aaf6646b8b5ad7250b05aac54eb..a6f0b605d6aa8efd1a63af5c74c49cea4296a4b5 100644 --- a/media/libmedia/MediaResource.cpp +++ b/media/libmedia/MediaResource.cpp @@ -43,10 +43,10 @@ MediaResource::MediaResource(Type type, const std::vector &id, int64_t } //static -MediaResource MediaResource::CodecResource(bool secure, bool video, int64_t instanceCount) { +MediaResource MediaResource::CodecResource(bool secure, SubType subType, int64_t instanceCount) { return MediaResource( secure ? Type::kSecureCodec : Type::kNonSecureCodec, - video ? SubType::kVideoCodec : SubType::kAudioCodec, + subType, instanceCount); } diff --git a/media/libmedia/include/media/MediaProfiles.h b/media/libmedia/include/media/MediaProfiles.h index 4a898e2614287e685ec1d39cf77fb9e184e94841..e75b694d8ae2970cc522b7fda224636573e3697e 100644 --- a/media/libmedia/include/media/MediaProfiles.h +++ b/media/libmedia/include/media/MediaProfiles.h @@ -81,6 +81,19 @@ enum audio_decoder { AUDIO_DECODER_WMA, }; +enum chroma_subsampling { + CHROMA_SUBSAMPLING_YUV_420, + CHROMA_SUBSAMPLING_YUV_422, + CHROMA_SUBSAMPLING_YUV_444, +}; + +enum hdr_format { + HDR_FORMAT_NONE, + HDR_FORMAT_HLG, + HDR_FORMAT_HDR10, + HDR_FORMAT_HDR10PLUS, + HDR_FORMAT_DOLBY_VISION, +}; class MediaProfiles { @@ -117,13 +130,19 @@ public: * @param profile codec profile (for MediaCodec) or -1 for none */ VideoCodec(video_encoder codec, int bitrate, int frameWidth, int frameHeight, int frameRate, - int profile = -1) + int profile = -1, + chroma_subsampling chroma = CHROMA_SUBSAMPLING_YUV_420, + int bitDepth = 8, + hdr_format hdr = HDR_FORMAT_NONE) : mCodec(codec), mBitRate(bitrate), mFrameWidth(frameWidth), mFrameHeight(frameHeight), mFrameRate(frameRate), - mProfile(profile) { + mProfile(profile), + mChromaSubsampling(chroma), + mBitDepth(bitDepth), + mHdrFormat(hdr) { } VideoCodec(const VideoCodec&) = default; @@ -160,6 +179,21 @@ public: return mProfile; } + /** Returns the chroma subsampling. */ + chroma_subsampling getChromaSubsampling() const { + return mChromaSubsampling; + } + + /** Returns the bit depth. */ + int getBitDepth() const { + return mBitDepth; + } + + /** Returns the chroma subsampling. */ + hdr_format getHdrFormat() const { + return mHdrFormat; + } + private: video_encoder mCodec; int mBitRate; @@ -167,6 +201,9 @@ public: int mFrameHeight; int mFrameRate; int mProfile; + chroma_subsampling mChromaSubsampling; + int mBitDepth; + hdr_format mHdrFormat; friend class MediaProfiles; }; @@ -532,6 +569,39 @@ private: static int findTagForName(const NameToTagMap *map, size_t nMappings, const char *name); + /** + * Finds the string representation for an integer enum tag. + * + * This is the reverse for findTagForName + * + * @param map the name-to-tag map to search + * @param nMappings the number of mappings in |map| + * @param tag the enum value to find + * @param def_ the return value if the enum is not found + * + * @return the string name corresponding to |tag| or |def_| if not found. + */ + static const char *findNameForTag( + const NameToTagMap *map, size_t nMappings, + int tag, const char *def_ = "(unknown)"); + + /** + * Updates the chroma subsampling, bit-depth and hdr-format for + * advanced codec profiles. + * + * @param codec the video codec type + * @param profile the MediaCodec profile + * @param chroma pointer to the chroma subsampling output + * @param bitDepth pointer to the bit depth output + * @param hdr pointer to the hdr format output + * + * @return true, if the profile fully determined chroma, bit-depth and hdr-format, false + * otherwise. + */ + static bool detectAdvancedVideoProfile( + video_encoder codec, int profile, + chroma_subsampling *chroma, int *bitDepth, hdr_format *hdr); + /** * Check on existing profiles with the following criteria: * 1. Low quality profile must have the lowest video @@ -549,6 +619,8 @@ private: // Mappings from name (for instance, codec name) to enum value static const NameToTagMap sVideoEncoderNameMap[]; + static const NameToTagMap sChromaSubsamplingNameMap[]; + static const NameToTagMap sHdrFormatNameMap[]; static const NameToTagMap sAudioEncoderNameMap[]; static const NameToTagMap sFileFormatMap[]; static const NameToTagMap sVideoDecoderNameMap[]; diff --git a/media/libmedia/include/media/MediaResource.h b/media/libmedia/include/media/MediaResource.h index 4712528c7a65a35469a9d1d53e9ec0fb862e07c7..3b69d4fbb953da95a6ad4162ebd41201c0ac903e 100644 --- a/media/libmedia/include/media/MediaResource.h +++ b/media/libmedia/include/media/MediaResource.h @@ -37,7 +37,8 @@ public: MediaResource(Type type, SubType subType, int64_t value); MediaResource(Type type, const std::vector &id, int64_t value); - static MediaResource CodecResource(bool secure, bool video, int64_t instanceCount = 1); + static MediaResource CodecResource(bool secure, MediaResourceSubType subType, + int64_t instanceCount = 1); static MediaResource GraphicMemoryResource(int64_t value); static MediaResource CpuBoostResource(); static MediaResource VideoBatteryResource(); @@ -62,6 +63,7 @@ inline static const char *asString(MediaResource::SubType i, const char *def = " case MediaResource::SubType::kUnspecifiedSubType: return "unspecified"; case MediaResource::SubType::kAudioCodec: return "audio-codec"; case MediaResource::SubType::kVideoCodec: return "video-codec"; + case MediaResource::SubType::kImageCodec: return "image-codec"; default: return def; } } diff --git a/media/libmedia/include/media/mediarecorder.h b/media/libmedia/include/media/mediarecorder.h index d54ff3285b10e6af15c0c5215c43929e4012f74f..dd181440ea1fead7bb06e06acb5575fb89f8e051 100644 --- a/media/libmedia/include/media/mediarecorder.h +++ b/media/libmedia/include/media/mediarecorder.h @@ -108,7 +108,9 @@ enum video_encoder { VIDEO_ENCODER_MPEG_4_SP = 3, VIDEO_ENCODER_VP8 = 4, VIDEO_ENCODER_HEVC = 5, - + VIDEO_ENCODER_VP9 = 6, + VIDEO_ENCODER_DOLBY_VISION = 7, + VIDEO_ENCODER_AV1 = 8, VIDEO_ENCODER_LIST_END // must be the last - used to validate the video encoder type }; diff --git a/media/libmedia/tests/codeclist/Android.bp b/media/libmedia/tests/codeclist/Android.bp index 7dd0caa0f9822d684eb7762b93d9bb82e6b7df69..2ed3126ca73d1d0d5a7d7379fdb78e92d5f454a1 100644 --- a/media/libmedia/tests/codeclist/Android.bp +++ b/media/libmedia/tests/codeclist/Android.bp @@ -25,9 +25,25 @@ package { cc_test { name: "CodecListTest", - test_suites: ["device-tests"], + test_suites: ["device-tests", "mts"], gtest: true, + // Support multilib variants (using different suffix per sub-architecture), which is needed on + // build targets with secondary architectures, as the MTS test suite packaging logic flattens + // all test artifacts into a single `testcases` directory. + compile_multilib: "both", + multilib: { + lib32: { + suffix: "32", + }, + lib64: { + suffix: "64", + }, + }, + + // used within mainline MTS, but only to R, not to Q. + min_sdk_version: "30", + srcs: [ "CodecListTest.cpp", ], @@ -35,7 +51,7 @@ cc_test { shared_libs: [ "libbinder", "liblog", - "libmedia_codeclist", + "libmedia_codeclist", // available >= R "libstagefright", "libstagefright_foundation", "libstagefright_xmlparser", diff --git a/media/libmedia/tests/codeclist/AndroidTest.xml b/media/libmedia/tests/codeclist/AndroidTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..eeaab8e645a5df217c02b6e6285daabd7f32a8a9 --- /dev/null +++ b/media/libmedia/tests/codeclist/AndroidTest.xml @@ -0,0 +1,32 @@ + + + +