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

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

Conditional behavior for a11y stream volume

VolumePolicy: define two a11y modes: 1/ legacy where
  a11y volume aliases to media volume, 2/ a11y where
  the a11y volume is independent from any other stream
  type volume.
Refactor accessibility service listening to accomodate
  communicating to the volume controller when the
  a11y service is running.
Make accessibility stream alias conditional on a11y
  service running. Reflect the behavior in the
  volume controller.

Test: enable Talkback and verify logs in Vol controller
Bug 30448020

Change-Id: I80535ba259a22b6d93e62a7b3ca462a19d4c84b7
parent e3fb0afa
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -32,4 +32,11 @@ oneway interface IVolumeController {
    void setLayoutDirection(int layoutDirection);

    void dismiss();

    /**
     * Change the a11y mode.
     * @param a11yMode one of {@link VolumePolicy#A11Y_MODE_MEDIA_A11Y_VOLUME},
     *     {@link VolumePolicy#A11Y_MODE_INDEPENDENT_A11Y_VOLUME}
     */
    void setA11yMode(int mode);
}
+11 −0
Original line number Diff line number Diff line
@@ -25,6 +25,17 @@ import java.util.Objects;
public final class VolumePolicy implements Parcelable {
    public static final VolumePolicy DEFAULT = new VolumePolicy(false, false, true, 400);

    /**
     * Accessibility volume policy where the STREAM_MUSIC volume (i.e. media volume) affects
     * the STREAM_ACCESSIBILITY volume, and vice-versa.
     */
    public static final int A11Y_MODE_MEDIA_A11Y_VOLUME = 0;
    /**
     * Accessibility volume policy where the STREAM_ACCESSIBILITY volume is independent from
     * any other volume.
     */
    public static final int A11Y_MODE_INDEPENDENT_A11Y_VOLUME = 1;

    /** Allow volume adjustments lower from vibrate to enter ringer mode = silent */
    public final boolean volumeDownToEnterSilent;

+1 −0
Original line number Diff line number Diff line
@@ -1120,6 +1120,7 @@
        <item></item> <!-- STREAM_SYSTEM_ENFORCED -->
        <item></item> <!-- STREAM_DTMF -->
        <item></item> <!-- STREAM_TTS -->
        <item>Accessibility</item> <!-- STREAM_ACCESSIBILITY -->
    </string-array>

    <string name="volume_stream_muted" translatable="false">%s silent</string>
+17 −0
Original line number Diff line number Diff line
@@ -78,6 +78,7 @@ public class VolumeDialogController {
        AudioSystem.STREAM_SYSTEM_ENFORCED,
        AudioSystem.STREAM_TTS,
        AudioSystem.STREAM_VOICE_CALL,
        AudioSystem.STREAM_ACCESSIBILITY,
    };

    private final HandlerThread mWorkerThread;
@@ -562,6 +563,22 @@ public class VolumeDialogController {
                    .sendToTarget();
            mWorker.sendEmptyMessage(W.DISMISS_REQUESTED);
        }

