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