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