blob: 2e23920a07989f60072fa1024483503e8d974f1e [file] [log] [blame]
John Spurlockf88d8082015-03-25 18:09:51 -04001/*
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
17package com.android.systemui.volume;
18
19import android.app.NotificationManager;
20import android.content.BroadcastReceiver;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.content.pm.ApplicationInfo;
26import android.content.pm.PackageManager;
27import android.content.pm.PackageManager.NameNotFoundException;
28import android.database.ContentObserver;
Julia Reynolds6692b472018-01-24 07:23:56 -050029import android.media.AudioDeviceCallback;
30import android.media.AudioDeviceInfo;
John Spurlockf88d8082015-03-25 18:09:51 -040031import android.media.AudioManager;
32import android.media.AudioSystem;
33import android.media.IVolumeController;
34import android.media.VolumePolicy;
35import android.media.session.MediaController.PlaybackInfo;
36import android.media.session.MediaSession.Token;
37import android.net.Uri;
38import android.os.Handler;
39import android.os.HandlerThread;
40import android.os.Looper;
41import android.os.Message;
42import android.os.RemoteException;
43import android.os.Vibrator;
44import android.provider.Settings;
45import android.service.notification.Condition;
Julia Reynolds03c548f2016-12-14 15:02:38 -050046import android.util.ArrayMap;
John Spurlockf88d8082015-03-25 18:09:51 -040047import android.util.Log;
Jason Monkae305972017-06-27 14:46:03 -040048import android.view.accessibility.AccessibilityManager;
John Spurlockf88d8082015-03-25 18:09:51 -040049
Yao Chen634acb92016-04-13 16:17:47 -070050import com.android.internal.annotations.GuardedBy;
Jason Monk782cd672017-03-22 12:50:57 -040051import com.android.systemui.Dumpable;
John Spurlockf88d8082015-03-25 18:09:51 -040052import com.android.systemui.R;
Beverly8ebef842017-07-12 10:58:22 -040053import com.android.systemui.SysUiServiceProvider;
Beverly85e05eb2017-07-21 14:49:27 -040054import com.android.systemui.keyguard.WakefulnessLifecycle;
Jason Monk782cd672017-03-22 12:50:57 -040055import com.android.systemui.plugins.VolumeDialogController;
John Spurlockcd863ad2015-04-07 14:01:28 -040056import com.android.systemui.qs.tiles.DndTile;
Beverly8ebef842017-07-12 10:58:22 -040057import com.android.systemui.statusbar.phone.StatusBar;
John Spurlockf88d8082015-03-25 18:09:51 -040058
59import java.io.FileDescriptor;
60import java.io.PrintWriter;
61import java.util.HashMap;
62import java.util.Map;
63import java.util.Objects;
64
65/**
66 * Source of truth for all state / events related to the volume dialog. No presentation.
67 *
68 * All work done on a dedicated background worker thread & associated worker.
69 *
70 * Methods ending in "W" must be called on the worker thread.
71 */
Jason Monk782cd672017-03-22 12:50:57 -040072public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpable {
73 private static final String TAG = Util.logTag(VolumeDialogControllerImpl.class);
John Spurlockf88d8082015-03-25 18:09:51 -040074
75 private static final int DYNAMIC_STREAM_START_INDEX = 100;
76 private static final int VIBRATE_HINT_DURATION = 50;
77
Julia Reynolds03c548f2016-12-14 15:02:38 -050078 private static final ArrayMap<Integer, Integer> STREAMS = new ArrayMap<>();
79 static {
80 STREAMS.put(AudioSystem.STREAM_ALARM, R.string.stream_alarm);
81 STREAMS.put(AudioSystem.STREAM_BLUETOOTH_SCO, R.string.stream_bluetooth_sco);
82 STREAMS.put(AudioSystem.STREAM_DTMF, R.string.stream_dtmf);
83 STREAMS.put(AudioSystem.STREAM_MUSIC, R.string.stream_music);
Julia Reynoldsdbfb40f2017-01-23 11:05:57 -050084 STREAMS.put(AudioSystem.STREAM_ACCESSIBILITY, R.string.stream_accessibility);
Julia Reynolds03c548f2016-12-14 15:02:38 -050085 STREAMS.put(AudioSystem.STREAM_NOTIFICATION, R.string.stream_notification);
86 STREAMS.put(AudioSystem.STREAM_RING, R.string.stream_ring);
87 STREAMS.put(AudioSystem.STREAM_SYSTEM, R.string.stream_system);
88 STREAMS.put(AudioSystem.STREAM_SYSTEM_ENFORCED, R.string.stream_system_enforced);
89 STREAMS.put(AudioSystem.STREAM_TTS, R.string.stream_tts);
90 STREAMS.put(AudioSystem.STREAM_VOICE_CALL, R.string.stream_voice_call);
Julia Reynolds03c548f2016-12-14 15:02:38 -050091 }
John Spurlockf88d8082015-03-25 18:09:51 -040092
93 private final HandlerThread mWorkerThread;
94 private final W mWorker;
95 private final Context mContext;
Yao Chen634acb92016-04-13 16:17:47 -070096 private AudioManager mAudio;
Beverly8ebef842017-07-12 10:58:22 -040097 protected StatusBar mStatusBar;
John Spurlockf88d8082015-03-25 18:09:51 -040098 private final NotificationManager mNoMan;
John Spurlockf88d8082015-03-25 18:09:51 -040099 private final SettingObserver mObserver;
100 private final Receiver mReceiver = new Receiver();
101 private final MediaSessions mMediaSessions;
Beverly8ebef842017-07-12 10:58:22 -0400102 protected C mCallbacks = new C();
John Spurlockf88d8082015-03-25 18:09:51 -0400103 private final State mState = new State();
Beverly4e936612017-07-28 14:05:30 -0400104 protected final MediaSessionsCallbacks mMediaSessionsCallbacksW = new MediaSessionsCallbacks();
John Spurlockf88d8082015-03-25 18:09:51 -0400105 private final Vibrator mVibrator;
106 private final boolean mHasVibrator;
Julia Reynoldsdbfb40f2017-01-23 11:05:57 -0500107 private boolean mShowA11yStream;
Luke Songe0036662017-12-12 12:27:08 -0800108 private boolean mShowVolumeDialog;
109 private boolean mShowSafetyWarning;
Julia Reynolds6692b472018-01-24 07:23:56 -0500110 private DeviceCallback mDeviceCallback = new DeviceCallback();
111 private AudioDeviceInfo mConnectedDevice;
John Spurlockf88d8082015-03-25 18:09:51 -0400112
John Spurlockf88d8082015-03-25 18:09:51 -0400113 private boolean mDestroyed;
John Spurlockb02c7442015-04-14 09:32:25 -0400114 private VolumePolicy mVolumePolicy;
John Spurlockd9c75db2015-04-28 11:19:13 -0400115 private boolean mShowDndTile = true;
Yao Chen634acb92016-04-13 16:17:47 -0700116 @GuardedBy("this")
117 private UserActivityListener mUserActivityListener;
118
119 protected final VC mVolumeController = new VC();
John Spurlockf88d8082015-03-25 18:09:51 -0400120
Jason Monk782cd672017-03-22 12:50:57 -0400121 public VolumeDialogControllerImpl(Context context) {
John Spurlockf88d8082015-03-25 18:09:51 -0400122 mContext = context.getApplicationContext();
Chris Wrene565ee62015-06-17 15:24:56 -0400123 Events.writeEvent(mContext, Events.EVENT_COLLECTION_STARTED);
Jason Monk782cd672017-03-22 12:50:57 -0400124 mWorkerThread = new HandlerThread(VolumeDialogControllerImpl.class.getSimpleName());
John Spurlockf88d8082015-03-25 18:09:51 -0400125 mWorkerThread.start();
126 mWorker = new W(mWorkerThread.getLooper());
127 mMediaSessions = createMediaSessions(mContext, mWorkerThread.getLooper(),
128 mMediaSessionsCallbacksW);
129 mAudio = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
130 mNoMan = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
131 mObserver = new SettingObserver(mWorker);
132 mObserver.init();
133 mReceiver.init();
John Spurlockf88d8082015-03-25 18:09:51 -0400134 mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
135 mHasVibrator = mVibrator != null && mVibrator.hasVibrator();
Beverly8ebef842017-07-12 10:58:22 -0400136 updateStatusBar();
Jason Monkae305972017-06-27 14:46:03 -0400137
138 boolean accessibilityVolumeStreamActive = context.getSystemService(
139 AccessibilityManager.class).isAccessibilityVolumeStreamActive();
140 mVolumeController.setA11yMode(accessibilityVolumeStreamActive ?
141 VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME :
142 VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME);
John Spurlockf88d8082015-03-25 18:09:51 -0400143 }
144
John Spurlock76b52b32015-04-03 00:00:12 -0400145 public AudioManager getAudioManager() {
146 return mAudio;
147 }
148
John Spurlockf88d8082015-03-25 18:09:51 -0400149 public void dismiss() {
150 mCallbacks.onDismissRequested(Events.DISMISS_REASON_VOLUME_CONTROLLER);
151 }
152
Yao Chen634acb92016-04-13 16:17:47 -0700153 protected void setVolumeController() {
John Spurlockf88d8082015-03-25 18:09:51 -0400154 try {
155 mAudio.setVolumeController(mVolumeController);
156 } catch (SecurityException e) {
157 Log.w(TAG, "Unable to set the volume controller", e);
158 return;
159 }
Yao Chen634acb92016-04-13 16:17:47 -0700160 }
161
162 protected void setAudioManagerStreamVolume(int stream, int level, int flag) {
163 mAudio.setStreamVolume(stream, level, flag);
164 }
165
166 protected int getAudioManagerStreamVolume(int stream) {
167 return mAudio.getLastAudibleStreamVolume(stream);
168 }
169
170 protected int getAudioManagerStreamMaxVolume(int stream) {
171 return mAudio.getStreamMaxVolume(stream);
172 }
173
174 protected int getAudioManagerStreamMinVolume(int stream) {
Jean-Michel Trivi8e7aca32017-11-28 18:12:38 -0800175 return mAudio.getStreamMinVolumeInt(stream);
Yao Chen634acb92016-04-13 16:17:47 -0700176 }
177
178 public void register() {
179 setVolumeController();
John Spurlockf88d8082015-03-25 18:09:51 -0400180 setVolumePolicy(mVolumePolicy);
181 showDndTile(mShowDndTile);
182 try {
183 mMediaSessions.init();
184 } catch (SecurityException e) {
185 Log.w(TAG, "No access to media sessions", e);
186 }
Julia Reynolds6692b472018-01-24 07:23:56 -0500187 mAudio.registerAudioDeviceCallback(mDeviceCallback, mWorker);
John Spurlockf88d8082015-03-25 18:09:51 -0400188 }
189
190 public void setVolumePolicy(VolumePolicy policy) {
191 mVolumePolicy = policy;
John Spurlockb02c7442015-04-14 09:32:25 -0400192 if (mVolumePolicy == null) return;
John Spurlockf88d8082015-03-25 18:09:51 -0400193 try {
194 mAudio.setVolumePolicy(mVolumePolicy);
195 } catch (NoSuchMethodError e) {
196 Log.w(TAG, "No volume policy api");
197 }
198 }
199
200 protected MediaSessions createMediaSessions(Context context, Looper looper,
201 MediaSessions.Callbacks callbacks) {
202 return new MediaSessions(context, looper, callbacks);
203 }
204
205 public void destroy() {
206 if (D.BUG) Log.d(TAG, "destroy");
207 if (mDestroyed) return;
208 mDestroyed = true;
Chris Wrene565ee62015-06-17 15:24:56 -0400209 Events.writeEvent(mContext, Events.EVENT_COLLECTION_STOPPED);
John Spurlockf88d8082015-03-25 18:09:51 -0400210 mMediaSessions.destroy();
211 mObserver.destroy();
212 mReceiver.destroy();
Julia Reynolds6692b472018-01-24 07:23:56 -0500213 mAudio.unregisterAudioDeviceCallback(mDeviceCallback);
John Spurlockf88d8082015-03-25 18:09:51 -0400214 mWorkerThread.quitSafely();
215 }
216
217 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jason Monk782cd672017-03-22 12:50:57 -0400218 pw.println(VolumeDialogControllerImpl.class.getSimpleName() + " state:");
John Spurlockf88d8082015-03-25 18:09:51 -0400219 pw.print(" mDestroyed: "); pw.println(mDestroyed);
220 pw.print(" mVolumePolicy: "); pw.println(mVolumePolicy);
John Spurlocke56efa712015-05-19 12:26:25 -0400221 pw.print(" mState: "); pw.println(mState.toString(4));
John Spurlockf88d8082015-03-25 18:09:51 -0400222 pw.print(" mShowDndTile: "); pw.println(mShowDndTile);
223 pw.print(" mHasVibrator: "); pw.println(mHasVibrator);
224 pw.print(" mRemoteStreams: "); pw.println(mMediaSessionsCallbacksW.mRemoteStreams
225 .values());
Julia Reynoldsdbfb40f2017-01-23 11:05:57 -0500226 pw.print(" mShowA11yStream: "); pw.println(mShowA11yStream);
John Spurlockf88d8082015-03-25 18:09:51 -0400227 pw.println();
228 mMediaSessions.dump(pw);
229 }
230
231 public void addCallback(Callbacks callback, Handler handler) {
232 mCallbacks.add(callback, handler);
Jason Monkae305972017-06-27 14:46:03 -0400233 callback.onAccessibilityModeChanged(mShowA11yStream);
John Spurlockf88d8082015-03-25 18:09:51 -0400234 }
235
Yao Chen634acb92016-04-13 16:17:47 -0700236 public void setUserActivityListener(UserActivityListener listener) {
237 if (mDestroyed) return;
238 synchronized (this) {
239 mUserActivityListener = listener;
240 }
241 }
242
John Spurlockf88d8082015-03-25 18:09:51 -0400243 public void removeCallback(Callbacks callback) {
244 mCallbacks.remove(callback);
245 }
246
247 public void getState() {
248 if (mDestroyed) return;
249 mWorker.sendEmptyMessage(W.GET_STATE);
250 }
251
252 public void notifyVisible(boolean visible) {
253 if (mDestroyed) return;
254 mWorker.obtainMessage(W.NOTIFY_VISIBLE, visible ? 1 : 0, 0).sendToTarget();
255 }
256
257 public void userActivity() {
258 if (mDestroyed) return;
259 mWorker.removeMessages(W.USER_ACTIVITY);
260 mWorker.sendEmptyMessage(W.USER_ACTIVITY);
261 }
262
263 public void setRingerMode(int value, boolean external) {
264 if (mDestroyed) return;
265 mWorker.obtainMessage(W.SET_RINGER_MODE, value, external ? 1 : 0).sendToTarget();
266 }
267
268 public void setZenMode(int value) {
269 if (mDestroyed) return;
270 mWorker.obtainMessage(W.SET_ZEN_MODE, value, 0).sendToTarget();
271 }
272
273 public void setExitCondition(Condition condition) {
274 if (mDestroyed) return;
275 mWorker.obtainMessage(W.SET_EXIT_CONDITION, condition).sendToTarget();
276 }
277
278 public void setStreamMute(int stream, boolean mute) {
279 if (mDestroyed) return;
280 mWorker.obtainMessage(W.SET_STREAM_MUTE, stream, mute ? 1 : 0).sendToTarget();
281 }
282
283 public void setStreamVolume(int stream, int level) {
284 if (mDestroyed) return;
285 mWorker.obtainMessage(W.SET_STREAM_VOLUME, stream, level).sendToTarget();
286 }
287
288 public void setActiveStream(int stream) {
289 if (mDestroyed) return;
290 mWorker.obtainMessage(W.SET_ACTIVE_STREAM, stream, 0).sendToTarget();
291 }
292
Luke Songe0036662017-12-12 12:27:08 -0800293 public void setEnableDialogs(boolean volumeUi, boolean safetyWarning) {
294 mShowVolumeDialog = volumeUi;
295 mShowSafetyWarning = safetyWarning;
296 }
297
John Spurlockf88d8082015-03-25 18:09:51 -0400298 public void vibrate() {
299 if (mHasVibrator) {
300 mVibrator.vibrate(VIBRATE_HINT_DURATION);
301 }
302 }
303
304 public boolean hasVibrator() {
305 return mHasVibrator;
306 }
307
308 private void onNotifyVisibleW(boolean visible) {
309 if (mDestroyed) return;
310 mAudio.notifyVolumeControllerVisible(mVolumeController, visible);
311 if (!visible) {
312 if (updateActiveStreamW(-1)) {
313 mCallbacks.onStateChanged(mState);
314 }
315 }
316 }
317
Yao Chen634acb92016-04-13 16:17:47 -0700318 private void onUserActivityW() {
319 synchronized (this) {
320 if (mUserActivityListener != null) {
321 mUserActivityListener.onUserActivity();
322 }
323 }
John Spurlockf88d8082015-03-25 18:09:51 -0400324 }
325
John Spurlock76b52b32015-04-03 00:00:12 -0400326 private void onShowSafetyWarningW(int flags) {
Luke Songe0036662017-12-12 12:27:08 -0800327 if (mShowSafetyWarning) {
328 mCallbacks.onShowSafetyWarning(flags);
329 }
John Spurlock76b52b32015-04-03 00:00:12 -0400330 }
331
Julia Reynoldsdbfb40f2017-01-23 11:05:57 -0500332 private void onAccessibilityModeChanged(Boolean showA11yStream) {
333 mCallbacks.onAccessibilityModeChanged(showA11yStream);
334 }
335
John Spurlockf88d8082015-03-25 18:09:51 -0400336 private boolean checkRoutedToBluetoothW(int stream) {
337 boolean changed = false;
338 if (stream == AudioManager.STREAM_MUSIC) {
339 final boolean routedToBluetooth =
340 (mAudio.getDevicesForStream(AudioManager.STREAM_MUSIC) &
341 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
342 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
343 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0;
344 changed |= updateStreamRoutedToBluetoothW(stream, routedToBluetooth);
345 }
346 return changed;
347 }
348
Beverly8ebef842017-07-12 10:58:22 -0400349 private void updateStatusBar() {
350 if (mStatusBar == null) {
351 mStatusBar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
352 }
353 }
354
Beverly85e05eb2017-07-21 14:49:27 -0400355 private boolean shouldShowUI(int flags) {
Beverly8ebef842017-07-12 10:58:22 -0400356 updateStatusBar();
Beverly85e05eb2017-07-21 14:49:27 -0400357 return mStatusBar != null
358 && mStatusBar.getWakefulnessState() != WakefulnessLifecycle.WAKEFULNESS_ASLEEP
359 && mStatusBar.getWakefulnessState() != WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP
360 && mStatusBar.isDeviceInteractive()
Luke Songe0036662017-12-12 12:27:08 -0800361 && (flags & AudioManager.FLAG_SHOW_UI) != 0
362 && mShowVolumeDialog;
Beverly85e05eb2017-07-21 14:49:27 -0400363 }
Beverly8ebef842017-07-12 10:58:22 -0400364
Beverly85e05eb2017-07-21 14:49:27 -0400365 boolean onVolumeChangedW(int stream, int flags) {
366 final boolean showUI = shouldShowUI(flags);
John Spurlockf88d8082015-03-25 18:09:51 -0400367 final boolean fromKey = (flags & AudioManager.FLAG_FROM_KEY) != 0;
368 final boolean showVibrateHint = (flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0;
369 final boolean showSilentHint = (flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0;
370 boolean changed = false;
371 if (showUI) {
372 changed |= updateActiveStreamW(stream);
373 }
Yao Chen634acb92016-04-13 16:17:47 -0700374 int lastAudibleStreamVolume = getAudioManagerStreamVolume(stream);
Chris Wrene565ee62015-06-17 15:24:56 -0400375 changed |= updateStreamLevelW(stream, lastAudibleStreamVolume);
John Spurlockf88d8082015-03-25 18:09:51 -0400376 changed |= checkRoutedToBluetoothW(showUI ? AudioManager.STREAM_MUSIC : stream);
377 if (changed) {
378 mCallbacks.onStateChanged(mState);
379 }
380 if (showUI) {
381 mCallbacks.onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
382 }
383 if (showVibrateHint) {
384 mCallbacks.onShowVibrateHint();
385 }
386 if (showSilentHint) {
387 mCallbacks.onShowSilentHint();
388 }
389 if (changed && fromKey) {
Chris Wrene565ee62015-06-17 15:24:56 -0400390 Events.writeEvent(mContext, Events.EVENT_KEY, stream, lastAudibleStreamVolume);
John Spurlockf88d8082015-03-25 18:09:51 -0400391 }
Julia Reynolds8ae994f2015-09-14 11:12:42 -0400392 return changed;
John Spurlockf88d8082015-03-25 18:09:51 -0400393 }
394
395 private boolean updateActiveStreamW(int activeStream) {
396 if (activeStream == mState.activeStream) return false;
397 mState.activeStream = activeStream;
Chris Wrene565ee62015-06-17 15:24:56 -0400398 Events.writeEvent(mContext, Events.EVENT_ACTIVE_STREAM_CHANGED, activeStream);
John Spurlockf88d8082015-03-25 18:09:51 -0400399 if (D.BUG) Log.d(TAG, "updateActiveStreamW " + activeStream);
400 final int s = activeStream < DYNAMIC_STREAM_START_INDEX ? activeStream : -1;
401 if (D.BUG) Log.d(TAG, "forceVolumeControlStream " + s);
402 mAudio.forceVolumeControlStream(s);
403 return true;
404 }
405
406 private StreamState streamStateW(int stream) {
407 StreamState ss = mState.states.get(stream);
408 if (ss == null) {
409 ss = new StreamState();
410 mState.states.put(stream, ss);
411 }
412 return ss;
413 }
414
415 private void onGetStateW() {
Julia Reynolds03c548f2016-12-14 15:02:38 -0500416 for (int stream : STREAMS.keySet()) {
Yao Chen634acb92016-04-13 16:17:47 -0700417 updateStreamLevelW(stream, getAudioManagerStreamVolume(stream));
418 streamStateW(stream).levelMin = getAudioManagerStreamMinVolume(stream);
419 streamStateW(stream).levelMax = getAudioManagerStreamMaxVolume(stream);
John Spurlockf88d8082015-03-25 18:09:51 -0400420 updateStreamMuteW(stream, mAudio.isStreamMute(stream));
421 final StreamState ss = streamStateW(stream);
422 ss.muteSupported = mAudio.isStreamAffectedByMute(stream);
Julia Reynolds03c548f2016-12-14 15:02:38 -0500423 ss.name = STREAMS.get(stream);
John Spurlockf88d8082015-03-25 18:09:51 -0400424 checkRoutedToBluetoothW(stream);
425 }
426 updateRingerModeExternalW(mAudio.getRingerMode());
427 updateZenModeW();
428 updateEffectsSuppressorW(mNoMan.getEffectsSuppressor());
John Spurlockf88d8082015-03-25 18:09:51 -0400429 mCallbacks.onStateChanged(mState);
430 }
431
432 private boolean updateStreamRoutedToBluetoothW(int stream, boolean routedToBluetooth) {
433 final StreamState ss = streamStateW(stream);
434 if (ss.routedToBluetooth == routedToBluetooth) return false;
435 ss.routedToBluetooth = routedToBluetooth;
436 if (D.BUG) Log.d(TAG, "updateStreamRoutedToBluetoothW stream=" + stream
437 + " routedToBluetooth=" + routedToBluetooth);
438 return true;
439 }
440
441 private boolean updateStreamLevelW(int stream, int level) {
442 final StreamState ss = streamStateW(stream);
443 if (ss.level == level) return false;
444 ss.level = level;
445 if (isLogWorthy(stream)) {
Chris Wrene565ee62015-06-17 15:24:56 -0400446 Events.writeEvent(mContext, Events.EVENT_LEVEL_CHANGED, stream, level);
John Spurlockf88d8082015-03-25 18:09:51 -0400447 }
448 return true;
449 }
450
451 private static boolean isLogWorthy(int stream) {
452 switch (stream) {
453 case AudioSystem.STREAM_ALARM:
454 case AudioSystem.STREAM_BLUETOOTH_SCO:
455 case AudioSystem.STREAM_MUSIC:
456 case AudioSystem.STREAM_RING:
457 case AudioSystem.STREAM_SYSTEM:
458 case AudioSystem.STREAM_VOICE_CALL:
459 return true;
460 }
461 return false;
462 }
463
464 private boolean updateStreamMuteW(int stream, boolean muted) {
465 final StreamState ss = streamStateW(stream);
466 if (ss.muted == muted) return false;
467 ss.muted = muted;
468 if (isLogWorthy(stream)) {
Chris Wrene565ee62015-06-17 15:24:56 -0400469 Events.writeEvent(mContext, Events.EVENT_MUTE_CHANGED, stream, muted);
John Spurlockf88d8082015-03-25 18:09:51 -0400470 }
471 if (muted && isRinger(stream)) {
472 updateRingerModeInternalW(mAudio.getRingerModeInternal());
473 }
474 return true;
475 }
476
477 private static boolean isRinger(int stream) {
478 return stream == AudioManager.STREAM_RING || stream == AudioManager.STREAM_NOTIFICATION;
479 }
480
John Spurlockf88d8082015-03-25 18:09:51 -0400481 private boolean updateEffectsSuppressorW(ComponentName effectsSuppressor) {
482 if (Objects.equals(mState.effectsSuppressor, effectsSuppressor)) return false;
483 mState.effectsSuppressor = effectsSuppressor;
484 mState.effectsSuppressorName = getApplicationName(mContext, mState.effectsSuppressor);
Chris Wrene565ee62015-06-17 15:24:56 -0400485 Events.writeEvent(mContext, Events.EVENT_SUPPRESSOR_CHANGED, mState.effectsSuppressor,
John Spurlockf88d8082015-03-25 18:09:51 -0400486 mState.effectsSuppressorName);
487 return true;
488 }
489
490 private static String getApplicationName(Context context, ComponentName component) {
491 if (component == null) return null;
492 final PackageManager pm = context.getPackageManager();
493 final String pkg = component.getPackageName();
494 try {
495 final ApplicationInfo ai = pm.getApplicationInfo(pkg, 0);
496 final String rt = Objects.toString(ai.loadLabel(pm), "").trim();
497 if (rt.length() > 0) {
498 return rt;
499 }
500 } catch (NameNotFoundException e) {}
501 return pkg;
502 }
503
504 private boolean updateZenModeW() {
505 final int zen = Settings.Global.getInt(mContext.getContentResolver(),
506 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
507 if (mState.zenMode == zen) return false;
508 mState.zenMode = zen;
Chris Wrene565ee62015-06-17 15:24:56 -0400509 Events.writeEvent(mContext, Events.EVENT_ZEN_MODE_CHANGED, zen);
John Spurlockf88d8082015-03-25 18:09:51 -0400510 return true;
511 }
512
513 private boolean updateRingerModeExternalW(int rm) {
514 if (rm == mState.ringerModeExternal) return false;
515 mState.ringerModeExternal = rm;
Chris Wrene565ee62015-06-17 15:24:56 -0400516 Events.writeEvent(mContext, Events.EVENT_EXTERNAL_RINGER_MODE_CHANGED, rm);
John Spurlockf88d8082015-03-25 18:09:51 -0400517 return true;
518 }
519
520 private boolean updateRingerModeInternalW(int rm) {
521 if (rm == mState.ringerModeInternal) return false;
522 mState.ringerModeInternal = rm;
Chris Wrene565ee62015-06-17 15:24:56 -0400523 Events.writeEvent(mContext, Events.EVENT_INTERNAL_RINGER_MODE_CHANGED, rm);
John Spurlockf88d8082015-03-25 18:09:51 -0400524 return true;
525 }
526
527 private void onSetRingerModeW(int mode, boolean external) {
528 if (external) {
529 mAudio.setRingerMode(mode);
530 } else {
531 mAudio.setRingerModeInternal(mode);
532 }
533 }
534
535 private void onSetStreamMuteW(int stream, boolean mute) {
536 mAudio.adjustStreamVolume(stream, mute ? AudioManager.ADJUST_MUTE
537 : AudioManager.ADJUST_UNMUTE, 0);
538 }
539
540 private void onSetStreamVolumeW(int stream, int level) {
541 if (D.BUG) Log.d(TAG, "onSetStreamVolume " + stream + " level=" + level);
542 if (stream >= DYNAMIC_STREAM_START_INDEX) {
543 mMediaSessionsCallbacksW.setStreamVolume(stream, level);
544 return;
545 }
Yao Chen634acb92016-04-13 16:17:47 -0700546 setAudioManagerStreamVolume(stream, level, 0);
John Spurlockf88d8082015-03-25 18:09:51 -0400547 }
548
549 private void onSetActiveStreamW(int stream) {
550 boolean changed = updateActiveStreamW(stream);
551 if (changed) {
552 mCallbacks.onStateChanged(mState);
553 }
554 }
555
556 private void onSetExitConditionW(Condition condition) {
John Spurlockb2278d62015-04-07 12:47:12 -0400557 mNoMan.setZenMode(mState.zenMode, condition != null ? condition.id : null, TAG);
John Spurlockf88d8082015-03-25 18:09:51 -0400558 }
559
560 private void onSetZenModeW(int mode) {
561 if (D.BUG) Log.d(TAG, "onSetZenModeW " + mode);
John Spurlockb2278d62015-04-07 12:47:12 -0400562 mNoMan.setZenMode(mode, null, TAG);
John Spurlockf88d8082015-03-25 18:09:51 -0400563 }
564
565 private void onDismissRequestedW(int reason) {
566 mCallbacks.onDismissRequested(reason);
567 }
568
569 public void showDndTile(boolean visible) {
570 if (D.BUG) Log.d(TAG, "showDndTile");
John Spurlockcd863ad2015-04-07 14:01:28 -0400571 DndTile.setVisible(mContext, visible);
John Spurlockf88d8082015-03-25 18:09:51 -0400572 }
573
574 private final class VC extends IVolumeController.Stub {
Jason Monk782cd672017-03-22 12:50:57 -0400575 private final String TAG = VolumeDialogControllerImpl.TAG + ".VC";
John Spurlockf88d8082015-03-25 18:09:51 -0400576
577 @Override
578 public void displaySafeVolumeWarning(int flags) throws RemoteException {
John Spurlock76b52b32015-04-03 00:00:12 -0400579 if (D.BUG) Log.d(TAG, "displaySafeVolumeWarning "
580 + Util.audioManagerFlagsToString(flags));
581 if (mDestroyed) return;
582 mWorker.obtainMessage(W.SHOW_SAFETY_WARNING, flags, 0).sendToTarget();
John Spurlockf88d8082015-03-25 18:09:51 -0400583 }
584
585 @Override
586 public void volumeChanged(int streamType, int flags) throws RemoteException {
587 if (D.BUG) Log.d(TAG, "volumeChanged " + AudioSystem.streamToString(streamType)
588 + " " + Util.audioManagerFlagsToString(flags));
589 if (mDestroyed) return;
590 mWorker.obtainMessage(W.VOLUME_CHANGED, streamType, flags).sendToTarget();
591 }
592
593 @Override
594 public void masterMuteChanged(int flags) throws RemoteException {
595 if (D.BUG) Log.d(TAG, "masterMuteChanged");
596 }
597
598 @Override
599 public void setLayoutDirection(int layoutDirection) throws RemoteException {
600 if (D.BUG) Log.d(TAG, "setLayoutDirection");
601 if (mDestroyed) return;
602 mWorker.obtainMessage(W.LAYOUT_DIRECTION_CHANGED, layoutDirection, 0).sendToTarget();
603 }
604
605 @Override
606 public void dismiss() throws RemoteException {
607 if (D.BUG) Log.d(TAG, "dismiss requested");
608 if (mDestroyed) return;
609 mWorker.obtainMessage(W.DISMISS_REQUESTED, Events.DISMISS_REASON_VOLUME_CONTROLLER, 0)
610 .sendToTarget();
611 mWorker.sendEmptyMessage(W.DISMISS_REQUESTED);
612 }
Jean-Michel Triviac487672016-11-11 10:05:18 -0800613
614 @Override
615 public void setA11yMode(int mode) {
616 if (D.BUG) Log.d(TAG, "setA11yMode to " + mode);
617 if (mDestroyed) return;
618 switch (mode) {
619 case VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME:
620 // "legacy" mode
Julia Reynoldsdbfb40f2017-01-23 11:05:57 -0500621 mShowA11yStream = false;
Jean-Michel Triviac487672016-11-11 10:05:18 -0800622 break;
623 case VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME:
Julia Reynoldsdbfb40f2017-01-23 11:05:57 -0500624 mShowA11yStream = true;
Jean-Michel Triviac487672016-11-11 10:05:18 -0800625 break;
626 default:
627 Log.e(TAG, "Invalid accessibility mode " + mode);
628 break;
629 }
Julia Reynoldsdbfb40f2017-01-23 11:05:57 -0500630 mWorker.obtainMessage(W.ACCESSIBILITY_MODE_CHANGED, mShowA11yStream).sendToTarget();
Jean-Michel Triviac487672016-11-11 10:05:18 -0800631 }
John Spurlockf88d8082015-03-25 18:09:51 -0400632 }
633
634 private final class W extends Handler {
635 private static final int VOLUME_CHANGED = 1;
636 private static final int DISMISS_REQUESTED = 2;
637 private static final int GET_STATE = 3;
638 private static final int SET_RINGER_MODE = 4;
639 private static final int SET_ZEN_MODE = 5;
640 private static final int SET_EXIT_CONDITION = 6;
641 private static final int SET_STREAM_MUTE = 7;
642 private static final int LAYOUT_DIRECTION_CHANGED = 8;
643 private static final int CONFIGURATION_CHANGED = 9;
644 private static final int SET_STREAM_VOLUME = 10;
645 private static final int SET_ACTIVE_STREAM = 11;
646 private static final int NOTIFY_VISIBLE = 12;
647 private static final int USER_ACTIVITY = 13;
John Spurlock76b52b32015-04-03 00:00:12 -0400648 private static final int SHOW_SAFETY_WARNING = 14;
Julia Reynoldsdbfb40f2017-01-23 11:05:57 -0500649 private static final int ACCESSIBILITY_MODE_CHANGED = 15;
John Spurlockf88d8082015-03-25 18:09:51 -0400650
651 W(Looper looper) {
652 super(looper);
653 }
654
655 @Override
656 public void handleMessage(Message msg) {
657 switch (msg.what) {
658 case VOLUME_CHANGED: onVolumeChangedW(msg.arg1, msg.arg2); break;
659 case DISMISS_REQUESTED: onDismissRequestedW(msg.arg1); break;
660 case GET_STATE: onGetStateW(); break;
661 case SET_RINGER_MODE: onSetRingerModeW(msg.arg1, msg.arg2 != 0); break;
662 case SET_ZEN_MODE: onSetZenModeW(msg.arg1); break;
663 case SET_EXIT_CONDITION: onSetExitConditionW((Condition) msg.obj); break;
664 case SET_STREAM_MUTE: onSetStreamMuteW(msg.arg1, msg.arg2 != 0); break;
665 case LAYOUT_DIRECTION_CHANGED: mCallbacks.onLayoutDirectionChanged(msg.arg1); break;
666 case CONFIGURATION_CHANGED: mCallbacks.onConfigurationChanged(); break;
667 case SET_STREAM_VOLUME: onSetStreamVolumeW(msg.arg1, msg.arg2); break;
668 case SET_ACTIVE_STREAM: onSetActiveStreamW(msg.arg1); break;
John Spurlock76b52b32015-04-03 00:00:12 -0400669 case NOTIFY_VISIBLE: onNotifyVisibleW(msg.arg1 != 0); break;
670 case USER_ACTIVITY: onUserActivityW(); break;
671 case SHOW_SAFETY_WARNING: onShowSafetyWarningW(msg.arg1); break;
Julia Reynoldsdbfb40f2017-01-23 11:05:57 -0500672 case ACCESSIBILITY_MODE_CHANGED: onAccessibilityModeChanged((Boolean) msg.obj);
Julia Reynolds6692b472018-01-24 07:23:56 -0500673
John Spurlockf88d8082015-03-25 18:09:51 -0400674 }
675 }
676 }
677
Beverly8ebef842017-07-12 10:58:22 -0400678 class C implements Callbacks {
John Spurlockf88d8082015-03-25 18:09:51 -0400679 private final HashMap<Callbacks, Handler> mCallbackMap = new HashMap<>();
680
681 public void add(Callbacks callback, Handler handler) {
682 if (callback == null || handler == null) throw new IllegalArgumentException();
683 mCallbackMap.put(callback, handler);
684 }
685
686 public void remove(Callbacks callback) {
687 mCallbackMap.remove(callback);
688 }
689
690 @Override
691 public void onShowRequested(final int reason) {
692 for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
693 entry.getValue().post(new Runnable() {
694 @Override
695 public void run() {
696 entry.getKey().onShowRequested(reason);
697 }
698 });
699 }
700 }
701
702 @Override
703 public void onDismissRequested(final int reason) {
704 for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
705 entry.getValue().post(new Runnable() {
706 @Override
707 public void run() {
708 entry.getKey().onDismissRequested(reason);
709 }
710 });
711 }
712 }
713
714 @Override
715 public void onStateChanged(final State state) {
716 final long time = System.currentTimeMillis();
717 final State copy = state.copy();
718 for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
719 entry.getValue().post(new Runnable() {
720 @Override
721 public void run() {
722 entry.getKey().onStateChanged(copy);
723 }
724 });
725 }
726 Events.writeState(time, copy);
727 }
728
729 @Override
730 public void onLayoutDirectionChanged(final int layoutDirection) {
731 for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
732 entry.getValue().post(new Runnable() {
733 @Override
734 public void run() {
735 entry.getKey().onLayoutDirectionChanged(layoutDirection);
736 }
737 });
738 }
739 }
740
741 @Override
742 public void onConfigurationChanged() {
743 for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
744 entry.getValue().post(new Runnable() {
745 @Override
746 public void run() {
747 entry.getKey().onConfigurationChanged();
748 }
749 });
750 }
751 }
752
753 @Override
754 public void onShowVibrateHint() {
755 for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
756 entry.getValue().post(new Runnable() {
757 @Override
758 public void run() {
759 entry.getKey().onShowVibrateHint();
760 }
761 });
762 }
763 }
764
765 @Override
766 public void onShowSilentHint() {
767 for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
768 entry.getValue().post(new Runnable() {
769 @Override
770 public void run() {
771 entry.getKey().onShowSilentHint();
772 }
773 });
774 }
775 }
776
777 @Override
778 public void onScreenOff() {
779 for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
780 entry.getValue().post(new Runnable() {
781 @Override
782 public void run() {
783 entry.getKey().onScreenOff();
784 }
785 });
786 }
787 }
John Spurlock76b52b32015-04-03 00:00:12 -0400788
789 @Override
790 public void onShowSafetyWarning(final int flags) {
791 for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
792 entry.getValue().post(new Runnable() {
793 @Override
794 public void run() {
795 entry.getKey().onShowSafetyWarning(flags);
796 }
797 });
798 }
799 }
Julia Reynoldsdbfb40f2017-01-23 11:05:57 -0500800
801 @Override
802 public void onAccessibilityModeChanged(Boolean showA11yStream) {
803 boolean show = showA11yStream == null ? false : showA11yStream;
804 for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
805 entry.getValue().post(new Runnable() {
806 @Override
807 public void run() {
808 entry.getKey().onAccessibilityModeChanged(show);
809 }
810 });
811 }
812 }
Julia Reynolds6692b472018-01-24 07:23:56 -0500813
814 @Override
815 public void onConnectedDeviceChanged(String deviceName) {
816 for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
817 entry.getValue().post(new Runnable() {
818 @Override
819 public void run() {
820 entry.getKey().onConnectedDeviceChanged(deviceName);
821 }
822 });
823 }
824 }
John Spurlockf88d8082015-03-25 18:09:51 -0400825 }
826
827
828 private final class SettingObserver extends ContentObserver {
John Spurlockf88d8082015-03-25 18:09:51 -0400829 private final Uri ZEN_MODE_URI =
830 Settings.Global.getUriFor(Settings.Global.ZEN_MODE);
831 private final Uri ZEN_MODE_CONFIG_URI =
832 Settings.Global.getUriFor(Settings.Global.ZEN_MODE_CONFIG_ETAG);
833
834 public SettingObserver(Handler handler) {
835 super(handler);
836 }
837
838 public void init() {
John Spurlockf88d8082015-03-25 18:09:51 -0400839 mContext.getContentResolver().registerContentObserver(ZEN_MODE_URI, false, this);
840 mContext.getContentResolver().registerContentObserver(ZEN_MODE_CONFIG_URI, false, this);
John Spurlockf88d8082015-03-25 18:09:51 -0400841 }
842
843 public void destroy() {
844 mContext.getContentResolver().unregisterContentObserver(this);
845 }
846
847 @Override
848 public void onChange(boolean selfChange, Uri uri) {
849 boolean changed = false;
John Spurlockf88d8082015-03-25 18:09:51 -0400850 if (ZEN_MODE_URI.equals(uri)) {
851 changed = updateZenModeW();
852 }
John Spurlockf88d8082015-03-25 18:09:51 -0400853 if (changed) {
854 mCallbacks.onStateChanged(mState);
855 }
856 }
857 }
858
859 private final class Receiver extends BroadcastReceiver {
860
861 public void init() {
862 final IntentFilter filter = new IntentFilter();
863 filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
864 filter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
865 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
866 filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
867 filter.addAction(AudioManager.STREAM_MUTE_CHANGED_ACTION);
868 filter.addAction(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
869 filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
870 filter.addAction(Intent.ACTION_SCREEN_OFF);
John Spurlockbc7233a2015-06-29 15:34:18 -0400871 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
John Spurlockf88d8082015-03-25 18:09:51 -0400872 mContext.registerReceiver(this, filter, null, mWorker);
873 }
874
875 public void destroy() {
876 mContext.unregisterReceiver(this);
877 }
878
879 @Override
880 public void onReceive(Context context, Intent intent) {
881 final String action = intent.getAction();
882 boolean changed = false;
883 if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
884 final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
885 final int level = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
886 final int oldLevel = intent
887 .getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, -1);
888 if (D.BUG) Log.d(TAG, "onReceive VOLUME_CHANGED_ACTION stream=" + stream
889 + " level=" + level + " oldLevel=" + oldLevel);
890 changed = updateStreamLevelW(stream, level);
891 } else if (action.equals(AudioManager.STREAM_DEVICES_CHANGED_ACTION)) {
892 final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
893 final int devices = intent
894 .getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_DEVICES, -1);
895 final int oldDevices = intent
896 .getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_DEVICES, -1);
897 if (D.BUG) Log.d(TAG, "onReceive STREAM_DEVICES_CHANGED_ACTION stream="
898 + stream + " devices=" + devices + " oldDevices=" + oldDevices);
899 changed = checkRoutedToBluetoothW(stream);
Julia Reynolds8ae994f2015-09-14 11:12:42 -0400900 changed |= onVolumeChangedW(stream, 0);
John Spurlockf88d8082015-03-25 18:09:51 -0400901 } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
902 final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
903 if (D.BUG) Log.d(TAG, "onReceive RINGER_MODE_CHANGED_ACTION rm="
904 + Util.ringerModeToString(rm));
905 changed = updateRingerModeExternalW(rm);
906 } else if (action.equals(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)) {
907 final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
908 if (D.BUG) Log.d(TAG, "onReceive INTERNAL_RINGER_MODE_CHANGED_ACTION rm="
909 + Util.ringerModeToString(rm));
910 changed = updateRingerModeInternalW(rm);
911 } else if (action.equals(AudioManager.STREAM_MUTE_CHANGED_ACTION)) {
912 final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
913 final boolean muted = intent
914 .getBooleanExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, false);
915 if (D.BUG) Log.d(TAG, "onReceive STREAM_MUTE_CHANGED_ACTION stream=" + stream
916 + " muted=" + muted);
917 changed = updateStreamMuteW(stream, muted);
918 } else if (action.equals(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED)) {
919 if (D.BUG) Log.d(TAG, "onReceive ACTION_EFFECTS_SUPPRESSOR_CHANGED");
920 changed = updateEffectsSuppressorW(mNoMan.getEffectsSuppressor());
921 } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
922 if (D.BUG) Log.d(TAG, "onReceive ACTION_CONFIGURATION_CHANGED");
923 mCallbacks.onConfigurationChanged();
924 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
925 if (D.BUG) Log.d(TAG, "onReceive ACTION_SCREEN_OFF");
926 mCallbacks.onScreenOff();
John Spurlockbc7233a2015-06-29 15:34:18 -0400927 } else if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
928 if (D.BUG) Log.d(TAG, "onReceive ACTION_CLOSE_SYSTEM_DIALOGS");
929 dismiss();
John Spurlockf88d8082015-03-25 18:09:51 -0400930 }
931 if (changed) {
932 mCallbacks.onStateChanged(mState);
933 }
934 }
935 }
936
Beverly4e936612017-07-28 14:05:30 -0400937 protected final class MediaSessionsCallbacks implements MediaSessions.Callbacks {
John Spurlockf88d8082015-03-25 18:09:51 -0400938 private final HashMap<Token, Integer> mRemoteStreams = new HashMap<>();
939
940 private int mNextStream = DYNAMIC_STREAM_START_INDEX;
941
942 @Override
943 public void onRemoteUpdate(Token token, String name, PlaybackInfo pi) {
Beverlyb5a27812017-07-24 11:34:48 -0400944 addStream(token, "onRemoteUpdate");
John Spurlockf88d8082015-03-25 18:09:51 -0400945 final int stream = mRemoteStreams.get(token);
946 boolean changed = mState.states.indexOfKey(stream) < 0;
947 final StreamState ss = streamStateW(stream);
948 ss.dynamic = true;
949 ss.levelMin = 0;
950 ss.levelMax = pi.getMaxVolume();
951 if (ss.level != pi.getCurrentVolume()) {
952 ss.level = pi.getCurrentVolume();
953 changed = true;
954 }
Julia Reynolds03c548f2016-12-14 15:02:38 -0500955 if (!Objects.equals(ss.remoteLabel, name)) {
956 ss.name = -1;
957 ss.remoteLabel = name;
John Spurlockf88d8082015-03-25 18:09:51 -0400958 changed = true;
959 }
960 if (changed) {
961 if (D.BUG) Log.d(TAG, "onRemoteUpdate: " + name + ": " + ss.level
962 + " of " + ss.levelMax);
963 mCallbacks.onStateChanged(mState);
964 }
965 }
966
967 @Override
968 public void onRemoteVolumeChanged(Token token, int flags) {
Beverlyb5a27812017-07-24 11:34:48 -0400969 addStream(token, "onRemoteVolumeChanged");
John Spurlockf88d8082015-03-25 18:09:51 -0400970 final int stream = mRemoteStreams.get(token);
Beverly85e05eb2017-07-21 14:49:27 -0400971 final boolean showUI = shouldShowUI(flags);
John Spurlockf88d8082015-03-25 18:09:51 -0400972 boolean changed = updateActiveStreamW(stream);
973 if (showUI) {
974 changed |= checkRoutedToBluetoothW(AudioManager.STREAM_MUSIC);
975 }
976 if (changed) {
977 mCallbacks.onStateChanged(mState);
978 }
979 if (showUI) {
980 mCallbacks.onShowRequested(Events.SHOW_REASON_REMOTE_VOLUME_CHANGED);
981 }
982 }
983
984 @Override
985 public void onRemoteRemoved(Token token) {
Beverlyb5a27812017-07-24 11:34:48 -0400986 if (!mRemoteStreams.containsKey(token)) {
987 if (D.BUG) Log.d(TAG, "onRemoteRemoved: stream doesn't exist, "
988 + "aborting remote removed for token:" + token.toString());
989 return;
990 }
John Spurlockf88d8082015-03-25 18:09:51 -0400991 final int stream = mRemoteStreams.get(token);
992 mState.states.remove(stream);
993 if (mState.activeStream == stream) {
994 updateActiveStreamW(-1);
995 }
996 mCallbacks.onStateChanged(mState);
997 }
998
999 public void setStreamVolume(int stream, int level) {
1000 final Token t = findToken(stream);
1001 if (t == null) {
1002 Log.w(TAG, "setStreamVolume: No token found for stream: " + stream);
1003 return;
1004 }
1005 mMediaSessions.setVolume(t, level);
1006 }
1007
1008 private Token findToken(int stream) {
1009 for (Map.Entry<Token, Integer> entry : mRemoteStreams.entrySet()) {
1010 if (entry.getValue().equals(stream)) {
1011 return entry.getKey();
1012 }
1013 }
1014 return null;
1015 }
Beverlyb5a27812017-07-24 11:34:48 -04001016
1017 private void addStream(Token token, String triggeringMethod) {
1018 if (!mRemoteStreams.containsKey(token)) {
1019 mRemoteStreams.put(token, mNextStream);
1020 if (D.BUG) Log.d(TAG, triggeringMethod + ": added stream " + mNextStream
1021 + " from token + "+ token.toString());
1022 mNextStream++;
1023 }
1024 }
John Spurlockf88d8082015-03-25 18:09:51 -04001025 }
1026
Julia Reynolds6692b472018-01-24 07:23:56 -05001027 protected final class DeviceCallback extends AudioDeviceCallback {
1028 public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
1029 for (AudioDeviceInfo info : addedDevices) {
1030 if (info.isSink()
1031 && (info.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP
1032 || info.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_SCO)) {
1033 mConnectedDevice = info;
1034 mCallbacks.onConnectedDeviceChanged(info.getProductName().toString());
1035 }
1036 }
1037 }
1038
1039 public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
1040 if (mConnectedDevice == null) {
1041 mCallbacks.onConnectedDeviceChanged(null);
1042 return;
1043 }
1044 for (AudioDeviceInfo info : removedDevices) {
1045 if (info.isSink() == mConnectedDevice.isSink()
1046 && Objects.equals(info.getProductName(), mConnectedDevice.getProductName())
1047 && info.getType() == mConnectedDevice.getType()) {
1048 mConnectedDevice = null;
1049 mCallbacks.onConnectedDeviceChanged(null);
1050 }
1051 }
1052 }
1053 }
1054
Yao Chen634acb92016-04-13 16:17:47 -07001055 public interface UserActivityListener {
1056 void onUserActivity();
1057 }
John Spurlockf88d8082015-03-25 18:09:51 -04001058}