Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 25b0e6bb authored by Jean-Michel Trivi's avatar Jean-Michel Trivi
Browse files

AudioService: test mic muting API behavior with errors

 Implement unit test for AudioService's mic muting API.
 This requires being able to inject existing
AudioSystemAdapter in AudioService class, and creating an adapter
for operations that require running in system_server.

Bug: 153103117
Test: atest AudioServiceTest
Change-Id: I92538f585c5567718a4bad5666b2efafd446f4d3
Merged-In: I92538f585c5567718a4bad5666b2efafd446f4d3
parent ab218872
Loading
Loading
Loading
Loading
+41 −12
Original line number Diff line number Diff line
@@ -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);

        if (mSystemServer.isPrivileged()) {
            LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal());

            mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);

            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;
            }
        }
+65 −4
Original line number Diff line number Diff line
@@ -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);
    }

    /**
     * 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 <var>true</var> to mute the microphone;
     *           <var>false</var> 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 AudioSystemOkAdapter extends AudioSystemAdapter {
    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;
        }
    }
}
+90 −0
Original line number Diff line number Diff line
/*
 * 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
        }
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -78,6 +78,7 @@
    <uses-permission android:name="android.permission.DUMP"/>
    <uses-permission android:name="android.permission.READ_DREAM_STATE"/>
    <uses-permission android:name="android.permission.WRITE_DREAM_STATE"/>
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>

    <!-- Uses API introduced in O (26) -->
    <uses-sdk android:minSdkVersion="1"
+1 −1
Original line number Diff line number Diff line
@@ -66,7 +66,7 @@ public class AudioDeviceBrokerTest {
        mContext = InstrumentationRegistry.getTargetContext();

        mMockAudioService = mock(AudioService.class);
        mSpyAudioSystem = spy(AudioSystemAdapter.getAlwaysOkAdapter());
        mSpyAudioSystem = spy(AudioSystemAdapter.getConfigurableAdapter());
        mSpyDevInventory = spy(new AudioDeviceInventory(mSpyAudioSystem));
        mAudioDeviceBroker = new AudioDeviceBroker(mContext, mMockAudioService, mSpyDevInventory);
        mSpyDevInventory.setDeviceBroker(mAudioDeviceBroker);
Loading