Loading media/java/android/media/AudioManager.java +24 −4 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAUL import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO; import static android.content.Context.DEVICE_ID_DEFAULT; import static com.android.media.audio.flags.Flags.autoPublicVolumeApiHardening; import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.IntDef; Loading Loading @@ -1060,9 +1062,18 @@ public class AudioManager { * @see #isVolumeFixed() */ public void adjustVolume(int direction, @PublicVolumeFlags int flags) { if (autoPublicVolumeApiHardening()) { final IAudioService service = getService(); try { service.adjustVolume(direction, flags); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } else { MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext()); helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags); } } /** * Adjusts the volume of the most relevant stream, or the given fallback Loading Loading @@ -1090,9 +1101,18 @@ public class AudioManager { */ public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, @PublicVolumeFlags int flags) { if (autoPublicVolumeApiHardening()) { final IAudioService service = getService(); try { service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } else { MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext()); helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags); } } /** @hide */ @UnsupportedAppUsage Loading media/java/android/media/IAudioService.aidl +4 −0 Original line number Diff line number Diff line Loading @@ -498,6 +498,10 @@ interface IAudioService { in String packageName, int uid, int pid, in UserHandle userHandle, int targetSdkVersion); oneway void adjustVolume(int direction, int flags); oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags); boolean isMusicActive(in boolean remotely); int getDeviceMaskForStream(in int streamType); Loading services/core/java/com/android/server/audio/AudioService.java +59 −2 Original line number Diff line number Diff line Loading @@ -16,8 +16,8 @@ package com.android.server.audio; import static android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED; import static android.app.BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT; import static android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED; import static android.media.AudioDeviceInfo.TYPE_BLE_HEADSET; import static android.media.AudioDeviceInfo.TYPE_BLE_SPEAKER; import static android.media.AudioDeviceInfo.TYPE_BLUETOOTH_A2DP; Loading Loading @@ -150,6 +150,7 @@ import android.media.permission.SafeCloseable; import android.media.projection.IMediaProjection; import android.media.projection.IMediaProjectionCallback; import android.media.projection.IMediaProjectionManager; import android.media.session.MediaSessionManager; import android.net.Uri; import android.os.Binder; import android.os.Build; Loading Loading @@ -309,6 +310,9 @@ public class AudioService extends IAudioService.Stub private final ContentResolver mContentResolver; private final AppOpsManager mAppOps; /** do not use directly, use getMediaSessionManager() which handles lazy initialization */ @Nullable private volatile MediaSessionManager mMediaSessionManager; // the platform type affects volume and silent mode behavior private final int mPlatformType; Loading Loading @@ -940,6 +944,8 @@ public class AudioService extends IAudioService.Stub private final SoundDoseHelper mSoundDoseHelper; private final HardeningEnforcer mHardeningEnforcer; private final Object mSupportedSystemUsagesLock = new Object(); @GuardedBy("mSupportedSystemUsagesLock") private @AttributeSystemUsage int[] mSupportedSystemUsages = Loading Loading @@ -1314,6 +1320,8 @@ public class AudioService extends IAudioService.Stub mDisplayManager = context.getSystemService(DisplayManager.class); mMusicFxHelper = new MusicFxHelper(mContext, mAudioHandler); mHardeningEnforcer = new HardeningEnforcer(mContext, isPlatformAutomotive()); } private void initVolumeStreamStates() { Loading Loading @@ -1383,7 +1391,6 @@ public class AudioService extends IAudioService.Stub // check on volume initialization checkVolumeRangeInitialization("AudioService()"); } private SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionChangedListener = Loading @@ -1396,6 +1403,14 @@ public class AudioService extends IAudioService.Stub } }; private MediaSessionManager getMediaSessionManager() { if (mMediaSessionManager == null) { mMediaSessionManager = (MediaSessionManager) mContext .getSystemService(Context.MEDIA_SESSION_SERVICE); } return mMediaSessionManager; } /** * Initialize intent receives and settings observers for this service. * Must be called after createStreamStates() as the handling of some events Loading Loading @@ -3424,6 +3439,10 @@ public class AudioService extends IAudioService.Stub * Part of service interface, check permissions here */ public void adjustStreamVolumeWithAttribution(int streamType, int direction, int flags, String callingPackage, String attributionTag) { if (mHardeningEnforcer.blockVolumeMethod( HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_STREAM_VOLUME)) { return; } if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) { Log.w(TAG, "Trying to call adjustStreamVolume() for a11y without" + "CHANGE_ACCESSIBILITY_VOLUME / callingPackage=" + callingPackage); Loading Loading @@ -4200,6 +4219,10 @@ public class AudioService extends IAudioService.Stub * Part of service interface, check permissions here */ public void setStreamVolumeWithAttribution(int streamType, int index, int flags, String callingPackage, String attributionTag) { if (mHardeningEnforcer.blockVolumeMethod( HardeningEnforcer.METHOD_AUDIO_MANAGER_SET_STREAM_VOLUME)) { return; } setStreamVolumeWithAttributionInt(streamType, index, flags, /*device*/ null, callingPackage, attributionTag); } Loading Loading @@ -5052,6 +5075,7 @@ public class AudioService extends IAudioService.Stub /** @see AudioManager#setMasterMute(boolean, int) */ public void setMasterMute(boolean mute, int flags, String callingPackage, int userId, String attributionTag) { super.setMasterMute_enforcePermission(); setMasterMuteInternal(mute, flags, callingPackage, Loading Loading @@ -5417,6 +5441,10 @@ public class AudioService extends IAudioService.Stub } public void setRingerModeExternal(int ringerMode, String caller) { if (mHardeningEnforcer.blockVolumeMethod( HardeningEnforcer.METHOD_AUDIO_MANAGER_SET_RINGER_MODE)) { return; } if (isAndroidNPlus(caller) && wouldToggleZenMode(ringerMode) && !mNm.isNotificationPolicyAccessGrantedForPackage(caller)) { throw new SecurityException("Not allowed to change Do Not Disturb state"); Loading Loading @@ -6169,6 +6197,35 @@ public class AudioService extends IAudioService.Stub AudioDeviceVolumeManager.ADJUST_MODE_NORMAL); } /** * @see AudioManager#adjustVolume(int, int) * This method is redirected from AudioManager to AudioService for API hardening rules * enforcement then to MediaSession for implementation. */ @Override public void adjustVolume(int direction, int flags) { if (mHardeningEnforcer.blockVolumeMethod( HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_VOLUME)) { return; } getMediaSessionManager().dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE, direction, flags); } /** * @see AudioManager#adjustSuggestedStreamVolume(int, int, int) * This method is redirected from AudioManager to AudioService for API hardening rules * enforcement then to MediaSession for implementation. */ @Override public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) { if (mHardeningEnforcer.blockVolumeMethod( HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_SUGGESTED_STREAM_VOLUME)) { return; } getMediaSessionManager().dispatchAdjustVolume(suggestedStreamType, direction, flags); } /** @see AudioManager#setStreamVolumeForUid(int, int, int, String, int, int, int) */ @Override public void setStreamVolumeForUid(int streamType, int index, int flags, Loading services/core/java/com/android/server/audio/HardeningEnforcer.java 0 → 100644 +109 −0 Original line number Diff line number Diff line /* * Copyright 2023 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 com.android.server.audio; import static com.android.media.audio.flags.Flags.autoPublicVolumeApiHardening; import android.Manifest; import android.content.Context; import android.content.pm.PackageManager; import android.media.AudioManager; import android.os.Binder; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; /** * Class to encapsulate all audio API hardening operations */ public class HardeningEnforcer { private static final String TAG = "AS.HardeningEnforcer"; final Context mContext; final boolean mIsAutomotive; /** * Matches calls from {@link AudioManager#setStreamVolume(int, int, int)} */ public static final int METHOD_AUDIO_MANAGER_SET_STREAM_VOLUME = 100; /** * Matches calls from {@link AudioManager#adjustVolume(int, int)} */ public static final int METHOD_AUDIO_MANAGER_ADJUST_VOLUME = 101; /** * Matches calls from {@link AudioManager#adjustSuggestedStreamVolume(int, int, int)} */ public static final int METHOD_AUDIO_MANAGER_ADJUST_SUGGESTED_STREAM_VOLUME = 102; /** * Matches calls from {@link AudioManager#adjustStreamVolume(int, int, int)} */ public static final int METHOD_AUDIO_MANAGER_ADJUST_STREAM_VOLUME = 103; /** * Matches calls from {@link AudioManager#setRingerMode(int)} */ public static final int METHOD_AUDIO_MANAGER_SET_RINGER_MODE = 200; public HardeningEnforcer(Context ctxt, boolean isAutomotive) { mContext = ctxt; mIsAutomotive = isAutomotive; } /** * Checks whether the call in the current thread should be allowed or blocked * @param volumeMethod name of the method to check, for logging purposes * @return false if the method call is allowed, true if it should be a no-op */ protected boolean blockVolumeMethod(int volumeMethod) { // for Auto, volume methods require MODIFY_AUDIO_SETTINGS_PRIVILEGED if (mIsAutomotive) { if (!autoPublicVolumeApiHardening()) { // automotive hardening flag disabled, no blocking on auto return false; } if (mContext.checkCallingOrSelfPermission( Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) == PackageManager.PERMISSION_GRANTED) { return false; } if (Binder.getCallingUid() < UserHandle.AID_APP_START) { return false; } // TODO metrics? // TODO log for audio dumpsys? Log.e(TAG, "Preventing volume method " + volumeMethod + " for " + getPackNameForUid(Binder.getCallingUid())); return true; } // not blocking return false; } private String getPackNameForUid(int uid) { final long token = Binder.clearCallingIdentity(); try { final String[] names = mContext.getPackageManager().getPackagesForUid(uid); if (names == null || names.length == 0 || TextUtils.isEmpty(names[0])) { return "[" + uid + "]"; } return names[0]; } finally { Binder.restoreCallingIdentity(token); } } } Loading
media/java/android/media/AudioManager.java +24 −4 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAUL import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO; import static android.content.Context.DEVICE_ID_DEFAULT; import static com.android.media.audio.flags.Flags.autoPublicVolumeApiHardening; import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.IntDef; Loading Loading @@ -1060,9 +1062,18 @@ public class AudioManager { * @see #isVolumeFixed() */ public void adjustVolume(int direction, @PublicVolumeFlags int flags) { if (autoPublicVolumeApiHardening()) { final IAudioService service = getService(); try { service.adjustVolume(direction, flags); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } else { MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext()); helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags); } } /** * Adjusts the volume of the most relevant stream, or the given fallback Loading Loading @@ -1090,9 +1101,18 @@ public class AudioManager { */ public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, @PublicVolumeFlags int flags) { if (autoPublicVolumeApiHardening()) { final IAudioService service = getService(); try { service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } else { MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext()); helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags); } } /** @hide */ @UnsupportedAppUsage Loading
media/java/android/media/IAudioService.aidl +4 −0 Original line number Diff line number Diff line Loading @@ -498,6 +498,10 @@ interface IAudioService { in String packageName, int uid, int pid, in UserHandle userHandle, int targetSdkVersion); oneway void adjustVolume(int direction, int flags); oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags); boolean isMusicActive(in boolean remotely); int getDeviceMaskForStream(in int streamType); Loading
services/core/java/com/android/server/audio/AudioService.java +59 −2 Original line number Diff line number Diff line Loading @@ -16,8 +16,8 @@ package com.android.server.audio; import static android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED; import static android.app.BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT; import static android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED; import static android.media.AudioDeviceInfo.TYPE_BLE_HEADSET; import static android.media.AudioDeviceInfo.TYPE_BLE_SPEAKER; import static android.media.AudioDeviceInfo.TYPE_BLUETOOTH_A2DP; Loading Loading @@ -150,6 +150,7 @@ import android.media.permission.SafeCloseable; import android.media.projection.IMediaProjection; import android.media.projection.IMediaProjectionCallback; import android.media.projection.IMediaProjectionManager; import android.media.session.MediaSessionManager; import android.net.Uri; import android.os.Binder; import android.os.Build; Loading Loading @@ -309,6 +310,9 @@ public class AudioService extends IAudioService.Stub private final ContentResolver mContentResolver; private final AppOpsManager mAppOps; /** do not use directly, use getMediaSessionManager() which handles lazy initialization */ @Nullable private volatile MediaSessionManager mMediaSessionManager; // the platform type affects volume and silent mode behavior private final int mPlatformType; Loading Loading @@ -940,6 +944,8 @@ public class AudioService extends IAudioService.Stub private final SoundDoseHelper mSoundDoseHelper; private final HardeningEnforcer mHardeningEnforcer; private final Object mSupportedSystemUsagesLock = new Object(); @GuardedBy("mSupportedSystemUsagesLock") private @AttributeSystemUsage int[] mSupportedSystemUsages = Loading Loading @@ -1314,6 +1320,8 @@ public class AudioService extends IAudioService.Stub mDisplayManager = context.getSystemService(DisplayManager.class); mMusicFxHelper = new MusicFxHelper(mContext, mAudioHandler); mHardeningEnforcer = new HardeningEnforcer(mContext, isPlatformAutomotive()); } private void initVolumeStreamStates() { Loading Loading @@ -1383,7 +1391,6 @@ public class AudioService extends IAudioService.Stub // check on volume initialization checkVolumeRangeInitialization("AudioService()"); } private SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionChangedListener = Loading @@ -1396,6 +1403,14 @@ public class AudioService extends IAudioService.Stub } }; private MediaSessionManager getMediaSessionManager() { if (mMediaSessionManager == null) { mMediaSessionManager = (MediaSessionManager) mContext .getSystemService(Context.MEDIA_SESSION_SERVICE); } return mMediaSessionManager; } /** * Initialize intent receives and settings observers for this service. * Must be called after createStreamStates() as the handling of some events Loading Loading @@ -3424,6 +3439,10 @@ public class AudioService extends IAudioService.Stub * Part of service interface, check permissions here */ public void adjustStreamVolumeWithAttribution(int streamType, int direction, int flags, String callingPackage, String attributionTag) { if (mHardeningEnforcer.blockVolumeMethod( HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_STREAM_VOLUME)) { return; } if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) { Log.w(TAG, "Trying to call adjustStreamVolume() for a11y without" + "CHANGE_ACCESSIBILITY_VOLUME / callingPackage=" + callingPackage); Loading Loading @@ -4200,6 +4219,10 @@ public class AudioService extends IAudioService.Stub * Part of service interface, check permissions here */ public void setStreamVolumeWithAttribution(int streamType, int index, int flags, String callingPackage, String attributionTag) { if (mHardeningEnforcer.blockVolumeMethod( HardeningEnforcer.METHOD_AUDIO_MANAGER_SET_STREAM_VOLUME)) { return; } setStreamVolumeWithAttributionInt(streamType, index, flags, /*device*/ null, callingPackage, attributionTag); } Loading Loading @@ -5052,6 +5075,7 @@ public class AudioService extends IAudioService.Stub /** @see AudioManager#setMasterMute(boolean, int) */ public void setMasterMute(boolean mute, int flags, String callingPackage, int userId, String attributionTag) { super.setMasterMute_enforcePermission(); setMasterMuteInternal(mute, flags, callingPackage, Loading Loading @@ -5417,6 +5441,10 @@ public class AudioService extends IAudioService.Stub } public void setRingerModeExternal(int ringerMode, String caller) { if (mHardeningEnforcer.blockVolumeMethod( HardeningEnforcer.METHOD_AUDIO_MANAGER_SET_RINGER_MODE)) { return; } if (isAndroidNPlus(caller) && wouldToggleZenMode(ringerMode) && !mNm.isNotificationPolicyAccessGrantedForPackage(caller)) { throw new SecurityException("Not allowed to change Do Not Disturb state"); Loading Loading @@ -6169,6 +6197,35 @@ public class AudioService extends IAudioService.Stub AudioDeviceVolumeManager.ADJUST_MODE_NORMAL); } /** * @see AudioManager#adjustVolume(int, int) * This method is redirected from AudioManager to AudioService for API hardening rules * enforcement then to MediaSession for implementation. */ @Override public void adjustVolume(int direction, int flags) { if (mHardeningEnforcer.blockVolumeMethod( HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_VOLUME)) { return; } getMediaSessionManager().dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE, direction, flags); } /** * @see AudioManager#adjustSuggestedStreamVolume(int, int, int) * This method is redirected from AudioManager to AudioService for API hardening rules * enforcement then to MediaSession for implementation. */ @Override public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) { if (mHardeningEnforcer.blockVolumeMethod( HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_SUGGESTED_STREAM_VOLUME)) { return; } getMediaSessionManager().dispatchAdjustVolume(suggestedStreamType, direction, flags); } /** @see AudioManager#setStreamVolumeForUid(int, int, int, String, int, int, int) */ @Override public void setStreamVolumeForUid(int streamType, int index, int flags, Loading
services/core/java/com/android/server/audio/HardeningEnforcer.java 0 → 100644 +109 −0 Original line number Diff line number Diff line /* * Copyright 2023 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 com.android.server.audio; import static com.android.media.audio.flags.Flags.autoPublicVolumeApiHardening; import android.Manifest; import android.content.Context; import android.content.pm.PackageManager; import android.media.AudioManager; import android.os.Binder; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; /** * Class to encapsulate all audio API hardening operations */ public class HardeningEnforcer { private static final String TAG = "AS.HardeningEnforcer"; final Context mContext; final boolean mIsAutomotive; /** * Matches calls from {@link AudioManager#setStreamVolume(int, int, int)} */ public static final int METHOD_AUDIO_MANAGER_SET_STREAM_VOLUME = 100; /** * Matches calls from {@link AudioManager#adjustVolume(int, int)} */ public static final int METHOD_AUDIO_MANAGER_ADJUST_VOLUME = 101; /** * Matches calls from {@link AudioManager#adjustSuggestedStreamVolume(int, int, int)} */ public static final int METHOD_AUDIO_MANAGER_ADJUST_SUGGESTED_STREAM_VOLUME = 102; /** * Matches calls from {@link AudioManager#adjustStreamVolume(int, int, int)} */ public static final int METHOD_AUDIO_MANAGER_ADJUST_STREAM_VOLUME = 103; /** * Matches calls from {@link AudioManager#setRingerMode(int)} */ public static final int METHOD_AUDIO_MANAGER_SET_RINGER_MODE = 200; public HardeningEnforcer(Context ctxt, boolean isAutomotive) { mContext = ctxt; mIsAutomotive = isAutomotive; } /** * Checks whether the call in the current thread should be allowed or blocked * @param volumeMethod name of the method to check, for logging purposes * @return false if the method call is allowed, true if it should be a no-op */ protected boolean blockVolumeMethod(int volumeMethod) { // for Auto, volume methods require MODIFY_AUDIO_SETTINGS_PRIVILEGED if (mIsAutomotive) { if (!autoPublicVolumeApiHardening()) { // automotive hardening flag disabled, no blocking on auto return false; } if (mContext.checkCallingOrSelfPermission( Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) == PackageManager.PERMISSION_GRANTED) { return false; } if (Binder.getCallingUid() < UserHandle.AID_APP_START) { return false; } // TODO metrics? // TODO log for audio dumpsys? Log.e(TAG, "Preventing volume method " + volumeMethod + " for " + getPackNameForUid(Binder.getCallingUid())); return true; } // not blocking return false; } private String getPackNameForUid(int uid) { final long token = Binder.clearCallingIdentity(); try { final String[] names = mContext.getPackageManager().getPackagesForUid(uid); if (names == null || names.length == 0 || TextUtils.isEmpty(names[0])) { return "[" + uid + "]"; } return names[0]; } finally { Binder.restoreCallingIdentity(token); } } }