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