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