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