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