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