1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.volume;
18 
19 import static android.media.AudioManager.RINGER_MODE_NORMAL;
20 
21 import android.app.ActivityManager;
22 import android.app.KeyguardManager;
23 import android.app.NotificationManager;
24 import android.content.BroadcastReceiver;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.content.pm.ApplicationInfo;
30 import android.content.pm.PackageManager;
31 import android.content.pm.PackageManager.NameNotFoundException;
32 import android.database.ContentObserver;
33 import android.media.AudioAttributes;
34 import android.media.AudioManager;
35 import android.media.AudioSystem;
36 import android.media.IAudioService;
37 import android.media.IVolumeController;
38 import android.media.MediaRoute2Info;
39 import android.media.MediaRouter2Manager;
40 import android.media.RoutingSessionInfo;
41 import android.media.VolumePolicy;
42 import android.media.session.MediaController;
43 import android.media.session.MediaController.PlaybackInfo;
44 import android.media.session.MediaSession.Token;
45 import android.net.Uri;
46 import android.os.Handler;
47 import android.os.HandlerExecutor;
48 import android.os.Looper;
49 import android.os.Message;
50 import android.os.RemoteException;
51 import android.os.VibrationEffect;
52 import android.provider.Settings;
53 import android.service.notification.Condition;
54 import android.service.notification.ZenModeConfig;
55 import android.util.ArrayMap;
56 import android.util.Log;
57 import android.util.Slog;
58 import android.view.accessibility.AccessibilityManager;
59 import android.view.accessibility.CaptioningManager;
60 
61 import androidx.annotation.NonNull;
62 import androidx.lifecycle.Observer;
63 
64 import com.android.internal.annotations.GuardedBy;
65 import com.android.settingslib.volume.MediaSessions;
66 import com.android.systemui.Dumpable;
67 import com.android.systemui.broadcast.BroadcastDispatcher;
68 import com.android.systemui.dagger.SysUISingleton;
69 import com.android.systemui.dump.DumpManager;
70 import com.android.systemui.keyguard.WakefulnessLifecycle;
71 import com.android.systemui.plugins.VolumeDialogController;
72 import com.android.systemui.qs.tiles.DndTile;
73 import com.android.systemui.res.R;
74 import com.android.systemui.settings.UserTracker;
75 import com.android.systemui.statusbar.VibratorHelper;
76 import com.android.systemui.util.RingerModeLiveData;
77 import com.android.systemui.util.RingerModeTracker;
78 import com.android.systemui.util.concurrency.ThreadFactory;
79 
80 import dalvik.annotation.optimization.NeverCompile;
81 
82 import java.io.PrintWriter;
83 import java.util.HashMap;
84 import java.util.List;
85 import java.util.Map;
86 import java.util.Objects;
87 import java.util.concurrent.ConcurrentHashMap;
88 import java.util.concurrent.atomic.AtomicReference;
89 
90 import javax.inject.Inject;
91 
92 /**
93  *  Source of truth for all state / events related to the volume dialog.  No presentation.
94  *
95  *  All work done on a dedicated background worker thread & associated worker.
96  *
97  *  Methods ending in "W" must be called on the worker thread.
98  */
99 @SysUISingleton
100 public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpable {
101     private static final String TAG = Util.logTag(VolumeDialogControllerImpl.class);
102     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
103 
104     private static final int TOUCH_FEEDBACK_TIMEOUT_MS = 1000;
105     private static final int DYNAMIC_STREAM_START_INDEX = 100;
106     private static final AudioAttributes SONIFICIATION_VIBRATION_ATTRIBUTES =
107             new AudioAttributes.Builder()
108                     .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
109                     .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
110                     .build();
111 
112     static final ArrayMap<Integer, Integer> STREAMS = new ArrayMap<>();
113     static {
STREAMS.put(AudioSystem.STREAM_ALARM, R.string.stream_alarm)114         STREAMS.put(AudioSystem.STREAM_ALARM, R.string.stream_alarm);
STREAMS.put(AudioSystem.STREAM_BLUETOOTH_SCO, R.string.stream_bluetooth_sco)115         STREAMS.put(AudioSystem.STREAM_BLUETOOTH_SCO, R.string.stream_bluetooth_sco);
STREAMS.put(AudioSystem.STREAM_DTMF, R.string.stream_dtmf)116         STREAMS.put(AudioSystem.STREAM_DTMF, R.string.stream_dtmf);
STREAMS.put(AudioSystem.STREAM_MUSIC, R.string.stream_music)117         STREAMS.put(AudioSystem.STREAM_MUSIC, R.string.stream_music);
STREAMS.put(AudioSystem.STREAM_ACCESSIBILITY, R.string.stream_accessibility)118         STREAMS.put(AudioSystem.STREAM_ACCESSIBILITY, R.string.stream_accessibility);
STREAMS.put(AudioSystem.STREAM_NOTIFICATION, R.string.stream_notification)119         STREAMS.put(AudioSystem.STREAM_NOTIFICATION, R.string.stream_notification);
STREAMS.put(AudioSystem.STREAM_RING, R.string.stream_ring)120         STREAMS.put(AudioSystem.STREAM_RING, R.string.stream_ring);
STREAMS.put(AudioSystem.STREAM_SYSTEM, R.string.stream_system)121         STREAMS.put(AudioSystem.STREAM_SYSTEM, R.string.stream_system);
STREAMS.put(AudioSystem.STREAM_SYSTEM_ENFORCED, R.string.stream_system_enforced)122         STREAMS.put(AudioSystem.STREAM_SYSTEM_ENFORCED, R.string.stream_system_enforced);
STREAMS.put(AudioSystem.STREAM_TTS, R.string.stream_tts)123         STREAMS.put(AudioSystem.STREAM_TTS, R.string.stream_tts);
STREAMS.put(AudioSystem.STREAM_VOICE_CALL, R.string.stream_voice_call)124         STREAMS.put(AudioSystem.STREAM_VOICE_CALL, R.string.stream_voice_call);
125     }
126 
127     private final W mWorker;
128     private final Context mContext;
129     private final Looper mWorkerLooper;
130     private final PackageManager mPackageManager;
131     private final MediaRouter2Manager mRouter2Manager;
132     private final WakefulnessLifecycle mWakefulnessLifecycle;
133     private final AudioManager mAudio;
134     private final IAudioService mAudioService;
135     private final NotificationManager mNoMan;
136     private final SettingObserver mObserver;
137     private final Receiver mReceiver = new Receiver();
138     private final RingerModeObservers mRingerModeObservers;
139     private final MediaSessions mMediaSessions;
140     private final AtomicReference<CaptioningManager> mCaptioningManager = new AtomicReference<>();
141     private final KeyguardManager mKeyguardManager;
142     private final ActivityManager mActivityManager;
143     private final UserTracker mUserTracker;
144     protected C mCallbacks = new C();
145     private final State mState = new State();
146     protected final MediaSessionsCallbacks mMediaSessionsCallbacksW;
147     private final VibratorHelper mVibrator;
148     private final boolean mHasVibrator;
149     private boolean mShowA11yStream;
150     private boolean mShowVolumeDialog;
151     private boolean mShowSafetyWarning;
152     private long mLastToggledRingerOn;
153     private boolean mDeviceInteractive = true;
154 
155     private VolumePolicy mVolumePolicy;
156     @GuardedBy("this")
157     private UserActivityListener mUserActivityListener;
158 
159     protected final VC mVolumeController = new VC();
160     protected final BroadcastDispatcher mBroadcastDispatcher;
161 
162     private final WakefulnessLifecycle.Observer mWakefullnessLifecycleObserver =
163             new WakefulnessLifecycle.Observer() {
164                 @Override
165                 public void onStartedWakingUp() {
166                     mDeviceInteractive = true;
167                 }
168 
169                 @Override
170                 public void onFinishedGoingToSleep() {
171                     mDeviceInteractive = false;
172                 }
173             };
174 
175     @Inject
VolumeDialogControllerImpl( Context context, BroadcastDispatcher broadcastDispatcher, RingerModeTracker ringerModeTracker, ThreadFactory theadFactory, AudioManager audioManager, NotificationManager notificationManager, VibratorHelper vibrator, IAudioService iAudioService, AccessibilityManager accessibilityManager, PackageManager packageManager, WakefulnessLifecycle wakefulnessLifecycle, KeyguardManager keyguardManager, ActivityManager activityManager, UserTracker userTracker, DumpManager dumpManager )176     public VolumeDialogControllerImpl(
177             Context context,
178             BroadcastDispatcher broadcastDispatcher,
179             RingerModeTracker ringerModeTracker,
180             ThreadFactory theadFactory,
181             AudioManager audioManager,
182             NotificationManager notificationManager,
183             VibratorHelper vibrator,
184             IAudioService iAudioService,
185             AccessibilityManager accessibilityManager,
186             PackageManager packageManager,
187             WakefulnessLifecycle wakefulnessLifecycle,
188             KeyguardManager keyguardManager,
189             ActivityManager activityManager,
190             UserTracker userTracker,
191             DumpManager dumpManager
192     ) {
193         mContext = context.getApplicationContext();
194         mPackageManager = packageManager;
195         mWakefulnessLifecycle = wakefulnessLifecycle;
196         Events.writeEvent(Events.EVENT_COLLECTION_STARTED);
197         mWorkerLooper = theadFactory.buildLooperOnNewThread(
198                 VolumeDialogControllerImpl.class.getSimpleName());
199         mWorker = new W(mWorkerLooper);
200         mRouter2Manager = MediaRouter2Manager.getInstance(mContext);
201         mMediaSessionsCallbacksW = new MediaSessionsCallbacks(mContext);
202         mMediaSessions = createMediaSessions(mContext, mWorkerLooper, mMediaSessionsCallbacksW);
203         mAudio = audioManager;
204         mNoMan = notificationManager;
205         mObserver = new SettingObserver(mWorker);
206         mRingerModeObservers = new RingerModeObservers(
207                 (RingerModeLiveData) ringerModeTracker.getRingerMode(),
208                 (RingerModeLiveData) ringerModeTracker.getRingerModeInternal()
209         );
210         mRingerModeObservers.init();
211         mBroadcastDispatcher = broadcastDispatcher;
212         mObserver.init();
213         mReceiver.init();
214         mVibrator = vibrator;
215         mHasVibrator = mVibrator.hasVibrator();
216         mAudioService = iAudioService;
217         mKeyguardManager = keyguardManager;
218         mActivityManager = activityManager;
219         mUserTracker = userTracker;
220         mUserTracker.addCallback(mUserChangedCallback, new HandlerExecutor(mWorker));
221         createCaptioningManagerServiceByUserContext(mUserTracker.getUserContext());
222 
223         dumpManager.registerDumpable("VolumeDialogControllerImpl", this);
224 
225         boolean accessibilityVolumeStreamActive = accessibilityManager
226                 .isAccessibilityVolumeStreamActive();
227         mVolumeController.setA11yMode(accessibilityVolumeStreamActive ?
228                 VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME :
229                 VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME);
230 
231         mWakefulnessLifecycle.addObserver(mWakefullnessLifecycleObserver);
232     }
233 
getAudioManager()234     public AudioManager getAudioManager() {
235         return mAudio;
236     }
237 
dismiss()238     public void dismiss() {
239         mCallbacks.onDismissRequested(Events.DISMISS_REASON_VOLUME_CONTROLLER);
240     }
241 
setVolumeController()242     protected void setVolumeController() {
243         try {
244             mAudio.setVolumeController(mVolumeController);
245         } catch (SecurityException e) {
246             Log.w(TAG, "Unable to set the volume controller", e);
247         }
248     }
249 
setAudioManagerStreamVolume(int stream, int level, int flag)250     protected void setAudioManagerStreamVolume(int stream, int level, int flag) {
251         mAudio.setStreamVolume(stream, level, flag);
252     }
253 
getAudioManagerStreamVolume(int stream)254     protected int getAudioManagerStreamVolume(int stream) {
255         return mAudio.getLastAudibleStreamVolume(stream);
256     }
257 
getAudioManagerStreamMaxVolume(int stream)258     protected int getAudioManagerStreamMaxVolume(int stream) {
259         return mAudio.getStreamMaxVolume(stream);
260     }
261 
getAudioManagerStreamMinVolume(int stream)262     protected int getAudioManagerStreamMinVolume(int stream) {
263         return mAudio.getStreamMinVolumeInt(stream);
264     }
265 
register()266     public void register() {
267         setVolumeController();
268         setVolumePolicy(mVolumePolicy);
269         showDndTile();
270         try {
271             mMediaSessions.init();
272         } catch (SecurityException e) {
273             Log.w(TAG, "No access to media sessions", e);
274         }
275     }
276 
setVolumePolicy(VolumePolicy policy)277     public void setVolumePolicy(VolumePolicy policy) {
278         mVolumePolicy = policy;
279         if (mVolumePolicy == null) return;
280         try {
281             mAudio.setVolumePolicy(mVolumePolicy);
282         } catch (NoSuchMethodError e) {
283             Log.w(TAG, "No volume policy api");
284         }
285     }
286 
createMediaSessions(Context context, Looper looper, MediaSessions.Callbacks callbacks)287     protected MediaSessions createMediaSessions(Context context, Looper looper,
288             MediaSessions.Callbacks callbacks) {
289         return new MediaSessions(context, looper, callbacks);
290     }
291 
292     @NeverCompile
dump(PrintWriter pw, String[] args)293     public void dump(PrintWriter pw, String[] args) {
294         pw.println(VolumeDialogControllerImpl.class.getSimpleName() + " state:");
295         pw.print("  mVolumePolicy: "); pw.println(mVolumePolicy);
296         pw.print("  mState: "); pw.println(mState.toString(4));
297         pw.print("  mHasVibrator: "); pw.println(mHasVibrator);
298         synchronized (mMediaSessionsCallbacksW.mRemoteStreams) {
299             pw.print("  mRemoteStreams: ");
300             pw.println(mMediaSessionsCallbacksW.mRemoteStreams
301                     .values());
302         }
303         pw.print("  mShowA11yStream: "); pw.println(mShowA11yStream);
304         pw.println();
305         mMediaSessions.dump(pw);
306     }
307 
addCallback(Callbacks callback, Handler handler)308     public void addCallback(Callbacks callback, Handler handler) {
309         mCallbacks.add(callback, handler);
310         callback.onAccessibilityModeChanged(mShowA11yStream);
311     }
312 
setUserActivityListener(UserActivityListener listener)313     public void setUserActivityListener(UserActivityListener listener) {
314         synchronized (this) {
315             mUserActivityListener = listener;
316         }
317     }
318 
removeCallback(Callbacks callback)319     public void removeCallback(Callbacks callback) {
320         mCallbacks.remove(callback);
321     }
322 
getState()323     public void getState() {
324         mWorker.sendEmptyMessage(W.GET_STATE);
325     }
326 
327     /**
328      * We met issues about the wrong state of System Caption in multi-user mode.
329      * It happened in the usage of CaptioningManager Service from SysUI process
330      * that is a global system process of User 0.
331      * Therefore, we have to add callback on UserTracker that allows us to get the Context of
332      * active User and then get the corresponding CaptioningManager Service for further usages.
333      */
334     private final UserTracker.Callback mUserChangedCallback =
335             new UserTracker.Callback() {
336                 @Override
337                 public void onUserChanged(int newUser, @NonNull Context userContext) {
338                     createCaptioningManagerServiceByUserContext(userContext);
339                 }
340             };
341 
createCaptioningManagerServiceByUserContext(@onNull Context userContext)342     private void createCaptioningManagerServiceByUserContext(@NonNull Context userContext) {
343         mCaptioningManager.set(userContext.getSystemService(CaptioningManager.class));
344     }
345 
getCaptionsEnabledState(boolean checkForSwitchState)346     public void getCaptionsEnabledState(boolean checkForSwitchState) {
347         mWorker.obtainMessage(W.GET_CAPTIONS_ENABLED_STATE, checkForSwitchState).sendToTarget();
348     }
349 
setCaptionsEnabledState(boolean enabled)350     public void setCaptionsEnabledState(boolean enabled) {
351         mWorker.obtainMessage(W.SET_CAPTIONS_ENABLED_STATE, enabled).sendToTarget();
352     }
353 
getCaptionsComponentState(boolean fromTooltip)354     public void getCaptionsComponentState(boolean fromTooltip) {
355         mWorker.obtainMessage(W.GET_CAPTIONS_COMPONENT_STATE, fromTooltip).sendToTarget();
356     }
357 
notifyVisible(boolean visible)358     public void notifyVisible(boolean visible) {
359         mWorker.obtainMessage(W.NOTIFY_VISIBLE, visible ? 1 : 0, 0).sendToTarget();
360     }
361 
userActivity()362     public void userActivity() {
363         mWorker.removeMessages(W.USER_ACTIVITY);
364         mWorker.sendEmptyMessage(W.USER_ACTIVITY);
365     }
366 
setRingerMode(int value, boolean external)367     public void setRingerMode(int value, boolean external) {
368         mWorker.obtainMessage(W.SET_RINGER_MODE, value, external ? 1 : 0).sendToTarget();
369     }
370 
setZenMode(int value)371     public void setZenMode(int value) {
372         mWorker.obtainMessage(W.SET_ZEN_MODE, value, 0).sendToTarget();
373     }
374 
setExitCondition(Condition condition)375     public void setExitCondition(Condition condition) {
376         mWorker.obtainMessage(W.SET_EXIT_CONDITION, condition).sendToTarget();
377     }
378 
setStreamMute(int stream, boolean mute)379     public void setStreamMute(int stream, boolean mute) {
380         mWorker.obtainMessage(W.SET_STREAM_MUTE, stream, mute ? 1 : 0).sendToTarget();
381     }
382 
setStreamVolume(int stream, int level)383     public void setStreamVolume(int stream, int level) {
384         mWorker.obtainMessage(W.SET_STREAM_VOLUME, stream, level).sendToTarget();
385     }
386 
setActiveStream(int stream)387     public void setActiveStream(int stream) {
388         mWorker.obtainMessage(W.SET_ACTIVE_STREAM, stream, 0).sendToTarget();
389     }
390 
setEnableDialogs(boolean volumeUi, boolean safetyWarning)391     public void setEnableDialogs(boolean volumeUi, boolean safetyWarning) {
392         mShowVolumeDialog = volumeUi;
393         mShowSafetyWarning = safetyWarning;
394     }
395 
396     @Override
scheduleTouchFeedback()397     public void scheduleTouchFeedback() {
398         mLastToggledRingerOn = System.currentTimeMillis();
399     }
400 
playTouchFeedback()401     private void playTouchFeedback() {
402         if (System.currentTimeMillis() - mLastToggledRingerOn < TOUCH_FEEDBACK_TIMEOUT_MS) {
403             try {
404                 mAudioService.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD,
405                         mUserTracker.getUserId());
406             } catch (RemoteException e) {
407                 // ignore
408             }
409         }
410     }
411 
vibrate(VibrationEffect effect)412     public void vibrate(VibrationEffect effect) {
413         mVibrator.vibrate(effect, SONIFICIATION_VIBRATION_ATTRIBUTES);
414     }
415 
hasVibrator()416     public boolean hasVibrator() {
417         return mHasVibrator;
418     }
419 
onNotifyVisibleW(boolean visible)420     private void onNotifyVisibleW(boolean visible) {
421         mAudio.notifyVolumeControllerVisible(mVolumeController, visible);
422         if (!visible) {
423             if (updateActiveStreamW(-1)) {
424                 mCallbacks.onStateChanged(mState);
425             }
426         }
427     }
428 
onUserActivityW()429     private void onUserActivityW() {
430         synchronized (this) {
431             if (mUserActivityListener != null) {
432                 mUserActivityListener.onUserActivity();
433             }
434         }
435     }
436 
onShowSafetyWarningW(int flags)437     private void onShowSafetyWarningW(int flags) {
438         if (mShowSafetyWarning) {
439             mCallbacks.onShowSafetyWarning(flags);
440         }
441     }
442 
onShowCsdWarningW(@udioManager.CsdWarning int csdWarning, int durationMs)443     private void onShowCsdWarningW(@AudioManager.CsdWarning int csdWarning, int durationMs) {
444         mCallbacks.onShowCsdWarning(csdWarning, durationMs);
445     }
446 
onGetCaptionsComponentStateW(boolean fromTooltip)447     private void onGetCaptionsComponentStateW(boolean fromTooltip) {
448         CaptioningManager captioningManager = mCaptioningManager.get();
449         if (null != captioningManager) {
450             mCallbacks.onCaptionComponentStateChanged(
451                     captioningManager.isSystemAudioCaptioningUiEnabled(), fromTooltip);
452         } else {
453             Log.e(TAG, "onGetCaptionsComponentStateW(), null captioningManager");
454         }
455     }
456 
onGetCaptionsEnabledStateW(boolean checkForSwitchState)457     private void onGetCaptionsEnabledStateW(boolean checkForSwitchState) {
458         CaptioningManager captioningManager = mCaptioningManager.get();
459         if (null != captioningManager) {
460             mCallbacks.onCaptionEnabledStateChanged(
461                     captioningManager.isSystemAudioCaptioningEnabled(), checkForSwitchState);
462         } else {
463             Log.e(TAG, "onGetCaptionsEnabledStateW(), null captioningManager");
464         }
465     }
466 
onSetCaptionsEnabledStateW(boolean enabled)467     private void onSetCaptionsEnabledStateW(boolean enabled) {
468         CaptioningManager captioningManager = mCaptioningManager.get();
469         if (null != captioningManager) {
470             captioningManager.setSystemAudioCaptioningEnabled(enabled);
471             mCallbacks.onCaptionEnabledStateChanged(
472                     captioningManager.isSystemAudioCaptioningEnabled(), false);
473         } else {
474             Log.e(TAG, "onGetCaptionsEnabledStateW(), null captioningManager");
475         }
476     }
477 
onAccessibilityModeChanged(Boolean showA11yStream)478     private void onAccessibilityModeChanged(Boolean showA11yStream) {
479         mCallbacks.onAccessibilityModeChanged(showA11yStream);
480     }
481 
checkRoutedToBluetoothW(int stream)482     private boolean checkRoutedToBluetoothW(int stream) {
483         boolean changed = false;
484         if (stream == AudioManager.STREAM_MUSIC) {
485             // Note: Here we didn't use DEVICE_OUT_BLE_SPEAKER and DEVICE_OUT_BLE_BROADCAST
486             //       Since their values overlap with DEVICE_OUT_EARPIECE and DEVICE_OUT_SPEAKER.
487             //       Anyway, we can check BLE devices by using just DEVICE_OUT_BLE_HEADSET.
488             final boolean routedToBluetooth =
489                     (mAudio.getDevicesForStream(AudioManager.STREAM_MUSIC) &
490                             (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
491                             AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
492                             AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER |
493                             AudioManager.DEVICE_OUT_BLE_HEADSET)) != 0;
494             changed |= updateStreamRoutedToBluetoothW(stream, routedToBluetooth);
495         } else if (stream == AudioManager.STREAM_VOICE_CALL) {
496             final boolean routedToBluetooth =
497                     (mAudio.getDevicesForStream(AudioManager.STREAM_VOICE_CALL)
498                             & AudioManager.DEVICE_OUT_BLE_HEADSET) != 0;
499             changed |= updateStreamRoutedToBluetoothW(stream, routedToBluetooth);
500         }
501         return changed;
502     }
503 
shouldShowUI(int flags)504     private boolean shouldShowUI(int flags) {
505         int wakefulness = mWakefulnessLifecycle.getWakefulness();
506         return wakefulness != WakefulnessLifecycle.WAKEFULNESS_ASLEEP
507                 && wakefulness != WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP
508                 && mDeviceInteractive && (flags & AudioManager.FLAG_SHOW_UI) != 0
509                 && mShowVolumeDialog;
510     }
511 
onVolumeChangedW(int stream, int flags)512     boolean onVolumeChangedW(int stream, int flags) {
513         final boolean showUI = shouldShowUI(flags);
514         final boolean fromKey = (flags & AudioManager.FLAG_FROM_KEY) != 0;
515         final boolean showVibrateHint = (flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0;
516         final boolean showSilentHint = (flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0;
517         boolean changed = false;
518         if (showUI) {
519             changed |= updateActiveStreamW(stream);
520         }
521         int lastAudibleStreamVolume = getAudioManagerStreamVolume(stream);
522         changed |= updateStreamLevelW(stream, lastAudibleStreamVolume);
523         changed |= checkRoutedToBluetoothW(showUI ? AudioManager.STREAM_MUSIC : stream);
524         if (changed) {
525             mCallbacks.onStateChanged(mState);
526         }
527         if (showUI) {
528             onShowRequestedW(Events.SHOW_REASON_VOLUME_CHANGED);
529         }
530         if (showVibrateHint) {
531             mCallbacks.onShowVibrateHint();
532         }
533         if (showSilentHint) {
534             mCallbacks.onShowSilentHint();
535         }
536         if (changed && fromKey) {
537             Events.writeEvent(Events.EVENT_KEY, stream, lastAudibleStreamVolume);
538             mCallbacks.onVolumeChangedFromKey();
539         }
540         return changed;
541     }
542 
updateActiveStreamW(int activeStream)543     private boolean updateActiveStreamW(int activeStream) {
544         if (activeStream == mState.activeStream) return false;
545         mState.activeStream = activeStream;
546         Events.writeEvent(Events.EVENT_ACTIVE_STREAM_CHANGED, activeStream);
547         if (D.BUG) Log.d(TAG, "updateActiveStreamW " + activeStream);
548         final int s = activeStream < DYNAMIC_STREAM_START_INDEX ? activeStream : -1;
549         if (D.BUG) Log.d(TAG, "forceVolumeControlStream " + s);
550         mAudio.forceVolumeControlStream(s);
551         return true;
552     }
553 
554     private StreamState streamStateW(int stream) {
555         StreamState ss = mState.states.get(stream);
556         if (ss == null) {
557             ss = new StreamState();
558             mState.states.put(stream, ss);
559         }
560         return ss;
561     }
562 
563     private void onGetStateW() {
564         for (int stream : STREAMS.keySet()) {
565             updateStreamLevelW(stream, getAudioManagerStreamVolume(stream));
566             streamStateW(stream).levelMin = getAudioManagerStreamMinVolume(stream);
567             streamStateW(stream).levelMax = Math.max(1, getAudioManagerStreamMaxVolume(stream));
568             updateStreamMuteW(stream, mAudio.isStreamMute(stream));
569             final StreamState ss = streamStateW(stream);
570             ss.muteSupported = mAudio.isStreamMutableByUi(stream);
571             ss.name = STREAMS.get(stream);
572             checkRoutedToBluetoothW(stream);
573         }
574         // We are not destroyed so this is listening and has updated information
575         updateRingerModeExternalW(mRingerModeObservers.mRingerMode.getValue());
576         updateZenModeW();
577         updateZenConfig();
578         updateEffectsSuppressorW(mNoMan.getEffectsSuppressor());
579         mCallbacks.onStateChanged(mState);
580     }
581 
582     private boolean updateStreamRoutedToBluetoothW(int stream, boolean routedToBluetooth) {
583         final StreamState ss = streamStateW(stream);
584         if (ss.routedToBluetooth == routedToBluetooth) return false;
585         ss.routedToBluetooth = routedToBluetooth;
586         if (D.BUG) Log.d(TAG, "updateStreamRoutedToBluetoothW stream=" + stream
587                 + " routedToBluetooth=" + routedToBluetooth);
588         return true;
589     }
590 
591     private boolean updateStreamLevelW(int stream, int level) {
592         final StreamState ss = streamStateW(stream);
593         if (ss.level == level) return false;
594         ss.level = level;
595         if (isLogWorthy(stream)) {
596             Events.writeEvent(Events.EVENT_LEVEL_CHANGED, stream, level);
597         }
598         return true;
599     }
600 
601     private static boolean isLogWorthy(int stream) {
602         switch (stream) {
603             case AudioSystem.STREAM_ALARM:
604             case AudioSystem.STREAM_BLUETOOTH_SCO:
605             case AudioSystem.STREAM_MUSIC:
606             case AudioSystem.STREAM_RING:
607             case AudioSystem.STREAM_SYSTEM:
608             case AudioSystem.STREAM_VOICE_CALL:
609                 return true;
610         }
611         return false;
612     }
613 
614     private boolean updateStreamMuteW(int stream, boolean muted) {
615         final StreamState ss = streamStateW(stream);
616         if (ss.muted == muted) return false;
617         ss.muted = muted;
618         if (isLogWorthy(stream)) {
619             Events.writeEvent(Events.EVENT_MUTE_CHANGED, stream, muted);
620         }
621         if (muted && isRinger(stream)) {
622             updateRingerModeInternalW(mRingerModeObservers.mRingerModeInternal.getValue());
623         }
624         return true;
625     }
626 
627     private static boolean isRinger(int stream) {
628         return stream == AudioManager.STREAM_RING || stream == AudioManager.STREAM_NOTIFICATION;
629     }
630 
631     private boolean updateEffectsSuppressorW(ComponentName effectsSuppressor) {
632         if (Objects.equals(mState.effectsSuppressor, effectsSuppressor)) return false;
633         mState.effectsSuppressor = effectsSuppressor;
634         mState.effectsSuppressorName =
635                 getApplicationName(mPackageManager, mState.effectsSuppressor);
636         Events.writeEvent(Events.EVENT_SUPPRESSOR_CHANGED, mState.effectsSuppressor,
637                 mState.effectsSuppressorName);
638         return true;
639     }
640 
641     private static String getApplicationName(PackageManager pm, ComponentName component) {
642         if (component == null) return null;
643         final String pkg = component.getPackageName();
644         try {
645             final ApplicationInfo ai = pm.getApplicationInfo(pkg, 0);
646             final String rt = Objects.toString(ai.loadLabel(pm), "").trim();
647             if (rt.length() > 0) {
648                 return rt;
649             }
650         } catch (NameNotFoundException e) {}
651         return pkg;
652     }
653 
updateZenModeW()654     private boolean updateZenModeW() {
655         final int zen = Settings.Global.getInt(mContext.getContentResolver(),
656                 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
657         if (mState.zenMode == zen) return false;
658         mState.zenMode = zen;
659         Events.writeEvent(Events.EVENT_ZEN_MODE_CHANGED, zen);
660         return true;
661     }
662 
updateZenConfig()663     private boolean updateZenConfig() {
664         final NotificationManager.Policy policy = mNoMan.getConsolidatedNotificationPolicy();
665         boolean disallowAlarms = (policy.priorityCategories & NotificationManager.Policy
666                 .PRIORITY_CATEGORY_ALARMS) == 0;
667         boolean disallowMedia = (policy.priorityCategories & NotificationManager.Policy
668                 .PRIORITY_CATEGORY_MEDIA) == 0;
669         boolean disallowSystem = (policy.priorityCategories & NotificationManager.Policy
670                 .PRIORITY_CATEGORY_SYSTEM) == 0;
671         // ringer controls notifications, ringer and system sounds, so only disallow ringer changes
672         // if all relevant (notifications + ringer + system) sounds are not allowed to bypass DND
673         boolean disallowRinger = ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(policy);
674         if (mState.disallowAlarms == disallowAlarms
675                 && mState.disallowMedia == disallowMedia
676                 && mState.disallowRinger == disallowRinger
677                 && mState.disallowSystem == disallowSystem) {
678             return false;
679         }
680         mState.disallowAlarms = disallowAlarms;
681         mState.disallowMedia = disallowMedia;
682         mState.disallowSystem = disallowSystem;
683         mState.disallowRinger = disallowRinger;
684         Events.writeEvent(Events.EVENT_ZEN_CONFIG_CHANGED, "disallowAlarms="
685                 + disallowAlarms + " disallowMedia=" + disallowMedia + " disallowSystem="
686                 + disallowSystem + " disallowRinger=" + disallowRinger);
687         return true;
688     }
689 
updateRingerModeExternalW(int rm)690     private boolean updateRingerModeExternalW(int rm) {
691         if (rm == mState.ringerModeExternal) return false;
692         mState.ringerModeExternal = rm;
693         Events.writeEvent(Events.EVENT_EXTERNAL_RINGER_MODE_CHANGED, rm);
694         return true;
695     }
696 
updateRingerModeInternalW(int rm)697     private boolean updateRingerModeInternalW(int rm) {
698         if (rm == mState.ringerModeInternal) return false;
699         mState.ringerModeInternal = rm;
700         Events.writeEvent(Events.EVENT_INTERNAL_RINGER_MODE_CHANGED, rm);
701 
702         if (mState.ringerModeInternal == RINGER_MODE_NORMAL) {
703             playTouchFeedback();
704         }
705 
706         return true;
707     }
708 
onShowRequestedW(int reason)709     private void onShowRequestedW(int reason) {
710         mCallbacks.onShowRequested(reason, mKeyguardManager.isKeyguardLocked(),
711                 mActivityManager.getLockTaskModeState());
712     }
713 
onSetRingerModeW(int mode, boolean external)714     private void onSetRingerModeW(int mode, boolean external) {
715         if (external) {
716             mAudio.setRingerMode(mode);
717         } else {
718             mAudio.setRingerModeInternal(mode);
719         }
720     }
721 
onSetStreamMuteW(int stream, boolean mute)722     private void onSetStreamMuteW(int stream, boolean mute) {
723         mAudio.adjustStreamVolume(stream, mute ? AudioManager.ADJUST_MUTE
724                 : AudioManager.ADJUST_UNMUTE, 0);
725     }
726 
onSetStreamVolumeW(int stream, int level)727     private void onSetStreamVolumeW(int stream, int level) {
728         if (D.BUG) Log.d(TAG, "onSetStreamVolume " + stream + " level=" + level);
729         if (stream >= DYNAMIC_STREAM_START_INDEX) {
730             mMediaSessionsCallbacksW.setStreamVolume(stream, level);
731             return;
732         }
733         setAudioManagerStreamVolume(stream, level, 0);
734     }
735 
onSetActiveStreamW(int stream)736     private void onSetActiveStreamW(int stream) {
737         boolean changed = updateActiveStreamW(stream);
738         if (changed) {
739             mCallbacks.onStateChanged(mState);
740         }
741     }
742 
onSetExitConditionW(Condition condition)743     private void onSetExitConditionW(Condition condition) {
744         mNoMan.setZenMode(mState.zenMode, condition != null ? condition.id : null, TAG);
745     }
746 
onSetZenModeW(int mode)747     private void onSetZenModeW(int mode) {
748         if (D.BUG) Log.d(TAG, "onSetZenModeW " + mode);
749         mNoMan.setZenMode(mode, null, TAG);
750     }
751 
onDismissRequestedW(int reason)752     private void onDismissRequestedW(int reason) {
753         mCallbacks.onDismissRequested(reason);
754     }
755 
showDndTile()756     public void showDndTile() {
757         if (D.BUG) Log.d(TAG, "showDndTile");
758         DndTile.setVisible(mContext, true);
759     }
760 
761     private final class VC extends IVolumeController.Stub {
762         private final String TAG = VolumeDialogControllerImpl.TAG + ".VC";
763 
764         @Override
displaySafeVolumeWarning(int flags)765         public void displaySafeVolumeWarning(int flags) throws RemoteException {
766             if (D.BUG) Log.d(TAG, "displaySafeVolumeWarning "
767                     + Util.audioManagerFlagsToString(flags));
768             mWorker.obtainMessage(W.SHOW_SAFETY_WARNING, flags, 0).sendToTarget();
769         }
770 
771         /**
772          * Display a sound-dose related warning.
773          * This method will never be called if the CSD (Computed Sound Dose) feature is
774          * not enabled. See com.android.android.server.audio.SoundDoseHelper for the state of
775          * the feature.
776          * @param csdWarning the type of warning to display, values are one of
777          *        {@link android.media.AudioManager#CSD_WARNING_DOSE_REACHED_1X},
778          *        {@link android.media.AudioManager#CSD_WARNING_DOSE_REPEATED_5X},
779          *        {@link android.media.AudioManager#CSD_WARNING_MOMENTARY_EXPOSURE},
780          *        {@link android.media.AudioManager#CSD_WARNING_ACCUMULATION_START}.
781          * @param displayDurationMs the time expressed in milliseconds after which the dialog will be
782          *        automatically dismissed, or -1 if there is no automatic timeout.
783          */
784         @Override
displayCsdWarning(int csdWarning, int displayDurationMs)785         public void displayCsdWarning(int csdWarning, int displayDurationMs) throws RemoteException
786         {
787             if (D.BUG) Log.d(TAG, "displayCsdWarning durMs=" + displayDurationMs);
788             mWorker.obtainMessage(W.SHOW_CSD_WARNING, csdWarning, displayDurationMs)
789                     .sendToTarget();
790         }
791 
792         @Override
volumeChanged(int streamType, int flags)793         public void volumeChanged(int streamType, int flags) throws RemoteException {
794             if (D.BUG) Log.d(TAG, "volumeChanged " + AudioSystem.streamToString(streamType)
795                     + " " + Util.audioManagerFlagsToString(flags));
796             mWorker.obtainMessage(W.VOLUME_CHANGED, streamType, flags).sendToTarget();
797         }
798 
799         @Override
masterMuteChanged(int flags)800         public void masterMuteChanged(int flags) throws RemoteException {
801             if (D.BUG) Log.d(TAG, "masterMuteChanged");
802         }
803 
804         @Override
setLayoutDirection(int layoutDirection)805         public void setLayoutDirection(int layoutDirection) throws RemoteException {
806             if (D.BUG) Log.d(TAG, "setLayoutDirection");
807             mWorker.obtainMessage(W.LAYOUT_DIRECTION_CHANGED, layoutDirection, 0).sendToTarget();
808         }
809 
810         @Override
dismiss()811         public void dismiss() throws RemoteException {
812             if (D.BUG) Log.d(TAG, "dismiss requested");
813             mWorker.obtainMessage(W.DISMISS_REQUESTED, Events.DISMISS_REASON_VOLUME_CONTROLLER, 0)
814                     .sendToTarget();
815             mWorker.sendEmptyMessage(W.DISMISS_REQUESTED);
816         }
817 
818         @Override
setA11yMode(int mode)819         public void setA11yMode(int mode) {
820             if (D.BUG) Log.d(TAG, "setA11yMode to " + mode);
821             switch (mode) {
822                 case VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME:
823                     // "legacy" mode
824                     mShowA11yStream = false;
825                     break;
826                 case VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME:
827                     mShowA11yStream = true;
828                     break;
829                 default:
830                     Log.e(TAG, "Invalid accessibility mode " + mode);
831                     break;
832             }
833             mWorker.obtainMessage(W.ACCESSIBILITY_MODE_CHANGED, mShowA11yStream).sendToTarget();
834         }
835     }
836 
837     private final class W extends Handler {
838         private static final int VOLUME_CHANGED = 1;
839         private static final int DISMISS_REQUESTED = 2;
840         private static final int GET_STATE = 3;
841         private static final int SET_RINGER_MODE = 4;
842         private static final int SET_ZEN_MODE = 5;
843         private static final int SET_EXIT_CONDITION = 6;
844         private static final int SET_STREAM_MUTE = 7;
845         private static final int LAYOUT_DIRECTION_CHANGED = 8;
846         private static final int CONFIGURATION_CHANGED = 9;
847         private static final int SET_STREAM_VOLUME = 10;
848         private static final int SET_ACTIVE_STREAM = 11;
849         private static final int NOTIFY_VISIBLE = 12;
850         private static final int USER_ACTIVITY = 13;
851         private static final int SHOW_SAFETY_WARNING = 14;
852         private static final int ACCESSIBILITY_MODE_CHANGED = 15;
853         private static final int GET_CAPTIONS_COMPONENT_STATE = 16;
854         private static final int SHOW_CSD_WARNING = 17;
855         private static final int GET_CAPTIONS_ENABLED_STATE = 18;
856         private static final int SET_CAPTIONS_ENABLED_STATE = 19;
857 
W(Looper looper)858         W(Looper looper) {
859             super(looper);
860         }
861 
862         @Override
handleMessage(Message msg)863         public void handleMessage(Message msg) {
864             switch (msg.what) {
865                 case VOLUME_CHANGED: onVolumeChangedW(msg.arg1, msg.arg2); break;
866                 case DISMISS_REQUESTED: onDismissRequestedW(msg.arg1); break;
867                 case GET_STATE: onGetStateW(); break;
868                 case SET_RINGER_MODE: onSetRingerModeW(msg.arg1, msg.arg2 != 0); break;
869                 case SET_ZEN_MODE: onSetZenModeW(msg.arg1); break;
870                 case SET_EXIT_CONDITION: onSetExitConditionW((Condition) msg.obj); break;
871                 case SET_STREAM_MUTE: onSetStreamMuteW(msg.arg1, msg.arg2 != 0); break;
872                 case LAYOUT_DIRECTION_CHANGED: mCallbacks.onLayoutDirectionChanged(msg.arg1); break;
873                 case CONFIGURATION_CHANGED: mCallbacks.onConfigurationChanged(); break;
874                 case SET_STREAM_VOLUME: onSetStreamVolumeW(msg.arg1, msg.arg2); break;
875                 case SET_ACTIVE_STREAM: onSetActiveStreamW(msg.arg1); break;
876                 case NOTIFY_VISIBLE: onNotifyVisibleW(msg.arg1 != 0); break;
877                 case USER_ACTIVITY: onUserActivityW(); break;
878                 case SHOW_SAFETY_WARNING: onShowSafetyWarningW(msg.arg1); break;
879                 case GET_CAPTIONS_COMPONENT_STATE:
880                     onGetCaptionsComponentStateW((Boolean) msg.obj); break;
881                 case ACCESSIBILITY_MODE_CHANGED: onAccessibilityModeChanged((Boolean) msg.obj);
882                     break;
883                 case SHOW_CSD_WARNING: onShowCsdWarningW(msg.arg1, msg.arg2); break;
884                 case GET_CAPTIONS_ENABLED_STATE:
885                     onGetCaptionsEnabledStateW((Boolean) msg.obj); break;
886                 case SET_CAPTIONS_ENABLED_STATE:
887                     onSetCaptionsEnabledStateW((Boolean) msg.obj); break;
888             }
889         }
890     }
891 
892     static class C implements Callbacks {
893         private final Map<Callbacks, Handler> mCallbackMap = new ConcurrentHashMap<>();
894 
add(Callbacks callback, Handler handler)895         public void add(Callbacks callback, Handler handler) {
896             if (callback == null || handler == null) throw new IllegalArgumentException();
897             mCallbackMap.put(callback, handler);
898         }
899 
remove(Callbacks callback)900         public void remove(Callbacks callback) {
901             mCallbackMap.remove(callback);
902         }
903 
904         @Override
onShowRequested( final int reason, final boolean keyguardLocked, final int lockTaskModeState)905         public void onShowRequested(
906                 final int reason,
907                 final boolean keyguardLocked,
908                 final int lockTaskModeState) {
909             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
910                 entry.getValue().post(new Runnable() {
911                     @Override
912                     public void run() {
913                         entry.getKey().onShowRequested(reason, keyguardLocked, lockTaskModeState);
914                     }
915                 });
916             }
917         }
918 
919         @Override
onDismissRequested(final int reason)920         public void onDismissRequested(final int reason) {
921             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
922                 entry.getValue().post(new Runnable() {
923                     @Override
924                     public void run() {
925                         entry.getKey().onDismissRequested(reason);
926                     }
927                 });
928             }
929         }
930 
931         @Override
onStateChanged(final State state)932         public void onStateChanged(final State state) {
933             final long time = System.currentTimeMillis();
934             final State copy = state.copy();
935             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
936                 entry.getValue().post(new Runnable() {
937                     @Override
938                     public void run() {
939                         entry.getKey().onStateChanged(copy);
940                     }
941                 });
942             }
943             Events.writeState(time, copy);
944         }
945 
946         @Override
onLayoutDirectionChanged(final int layoutDirection)947         public void onLayoutDirectionChanged(final int layoutDirection) {
948             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
949                 entry.getValue().post(new Runnable() {
950                     @Override
951                     public void run() {
952                         entry.getKey().onLayoutDirectionChanged(layoutDirection);
953                     }
954                 });
955             }
956         }
957 
958         @Override
onConfigurationChanged()959         public void onConfigurationChanged() {
960             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
961                 entry.getValue().post(new Runnable() {
962                     @Override
963                     public void run() {
964                         entry.getKey().onConfigurationChanged();
965                     }
966                 });
967             }
968         }
969 
970         @Override
onShowVibrateHint()971         public void onShowVibrateHint() {
972             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
973                 entry.getValue().post(new Runnable() {
974                     @Override
975                     public void run() {
976                         entry.getKey().onShowVibrateHint();
977                     }
978                 });
979             }
980         }
981 
982         @Override
onShowSilentHint()983         public void onShowSilentHint() {
984             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
985                 entry.getValue().post(new Runnable() {
986                     @Override
987                     public void run() {
988                         entry.getKey().onShowSilentHint();
989                     }
990                 });
991             }
992         }
993 
994         @Override
onScreenOff()995         public void onScreenOff() {
996             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
997                 entry.getValue().post(new Runnable() {
998                     @Override
999                     public void run() {
1000                         entry.getKey().onScreenOff();
1001                     }
1002                 });
1003             }
1004         }
1005 
1006         @Override
onShowSafetyWarning(final int flags)1007         public void onShowSafetyWarning(final int flags) {
1008             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
1009                 entry.getValue().post(new Runnable() {
1010                     @Override
1011                     public void run() {
1012                         entry.getKey().onShowSafetyWarning(flags);
1013                     }
1014                 });
1015             }
1016         }
1017 
1018         @Override
onShowCsdWarning(int csdWarning, int durationMs)1019         public void onShowCsdWarning(int csdWarning, int durationMs) {
1020             if (Callbacks.VERSION < 2) {
1021                 return;
1022             }
1023             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
1024                 entry.getValue().post(new Runnable() {
1025                     @Override
1026                     public void run() {
1027                         entry.getKey().onShowCsdWarning(csdWarning, durationMs);
1028                     }
1029                 });
1030             }
1031         }
1032 
1033         @Override
onVolumeChangedFromKey()1034         public void onVolumeChangedFromKey() {
1035             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
1036                 entry.getValue().post(new Runnable() {
1037                     @Override
1038                     public void run() {
1039                         entry.getKey().onVolumeChangedFromKey();
1040                     }
1041                 });
1042             }
1043         }
1044 
1045         @Override
onAccessibilityModeChanged(Boolean showA11yStream)1046         public void onAccessibilityModeChanged(Boolean showA11yStream) {
1047             boolean show = showA11yStream != null && showA11yStream;
1048             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
1049                 entry.getValue().post(new Runnable() {
1050                     @Override
1051                     public void run() {
1052                         entry.getKey().onAccessibilityModeChanged(show);
1053                     }
1054                 });
1055             }
1056         }
1057 
1058         @Override
onCaptionComponentStateChanged( Boolean isComponentEnabled, Boolean fromTooltip)1059         public void onCaptionComponentStateChanged(
1060                 Boolean isComponentEnabled, Boolean fromTooltip) {
1061             boolean componentEnabled = isComponentEnabled != null && isComponentEnabled;
1062             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
1063                 entry.getValue().post(
1064                         () -> entry.getKey().onCaptionComponentStateChanged(
1065                                 componentEnabled, fromTooltip));
1066             }
1067         }
1068 
1069         @Override
onCaptionEnabledStateChanged(Boolean isEnabled, Boolean checkBeforeSwitch)1070         public void onCaptionEnabledStateChanged(Boolean isEnabled, Boolean checkBeforeSwitch) {
1071             boolean captionsEnabled = isEnabled != null && isEnabled;
1072             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
1073                 entry.getValue().post(
1074                         () -> entry.getKey().onCaptionEnabledStateChanged(
1075                                 captionsEnabled, checkBeforeSwitch));
1076             }
1077         }
1078 
1079     }
1080 
1081     private final class RingerModeObservers {
1082 
1083         private final RingerModeLiveData mRingerMode;
1084         private final RingerModeLiveData mRingerModeInternal;
1085 
1086         private final Observer<Integer> mRingerModeObserver = new Observer<Integer>() {
1087             @Override
1088             public void onChanged(Integer value) {
1089                 mWorker.post(() -> {
1090                             final int rm = value;
1091                             if (mRingerMode.getInitialSticky()) {
1092                                 mState.ringerModeExternal = rm;
1093                             }
1094                             if (D.BUG) {
1095                                 Log.d(TAG, "onChange ringer_mode rm="
1096                                         + Util.ringerModeToString(rm));
1097                             }
1098                             if (updateRingerModeExternalW(rm)) {
1099                                 mCallbacks.onStateChanged(mState);
1100                             }
1101                         }
1102                 );
1103             }
1104         };
1105 
1106         private final Observer<Integer> mRingerModeInternalObserver = new Observer<Integer>() {
1107             @Override
1108             public void onChanged(Integer value) {
1109                 mWorker.post(() -> {
1110                             final int rm = value;
1111                             if (mRingerModeInternal.getInitialSticky()) {
1112                                 mState.ringerModeInternal = rm;
1113                             }
1114                             if (D.BUG) {
1115                                 Log.d(TAG, "onChange internal_ringer_mode rm="
1116                                         + Util.ringerModeToString(rm));
1117                             }
1118                             if (updateRingerModeInternalW(rm)) {
1119                                 mCallbacks.onStateChanged(mState);
1120                             }
1121                         }
1122                 );
1123             }
1124         };
1125 
RingerModeObservers(RingerModeLiveData ringerMode, RingerModeLiveData ringerModeInternal)1126         RingerModeObservers(RingerModeLiveData ringerMode,
1127                 RingerModeLiveData ringerModeInternal) {
1128             mRingerMode = ringerMode;
1129             mRingerModeInternal = ringerModeInternal;
1130         }
1131 
init()1132         public void init() {
1133             int initialValue = mRingerMode.getValue();
1134             if (initialValue != -1) {
1135                 // If it's not -1, set it to the initial value, if it's -1, it means that the
1136                 // tracker is not listening already and will obtain the sticky value.
1137                 mState.ringerModeExternal = initialValue;
1138             }
1139             mRingerMode.observeForever(mRingerModeObserver);
1140             initialValue = mRingerModeInternal.getValue();
1141             if (initialValue != -1) {
1142                 // If it's not -1, set it to the initial value, if it's -1, it means that the
1143                 // tracker is not listening already and will obtain the sticky value.
1144                 mState.ringerModeInternal = initialValue;
1145             }
1146             mRingerModeInternal.observeForever(mRingerModeInternalObserver);
1147         }
1148 
destroy()1149         public void destroy() {
1150             mRingerMode.removeObserver(mRingerModeObserver);
1151             mRingerModeInternal.removeObserver(mRingerModeInternalObserver);
1152         }
1153     }
1154 
1155     private final class SettingObserver extends ContentObserver {
1156         private final Uri ZEN_MODE_URI =
1157                 Settings.Global.getUriFor(Settings.Global.ZEN_MODE);
1158         private final Uri ZEN_MODE_CONFIG_URI =
1159                 Settings.Global.getUriFor(Settings.Global.ZEN_MODE_CONFIG_ETAG);
1160 
SettingObserver(Handler handler)1161         public SettingObserver(Handler handler) {
1162             super(handler);
1163         }
1164 
init()1165         public void init() {
1166             mContext.getContentResolver().registerContentObserver(ZEN_MODE_URI, false, this);
1167             mContext.getContentResolver().registerContentObserver(ZEN_MODE_CONFIG_URI, false, this);
1168         }
1169 
destroy()1170         public void destroy() {
1171             mContext.getContentResolver().unregisterContentObserver(this);
1172         }
1173 
1174         @Override
onChange(boolean selfChange, Uri uri)1175         public void onChange(boolean selfChange, Uri uri) {
1176             boolean changed = false;
1177             if (ZEN_MODE_URI.equals(uri)) {
1178                 changed = updateZenModeW();
1179             }
1180             if (ZEN_MODE_CONFIG_URI.equals(uri)) {
1181                 changed |= updateZenConfig();
1182             }
1183 
1184             if (changed) {
1185                 mCallbacks.onStateChanged(mState);
1186             }
1187         }
1188     }
1189 
1190     private final class Receiver extends BroadcastReceiver {
1191 
init()1192         public void init() {
1193             final IntentFilter filter = new IntentFilter();
1194             filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
1195             filter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
1196             filter.addAction(AudioManager.STREAM_MUTE_CHANGED_ACTION);
1197             filter.addAction(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
1198             filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
1199             filter.addAction(Intent.ACTION_SCREEN_OFF);
1200             filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
1201             mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mWorker);
1202         }
1203 
destroy()1204         public void destroy() {
1205             mBroadcastDispatcher.unregisterReceiver(this);
1206         }
1207 
1208         @Override
onReceive(Context context, Intent intent)1209         public void onReceive(Context context, Intent intent) {
1210             final String action = intent.getAction();
1211             boolean changed = false;
1212             if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
1213                 final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
1214                 final int level = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
1215                 final int oldLevel = intent
1216                         .getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, -1);
1217                 if (D.BUG) Log.d(TAG, "onReceive VOLUME_CHANGED_ACTION stream=" + stream
1218                         + " level=" + level + " oldLevel=" + oldLevel);
1219                 changed = updateStreamLevelW(stream, level);
1220             } else if (action.equals(AudioManager.STREAM_DEVICES_CHANGED_ACTION)) {
1221                 final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
1222                 final int devices = intent
1223                         .getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_DEVICES, -1);
1224                 final int oldDevices = intent
1225                         .getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_DEVICES, -1);
1226                 if (D.BUG) Log.d(TAG, "onReceive STREAM_DEVICES_CHANGED_ACTION stream="
1227                         + stream + " devices=" + devices + " oldDevices=" + oldDevices);
1228                 changed = checkRoutedToBluetoothW(stream);
1229                 changed |= onVolumeChangedW(stream, 0);
1230             } else if (action.equals(AudioManager.STREAM_MUTE_CHANGED_ACTION)) {
1231                 final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
1232                 final boolean muted = intent
1233                         .getBooleanExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, false);
1234                 if (D.BUG) Log.d(TAG, "onReceive STREAM_MUTE_CHANGED_ACTION stream=" + stream
1235                         + " muted=" + muted);
1236                 changed = updateStreamMuteW(stream, muted);
1237             } else if (action.equals(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED)) {
1238                 if (D.BUG) Log.d(TAG, "onReceive ACTION_EFFECTS_SUPPRESSOR_CHANGED");
1239                 changed = updateEffectsSuppressorW(mNoMan.getEffectsSuppressor());
1240             } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
1241                 if (D.BUG) Log.d(TAG, "onReceive ACTION_CONFIGURATION_CHANGED");
1242                 mCallbacks.onConfigurationChanged();
1243             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1244                 if (D.BUG) Log.d(TAG, "onReceive ACTION_SCREEN_OFF");
1245                 mCallbacks.onScreenOff();
1246             } else if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
1247                 if (D.BUG) Log.d(TAG, "onReceive ACTION_CLOSE_SYSTEM_DIALOGS");
1248                 dismiss();
1249             }
1250             if (changed) {
1251                 mCallbacks.onStateChanged(mState);
1252             }
1253         }
1254     }
1255 
1256     protected final class MediaSessionsCallbacks implements MediaSessions.Callbacks {
1257         private final HashMap<Token, Integer> mRemoteStreams = new HashMap<>();
1258 
1259         private int mNextStream = DYNAMIC_STREAM_START_INDEX;
1260         private final boolean mVolumeAdjustmentForRemoteGroupSessions;
1261 
MediaSessionsCallbacks(Context context)1262         public MediaSessionsCallbacks(Context context) {
1263             mVolumeAdjustmentForRemoteGroupSessions = context.getResources().getBoolean(
1264                     com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions);
1265         }
1266 
1267         @Override
onRemoteUpdate(Token token, String name, PlaybackInfo pi)1268         public void onRemoteUpdate(Token token, String name, PlaybackInfo pi) {
1269             if (showForSession(token)) {
1270                 addStream(token, "onRemoteUpdate");
1271 
1272                 int stream = 0;
1273                 synchronized (mRemoteStreams) {
1274                     stream = mRemoteStreams.get(token);
1275                 }
1276                 Slog.d(TAG,
1277                         "onRemoteUpdate: stream: " + stream + " volume: " + pi.getCurrentVolume());
1278                 boolean changed = mState.states.indexOfKey(stream) < 0;
1279                 final StreamState ss = streamStateW(stream);
1280                 ss.dynamic = true;
1281                 ss.levelMin = 0;
1282                 ss.levelMax = pi.getMaxVolume();
1283                 if (ss.level != pi.getCurrentVolume()) {
1284                     ss.level = pi.getCurrentVolume();
1285                     changed = true;
1286                 }
1287                 if (!Objects.equals(ss.remoteLabel, name)) {
1288                     ss.name = -1;
1289                     ss.remoteLabel = name;
1290                     changed = true;
1291                 }
1292                 if (changed) {
1293                     Log.d(TAG, "onRemoteUpdate: " + name + ": " + ss.level + " of " + ss.levelMax);
1294                     mCallbacks.onStateChanged(mState);
1295                 }
1296             }
1297         }
1298 
1299         @Override
1300         public void onRemoteVolumeChanged(Token token, int flags) {
1301             if (showForSession(token)) {
1302                 addStream(token, "onRemoteVolumeChanged");
1303                 int stream = 0;
1304                 synchronized (mRemoteStreams) {
1305                     stream = mRemoteStreams.get(token);
1306                 }
1307                 final boolean showUI = shouldShowUI(flags);
1308                 Slog.d(TAG, "onRemoteVolumeChanged: stream: " + stream + " showui? " + showUI);
1309                 boolean changed = updateActiveStreamW(stream);
1310                 if (showUI) {
1311                     changed |= checkRoutedToBluetoothW(AudioManager.STREAM_MUSIC);
1312                 }
1313                 if (changed) {
1314                     Slog.d(TAG, "onRemoteChanged: updatingState");
1315                     mCallbacks.onStateChanged(mState);
1316                 }
1317                 if (showUI) {
1318                     onShowRequestedW(Events.SHOW_REASON_REMOTE_VOLUME_CHANGED);
1319                 }
1320             }
1321         }
1322 
1323         @Override
1324         public void onRemoteRemoved(Token token) {
1325             if (showForSession(token)) {
1326                 int stream = 0;
1327                 synchronized (mRemoteStreams) {
1328                     if (!mRemoteStreams.containsKey(token)) {
1329                         Log.d(TAG, "onRemoteRemoved: stream doesn't exist, "
1330                                 + "aborting remote removed for token:" + token.toString());
1331                         return;
1332                     }
1333                     stream = mRemoteStreams.get(token);
1334                 }
1335                 mState.states.remove(stream);
1336                 if (mState.activeStream == stream) {
1337                     updateActiveStreamW(-1);
1338                 }
1339                 mCallbacks.onStateChanged(mState);
1340             }
1341         }
1342 
1343         public void setStreamVolume(int stream, int level) {
1344             final Token token = findToken(stream);
1345             if (token == null) {
1346                 Log.w(TAG, "setStreamVolume: No token found for stream: " + stream);
1347                 return;
1348             }
1349             if (showForSession(token)) {
1350                 mMediaSessions.setVolume(token, level);
1351             }
1352         }
1353 
1354         private boolean showForSession(Token token) {
1355             if (mVolumeAdjustmentForRemoteGroupSessions) {
1356                 if (DEBUG) {
1357                     Log.d(TAG, "Volume adjustment for remote group sessions allowed,"
1358                             + " showForSession: true");
1359                 }
1360                 return true;
1361             }
1362             MediaController ctr = new MediaController(mContext, token);
1363             String packageName = ctr.getPackageName();
1364             List<RoutingSessionInfo> sessions =
1365                     mRouter2Manager.getRoutingSessions(packageName);
1366             if (DEBUG) {
1367                 Log.d(TAG, "Found " + sessions.size() + " routing sessions for package name "
1368                         + packageName);
1369             }
1370             for (RoutingSessionInfo session : sessions) {
1371                 if (DEBUG) {
1372                     Log.d(TAG, "Found routingSessionInfo: " + session);
1373                 }
1374                 if (!session.isSystemSession()
1375                         && session.getVolumeHandling() != MediaRoute2Info.PLAYBACK_VOLUME_FIXED) {
1376                     return true;
1377                 }
1378             }
1379 
1380             Log.d(TAG, "No routing session for " + packageName);
1381             return false;
1382         }
1383 
1384         private Token findToken(int stream) {
1385             synchronized (mRemoteStreams) {
1386                 for (Map.Entry<Token, Integer> entry : mRemoteStreams.entrySet()) {
1387                     if (entry.getValue().equals(stream)) {
1388                         return entry.getKey();
1389                     }
1390                 }
1391             }
1392             return null;
1393         }
1394 
1395         private void addStream(Token token, String triggeringMethod) {
1396             synchronized (mRemoteStreams) {
1397                 if (!mRemoteStreams.containsKey(token)) {
1398                     mRemoteStreams.put(token, mNextStream);
1399                     Log.d(TAG, triggeringMethod + ": added stream " + mNextStream
1400                             + " from token + " + token.toString());
1401                     mNextStream++;
1402                 }
1403             }
1404         }
1405     }
1406 
1407     public interface UserActivityListener {
1408         void onUserActivity();
1409     }
1410 }
1411