diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 435e6ba159e91..65eadb6e59a71 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -801,8 +801,8 @@ public class AudioManager {
* management of audio settings or the main telephony application.
*
* @param streamType The stream type to adjust. One of {@link #STREAM_VOICE_CALL},
- * {@link #STREAM_SYSTEM}, {@link #STREAM_RING}, {@link #STREAM_MUSIC} or
- * {@link #STREAM_ALARM}
+ * {@link #STREAM_SYSTEM}, {@link #STREAM_RING}, {@link #STREAM_MUSIC},
+ * {@link #STREAM_ALARM} or {@link #STREAM_ACCESSIBILITY}.
* @param direction The direction to adjust the volume. One of
* {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
* {@link #ADJUST_SAME}.
@@ -3195,7 +3195,8 @@ public class AudioManager {
* {@link #STREAM_MUSIC},
* {@link #STREAM_ALARM},
* {@link #STREAM_NOTIFICATION},
- * {@link #STREAM_DTMF}.
+ * {@link #STREAM_DTMF},
+ * {@link #STREAM_ACCESSIBILITY}.
*
* @return The bit-mask "or" of audio output device codes for all enabled devices on this
* stream. Zero or more of
@@ -3238,6 +3239,7 @@ public class AudioManager {
case STREAM_ALARM:
case STREAM_NOTIFICATION:
case STREAM_DTMF:
+ case STREAM_ACCESSIBILITY:
return AudioSystem.getDevicesForStream(streamType);
default:
return 0;
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);
+ }
+ }
}
/**