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