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