diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 8c64e4046013c..347f0287017e1 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1629,20 +1629,27 @@ package android.media { public class AudioManager { method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int abandonAudioFocusForTest(@NonNull android.media.AudioFocusRequest, @NonNull String); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public void forceComputeCsdOnAllDevices(boolean); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public void forceUseFrameworkMel(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public android.media.AudioRecord getCallDownlinkExtractionAudioRecord(@NonNull android.media.AudioFormat); method @NonNull @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public android.media.AudioTrack getCallUplinkInjectionAudioTrack(@NonNull android.media.AudioFormat); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public float getCsd(); method @Nullable public static android.media.AudioDeviceInfo getDeviceInfoFromType(int); method @IntRange(from=0) @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFadeOutDurationOnFocusLossMillis(@NonNull android.media.AudioAttributes); method @Nullable public static android.media.AudioHalVersionInfo getHalVersion(); method public static final int[] getPublicStreamTypes(); method @NonNull public java.util.List getReportedSurroundFormats(); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public float getRs2Value(); method public int getStreamMinVolumeInt(int); method @NonNull public java.util.Map getSurroundFormats(); method public boolean hasRegisteredDynamicPolicy(); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public boolean isCsdEnabled(); method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.QUERY_AUDIO_STATE}) public boolean isFullVolumeDevice(); method @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public boolean isPstnCallAudioInterceptable(); method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int requestAudioFocusForTest(@NonNull android.media.AudioFocusRequest, @NonNull String, int, int); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public void setCsd(float); method public void setRampingRingerEnabled(boolean); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) public void setRs2Value(float); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setTestDeviceConnectionState(@NonNull android.media.AudioDeviceAttributes, boolean); } diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 24c5b41727320..b959bc01f181e 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -6442,6 +6442,20 @@ public class AudioManager { } } + /** + * Returns the registered volume controller interface. + * + * @hide + */ + @Nullable + public IVolumeController getVolumeController() { + try { + return getService().getVolumeController(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Notify audio manager about volume controller visibility changes. * Currently limited to SystemUI. @@ -6504,6 +6518,106 @@ public class AudioManager { } } + /** + * @hide + * @return the RS2 value used for momentary exposure warnings + */ + @TestApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public float getRs2Value() { + try { + return getService().getRs2Value(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + * Sets the RS2 value used for momentary exposure warnings + */ + @TestApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public void setRs2Value(float rs2Value) { + try { + getService().setRs2Value(rs2Value); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + * @return the current computed sound dose value + */ + @TestApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public float getCsd() { + try { + return getService().getCsd(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + * Sets the computed sound dose value to {@code csd} + */ + @TestApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public void setCsd(float csd) { + try { + getService().setCsd(csd); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + * Forces the computation of MEL values (used for CSD) on framework level. This will have the + * result of ignoring the MEL values computed on HAL level. Should only be used in testing + * since this can affect the certification of a device with EN50332-3 regulation. + */ + @TestApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public void forceUseFrameworkMel(boolean useFrameworkMel) { + try { + getService().forceUseFrameworkMel(useFrameworkMel); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + * Forces the computation of CSD on all output devices. + */ + @TestApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public void forceComputeCsdOnAllDevices(boolean computeCsdOnAllDevices) { + try { + getService().forceComputeCsdOnAllDevices(computeCsdOnAllDevices); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + * Returns whether CSD is enabled on this device. + */ + @TestApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public boolean isCsdEnabled() { + try { + return getService().isCsdEnabled(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * @hide * Sound dose warning at every 100% of dose during integration window diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index c06352c21ee4f..5ba7891065ab6 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -260,6 +260,8 @@ interface IAudioService { void setVolumeController(in IVolumeController controller); + @nullable IVolumeController getVolumeController(); + void notifyVolumeControllerVisible(in IVolumeController controller, boolean visible); boolean isStreamAffectedByRingerMode(int streamType); @@ -270,6 +272,27 @@ interface IAudioService { void lowerVolumeToRs1(String callingPackage); + @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS") + float getRs2Value(); + + @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS") + oneway void setRs2Value(float rs2Value); + + @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS") + float getCsd(); + + @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS") + oneway void setCsd(float csd); + + @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS") + oneway void forceUseFrameworkMel(boolean useFrameworkMel); + + @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS") + oneway void forceComputeCsdOnAllDevices(boolean computeCsdOnAllDevices); + + @EnforcePermission("MODIFY_AUDIO_SYSTEM_SETTINGS") + boolean isCsdEnabled(); + int setHdmiSystemAudioSupported(boolean on); boolean isHdmiSystemAudioSupported(); diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index c641a8535c3c2..b4598bff0b82b 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -646,6 +646,9 @@ + + + diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index c794b0429fccb..9a9b870ed18d0 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -908,9 +908,6 @@ public class AudioService extends IAudioService.Stub private final SoundDoseHelper mSoundDoseHelper; - @GuardedBy("mSettingsLock") - private int mCurrentImeUid; - private final Object mSupportedSystemUsagesLock = new Object(); @GuardedBy("mSupportedSystemUsagesLock") private @AttributeSystemUsage int[] mSupportedSystemUsages = @@ -9939,6 +9936,55 @@ public class AudioService extends IAudioService.Stub "com.android.server.audio", "AudioService"); } + @Override + @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public float getRs2Value() { + super.getRs2Value_enforcePermission(); + return mSoundDoseHelper.getRs2Value(); + } + + @Override + @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public void setRs2Value(float rs2Value) { + super.setRs2Value_enforcePermission(); + mSoundDoseHelper.setRs2Value(rs2Value); + } + + @Override + @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public float getCsd() { + super.getCsd_enforcePermission(); + return mSoundDoseHelper.getCsd(); + } + + @Override + @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public void setCsd(float csd) { + super.setCsd_enforcePermission(); + mSoundDoseHelper.setCsd(csd); + } + + @Override + @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public void forceUseFrameworkMel(boolean useFrameworkMel) { + super.forceUseFrameworkMel_enforcePermission(); + mSoundDoseHelper.forceUseFrameworkMel(useFrameworkMel); + } + + @Override + @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public void forceComputeCsdOnAllDevices(boolean computeCsdOnAllDevices) { + super.forceComputeCsdOnAllDevices_enforcePermission(); + mSoundDoseHelper.forceComputeCsdOnAllDevices(computeCsdOnAllDevices); + } + + @Override + @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS) + public boolean isCsdEnabled() { + super.isCsdEnabled_enforcePermission(); + return mSoundDoseHelper.isCsdEnabled(); + } + //========================================================================================== // Hdmi CEC: // - System audio mode: @@ -10290,7 +10336,6 @@ public class AudioService extends IAudioService.Stub + " FromRestrictions=" + mMicMuteFromRestrictions + " FromApi=" + mMicMuteFromApi + " from system=" + mMicMuteFromSystemCached); - pw.print(" mCurrentImeUid="); pw.println(mCurrentImeUid); dumpAccessibilityServiceUids(pw); dumpAssistantServicesUids(pw); @@ -10424,6 +10469,15 @@ public class AudioService extends IAudioService.Stub if (DEBUG_VOL) Log.d(TAG, "Volume controller: " + mVolumeController); } + @Override + @Nullable + public IVolumeController getVolumeController() { + enforceVolumeController("get the volume controller"); + if (DEBUG_VOL) Log.d(TAG, "Volume controller: " + mVolumeController); + + return mVolumeController.getController(); + } + @Override public void notifyVolumeControllerVisible(final IVolumeController controller, boolean visible) { enforceVolumeController("notify about volume controller visibility"); @@ -10469,6 +10523,10 @@ public class AudioService extends IAudioService.Stub mVisible = false; } + public IVolumeController getController() { + return mController; + } + public void loadSettings(ContentResolver cr) { mLongPressTimeout = mSettings.getSecureIntForUser(cr, Settings.Secure.LONG_PRESS_TIMEOUT, 500, UserHandle.USER_CURRENT); diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java index 919b85061db55..bc61b37883869 100644 --- a/services/core/java/com/android/server/audio/SoundDoseHelper.java +++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java @@ -53,6 +53,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; /** @@ -70,9 +71,6 @@ public class SoundDoseHelper { /*package*/ static final String ACTION_CHECK_MUSIC_ACTIVE = "com.android.server.audio.action.CHECK_MUSIC_ACTIVE"; - /** Flag to enable/disable the sound dose computation. */ - private static final boolean USE_CSD_FOR_SAFE_HEARING = false; - // mSafeMediaVolumeState indicates whether the media volume is limited over headphones. // It is SAFE_MEDIA_VOLUME_NOT_CONFIGURED at boot time until a network service is connected // or the configure time is elapsed. It is then set to SAFE_MEDIA_VOLUME_ACTIVE or @@ -96,8 +94,6 @@ public class SoundDoseHelper { private static final int MUSIC_ACTIVE_POLL_PERIOD_MS = 60000; // 1 minute polling interval private static final int REQUEST_CODE_CHECK_MUSIC_ACTIVE = 1; - private static final float CUSTOM_RS2_VALUE = 90; - // timeouts for the CSD warnings, -1 means no timeout (dialog must be ack'd by user) private static final int CSD_WARNING_TIMEOUT_MS_DOSE_1X = 7000; private static final int CSD_WARNING_TIMEOUT_MS_DOSE_5X = 5000; @@ -233,6 +229,94 @@ public class SoundDoseHelper { initCsd(); } + float getRs2Value() { + if (!mEnableCsd) { + return 0.f; + } + + Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized"); + try { + return mSoundDose.getOutputRs2(); + } catch (RemoteException e) { + Log.e(TAG, "Exception while getting the RS2 exposure value", e); + return 0.f; + } + } + + void setRs2Value(float rs2Value) { + if (!mEnableCsd) { + return; + } + + Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized"); + try { + mSoundDose.setOutputRs2(rs2Value); + } catch (RemoteException e) { + Log.e(TAG, "Exception while setting the RS2 exposure value", e); + } + } + + float getCsd() { + if (!mEnableCsd) { + return -1.f; + } + + Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized"); + try { + return mSoundDose.getCsd(); + } catch (RemoteException e) { + Log.e(TAG, "Exception while getting the CSD value", e); + return -1.f; + } + } + + void setCsd(float csd) { + if (!mEnableCsd) { + return; + } + + Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized"); + try { + final SoundDoseRecord record = new SoundDoseRecord(); + record.timestamp = System.currentTimeMillis(); + record.value = csd; + final SoundDoseRecord[] recordArray = new SoundDoseRecord[] { record }; + mSoundDose.resetCsd(csd, recordArray); + } catch (RemoteException e) { + Log.e(TAG, "Exception while setting the CSD value", e); + } + } + + void forceUseFrameworkMel(boolean useFrameworkMel) { + if (!mEnableCsd) { + return; + } + + Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized"); + try { + mSoundDose.forceUseFrameworkMel(useFrameworkMel); + } catch (RemoteException e) { + Log.e(TAG, "Exception while forcing the internal MEL computation", e); + } + } + + void forceComputeCsdOnAllDevices(boolean computeCsdOnAllDevices) { + if (!mEnableCsd) { + return; + } + + Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized"); + try { + mSoundDose.forceComputeCsdOnAllDevices(computeCsdOnAllDevices); + } catch (RemoteException e) { + Log.e(TAG, "Exception while forcing CSD computation on all devices", e); + } + } + + boolean isCsdEnabled() { + return mEnableCsd; + } + /*package*/ int safeMediaVolumeIndex(int device) { if (!mSafeMediaVolumeDevices.contains(device)) { return MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]; @@ -293,7 +377,7 @@ public class SoundDoseHelper { || (AudioService.mStreamVolumeAlias[streamType] != AudioSystem.STREAM_MUSIC) || (!mSafeMediaVolumeDevices.contains(device)) || (index <= safeMediaVolumeIndex(device)) - || USE_CSD_FOR_SAFE_HEARING; + || mEnableCsd; } /*package*/ boolean willDisplayWarningAfterCheckVolume(int streamType, int index, int device, @@ -329,7 +413,7 @@ public class SoundDoseHelper { /*package*/ void scheduleMusicActiveCheck() { synchronized (mSafeMediaVolumeStateLock) { cancelMusicActiveCheck(); - if (!USE_CSD_FOR_SAFE_HEARING) { + if (!mEnableCsd) { mMusicActiveIntent = PendingIntent.getBroadcast(mContext, REQUEST_CODE_CHECK_MUSIC_ACTIVE, new Intent(ACTION_CHECK_MUSIC_ACTIVE), @@ -460,14 +544,13 @@ public class SoundDoseHelper { private void initCsd() { synchronized (mSafeMediaVolumeStateLock) { - if (USE_CSD_FOR_SAFE_HEARING) { + if (mEnableCsd) { Log.v(TAG, "Initializing sound dose"); mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_DISABLED; mSoundDose = AudioSystem.getSoundDoseInterface(mSoundDoseCallback); try { if (mSoundDose != null && mSoundDose.asBinder().isBinderAlive()) { - mSoundDose.setOutputRs2(CUSTOM_RS2_VALUE); if (mCurrentCsd != 0.f) { Log.d(TAG, "Resetting the saved sound dose value " + mCurrentCsd); SoundDoseRecord[] records = mDoseRecords.toArray( @@ -502,7 +585,7 @@ public class SoundDoseHelper { // The persisted state is either "disabled" or "active": this is the state applied // next time we boot and cannot be "inactive" int persistedState; - if (safeMediaVolumeEnabled && !safeMediaVolumeBypass && !USE_CSD_FOR_SAFE_HEARING) { + if (safeMediaVolumeEnabled && !safeMediaVolumeBypass && !mEnableCsd) { persistedState = SAFE_MEDIA_VOLUME_ACTIVE; // The state can already be "inactive" here if the user has forced it before // the 30 seconds timeout for forced configuration. In this case we don't reset