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