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