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