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