        @Override
        public void setA11yMode(int mode) {
            if (D.BUG) Log.d(TAG, "setA11yMode to " + mode);
            if (mDestroyed) return;
            switch (mode) {
                case VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME:
                    // "legacy" mode
                    break;
                case VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME:
                    break;
                default:
                    Log.e(TAG, "Invalid accessibility mode " + mode);
                    break;
            }
        }
    }

    private final class W extends Handler {
+88 −37
Original line number Diff line number Diff line
@@ -139,7 +139,9 @@ import java.util.Objects;
 *
 * @hide
 */
public class AudioService extends IAudioService.Stub {
public class AudioService extends IAudioService.Stub
        implements AccessibilityManager.TouchExplorationStateChangeListener,
            AccessibilityManager.AccessibilityStateChangeListener{

    private static final String TAG = "AudioService";

@@ -775,7 +777,7 @@ public class AudioService extends IAudioService.Stub {
                TAG,
                SAFE_VOLUME_CONFIGURE_TIMEOUT_MS);

        StreamOverride.init(mContext);
        initA11yMonitoring(mContext);
        mControllerService.init();
        onIndicateSystemReady();
    }
@@ -972,6 +974,8 @@ public class AudioService extends IAudioService.Stub {

    private void updateStreamVolumeAlias(boolean updateVolumes, String caller) {
        int dtmfStreamAlias;
        final int a11yStreamAlias = sIndependentA11yVolume ?
                AudioSystem.STREAM_ACCESSIBILITY : AudioSystem.STREAM_MUSIC;

        if (mIsSingleVolume) {
            mStreamVolumeAlias = STREAM_VOLUME_ALIAS_TELEVISION;
@@ -1000,9 +1004,13 @@ public class AudioService extends IAudioService.Stub {
        }

        mStreamVolumeAlias[AudioSystem.STREAM_DTMF] = dtmfStreamAlias;
        mStreamVolumeAlias[AudioSystem.STREAM_ACCESSIBILITY] = a11yStreamAlias;

        if (updateVolumes) {
            mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias],
                    caller);
            mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].setAllIndexes(
                    mStreamStates[a11yStreamAlias], caller);
            // apply stream mute states according to new value of mRingerModeAffectedStreams
            setRingerModeInt(getRingerModeInternal(), false);
            sendMsg(mAudioHandler,
@@ -1011,6 +1019,12 @@ public class AudioService extends IAudioService.Stub {
                    0,
                    0,
                    mStreamStates[AudioSystem.STREAM_DTMF], 0);
            sendMsg(mAudioHandler,
                    MSG_SET_ALL_VOLUMES,
                    SENDMSG_QUEUE,
                    0,
                    0,
                    mStreamStates[AudioSystem.STREAM_ACCESSIBILITY], 0);
        }
    }

@@ -1536,6 +1550,10 @@ public class AudioService extends IAudioService.Stub {

    private void setStreamVolume(int streamType, int index, int flags, String callingPackage,
            String caller, int uid) {
        if (DEBUG_VOL) {
            Log.d(TAG, "setStreamVolume(stream=" + streamType+", index=" + index
                    + ", calling=" + callingPackage + ")");
        }
        if (mUseFixedVolume) {
            return;
        }
@@ -3639,7 +3657,7 @@ public class AudioService extends IAudioService.Stub {
                    return AudioSystem.STREAM_VOICE_CALL;
                }
            } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
                if (isAfMusicActiveRecently(StreamOverride.sDelayMs)) {
                if (isAfMusicActiveRecently(sStreamOverrideDelayMs)) {
                    if (DEBUG_VOL)
                        Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
                    return AudioSystem.STREAM_MUSIC;
@@ -3665,13 +3683,13 @@ public class AudioService extends IAudioService.Stub {
                    return AudioSystem.STREAM_VOICE_CALL;
                }
            } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION,
                    StreamOverride.sDelayMs) ||
                    sStreamOverrideDelayMs) ||
                    AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
                            StreamOverride.sDelayMs)) {
                            sStreamOverrideDelayMs)) {
                if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
                return AudioSystem.STREAM_NOTIFICATION;
            } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
                if (isAfMusicActiveRecently(StreamOverride.sDelayMs)) {
                if (isAfMusicActiveRecently(sStreamOverrideDelayMs)) {
                    if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: forcing STREAM_MUSIC");
                    return AudioSystem.STREAM_MUSIC;
                } else {
@@ -5861,11 +5879,21 @@ public class AudioService extends IAudioService.Stub {
    }

    //==========================================================================================
    // Accessibility: taking touch exploration into account for selecting the default
    // Accessibility

    private void initA11yMonitoring(Context ctxt) {
        AccessibilityManager accessibilityManager =
                (AccessibilityManager) ctxt.getSystemService(Context.ACCESSIBILITY_SERVICE);
        updateDefaultStreamOverrideDelay(accessibilityManager.isTouchExplorationEnabled());
        updateA11yVolumeAlias(accessibilityManager.isEnabled());
        accessibilityManager.addTouchExplorationStateChangeListener(this);
        accessibilityManager.addAccessibilityStateChangeListener(this);
    }

    //---------------------------------------------------------------------------------
    // A11y: taking touch exploration into account for selecting the default
    //   stream override timeout when adjusting volume
    //==========================================================================================
    private static class StreamOverride
            implements AccessibilityManager.TouchExplorationStateChangeListener {
    //---------------------------------------------------------------------------------

    // AudioService.getActiveStreamType() will return:
    // - STREAM_NOTIFICATION on tablets during this period after a notification stopped
@@ -5874,31 +5902,44 @@ public class AudioService extends IAudioService.Stub {
    private static final int DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS = 0;
    private static final int TOUCH_EXPLORE_STREAM_TYPE_OVERRIDE_DELAY_MS = 1000;

        static int sDelayMs;

        static void init(Context ctxt) {
            AccessibilityManager accessibilityManager =
                    (AccessibilityManager) ctxt.getSystemService(Context.ACCESSIBILITY_SERVICE);
            updateDefaultStreamOverrideDelay(
                    accessibilityManager.isTouchExplorationEnabled());
            accessibilityManager.addTouchExplorationStateChangeListener(
                    new StreamOverride());
        }
    private static int sStreamOverrideDelayMs;

    @Override
    public void onTouchExplorationStateChanged(boolean enabled) {
        updateDefaultStreamOverrideDelay(enabled);
    }

        private static void updateDefaultStreamOverrideDelay(boolean touchExploreEnabled) {
    private void updateDefaultStreamOverrideDelay(boolean touchExploreEnabled) {
        if (touchExploreEnabled) {
                sDelayMs = TOUCH_EXPLORE_STREAM_TYPE_OVERRIDE_DELAY_MS;
            sStreamOverrideDelayMs = TOUCH_EXPLORE_STREAM_TYPE_OVERRIDE_DELAY_MS;
        } else {
                sDelayMs = DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS;
            sStreamOverrideDelayMs = DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS;
        }
        if (DEBUG_VOL) Log.d(TAG, "Touch exploration enabled=" + touchExploreEnabled
                    + " stream override delay is now " + sDelayMs + " ms");
                + " stream override delay is now " + sStreamOverrideDelayMs + " ms");
    }

    //---------------------------------------------------------------------------------
    // A11y: taking a11y state into account for the handling of a11y prompts volume
    //---------------------------------------------------------------------------------

    private static boolean sIndependentA11yVolume = false;

    @Override
    public void onAccessibilityStateChanged(boolean enabled) {
        updateA11yVolumeAlias(enabled);
    }

    private void updateA11yVolumeAlias(boolean a11Enabled) {
        if (DEBUG_VOL) Log.d(TAG, "Accessibility mode changed to " + a11Enabled);
        // a11y has its own volume stream when a11y service is enabled
        sIndependentA11yVolume = a11Enabled;
        // update the volume mapping scheme
        updateStreamVolumeAlias(true /*updateVolumes*/, TAG);
        // update the volume controller behavior
        mVolumeController.setA11yMode(sIndependentA11yVolume ?
                VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME :
                    VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME);
    }

    //==========================================================================================
@@ -6178,6 +6219,16 @@ public class AudioService extends IAudioService.Stub {
                Log.w(TAG, "Error calling dismiss", e);
            }
        }

        public void setA11yMode(int a11yMode) {
            if (mController == null)
                return;
            try {
                mController.setA11yMode(a11yMode);
            } catch (RemoteException e) {
                Log.w(TAG, "Error calling setA11Mode", e);
            }
        }
    }

    /**