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