Loading services/core/java/com/android/server/audio/AudioPolicyFacade.java +3 −1 Original line number Diff line number Diff line Loading @@ -16,12 +16,14 @@ package com.android.server.audio; import com.android.media.permission.INativePermissionController; /** * Facade to IAudioPolicyService which fulfills AudioService dependencies. * See @link{IAudioPolicyService.aidl} */ public interface AudioPolicyFacade { public boolean isHotwordStreamSupported(boolean lookbackAudio); public INativePermissionController getPermissionController(); public void registerOnStartTask(Runnable r); } services/core/java/com/android/server/audio/AudioServerPermissionProvider.java 0 → 100644 +149 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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 android.annotation.Nullable; import android.os.RemoteException; import android.os.UserHandle; import android.util.ArraySet; import com.android.internal.annotations.GuardedBy; import com.android.media.permission.INativePermissionController; import com.android.media.permission.UidPackageState; import com.android.server.pm.pkg.PackageState; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collector; import java.util.stream.Collectors; /** Responsible for synchronizing system server permission state to the native audioserver. */ public class AudioServerPermissionProvider { private final Object mLock = new Object(); @GuardedBy("mLock") private INativePermissionController mDest; @GuardedBy("mLock") private final Map<Integer, Set<String>> mPackageMap; /** * @param appInfos - PackageState for all apps on the device, used to populate init state */ public AudioServerPermissionProvider(Collection<PackageState> appInfos) { // Initialize the package state mPackageMap = generatePackageMappings(appInfos); } /** * Called whenever audioserver starts (or started before us) * * @param pc - The permission controller interface from audioserver, which we push updates to */ public void onServiceStart(@Nullable INativePermissionController pc) { if (pc == null) return; synchronized (mLock) { mDest = pc; resetNativePackageState(); } } /** * Called when a package is added or removed * * @param uid - uid of modified package (only app-id matters) * @param packageName - the (new) packageName * @param isRemove - true if the package is being removed, false if it is being added */ public void onModifyPackageState(int uid, String packageName, boolean isRemove) { // No point in maintaining package mappings for uids of different users uid = UserHandle.getAppId(uid); synchronized (mLock) { // Update state Set<String> packages; if (!isRemove) { packages = mPackageMap.computeIfAbsent(uid, unused -> new ArraySet(1)); packages.add(packageName); } else { packages = mPackageMap.get(uid); if (packages != null) { packages.remove(packageName); if (packages.isEmpty()) mPackageMap.remove(uid); } } // Push state to destination if (mDest == null) { return; } var state = new UidPackageState(); state.uid = uid; state.packageNames = packages != null ? List.copyOf(packages) : Collections.emptyList(); try { mDest.updatePackagesForUid(state); } catch (RemoteException e) { // We will re-init the state when the service comes back up mDest = null; } } } /** Called when full syncing package state to audioserver. */ @GuardedBy("mLock") private void resetNativePackageState() { if (mDest == null) return; List<UidPackageState> states = mPackageMap.entrySet().stream() .map( entry -> { UidPackageState state = new UidPackageState(); state.uid = entry.getKey(); state.packageNames = List.copyOf(entry.getValue()); return state; }) .toList(); try { mDest.populatePackagesForUids(states); } catch (RemoteException e) { // We will re-init the state when the service comes back up mDest = null; } } /** * Aggregation operation on all package states list: groups by states by app-id and merges the * packageName for each state into an ArraySet. */ private static Map<Integer, Set<String>> generatePackageMappings( Collection<PackageState> appInfos) { Collector<PackageState, Object, Set<String>> reducer = Collectors.mapping( (PackageState p) -> p.getPackageName(), Collectors.toCollection(() -> new ArraySet(1))); return appInfos.stream() .collect( Collectors.groupingBy( /* predicate */ (PackageState p) -> p.getAppId(), /* factory */ HashMap::new, /* downstream collector */ reducer)); } } services/core/java/com/android/server/audio/AudioService.java +67 −23 Original line number Diff line number Diff line Loading @@ -31,6 +31,10 @@ import static android.Manifest.permission.MODIFY_PHONE_STATE; import static android.Manifest.permission.QUERY_AUDIO_STATE; import static android.Manifest.permission.WRITE_SETTINGS; import static android.app.BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT; import static android.content.Intent.ACTION_PACKAGE_ADDED; import static android.content.Intent.ACTION_PACKAGE_REMOVED; import static android.content.Intent.EXTRA_ARCHIVAL; import static android.content.Intent.EXTRA_REPLACING; 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 @@ -59,6 +63,7 @@ import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import static com.android.media.audio.Flags.absVolumeIndexFix; import static com.android.media.audio.Flags.alarmMinVolumeZero; import static com.android.media.audio.Flags.audioserverPermissions; import static com.android.media.audio.Flags.disablePrescaleAbsoluteVolume; import static com.android.media.audio.Flags.ringerModeAffectsAlarm; import static com.android.media.audio.Flags.setStreamVolumeOrder; Loading Loading @@ -240,15 +245,18 @@ import com.android.internal.os.SomeArgs; import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; import com.android.server.EventLogTags; import com.android.server.LocalManagerRegistry; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.audio.AudioServiceEvents.DeviceVolumeEvent; import com.android.server.audio.AudioServiceEvents.PhoneStateEvent; import com.android.server.audio.AudioServiceEvents.VolChangedBroadcastEvent; import com.android.server.audio.AudioServiceEvents.VolumeEvent; import com.android.server.pm.PackageManagerLocal; import com.android.server.pm.UserManagerInternal; import com.android.server.pm.UserManagerInternal.UserRestrictionsListener; import com.android.server.pm.UserManagerService; import com.android.server.pm.pkg.PackageState; import com.android.server.utils.EventLogger; import com.android.server.wm.ActivityTaskManagerInternal; Loading @@ -273,6 +281,7 @@ import java.util.Objects; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BooleanSupplier; Loading Loading @@ -303,6 +312,8 @@ public class AudioService extends IAudioService.Stub private final SettingsAdapter mSettings; private final AudioPolicyFacade mAudioPolicy; private final AudioServerPermissionProvider mPermissionProvider; private final MusicFxHelper mMusicFxHelper; /** Debug audio mode */ Loading Loading @@ -1021,14 +1032,22 @@ public class AudioService extends IAudioService.Stub public Lifecycle(Context context) { super(context); var audioserverLifecycleExecutor = Executors.newSingleThreadExecutor(); var audioPolicyFacade = new DefaultAudioPolicyFacade(audioserverLifecycleExecutor); mService = new AudioService(context, AudioSystemAdapter.getDefaultAdapter(), SystemServerAdapter.getDefaultAdapter(context), SettingsAdapter.getDefaultAdapter(), new AudioVolumeGroupHelper(), new DefaultAudioPolicyFacade(r -> r.run()), null); audioPolicyFacade, null, context.getSystemService(AppOpsManager.class), PermissionEnforcer.fromContext(context), audioserverPermissions() ? initializeAudioServerPermissionProvider( context, audioPolicyFacade, audioserverLifecycleExecutor) : null ); } @Override Loading Loading @@ -1102,25 +1121,6 @@ public class AudioService extends IAudioService.Stub /////////////////////////////////////////////////////////////////////////// /** * @param context * @param audioSystem Adapter for {@link AudioSystem} * @param systemServer Adapter for privileged functionality for system server components * @param settings Adapter for {@link Settings} * @param audioVolumeGroupHelper Adapter for {@link AudioVolumeGroup} * @param audioPolicy Interface of a facade to IAudioPolicyManager * @param looper Looper to use for the service's message handler. If this is null, an * {@link AudioSystemThread} is created as the messaging thread instead. */ public AudioService(Context context, AudioSystemAdapter audioSystem, SystemServerAdapter systemServer, SettingsAdapter settings, AudioVolumeGroupHelperBase audioVolumeGroupHelper, AudioPolicyFacade audioPolicy, @Nullable Looper looper) { this (context, audioSystem, systemServer, settings, audioVolumeGroupHelper, audioPolicy, looper, context.getSystemService(AppOpsManager.class), PermissionEnforcer.fromContext(context)); } /** * @param context * @param audioSystem Adapter for {@link AudioSystem} Loading @@ -1137,13 +1137,16 @@ public class AudioService extends IAudioService.Stub public AudioService(Context context, AudioSystemAdapter audioSystem, SystemServerAdapter systemServer, SettingsAdapter settings, AudioVolumeGroupHelperBase audioVolumeGroupHelper, AudioPolicyFacade audioPolicy, @Nullable Looper looper, AppOpsManager appOps, @NonNull PermissionEnforcer enforcer) { @Nullable Looper looper, AppOpsManager appOps, @NonNull PermissionEnforcer enforcer, /* @NonNull */ AudioServerPermissionProvider permissionProvider) { super(enforcer); sLifecycleLogger.enqueue(new EventLogger.StringEvent("AudioService()")); mContext = context; mContentResolver = context.getContentResolver(); mAppOps = appOps; mPermissionProvider = permissionProvider; mAudioSystem = audioSystem; mSystemServer = systemServer; mAudioVolumeGroupHelper = audioVolumeGroupHelper; Loading Loading @@ -4559,6 +4562,8 @@ public class AudioService extends IAudioService.Stub + featureSpatialAudioHeadtrackingLowLatency()); pw.println("\tandroid.media.audio.focusFreezeTestApi:" + focusFreezeTestApi()); pw.println("\tcom.android.media.audio.audioserverPermissions:" + audioserverPermissions()); pw.println("\tcom.android.media.audio.disablePrescaleAbsoluteVolume:" + disablePrescaleAbsoluteVolume()); pw.println("\tcom.android.media.audio.setStreamVolumeOrder:" Loading Loading @@ -11937,6 +11942,45 @@ public class AudioService extends IAudioService.Stub private static final String mMetricsId = MediaMetrics.Name.AUDIO_SERVICE + MediaMetrics.SEPARATOR; private static AudioServerPermissionProvider initializeAudioServerPermissionProvider( Context context, AudioPolicyFacade audioPolicy, Executor audioserverExecutor) { Collection<PackageState> packageStates = null; try (PackageManagerLocal.UnfilteredSnapshot snapshot = LocalManagerRegistry.getManager(PackageManagerLocal.class) .withUnfilteredSnapshot()) { packageStates = snapshot.getPackageStates().values(); } var provider = new AudioServerPermissionProvider(packageStates); audioPolicy.registerOnStartTask(() -> { provider.onServiceStart(audioPolicy.getPermissionController()); }); // Set up event listeners IntentFilter packageUpdateFilter = new IntentFilter(); packageUpdateFilter.addAction(ACTION_PACKAGE_ADDED); packageUpdateFilter.addAction(ACTION_PACKAGE_REMOVED); packageUpdateFilter.addDataScheme("package"); context.registerReceiverForAllUsers(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); String pkgName = intent.getData().getEncodedSchemeSpecificPart(); int uid = intent.getIntExtra(Intent.EXTRA_UID, Process.INVALID_UID); if (intent.getBooleanExtra(EXTRA_REPLACING, false) || intent.getBooleanExtra(EXTRA_ARCHIVAL, false)) return; if (action.equals(ACTION_PACKAGE_ADDED)) { audioserverExecutor.execute(() -> provider.onModifyPackageState(uid, pkgName, false /* isRemoved */)); } else if (action.equals(ACTION_PACKAGE_REMOVED)) { audioserverExecutor.execute(() -> provider.onModifyPackageState(uid, pkgName, true /* isRemoved */)); } } }, packageUpdateFilter, null, null); // main thread is fine, since dispatch on executor return provider; } // Inform AudioFlinger of our device's low RAM attribute private static void readAndSetLowRamDevice() { services/core/java/com/android/server/audio/DefaultAudioPolicyFacade.java +25 −0 Original line number Diff line number Diff line Loading @@ -16,10 +16,15 @@ package com.android.server.audio; import android.annotation.Nullable; import android.media.IAudioPolicyService; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import com.android.media.permission.INativePermissionController; import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Function; Loading @@ -43,6 +48,7 @@ public class DefaultAudioPolicyFacade implements AudioPolicyFacade { (Function<IBinder, IAudioPolicyService>) IAudioPolicyService.Stub::asInterface, e); mServiceHolder.registerOnStartTask(i -> Binder.allowBlocking(i.asBinder())); } @Override Loading @@ -55,4 +61,23 @@ public class DefaultAudioPolicyFacade implements AudioPolicyFacade { throw new IllegalStateException(); } } @Override public @Nullable INativePermissionController getPermissionController() { IAudioPolicyService ap = mServiceHolder.checkService(); if (ap == null) return null; try { var res = Objects.requireNonNull(ap.getPermissionController()); Binder.allowBlocking(res.asBinder()); return res; } catch (RemoteException e) { mServiceHolder.attemptClear(ap.asBinder()); return null; } } @Override public void registerOnStartTask(Runnable task) { mServiceHolder.registerOnStartTask(unused -> task.run()); } } services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java +4 −1 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.AppOpsManager; import android.content.Context; import android.content.res.Resources; import android.media.AudioDeviceAttributes; Loading @@ -37,6 +38,7 @@ import android.media.AudioManager; import android.media.AudioSystem; import android.media.IAudioDeviceVolumeDispatcher; import android.media.VolumeInfo; import android.os.PermissionEnforcer; import android.os.RemoteException; import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; Loading Loading @@ -98,7 +100,8 @@ public class AbsoluteVolumeBehaviorTest { mAudioService = new AudioService(mContext, mSpyAudioSystem, mSystemServer, mSettingsAdapter, mAudioVolumeGroupHelper, mMockAudioPolicy, mTestLooper.getLooper()) { mTestLooper.getLooper(), mock(AppOpsManager.class), mock(PermissionEnforcer.class), mock(AudioServerPermissionProvider.class)) { @Override public int getDeviceForStream(int stream) { return AudioSystem.DEVICE_OUT_SPEAKER; Loading Loading
services/core/java/com/android/server/audio/AudioPolicyFacade.java +3 −1 Original line number Diff line number Diff line Loading @@ -16,12 +16,14 @@ package com.android.server.audio; import com.android.media.permission.INativePermissionController; /** * Facade to IAudioPolicyService which fulfills AudioService dependencies. * See @link{IAudioPolicyService.aidl} */ public interface AudioPolicyFacade { public boolean isHotwordStreamSupported(boolean lookbackAudio); public INativePermissionController getPermissionController(); public void registerOnStartTask(Runnable r); }
services/core/java/com/android/server/audio/AudioServerPermissionProvider.java 0 → 100644 +149 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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 android.annotation.Nullable; import android.os.RemoteException; import android.os.UserHandle; import android.util.ArraySet; import com.android.internal.annotations.GuardedBy; import com.android.media.permission.INativePermissionController; import com.android.media.permission.UidPackageState; import com.android.server.pm.pkg.PackageState; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collector; import java.util.stream.Collectors; /** Responsible for synchronizing system server permission state to the native audioserver. */ public class AudioServerPermissionProvider { private final Object mLock = new Object(); @GuardedBy("mLock") private INativePermissionController mDest; @GuardedBy("mLock") private final Map<Integer, Set<String>> mPackageMap; /** * @param appInfos - PackageState for all apps on the device, used to populate init state */ public AudioServerPermissionProvider(Collection<PackageState> appInfos) { // Initialize the package state mPackageMap = generatePackageMappings(appInfos); } /** * Called whenever audioserver starts (or started before us) * * @param pc - The permission controller interface from audioserver, which we push updates to */ public void onServiceStart(@Nullable INativePermissionController pc) { if (pc == null) return; synchronized (mLock) { mDest = pc; resetNativePackageState(); } } /** * Called when a package is added or removed * * @param uid - uid of modified package (only app-id matters) * @param packageName - the (new) packageName * @param isRemove - true if the package is being removed, false if it is being added */ public void onModifyPackageState(int uid, String packageName, boolean isRemove) { // No point in maintaining package mappings for uids of different users uid = UserHandle.getAppId(uid); synchronized (mLock) { // Update state Set<String> packages; if (!isRemove) { packages = mPackageMap.computeIfAbsent(uid, unused -> new ArraySet(1)); packages.add(packageName); } else { packages = mPackageMap.get(uid); if (packages != null) { packages.remove(packageName); if (packages.isEmpty()) mPackageMap.remove(uid); } } // Push state to destination if (mDest == null) { return; } var state = new UidPackageState(); state.uid = uid; state.packageNames = packages != null ? List.copyOf(packages) : Collections.emptyList(); try { mDest.updatePackagesForUid(state); } catch (RemoteException e) { // We will re-init the state when the service comes back up mDest = null; } } } /** Called when full syncing package state to audioserver. */ @GuardedBy("mLock") private void resetNativePackageState() { if (mDest == null) return; List<UidPackageState> states = mPackageMap.entrySet().stream() .map( entry -> { UidPackageState state = new UidPackageState(); state.uid = entry.getKey(); state.packageNames = List.copyOf(entry.getValue()); return state; }) .toList(); try { mDest.populatePackagesForUids(states); } catch (RemoteException e) { // We will re-init the state when the service comes back up mDest = null; } } /** * Aggregation operation on all package states list: groups by states by app-id and merges the * packageName for each state into an ArraySet. */ private static Map<Integer, Set<String>> generatePackageMappings( Collection<PackageState> appInfos) { Collector<PackageState, Object, Set<String>> reducer = Collectors.mapping( (PackageState p) -> p.getPackageName(), Collectors.toCollection(() -> new ArraySet(1))); return appInfos.stream() .collect( Collectors.groupingBy( /* predicate */ (PackageState p) -> p.getAppId(), /* factory */ HashMap::new, /* downstream collector */ reducer)); } }
services/core/java/com/android/server/audio/AudioService.java +67 −23 Original line number Diff line number Diff line Loading @@ -31,6 +31,10 @@ import static android.Manifest.permission.MODIFY_PHONE_STATE; import static android.Manifest.permission.QUERY_AUDIO_STATE; import static android.Manifest.permission.WRITE_SETTINGS; import static android.app.BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT; import static android.content.Intent.ACTION_PACKAGE_ADDED; import static android.content.Intent.ACTION_PACKAGE_REMOVED; import static android.content.Intent.EXTRA_ARCHIVAL; import static android.content.Intent.EXTRA_REPLACING; 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 @@ -59,6 +63,7 @@ import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import static com.android.media.audio.Flags.absVolumeIndexFix; import static com.android.media.audio.Flags.alarmMinVolumeZero; import static com.android.media.audio.Flags.audioserverPermissions; import static com.android.media.audio.Flags.disablePrescaleAbsoluteVolume; import static com.android.media.audio.Flags.ringerModeAffectsAlarm; import static com.android.media.audio.Flags.setStreamVolumeOrder; Loading Loading @@ -240,15 +245,18 @@ import com.android.internal.os.SomeArgs; import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; import com.android.server.EventLogTags; import com.android.server.LocalManagerRegistry; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.audio.AudioServiceEvents.DeviceVolumeEvent; import com.android.server.audio.AudioServiceEvents.PhoneStateEvent; import com.android.server.audio.AudioServiceEvents.VolChangedBroadcastEvent; import com.android.server.audio.AudioServiceEvents.VolumeEvent; import com.android.server.pm.PackageManagerLocal; import com.android.server.pm.UserManagerInternal; import com.android.server.pm.UserManagerInternal.UserRestrictionsListener; import com.android.server.pm.UserManagerService; import com.android.server.pm.pkg.PackageState; import com.android.server.utils.EventLogger; import com.android.server.wm.ActivityTaskManagerInternal; Loading @@ -273,6 +281,7 @@ import java.util.Objects; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BooleanSupplier; Loading Loading @@ -303,6 +312,8 @@ public class AudioService extends IAudioService.Stub private final SettingsAdapter mSettings; private final AudioPolicyFacade mAudioPolicy; private final AudioServerPermissionProvider mPermissionProvider; private final MusicFxHelper mMusicFxHelper; /** Debug audio mode */ Loading Loading @@ -1021,14 +1032,22 @@ public class AudioService extends IAudioService.Stub public Lifecycle(Context context) { super(context); var audioserverLifecycleExecutor = Executors.newSingleThreadExecutor(); var audioPolicyFacade = new DefaultAudioPolicyFacade(audioserverLifecycleExecutor); mService = new AudioService(context, AudioSystemAdapter.getDefaultAdapter(), SystemServerAdapter.getDefaultAdapter(context), SettingsAdapter.getDefaultAdapter(), new AudioVolumeGroupHelper(), new DefaultAudioPolicyFacade(r -> r.run()), null); audioPolicyFacade, null, context.getSystemService(AppOpsManager.class), PermissionEnforcer.fromContext(context), audioserverPermissions() ? initializeAudioServerPermissionProvider( context, audioPolicyFacade, audioserverLifecycleExecutor) : null ); } @Override Loading Loading @@ -1102,25 +1121,6 @@ public class AudioService extends IAudioService.Stub /////////////////////////////////////////////////////////////////////////// /** * @param context * @param audioSystem Adapter for {@link AudioSystem} * @param systemServer Adapter for privileged functionality for system server components * @param settings Adapter for {@link Settings} * @param audioVolumeGroupHelper Adapter for {@link AudioVolumeGroup} * @param audioPolicy Interface of a facade to IAudioPolicyManager * @param looper Looper to use for the service's message handler. If this is null, an * {@link AudioSystemThread} is created as the messaging thread instead. */ public AudioService(Context context, AudioSystemAdapter audioSystem, SystemServerAdapter systemServer, SettingsAdapter settings, AudioVolumeGroupHelperBase audioVolumeGroupHelper, AudioPolicyFacade audioPolicy, @Nullable Looper looper) { this (context, audioSystem, systemServer, settings, audioVolumeGroupHelper, audioPolicy, looper, context.getSystemService(AppOpsManager.class), PermissionEnforcer.fromContext(context)); } /** * @param context * @param audioSystem Adapter for {@link AudioSystem} Loading @@ -1137,13 +1137,16 @@ public class AudioService extends IAudioService.Stub public AudioService(Context context, AudioSystemAdapter audioSystem, SystemServerAdapter systemServer, SettingsAdapter settings, AudioVolumeGroupHelperBase audioVolumeGroupHelper, AudioPolicyFacade audioPolicy, @Nullable Looper looper, AppOpsManager appOps, @NonNull PermissionEnforcer enforcer) { @Nullable Looper looper, AppOpsManager appOps, @NonNull PermissionEnforcer enforcer, /* @NonNull */ AudioServerPermissionProvider permissionProvider) { super(enforcer); sLifecycleLogger.enqueue(new EventLogger.StringEvent("AudioService()")); mContext = context; mContentResolver = context.getContentResolver(); mAppOps = appOps; mPermissionProvider = permissionProvider; mAudioSystem = audioSystem; mSystemServer = systemServer; mAudioVolumeGroupHelper = audioVolumeGroupHelper; Loading Loading @@ -4559,6 +4562,8 @@ public class AudioService extends IAudioService.Stub + featureSpatialAudioHeadtrackingLowLatency()); pw.println("\tandroid.media.audio.focusFreezeTestApi:" + focusFreezeTestApi()); pw.println("\tcom.android.media.audio.audioserverPermissions:" + audioserverPermissions()); pw.println("\tcom.android.media.audio.disablePrescaleAbsoluteVolume:" + disablePrescaleAbsoluteVolume()); pw.println("\tcom.android.media.audio.setStreamVolumeOrder:" Loading Loading @@ -11937,6 +11942,45 @@ public class AudioService extends IAudioService.Stub private static final String mMetricsId = MediaMetrics.Name.AUDIO_SERVICE + MediaMetrics.SEPARATOR; private static AudioServerPermissionProvider initializeAudioServerPermissionProvider( Context context, AudioPolicyFacade audioPolicy, Executor audioserverExecutor) { Collection<PackageState> packageStates = null; try (PackageManagerLocal.UnfilteredSnapshot snapshot = LocalManagerRegistry.getManager(PackageManagerLocal.class) .withUnfilteredSnapshot()) { packageStates = snapshot.getPackageStates().values(); } var provider = new AudioServerPermissionProvider(packageStates); audioPolicy.registerOnStartTask(() -> { provider.onServiceStart(audioPolicy.getPermissionController()); }); // Set up event listeners IntentFilter packageUpdateFilter = new IntentFilter(); packageUpdateFilter.addAction(ACTION_PACKAGE_ADDED); packageUpdateFilter.addAction(ACTION_PACKAGE_REMOVED); packageUpdateFilter.addDataScheme("package"); context.registerReceiverForAllUsers(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); String pkgName = intent.getData().getEncodedSchemeSpecificPart(); int uid = intent.getIntExtra(Intent.EXTRA_UID, Process.INVALID_UID); if (intent.getBooleanExtra(EXTRA_REPLACING, false) || intent.getBooleanExtra(EXTRA_ARCHIVAL, false)) return; if (action.equals(ACTION_PACKAGE_ADDED)) { audioserverExecutor.execute(() -> provider.onModifyPackageState(uid, pkgName, false /* isRemoved */)); } else if (action.equals(ACTION_PACKAGE_REMOVED)) { audioserverExecutor.execute(() -> provider.onModifyPackageState(uid, pkgName, true /* isRemoved */)); } } }, packageUpdateFilter, null, null); // main thread is fine, since dispatch on executor return provider; } // Inform AudioFlinger of our device's low RAM attribute private static void readAndSetLowRamDevice() {
services/core/java/com/android/server/audio/DefaultAudioPolicyFacade.java +25 −0 Original line number Diff line number Diff line Loading @@ -16,10 +16,15 @@ package com.android.server.audio; import android.annotation.Nullable; import android.media.IAudioPolicyService; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import com.android.media.permission.INativePermissionController; import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Function; Loading @@ -43,6 +48,7 @@ public class DefaultAudioPolicyFacade implements AudioPolicyFacade { (Function<IBinder, IAudioPolicyService>) IAudioPolicyService.Stub::asInterface, e); mServiceHolder.registerOnStartTask(i -> Binder.allowBlocking(i.asBinder())); } @Override Loading @@ -55,4 +61,23 @@ public class DefaultAudioPolicyFacade implements AudioPolicyFacade { throw new IllegalStateException(); } } @Override public @Nullable INativePermissionController getPermissionController() { IAudioPolicyService ap = mServiceHolder.checkService(); if (ap == null) return null; try { var res = Objects.requireNonNull(ap.getPermissionController()); Binder.allowBlocking(res.asBinder()); return res; } catch (RemoteException e) { mServiceHolder.attemptClear(ap.asBinder()); return null; } } @Override public void registerOnStartTask(Runnable task) { mServiceHolder.registerOnStartTask(unused -> task.run()); } }
services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java +4 −1 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.AppOpsManager; import android.content.Context; import android.content.res.Resources; import android.media.AudioDeviceAttributes; Loading @@ -37,6 +38,7 @@ import android.media.AudioManager; import android.media.AudioSystem; import android.media.IAudioDeviceVolumeDispatcher; import android.media.VolumeInfo; import android.os.PermissionEnforcer; import android.os.RemoteException; import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; Loading Loading @@ -98,7 +100,8 @@ public class AbsoluteVolumeBehaviorTest { mAudioService = new AudioService(mContext, mSpyAudioSystem, mSystemServer, mSettingsAdapter, mAudioVolumeGroupHelper, mMockAudioPolicy, mTestLooper.getLooper()) { mTestLooper.getLooper(), mock(AppOpsManager.class), mock(PermissionEnforcer.class), mock(AudioServerPermissionProvider.class)) { @Override public int getDeviceForStream(int stream) { return AudioSystem.DEVICE_OUT_SPEAKER; Loading