diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 387a2be41d3c4..f840f2d359d57 100755 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -185,6 +185,9 @@ public class AudioService extends IAudioService.Stub private static final String TAG = "AS.AudioService"; + private final AudioSystemAdapter mAudioSystem; + private final SystemServerAdapter mSystemServer; + /** Debug audio mode */ protected static final boolean DEBUG_MODE = false; @@ -649,10 +652,19 @@ public class AudioService extends IAudioService.Stub /** @hide */ public AudioService(Context context) { + this(context, AudioSystemAdapter.getDefaultAdapter(), + SystemServerAdapter.getDefaultAdapter(context)); + } + + public AudioService(Context context, AudioSystemAdapter audioSystem, + SystemServerAdapter systemServer) { mContext = context; mContentResolver = context.getContentResolver(); mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE); + mAudioSystem = audioSystem; + mSystemServer = systemServer; + mPlatformType = AudioSystem.getPlatformType(context); mIsSingleVolume = AudioSystem.isSingleVolume(context); @@ -842,11 +854,13 @@ public class AudioService extends IAudioService.Stub context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null); - LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal()); + if (mSystemServer.isPrivileged()) { + LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal()); - mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener); + mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener); - mRecordMonitor.initMonitor(); + mRecordMonitor.initMonitor(); + } final float[] preScale = new float[3]; preScale[0] = mContext.getResources().getFraction( @@ -935,7 +949,7 @@ public class AudioService extends IAudioService.Stub onIndicateSystemReady(); - mMicMuteFromSystemCached = AudioSystem.isMicrophoneMuted(); + mMicMuteFromSystemCached = mAudioSystem.isMicrophoneMuted(); setMicMuteFromSwitchInput(); } @@ -1636,12 +1650,15 @@ public class AudioService extends IAudioService.Stub } if (currentImeUid != mCurrentImeUid || forceUpdate) { - AudioSystem.setCurrentImeUid(currentImeUid); + mAudioSystem.setCurrentImeUid(currentImeUid); mCurrentImeUid = currentImeUid; } } private void readPersistedSettings() { + if (!mSystemServer.isPrivileged()) { + return; + } final ContentResolver cr = mContentResolver; int ringerModeFromSettings = @@ -1712,6 +1729,9 @@ public class AudioService extends IAudioService.Stub } private void readUserRestrictions() { + if (!mSystemServer.isPrivileged()) { + return; + } final int currentUser = getCurrentUserId(); // Check the current user restriction. @@ -2782,6 +2802,9 @@ public class AudioService extends IAudioService.Stub } private void sendBroadcastToAll(Intent intent) { + if (!mSystemServer.isPrivileged()) { + return; + } intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); final long ident = Binder.clearCallingIdentity(); @@ -3174,12 +3197,12 @@ public class AudioService extends IAudioService.Stub } // only mute for the current user if (getCurrentUserId() == userId || userId == android.os.Process.SYSTEM_UID) { - final boolean currentMute = AudioSystem.isMicrophoneMuted(); + final boolean currentMute = mAudioSystem.isMicrophoneMuted(); final long identity = Binder.clearCallingIdentity(); - final int ret = AudioSystem.muteMicrophone(muted); + final int ret = mAudioSystem.muteMicrophone(muted); // update cache with the real state independently from what was set - mMicMuteFromSystemCached = AudioSystem.isMicrophoneMuted(); + mMicMuteFromSystemCached = mAudioSystem.isMicrophoneMuted(); if (ret != AudioSystem.AUDIO_STATUS_OK) { Log.e(TAG, "Error changing mic mute state to " + muted + " current:" + mMicMuteFromSystemCached); @@ -4518,6 +4541,9 @@ public class AudioService extends IAudioService.Stub } private void broadcastRingerMode(String action, int ringerMode) { + if (!mSystemServer.isPrivileged()) { + return; + } // Send sticky broadcast Intent broadcast = new Intent(action); broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, ringerMode); @@ -4527,6 +4553,9 @@ public class AudioService extends IAudioService.Stub } private void broadcastVibrateSetting(int vibrateType) { + if (!mSystemServer.isPrivileged()) { + return; + } // Send broadcast if (mActivityManagerInternal.isSystemReady()) { Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION); @@ -5258,6 +5287,9 @@ public class AudioService extends IAudioService.Stub } public int observeDevicesForStream_syncVSS(boolean checkOthers) { + if (!mSystemServer.isPrivileged()) { + return AudioSystem.DEVICE_NONE; + } final int devices = AudioSystem.getDevicesForStream(mStreamType); if (devices == mObservedDevices) { return devices; @@ -5998,10 +6030,7 @@ public class AudioService extends IAudioService.Stub break; case MSG_BROADCAST_MICROPHONE_MUTE: - mContext.sendBroadcastAsUser( - new Intent(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED) - .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), - UserHandle.ALL); + mSystemServer.sendMicrophoneMuteChangedIntent(); break; } } diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java index 9f8f9f8fddde4..40c13904fbc95 100644 --- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java +++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java @@ -40,10 +40,11 @@ public class AudioSystemAdapter { /** * Create an adapter for AudioSystem that always succeeds, and does nothing. - * @return a no-op AudioSystem adapter + * Overridden methods can be configured + * @return a no-op AudioSystem adapter with configurable adapter */ - static final @NonNull AudioSystemAdapter getAlwaysOkAdapter() { - return new AudioSystemOkAdapter(); + static final @NonNull AudioSystemAdapter getConfigurableAdapter() { + return new AudioSystemConfigurableAdapter(); } /** @@ -113,10 +114,51 @@ public class AudioSystemAdapter { return AudioSystem.setParameters(keyValuePairs); } - //-------------------------------------------------------------------- - protected static class AudioSystemOkAdapter extends AudioSystemAdapter { - private static final String TAG = "ASA"; + /** + * Same as {@link AudioSystem#isMicrophoneMuted()}} + * Checks whether the microphone mute is on or off. + * @return true if microphone is muted, false if it's not + */ + public boolean isMicrophoneMuted() { + return AudioSystem.isMicrophoneMuted(); + } + /** + * Same as {@link AudioSystem#muteMicrophone(boolean)} + * Sets the microphone mute on or off. + * + * @param on set true to mute the microphone; + * false to turn mute off + * @return command completion status see AUDIO_STATUS_OK, see AUDIO_STATUS_ERROR + */ + public int muteMicrophone(boolean on) { + return AudioSystem.muteMicrophone(on); + } + + /** + * Same as {@link AudioSystem#setCurrentImeUid(int)} + * Communicate UID of current InputMethodService to audio policy service. + */ + public int setCurrentImeUid(int uid) { + return AudioSystem.setCurrentImeUid(uid); + } + + //-------------------------------------------------------------------- + protected static class AudioSystemConfigurableAdapter extends AudioSystemAdapter { + private static final String TAG = "ASA"; + private boolean mIsMicMuted = false; + private boolean mMuteMicrophoneFails = false; + + public void configureIsMicrophoneMuted(boolean muted) { + mIsMicMuted = muted; + } + + public void configureMuteMicrophoneToFail(boolean fail) { + mMuteMicrophoneFails = fail; + } + + //----------------------------------------------------------------- + // Overrides of AudioSystemAdapter @Override public int setDeviceConnectionState(int device, int state, String deviceAddress, String deviceName, int codecFormat) { @@ -152,5 +194,24 @@ public class AudioSystemAdapter { public int setParameters(String keyValuePairs) { return AudioSystem.AUDIO_STATUS_OK; } + + @Override + public boolean isMicrophoneMuted() { + return mIsMicMuted; + } + + @Override + public int muteMicrophone(boolean on) { + if (mMuteMicrophoneFails) { + return AudioSystem.AUDIO_STATUS_ERROR; + } + mIsMicMuted = on; + return AudioSystem.AUDIO_STATUS_OK; + } + + @Override + public int setCurrentImeUid(int uid) { + return AudioSystem.AUDIO_STATUS_OK; + } } } diff --git a/services/core/java/com/android/server/audio/SystemServerAdapter.java b/services/core/java/com/android/server/audio/SystemServerAdapter.java new file mode 100644 index 0000000000000..509f6be76f171 --- /dev/null +++ b/services/core/java/com/android/server/audio/SystemServerAdapter.java @@ -0,0 +1,90 @@ +/* + * Copyright 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 com.android.server.audio; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.content.Intent; +import android.media.AudioManager; +import android.os.UserHandle; + +/** + * Provides an adapter to access functionality reserved to components running in system_server + * Functionality such as sending privileged broadcasts is to be accessed through the default + * adapter, whereas tests can inject a no-op adapter. + */ +public class SystemServerAdapter { + + protected final Context mContext; + + private SystemServerAdapter(@Nullable Context context) { + mContext = context; + } + /** + * Create a wrapper around privileged functionality. + * @return the adapter + */ + static final @NonNull SystemServerAdapter getDefaultAdapter(Context context) { + return new SystemServerAdapter(context); + } + + /** + * Create an adapter that does nothing. + * Use for running non-privileged tests, such as unit tests + * @return a no-op adapter + */ + static final @NonNull SystemServerAdapter getNoOpAdapter() { + return new NoOpSystemServerAdapter(); + } + + /** + * @return true if this is supposed to be run in system_server, false otherwise (e.g. for a + * unit test) + */ + public boolean isPrivileged() { + return true; + } + + /** + * Broadcast ACTION_MICROPHONE_MUTE_CHANGED + */ + public void sendMicrophoneMuteChangedIntent() { + mContext.sendBroadcastAsUser( + new Intent(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED) + .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), + UserHandle.ALL); + } + + //-------------------------------------------------------------------- + protected static class NoOpSystemServerAdapter extends SystemServerAdapter { + + NoOpSystemServerAdapter() { + super(null); + } + + @Override + public boolean isPrivileged() { + return false; + } + + @Override + public void sendMicrophoneMuteChangedIntent() { + // no-op + } + } +} diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index 109c1191523b2..270a3b50b9343 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -78,6 +78,7 @@ +