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 { STREAMS.put(AudioSystem.STREAM_ALARM, R.string.stream_alarm)79 STREAMS.put(AudioSystem.STREAM_ALARM, R.string.stream_alarm); STREAMS.put(AudioSystem.STREAM_BLUETOOTH_SCO, R.string.stream_bluetooth_sco)80 STREAMS.put(AudioSystem.STREAM_BLUETOOTH_SCO, R.string.stream_bluetooth_sco); STREAMS.put(AudioSystem.STREAM_DTMF, R.string.stream_dtmf)81 STREAMS.put(AudioSystem.STREAM_DTMF, R.string.stream_dtmf); STREAMS.put(AudioSystem.STREAM_MUSIC, R.string.stream_music)82 STREAMS.put(AudioSystem.STREAM_MUSIC, R.string.stream_music); STREAMS.put(AudioSystem.STREAM_ACCESSIBILITY, R.string.stream_accessibility)83 STREAMS.put(AudioSystem.STREAM_ACCESSIBILITY, R.string.stream_accessibility); STREAMS.put(AudioSystem.STREAM_NOTIFICATION, R.string.stream_notification)84 STREAMS.put(AudioSystem.STREAM_NOTIFICATION, R.string.stream_notification); STREAMS.put(AudioSystem.STREAM_RING, R.string.stream_ring)85 STREAMS.put(AudioSystem.STREAM_RING, R.string.stream_ring); STREAMS.put(AudioSystem.STREAM_SYSTEM, R.string.stream_system)86 STREAMS.put(AudioSystem.STREAM_SYSTEM, R.string.stream_system); STREAMS.put(AudioSystem.STREAM_SYSTEM_ENFORCED, R.string.stream_system_enforced)87 STREAMS.put(AudioSystem.STREAM_SYSTEM_ENFORCED, R.string.stream_system_enforced); STREAMS.put(AudioSystem.STREAM_TTS, R.string.stream_tts)88 STREAMS.put(AudioSystem.STREAM_TTS, R.string.stream_tts); STREAMS.put(AudioSystem.STREAM_VOICE_CALL, R.string.stream_voice_call)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 VolumeDialogControllerImpl(Context context)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 getAudioManager()140 public AudioManager getAudioManager() { 141 return mAudio; 142 } 143 dismiss()144 public void dismiss() { 145 mCallbacks.onDismissRequested(Events.DISMISS_REASON_VOLUME_CONTROLLER); 146 } 147 setVolumeController()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 setAudioManagerStreamVolume(int stream, int level, int flag)157 protected void setAudioManagerStreamVolume(int stream, int level, int flag) { 158 mAudio.setStreamVolume(stream, level, flag); 159 } 160 getAudioManagerStreamVolume(int stream)161 protected int getAudioManagerStreamVolume(int stream) { 162 return mAudio.getLastAudibleStreamVolume(stream); 163 } 164 getAudioManagerStreamMaxVolume(int stream)165 protected int getAudioManagerStreamMaxVolume(int stream) { 166 return mAudio.getStreamMaxVolume(stream); 167 } 168 getAudioManagerStreamMinVolume(int stream)169 protected int getAudioManagerStreamMinVolume(int stream) { 170 return mAudio.getStreamMinVolume(stream); 171 } 172 register()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 setVolumePolicy(VolumePolicy policy)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 createMediaSessions(Context context, Looper looper, MediaSessions.Callbacks callbacks)194 protected MediaSessions createMediaSessions(Context context, Looper looper, 195 MediaSessions.Callbacks callbacks) { 196 return new MediaSessions(context, looper, callbacks); 197 } 198 destroy()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 dump(FileDescriptor fd, PrintWriter pw, String[] args)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 addCallback(Callbacks callback, Handler handler)224 public void addCallback(Callbacks callback, Handler handler) { 225 mCallbacks.add(callback, handler); 226 callback.onAccessibilityModeChanged(mShowA11yStream); 227 } 228 setUserActivityListener(UserActivityListener listener)229 public void setUserActivityListener(UserActivityListener listener) { 230 if (mDestroyed) return; 231 synchronized (this) { 232 mUserActivityListener = listener; 233 } 234 } 235 removeCallback(Callbacks callback)236 public void removeCallback(Callbacks callback) { 237 mCallbacks.remove(callback); 238 } 239 getState()240 public void getState() { 241 if (mDestroyed) return; 242 mWorker.sendEmptyMessage(W.GET_STATE); 243 } 244 notifyVisible(boolean visible)245 public void notifyVisible(boolean visible) { 246 if (mDestroyed) return; 247 mWorker.obtainMessage(W.NOTIFY_VISIBLE, visible ? 1 : 0, 0).sendToTarget(); 248 } 249 userActivity()250 public void userActivity() { 251 if (mDestroyed) return; 252 mWorker.removeMessages(W.USER_ACTIVITY); 253 mWorker.sendEmptyMessage(W.USER_ACTIVITY); 254 } 255 setRingerMode(int value, boolean external)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 setZenMode(int value)261 public void setZenMode(int value) { 262 if (mDestroyed) return; 263 mWorker.obtainMessage(W.SET_ZEN_MODE, value, 0).sendToTarget(); 264 } 265 setExitCondition(Condition condition)266 public void setExitCondition(Condition condition) { 267 if (mDestroyed) return; 268 mWorker.obtainMessage(W.SET_EXIT_CONDITION, condition).sendToTarget(); 269 } 270 setStreamMute(int stream, boolean mute)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 setStreamVolume(int stream, int level)276 public void setStreamVolume(int stream, int level) { 277 if (mDestroyed) return; 278 mWorker.obtainMessage(W.SET_STREAM_VOLUME, stream, level).sendToTarget(); 279 } 280 setActiveStream(int stream)281 public void setActiveStream(int stream) { 282 if (mDestroyed) return; 283 mWorker.obtainMessage(W.SET_ACTIVE_STREAM, stream, 0).sendToTarget(); 284 } 285 vibrate()286 public void vibrate() { 287 if (mHasVibrator) { 288 mVibrator.vibrate(VIBRATE_HINT_DURATION); 289 } 290 } 291 hasVibrator()292 public boolean hasVibrator() { 293 return mHasVibrator; 294 } 295 onNotifyVisibleW(boolean visible)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 onUserActivityW()306 private void onUserActivityW() { 307 synchronized (this) { 308 if (mUserActivityListener != null) { 309 mUserActivityListener.onUserActivity(); 310 } 311 } 312 } 313 onShowSafetyWarningW(int flags)314 private void onShowSafetyWarningW(int flags) { 315 mCallbacks.onShowSafetyWarning(flags); 316 } 317 onAccessibilityModeChanged(Boolean showA11yStream)318 private void onAccessibilityModeChanged(Boolean showA11yStream) { 319 mCallbacks.onAccessibilityModeChanged(showA11yStream); 320 } 321 checkRoutedToBluetoothW(int stream)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 updateStatusBar()335 private void updateStatusBar() { 336 if (mStatusBar == null) { 337 mStatusBar = SysUiServiceProvider.getComponent(mContext, StatusBar.class); 338 } 339 } 340 shouldShowUI(int flags)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 onVolumeChangedW(int stream, int flags)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 updateActiveStreamW(int activeStream)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 updateZenModeW()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 updateRingerModeExternalW(int rm)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 updateRingerModeInternalW(int rm)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 onSetRingerModeW(int mode, boolean external)512 private void onSetRingerModeW(int mode, boolean external) { 513 if (external) { 514 mAudio.setRingerMode(mode); 515 } else { 516 mAudio.setRingerModeInternal(mode); 517 } 518 } 519 onSetStreamMuteW(int stream, boolean mute)520 private void onSetStreamMuteW(int stream, boolean mute) { 521 mAudio.adjustStreamVolume(stream, mute ? AudioManager.ADJUST_MUTE 522 : AudioManager.ADJUST_UNMUTE, 0); 523 } 524 onSetStreamVolumeW(int stream, int level)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 onSetActiveStreamW(int stream)534 private void onSetActiveStreamW(int stream) { 535 boolean changed = updateActiveStreamW(stream); 536 if (changed) { 537 mCallbacks.onStateChanged(mState); 538 } 539 } 540 onSetExitConditionW(Condition condition)541 private void onSetExitConditionW(Condition condition) { 542 mNoMan.setZenMode(mState.zenMode, condition != null ? condition.id : null, TAG); 543 } 544 onSetZenModeW(int mode)545 private void onSetZenModeW(int mode) { 546 if (D.BUG) Log.d(TAG, "onSetZenModeW " + mode); 547 mNoMan.setZenMode(mode, null, TAG); 548 } 549 onDismissRequestedW(int reason)550 private void onDismissRequestedW(int reason) { 551 mCallbacks.onDismissRequested(reason); 552 } 553 showDndTile(boolean visible)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 displaySafeVolumeWarning(int flags)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 volumeChanged(int streamType, int flags)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 masterMuteChanged(int flags)579 public void masterMuteChanged(int flags) throws RemoteException { 580 if (D.BUG) Log.d(TAG, "masterMuteChanged"); 581 } 582 583 @Override setLayoutDirection(int layoutDirection)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 dismiss()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 setA11yMode(int mode)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 W(Looper looper)636 W(Looper looper) { 637 super(looper); 638 } 639 640 @Override handleMessage(Message msg)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 add(Callbacks callback, Handler handler)665 public void add(Callbacks callback, Handler handler) { 666 if (callback == null || handler == null) throw new IllegalArgumentException(); 667 mCallbackMap.put(callback, handler); 668 } 669 remove(Callbacks callback)670 public void remove(Callbacks callback) { 671 mCallbackMap.remove(callback); 672 } 673 674 @Override onShowRequested(final int reason)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 onDismissRequested(final int reason)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 onStateChanged(final State state)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 onLayoutDirectionChanged(final int layoutDirection)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 onConfigurationChanged()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 onShowVibrateHint()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 onShowSilentHint()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 onScreenOff()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 onShowSafetyWarning(final int flags)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 onAccessibilityModeChanged(Boolean showA11yStream)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 SettingObserver(Handler handler)806 public SettingObserver(Handler handler) { 807 super(handler); 808 } 809 init()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 destroy()815 public void destroy() { 816 mContext.getContentResolver().unregisterContentObserver(this); 817 } 818 819 @Override onChange(boolean selfChange, Uri uri)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 init()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 destroy()847 public void destroy() { 848 mContext.unregisterReceiver(this); 849 } 850 851 @Override onReceive(Context context, Intent intent)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 onRemoteUpdate(Token token, String name, PlaybackInfo pi)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