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