• 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.ActivityManager;
22 import android.app.KeyguardManager;
23 import android.app.NotificationManager;
24 import android.content.BroadcastReceiver;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.content.pm.ApplicationInfo;
30 import android.content.pm.PackageManager;
31 import android.content.pm.PackageManager.NameNotFoundException;
32 import android.database.ContentObserver;
33 import android.media.AudioAttributes;
34 import android.media.AudioManager;
35 import android.media.AudioSystem;
36 import android.media.IAudioService;
37 import android.media.IVolumeController;
38 import android.media.MediaRouter2Manager;
39 import android.media.RoutingSessionInfo;
40 import android.media.VolumePolicy;
41 import android.media.session.MediaController;
42 import android.media.session.MediaController.PlaybackInfo;
43 import android.media.session.MediaSession.Token;
44 import android.net.Uri;
45 import android.os.Handler;
46 import android.os.Looper;
47 import android.os.Message;
48 import android.os.RemoteException;
49 import android.os.VibrationEffect;
50 import android.provider.Settings;
51 import android.service.notification.Condition;
52 import android.service.notification.ZenModeConfig;
53 import android.util.ArrayMap;
54 import android.util.Log;
55 import android.util.Slog;
56 import android.view.accessibility.AccessibilityManager;
57 import android.view.accessibility.CaptioningManager;
58 
59 import androidx.lifecycle.Observer;
60 
61 import com.android.internal.annotations.GuardedBy;
62 import com.android.settingslib.volume.MediaSessions;
63 import com.android.systemui.Dumpable;
64 import com.android.systemui.R;
65 import com.android.systemui.broadcast.BroadcastDispatcher;
66 import com.android.systemui.dagger.SysUISingleton;
67 import com.android.systemui.dump.DumpManager;
68 import com.android.systemui.keyguard.WakefulnessLifecycle;
69 import com.android.systemui.plugins.VolumeDialogController;
70 import com.android.systemui.qs.tiles.DndTile;
71 import com.android.systemui.settings.UserTracker;
72 import com.android.systemui.statusbar.VibratorHelper;
73 import com.android.systemui.util.RingerModeLiveData;
74 import com.android.systemui.util.RingerModeTracker;
75 import com.android.systemui.util.concurrency.ThreadFactory;
76 
77 import java.io.PrintWriter;
78 import java.util.HashMap;
79 import java.util.List;
80 import java.util.Map;
81 import java.util.Objects;
82 import java.util.concurrent.ConcurrentHashMap;
83 
84 import javax.inject.Inject;
85 
86 /**
87  *  Source of truth for all state / events related to the volume dialog.  No presentation.
88  *
89  *  All work done on a dedicated background worker thread & associated worker.
90  *
91  *  Methods ending in "W" must be called on the worker thread.
92  */
93 @SysUISingleton
94 public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpable {
95     private static final String TAG = Util.logTag(VolumeDialogControllerImpl.class);
96 
97     private static final int TOUCH_FEEDBACK_TIMEOUT_MS = 1000;
98     private static final int DYNAMIC_STREAM_START_INDEX = 100;
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 W mWorker;
121     private final Context mContext;
122     private final Looper mWorkerLooper;
123     private final PackageManager mPackageManager;
124     private final MediaRouter2Manager mRouter2Manager;
125     private final WakefulnessLifecycle mWakefulnessLifecycle;
126     private final AudioManager mAudio;
127     private final IAudioService mAudioService;
128     private final NotificationManager mNoMan;
129     private final SettingObserver mObserver;
130     private final Receiver mReceiver = new Receiver();
131     private final RingerModeObservers mRingerModeObservers;
132     private final MediaSessions mMediaSessions;
133     private final CaptioningManager mCaptioningManager;
134     private final KeyguardManager mKeyguardManager;
135     private final ActivityManager mActivityManager;
136     private final UserTracker mUserTracker;
137     protected C mCallbacks = new C();
138     private final State mState = new State();
139     protected final MediaSessionsCallbacks mMediaSessionsCallbacksW;
140     private final VibratorHelper mVibrator;
141     private final boolean mHasVibrator;
142     private boolean mShowA11yStream;
143     private boolean mShowVolumeDialog;
144     private boolean mShowSafetyWarning;
145     private long mLastToggledRingerOn;
146     private boolean mDeviceInteractive = true;
147 
148     private VolumePolicy mVolumePolicy;
149     @GuardedBy("this")
150     private UserActivityListener mUserActivityListener;
151 
152     protected final VC mVolumeController = new VC();
153     protected final BroadcastDispatcher mBroadcastDispatcher;
154 
155     private final WakefulnessLifecycle.Observer mWakefullnessLifecycleObserver =
156             new WakefulnessLifecycle.Observer() {
157         @Override
158         public void onStartedWakingUp() {
159             mDeviceInteractive = true;
160         }
161 
162         @Override
163         public void onFinishedGoingToSleep() {
164             mDeviceInteractive = false;
165         }
166     };
167 
168     @Inject
VolumeDialogControllerImpl( Context context, BroadcastDispatcher broadcastDispatcher, RingerModeTracker ringerModeTracker, ThreadFactory theadFactory, AudioManager audioManager, NotificationManager notificationManager, VibratorHelper vibrator, IAudioService iAudioService, AccessibilityManager accessibilityManager, PackageManager packageManager, WakefulnessLifecycle wakefulnessLifecycle, CaptioningManager captioningManager, KeyguardManager keyguardManager, ActivityManager activityManager, UserTracker userTracker, DumpManager dumpManager )169     public VolumeDialogControllerImpl(
170             Context context,
171             BroadcastDispatcher broadcastDispatcher,
172             RingerModeTracker ringerModeTracker,
173             ThreadFactory theadFactory,
174             AudioManager audioManager,
175             NotificationManager notificationManager,
176             VibratorHelper vibrator,
177             IAudioService iAudioService,
178             AccessibilityManager accessibilityManager,
179             PackageManager packageManager,
180             WakefulnessLifecycle wakefulnessLifecycle,
181             CaptioningManager captioningManager,
182             KeyguardManager keyguardManager,
183             ActivityManager activityManager,
184             UserTracker userTracker,
185             DumpManager dumpManager
186     ) {
187         mContext = context.getApplicationContext();
188         mPackageManager = packageManager;
189         mWakefulnessLifecycle = wakefulnessLifecycle;
190         Events.writeEvent(Events.EVENT_COLLECTION_STARTED);
191         mWorkerLooper = theadFactory.buildLooperOnNewThread(
192                 VolumeDialogControllerImpl.class.getSimpleName());
193         mWorker = new W(mWorkerLooper);
194         mRouter2Manager = MediaRouter2Manager.getInstance(mContext);
195         mMediaSessionsCallbacksW = new MediaSessionsCallbacks(mContext);
196         mMediaSessions = createMediaSessions(mContext, mWorkerLooper, mMediaSessionsCallbacksW);
197         mAudio = audioManager;
198         mNoMan = notificationManager;
199         mObserver = new SettingObserver(mWorker);
200         mRingerModeObservers = new RingerModeObservers(
201                 (RingerModeLiveData) ringerModeTracker.getRingerMode(),
202                 (RingerModeLiveData) ringerModeTracker.getRingerModeInternal()
203         );
204         mRingerModeObservers.init();
205         mBroadcastDispatcher = broadcastDispatcher;
206         mObserver.init();
207         mReceiver.init();
208         mVibrator = vibrator;
209         mHasVibrator = mVibrator.hasVibrator();
210         mAudioService = iAudioService;
211         mCaptioningManager = captioningManager;
212         mKeyguardManager = keyguardManager;
213         mActivityManager = activityManager;
214         mUserTracker = userTracker;
215         dumpManager.registerDumpable("VolumeDialogControllerImpl", this);
216 
217         boolean accessibilityVolumeStreamActive = accessibilityManager
218                 .isAccessibilityVolumeStreamActive();
219         mVolumeController.setA11yMode(accessibilityVolumeStreamActive ?
220                     VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME :
221                         VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME);
222 
223         mWakefulnessLifecycle.addObserver(mWakefullnessLifecycleObserver);
224     }
225 
getAudioManager()226     public AudioManager getAudioManager() {
227         return mAudio;
228     }
229 
dismiss()230     public void dismiss() {
231         mCallbacks.onDismissRequested(Events.DISMISS_REASON_VOLUME_CONTROLLER);
232     }
233 
setVolumeController()234     protected void setVolumeController() {
235         try {
236             mAudio.setVolumeController(mVolumeController);
237         } catch (SecurityException e) {
238             Log.w(TAG, "Unable to set the volume controller", e);
239         }
240     }
241 
setAudioManagerStreamVolume(int stream, int level, int flag)242     protected void setAudioManagerStreamVolume(int stream, int level, int flag) {
243         mAudio.setStreamVolume(stream, level, flag);
244     }
245 
getAudioManagerStreamVolume(int stream)246     protected int getAudioManagerStreamVolume(int stream) {
247         return mAudio.getLastAudibleStreamVolume(stream);
248     }
249 
getAudioManagerStreamMaxVolume(int stream)250     protected int getAudioManagerStreamMaxVolume(int stream) {
251         return mAudio.getStreamMaxVolume(stream);
252     }
253 
getAudioManagerStreamMinVolume(int stream)254     protected int getAudioManagerStreamMinVolume(int stream) {
255         return mAudio.getStreamMinVolumeInt(stream);
256     }
257 
register()258     public void register() {
259         setVolumeController();
260         setVolumePolicy(mVolumePolicy);
261         showDndTile();
262         try {
263             mMediaSessions.init();
264         } catch (SecurityException e) {
265             Log.w(TAG, "No access to media sessions", e);
266         }
267     }
268 
setVolumePolicy(VolumePolicy policy)269     public void setVolumePolicy(VolumePolicy policy) {
270         mVolumePolicy = policy;
271         if (mVolumePolicy == null) return;
272         try {
273             mAudio.setVolumePolicy(mVolumePolicy);
274         } catch (NoSuchMethodError e) {
275             Log.w(TAG, "No volume policy api");
276         }
277     }
278 
createMediaSessions(Context context, Looper looper, MediaSessions.Callbacks callbacks)279     protected MediaSessions createMediaSessions(Context context, Looper looper,
280             MediaSessions.Callbacks callbacks) {
281         return new MediaSessions(context, looper, callbacks);
282     }
283 
dump(PrintWriter pw, String[] args)284     public void dump(PrintWriter pw, String[] args) {
285         pw.println(VolumeDialogControllerImpl.class.getSimpleName() + " state:");
286         pw.print("  mVolumePolicy: "); pw.println(mVolumePolicy);
287         pw.print("  mState: "); pw.println(mState.toString(4));
288         pw.print("  mHasVibrator: "); pw.println(mHasVibrator);
289         synchronized (mMediaSessionsCallbacksW.mRemoteStreams) {
290             pw.print("  mRemoteStreams: ");
291             pw.println(mMediaSessionsCallbacksW.mRemoteStreams
292                     .values());
293         }
294         pw.print("  mShowA11yStream: "); pw.println(mShowA11yStream);
295         pw.println();
296         mMediaSessions.dump(pw);
297     }
298 
addCallback(Callbacks callback, Handler handler)299     public void addCallback(Callbacks callback, Handler handler) {
300         mCallbacks.add(callback, handler);
301         callback.onAccessibilityModeChanged(mShowA11yStream);
302     }
303 
setUserActivityListener(UserActivityListener listener)304     public void setUserActivityListener(UserActivityListener listener) {
305         synchronized (this) {
306             mUserActivityListener = listener;
307         }
308     }
309 
removeCallback(Callbacks callback)310     public void removeCallback(Callbacks callback) {
311         mCallbacks.remove(callback);
312     }
313 
getState()314     public void getState() {
315         mWorker.sendEmptyMessage(W.GET_STATE);
316     }
317 
areCaptionsEnabled()318     public boolean areCaptionsEnabled() {
319         return mCaptioningManager.isSystemAudioCaptioningEnabled();
320     }
321 
setCaptionsEnabled(boolean isEnabled)322     public void setCaptionsEnabled(boolean isEnabled) {
323         mCaptioningManager.setSystemAudioCaptioningEnabled(isEnabled);
324     }
325 
getCaptionsComponentState(boolean fromTooltip)326     public void getCaptionsComponentState(boolean fromTooltip) {
327         mWorker.obtainMessage(W.GET_CAPTIONS_COMPONENT_STATE, fromTooltip).sendToTarget();
328     }
329 
notifyVisible(boolean visible)330     public void notifyVisible(boolean visible) {
331         mWorker.obtainMessage(W.NOTIFY_VISIBLE, visible ? 1 : 0, 0).sendToTarget();
332     }
333 
userActivity()334     public void userActivity() {
335         mWorker.removeMessages(W.USER_ACTIVITY);
336         mWorker.sendEmptyMessage(W.USER_ACTIVITY);
337     }
338 
setRingerMode(int value, boolean external)339     public void setRingerMode(int value, boolean external) {
340         mWorker.obtainMessage(W.SET_RINGER_MODE, value, external ? 1 : 0).sendToTarget();
341     }
342 
setZenMode(int value)343     public void setZenMode(int value) {
344         mWorker.obtainMessage(W.SET_ZEN_MODE, value, 0).sendToTarget();
345     }
346 
setExitCondition(Condition condition)347     public void setExitCondition(Condition condition) {
348         mWorker.obtainMessage(W.SET_EXIT_CONDITION, condition).sendToTarget();
349     }
350 
setStreamMute(int stream, boolean mute)351     public void setStreamMute(int stream, boolean mute) {
352         mWorker.obtainMessage(W.SET_STREAM_MUTE, stream, mute ? 1 : 0).sendToTarget();
353     }
354 
setStreamVolume(int stream, int level)355     public void setStreamVolume(int stream, int level) {
356         mWorker.obtainMessage(W.SET_STREAM_VOLUME, stream, level).sendToTarget();
357     }
358 
setActiveStream(int stream)359     public void setActiveStream(int stream) {
360         mWorker.obtainMessage(W.SET_ACTIVE_STREAM, stream, 0).sendToTarget();
361     }
362 
setEnableDialogs(boolean volumeUi, boolean safetyWarning)363     public void setEnableDialogs(boolean volumeUi, boolean safetyWarning) {
364       mShowVolumeDialog = volumeUi;
365       mShowSafetyWarning = safetyWarning;
366     }
367 
368     @Override
scheduleTouchFeedback()369     public void scheduleTouchFeedback() {
370         mLastToggledRingerOn = System.currentTimeMillis();
371     }
372 
playTouchFeedback()373     private void playTouchFeedback() {
374         if (System.currentTimeMillis() - mLastToggledRingerOn < TOUCH_FEEDBACK_TIMEOUT_MS) {
375             try {
376                 mAudioService.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD,
377                         mUserTracker.getUserId());
378             } catch (RemoteException e) {
379                 // ignore
380             }
381         }
382     }
383 
vibrate(VibrationEffect effect)384     public void vibrate(VibrationEffect effect) {
385         mVibrator.vibrate(effect, SONIFICIATION_VIBRATION_ATTRIBUTES);
386     }
387 
hasVibrator()388     public boolean hasVibrator() {
389         return mHasVibrator;
390     }
391 
onNotifyVisibleW(boolean visible)392     private void onNotifyVisibleW(boolean visible) {
393         mAudio.notifyVolumeControllerVisible(mVolumeController, visible);
394         if (!visible) {
395             if (updateActiveStreamW(-1)) {
396                 mCallbacks.onStateChanged(mState);
397             }
398         }
399     }
400 
onUserActivityW()401     private void onUserActivityW() {
402         synchronized (this) {
403             if (mUserActivityListener != null) {
404                 mUserActivityListener.onUserActivity();
405             }
406         }
407     }
408 
onShowSafetyWarningW(int flags)409     private void onShowSafetyWarningW(int flags) {
410         if (mShowSafetyWarning) {
411             mCallbacks.onShowSafetyWarning(flags);
412         }
413     }
414 
onGetCaptionsComponentStateW(boolean fromTooltip)415     private void onGetCaptionsComponentStateW(boolean fromTooltip) {
416         mCallbacks.onCaptionComponentStateChanged(
417                 mCaptioningManager.isSystemAudioCaptioningUiEnabled(), fromTooltip);
418     }
419 
onAccessibilityModeChanged(Boolean showA11yStream)420     private void onAccessibilityModeChanged(Boolean showA11yStream) {
421         mCallbacks.onAccessibilityModeChanged(showA11yStream);
422     }
423 
checkRoutedToBluetoothW(int stream)424     private boolean checkRoutedToBluetoothW(int stream) {
425         boolean changed = false;
426         if (stream == AudioManager.STREAM_MUSIC) {
427             // Note: Here we didn't use DEVICE_OUT_BLE_SPEAKER and DEVICE_OUT_BLE_BROADCAST
428             //       Since their values overlap with DEVICE_OUT_EARPIECE and DEVICE_OUT_SPEAKER.
429             //       Anyway, we can check BLE devices by using just DEVICE_OUT_BLE_HEADSET.
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 |
435                             AudioManager.DEVICE_OUT_BLE_HEADSET)) != 0;
436             changed |= updateStreamRoutedToBluetoothW(stream, routedToBluetooth);
437         } else if (stream == AudioManager.STREAM_VOICE_CALL) {
438             final boolean routedToBluetooth =
439                     (mAudio.getDevicesForStream(AudioManager.STREAM_VOICE_CALL)
440                             & AudioManager.DEVICE_OUT_BLE_HEADSET) != 0;
441             changed |= updateStreamRoutedToBluetoothW(stream, routedToBluetooth);
442         }
443         return changed;
444     }
445 
shouldShowUI(int flags)446     private boolean shouldShowUI(int flags) {
447         int wakefulness = mWakefulnessLifecycle.getWakefulness();
448         return wakefulness != WakefulnessLifecycle.WAKEFULNESS_ASLEEP
449                 && wakefulness != WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP
450                 && mDeviceInteractive && (flags & AudioManager.FLAG_SHOW_UI) != 0
451                 && mShowVolumeDialog;
452     }
453 
onVolumeChangedW(int stream, int flags)454     boolean onVolumeChangedW(int stream, int flags) {
455         final boolean showUI = shouldShowUI(flags);
456         final boolean fromKey = (flags & AudioManager.FLAG_FROM_KEY) != 0;
457         final boolean showVibrateHint = (flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0;
458         final boolean showSilentHint = (flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0;
459         boolean changed = false;
460         if (showUI) {
461             changed |= updateActiveStreamW(stream);
462         }
463         int lastAudibleStreamVolume = getAudioManagerStreamVolume(stream);
464         changed |= updateStreamLevelW(stream, lastAudibleStreamVolume);
465         changed |= checkRoutedToBluetoothW(showUI ? AudioManager.STREAM_MUSIC : stream);
466         if (changed) {
467             mCallbacks.onStateChanged(mState);
468         }
469         if (showUI) {
470             onShowRequestedW(Events.SHOW_REASON_VOLUME_CHANGED);
471         }
472         if (showVibrateHint) {
473             mCallbacks.onShowVibrateHint();
474         }
475         if (showSilentHint) {
476             mCallbacks.onShowSilentHint();
477         }
478         if (changed && fromKey) {
479             Events.writeEvent(Events.EVENT_KEY, stream, lastAudibleStreamVolume);
480         }
481         return changed;
482     }
483 
updateActiveStreamW(int activeStream)484     private boolean updateActiveStreamW(int activeStream) {
485         if (activeStream == mState.activeStream) return false;
486         mState.activeStream = activeStream;
487         Events.writeEvent(Events.EVENT_ACTIVE_STREAM_CHANGED, activeStream);
488         if (D.BUG) Log.d(TAG, "updateActiveStreamW " + activeStream);
489         final int s = activeStream < DYNAMIC_STREAM_START_INDEX ? activeStream : -1;
490         if (D.BUG) Log.d(TAG, "forceVolumeControlStream " + s);
491         mAudio.forceVolumeControlStream(s);
492         return true;
493     }
494 
495     private StreamState streamStateW(int stream) {
496         StreamState ss = mState.states.get(stream);
497         if (ss == null) {
498             ss = new StreamState();
499             mState.states.put(stream, ss);
500         }
501         return ss;
502     }
503 
504     private void onGetStateW() {
505         for (int stream : STREAMS.keySet()) {
506             updateStreamLevelW(stream, getAudioManagerStreamVolume(stream));
507             streamStateW(stream).levelMin = getAudioManagerStreamMinVolume(stream);
508             streamStateW(stream).levelMax = Math.max(1, getAudioManagerStreamMaxVolume(stream));
509             updateStreamMuteW(stream, mAudio.isStreamMute(stream));
510             final StreamState ss = streamStateW(stream);
511             ss.muteSupported = mAudio.isStreamAffectedByMute(stream);
512             ss.name = STREAMS.get(stream);
513             checkRoutedToBluetoothW(stream);
514         }
515         // We are not destroyed so this is listening and has updated information
516         updateRingerModeExternalW(mRingerModeObservers.mRingerMode.getValue());
517         updateZenModeW();
518         updateZenConfig();
519         updateEffectsSuppressorW(mNoMan.getEffectsSuppressor());
520         mCallbacks.onStateChanged(mState);
521     }
522 
523     private boolean updateStreamRoutedToBluetoothW(int stream, boolean routedToBluetooth) {
524         final StreamState ss = streamStateW(stream);
525         if (ss.routedToBluetooth == routedToBluetooth) return false;
526         ss.routedToBluetooth = routedToBluetooth;
527         if (D.BUG) Log.d(TAG, "updateStreamRoutedToBluetoothW stream=" + stream
528                 + " routedToBluetooth=" + routedToBluetooth);
529         return true;
530     }
531 
532     private boolean updateStreamLevelW(int stream, int level) {
533         final StreamState ss = streamStateW(stream);
534         if (ss.level == level) return false;
535         ss.level = level;
536         if (isLogWorthy(stream)) {
537             Events.writeEvent(Events.EVENT_LEVEL_CHANGED, stream, level);
538         }
539         return true;
540     }
541 
542     private static boolean isLogWorthy(int stream) {
543         switch (stream) {
544             case AudioSystem.STREAM_ALARM:
545             case AudioSystem.STREAM_BLUETOOTH_SCO:
546             case AudioSystem.STREAM_MUSIC:
547             case AudioSystem.STREAM_RING:
548             case AudioSystem.STREAM_SYSTEM:
549             case AudioSystem.STREAM_VOICE_CALL:
550                 return true;
551         }
552         return false;
553     }
554 
555     private boolean updateStreamMuteW(int stream, boolean muted) {
556         final StreamState ss = streamStateW(stream);
557         if (ss.muted == muted) return false;
558         ss.muted = muted;
559         if (isLogWorthy(stream)) {
560             Events.writeEvent(Events.EVENT_MUTE_CHANGED, stream, muted);
561         }
562         if (muted && isRinger(stream)) {
563             updateRingerModeInternalW(mRingerModeObservers.mRingerModeInternal.getValue());
564         }
565         return true;
566     }
567 
568     private static boolean isRinger(int stream) {
569         return stream == AudioManager.STREAM_RING || stream == AudioManager.STREAM_NOTIFICATION;
570     }
571 
572     private boolean updateEffectsSuppressorW(ComponentName effectsSuppressor) {
573         if (Objects.equals(mState.effectsSuppressor, effectsSuppressor)) return false;
574         mState.effectsSuppressor = effectsSuppressor;
575         mState.effectsSuppressorName =
576                 getApplicationName(mPackageManager, mState.effectsSuppressor);
577         Events.writeEvent(Events.EVENT_SUPPRESSOR_CHANGED, mState.effectsSuppressor,
578                 mState.effectsSuppressorName);
579         return true;
580     }
581 
582     private static String getApplicationName(PackageManager pm, ComponentName component) {
583         if (component == null) return null;
584         final String pkg = component.getPackageName();
585         try {
586             final ApplicationInfo ai = pm.getApplicationInfo(pkg, 0);
587             final String rt = Objects.toString(ai.loadLabel(pm), "").trim();
588             if (rt.length() > 0) {
589                 return rt;
590             }
591         } catch (NameNotFoundException e) {}
592         return pkg;
593     }
594 
updateZenModeW()595     private boolean updateZenModeW() {
596         final int zen = Settings.Global.getInt(mContext.getContentResolver(),
597                 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
598         if (mState.zenMode == zen) return false;
599         mState.zenMode = zen;
600         Events.writeEvent(Events.EVENT_ZEN_MODE_CHANGED, zen);
601         return true;
602     }
603 
updateZenConfig()604     private boolean updateZenConfig() {
605         final NotificationManager.Policy policy = mNoMan.getConsolidatedNotificationPolicy();
606         boolean disallowAlarms = (policy.priorityCategories & NotificationManager.Policy
607                 .PRIORITY_CATEGORY_ALARMS) == 0;
608         boolean disallowMedia = (policy.priorityCategories & NotificationManager.Policy
609                 .PRIORITY_CATEGORY_MEDIA) == 0;
610         boolean disallowSystem = (policy.priorityCategories & NotificationManager.Policy
611                 .PRIORITY_CATEGORY_SYSTEM) == 0;
612         // ringer controls notifications, ringer and system sounds, so only disallow ringer changes
613         // if all relevant (notifications + ringer + system) sounds are not allowed to bypass DND
614         boolean disallowRinger = ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(policy);
615         if (mState.disallowAlarms == disallowAlarms
616                 && mState.disallowMedia == disallowMedia
617                 && mState.disallowRinger == disallowRinger
618                 && mState.disallowSystem == disallowSystem) {
619             return false;
620         }
621         mState.disallowAlarms = disallowAlarms;
622         mState.disallowMedia = disallowMedia;
623         mState.disallowSystem = disallowSystem;
624         mState.disallowRinger = disallowRinger;
625         Events.writeEvent(Events.EVENT_ZEN_CONFIG_CHANGED, "disallowAlarms="
626                 + disallowAlarms + " disallowMedia=" + disallowMedia + " disallowSystem="
627                 + disallowSystem + " disallowRinger=" + disallowRinger);
628         return true;
629     }
630 
updateRingerModeExternalW(int rm)631     private boolean updateRingerModeExternalW(int rm) {
632         if (rm == mState.ringerModeExternal) return false;
633         mState.ringerModeExternal = rm;
634         Events.writeEvent(Events.EVENT_EXTERNAL_RINGER_MODE_CHANGED, rm);
635         return true;
636     }
637 
updateRingerModeInternalW(int rm)638     private boolean updateRingerModeInternalW(int rm) {
639         if (rm == mState.ringerModeInternal) return false;
640         mState.ringerModeInternal = rm;
641         Events.writeEvent(Events.EVENT_INTERNAL_RINGER_MODE_CHANGED, rm);
642 
643         if (mState.ringerModeInternal == RINGER_MODE_NORMAL) {
644             playTouchFeedback();
645         }
646 
647         return true;
648     }
649 
onShowRequestedW(int reason)650     private void onShowRequestedW(int reason) {
651         mCallbacks.onShowRequested(reason, mKeyguardManager.isKeyguardLocked(),
652                 mActivityManager.getLockTaskModeState());
653     }
654 
onSetRingerModeW(int mode, boolean external)655     private void onSetRingerModeW(int mode, boolean external) {
656         if (external) {
657             mAudio.setRingerMode(mode);
658         } else {
659             mAudio.setRingerModeInternal(mode);
660         }
661     }
662 
onSetStreamMuteW(int stream, boolean mute)663     private void onSetStreamMuteW(int stream, boolean mute) {
664         mAudio.adjustStreamVolume(stream, mute ? AudioManager.ADJUST_MUTE
665                 : AudioManager.ADJUST_UNMUTE, 0);
666     }
667 
onSetStreamVolumeW(int stream, int level)668     private void onSetStreamVolumeW(int stream, int level) {
669         if (D.BUG) Log.d(TAG, "onSetStreamVolume " + stream + " level=" + level);
670         if (stream >= DYNAMIC_STREAM_START_INDEX) {
671             mMediaSessionsCallbacksW.setStreamVolume(stream, level);
672             return;
673         }
674         setAudioManagerStreamVolume(stream, level, 0);
675     }
676 
onSetActiveStreamW(int stream)677     private void onSetActiveStreamW(int stream) {
678         boolean changed = updateActiveStreamW(stream);
679         if (changed) {
680             mCallbacks.onStateChanged(mState);
681         }
682     }
683 
onSetExitConditionW(Condition condition)684     private void onSetExitConditionW(Condition condition) {
685         mNoMan.setZenMode(mState.zenMode, condition != null ? condition.id : null, TAG);
686     }
687 
onSetZenModeW(int mode)688     private void onSetZenModeW(int mode) {
689         if (D.BUG) Log.d(TAG, "onSetZenModeW " + mode);
690         mNoMan.setZenMode(mode, null, TAG);
691     }
692 
onDismissRequestedW(int reason)693     private void onDismissRequestedW(int reason) {
694         mCallbacks.onDismissRequested(reason);
695     }
696 
showDndTile()697     public void showDndTile() {
698         if (D.BUG) Log.d(TAG, "showDndTile");
699         DndTile.setVisible(mContext, true);
700     }
701 
702     private final class VC extends IVolumeController.Stub {
703         private final String TAG = VolumeDialogControllerImpl.TAG + ".VC";
704 
705         @Override
displaySafeVolumeWarning(int flags)706         public void displaySafeVolumeWarning(int flags) throws RemoteException {
707             if (D.BUG) Log.d(TAG, "displaySafeVolumeWarning "
708                     + Util.audioManagerFlagsToString(flags));
709             mWorker.obtainMessage(W.SHOW_SAFETY_WARNING, flags, 0).sendToTarget();
710         }
711 
712         @Override
volumeChanged(int streamType, int flags)713         public void volumeChanged(int streamType, int flags) throws RemoteException {
714             if (D.BUG) Log.d(TAG, "volumeChanged " + AudioSystem.streamToString(streamType)
715                     + " " + Util.audioManagerFlagsToString(flags));
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             mWorker.obtainMessage(W.LAYOUT_DIRECTION_CHANGED, layoutDirection, 0).sendToTarget();
728         }
729 
730         @Override
dismiss()731         public void dismiss() throws RemoteException {
732             if (D.BUG) Log.d(TAG, "dismiss requested");
733             mWorker.obtainMessage(W.DISMISS_REQUESTED, Events.DISMISS_REASON_VOLUME_CONTROLLER, 0)
734                     .sendToTarget();
735             mWorker.sendEmptyMessage(W.DISMISS_REQUESTED);
736         }
737 
738         @Override
setA11yMode(int mode)739         public void setA11yMode(int mode) {
740             if (D.BUG) Log.d(TAG, "setA11yMode to " + mode);
741             switch (mode) {
742                 case VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME:
743                     // "legacy" mode
744                     mShowA11yStream = false;
745                     break;
746                 case VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME:
747                     mShowA11yStream = true;
748                     break;
749                 default:
750                     Log.e(TAG, "Invalid accessibility mode " + mode);
751                     break;
752             }
753             mWorker.obtainMessage(W.ACCESSIBILITY_MODE_CHANGED, mShowA11yStream).sendToTarget();
754         }
755     }
756 
757     private final class W extends Handler {
758         private static final int VOLUME_CHANGED = 1;
759         private static final int DISMISS_REQUESTED = 2;
760         private static final int GET_STATE = 3;
761         private static final int SET_RINGER_MODE = 4;
762         private static final int SET_ZEN_MODE = 5;
763         private static final int SET_EXIT_CONDITION = 6;
764         private static final int SET_STREAM_MUTE = 7;
765         private static final int LAYOUT_DIRECTION_CHANGED = 8;
766         private static final int CONFIGURATION_CHANGED = 9;
767         private static final int SET_STREAM_VOLUME = 10;
768         private static final int SET_ACTIVE_STREAM = 11;
769         private static final int NOTIFY_VISIBLE = 12;
770         private static final int USER_ACTIVITY = 13;
771         private static final int SHOW_SAFETY_WARNING = 14;
772         private static final int ACCESSIBILITY_MODE_CHANGED = 15;
773         private static final int GET_CAPTIONS_COMPONENT_STATE = 16;
774 
W(Looper looper)775         W(Looper looper) {
776             super(looper);
777         }
778 
779         @Override
handleMessage(Message msg)780         public void handleMessage(Message msg) {
781             switch (msg.what) {
782                 case VOLUME_CHANGED: onVolumeChangedW(msg.arg1, msg.arg2); break;
783                 case DISMISS_REQUESTED: onDismissRequestedW(msg.arg1); break;
784                 case GET_STATE: onGetStateW(); break;
785                 case SET_RINGER_MODE: onSetRingerModeW(msg.arg1, msg.arg2 != 0); break;
786                 case SET_ZEN_MODE: onSetZenModeW(msg.arg1); break;
787                 case SET_EXIT_CONDITION: onSetExitConditionW((Condition) msg.obj); break;
788                 case SET_STREAM_MUTE: onSetStreamMuteW(msg.arg1, msg.arg2 != 0); break;
789                 case LAYOUT_DIRECTION_CHANGED: mCallbacks.onLayoutDirectionChanged(msg.arg1); break;
790                 case CONFIGURATION_CHANGED: mCallbacks.onConfigurationChanged(); break;
791                 case SET_STREAM_VOLUME: onSetStreamVolumeW(msg.arg1, msg.arg2); break;
792                 case SET_ACTIVE_STREAM: onSetActiveStreamW(msg.arg1); break;
793                 case NOTIFY_VISIBLE: onNotifyVisibleW(msg.arg1 != 0); break;
794                 case USER_ACTIVITY: onUserActivityW(); break;
795                 case SHOW_SAFETY_WARNING: onShowSafetyWarningW(msg.arg1); break;
796                 case GET_CAPTIONS_COMPONENT_STATE:
797                     onGetCaptionsComponentStateW((Boolean) msg.obj); break;
798                 case ACCESSIBILITY_MODE_CHANGED: onAccessibilityModeChanged((Boolean) msg.obj);
799             }
800         }
801     }
802 
803     static class C implements Callbacks {
804         private final Map<Callbacks, Handler> mCallbackMap = new ConcurrentHashMap<>();
805 
add(Callbacks callback, Handler handler)806         public void add(Callbacks callback, Handler handler) {
807             if (callback == null || handler == null) throw new IllegalArgumentException();
808             mCallbackMap.put(callback, handler);
809         }
810 
remove(Callbacks callback)811         public void remove(Callbacks callback) {
812             mCallbackMap.remove(callback);
813         }
814 
815         @Override
onShowRequested( final int reason, final boolean keyguardLocked, final int lockTaskModeState)816         public void onShowRequested(
817                 final int reason,
818                 final boolean keyguardLocked,
819                 final int lockTaskModeState) {
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, keyguardLocked, lockTaskModeState);
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 && 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 && 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     private final class RingerModeObservers {
955 
956         private final RingerModeLiveData mRingerMode;
957         private final RingerModeLiveData mRingerModeInternal;
958 
959         private final Observer<Integer> mRingerModeObserver = new Observer<Integer>() {
960             @Override
961             public void onChanged(Integer value) {
962                 mWorker.post(() -> {
963                             final int rm = value;
964                             if (mRingerMode.getInitialSticky()) {
965                                 mState.ringerModeExternal = rm;
966                             }
967                             if (D.BUG) {
968                                 Log.d(TAG, "onChange ringer_mode rm="
969                                         + Util.ringerModeToString(rm));
970                             }
971                             if (updateRingerModeExternalW(rm)) {
972                                 mCallbacks.onStateChanged(mState);
973                             }
974                         }
975                 );
976             }
977         };
978 
979         private final Observer<Integer> mRingerModeInternalObserver = new Observer<Integer>() {
980             @Override
981             public void onChanged(Integer value) {
982                 mWorker.post(() -> {
983                             final int rm = value;
984                             if (mRingerModeInternal.getInitialSticky()) {
985                                 mState.ringerModeInternal = rm;
986                             }
987                             if (D.BUG) {
988                                 Log.d(TAG, "onChange internal_ringer_mode rm="
989                                         + Util.ringerModeToString(rm));
990                             }
991                             if (updateRingerModeInternalW(rm)) {
992                                 mCallbacks.onStateChanged(mState);
993                             }
994                         }
995                 );
996             }
997         };
998 
RingerModeObservers(RingerModeLiveData ringerMode, RingerModeLiveData ringerModeInternal)999         RingerModeObservers(RingerModeLiveData ringerMode,
1000                 RingerModeLiveData ringerModeInternal) {
1001             mRingerMode = ringerMode;
1002             mRingerModeInternal = ringerModeInternal;
1003         }
1004 
init()1005         public void init() {
1006             int initialValue = mRingerMode.getValue();
1007             if (initialValue != -1) {
1008                 // If it's not -1, set it to the initial value, if it's -1, it means that the
1009                 // tracker is not listening already and will obtain the sticky value.
1010                 mState.ringerModeExternal = initialValue;
1011             }
1012             mRingerMode.observeForever(mRingerModeObserver);
1013             initialValue = mRingerModeInternal.getValue();
1014             if (initialValue != -1) {
1015                 // If it's not -1, set it to the initial value, if it's -1, it means that the
1016                 // tracker is not listening already and will obtain the sticky value.
1017                 mState.ringerModeInternal = initialValue;
1018             }
1019             mRingerModeInternal.observeForever(mRingerModeInternalObserver);
1020         }
1021 
destroy()1022         public void destroy() {
1023             mRingerMode.removeObserver(mRingerModeObserver);
1024             mRingerModeInternal.removeObserver(mRingerModeInternalObserver);
1025         }
1026     }
1027 
1028     private final class SettingObserver extends ContentObserver {
1029         private final Uri ZEN_MODE_URI =
1030                 Settings.Global.getUriFor(Settings.Global.ZEN_MODE);
1031         private final Uri ZEN_MODE_CONFIG_URI =
1032                 Settings.Global.getUriFor(Settings.Global.ZEN_MODE_CONFIG_ETAG);
1033 
SettingObserver(Handler handler)1034         public SettingObserver(Handler handler) {
1035             super(handler);
1036         }
1037 
init()1038         public void init() {
1039             mContext.getContentResolver().registerContentObserver(ZEN_MODE_URI, false, this);
1040             mContext.getContentResolver().registerContentObserver(ZEN_MODE_CONFIG_URI, false, this);
1041         }
1042 
destroy()1043         public void destroy() {
1044             mContext.getContentResolver().unregisterContentObserver(this);
1045         }
1046 
1047         @Override
onChange(boolean selfChange, Uri uri)1048         public void onChange(boolean selfChange, Uri uri) {
1049             boolean changed = false;
1050             if (ZEN_MODE_URI.equals(uri)) {
1051                 changed = updateZenModeW();
1052             }
1053             if (ZEN_MODE_CONFIG_URI.equals(uri)) {
1054                 changed |= updateZenConfig();
1055             }
1056 
1057             if (changed) {
1058                 mCallbacks.onStateChanged(mState);
1059             }
1060         }
1061     }
1062 
1063     private final class Receiver extends BroadcastReceiver {
1064 
init()1065         public void init() {
1066             final IntentFilter filter = new IntentFilter();
1067             filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
1068             filter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
1069             filter.addAction(AudioManager.STREAM_MUTE_CHANGED_ACTION);
1070             filter.addAction(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
1071             filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
1072             filter.addAction(Intent.ACTION_SCREEN_OFF);
1073             filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
1074             mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mWorker);
1075         }
1076 
destroy()1077         public void destroy() {
1078             mBroadcastDispatcher.unregisterReceiver(this);
1079         }
1080 
1081         @Override
onReceive(Context context, Intent intent)1082         public void onReceive(Context context, Intent intent) {
1083             final String action = intent.getAction();
1084             boolean changed = false;
1085             if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
1086                 final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
1087                 final int level = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
1088                 final int oldLevel = intent
1089                         .getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, -1);
1090                 if (D.BUG) Log.d(TAG, "onReceive VOLUME_CHANGED_ACTION stream=" + stream
1091                         + " level=" + level + " oldLevel=" + oldLevel);
1092                 changed = updateStreamLevelW(stream, level);
1093             } else if (action.equals(AudioManager.STREAM_DEVICES_CHANGED_ACTION)) {
1094                 final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
1095                 final int devices = intent
1096                         .getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_DEVICES, -1);
1097                 final int oldDevices = intent
1098                         .getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_DEVICES, -1);
1099                 if (D.BUG) Log.d(TAG, "onReceive STREAM_DEVICES_CHANGED_ACTION stream="
1100                         + stream + " devices=" + devices + " oldDevices=" + oldDevices);
1101                 changed = checkRoutedToBluetoothW(stream);
1102                 changed |= onVolumeChangedW(stream, 0);
1103             } else if (action.equals(AudioManager.STREAM_MUTE_CHANGED_ACTION)) {
1104                 final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
1105                 final boolean muted = intent
1106                         .getBooleanExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, false);
1107                 if (D.BUG) Log.d(TAG, "onReceive STREAM_MUTE_CHANGED_ACTION stream=" + stream
1108                         + " muted=" + muted);
1109                 changed = updateStreamMuteW(stream, muted);
1110             } else if (action.equals(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED)) {
1111                 if (D.BUG) Log.d(TAG, "onReceive ACTION_EFFECTS_SUPPRESSOR_CHANGED");
1112                 changed = updateEffectsSuppressorW(mNoMan.getEffectsSuppressor());
1113             } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
1114                 if (D.BUG) Log.d(TAG, "onReceive ACTION_CONFIGURATION_CHANGED");
1115                 mCallbacks.onConfigurationChanged();
1116             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1117                 if (D.BUG) Log.d(TAG, "onReceive ACTION_SCREEN_OFF");
1118                 mCallbacks.onScreenOff();
1119             } else if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
1120                 if (D.BUG) Log.d(TAG, "onReceive ACTION_CLOSE_SYSTEM_DIALOGS");
1121                 dismiss();
1122             }
1123             if (changed) {
1124                 mCallbacks.onStateChanged(mState);
1125             }
1126         }
1127     }
1128 
1129     protected final class MediaSessionsCallbacks implements MediaSessions.Callbacks {
1130         private final HashMap<Token, Integer> mRemoteStreams = new HashMap<>();
1131 
1132         private int mNextStream = DYNAMIC_STREAM_START_INDEX;
1133         private final boolean mVolumeAdjustmentForRemoteGroupSessions;
1134 
MediaSessionsCallbacks(Context context)1135         public MediaSessionsCallbacks(Context context) {
1136             mVolumeAdjustmentForRemoteGroupSessions = context.getResources().getBoolean(
1137                     com.android.internal.R.bool.config_volumeAdjustmentForRemoteGroupSessions);
1138         }
1139 
1140         @Override
onRemoteUpdate(Token token, String name, PlaybackInfo pi)1141         public void onRemoteUpdate(Token token, String name, PlaybackInfo pi) {
1142             if (showForSession(token)) {
1143                 addStream(token, "onRemoteUpdate");
1144 
1145                 int stream = 0;
1146                 synchronized (mRemoteStreams) {
1147                     stream = mRemoteStreams.get(token);
1148                 }
1149                 Slog.d(TAG,
1150                         "onRemoteUpdate: stream: " + stream + " volume: " + pi.getCurrentVolume());
1151                 boolean changed = mState.states.indexOfKey(stream) < 0;
1152                 final StreamState ss = streamStateW(stream);
1153                 ss.dynamic = true;
1154                 ss.levelMin = 0;
1155                 ss.levelMax = pi.getMaxVolume();
1156                 if (ss.level != pi.getCurrentVolume()) {
1157                     ss.level = pi.getCurrentVolume();
1158                     changed = true;
1159                 }
1160                 if (!Objects.equals(ss.remoteLabel, name)) {
1161                     ss.name = -1;
1162                     ss.remoteLabel = name;
1163                     changed = true;
1164                 }
1165                 if (changed) {
1166                     Log.d(TAG, "onRemoteUpdate: " + name + ": " + ss.level + " of " + ss.levelMax);
1167                     mCallbacks.onStateChanged(mState);
1168                 }
1169             }
1170         }
1171 
1172         @Override
1173         public void onRemoteVolumeChanged(Token token, int flags) {
1174             if (showForSession(token)) {
1175                 addStream(token, "onRemoteVolumeChanged");
1176                 int stream = 0;
1177                 synchronized (mRemoteStreams) {
1178                     stream = mRemoteStreams.get(token);
1179                 }
1180                 final boolean showUI = shouldShowUI(flags);
1181                 Slog.d(TAG, "onRemoteVolumeChanged: stream: " + stream + " showui? " + showUI);
1182                 boolean changed = updateActiveStreamW(stream);
1183                 if (showUI) {
1184                     changed |= checkRoutedToBluetoothW(AudioManager.STREAM_MUSIC);
1185                 }
1186                 if (changed) {
1187                     Slog.d(TAG, "onRemoteChanged: updatingState");
1188                     mCallbacks.onStateChanged(mState);
1189                 }
1190                 if (showUI) {
1191                     onShowRequestedW(Events.SHOW_REASON_REMOTE_VOLUME_CHANGED);
1192                 }
1193             }
1194         }
1195 
1196         @Override
1197         public void onRemoteRemoved(Token token) {
1198             if (showForSession(token)) {
1199                 int stream = 0;
1200                 synchronized (mRemoteStreams) {
1201                     if (!mRemoteStreams.containsKey(token)) {
1202                         Log.d(TAG, "onRemoteRemoved: stream doesn't exist, "
1203                                 + "aborting remote removed for token:" + token.toString());
1204                         return;
1205                     }
1206                     stream = mRemoteStreams.get(token);
1207                 }
1208                 mState.states.remove(stream);
1209                 if (mState.activeStream == stream) {
1210                     updateActiveStreamW(-1);
1211                 }
1212                 mCallbacks.onStateChanged(mState);
1213             }
1214         }
1215 
1216         public void setStreamVolume(int stream, int level) {
1217             final Token token = findToken(stream);
1218             if (token == null) {
1219                 Log.w(TAG, "setStreamVolume: No token found for stream: " + stream);
1220                 return;
1221             }
1222             if (showForSession(token)) {
1223                 mMediaSessions.setVolume(token, level);
1224             }
1225         }
1226 
1227         private boolean showForSession(Token token) {
1228             if (mVolumeAdjustmentForRemoteGroupSessions) {
1229                 return true;
1230             }
1231             MediaController ctr = new MediaController(mContext, token);
1232             String packageName = ctr.getPackageName();
1233             List<RoutingSessionInfo> sessions =
1234                     mRouter2Manager.getRoutingSessions(packageName);
1235             boolean foundNonSystemSession = false;
1236             boolean isGroup = false;
1237             for (RoutingSessionInfo session : sessions) {
1238                 if (!session.isSystemSession()) {
1239                     foundNonSystemSession = true;
1240                     int selectedRouteCount = session.getSelectedRoutes().size();
1241                     if (selectedRouteCount > 1) {
1242                         isGroup = true;
1243                         break;
1244                     }
1245                 }
1246             }
1247             if (!foundNonSystemSession) {
1248                 Log.d(TAG, "No routing session for " + packageName);
1249                 return false;
1250             }
1251             return !isGroup;
1252         }
1253 
findToken(int stream)1254         private Token findToken(int stream) {
1255             synchronized (mRemoteStreams) {
1256                 for (Map.Entry<Token, Integer> entry : mRemoteStreams.entrySet()) {
1257                     if (entry.getValue().equals(stream)) {
1258                         return entry.getKey();
1259                     }
1260                 }
1261             }
1262             return null;
1263         }
1264 
addStream(Token token, String triggeringMethod)1265         private void addStream(Token token, String triggeringMethod) {
1266             synchronized (mRemoteStreams) {
1267                 if (!mRemoteStreams.containsKey(token)) {
1268                     mRemoteStreams.put(token, mNextStream);
1269                     Log.d(TAG, triggeringMethod + ": added stream " + mNextStream
1270                             + " from token + " + token.toString());
1271                     mNextStream++;
1272                 }
1273             }
1274         }
1275     }
1276 
1277     public interface UserActivityListener {
onUserActivity()1278         void onUserActivity();
1279     }
1280 }
1281