• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.NotificationManager;
22 import android.content.BroadcastReceiver;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.content.pm.ApplicationInfo;
28 import android.content.pm.PackageManager;
29 import android.content.pm.PackageManager.NameNotFoundException;
30 import android.database.ContentObserver;
31 import android.media.AudioAttributes;
32 import android.media.AudioManager;
33 import android.media.AudioSystem;
34 import android.media.IAudioService;
35 import android.media.IVolumeController;
36 import android.media.VolumePolicy;
37 import android.media.session.MediaController.PlaybackInfo;
38 import android.media.session.MediaSession.Token;
39 import android.net.Uri;
40 import android.os.Handler;
41 import android.os.HandlerThread;
42 import android.os.Looper;
43 import android.os.Message;
44 import android.os.RemoteException;
45 import android.os.ServiceManager;
46 import android.os.VibrationEffect;
47 import android.os.Vibrator;
48 import android.provider.Settings;
49 import android.service.notification.Condition;
50 import android.service.notification.ZenModeConfig;
51 import android.text.TextUtils;
52 import android.util.ArrayMap;
53 import android.util.Log;
54 import android.view.accessibility.AccessibilityManager;
55 
56 import com.android.internal.annotations.GuardedBy;
57 import com.android.settingslib.volume.MediaSessions;
58 import com.android.systemui.Dumpable;
59 import com.android.systemui.R;
60 import com.android.systemui.SysUiServiceProvider;
61 import com.android.systemui.keyguard.WakefulnessLifecycle;
62 import com.android.systemui.plugins.VolumeDialogController;
63 import com.android.systemui.qs.tiles.DndTile;
64 import com.android.systemui.statusbar.phone.StatusBar;
65 
66 import java.io.FileDescriptor;
67 import java.io.PrintWriter;
68 import java.util.HashMap;
69 import java.util.Map;
70 import java.util.Objects;
71 
72 import javax.inject.Inject;
73 import javax.inject.Singleton;
74 
75 /**
76  *  Source of truth for all state / events related to the volume dialog.  No presentation.
77  *
78  *  All work done on a dedicated background worker thread & associated worker.
79  *
80  *  Methods ending in "W" must be called on the worker thread.
81  */
82 @Singleton
83 public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpable {
84     private static final String TAG = Util.logTag(VolumeDialogControllerImpl.class);
85 
86 
87     private static final int TOUCH_FEEDBACK_TIMEOUT_MS = 1000;
88     private static final int DYNAMIC_STREAM_START_INDEX = 100;
89     private static final int VIBRATE_HINT_DURATION = 50;
90     private static final AudioAttributes SONIFICIATION_VIBRATION_ATTRIBUTES =
91             new AudioAttributes.Builder()
92                     .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
93                     .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
94                     .build();
95 
96     static final ArrayMap<Integer, Integer> STREAMS = new ArrayMap<>();
97     static {
STREAMS.put(AudioSystem.STREAM_ALARM, R.string.stream_alarm)98         STREAMS.put(AudioSystem.STREAM_ALARM, R.string.stream_alarm);
STREAMS.put(AudioSystem.STREAM_BLUETOOTH_SCO, R.string.stream_bluetooth_sco)99         STREAMS.put(AudioSystem.STREAM_BLUETOOTH_SCO, R.string.stream_bluetooth_sco);
STREAMS.put(AudioSystem.STREAM_DTMF, R.string.stream_dtmf)100         STREAMS.put(AudioSystem.STREAM_DTMF, R.string.stream_dtmf);
STREAMS.put(AudioSystem.STREAM_MUSIC, R.string.stream_music)101         STREAMS.put(AudioSystem.STREAM_MUSIC, R.string.stream_music);
STREAMS.put(AudioSystem.STREAM_ACCESSIBILITY, R.string.stream_accessibility)102         STREAMS.put(AudioSystem.STREAM_ACCESSIBILITY, R.string.stream_accessibility);
STREAMS.put(AudioSystem.STREAM_NOTIFICATION, R.string.stream_notification)103         STREAMS.put(AudioSystem.STREAM_NOTIFICATION, R.string.stream_notification);
STREAMS.put(AudioSystem.STREAM_RING, R.string.stream_ring)104         STREAMS.put(AudioSystem.STREAM_RING, R.string.stream_ring);
STREAMS.put(AudioSystem.STREAM_SYSTEM, R.string.stream_system)105         STREAMS.put(AudioSystem.STREAM_SYSTEM, R.string.stream_system);
STREAMS.put(AudioSystem.STREAM_SYSTEM_ENFORCED, R.string.stream_system_enforced)106         STREAMS.put(AudioSystem.STREAM_SYSTEM_ENFORCED, R.string.stream_system_enforced);
STREAMS.put(AudioSystem.STREAM_TTS, R.string.stream_tts)107         STREAMS.put(AudioSystem.STREAM_TTS, R.string.stream_tts);
STREAMS.put(AudioSystem.STREAM_VOICE_CALL, R.string.stream_voice_call)108         STREAMS.put(AudioSystem.STREAM_VOICE_CALL, R.string.stream_voice_call);
109     }
110 
111     private final HandlerThread mWorkerThread;
112     private final W mWorker;
113     private final Context mContext;
114     private AudioManager mAudio;
115     private IAudioService mAudioService;
116     protected StatusBar mStatusBar;
117     private final NotificationManager mNoMan;
118     private final SettingObserver mObserver;
119     private final Receiver mReceiver = new Receiver();
120     private final MediaSessions mMediaSessions;
121     protected C mCallbacks = new C();
122     private final State mState = new State();
123     protected final MediaSessionsCallbacks mMediaSessionsCallbacksW = new MediaSessionsCallbacks();
124     private final Vibrator mVibrator;
125     private final boolean mHasVibrator;
126     private boolean mShowA11yStream;
127     private boolean mShowVolumeDialog;
128     private boolean mShowSafetyWarning;
129     private long mLastToggledRingerOn;
130     private final NotificationManager mNotificationManager;
131 
132     private boolean mDestroyed;
133     private VolumePolicy mVolumePolicy;
134     private boolean mShowDndTile = true;
135     @GuardedBy("this")
136     private UserActivityListener mUserActivityListener;
137 
138     protected final VC mVolumeController = new VC();
139 
140     @Inject
VolumeDialogControllerImpl(Context context)141     public VolumeDialogControllerImpl(Context context) {
142         mContext = context.getApplicationContext();
143         mNotificationManager = (NotificationManager) mContext.getSystemService(
144                 Context.NOTIFICATION_SERVICE);
145         Events.writeEvent(mContext, Events.EVENT_COLLECTION_STARTED);
146         mWorkerThread = new HandlerThread(VolumeDialogControllerImpl.class.getSimpleName());
147         mWorkerThread.start();
148         mWorker = new W(mWorkerThread.getLooper());
149         mMediaSessions = createMediaSessions(mContext, mWorkerThread.getLooper(),
150                 mMediaSessionsCallbacksW);
151         mAudio = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
152         mNoMan = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
153         mObserver = new SettingObserver(mWorker);
154         mObserver.init();
155         mReceiver.init();
156         mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
157         mHasVibrator = mVibrator != null && mVibrator.hasVibrator();
158         mAudioService = IAudioService.Stub.asInterface(
159                 ServiceManager.getService(Context.AUDIO_SERVICE));
160         updateStatusBar();
161 
162         boolean accessibilityVolumeStreamActive = context.getSystemService(
163                 AccessibilityManager.class).isAccessibilityVolumeStreamActive();
164         mVolumeController.setA11yMode(accessibilityVolumeStreamActive ?
165                     VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME :
166                         VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME);
167     }
168 
getAudioManager()169     public AudioManager getAudioManager() {
170         return mAudio;
171     }
172 
dismiss()173     public void dismiss() {
174         mCallbacks.onDismissRequested(Events.DISMISS_REASON_VOLUME_CONTROLLER);
175     }
176 
setVolumeController()177     protected void setVolumeController() {
178         try {
179             mAudio.setVolumeController(mVolumeController);
180         } catch (SecurityException e) {
181             Log.w(TAG, "Unable to set the volume controller", e);
182             return;
183         }
184     }
185 
setAudioManagerStreamVolume(int stream, int level, int flag)186     protected void setAudioManagerStreamVolume(int stream, int level, int flag) {
187         mAudio.setStreamVolume(stream, level, flag);
188     }
189 
getAudioManagerStreamVolume(int stream)190     protected int getAudioManagerStreamVolume(int stream) {
191         return mAudio.getLastAudibleStreamVolume(stream);
192     }
193 
getAudioManagerStreamMaxVolume(int stream)194     protected int getAudioManagerStreamMaxVolume(int stream) {
195         return mAudio.getStreamMaxVolume(stream);
196     }
197 
getAudioManagerStreamMinVolume(int stream)198     protected int getAudioManagerStreamMinVolume(int stream) {
199         return mAudio.getStreamMinVolumeInt(stream);
200     }
201 
register()202     public void register() {
203         setVolumeController();
204         setVolumePolicy(mVolumePolicy);
205         showDndTile(mShowDndTile);
206         try {
207             mMediaSessions.init();
208         } catch (SecurityException e) {
209             Log.w(TAG, "No access to media sessions", e);
210         }
211     }
212 
setVolumePolicy(VolumePolicy policy)213     public void setVolumePolicy(VolumePolicy policy) {
214         mVolumePolicy = policy;
215         if (mVolumePolicy == null) return;
216         try {
217             mAudio.setVolumePolicy(mVolumePolicy);
218         } catch (NoSuchMethodError e) {
219             Log.w(TAG, "No volume policy api");
220         }
221     }
222 
createMediaSessions(Context context, Looper looper, MediaSessions.Callbacks callbacks)223     protected MediaSessions createMediaSessions(Context context, Looper looper,
224             MediaSessions.Callbacks callbacks) {
225         return new MediaSessions(context, looper, callbacks);
226     }
227 
destroy()228     public void destroy() {
229         if (D.BUG) Log.d(TAG, "destroy");
230         if (mDestroyed) return;
231         mDestroyed = true;
232         Events.writeEvent(mContext, Events.EVENT_COLLECTION_STOPPED);
233         mMediaSessions.destroy();
234         mObserver.destroy();
235         mReceiver.destroy();
236         mWorkerThread.quitSafely();
237     }
238 
dump(FileDescriptor fd, PrintWriter pw, String[] args)239     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
240         pw.println(VolumeDialogControllerImpl.class.getSimpleName() + " state:");
241         pw.print("  mDestroyed: "); pw.println(mDestroyed);
242         pw.print("  mVolumePolicy: "); pw.println(mVolumePolicy);
243         pw.print("  mState: "); pw.println(mState.toString(4));
244         pw.print("  mShowDndTile: "); pw.println(mShowDndTile);
245         pw.print("  mHasVibrator: "); pw.println(mHasVibrator);
246         pw.print("  mRemoteStreams: "); pw.println(mMediaSessionsCallbacksW.mRemoteStreams
247                 .values());
248         pw.print("  mShowA11yStream: "); pw.println(mShowA11yStream);
249         pw.println();
250         mMediaSessions.dump(pw);
251     }
252 
addCallback(Callbacks callback, Handler handler)253     public void addCallback(Callbacks callback, Handler handler) {
254         mCallbacks.add(callback, handler);
255         callback.onAccessibilityModeChanged(mShowA11yStream);
256     }
257 
setUserActivityListener(UserActivityListener listener)258     public void setUserActivityListener(UserActivityListener listener) {
259         if (mDestroyed) return;
260         synchronized (this) {
261             mUserActivityListener = listener;
262         }
263     }
264 
removeCallback(Callbacks callback)265     public void removeCallback(Callbacks callback) {
266         mCallbacks.remove(callback);
267     }
268 
getState()269     public void getState() {
270         if (mDestroyed) return;
271         mWorker.sendEmptyMessage(W.GET_STATE);
272     }
273 
areCaptionsEnabled()274     public boolean areCaptionsEnabled() {
275         int currentValue = Settings.Secure.getInt(mContext.getContentResolver(),
276                 Settings.Secure.ODI_CAPTIONS_ENABLED, 0);
277         return currentValue == 1;
278     }
279 
setCaptionsEnabled(boolean isEnabled)280     public void setCaptionsEnabled(boolean isEnabled) {
281         Settings.Secure.putInt(mContext.getContentResolver(),
282                 Settings.Secure.ODI_CAPTIONS_ENABLED, isEnabled ? 1 : 0);
283     }
284 
285     @Override
isCaptionStreamOptedOut()286     public boolean isCaptionStreamOptedOut() {
287         // TODO(b/129768185): Removing secure setting, to be replaced by sound event listener
288         return false;
289     }
290 
getCaptionsComponentState(boolean fromTooltip)291     public void getCaptionsComponentState(boolean fromTooltip) {
292         if (mDestroyed) return;
293         mWorker.obtainMessage(W.GET_CAPTIONS_COMPONENT_STATE, fromTooltip).sendToTarget();
294     }
295 
notifyVisible(boolean visible)296     public void notifyVisible(boolean visible) {
297         if (mDestroyed) return;
298         mWorker.obtainMessage(W.NOTIFY_VISIBLE, visible ? 1 : 0, 0).sendToTarget();
299     }
300 
userActivity()301     public void userActivity() {
302         if (mDestroyed) return;
303         mWorker.removeMessages(W.USER_ACTIVITY);
304         mWorker.sendEmptyMessage(W.USER_ACTIVITY);
305     }
306 
setRingerMode(int value, boolean external)307     public void setRingerMode(int value, boolean external) {
308         if (mDestroyed) return;
309         mWorker.obtainMessage(W.SET_RINGER_MODE, value, external ? 1 : 0).sendToTarget();
310     }
311 
setZenMode(int value)312     public void setZenMode(int value) {
313         if (mDestroyed) return;
314         mWorker.obtainMessage(W.SET_ZEN_MODE, value, 0).sendToTarget();
315     }
316 
setExitCondition(Condition condition)317     public void setExitCondition(Condition condition) {
318         if (mDestroyed) return;
319         mWorker.obtainMessage(W.SET_EXIT_CONDITION, condition).sendToTarget();
320     }
321 
setStreamMute(int stream, boolean mute)322     public void setStreamMute(int stream, boolean mute) {
323         if (mDestroyed) return;
324         mWorker.obtainMessage(W.SET_STREAM_MUTE, stream, mute ? 1 : 0).sendToTarget();
325     }
326 
setStreamVolume(int stream, int level)327     public void setStreamVolume(int stream, int level) {
328         if (mDestroyed) return;
329         mWorker.obtainMessage(W.SET_STREAM_VOLUME, stream, level).sendToTarget();
330     }
331 
setActiveStream(int stream)332     public void setActiveStream(int stream) {
333         if (mDestroyed) return;
334         mWorker.obtainMessage(W.SET_ACTIVE_STREAM, stream, 0).sendToTarget();
335     }
336 
setEnableDialogs(boolean volumeUi, boolean safetyWarning)337     public void setEnableDialogs(boolean volumeUi, boolean safetyWarning) {
338       mShowVolumeDialog = volumeUi;
339       mShowSafetyWarning = safetyWarning;
340     }
341 
342     @Override
scheduleTouchFeedback()343     public void scheduleTouchFeedback() {
344         mLastToggledRingerOn = System.currentTimeMillis();
345     }
346 
playTouchFeedback()347     private void playTouchFeedback() {
348         if (System.currentTimeMillis() - mLastToggledRingerOn < TOUCH_FEEDBACK_TIMEOUT_MS) {
349             try {
350                 mAudioService.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD);
351             } catch (RemoteException e) {
352                 // ignore
353             }
354         }
355     }
356 
vibrate(VibrationEffect effect)357     public void vibrate(VibrationEffect effect) {
358         if (mHasVibrator) {
359             mVibrator.vibrate(effect, SONIFICIATION_VIBRATION_ATTRIBUTES);
360         }
361     }
362 
hasVibrator()363     public boolean hasVibrator() {
364         return mHasVibrator;
365     }
366 
onNotifyVisibleW(boolean visible)367     private void onNotifyVisibleW(boolean visible) {
368         if (mDestroyed) return;
369         mAudio.notifyVolumeControllerVisible(mVolumeController, visible);
370         if (!visible) {
371             if (updateActiveStreamW(-1)) {
372                 mCallbacks.onStateChanged(mState);
373             }
374         }
375     }
376 
onUserActivityW()377     private void onUserActivityW() {
378         synchronized (this) {
379             if (mUserActivityListener != null) {
380                 mUserActivityListener.onUserActivity();
381             }
382         }
383     }
384 
onShowSafetyWarningW(int flags)385     private void onShowSafetyWarningW(int flags) {
386         if (mShowSafetyWarning) {
387             mCallbacks.onShowSafetyWarning(flags);
388         }
389     }
390 
onGetCaptionsComponentStateW(boolean fromTooltip)391     private void onGetCaptionsComponentStateW(boolean fromTooltip) {
392         try {
393             String componentNameString = mContext.getString(
394                     com.android.internal.R.string.config_defaultSystemCaptionsService);
395             if (TextUtils.isEmpty(componentNameString)) {
396                 // component doesn't exist
397                 mCallbacks.onCaptionComponentStateChanged(false, fromTooltip);
398                 return;
399             }
400 
401             if (D.BUG) {
402                 Log.i(TAG, String.format(
403                         "isCaptionsServiceEnabled componentNameString=%s", componentNameString));
404             }
405 
406             ComponentName componentName = ComponentName.unflattenFromString(componentNameString);
407             if (componentName == null) {
408                 mCallbacks.onCaptionComponentStateChanged(false, fromTooltip);
409                 return;
410             }
411 
412             PackageManager packageManager = mContext.getPackageManager();
413             mCallbacks.onCaptionComponentStateChanged(
414                     packageManager.getComponentEnabledSetting(componentName)
415                     == PackageManager.COMPONENT_ENABLED_STATE_ENABLED, fromTooltip);
416         } catch (Exception ex) {
417             Log.e(TAG,
418                     "isCaptionsServiceEnabled failed to check for captions component", ex);
419             mCallbacks.onCaptionComponentStateChanged(false, fromTooltip);
420         }
421     }
422 
onAccessibilityModeChanged(Boolean showA11yStream)423     private void onAccessibilityModeChanged(Boolean showA11yStream) {
424         mCallbacks.onAccessibilityModeChanged(showA11yStream);
425     }
426 
checkRoutedToBluetoothW(int stream)427     private boolean checkRoutedToBluetoothW(int stream) {
428         boolean changed = false;
429         if (stream == AudioManager.STREAM_MUSIC) {
430             final boolean routedToBluetooth =
431                     (mAudio.getDevicesForStream(AudioManager.STREAM_MUSIC) &
432                             (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
433                             AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
434                             AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0;
435             changed |= updateStreamRoutedToBluetoothW(stream, routedToBluetooth);
436         }
437         return changed;
438     }
439 
updateStatusBar()440     private void updateStatusBar() {
441         if (mStatusBar == null) {
442             mStatusBar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
443         }
444     }
445 
shouldShowUI(int flags)446     private boolean shouldShowUI(int flags) {
447         updateStatusBar();
448         // if status bar isn't null, check if phone is in AOD, else check flags
449         // since we could be using a different status bar
450         return mStatusBar != null ?
451                 mStatusBar.getWakefulnessState() != WakefulnessLifecycle.WAKEFULNESS_ASLEEP
452                 && mStatusBar.getWakefulnessState() !=
453                         WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP
454                 && mStatusBar.isDeviceInteractive()
455                 && (flags & AudioManager.FLAG_SHOW_UI) != 0 && mShowVolumeDialog
456                 : mShowVolumeDialog && (flags & AudioManager.FLAG_SHOW_UI) != 0;
457     }
458 
onVolumeChangedW(int stream, int flags)459     boolean onVolumeChangedW(int stream, int flags) {
460         final boolean showUI = shouldShowUI(flags);
461         final boolean fromKey = (flags & AudioManager.FLAG_FROM_KEY) != 0;
462         final boolean showVibrateHint = (flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0;
463         final boolean showSilentHint = (flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0;
464         boolean changed = false;
465         if (showUI) {
466             changed |= updateActiveStreamW(stream);
467         }
468         int lastAudibleStreamVolume = getAudioManagerStreamVolume(stream);
469         changed |= updateStreamLevelW(stream, lastAudibleStreamVolume);
470         changed |= checkRoutedToBluetoothW(showUI ? AudioManager.STREAM_MUSIC : stream);
471         if (changed) {
472             mCallbacks.onStateChanged(mState);
473         }
474         if (showUI) {
475             mCallbacks.onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
476         }
477         if (showVibrateHint) {
478             mCallbacks.onShowVibrateHint();
479         }
480         if (showSilentHint) {
481             mCallbacks.onShowSilentHint();
482         }
483         if (changed && fromKey) {
484             Events.writeEvent(mContext, Events.EVENT_KEY, stream, lastAudibleStreamVolume);
485         }
486         return changed;
487     }
488 
updateActiveStreamW(int activeStream)489     private boolean updateActiveStreamW(int activeStream) {
490         if (activeStream == mState.activeStream) return false;
491         mState.activeStream = activeStream;
492         Events.writeEvent(mContext, Events.EVENT_ACTIVE_STREAM_CHANGED, activeStream);
493         if (D.BUG) Log.d(TAG, "updateActiveStreamW " + activeStream);
494         final int s = activeStream < DYNAMIC_STREAM_START_INDEX ? activeStream : -1;
495         if (D.BUG) Log.d(TAG, "forceVolumeControlStream " + s);
496         mAudio.forceVolumeControlStream(s);
497         return true;
498     }
499 
500     private StreamState streamStateW(int stream) {
501         StreamState ss = mState.states.get(stream);
502         if (ss == null) {
503             ss = new StreamState();
504             mState.states.put(stream, ss);
505         }
506         return ss;
507     }
508 
509     private void onGetStateW() {
510         for (int stream : STREAMS.keySet()) {
511             updateStreamLevelW(stream, getAudioManagerStreamVolume(stream));
512             streamStateW(stream).levelMin = getAudioManagerStreamMinVolume(stream);
513             streamStateW(stream).levelMax = Math.max(1, getAudioManagerStreamMaxVolume(stream));
514             updateStreamMuteW(stream, mAudio.isStreamMute(stream));
515             final StreamState ss = streamStateW(stream);
516             ss.muteSupported = mAudio.isStreamAffectedByMute(stream);
517             ss.name = STREAMS.get(stream);
518             checkRoutedToBluetoothW(stream);
519         }
520         updateRingerModeExternalW(mAudio.getRingerMode());
521         updateZenModeW();
522         updateZenConfig();
523         updateEffectsSuppressorW(mNoMan.getEffectsSuppressor());
524         mCallbacks.onStateChanged(mState);
525     }
526 
527     private boolean updateStreamRoutedToBluetoothW(int stream, boolean routedToBluetooth) {
528         final StreamState ss = streamStateW(stream);
529         if (ss.routedToBluetooth == routedToBluetooth) return false;
530         ss.routedToBluetooth = routedToBluetooth;
531         if (D.BUG) Log.d(TAG, "updateStreamRoutedToBluetoothW stream=" + stream
532                 + " routedToBluetooth=" + routedToBluetooth);
533         return true;
534     }
535 
536     private boolean updateStreamLevelW(int stream, int level) {
537         final StreamState ss = streamStateW(stream);
538         if (ss.level == level) return false;
539         ss.level = level;
540         if (isLogWorthy(stream)) {
541             Events.writeEvent(mContext, Events.EVENT_LEVEL_CHANGED, stream, level);
542         }
543         return true;
544     }
545 
546     private static boolean isLogWorthy(int stream) {
547         switch (stream) {
548             case AudioSystem.STREAM_ALARM:
549             case AudioSystem.STREAM_BLUETOOTH_SCO:
550             case AudioSystem.STREAM_MUSIC:
551             case AudioSystem.STREAM_RING:
552             case AudioSystem.STREAM_SYSTEM:
553             case AudioSystem.STREAM_VOICE_CALL:
554                 return true;
555         }
556         return false;
557     }
558 
559     private boolean updateStreamMuteW(int stream, boolean muted) {
560         final StreamState ss = streamStateW(stream);
561         if (ss.muted == muted) return false;
562         ss.muted = muted;
563         if (isLogWorthy(stream)) {
564             Events.writeEvent(mContext, Events.EVENT_MUTE_CHANGED, stream, muted);
565         }
566         if (muted && isRinger(stream)) {
567             updateRingerModeInternalW(mAudio.getRingerModeInternal());
568         }
569         return true;
570     }
571 
572     private static boolean isRinger(int stream) {
573         return stream == AudioManager.STREAM_RING || stream == AudioManager.STREAM_NOTIFICATION;
574     }
575 
576     private boolean updateEffectsSuppressorW(ComponentName effectsSuppressor) {
577         if (Objects.equals(mState.effectsSuppressor, effectsSuppressor)) return false;
578         mState.effectsSuppressor = effectsSuppressor;
579         mState.effectsSuppressorName = getApplicationName(mContext, mState.effectsSuppressor);
580         Events.writeEvent(mContext, Events.EVENT_SUPPRESSOR_CHANGED, mState.effectsSuppressor,
581                 mState.effectsSuppressorName);
582         return true;
583     }
584 
585     private static String getApplicationName(Context context, ComponentName component) {
586         if (component == null) return null;
587         final PackageManager pm = context.getPackageManager();
588         final String pkg = component.getPackageName();
589         try {
590             final ApplicationInfo ai = pm.getApplicationInfo(pkg, 0);
591             final String rt = Objects.toString(ai.loadLabel(pm), "").trim();
592             if (rt.length() > 0) {
593                 return rt;
594             }
595         } catch (NameNotFoundException e) {}
596         return pkg;
597     }
598 
updateZenModeW()599     private boolean updateZenModeW() {
600         final int zen = Settings.Global.getInt(mContext.getContentResolver(),
601                 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
602         if (mState.zenMode == zen) return false;
603         mState.zenMode = zen;
604         Events.writeEvent(mContext, Events.EVENT_ZEN_MODE_CHANGED, zen);
605         return true;
606     }
607 
updateZenConfig()608     private boolean updateZenConfig() {
609         final NotificationManager.Policy policy =
610                 mNotificationManager.getConsolidatedNotificationPolicy();
611         boolean disallowAlarms = (policy.priorityCategories & NotificationManager.Policy
612                 .PRIORITY_CATEGORY_ALARMS) == 0;
613         boolean disallowMedia = (policy.priorityCategories & NotificationManager.Policy
614                 .PRIORITY_CATEGORY_MEDIA) == 0;
615         boolean disallowSystem = (policy.priorityCategories & NotificationManager.Policy
616                 .PRIORITY_CATEGORY_SYSTEM) == 0;
617         boolean disallowRinger = ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(policy);
618         if (mState.disallowAlarms == disallowAlarms
619                 && mState.disallowMedia == disallowMedia
620                 && mState.disallowRinger == disallowRinger
621                 && mState.disallowSystem == disallowSystem) {
622             return false;
623         }
624         mState.disallowAlarms = disallowAlarms;
625         mState.disallowMedia = disallowMedia;
626         mState.disallowSystem = disallowSystem;
627         mState.disallowRinger = disallowRinger;
628         Events.writeEvent(mContext, Events.EVENT_ZEN_CONFIG_CHANGED, "disallowAlarms=" +
629                 disallowAlarms + " disallowMedia=" + disallowMedia + " disallowSystem=" +
630                 disallowSystem + " disallowRinger=" + disallowRinger);
631         return true;
632     }
633 
updateRingerModeExternalW(int rm)634     private boolean updateRingerModeExternalW(int rm) {
635         if (rm == mState.ringerModeExternal) return false;
636         mState.ringerModeExternal = rm;
637         Events.writeEvent(mContext, Events.EVENT_EXTERNAL_RINGER_MODE_CHANGED, rm);
638         return true;
639     }
640 
updateRingerModeInternalW(int rm)641     private boolean updateRingerModeInternalW(int rm) {
642         if (rm == mState.ringerModeInternal) return false;
643         mState.ringerModeInternal = rm;
644         Events.writeEvent(mContext, Events.EVENT_INTERNAL_RINGER_MODE_CHANGED, rm);
645 
646         if (mState.ringerModeInternal == RINGER_MODE_NORMAL) {
647             playTouchFeedback();
648         }
649 
650         return true;
651     }
652 
onSetRingerModeW(int mode, boolean external)653     private void onSetRingerModeW(int mode, boolean external) {
654         if (external) {
655             mAudio.setRingerMode(mode);
656         } else {
657             mAudio.setRingerModeInternal(mode);
658         }
659     }
660 
onSetStreamMuteW(int stream, boolean mute)661     private void onSetStreamMuteW(int stream, boolean mute) {
662         mAudio.adjustStreamVolume(stream, mute ? AudioManager.ADJUST_MUTE
663                 : AudioManager.ADJUST_UNMUTE, 0);
664     }
665 
onSetStreamVolumeW(int stream, int level)666     private void onSetStreamVolumeW(int stream, int level) {
667         if (D.BUG) Log.d(TAG, "onSetStreamVolume " + stream + " level=" + level);
668         if (stream >= DYNAMIC_STREAM_START_INDEX) {
669             mMediaSessionsCallbacksW.setStreamVolume(stream, level);
670             return;
671         }
672         setAudioManagerStreamVolume(stream, level, 0);
673     }
674 
onSetActiveStreamW(int stream)675     private void onSetActiveStreamW(int stream) {
676         boolean changed = updateActiveStreamW(stream);
677         if (changed) {
678             mCallbacks.onStateChanged(mState);
679         }
680     }
681 
onSetExitConditionW(Condition condition)682     private void onSetExitConditionW(Condition condition) {
683         mNoMan.setZenMode(mState.zenMode, condition != null ? condition.id : null, TAG);
684     }
685 
onSetZenModeW(int mode)686     private void onSetZenModeW(int mode) {
687         if (D.BUG) Log.d(TAG, "onSetZenModeW " + mode);
688         mNoMan.setZenMode(mode, null, TAG);
689     }
690 
onDismissRequestedW(int reason)691     private void onDismissRequestedW(int reason) {
692         mCallbacks.onDismissRequested(reason);
693     }
694 
showDndTile(boolean visible)695     public void showDndTile(boolean visible) {
696         if (D.BUG) Log.d(TAG, "showDndTile");
697         DndTile.setVisible(mContext, visible);
698     }
699 
700     private final class VC extends IVolumeController.Stub {
701         private final String TAG = VolumeDialogControllerImpl.TAG + ".VC";
702 
703         @Override
displaySafeVolumeWarning(int flags)704         public void displaySafeVolumeWarning(int flags) throws RemoteException {
705             if (D.BUG) Log.d(TAG, "displaySafeVolumeWarning "
706                     + Util.audioManagerFlagsToString(flags));
707             if (mDestroyed) return;
708             mWorker.obtainMessage(W.SHOW_SAFETY_WARNING, flags, 0).sendToTarget();
709         }
710 
711         @Override
volumeChanged(int streamType, int flags)712         public void volumeChanged(int streamType, int flags) throws RemoteException {
713             if (D.BUG) Log.d(TAG, "volumeChanged " + AudioSystem.streamToString(streamType)
714                     + " " + Util.audioManagerFlagsToString(flags));
715             if (mDestroyed) return;
716             mWorker.obtainMessage(W.VOLUME_CHANGED, streamType, flags).sendToTarget();
717         }
718 
719         @Override
masterMuteChanged(int flags)720         public void masterMuteChanged(int flags) throws RemoteException {
721             if (D.BUG) Log.d(TAG, "masterMuteChanged");
722         }
723 
724         @Override
setLayoutDirection(int layoutDirection)725         public void setLayoutDirection(int layoutDirection) throws RemoteException {
726             if (D.BUG) Log.d(TAG, "setLayoutDirection");
727             if (mDestroyed) return;
728             mWorker.obtainMessage(W.LAYOUT_DIRECTION_CHANGED, layoutDirection, 0).sendToTarget();
729         }
730 
731         @Override
dismiss()732         public void dismiss() throws RemoteException {
733             if (D.BUG) Log.d(TAG, "dismiss requested");
734             if (mDestroyed) return;
735             mWorker.obtainMessage(W.DISMISS_REQUESTED, Events.DISMISS_REASON_VOLUME_CONTROLLER, 0)
736                     .sendToTarget();
737             mWorker.sendEmptyMessage(W.DISMISS_REQUESTED);
738         }
739 
740         @Override
setA11yMode(int mode)741         public void setA11yMode(int mode) {
742             if (D.BUG) Log.d(TAG, "setA11yMode to " + mode);
743             if (mDestroyed) return;
744             switch (mode) {
745                 case VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME:
746                     // "legacy" mode
747                     mShowA11yStream = false;
748                     break;
749                 case VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME:
750                     mShowA11yStream = true;
751                     break;
752                 default:
753                     Log.e(TAG, "Invalid accessibility mode " + mode);
754                     break;
755             }
756             mWorker.obtainMessage(W.ACCESSIBILITY_MODE_CHANGED, mShowA11yStream).sendToTarget();
757         }
758     }
759 
760     private final class W extends Handler {
761         private static final int VOLUME_CHANGED = 1;
762         private static final int DISMISS_REQUESTED = 2;
763         private static final int GET_STATE = 3;
764         private static final int SET_RINGER_MODE = 4;
765         private static final int SET_ZEN_MODE = 5;
766         private static final int SET_EXIT_CONDITION = 6;
767         private static final int SET_STREAM_MUTE = 7;
768         private static final int LAYOUT_DIRECTION_CHANGED = 8;
769         private static final int CONFIGURATION_CHANGED = 9;
770         private static final int SET_STREAM_VOLUME = 10;
771         private static final int SET_ACTIVE_STREAM = 11;
772         private static final int NOTIFY_VISIBLE = 12;
773         private static final int USER_ACTIVITY = 13;
774         private static final int SHOW_SAFETY_WARNING = 14;
775         private static final int ACCESSIBILITY_MODE_CHANGED = 15;
776         private static final int GET_CAPTIONS_COMPONENT_STATE = 16;
777 
W(Looper looper)778         W(Looper looper) {
779             super(looper);
780         }
781 
782         @Override
handleMessage(Message msg)783         public void handleMessage(Message msg) {
784             switch (msg.what) {
785                 case VOLUME_CHANGED: onVolumeChangedW(msg.arg1, msg.arg2); break;
786                 case DISMISS_REQUESTED: onDismissRequestedW(msg.arg1); break;
787                 case GET_STATE: onGetStateW(); break;
788                 case SET_RINGER_MODE: onSetRingerModeW(msg.arg1, msg.arg2 != 0); break;
789                 case SET_ZEN_MODE: onSetZenModeW(msg.arg1); break;
790                 case SET_EXIT_CONDITION: onSetExitConditionW((Condition) msg.obj); break;
791                 case SET_STREAM_MUTE: onSetStreamMuteW(msg.arg1, msg.arg2 != 0); break;
792                 case LAYOUT_DIRECTION_CHANGED: mCallbacks.onLayoutDirectionChanged(msg.arg1); break;
793                 case CONFIGURATION_CHANGED: mCallbacks.onConfigurationChanged(); break;
794                 case SET_STREAM_VOLUME: onSetStreamVolumeW(msg.arg1, msg.arg2); break;
795                 case SET_ACTIVE_STREAM: onSetActiveStreamW(msg.arg1); break;
796                 case NOTIFY_VISIBLE: onNotifyVisibleW(msg.arg1 != 0); break;
797                 case USER_ACTIVITY: onUserActivityW(); break;
798                 case SHOW_SAFETY_WARNING: onShowSafetyWarningW(msg.arg1); break;
799                 case GET_CAPTIONS_COMPONENT_STATE:
800                     onGetCaptionsComponentStateW((Boolean) msg.obj); break;
801                 case ACCESSIBILITY_MODE_CHANGED: onAccessibilityModeChanged((Boolean) msg.obj);
802             }
803         }
804     }
805 
806     class C implements Callbacks {
807         private final HashMap<Callbacks, Handler> mCallbackMap = new HashMap<>();
808 
add(Callbacks callback, Handler handler)809         public void add(Callbacks callback, Handler handler) {
810             if (callback == null || handler == null) throw new IllegalArgumentException();
811             mCallbackMap.put(callback, handler);
812         }
813 
remove(Callbacks callback)814         public void remove(Callbacks callback) {
815             mCallbackMap.remove(callback);
816         }
817 
818         @Override
onShowRequested(final int reason)819         public void onShowRequested(final int reason) {
820             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
821                 entry.getValue().post(new Runnable() {
822                     @Override
823                     public void run() {
824                         entry.getKey().onShowRequested(reason);
825                     }
826                 });
827             }
828         }
829 
830         @Override
onDismissRequested(final int reason)831         public void onDismissRequested(final int reason) {
832             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
833                 entry.getValue().post(new Runnable() {
834                     @Override
835                     public void run() {
836                         entry.getKey().onDismissRequested(reason);
837                     }
838                 });
839             }
840         }
841 
842         @Override
onStateChanged(final State state)843         public void onStateChanged(final State state) {
844             final long time = System.currentTimeMillis();
845             final State copy = state.copy();
846             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
847                 entry.getValue().post(new Runnable() {
848                     @Override
849                     public void run() {
850                         entry.getKey().onStateChanged(copy);
851                     }
852                 });
853             }
854             Events.writeState(time, copy);
855         }
856 
857         @Override
onLayoutDirectionChanged(final int layoutDirection)858         public void onLayoutDirectionChanged(final int layoutDirection) {
859             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
860                 entry.getValue().post(new Runnable() {
861                     @Override
862                     public void run() {
863                         entry.getKey().onLayoutDirectionChanged(layoutDirection);
864                     }
865                 });
866             }
867         }
868 
869         @Override
onConfigurationChanged()870         public void onConfigurationChanged() {
871             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
872                 entry.getValue().post(new Runnable() {
873                     @Override
874                     public void run() {
875                         entry.getKey().onConfigurationChanged();
876                     }
877                 });
878             }
879         }
880 
881         @Override
onShowVibrateHint()882         public void onShowVibrateHint() {
883             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
884                 entry.getValue().post(new Runnable() {
885                     @Override
886                     public void run() {
887                         entry.getKey().onShowVibrateHint();
888                     }
889                 });
890             }
891         }
892 
893         @Override
onShowSilentHint()894         public void onShowSilentHint() {
895             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
896                 entry.getValue().post(new Runnable() {
897                     @Override
898                     public void run() {
899                         entry.getKey().onShowSilentHint();
900                     }
901                 });
902             }
903         }
904 
905         @Override
onScreenOff()906         public void onScreenOff() {
907             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
908                 entry.getValue().post(new Runnable() {
909                     @Override
910                     public void run() {
911                         entry.getKey().onScreenOff();
912                     }
913                 });
914             }
915         }
916 
917         @Override
onShowSafetyWarning(final int flags)918         public void onShowSafetyWarning(final int flags) {
919             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
920                 entry.getValue().post(new Runnable() {
921                     @Override
922                     public void run() {
923                         entry.getKey().onShowSafetyWarning(flags);
924                     }
925                 });
926             }
927         }
928 
929         @Override
onAccessibilityModeChanged(Boolean showA11yStream)930         public void onAccessibilityModeChanged(Boolean showA11yStream) {
931             boolean show = showA11yStream == null ? false : showA11yStream;
932             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
933                 entry.getValue().post(new Runnable() {
934                     @Override
935                     public void run() {
936                         entry.getKey().onAccessibilityModeChanged(show);
937                     }
938                 });
939             }
940         }
941 
942         @Override
onCaptionComponentStateChanged( Boolean isComponentEnabled, Boolean fromTooltip)943         public void onCaptionComponentStateChanged(
944                 Boolean isComponentEnabled, Boolean fromTooltip) {
945             boolean componentEnabled = isComponentEnabled == null ? false : isComponentEnabled;
946             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
947                 entry.getValue().post(
948                         () -> entry.getKey().onCaptionComponentStateChanged(
949                                 componentEnabled, fromTooltip));
950             }
951         }
952     }
953 
954 
955     private final class SettingObserver extends ContentObserver {
956         private final Uri ZEN_MODE_URI =
957                 Settings.Global.getUriFor(Settings.Global.ZEN_MODE);
958         private final Uri ZEN_MODE_CONFIG_URI =
959                 Settings.Global.getUriFor(Settings.Global.ZEN_MODE_CONFIG_ETAG);
960 
SettingObserver(Handler handler)961         public SettingObserver(Handler handler) {
962             super(handler);
963         }
964 
init()965         public void init() {
966             mContext.getContentResolver().registerContentObserver(ZEN_MODE_URI, false, this);
967             mContext.getContentResolver().registerContentObserver(ZEN_MODE_CONFIG_URI, false, this);
968         }
969 
destroy()970         public void destroy() {
971             mContext.getContentResolver().unregisterContentObserver(this);
972         }
973 
974         @Override
onChange(boolean selfChange, Uri uri)975         public void onChange(boolean selfChange, Uri uri) {
976             boolean changed = false;
977             if (ZEN_MODE_URI.equals(uri)) {
978                 changed = updateZenModeW();
979             }
980             if (ZEN_MODE_CONFIG_URI.equals(uri)) {
981                 changed |= updateZenConfig();
982             }
983 
984             if (changed) {
985                 mCallbacks.onStateChanged(mState);
986             }
987         }
988     }
989 
990     private final class Receiver extends BroadcastReceiver {
991 
init()992         public void init() {
993             final IntentFilter filter = new IntentFilter();
994             filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
995             filter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
996             filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
997             filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
998             filter.addAction(AudioManager.STREAM_MUTE_CHANGED_ACTION);
999             filter.addAction(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
1000             filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
1001             filter.addAction(Intent.ACTION_SCREEN_OFF);
1002             filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
1003             mContext.registerReceiver(this, filter, null, mWorker);
1004         }
1005 
destroy()1006         public void destroy() {
1007             mContext.unregisterReceiver(this);
1008         }
1009 
1010         @Override
onReceive(Context context, Intent intent)1011         public void onReceive(Context context, Intent intent) {
1012             final String action = intent.getAction();
1013             boolean changed = false;
1014             if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
1015                 final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
1016                 final int level = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
1017                 final int oldLevel = intent
1018                         .getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, -1);
1019                 if (D.BUG) Log.d(TAG, "onReceive VOLUME_CHANGED_ACTION stream=" + stream
1020                         + " level=" + level + " oldLevel=" + oldLevel);
1021                 changed = updateStreamLevelW(stream, level);
1022             } else if (action.equals(AudioManager.STREAM_DEVICES_CHANGED_ACTION)) {
1023                 final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
1024                 final int devices = intent
1025                         .getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_DEVICES, -1);
1026                 final int oldDevices = intent
1027                         .getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_DEVICES, -1);
1028                 if (D.BUG) Log.d(TAG, "onReceive STREAM_DEVICES_CHANGED_ACTION stream="
1029                         + stream + " devices=" + devices + " oldDevices=" + oldDevices);
1030                 changed = checkRoutedToBluetoothW(stream);
1031                 changed |= onVolumeChangedW(stream, 0);
1032             } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
1033                 final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
1034                 if (isInitialStickyBroadcast()) mState.ringerModeExternal = rm;
1035                 if (D.BUG) Log.d(TAG, "onReceive RINGER_MODE_CHANGED_ACTION rm="
1036                         + Util.ringerModeToString(rm));
1037                 changed = updateRingerModeExternalW(rm);
1038             } else if (action.equals(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)) {
1039                 final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
1040                 if (isInitialStickyBroadcast()) mState.ringerModeInternal = rm;
1041                 if (D.BUG) Log.d(TAG, "onReceive INTERNAL_RINGER_MODE_CHANGED_ACTION rm="
1042                         + Util.ringerModeToString(rm));
1043                 changed = updateRingerModeInternalW(rm);
1044             } else if (action.equals(AudioManager.STREAM_MUTE_CHANGED_ACTION)) {
1045                 final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
1046                 final boolean muted = intent
1047                         .getBooleanExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, false);
1048                 if (D.BUG) Log.d(TAG, "onReceive STREAM_MUTE_CHANGED_ACTION stream=" + stream
1049                         + " muted=" + muted);
1050                 changed = updateStreamMuteW(stream, muted);
1051             } else if (action.equals(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED)) {
1052                 if (D.BUG) Log.d(TAG, "onReceive ACTION_EFFECTS_SUPPRESSOR_CHANGED");
1053                 changed = updateEffectsSuppressorW(mNoMan.getEffectsSuppressor());
1054             } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
1055                 if (D.BUG) Log.d(TAG, "onReceive ACTION_CONFIGURATION_CHANGED");
1056                 mCallbacks.onConfigurationChanged();
1057             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1058                 if (D.BUG) Log.d(TAG, "onReceive ACTION_SCREEN_OFF");
1059                 mCallbacks.onScreenOff();
1060             } else if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
1061                 if (D.BUG) Log.d(TAG, "onReceive ACTION_CLOSE_SYSTEM_DIALOGS");
1062                 dismiss();
1063             }
1064             if (changed) {
1065                 mCallbacks.onStateChanged(mState);
1066             }
1067         }
1068     }
1069 
1070     protected final class MediaSessionsCallbacks implements MediaSessions.Callbacks {
1071         private final HashMap<Token, Integer> mRemoteStreams = new HashMap<>();
1072 
1073         private int mNextStream = DYNAMIC_STREAM_START_INDEX;
1074 
1075         @Override
onRemoteUpdate(Token token, String name, PlaybackInfo pi)1076         public void onRemoteUpdate(Token token, String name, PlaybackInfo pi) {
1077             addStream(token, "onRemoteUpdate");
1078             final int stream = mRemoteStreams.get(token);
1079             boolean changed = mState.states.indexOfKey(stream) < 0;
1080             final StreamState ss = streamStateW(stream);
1081             ss.dynamic = true;
1082             ss.levelMin = 0;
1083             ss.levelMax = pi.getMaxVolume();
1084             if (ss.level != pi.getCurrentVolume()) {
1085                 ss.level = pi.getCurrentVolume();
1086                 changed = true;
1087             }
1088             if (!Objects.equals(ss.remoteLabel, name)) {
1089                 ss.name = -1;
1090                 ss.remoteLabel = name;
1091                 changed = true;
1092             }
1093             if (changed) {
1094                 if (D.BUG) Log.d(TAG, "onRemoteUpdate: " + name + ": " + ss.level
1095                         + " of " + ss.levelMax);
1096                 mCallbacks.onStateChanged(mState);
1097             }
1098         }
1099 
1100         @Override
1101         public void onRemoteVolumeChanged(Token token, int flags) {
1102             addStream(token, "onRemoteVolumeChanged");
1103             final int stream = mRemoteStreams.get(token);
1104             final boolean showUI = shouldShowUI(flags);
1105             boolean changed = updateActiveStreamW(stream);
1106             if (showUI) {
1107                 changed |= checkRoutedToBluetoothW(AudioManager.STREAM_MUSIC);
1108             }
1109             if (changed) {
1110                 mCallbacks.onStateChanged(mState);
1111             }
1112             if (showUI) {
1113                 mCallbacks.onShowRequested(Events.SHOW_REASON_REMOTE_VOLUME_CHANGED);
1114             }
1115         }
1116 
1117         @Override
1118         public void onRemoteRemoved(Token token) {
1119             if (!mRemoteStreams.containsKey(token)) {
1120                 if (D.BUG) Log.d(TAG, "onRemoteRemoved: stream doesn't exist, "
1121                         + "aborting remote removed for token:" +  token.toString());
1122                 return;
1123             }
1124             final int stream = mRemoteStreams.get(token);
1125             mState.states.remove(stream);
1126             if (mState.activeStream == stream) {
1127                 updateActiveStreamW(-1);
1128             }
1129             mCallbacks.onStateChanged(mState);
1130         }
1131 
1132         public void setStreamVolume(int stream, int level) {
1133             final Token t = findToken(stream);
1134             if (t == null) {
1135                 Log.w(TAG, "setStreamVolume: No token found for stream: " + stream);
1136                 return;
1137             }
1138             mMediaSessions.setVolume(t, level);
1139         }
1140 
1141         private Token findToken(int stream) {
1142             for (Map.Entry<Token, Integer> entry : mRemoteStreams.entrySet()) {
1143                 if (entry.getValue().equals(stream)) {
1144                     return entry.getKey();
1145                 }
1146             }
1147             return null;
1148         }
1149 
1150         private void addStream(Token token, String triggeringMethod) {
1151             if (!mRemoteStreams.containsKey(token)) {
1152                 mRemoteStreams.put(token, mNextStream);
1153                 if (D.BUG) Log.d(TAG, triggeringMethod + ": added stream " +  mNextStream
1154                         + " from token + "+ token.toString());
1155                 mNextStream++;
1156             }
1157         }
1158     }
1159 
1160     public interface UserActivityListener {
1161         void onUserActivity();
1162     }
1163 }
1164