From ac48767f919e2a641e4ff5597f7bc24c6d481d41 Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Fri, 11 Nov 2016 10:05:18 -0800 Subject: [PATCH] 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 --- .../java/android/media/IVolumeController.aidl | 7 + media/java/android/media/VolumePolicy.java | 11 ++ packages/SystemUI/res/values/strings.xml | 1 + .../volume/VolumeDialogController.java | 17 +++ .../android/server/audio/AudioService.java | 125 ++++++++++++------ 5 files changed, 124 insertions(+), 37 deletions(-) diff --git a/media/java/android/media/IVolumeController.aidl b/media/java/android/media/IVolumeController.aidl index 90ac4168bba70..7f372654124da 100644 --- a/media/java/android/media/IVolumeController.aidl +++ b/media/java/android/media/IVolumeController.aidl @@ -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); } diff --git a/media/java/android/media/VolumePolicy.java b/media/java/android/media/VolumePolicy.java index 1d33128ba752d..bbcce82f2e2d9 100644 --- a/media/java/android/media/VolumePolicy.java +++ b/media/java/android/media/VolumePolicy.java @@ -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; diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 331d09e4537a2..5fec64777a057 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1120,6 +1120,7 @@ + Accessibility %s silent diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java index c20cc842b607e..5393d60e4eddb 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java @@ -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 { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index ac9545c06c104..67f361441af7d 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -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,44 +5879,67 @@ 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 - // - STREAM_MUSIC on phones during this period after music or talkback/voice search prompt - // stopped - private static final int DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS = 0; - private static final int TOUCH_EXPLORE_STREAM_TYPE_OVERRIDE_DELAY_MS = 1000; + // AudioService.getActiveStreamType() will return: + // - STREAM_NOTIFICATION on tablets during this period after a notification stopped + // - STREAM_MUSIC on phones during this period after music or talkback/voice search prompt + // stopped + 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; + private static int sStreamOverrideDelayMs; - static void init(Context ctxt) { - AccessibilityManager accessibilityManager = - (AccessibilityManager) ctxt.getSystemService(Context.ACCESSIBILITY_SERVICE); - updateDefaultStreamOverrideDelay( - accessibilityManager.isTouchExplorationEnabled()); - accessibilityManager.addTouchExplorationStateChangeListener( - new StreamOverride()); + @Override + public void onTouchExplorationStateChanged(boolean enabled) { + updateDefaultStreamOverrideDelay(enabled); + } + + private void updateDefaultStreamOverrideDelay(boolean touchExploreEnabled) { + if (touchExploreEnabled) { + sStreamOverrideDelayMs = TOUCH_EXPLORE_STREAM_TYPE_OVERRIDE_DELAY_MS; + } else { + sStreamOverrideDelayMs = DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS; } + if (DEBUG_VOL) Log.d(TAG, "Touch exploration enabled=" + touchExploreEnabled + + " stream override delay is now " + sStreamOverrideDelayMs + " ms"); + } - @Override - public void onTouchExplorationStateChanged(boolean enabled) { - updateDefaultStreamOverrideDelay(enabled); - } + //--------------------------------------------------------------------------------- + // A11y: taking a11y state into account for the handling of a11y prompts volume + //--------------------------------------------------------------------------------- - private static void updateDefaultStreamOverrideDelay(boolean touchExploreEnabled) { - if (touchExploreEnabled) { - sDelayMs = TOUCH_EXPLORE_STREAM_TYPE_OVERRIDE_DELAY_MS; - } else { - sDelayMs = DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS; - } - if (DEBUG_VOL) Log.d(TAG, "Touch exploration enabled=" + touchExploreEnabled - + " stream override delay is now " + sDelayMs + " ms"); - } + 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); + } + } } /**