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