blob: 4475aeddf944346edd945e5b96b8395d25786ccb [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 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 android.media;
18
Jan Sebechlebskyfb48c022022-12-16 16:26:47 +010019import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_DEFAULT;
20import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
21import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
22
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -080023import android.annotation.CallbackExecutor;
Jean-Michel Trivi8e7aca32017-11-28 18:12:38 -080024import android.annotation.IntDef;
Hayden Gomes695f8022019-04-11 10:44:18 -070025import android.annotation.IntRange;
Jean-Michel Trivi958876f2014-11-16 15:40:22 -080026import android.annotation.NonNull;
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -070027import android.annotation.Nullable;
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060028import android.annotation.RequiresPermission;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.annotation.SdkConstant;
30import android.annotation.SdkConstant.SdkConstantType;
Jean-Michel Trivi26ae15f2017-08-25 19:20:49 -070031import android.annotation.SuppressLint;
Terry Heoe7d6d972014-09-04 21:05:28 +090032import android.annotation.SystemApi;
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060033import android.annotation.SystemService;
Jean-Michel Triviec977322019-04-12 11:20:35 -070034import android.annotation.TestApi;
Julia Reynolds48034f82016-03-09 10:15:16 -050035import android.app.NotificationManager;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -070036import android.app.PendingIntent;
Eric Laurent1c3408f2021-11-09 12:09:54 +010037import android.app.compat.CompatChanges;
Arun Mirpuricb102fa2019-01-11 18:39:21 -080038import android.bluetooth.BluetoothCodecConfig;
Eric Laurentb1fbaac2012-05-29 09:24:28 -070039import android.bluetooth.BluetoothDevice;
Patty46694212021-11-04 21:03:32 +080040import android.bluetooth.BluetoothLeAudioCodecConfig;
Jan Sebechlebskyfb48c022022-12-16 16:26:47 +010041import android.companion.virtual.VirtualDeviceManager;
Eric Laurent1c3408f2021-11-09 12:09:54 +010042import android.compat.annotation.ChangeId;
43import android.compat.annotation.EnabledSince;
Artur Satayev53fe9662019-12-10 17:47:55 +000044import android.compat.annotation.UnsupportedAppUsage;
Jean-Michel Trivid327f212010-03-16 21:44:33 -070045import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.content.Context;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -070047import android.content.Intent;
Hayden Gomes62812aa2019-12-23 11:40:27 -080048import android.media.AudioAttributes.AttributeSystemUsage;
Jean-Michel Trivi91f0baa2021-11-11 16:31:32 -080049import android.media.CallbackUtil.ListenerInfo;
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -070050import android.media.audiopolicy.AudioPolicy;
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -080051import android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener;
Hayden Gomes6d69bde2019-04-04 13:10:13 -070052import android.media.audiopolicy.AudioProductStrategy;
Hayden Gomesebd6aaa2019-04-04 13:14:21 -070053import android.media.audiopolicy.AudioVolumeGroup;
François Gaffieadcd00a2018-09-18 17:06:26 +020054import android.media.audiopolicy.AudioVolumeGroupChangeHandler;
Kevin Rocard2fe8b8b2019-02-20 11:25:23 -080055import android.media.projection.MediaProjection;
RoboErikb214efb2014-07-24 13:20:30 -070056import android.media.session.MediaController;
57import android.media.session.MediaSession;
RoboErikf1372422014-04-23 14:38:17 -070058import android.media.session.MediaSessionLegacyHelper;
RoboErikb214efb2014-07-24 13:20:30 -070059import android.media.session.MediaSessionManager;
jiabinad225202019-03-20 15:22:50 -070060import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061import android.os.Binder;
Jean-Michel Trivi461922f2017-04-25 15:23:17 -070062import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063import android.os.Handler;
64import android.os.IBinder;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -080065import android.os.Looper;
66import android.os.Message;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067import android.os.RemoteException;
68import android.os.ServiceManager;
Beverlye2d9a232017-11-08 18:14:59 -050069import android.os.SystemClock;
Kenny Guy70e0c582015-06-30 19:18:28 +010070import android.os.UserHandle;
Lais Andrade724d0cd2021-11-03 19:46:21 +000071import android.provider.Settings;
jiabinc0f49442018-01-05 10:23:50 -080072import android.text.TextUtils;
Paul McLeane3383cc2015-05-08 11:41:20 -070073import android.util.ArrayMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074import android.util.Log;
jiabin589a2362018-02-22 16:21:53 -080075import android.util.Pair;
Jean-Michel Trivid327f212010-03-16 21:44:33 -070076import android.view.KeyEvent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -080078import com.android.internal.annotations.GuardedBy;
François Gaffieadcd00a2018-09-18 17:06:26 +020079import com.android.internal.util.Preconditions;
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -080080
jiabinc0f49442018-01-05 10:23:50 -080081import java.io.IOException;
Jean-Michel Trivi8e7aca32017-11-28 18:12:38 -080082import java.lang.annotation.Retention;
83import java.lang.annotation.RetentionPolicy;
jiabin0f3339c2021-07-09 11:50:07 -070084import java.lang.ref.WeakReference;
Eric Laurenta198a292014-02-18 16:26:17 -080085import java.util.ArrayList;
jiabinf40141d2020-08-07 17:27:48 -070086import java.util.Arrays;
Dorin Drimusdaeb6a92021-12-22 11:46:26 +010087import java.util.Collections;
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -080088import java.util.HashMap;
jiabind0be5b22018-04-10 14:10:04 -070089import java.util.HashSet;
Wonsik Kimb561cce2015-01-30 17:48:51 +090090import java.util.Iterator;
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -080091import java.util.List;
jiabin39940752018-04-02 18:18:45 -070092import java.util.Map;
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -070093import java.util.Objects;
Hyundo Moonca0080d2018-12-26 16:16:55 +090094import java.util.TreeMap;
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -070095import java.util.concurrent.ConcurrentHashMap;
Eric Laurent1d3cdce2018-01-20 10:31:21 -080096import java.util.concurrent.Executor;
Eric Laurent78eef3a2021-11-09 16:10:42 +010097import java.util.concurrent.Executors;
Jean-Michel Trivi933bf142021-11-19 16:18:52 -080098import java.util.concurrent.TimeUnit;
Eric Laurent700e7342014-05-02 18:33:15 -070099
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100/**
101 * AudioManager provides access to volume and ringer mode control.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -0600103@SystemService(Context.AUDIO_SERVICE)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104public class AudioManager {
105
Marco Nelissen29f16932015-04-17 09:50:56 -0700106 private Context mOriginalContext;
107 private Context mApplicationContext;
Jan Sebechlebskyfb48c022022-12-16 16:26:47 +0100108 private @Nullable VirtualDeviceManager mVirtualDeviceManager; // Lazy initialized.
Joe Onorato86f67862010-11-05 18:57:34 -0700109 private long mVolumeKeyUpTime;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800110 private static final String TAG = "AudioManager";
111 private static final boolean DEBUG = false;
Eric Laurentf076db42015-01-14 13:23:27 -0800112 private static final AudioPortEventHandler sAudioPortEventHandler = new AudioPortEventHandler();
François Gaffieadcd00a2018-09-18 17:06:26 +0200113 private static final AudioVolumeGroupChangeHandler sAudioAudioVolumeGroupChangedHandler =
114 new AudioVolumeGroupChangeHandler();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115
jiabin0f3339c2021-07-09 11:50:07 -0700116 private static WeakReference<Context> sContext;
jiabincfcf1032021-07-01 16:30:50 -0700117
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118 /**
119 * Broadcast intent, a hint for applications that audio is about to become
120 * 'noisy' due to a change in audio outputs. For example, this intent may
121 * be sent when a wired headset is unplugged, or when an A2DP audio
122 * sink is disconnected, and the audio system is about to automatically
123 * switch audio route to the speaker. Applications that are controlling
124 * audio streams may consider pausing, reducing volume or some other action
125 * on receipt of this intent so as not to surprise the user with audio
126 * from the speaker.
127 */
128 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
129 public static final String ACTION_AUDIO_BECOMING_NOISY = "android.media.AUDIO_BECOMING_NOISY";
130
131 /**
132 * Sticky broadcast intent action indicating that the ringer mode has
133 * changed. Includes the new ringer mode.
134 *
135 * @see #EXTRA_RINGER_MODE
136 */
137 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
138 public static final String RINGER_MODE_CHANGED_ACTION = "android.media.RINGER_MODE_CHANGED";
139
140 /**
John Spurlockbcc10872014-11-28 15:29:21 -0500141 * @hide
142 * Sticky broadcast intent action indicating that the internal ringer mode has
143 * changed. Includes the new ringer mode.
144 *
145 * @see #EXTRA_RINGER_MODE
146 */
147 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
148 public static final String INTERNAL_RINGER_MODE_CHANGED_ACTION =
149 "android.media.INTERNAL_RINGER_MODE_CHANGED_ACTION";
150
151 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 * The new ringer mode.
153 *
154 * @see #RINGER_MODE_CHANGED_ACTION
155 * @see #RINGER_MODE_NORMAL
156 * @see #RINGER_MODE_SILENT
157 * @see #RINGER_MODE_VIBRATE
158 */
159 public static final String EXTRA_RINGER_MODE = "android.media.EXTRA_RINGER_MODE";
160
161 /**
162 * Broadcast intent action indicating that the vibrate setting has
163 * changed. Includes the vibrate type and its new setting.
164 *
165 * @see #EXTRA_VIBRATE_TYPE
166 * @see #EXTRA_VIBRATE_SETTING
Eric Laurentcd1cd732012-05-01 11:23:07 -0700167 * @deprecated Applications should maintain their own vibrate policy based on
168 * current ringer mode and listen to {@link #RINGER_MODE_CHANGED_ACTION} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 */
170 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Mike Lockwood0dc37cc2011-12-01 16:14:19 -0500171 public static final String VIBRATE_SETTING_CHANGED_ACTION =
172 "android.media.VIBRATE_SETTING_CHANGED";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173
174 /**
175 * @hide Broadcast intent when the volume for a particular stream type changes.
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700176 * Includes the stream, the new volume and previous volumes.
177 * Notes:
178 * - for internal platform use only, do not make public,
179 * - never used for "remote" volume changes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 *
181 * @see #EXTRA_VOLUME_STREAM_TYPE
182 * @see #EXTRA_VOLUME_STREAM_VALUE
Eric Laurent9ce379a2010-02-16 06:00:26 -0800183 * @see #EXTRA_PREV_VOLUME_STREAM_VALUE
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 */
185 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Mathew Inwood31a792a2018-08-17 08:54:26 +0100186 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187 public static final String VOLUME_CHANGED_ACTION = "android.media.VOLUME_CHANGED_ACTION";
188
189 /**
Roopa Sattirajuf2675c72022-01-27 21:31:35 -0800190 * @hide Broadcast intent when the volume for a particular stream type changes.
191 * Includes the stream, the new volume and previous volumes.
192 * Notes:
193 * - for internal platform use only, do not make public,
194 * - never used for "remote" volume changes
195 *
196 * @see #EXTRA_VOLUME_STREAM_TYPE
197 * @see #EXTRA_VOLUME_STREAM_VALUE
198 * @see #EXTRA_PREV_VOLUME_STREAM_VALUE
199 */
200 @SystemApi
201 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
202 @SuppressLint("ActionValue")
203 public static final String ACTION_VOLUME_CHANGED = "android.media.VOLUME_CHANGED_ACTION";
204
205 /**
John Spurlock8a52c442015-03-26 14:23:58 -0400206 * @hide Broadcast intent when the devices for a particular stream type changes.
207 * Includes the stream, the new devices and previous devices.
208 * Notes:
209 * - for internal platform use only, do not make public,
210 * - never used for "remote" volume changes
211 *
212 * @see #EXTRA_VOLUME_STREAM_TYPE
213 * @see #EXTRA_VOLUME_STREAM_DEVICES
214 * @see #EXTRA_PREV_VOLUME_STREAM_DEVICES
215 * @see #getDevicesForStream
216 */
217 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
218 public static final String STREAM_DEVICES_CHANGED_ACTION =
219 "android.media.STREAM_DEVICES_CHANGED_ACTION";
220
221 /**
RoboErik7c82ced2014-12-04 17:39:08 -0800222 * @hide Broadcast intent when a stream mute state changes.
223 * Includes the stream that changed and the new mute state
224 *
225 * @see #EXTRA_VOLUME_STREAM_TYPE
226 * @see #EXTRA_STREAM_VOLUME_MUTED
227 */
228 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
229 public static final String STREAM_MUTE_CHANGED_ACTION =
230 "android.media.STREAM_MUTE_CHANGED_ACTION";
231
232 /**
Mike Lockwood0dc37cc2011-12-01 16:14:19 -0500233 * @hide Broadcast intent when the master mute state changes.
234 * Includes the the new volume
235 *
236 * @see #EXTRA_MASTER_VOLUME_MUTED
237 */
238 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
239 public static final String MASTER_MUTE_CHANGED_ACTION =
240 "android.media.MASTER_MUTE_CHANGED_ACTION";
241
242 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 * The new vibrate setting for a particular type.
244 *
245 * @see #VIBRATE_SETTING_CHANGED_ACTION
246 * @see #EXTRA_VIBRATE_TYPE
247 * @see #VIBRATE_SETTING_ON
248 * @see #VIBRATE_SETTING_OFF
249 * @see #VIBRATE_SETTING_ONLY_SILENT
Eric Laurentcd1cd732012-05-01 11:23:07 -0700250 * @deprecated Applications should maintain their own vibrate policy based on
251 * current ringer mode and listen to {@link #RINGER_MODE_CHANGED_ACTION} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 */
253 public static final String EXTRA_VIBRATE_SETTING = "android.media.EXTRA_VIBRATE_SETTING";
254
255 /**
256 * The vibrate type whose setting has changed.
257 *
258 * @see #VIBRATE_SETTING_CHANGED_ACTION
259 * @see #VIBRATE_TYPE_NOTIFICATION
260 * @see #VIBRATE_TYPE_RINGER
Eric Laurentcd1cd732012-05-01 11:23:07 -0700261 * @deprecated Applications should maintain their own vibrate policy based on
262 * current ringer mode and listen to {@link #RINGER_MODE_CHANGED_ACTION} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 */
264 public static final String EXTRA_VIBRATE_TYPE = "android.media.EXTRA_VIBRATE_TYPE";
265
266 /**
267 * @hide The stream type for the volume changed intent.
268 */
Roopa Sattirajuf2675c72022-01-27 21:31:35 -0800269 @SystemApi
270 @SuppressLint("ActionValue")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800271 public static final String EXTRA_VOLUME_STREAM_TYPE = "android.media.EXTRA_VOLUME_STREAM_TYPE";
272
273 /**
Jean-Michel Trivi560877d2015-06-25 17:38:35 -0700274 * @hide
275 * The stream type alias for the volume changed intent.
276 * For instance the intent may indicate a change of the {@link #STREAM_NOTIFICATION} stream
277 * type (as indicated by the {@link #EXTRA_VOLUME_STREAM_TYPE} extra), but this is also
278 * reflected by a change of the volume of its alias, {@link #STREAM_RING} on some devices,
279 * {@link #STREAM_MUSIC} on others (e.g. a television).
280 */
281 public static final String EXTRA_VOLUME_STREAM_TYPE_ALIAS =
282 "android.media.EXTRA_VOLUME_STREAM_TYPE_ALIAS";
283
284 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800285 * @hide The volume associated with the stream for the volume changed intent.
286 */
Roopa Sattirajuf2675c72022-01-27 21:31:35 -0800287 @SystemApi
288 @SuppressLint("ActionValue")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 public static final String EXTRA_VOLUME_STREAM_VALUE =
290 "android.media.EXTRA_VOLUME_STREAM_VALUE";
291
Eric Laurent9ce379a2010-02-16 06:00:26 -0800292 /**
293 * @hide The previous volume associated with the stream for the volume changed intent.
294 */
295 public static final String EXTRA_PREV_VOLUME_STREAM_VALUE =
296 "android.media.EXTRA_PREV_VOLUME_STREAM_VALUE";
297
Mike Lockwood0dc37cc2011-12-01 16:14:19 -0500298 /**
John Spurlock8a52c442015-03-26 14:23:58 -0400299 * @hide The devices associated with the stream for the stream devices changed intent.
300 */
301 public static final String EXTRA_VOLUME_STREAM_DEVICES =
302 "android.media.EXTRA_VOLUME_STREAM_DEVICES";
303
304 /**
305 * @hide The previous devices associated with the stream for the stream devices changed intent.
306 */
307 public static final String EXTRA_PREV_VOLUME_STREAM_DEVICES =
308 "android.media.EXTRA_PREV_VOLUME_STREAM_DEVICES";
309
310 /**
Mike Lockwood0dc37cc2011-12-01 16:14:19 -0500311 * @hide The new master volume mute state for the master mute changed intent.
312 * Value is boolean
313 */
314 public static final String EXTRA_MASTER_VOLUME_MUTED =
315 "android.media.EXTRA_MASTER_VOLUME_MUTED";
316
Jean-Michel Trivic5258432014-08-27 15:46:54 -0700317 /**
RoboErik7c82ced2014-12-04 17:39:08 -0800318 * @hide The new stream volume mute state for the stream mute changed intent.
319 * Value is boolean
320 */
321 public static final String EXTRA_STREAM_VOLUME_MUTED =
322 "android.media.EXTRA_STREAM_VOLUME_MUTED";
323
324 /**
Jean-Michel Trivic5258432014-08-27 15:46:54 -0700325 * Broadcast Action: Wired Headset plugged in or unplugged.
326 *
327 * You <em>cannot</em> receive this through components declared
328 * in manifests, only by explicitly registering for it with
329 * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
330 * Context.registerReceiver()}.
331 *
332 * <p>The intent will have the following extra values:
333 * <ul>
334 * <li><em>state</em> - 0 for unplugged, 1 for plugged. </li>
335 * <li><em>name</em> - Headset type, human readable string </li>
336 * <li><em>microphone</em> - 1 if headset has a microphone, 0 otherwise </li>
337 * </ul>
338 * </ul>
339 */
340 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
341 public static final String ACTION_HEADSET_PLUG =
342 "android.intent.action.HEADSET_PLUG";
343
344 /**
Eemi Haukkalabc682562015-03-06 23:03:30 +0200345 * Broadcast Action: A sticky broadcast indicating an HDMI cable was plugged or unplugged.
Jean-Michel Trivic5258432014-08-27 15:46:54 -0700346 *
347 * The intent will have the following extra values: {@link #EXTRA_AUDIO_PLUG_STATE},
348 * {@link #EXTRA_MAX_CHANNEL_COUNT}, {@link #EXTRA_ENCODINGS}.
349 * <p>It can only be received by explicitly registering for it with
350 * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)}.
351 */
352 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
353 public static final String ACTION_HDMI_AUDIO_PLUG =
354 "android.media.action.HDMI_AUDIO_PLUG";
355
356 /**
357 * Extra used in {@link #ACTION_HDMI_AUDIO_PLUG} to communicate whether HDMI is plugged in
358 * or unplugged.
359 * An integer value of 1 indicates a plugged-in state, 0 is unplugged.
360 */
Jean-Michel Trivideddda52014-09-05 14:38:47 -0700361 public static final String EXTRA_AUDIO_PLUG_STATE = "android.media.extra.AUDIO_PLUG_STATE";
Jean-Michel Trivic5258432014-08-27 15:46:54 -0700362
363 /**
364 * Extra used in {@link #ACTION_HDMI_AUDIO_PLUG} to define the maximum number of channels
365 * supported by the HDMI device.
366 * The corresponding integer value is only available when the device is plugged in (as expressed
367 * by {@link #EXTRA_AUDIO_PLUG_STATE}).
368 */
Jean-Michel Trivideddda52014-09-05 14:38:47 -0700369 public static final String EXTRA_MAX_CHANNEL_COUNT = "android.media.extra.MAX_CHANNEL_COUNT";
Jean-Michel Trivic5258432014-08-27 15:46:54 -0700370
371 /**
372 * Extra used in {@link #ACTION_HDMI_AUDIO_PLUG} to define the audio encodings supported by
373 * the connected HDMI device.
374 * The corresponding array of encoding values is only available when the device is plugged in
375 * (as expressed by {@link #EXTRA_AUDIO_PLUG_STATE}). Encoding values are defined in
376 * {@link AudioFormat} (for instance see {@link AudioFormat#ENCODING_PCM_16BIT}). Use
377 * {@link android.content.Intent#getIntArrayExtra(String)} to retrieve the encoding values.
378 */
Jean-Michel Trivideddda52014-09-05 14:38:47 -0700379 public static final String EXTRA_ENCODINGS = "android.media.extra.ENCODINGS";
Jean-Michel Trivic5258432014-08-27 15:46:54 -0700380
Jean-Michel Trivi46d5b822016-10-04 12:37:53 -0700381 /** Used to identify the volume of audio streams for phone calls */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800382 public static final int STREAM_VOICE_CALL = AudioSystem.STREAM_VOICE_CALL;
Jean-Michel Trivi46d5b822016-10-04 12:37:53 -0700383 /** Used to identify the volume of audio streams for system sounds */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 public static final int STREAM_SYSTEM = AudioSystem.STREAM_SYSTEM;
Jean-Michel Trivi46d5b822016-10-04 12:37:53 -0700385 /** Used to identify the volume of audio streams for the phone ring */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 public static final int STREAM_RING = AudioSystem.STREAM_RING;
Jean-Michel Trivi46d5b822016-10-04 12:37:53 -0700387 /** Used to identify the volume of audio streams for music playback */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 public static final int STREAM_MUSIC = AudioSystem.STREAM_MUSIC;
Jean-Michel Trivi46d5b822016-10-04 12:37:53 -0700389 /** Used to identify the volume of audio streams for alarms */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390 public static final int STREAM_ALARM = AudioSystem.STREAM_ALARM;
Jean-Michel Trivi46d5b822016-10-04 12:37:53 -0700391 /** Used to identify the volume of audio streams for notifications */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 public static final int STREAM_NOTIFICATION = AudioSystem.STREAM_NOTIFICATION;
Jean-Michel Trivi46d5b822016-10-04 12:37:53 -0700393 /** @hide Used to identify the volume of audio streams for phone calls when connected
394 * to bluetooth */
Roopa Sattirajuf2675c72022-01-27 21:31:35 -0800395 @SystemApi
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800396 public static final int STREAM_BLUETOOTH_SCO = AudioSystem.STREAM_BLUETOOTH_SCO;
Jean-Michel Trivi46d5b822016-10-04 12:37:53 -0700397 /** @hide Used to identify the volume of audio streams for enforced system sounds
398 * in certain countries (e.g camera in Japan) */
Mathew Inwood31a792a2018-08-17 08:54:26 +0100399 @UnsupportedAppUsage
Eric Laurenta553c252009-07-17 12:17:14 -0700400 public static final int STREAM_SYSTEM_ENFORCED = AudioSystem.STREAM_SYSTEM_ENFORCED;
Jean-Michel Trivi46d5b822016-10-04 12:37:53 -0700401 /** Used to identify the volume of audio streams for DTMF Tones */
Eric Laurenta553c252009-07-17 12:17:14 -0700402 public static final int STREAM_DTMF = AudioSystem.STREAM_DTMF;
Jean-Michel Trivi46d5b822016-10-04 12:37:53 -0700403 /** @hide Used to identify the volume of audio streams exclusively transmitted through the
404 * speaker (TTS) of the device */
Mathew Inwood31a792a2018-08-17 08:54:26 +0100405 @UnsupportedAppUsage
Eric Laurenta553c252009-07-17 12:17:14 -0700406 public static final int STREAM_TTS = AudioSystem.STREAM_TTS;
Jean-Michel Trivi3f0945a2016-11-11 10:05:18 -0800407 /** Used to identify the volume of audio streams for accessibility prompts */
408 public static final int STREAM_ACCESSIBILITY = AudioSystem.STREAM_ACCESSIBILITY;
Kim Baekgyeongb64fac72019-12-09 10:35:58 +0000409 /** @hide Used to identify the volume of audio streams for virtual assistant */
410 @SystemApi
411 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
412 public static final int STREAM_ASSISTANT = AudioSystem.STREAM_ASSISTANT;
Jean-Michel Trivi3f0945a2016-11-11 10:05:18 -0800413
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414 /** Number of audio streams */
415 /**
Jean-Michel Trivi46d5b822016-10-04 12:37:53 -0700416 * @deprecated Do not iterate on volume stream type values.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 */
Eric Laurenta553c252009-07-17 12:17:14 -0700418 @Deprecated public static final int NUM_STREAMS = AudioSystem.NUM_STREAMS;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419
Paul McLeand6f87c82021-03-31 13:02:41 -0600420 /** @hide */
421 private static final int[] PUBLIC_STREAM_TYPES = { AudioManager.STREAM_VOICE_CALL,
422 AudioManager.STREAM_SYSTEM, AudioManager.STREAM_RING, AudioManager.STREAM_MUSIC,
423 AudioManager.STREAM_ALARM, AudioManager.STREAM_NOTIFICATION,
424 AudioManager.STREAM_DTMF, AudioManager.STREAM_ACCESSIBILITY };
425
426 /** @hide */
427 @TestApi
428 public static final int[] getPublicStreamTypes() {
429 return PUBLIC_STREAM_TYPES;
430 }
431
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 /**
433 * Increase the ringer volume.
434 *
435 * @see #adjustVolume(int, int)
436 * @see #adjustStreamVolume(int, int, int)
437 */
438 public static final int ADJUST_RAISE = 1;
439
440 /**
441 * Decrease the ringer volume.
442 *
443 * @see #adjustVolume(int, int)
444 * @see #adjustStreamVolume(int, int, int)
445 */
446 public static final int ADJUST_LOWER = -1;
447
448 /**
449 * Maintain the previous ringer volume. This may be useful when needing to
450 * show the volume toast without actually modifying the volume.
451 *
452 * @see #adjustVolume(int, int)
453 * @see #adjustStreamVolume(int, int, int)
454 */
455 public static final int ADJUST_SAME = 0;
456
RoboErik4197cb62015-01-21 15:45:32 -0800457 /**
458 * Mute the volume. Has no effect if the stream is already muted.
459 *
460 * @see #adjustVolume(int, int)
461 * @see #adjustStreamVolume(int, int, int)
462 */
463 public static final int ADJUST_MUTE = -100;
464
465 /**
466 * Unmute the volume. Has no effect if the stream is not muted.
467 *
468 * @see #adjustVolume(int, int)
469 * @see #adjustStreamVolume(int, int, int)
470 */
471 public static final int ADJUST_UNMUTE = 100;
472
473 /**
474 * Toggle the mute state. If muted the stream will be unmuted. If not muted
475 * the stream will be muted.
476 *
477 * @see #adjustVolume(int, int)
478 * @see #adjustStreamVolume(int, int, int)
479 */
480 public static final int ADJUST_TOGGLE_MUTE = 101;
481
Jean-Michel Trivicf170362017-08-24 17:24:57 -0700482 /** @hide */
Jean-Michel Trivi610aa9b2018-01-27 15:42:42 -0800483 @IntDef(flag = false, prefix = "ADJUST", value = {
484 ADJUST_RAISE,
485 ADJUST_LOWER,
486 ADJUST_SAME,
487 ADJUST_MUTE,
488 ADJUST_UNMUTE,
489 ADJUST_TOGGLE_MUTE }
490 )
491 @Retention(RetentionPolicy.SOURCE)
Jean-Michel Trivi1b926e72018-01-29 16:06:51 -0800492 public @interface VolumeAdjustment {}
Jean-Michel Trivi610aa9b2018-01-27 15:42:42 -0800493
494 /** @hide */
Jean-Michel Trivicf170362017-08-24 17:24:57 -0700495 public static final String adjustToString(int adj) {
496 switch (adj) {
497 case ADJUST_RAISE: return "ADJUST_RAISE";
498 case ADJUST_LOWER: return "ADJUST_LOWER";
499 case ADJUST_SAME: return "ADJUST_SAME";
500 case ADJUST_MUTE: return "ADJUST_MUTE";
501 case ADJUST_UNMUTE: return "ADJUST_UNMUTE";
502 case ADJUST_TOGGLE_MUTE: return "ADJUST_TOGGLE_MUTE";
503 default: return new StringBuilder("unknown adjust mode ").append(adj).toString();
504 }
505 }
506
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507 // Flags should be powers of 2!
508
509 /**
510 * Show a toast containing the current volume.
511 *
512 * @see #adjustStreamVolume(int, int, int)
513 * @see #adjustVolume(int, int)
514 * @see #setStreamVolume(int, int, int)
515 * @see #setRingerMode(int)
516 */
517 public static final int FLAG_SHOW_UI = 1 << 0;
518
519 /**
520 * Whether to include ringer modes as possible options when changing volume.
521 * For example, if true and volume level is 0 and the volume is adjusted
522 * with {@link #ADJUST_LOWER}, then the ringer mode may switch the silent or
523 * vibrate mode.
524 * <p>
525 * By default this is on for the ring stream. If this flag is included,
526 * this behavior will be present regardless of the stream type being
527 * affected by the ringer mode.
The Android Open Source Project10592532009-03-18 17:39:46 -0700528 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 * @see #adjustVolume(int, int)
530 * @see #adjustStreamVolume(int, int, int)
531 */
532 public static final int FLAG_ALLOW_RINGER_MODES = 1 << 1;
533
534 /**
535 * Whether to play a sound when changing the volume.
536 * <p>
537 * If this is given to {@link #adjustVolume(int, int)} or
538 * {@link #adjustSuggestedStreamVolume(int, int, int)}, it may be ignored
539 * in some cases (for example, the decided stream type is not
540 * {@link AudioManager#STREAM_RING}, or the volume is being adjusted
541 * downward).
542 *
543 * @see #adjustStreamVolume(int, int, int)
544 * @see #adjustVolume(int, int)
545 * @see #setStreamVolume(int, int, int)
546 */
547 public static final int FLAG_PLAY_SOUND = 1 << 2;
548
549 /**
550 * Removes any sounds/vibrate that may be in the queue, or are playing (related to
551 * changing volume).
552 */
553 public static final int FLAG_REMOVE_SOUND_AND_VIBRATE = 1 << 3;
554
555 /**
556 * Whether to vibrate if going into the vibrate ringer mode.
557 */
558 public static final int FLAG_VIBRATE = 1 << 4;
559
560 /**
Eric Laurent4bbcc652012-09-24 14:26:30 -0700561 * Indicates to VolumePanel that the volume slider should be disabled as user
562 * cannot change the stream volume
563 * @hide
564 */
565 public static final int FLAG_FIXED_VOLUME = 1 << 5;
566
567 /**
Matthew Xiec9d1d5f2013-09-12 00:32:22 -0700568 * Indicates the volume set/adjust call is for Bluetooth absolute volume
569 * @hide
570 */
Roopa Sattirajufb933242022-01-30 13:27:58 -0800571 @SystemApi
Matthew Xiec9d1d5f2013-09-12 00:32:22 -0700572 public static final int FLAG_BLUETOOTH_ABS_VOLUME = 1 << 6;
573
574 /**
John Spurlocka11b4af2014-06-01 11:52:23 -0400575 * Adjusting the volume was prevented due to silent mode, display a hint in the UI.
576 * @hide
577 */
578 public static final int FLAG_SHOW_SILENT_HINT = 1 << 7;
579
580 /**
Jungshik Jang41d97462014-06-30 22:26:29 +0900581 * Indicates the volume call is for Hdmi Cec system audio volume
582 * @hide
583 */
584 public static final int FLAG_HDMI_SYSTEM_AUDIO_VOLUME = 1 << 8;
585
586 /**
RoboErik3c45c292014-07-08 16:47:31 -0700587 * Indicates that this should only be handled if media is actively playing.
588 * @hide
589 */
590 public static final int FLAG_ACTIVE_MEDIA_ONLY = 1 << 9;
591
592 /**
John Spurlock35134602014-07-24 18:10:48 -0400593 * Like FLAG_SHOW_UI, but only dialog warnings and confirmations, no sliders.
594 * @hide
595 */
596 public static final int FLAG_SHOW_UI_WARNINGS = 1 << 10;
597
598 /**
John Spurlock661f2cf42014-11-17 10:29:10 -0500599 * Adjusting the volume down from vibrated was prevented, display a hint in the UI.
600 * @hide
601 */
602 public static final int FLAG_SHOW_VIBRATE_HINT = 1 << 11;
603
John Spurlockb94f2d62015-03-17 14:11:57 -0400604 /**
605 * Adjusting the volume due to a hardware key press.
Hyundo Moonca0080d2018-12-26 16:16:55 +0900606 * This flag can be used in the places in order to denote (or check) that a volume adjustment
607 * request is from a hardware key press. (e.g. {@link MediaController}).
John Spurlockb94f2d62015-03-17 14:11:57 -0400608 * @hide
609 */
Jin Seok Park4abc23e2020-07-30 22:28:50 +0900610 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
Hyundo Moonc3ce09e2019-03-11 20:00:00 +0900611 public static final int FLAG_FROM_KEY = 1 << 12;
John Spurlockb94f2d62015-03-17 14:11:57 -0400612
Yan Han7d419822022-01-31 19:10:16 +0100613 /**
614 * Indicates that an absolute volume controller is notifying AudioService of a change in the
615 * volume or mute status of an external audio system.
616 * @hide
617 */
618 public static final int FLAG_ABSOLUTE_VOLUME = 1 << 13;
619
Jin Seok Park5ccb6cc2021-02-05 01:47:13 +0900620 /** @hide */
Kriti Dang527e66c2021-03-04 10:37:22 +0100621 @IntDef(prefix = {"ENCODED_SURROUND_OUTPUT_"}, value = {
Kriti Dang98fdb262021-04-01 13:26:00 +0200622 ENCODED_SURROUND_OUTPUT_UNKNOWN,
Kriti Dang527e66c2021-03-04 10:37:22 +0100623 ENCODED_SURROUND_OUTPUT_AUTO,
624 ENCODED_SURROUND_OUTPUT_NEVER,
625 ENCODED_SURROUND_OUTPUT_ALWAYS,
626 ENCODED_SURROUND_OUTPUT_MANUAL
627 })
628 @Retention(RetentionPolicy.SOURCE)
629 public @interface EncodedSurroundOutputMode {}
630
631 /**
Kriti Dang98fdb262021-04-01 13:26:00 +0200632 * The mode for surround sound formats is unknown.
633 */
634 public static final int ENCODED_SURROUND_OUTPUT_UNKNOWN = -1;
635
636 /**
Kriti Dang527e66c2021-03-04 10:37:22 +0100637 * The surround sound formats are available for use if they are detected. This is the default
638 * mode.
639 */
640 public static final int ENCODED_SURROUND_OUTPUT_AUTO = 0;
641
642 /**
643 * The surround sound formats are NEVER available, even if they are detected by the hardware.
644 * Those formats will not be reported.
645 */
646 public static final int ENCODED_SURROUND_OUTPUT_NEVER = 1;
647
648 /**
649 * The surround sound formats are ALWAYS available, even if they are not detected by the
650 * hardware. Those formats will be reported as part of the HDMI output capability.
651 * Applications are then free to use either PCM or encoded output.
652 */
653 public static final int ENCODED_SURROUND_OUTPUT_ALWAYS = 2;
654
655 /**
656 * Surround sound formats are available according to the choice of user, even if they are not
657 * detected by the hardware. Those formats will be reported as part of the HDMI output
658 * capability. Applications are then free to use either PCM or encoded output.
659 */
660 public static final int ENCODED_SURROUND_OUTPUT_MANUAL = 3;
661
662 /** @hide */
Jin Seok Parkfc9db6c2021-02-26 02:37:20 +0900663 @IntDef(flag = true, prefix = "FLAG", value = {
Jin Seok Park5ccb6cc2021-02-05 01:47:13 +0900664 FLAG_SHOW_UI,
665 FLAG_ALLOW_RINGER_MODES,
666 FLAG_PLAY_SOUND,
667 FLAG_REMOVE_SOUND_AND_VIBRATE,
668 FLAG_VIBRATE,
669 FLAG_FIXED_VOLUME,
670 FLAG_BLUETOOTH_ABS_VOLUME,
671 FLAG_SHOW_SILENT_HINT,
672 FLAG_HDMI_SYSTEM_AUDIO_VOLUME,
673 FLAG_ACTIVE_MEDIA_ONLY,
674 FLAG_SHOW_UI_WARNINGS,
675 FLAG_SHOW_VIBRATE_HINT,
676 FLAG_FROM_KEY,
Yan Han7d419822022-01-31 19:10:16 +0100677 FLAG_ABSOLUTE_VOLUME,
Jin Seok Park5ccb6cc2021-02-05 01:47:13 +0900678 })
679 @Retention(RetentionPolicy.SOURCE)
680 public @interface Flags {}
681
Hyundo Moonca0080d2018-12-26 16:16:55 +0900682 // The iterator of TreeMap#entrySet() returns the entries in ascending key order.
683 private static final TreeMap<Integer, String> FLAG_NAMES = new TreeMap<>();
684
685 static {
686 FLAG_NAMES.put(FLAG_SHOW_UI, "FLAG_SHOW_UI");
687 FLAG_NAMES.put(FLAG_ALLOW_RINGER_MODES, "FLAG_ALLOW_RINGER_MODES");
688 FLAG_NAMES.put(FLAG_PLAY_SOUND, "FLAG_PLAY_SOUND");
689 FLAG_NAMES.put(FLAG_REMOVE_SOUND_AND_VIBRATE, "FLAG_REMOVE_SOUND_AND_VIBRATE");
690 FLAG_NAMES.put(FLAG_VIBRATE, "FLAG_VIBRATE");
691 FLAG_NAMES.put(FLAG_FIXED_VOLUME, "FLAG_FIXED_VOLUME");
692 FLAG_NAMES.put(FLAG_BLUETOOTH_ABS_VOLUME, "FLAG_BLUETOOTH_ABS_VOLUME");
693 FLAG_NAMES.put(FLAG_SHOW_SILENT_HINT, "FLAG_SHOW_SILENT_HINT");
694 FLAG_NAMES.put(FLAG_HDMI_SYSTEM_AUDIO_VOLUME, "FLAG_HDMI_SYSTEM_AUDIO_VOLUME");
695 FLAG_NAMES.put(FLAG_ACTIVE_MEDIA_ONLY, "FLAG_ACTIVE_MEDIA_ONLY");
696 FLAG_NAMES.put(FLAG_SHOW_UI_WARNINGS, "FLAG_SHOW_UI_WARNINGS");
697 FLAG_NAMES.put(FLAG_SHOW_VIBRATE_HINT, "FLAG_SHOW_VIBRATE_HINT");
698 FLAG_NAMES.put(FLAG_FROM_KEY, "FLAG_FROM_KEY");
Yan Han7d419822022-01-31 19:10:16 +0100699 FLAG_NAMES.put(FLAG_ABSOLUTE_VOLUME, "FLAG_ABSOLUTE_VOLUME");
Hyundo Moonca0080d2018-12-26 16:16:55 +0900700 }
John Spurlock661f2cf42014-11-17 10:29:10 -0500701
702 /** @hide */
703 public static String flagsToString(int flags) {
704 final StringBuilder sb = new StringBuilder();
Hyundo Moonca0080d2018-12-26 16:16:55 +0900705 for (Map.Entry<Integer, String> entry : FLAG_NAMES.entrySet()) {
706 final int flag = entry.getKey();
John Spurlock661f2cf42014-11-17 10:29:10 -0500707 if ((flags & flag) != 0) {
708 if (sb.length() > 0) {
709 sb.append(',');
710 }
Hyundo Moonca0080d2018-12-26 16:16:55 +0900711 sb.append(entry.getValue());
John Spurlock661f2cf42014-11-17 10:29:10 -0500712 flags &= ~flag;
713 }
714 }
715 if (flags != 0) {
716 if (sb.length() > 0) {
717 sb.append(',');
718 }
719 sb.append(flags);
720 }
721 return sb.toString();
722 }
723
724 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800725 * Ringer mode that will be silent and will not vibrate. (This overrides the
726 * vibrate setting.)
727 *
728 * @see #setRingerMode(int)
729 * @see #getRingerMode()
730 */
731 public static final int RINGER_MODE_SILENT = 0;
732
733 /**
734 * Ringer mode that will be silent and will vibrate. (This will cause the
735 * phone ringer to always vibrate, but the notification vibrate to only
736 * vibrate if set.)
737 *
738 * @see #setRingerMode(int)
739 * @see #getRingerMode()
740 */
741 public static final int RINGER_MODE_VIBRATE = 1;
742
743 /**
744 * Ringer mode that may be audible and may vibrate. It will be audible if
745 * the volume before changing out of this mode was audible. It will vibrate
746 * if the vibrate setting is on.
747 *
748 * @see #setRingerMode(int)
749 * @see #getRingerMode()
750 */
751 public static final int RINGER_MODE_NORMAL = 2;
752
John Spurlock97559372014-10-24 16:27:36 -0400753 /**
754 * Maximum valid ringer mode value. Values must start from 0 and be contiguous.
755 * @hide
756 */
757 public static final int RINGER_MODE_MAX = RINGER_MODE_NORMAL;
Eric Laurent72668b22011-07-19 16:04:27 -0700758
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 /**
760 * Vibrate type that corresponds to the ringer.
761 *
762 * @see #setVibrateSetting(int, int)
763 * @see #getVibrateSetting(int)
764 * @see #shouldVibrate(int)
Eric Laurentcd1cd732012-05-01 11:23:07 -0700765 * @deprecated Applications should maintain their own vibrate policy based on
766 * current ringer mode that can be queried via {@link #getRingerMode()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800767 */
768 public static final int VIBRATE_TYPE_RINGER = 0;
769
770 /**
771 * Vibrate type that corresponds to notifications.
772 *
773 * @see #setVibrateSetting(int, int)
774 * @see #getVibrateSetting(int)
775 * @see #shouldVibrate(int)
Eric Laurentcd1cd732012-05-01 11:23:07 -0700776 * @deprecated Applications should maintain their own vibrate policy based on
777 * current ringer mode that can be queried via {@link #getRingerMode()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800778 */
779 public static final int VIBRATE_TYPE_NOTIFICATION = 1;
780
781 /**
782 * Vibrate setting that suggests to never vibrate.
783 *
784 * @see #setVibrateSetting(int, int)
785 * @see #getVibrateSetting(int)
Eric Laurentcd1cd732012-05-01 11:23:07 -0700786 * @deprecated Applications should maintain their own vibrate policy based on
787 * current ringer mode that can be queried via {@link #getRingerMode()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800788 */
789 public static final int VIBRATE_SETTING_OFF = 0;
790
791 /**
792 * Vibrate setting that suggests to vibrate when possible.
793 *
794 * @see #setVibrateSetting(int, int)
795 * @see #getVibrateSetting(int)
Eric Laurentcd1cd732012-05-01 11:23:07 -0700796 * @deprecated Applications should maintain their own vibrate policy based on
797 * current ringer mode that can be queried via {@link #getRingerMode()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798 */
799 public static final int VIBRATE_SETTING_ON = 1;
800
801 /**
802 * Vibrate setting that suggests to only vibrate when in the vibrate ringer
803 * mode.
804 *
805 * @see #setVibrateSetting(int, int)
806 * @see #getVibrateSetting(int)
Eric Laurentcd1cd732012-05-01 11:23:07 -0700807 * @deprecated Applications should maintain their own vibrate policy based on
808 * current ringer mode that can be queried via {@link #getRingerMode()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 */
810 public static final int VIBRATE_SETTING_ONLY_SILENT = 2;
811
812 /**
813 * Suggests using the default stream type. This may not be used in all
814 * places a stream type is needed.
815 */
816 public static final int USE_DEFAULT_STREAM_TYPE = Integer.MIN_VALUE;
817
818 private static IAudioService sService;
819
820 /**
821 * @hide
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800822 * For test purposes only, will throw NPE with some methods that require a Context.
823 */
Mathew Inwood8e742f92020-10-27 11:47:29 +0000824 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800825 public AudioManager() {
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800826 }
827
828 /**
829 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800830 */
Mathew Inwood31a792a2018-08-17 08:54:26 +0100831 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 public AudioManager(Context context) {
Marco Nelissen29f16932015-04-17 09:50:56 -0700833 setContext(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800834 }
835
Marco Nelissen29f16932015-04-17 09:50:56 -0700836 private Context getContext() {
837 if (mApplicationContext == null) {
838 setContext(mOriginalContext);
839 }
840 if (mApplicationContext != null) {
841 return mApplicationContext;
842 }
843 return mOriginalContext;
844 }
845
846 private void setContext(Context context) {
847 mApplicationContext = context.getApplicationContext();
848 if (mApplicationContext != null) {
849 mOriginalContext = null;
850 } else {
851 mOriginalContext = context;
852 }
jiabin0f3339c2021-07-09 11:50:07 -0700853 sContext = new WeakReference<>(context);
Marco Nelissen29f16932015-04-17 09:50:56 -0700854 }
855
Mathew Inwood31a792a2018-08-17 08:54:26 +0100856 @UnsupportedAppUsage
Jean-Michel Trivie9dd14e2021-07-12 11:10:18 -0700857 static IAudioService getService()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800858 {
859 if (sService != null) {
860 return sService;
861 }
862 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
863 sService = IAudioService.Stub.asInterface(b);
864 return sService;
865 }
866
Jan Sebechlebskyfb48c022022-12-16 16:26:47 +0100867 private VirtualDeviceManager getVirtualDeviceManager() {
868 if (mVirtualDeviceManager != null) {
869 return mVirtualDeviceManager;
870 }
871 mVirtualDeviceManager = getContext().getSystemService(VirtualDeviceManager.class);
872 return mVirtualDeviceManager;
873 }
874
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800875 /**
Jean-Michel Trivia83487e2013-09-17 21:19:30 -0700876 * Sends a simulated key event for a media button.
877 * To simulate a key press, you must first send a KeyEvent built with a
878 * {@link KeyEvent#ACTION_DOWN} action, then another event with the {@link KeyEvent#ACTION_UP}
879 * action.
880 * <p>The key event will be sent to the current media key event consumer which registered with
881 * {@link AudioManager#registerMediaButtonEventReceiver(PendingIntent)}.
882 * @param keyEvent a {@link KeyEvent} instance whose key code is one of
883 * {@link KeyEvent#KEYCODE_MUTE},
884 * {@link KeyEvent#KEYCODE_HEADSETHOOK},
885 * {@link KeyEvent#KEYCODE_MEDIA_PLAY},
886 * {@link KeyEvent#KEYCODE_MEDIA_PAUSE},
887 * {@link KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE},
888 * {@link KeyEvent#KEYCODE_MEDIA_STOP},
889 * {@link KeyEvent#KEYCODE_MEDIA_NEXT},
890 * {@link KeyEvent#KEYCODE_MEDIA_PREVIOUS},
891 * {@link KeyEvent#KEYCODE_MEDIA_REWIND},
892 * {@link KeyEvent#KEYCODE_MEDIA_RECORD},
893 * {@link KeyEvent#KEYCODE_MEDIA_FAST_FORWARD},
894 * {@link KeyEvent#KEYCODE_MEDIA_CLOSE},
895 * {@link KeyEvent#KEYCODE_MEDIA_EJECT},
896 * or {@link KeyEvent#KEYCODE_MEDIA_AUDIO_TRACK}.
Jean-Michel Trivi7ddd2262013-09-01 18:06:45 -0700897 */
Jean-Michel Trivia83487e2013-09-17 21:19:30 -0700898 public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
Marco Nelissen29f16932015-04-17 09:50:56 -0700899 MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
RoboErik430fc482014-06-12 15:49:20 -0700900 helper.sendMediaButtonEvent(keyEvent, false);
Jean-Michel Trivi7ddd2262013-09-01 18:06:45 -0700901 }
902
903 /**
904 * @hide
Joe Onorato86f67862010-11-05 18:57:34 -0700905 */
Mike Lockwoodce952c82011-11-14 10:47:42 -0800906 public void preDispatchKeyEvent(KeyEvent event, int stream) {
Joe Onorato86f67862010-11-05 18:57:34 -0700907 /*
908 * If the user hits another key within the play sound delay, then
909 * cancel the sound
910 */
Mike Lockwoodce952c82011-11-14 10:47:42 -0800911 int keyCode = event.getKeyCode();
Joe Onorato86f67862010-11-05 18:57:34 -0700912 if (keyCode != KeyEvent.KEYCODE_VOLUME_DOWN && keyCode != KeyEvent.KEYCODE_VOLUME_UP
913 && keyCode != KeyEvent.KEYCODE_VOLUME_MUTE
John Spurlock61560172015-02-06 19:46:04 -0500914 && mVolumeKeyUpTime + AudioSystem.PLAY_SOUND_DELAY > SystemClock.uptimeMillis()) {
Joe Onorato86f67862010-11-05 18:57:34 -0700915 /*
916 * The user has hit another key during the delay (e.g., 300ms)
917 * since the last volume key up, so cancel any sounds.
918 */
John Spurlockee5ad722015-03-03 16:17:21 -0500919 adjustSuggestedStreamVolume(ADJUST_SAME,
920 stream, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
Joe Onorato86f67862010-11-05 18:57:34 -0700921 }
922 }
923
924 /**
Eric Laurentba207e72014-05-15 17:08:16 -0700925 * Indicates if the device implements a fixed volume policy.
926 * <p>Some devices may not have volume control and may operate at a fixed volume,
927 * and may not enable muting or changing the volume of audio streams.
928 * This method will return true on such devices.
929 * <p>The following APIs have no effect when volume is fixed:
930 * <ul>
931 * <li> {@link #adjustVolume(int, int)}
932 * <li> {@link #adjustSuggestedStreamVolume(int, int, int)}
933 * <li> {@link #adjustStreamVolume(int, int, int)}
934 * <li> {@link #setStreamVolume(int, int, int)}
935 * <li> {@link #setRingerMode(int)}
936 * <li> {@link #setStreamSolo(int, boolean)}
937 * <li> {@link #setStreamMute(int, boolean)}
938 * </ul>
939 */
940 public boolean isVolumeFixed() {
Jean-Michel Trivi547d2632021-10-26 15:40:58 -0700941 boolean res = false;
942 try {
943 res = getService().isVolumeFixed();
944 } catch (RemoteException e) {
945 Log.e(TAG, "Error querying isVolumeFixed", e);
Jean-Michel Trivia0513082020-09-22 18:43:53 -0700946 }
Jean-Michel Trivi547d2632021-10-26 15:40:58 -0700947 return res;
Eric Laurentba207e72014-05-15 17:08:16 -0700948 }
949
950 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800951 * Adjusts the volume of a particular stream by one step in a direction.
Jean-Michel Trivifccb25d2009-09-15 16:06:10 -0700952 * <p>
953 * This method should only be used by applications that replace the platform-wide
954 * management of audio settings or the main telephony application.
Jean-Michel Trivi59773622018-06-19 17:17:57 -0700955 * <p>This method has no effect if the device implements a fixed volume policy
956 * as indicated by {@link #isVolumeFixed()}.
957 * <p>From N onward, ringer mode adjustments that would toggle Do Not Disturb are not allowed
958 * unless the app has been granted Do Not Disturb Access.
959 * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800960 *
961 * @param streamType The stream type to adjust. One of {@link #STREAM_VOICE_CALL},
Jean-Michel Trivi8e0bc782016-11-25 10:22:23 -0800962 * {@link #STREAM_SYSTEM}, {@link #STREAM_RING}, {@link #STREAM_MUSIC},
963 * {@link #STREAM_ALARM} or {@link #STREAM_ACCESSIBILITY}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800964 * @param direction The direction to adjust the volume. One of
965 * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
966 * {@link #ADJUST_SAME}.
967 * @param flags One or more flags.
968 * @see #adjustVolume(int, int)
969 * @see #setStreamVolume(int, int, int)
Jean-Michel Trivi59773622018-06-19 17:17:57 -0700970 * @throws SecurityException if the adjustment triggers a Do Not Disturb change
971 * and the caller is not granted notification policy access.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800972 */
973 public void adjustStreamVolume(int streamType, int direction, int flags) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -0700974 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800975 try {
John Wu4f7e5102021-06-22 17:29:11 +0000976 service.adjustStreamVolumeWithAttribution(streamType, direction, flags,
977 getContext().getOpPackageName(), getContext().getAttributionTag());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800978 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -0700979 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800980 }
981 }
982
983 /**
984 * Adjusts the volume of the most relevant stream. For example, if a call is
985 * active, it will have the highest priority regardless of if the in-call
986 * screen is showing. Another example, if music is playing in the background
987 * and a call is not active, the music stream will be adjusted.
Jean-Michel Trivifccb25d2009-09-15 16:06:10 -0700988 * <p>
RoboErik4197cb62015-01-21 15:45:32 -0800989 * This method should only be used by applications that replace the
990 * platform-wide management of audio settings or the main telephony
991 * application.
992 * <p>
993 * This method has no effect if the device implements a fixed volume policy
Eric Laurentba207e72014-05-15 17:08:16 -0700994 * as indicated by {@link #isVolumeFixed()}.
RoboErik4197cb62015-01-21 15:45:32 -0800995 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800996 * @param direction The direction to adjust the volume. One of
RoboErik4197cb62015-01-21 15:45:32 -0800997 * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE},
998 * {@link #ADJUST_SAME}, {@link #ADJUST_MUTE},
999 * {@link #ADJUST_UNMUTE}, or {@link #ADJUST_TOGGLE_MUTE}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001000 * @param flags One or more flags.
1001 * @see #adjustSuggestedStreamVolume(int, int, int)
1002 * @see #adjustStreamVolume(int, int, int)
1003 * @see #setStreamVolume(int, int, int)
Eric Laurentba207e72014-05-15 17:08:16 -07001004 * @see #isVolumeFixed()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001005 */
1006 public void adjustVolume(int direction, int flags) {
Marco Nelissen8dc50412015-04-28 09:42:54 -07001007 MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
John Spurlockee5ad722015-03-03 16:17:21 -05001008 helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001009 }
1010
1011 /**
1012 * Adjusts the volume of the most relevant stream, or the given fallback
1013 * stream.
Jean-Michel Trivifccb25d2009-09-15 16:06:10 -07001014 * <p>
RoboErik4197cb62015-01-21 15:45:32 -08001015 * This method should only be used by applications that replace the
1016 * platform-wide management of audio settings or the main telephony
1017 * application.
1018 * <p>
1019 * This method has no effect if the device implements a fixed volume policy
Eric Laurentba207e72014-05-15 17:08:16 -07001020 * as indicated by {@link #isVolumeFixed()}.
RoboErik4197cb62015-01-21 15:45:32 -08001021 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001022 * @param direction The direction to adjust the volume. One of
RoboErik4197cb62015-01-21 15:45:32 -08001023 * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE},
1024 * {@link #ADJUST_SAME}, {@link #ADJUST_MUTE},
1025 * {@link #ADJUST_UNMUTE}, or {@link #ADJUST_TOGGLE_MUTE}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001026 * @param suggestedStreamType The stream type that will be used if there
RoboErik4197cb62015-01-21 15:45:32 -08001027 * isn't a relevant stream. {@link #USE_DEFAULT_STREAM_TYPE} is
1028 * valid here.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001029 * @param flags One or more flags.
1030 * @see #adjustVolume(int, int)
1031 * @see #adjustStreamVolume(int, int, int)
1032 * @see #setStreamVolume(int, int, int)
Eric Laurentba207e72014-05-15 17:08:16 -07001033 * @see #isVolumeFixed()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001034 */
1035 public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
Marco Nelissen8dc50412015-04-28 09:42:54 -07001036 MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
John Spurlockee5ad722015-03-03 16:17:21 -05001037 helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags);
Mike Lockwoodcbdb49d2011-10-20 12:54:05 -04001038 }
1039
John Spurlockee5ad722015-03-03 16:17:21 -05001040 /** @hide */
Mathew Inwood31a792a2018-08-17 08:54:26 +01001041 @UnsupportedAppUsage
Jean-Michel Trivi582ccf62019-11-01 11:07:09 -07001042 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
John Spurlockee5ad722015-03-03 16:17:21 -05001043 public void setMasterMute(boolean mute, int flags) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001044 final IAudioService service = getService();
Mike Lockwoodcbdb49d2011-10-20 12:54:05 -04001045 try {
Kenny Guy70e0c582015-06-30 19:18:28 +01001046 service.setMasterMute(mute, flags, getContext().getOpPackageName(),
John Wu4f7e5102021-06-22 17:29:11 +00001047 UserHandle.getCallingUserId(), getContext().getAttributionTag());
Mike Lockwoodcbdb49d2011-10-20 12:54:05 -04001048 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001049 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001050 }
1051 }
1052
1053 /**
1054 * Returns the current ringtone mode.
1055 *
1056 * @return The current ringtone mode, one of {@link #RINGER_MODE_NORMAL},
1057 * {@link #RINGER_MODE_SILENT}, or {@link #RINGER_MODE_VIBRATE}.
1058 * @see #setRingerMode(int)
1059 */
1060 public int getRingerMode() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001061 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001062 try {
John Spurlock661f2cf42014-11-17 10:29:10 -05001063 return service.getRingerModeExternal();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001064 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001065 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001066 }
1067 }
1068
1069 /**
Lais Andrade724d0cd2021-11-03 19:46:21 +00001070 * Returns the current user setting for ramping ringer on incoming phone call ringtone.
1071 *
1072 * @return true if the incoming phone call ringtone is configured to gradually increase its
1073 * volume, false otherwise.
1074 */
1075 public boolean isRampingRingerEnabled() {
1076 return Settings.System.getInt(getContext().getContentResolver(),
1077 Settings.System.APPLY_RAMPING_RINGER, 0) != 0;
1078 }
1079
1080 /**
1081 * Sets the flag for enabling ramping ringer on incoming phone call ringtone.
1082 *
1083 * @see #isRampingRingerEnabled()
1084 * @hide
1085 */
1086 @TestApi
1087 public void setRampingRingerEnabled(boolean enabled) {
1088 Settings.System.putInt(getContext().getContentResolver(),
1089 Settings.System.APPLY_RAMPING_RINGER, enabled ? 1 : 0);
1090 }
1091
1092 /**
Eric Laurent72668b22011-07-19 16:04:27 -07001093 * Checks valid ringer mode values.
1094 *
1095 * @return true if the ringer mode indicated is valid, false otherwise.
1096 *
1097 * @see #setRingerMode(int)
1098 * @hide
1099 */
Mathew Inwood8e742f92020-10-27 11:47:29 +00001100 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Eric Laurent72668b22011-07-19 16:04:27 -07001101 public static boolean isValidRingerMode(int ringerMode) {
1102 if (ringerMode < 0 || ringerMode > RINGER_MODE_MAX) {
1103 return false;
1104 }
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001105 final IAudioService service = getService();
John Spurlock97559372014-10-24 16:27:36 -04001106 try {
1107 return service.isValidRingerMode(ringerMode);
1108 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001109 throw e.rethrowFromSystemServer();
John Spurlock97559372014-10-24 16:27:36 -04001110 }
Eric Laurent72668b22011-07-19 16:04:27 -07001111 }
1112
1113 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001114 * Returns the maximum volume index for a particular stream.
1115 *
1116 * @param streamType The stream type whose maximum volume index is returned.
1117 * @return The maximum valid volume index for the stream.
1118 * @see #getStreamVolume(int)
1119 */
1120 public int getStreamMaxVolume(int streamType) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001121 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001122 try {
John Spurlockee5ad722015-03-03 16:17:21 -05001123 return service.getStreamMaxVolume(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001124 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001125 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001126 }
1127 }
1128
1129 /**
John Spurlockb6e19e32015-03-10 21:33:44 -04001130 * Returns the minimum volume index for a particular stream.
Jean-Michel Trivi8e7aca32017-11-28 18:12:38 -08001131 * @param streamType The stream type whose minimum volume index is returned. Must be one of
1132 * {@link #STREAM_VOICE_CALL}, {@link #STREAM_SYSTEM},
1133 * {@link #STREAM_RING}, {@link #STREAM_MUSIC}, {@link #STREAM_ALARM},
1134 * {@link #STREAM_NOTIFICATION}, {@link #STREAM_DTMF} or {@link #STREAM_ACCESSIBILITY}.
1135 * @return The minimum valid volume index for the stream.
1136 * @see #getStreamVolume(int)
1137 */
1138 public int getStreamMinVolume(int streamType) {
1139 if (!isPublicStreamType(streamType)) {
1140 throw new IllegalArgumentException("Invalid stream type " + streamType);
1141 }
1142 return getStreamMinVolumeInt(streamType);
1143 }
1144
1145 /**
1146 * @hide
1147 * Same as {@link #getStreamMinVolume(int)} but without the check on the public stream type.
John Spurlockb6e19e32015-03-10 21:33:44 -04001148 * @param streamType The stream type whose minimum volume index is returned.
1149 * @return The minimum valid volume index for the stream.
1150 * @see #getStreamVolume(int)
John Spurlockb6e19e32015-03-10 21:33:44 -04001151 */
Paul McLeand6f87c82021-03-31 13:02:41 -06001152 @TestApi
Jean-Michel Trivi8e7aca32017-11-28 18:12:38 -08001153 public int getStreamMinVolumeInt(int streamType) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001154 final IAudioService service = getService();
John Spurlockb6e19e32015-03-10 21:33:44 -04001155 try {
1156 return service.getStreamMinVolume(streamType);
1157 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001158 throw e.rethrowFromSystemServer();
John Spurlockb6e19e32015-03-10 21:33:44 -04001159 }
1160 }
1161
1162 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001163 * Returns the current volume index for a particular stream.
1164 *
1165 * @param streamType The stream type whose volume index is returned.
1166 * @return The current volume index for the stream.
1167 * @see #getStreamMaxVolume(int)
1168 * @see #setStreamVolume(int, int, int)
1169 */
1170 public int getStreamVolume(int streamType) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001171 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001172 try {
John Spurlockee5ad722015-03-03 16:17:21 -05001173 return service.getStreamVolume(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001174 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001175 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001176 }
1177 }
1178
Jean-Michel Trivi8e7aca32017-11-28 18:12:38 -08001179 // keep in sync with frameworks/av/services/audiopolicy/common/include/Volume.h
1180 private static final float VOLUME_MIN_DB = -758.0f;
1181
1182 /** @hide */
1183 @IntDef(flag = false, prefix = "STREAM", value = {
1184 STREAM_VOICE_CALL,
1185 STREAM_SYSTEM,
1186 STREAM_RING,
1187 STREAM_MUSIC,
1188 STREAM_ALARM,
1189 STREAM_NOTIFICATION,
1190 STREAM_DTMF,
1191 STREAM_ACCESSIBILITY }
1192 )
1193 @Retention(RetentionPolicy.SOURCE)
1194 public @interface PublicStreamTypes {}
1195
1196 /**
1197 * Returns the volume in dB (decibel) for the given stream type at the given volume index, on
1198 * the given type of audio output device.
1199 * @param streamType stream type for which the volume is queried.
1200 * @param index the volume index for which the volume is queried. The index value must be
1201 * between the minimum and maximum index values for the given stream type (see
1202 * {@link #getStreamMinVolume(int)} and {@link #getStreamMaxVolume(int)}).
1203 * @param deviceType the type of audio output device for which volume is queried.
1204 * @return a volume expressed in dB.
1205 * A negative value indicates the audio signal is attenuated. A typical maximum value
1206 * at the maximum volume index is 0 dB (no attenuation nor amplification). Muting is
1207 * reflected by a value of {@link Float#NEGATIVE_INFINITY}.
1208 */
1209 public float getStreamVolumeDb(@PublicStreamTypes int streamType, int index,
1210 @AudioDeviceInfo.AudioDeviceTypeOut int deviceType) {
1211 if (!isPublicStreamType(streamType)) {
1212 throw new IllegalArgumentException("Invalid stream type " + streamType);
1213 }
1214 if (index > getStreamMaxVolume(streamType) || index < getStreamMinVolume(streamType)) {
1215 throw new IllegalArgumentException("Invalid stream volume index " + index);
1216 }
1217 if (!AudioDeviceInfo.isValidAudioDeviceTypeOut(deviceType)) {
1218 throw new IllegalArgumentException("Invalid audio output device type " + deviceType);
1219 }
1220 final float gain = AudioSystem.getStreamVolumeDB(streamType, index,
1221 AudioDeviceInfo.convertDeviceTypeToInternalDevice(deviceType));
1222 if (gain <= VOLUME_MIN_DB) {
1223 return Float.NEGATIVE_INFINITY;
1224 } else {
1225 return gain;
1226 }
1227 }
1228
Jean-Michel Trivi283a4c72022-09-28 21:46:51 +00001229 /**
1230 * @hide
1231 * Checks whether a stream type is part of the public SDK
1232 * @param streamType
1233 * @return true if the stream type is available in SDK
1234 */
1235 public static boolean isPublicStreamType(int streamType) {
Jean-Michel Trivi8e7aca32017-11-28 18:12:38 -08001236 switch (streamType) {
1237 case STREAM_VOICE_CALL:
1238 case STREAM_SYSTEM:
1239 case STREAM_RING:
1240 case STREAM_MUSIC:
1241 case STREAM_ALARM:
1242 case STREAM_NOTIFICATION:
1243 case STREAM_DTMF:
1244 case STREAM_ACCESSIBILITY:
1245 return true;
1246 default:
1247 return false;
1248 }
1249 }
1250
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001251 /**
Eric Laurent25101b02011-02-02 09:33:30 -08001252 * Get last audible volume before stream was muted.
1253 *
1254 * @hide
1255 */
wescandee178f8f2021-10-19 20:15:09 +02001256 @SystemApi
1257 @RequiresPermission("android.permission.QUERY_AUDIO_STATE")
Eric Laurent25101b02011-02-02 09:33:30 -08001258 public int getLastAudibleStreamVolume(int streamType) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001259 final IAudioService service = getService();
Eric Laurent25101b02011-02-02 09:33:30 -08001260 try {
John Spurlockee5ad722015-03-03 16:17:21 -05001261 return service.getLastAudibleStreamVolume(streamType);
Eric Laurent25101b02011-02-02 09:33:30 -08001262 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001263 throw e.rethrowFromSystemServer();
Eric Laurent25101b02011-02-02 09:33:30 -08001264 }
1265 }
1266
1267 /**
Eric Laurent6d517662012-04-23 18:42:39 -07001268 * Get the stream type whose volume is driving the UI sounds volume.
1269 * UI sounds are screen lock/unlock, camera shutter, key clicks...
John Spurlock4f0f1202014-08-05 13:28:33 -04001270 * It is assumed that this stream type is also tied to ringer mode changes.
Eric Laurent6d517662012-04-23 18:42:39 -07001271 * @hide
1272 */
John Spurlockee5ad722015-03-03 16:17:21 -05001273 public int getUiSoundsStreamType() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001274 final IAudioService service = getService();
Eric Laurent6d517662012-04-23 18:42:39 -07001275 try {
John Spurlockee5ad722015-03-03 16:17:21 -05001276 return service.getUiSoundsStreamType();
Eric Laurent6d517662012-04-23 18:42:39 -07001277 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001278 throw e.rethrowFromSystemServer();
Eric Laurent6d517662012-04-23 18:42:39 -07001279 }
1280 }
1281
1282 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001283 * Sets the ringer mode.
1284 * <p>
1285 * Silent mode will mute the volume and will not vibrate. Vibrate mode will
1286 * mute the volume and vibrate. Normal mode will be audible and may vibrate
1287 * according to user settings.
Eric Laurentba207e72014-05-15 17:08:16 -07001288 * <p>This method has no effect if the device implements a fixed volume policy
1289 * as indicated by {@link #isVolumeFixed()}.
Julia Reynolds48034f82016-03-09 10:15:16 -05001290 * * <p>From N onward, ringer mode adjustments that would toggle Do Not Disturb are not allowed
1291 * unless the app has been granted Do Not Disturb Access.
1292 * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001293 * @param ringerMode The ringer mode, one of {@link #RINGER_MODE_NORMAL},
1294 * {@link #RINGER_MODE_SILENT}, or {@link #RINGER_MODE_VIBRATE}.
1295 * @see #getRingerMode()
Eric Laurentba207e72014-05-15 17:08:16 -07001296 * @see #isVolumeFixed()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001297 */
1298 public void setRingerMode(int ringerMode) {
Eric Laurent72668b22011-07-19 16:04:27 -07001299 if (!isValidRingerMode(ringerMode)) {
1300 return;
1301 }
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001302 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001303 try {
Marco Nelissen29f16932015-04-17 09:50:56 -07001304 service.setRingerModeExternal(ringerMode, getContext().getOpPackageName());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001305 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001306 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001307 }
1308 }
1309
1310 /**
1311 * Sets the volume index for a particular stream.
Eric Laurentba207e72014-05-15 17:08:16 -07001312 * <p>This method has no effect if the device implements a fixed volume policy
1313 * as indicated by {@link #isVolumeFixed()}.
Julia Reynolds48034f82016-03-09 10:15:16 -05001314 * <p>From N onward, volume adjustments that would toggle Do Not Disturb are not allowed unless
1315 * the app has been granted Do Not Disturb Access.
1316 * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001317 * @param streamType The stream whose volume index should be set.
1318 * @param index The volume index to set. See
1319 * {@link #getStreamMaxVolume(int)} for the largest valid value.
1320 * @param flags One or more flags.
1321 * @see #getStreamMaxVolume(int)
1322 * @see #getStreamVolume(int)
Eric Laurentba207e72014-05-15 17:08:16 -07001323 * @see #isVolumeFixed()
Jean-Michel Trivi59773622018-06-19 17:17:57 -07001324 * @throws SecurityException if the volume change triggers a Do Not Disturb change
1325 * and the caller is not granted notification policy access.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001326 */
1327 public void setStreamVolume(int streamType, int index, int flags) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001328 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001329 try {
John Wu4f7e5102021-06-22 17:29:11 +00001330 service.setStreamVolumeWithAttribution(streamType, index, flags,
1331 getContext().getOpPackageName(), getContext().getAttributionTag());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001332 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001333 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001334 }
1335 }
1336
1337 /**
François Gaffie9c362102018-09-21 17:43:52 +02001338 * Sets the volume index for a particular {@link AudioAttributes}.
1339 * @param attr The {@link AudioAttributes} whose volume index should be set.
1340 * @param index The volume index to set. See
1341 * {@link #getMaxVolumeIndexForAttributes(AudioAttributes)} for the largest valid value
1342 * {@link #getMinVolumeIndexForAttributes(AudioAttributes)} for the lowest valid value.
1343 * @param flags One or more flags.
1344 * @see #getMaxVolumeIndexForAttributes(AudioAttributes)
1345 * @see #getMinVolumeIndexForAttributes(AudioAttributes)
1346 * @see #isVolumeFixed()
1347 * @hide
1348 */
1349 @SystemApi
1350 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
1351 public void setVolumeIndexForAttributes(@NonNull AudioAttributes attr, int index, int flags) {
1352 Preconditions.checkNotNull(attr, "attr must not be null");
1353 final IAudioService service = getService();
1354 try {
1355 service.setVolumeIndexForAttributes(attr, index, flags,
John Wu4f7e5102021-06-22 17:29:11 +00001356 getContext().getOpPackageName(), getContext().getAttributionTag());
François Gaffie9c362102018-09-21 17:43:52 +02001357 } catch (RemoteException e) {
1358 throw e.rethrowFromSystemServer();
1359 }
1360 }
1361
1362 /**
1363 * Returns the current volume index for a particular {@link AudioAttributes}.
1364 *
1365 * @param attr The {@link AudioAttributes} whose volume index is returned.
1366 * @return The current volume index for the stream.
1367 * @see #getMaxVolumeIndexForAttributes(AudioAttributes)
1368 * @see #getMinVolumeIndexForAttributes(AudioAttributes)
1369 * @see #setVolumeForAttributes(AudioAttributes, int, int)
1370 * @hide
1371 */
1372 @SystemApi
Hayden Gomes695f8022019-04-11 10:44:18 -07001373 @IntRange(from = 0)
François Gaffie9c362102018-09-21 17:43:52 +02001374 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
1375 public int getVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
1376 Preconditions.checkNotNull(attr, "attr must not be null");
1377 final IAudioService service = getService();
1378 try {
1379 return service.getVolumeIndexForAttributes(attr);
1380 } catch (RemoteException e) {
1381 throw e.rethrowFromSystemServer();
1382 }
1383 }
1384
1385 /**
1386 * Returns the maximum volume index for a particular {@link AudioAttributes}.
1387 *
1388 * @param attr The {@link AudioAttributes} whose maximum volume index is returned.
1389 * @return The maximum valid volume index for the {@link AudioAttributes}.
1390 * @see #getVolumeIndexForAttributes(AudioAttributes)
1391 * @hide
1392 */
1393 @SystemApi
Hayden Gomes695f8022019-04-11 10:44:18 -07001394 @IntRange(from = 0)
François Gaffie9c362102018-09-21 17:43:52 +02001395 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
1396 public int getMaxVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
1397 Preconditions.checkNotNull(attr, "attr must not be null");
1398 final IAudioService service = getService();
1399 try {
1400 return service.getMaxVolumeIndexForAttributes(attr);
1401 } catch (RemoteException e) {
1402 throw e.rethrowFromSystemServer();
1403 }
1404 }
1405
1406 /**
1407 * Returns the minimum volume index for a particular {@link AudioAttributes}.
1408 *
1409 * @param attr The {@link AudioAttributes} whose minimum volume index is returned.
1410 * @return The minimum valid volume index for the {@link AudioAttributes}.
1411 * @see #getVolumeIndexForAttributes(AudioAttributes)
1412 * @hide
1413 */
1414 @SystemApi
Hayden Gomes695f8022019-04-11 10:44:18 -07001415 @IntRange(from = 0)
François Gaffie9c362102018-09-21 17:43:52 +02001416 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
1417 public int getMinVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
1418 Preconditions.checkNotNull(attr, "attr must not be null");
1419 final IAudioService service = getService();
1420 try {
1421 return service.getMinVolumeIndexForAttributes(attr);
1422 } catch (RemoteException e) {
1423 throw e.rethrowFromSystemServer();
1424 }
1425 }
1426
1427 /**
Hayden Gomes62812aa2019-12-23 11:40:27 -08001428 * Set the system usages to be supported on this device.
1429 * @param systemUsages array of system usages to support {@link AttributeSystemUsage}
1430 * @hide
1431 */
1432 @SystemApi
1433 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
1434 public void setSupportedSystemUsages(@NonNull @AttributeSystemUsage int[] systemUsages) {
1435 Objects.requireNonNull(systemUsages, "systemUsages must not be null");
1436 final IAudioService service = getService();
1437 try {
1438 service.setSupportedSystemUsages(systemUsages);
1439 } catch (RemoteException e) {
1440 throw e.rethrowFromSystemServer();
1441 }
1442 }
1443
1444 /**
1445 * Get the system usages supported on this device.
1446 * @return array of supported system usages {@link AttributeSystemUsage}
1447 * @hide
1448 */
1449 @SystemApi
1450 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
1451 public @NonNull @AttributeSystemUsage int[] getSupportedSystemUsages() {
1452 final IAudioService service = getService();
1453 try {
1454 return service.getSupportedSystemUsages();
1455 } catch (RemoteException e) {
1456 throw e.rethrowFromSystemServer();
1457 }
1458 }
1459
1460 /**
RoboErik4197cb62015-01-21 15:45:32 -08001461 * Solo or unsolo a particular stream.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001462 * <p>
RoboErik4197cb62015-01-21 15:45:32 -08001463 * Do not use. This method has been deprecated and is now a no-op.
1464 * {@link #requestAudioFocus} should be used for exclusive audio playback.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001465 *
1466 * @param streamType The stream to be soloed/unsoloed.
RoboErik4197cb62015-01-21 15:45:32 -08001467 * @param state The required solo state: true for solo ON, false for solo
1468 * OFF
Eric Laurentba207e72014-05-15 17:08:16 -07001469 * @see #isVolumeFixed()
RoboErik4197cb62015-01-21 15:45:32 -08001470 * @deprecated Do not use. If you need exclusive audio playback use
1471 * {@link #requestAudioFocus}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001472 */
RoboErik4197cb62015-01-21 15:45:32 -08001473 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001474 public void setStreamSolo(int streamType, boolean state) {
RoboErik4197cb62015-01-21 15:45:32 -08001475 Log.w(TAG, "setStreamSolo has been deprecated. Do not use.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001476 }
1477
1478 /**
1479 * Mute or unmute an audio stream.
1480 * <p>
RoboErik4197cb62015-01-21 15:45:32 -08001481 * This method should only be used by applications that replace the
1482 * platform-wide management of audio settings or the main telephony
1483 * application.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001484 * <p>
RoboErik4197cb62015-01-21 15:45:32 -08001485 * This method has no effect if the device implements a fixed volume policy
Eric Laurentba207e72014-05-15 17:08:16 -07001486 * as indicated by {@link #isVolumeFixed()}.
RoboErik4197cb62015-01-21 15:45:32 -08001487 * <p>
1488 * This method was deprecated in API level 22. Prior to API level 22 this
1489 * method had significantly different behavior and should be used carefully.
1490 * The following applies only to pre-22 platforms:
1491 * <ul>
1492 * <li>The mute command is protected against client process death: if a
1493 * process with an active mute request on a stream dies, this stream will be
1494 * unmuted automatically.</li>
1495 * <li>The mute requests for a given stream are cumulative: the AudioManager
1496 * can receive several mute requests from one or more clients and the stream
1497 * will be unmuted only when the same number of unmute requests are
1498 * received.</li>
1499 * <li>For a better user experience, applications MUST unmute a muted stream
1500 * in onPause() and mute is again in onResume() if appropriate.</li>
1501 * </ul>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001502 *
1503 * @param streamType The stream to be muted/unmuted.
RoboErik4197cb62015-01-21 15:45:32 -08001504 * @param state The required mute state: true for mute ON, false for mute
1505 * OFF
Eric Laurentba207e72014-05-15 17:08:16 -07001506 * @see #isVolumeFixed()
RoboErik4197cb62015-01-21 15:45:32 -08001507 * @deprecated Use {@link #adjustStreamVolume(int, int, int)} with
1508 * {@link #ADJUST_MUTE} or {@link #ADJUST_UNMUTE} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001509 */
RoboErik4197cb62015-01-21 15:45:32 -08001510 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001511 public void setStreamMute(int streamType, boolean state) {
RoboErik4197cb62015-01-21 15:45:32 -08001512 Log.w(TAG, "setStreamMute is deprecated. adjustStreamVolume should be used instead.");
1513 int direction = state ? ADJUST_MUTE : ADJUST_UNMUTE;
1514 if (streamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
1515 adjustSuggestedStreamVolume(direction, streamType, 0);
1516 } else {
1517 adjustStreamVolume(streamType, direction, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001518 }
1519 }
1520
1521 /**
RoboErik4197cb62015-01-21 15:45:32 -08001522 * Returns the current mute state for a particular stream.
Eric Laurent25101b02011-02-02 09:33:30 -08001523 *
RoboErik4197cb62015-01-21 15:45:32 -08001524 * @param streamType The stream to get mute state for.
1525 * @return The mute state for the given stream.
1526 * @see #adjustStreamVolume(int, int, int)
Eric Laurent25101b02011-02-02 09:33:30 -08001527 */
1528 public boolean isStreamMute(int streamType) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001529 final IAudioService service = getService();
Eric Laurent25101b02011-02-02 09:33:30 -08001530 try {
John Spurlockee5ad722015-03-03 16:17:21 -05001531 return service.isStreamMute(streamType);
Eric Laurent25101b02011-02-02 09:33:30 -08001532 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001533 throw e.rethrowFromSystemServer();
Eric Laurent25101b02011-02-02 09:33:30 -08001534 }
1535 }
1536
1537 /**
Mike Lockwoodce952c82011-11-14 10:47:42 -08001538 * get master mute state.
1539 *
1540 * @hide
1541 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01001542 @UnsupportedAppUsage
Mike Lockwoodce952c82011-11-14 10:47:42 -08001543 public boolean isMasterMute() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001544 final IAudioService service = getService();
Mike Lockwoodce952c82011-11-14 10:47:42 -08001545 try {
1546 return service.isMasterMute();
1547 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001548 throw e.rethrowFromSystemServer();
Mike Lockwoodce952c82011-11-14 10:47:42 -08001549 }
1550 }
1551
1552 /**
Eric Laurent402f7f22011-02-04 12:30:32 -08001553 * forces the stream controlled by hard volume keys
1554 * specifying streamType == -1 releases control to the
1555 * logic.
1556 *
1557 * @hide
1558 */
Jean-Michel Trivi60eddfd2018-03-09 15:31:12 -08001559 @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
Mathew Inwood31a792a2018-08-17 08:54:26 +01001560 @UnsupportedAppUsage
Eric Laurent402f7f22011-02-04 12:30:32 -08001561 public void forceVolumeControlStream(int streamType) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001562 final IAudioService service = getService();
Eric Laurent45c90ce2012-04-24 18:44:22 -07001563 try {
1564 service.forceVolumeControlStream(streamType, mICallBack);
1565 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001566 throw e.rethrowFromSystemServer();
Eric Laurent45c90ce2012-04-24 18:44:22 -07001567 }
Eric Laurent402f7f22011-02-04 12:30:32 -08001568 }
1569
1570 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001571 * Returns whether a particular type should vibrate according to user
1572 * settings and the current ringer mode.
1573 * <p>
1574 * This shouldn't be needed by most clients that use notifications to
1575 * vibrate. The notification manager will not vibrate if the policy doesn't
1576 * allow it, so the client should always set a vibrate pattern and let the
1577 * notification manager control whether or not to actually vibrate.
1578 *
1579 * @param vibrateType The type of vibrate. One of
1580 * {@link #VIBRATE_TYPE_NOTIFICATION} or
1581 * {@link #VIBRATE_TYPE_RINGER}.
1582 * @return Whether the type should vibrate at the instant this method is
1583 * called.
1584 * @see #setVibrateSetting(int, int)
1585 * @see #getVibrateSetting(int)
Eric Laurentcd1cd732012-05-01 11:23:07 -07001586 * @deprecated Applications should maintain their own vibrate policy based on
1587 * current ringer mode that can be queried via {@link #getRingerMode()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001588 */
1589 public boolean shouldVibrate(int vibrateType) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001590 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001591 try {
1592 return service.shouldVibrate(vibrateType);
1593 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001594 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001595 }
1596 }
1597
1598 /**
1599 * Returns whether the user's vibrate setting for a vibrate type.
1600 * <p>
1601 * This shouldn't be needed by most clients that want to vibrate, instead
1602 * see {@link #shouldVibrate(int)}.
1603 *
1604 * @param vibrateType The type of vibrate. One of
1605 * {@link #VIBRATE_TYPE_NOTIFICATION} or
1606 * {@link #VIBRATE_TYPE_RINGER}.
1607 * @return The vibrate setting, one of {@link #VIBRATE_SETTING_ON},
1608 * {@link #VIBRATE_SETTING_OFF}, or
1609 * {@link #VIBRATE_SETTING_ONLY_SILENT}.
1610 * @see #setVibrateSetting(int, int)
1611 * @see #shouldVibrate(int)
Eric Laurentcd1cd732012-05-01 11:23:07 -07001612 * @deprecated Applications should maintain their own vibrate policy based on
1613 * current ringer mode that can be queried via {@link #getRingerMode()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001614 */
1615 public int getVibrateSetting(int vibrateType) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001616 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001617 try {
1618 return service.getVibrateSetting(vibrateType);
1619 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001620 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001621 }
1622 }
1623
1624 /**
1625 * Sets the setting for when the vibrate type should vibrate.
Jean-Michel Trivifccb25d2009-09-15 16:06:10 -07001626 * <p>
1627 * This method should only be used by applications that replace the platform-wide
1628 * management of audio settings or the main telephony application.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001629 *
1630 * @param vibrateType The type of vibrate. One of
1631 * {@link #VIBRATE_TYPE_NOTIFICATION} or
1632 * {@link #VIBRATE_TYPE_RINGER}.
1633 * @param vibrateSetting The vibrate setting, one of
1634 * {@link #VIBRATE_SETTING_ON},
1635 * {@link #VIBRATE_SETTING_OFF}, or
1636 * {@link #VIBRATE_SETTING_ONLY_SILENT}.
1637 * @see #getVibrateSetting(int)
1638 * @see #shouldVibrate(int)
Eric Laurentcd1cd732012-05-01 11:23:07 -07001639 * @deprecated Applications should maintain their own vibrate policy based on
1640 * current ringer mode that can be queried via {@link #getRingerMode()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001641 */
1642 public void setVibrateSetting(int vibrateType, int vibrateSetting) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001643 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001644 try {
1645 service.setVibrateSetting(vibrateType, vibrateSetting);
1646 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001647 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001648 }
1649 }
1650
1651 /**
1652 * Sets the speakerphone on or off.
Jean-Michel Trivifccb25d2009-09-15 16:06:10 -07001653 * <p>
1654 * This method should only be used by applications that replace the platform-wide
1655 * management of audio settings or the main telephony application.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001656 *
1657 * @param on set <var>true</var> to turn on speakerphone;
1658 * <var>false</var> to turn it off
Eric Laurentf23f1b72022-02-18 10:57:54 +01001659 * @deprecated Use {@link AudioManager#setCommunicationDevice(AudioDeviceInfo)} or
1660 * {@link AudioManager#clearCommunicationDevice()} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001661 */
Eric Laurentf23f1b72022-02-18 10:57:54 +01001662 @Deprecated public void setSpeakerphoneOn(boolean on) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001663 final IAudioService service = getService();
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001664 try {
Eric Laurent3aad0ad2020-05-14 12:45:18 -07001665 service.setSpeakerphoneOn(mICallBack, on);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001666 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001667 throw e.rethrowFromSystemServer();
Eric Laurenta553c252009-07-17 12:17:14 -07001668 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001669 }
1670
1671 /**
1672 * Checks whether the speakerphone is on or off.
1673 *
1674 * @return true if speakerphone is on, false if it's off
Eric Laurentf23f1b72022-02-18 10:57:54 +01001675 * @deprecated Use {@link AudioManager#getCommunicationDevice()} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001676 */
Eric Laurentf23f1b72022-02-18 10:57:54 +01001677 @Deprecated public boolean isSpeakerphoneOn() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001678 final IAudioService service = getService();
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001679 try {
1680 return service.isSpeakerphoneOn();
1681 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001682 throw e.rethrowFromSystemServer();
Eric Laurenta553c252009-07-17 12:17:14 -07001683 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001684 }
1685
Kevin Rocard7919c2a2019-03-21 10:28:01 -07001686 /**
Kevin Rocarddb0ff412019-05-02 17:55:35 -07001687 * Specifies whether the audio played by this app may or may not be captured by other apps or
Kevin Rocard654b3cb2019-04-03 16:51:11 -07001688 * the system.
Kevin Rocard7919c2a2019-03-21 10:28:01 -07001689 *
1690 * The default is {@link AudioAttributes#ALLOW_CAPTURE_BY_ALL}.
1691 *
Kevin Rocard654b3cb2019-04-03 16:51:11 -07001692 * There are multiple ways to set this policy:
Kevin Rocarddb0ff412019-05-02 17:55:35 -07001693 * <ul>
1694 * <li> for each track independently, see
1695 * {@link AudioAttributes.Builder#setAllowedCapturePolicy(int)} </li>
1696 * <li> application-wide at runtime, with this method </li>
1697 * <li> application-wide at build time, see {@code allowAudioPlaybackCapture} in the application
1698 * manifest. </li>
1699 * </ul>
Kevin Rocard654b3cb2019-04-03 16:51:11 -07001700 * The most restrictive policy is always applied.
1701 *
Kevin Rocarddb0ff412019-05-02 17:55:35 -07001702 * See {@link AudioPlaybackCaptureConfiguration} for more details on
Kevin Rocard654b3cb2019-04-03 16:51:11 -07001703 * which audio signals can be captured.
Kevin Rocard7919c2a2019-03-21 10:28:01 -07001704 *
1705 * @param capturePolicy one of
1706 * {@link AudioAttributes#ALLOW_CAPTURE_BY_ALL},
1707 * {@link AudioAttributes#ALLOW_CAPTURE_BY_SYSTEM},
1708 * {@link AudioAttributes#ALLOW_CAPTURE_BY_NONE}.
jiabinb33f3692019-12-23 13:09:58 -08001709 * @throws RuntimeException if the argument is not a valid value.
Kevin Rocard7919c2a2019-03-21 10:28:01 -07001710 */
1711 public void setAllowedCapturePolicy(@AudioAttributes.CapturePolicy int capturePolicy) {
Kevin Rocard7919c2a2019-03-21 10:28:01 -07001712 // TODO: also pass the package in case multiple packages have the same UID
jiabinb33f3692019-12-23 13:09:58 -08001713 final IAudioService service = getService();
1714 try {
1715 int result = service.setAllowedCapturePolicy(capturePolicy);
1716 if (result != AudioSystem.AUDIO_STATUS_OK) {
1717 Log.e(TAG, "Could not setAllowedCapturePolicy: " + result);
1718 return;
1719 }
1720 } catch (RemoteException e) {
1721 throw e.rethrowFromSystemServer();
Kevin Rocard7919c2a2019-03-21 10:28:01 -07001722 }
Kevin Rocard019f60d2019-04-09 16:25:26 -07001723 }
1724
Kevin Rocard019f60d2019-04-09 16:25:26 -07001725 /**
1726 * Return the capture policy.
1727 * @return the capture policy set by {@link #setAllowedCapturePolicy(int)} or
1728 * the default if it was not called.
1729 */
1730 @AudioAttributes.CapturePolicy
1731 public int getAllowedCapturePolicy() {
jiabinb33f3692019-12-23 13:09:58 -08001732 int result = AudioAttributes.ALLOW_CAPTURE_BY_ALL;
1733 try {
1734 result = getService().getAllowedCapturePolicy();
1735 } catch (RemoteException e) {
1736 Log.e(TAG, "Failed to query allowed capture policy: " + e);
1737 }
1738 return result;
Kevin Rocard7919c2a2019-03-21 10:28:01 -07001739 }
1740
Eric Laurent3def1ee2010-03-17 23:26:26 -07001741 //====================================================================
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07001742 // Audio Product Strategy routing
1743
1744 /**
1745 * @hide
1746 * Set the preferred device for a given strategy, i.e. the audio routing to be used by
1747 * this audio strategy. Note that the device may not be available at the time the preferred
1748 * device is set, but it will be used once made available.
1749 * <p>Use {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} to cancel setting
1750 * this preference for this strategy.</p>
1751 * @param strategy the audio strategy whose routing will be affected
1752 * @param device the audio device to route to when available
1753 * @return true if the operation was successful, false otherwise
1754 */
1755 @SystemApi
1756 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
1757 public boolean setPreferredDeviceForStrategy(@NonNull AudioProductStrategy strategy,
Jean-Michel Trivi0e87bca2020-02-18 14:30:29 -08001758 @NonNull AudioDeviceAttributes device) {
jiabinf40141d2020-08-07 17:27:48 -07001759 return setPreferredDevicesForStrategy(strategy, Arrays.asList(device));
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07001760 }
1761
1762 /**
1763 * @hide
jiabinf40141d2020-08-07 17:27:48 -07001764 * Removes the preferred audio device(s) previously set with
1765 * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
1766 * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}.
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07001767 * @param strategy the audio strategy whose routing will be affected
1768 * @return true if the operation was successful, false otherwise (invalid strategy, or no
1769 * device set for example)
1770 */
1771 @SystemApi
1772 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
1773 public boolean removePreferredDeviceForStrategy(@NonNull AudioProductStrategy strategy) {
1774 Objects.requireNonNull(strategy);
1775 try {
1776 final int status =
jiabinf40141d2020-08-07 17:27:48 -07001777 getService().removePreferredDevicesForStrategy(strategy.getId());
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07001778 return status == AudioSystem.SUCCESS;
1779 } catch (RemoteException e) {
1780 throw e.rethrowFromSystemServer();
1781 }
1782 }
1783
1784 /**
1785 * @hide
1786 * Return the preferred device for an audio strategy, previously set with
jiabinf40141d2020-08-07 17:27:48 -07001787 * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
1788 * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}
1789 * @param strategy the strategy to query
1790 * @return the preferred device for that strategy, if multiple devices are set as preferred
1791 * devices, the first one in the list will be returned. Null will be returned if none was
1792 * ever set or if the strategy is invalid
1793 */
1794 @SystemApi
1795 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
1796 @Nullable
1797 public AudioDeviceAttributes getPreferredDeviceForStrategy(
1798 @NonNull AudioProductStrategy strategy) {
1799 List<AudioDeviceAttributes> devices = getPreferredDevicesForStrategy(strategy);
1800 return devices.isEmpty() ? null : devices.get(0);
1801 }
1802
1803 /**
1804 * @hide
1805 * Set the preferred devices for a given strategy, i.e. the audio routing to be used by
1806 * this audio strategy. Note that the devices may not be available at the time the preferred
1807 * devices is set, but it will be used once made available.
1808 * <p>Use {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} to cancel setting
1809 * this preference for this strategy.</p>
1810 * Note that the list of devices is not a list ranked by preference, but a list of one or more
1811 * devices used simultaneously to output the same audio signal.
1812 * @param strategy the audio strategy whose routing will be affected
1813 * @param devices a non-empty list of the audio devices to route to when available
1814 * @return true if the operation was successful, false otherwise
1815 */
1816 @SystemApi
1817 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
1818 public boolean setPreferredDevicesForStrategy(@NonNull AudioProductStrategy strategy,
1819 @NonNull List<AudioDeviceAttributes> devices) {
1820 Objects.requireNonNull(strategy);
1821 Objects.requireNonNull(devices);
1822 if (devices.isEmpty()) {
1823 throw new IllegalArgumentException(
1824 "Tried to set preferred devices for strategy with a empty list");
1825 }
1826 for (AudioDeviceAttributes device : devices) {
1827 Objects.requireNonNull(device);
1828 }
1829 try {
1830 final int status =
1831 getService().setPreferredDevicesForStrategy(strategy.getId(), devices);
1832 return status == AudioSystem.SUCCESS;
1833 } catch (RemoteException e) {
1834 throw e.rethrowFromSystemServer();
1835 }
1836 }
1837
1838 /**
1839 * @hide
1840 * Return the preferred devices for an audio strategy, previously set with
Jean-Michel Trivi0e87bca2020-02-18 14:30:29 -08001841 * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)}
jiabinf40141d2020-08-07 17:27:48 -07001842 * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07001843 * @param strategy the strategy to query
Paul Wang8ee29602022-12-22 03:40:19 +00001844 * @return list of the preferred devices for that strategy
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07001845 */
1846 @SystemApi
1847 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
jiabinf40141d2020-08-07 17:27:48 -07001848 @NonNull
1849 public List<AudioDeviceAttributes> getPreferredDevicesForStrategy(
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07001850 @NonNull AudioProductStrategy strategy) {
1851 Objects.requireNonNull(strategy);
1852 try {
jiabinf40141d2020-08-07 17:27:48 -07001853 return getService().getPreferredDevicesForStrategy(strategy.getId());
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07001854 } catch (RemoteException e) {
1855 throw e.rethrowFromSystemServer();
1856 }
1857 }
1858
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08001859 /**
1860 * @hide
Paul Wangee4774a2022-08-23 09:41:03 +00001861 * Set a device as non-default for a given strategy, i.e. the audio routing to be avoided by
1862 * this audio strategy.
1863 * <p>Use
1864 * {@link #removeDeviceAsNonDefaultForStrategy(AudioProductStrategy, AudioDeviceAttributes)}
1865 * to cancel setting this preference for this strategy.</p>
1866 * @param strategy the audio strategy whose routing will be affected
1867 * @param device the audio device to not route to when available
1868 * @return true if the operation was successful, false otherwise
1869 */
1870 @SystemApi
1871 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
1872 public boolean setDeviceAsNonDefaultForStrategy(@NonNull AudioProductStrategy strategy,
1873 @NonNull AudioDeviceAttributes device) {
1874 Objects.requireNonNull(strategy);
1875 Objects.requireNonNull(device);
1876 try {
1877 final int status =
1878 getService().setDeviceAsNonDefaultForStrategy(strategy.getId(), device);
1879 return status == AudioSystem.SUCCESS;
1880 } catch (RemoteException e) {
1881 throw e.rethrowFromSystemServer();
1882 }
1883 }
1884
1885 /**
1886 * @hide
1887 * Removes the audio device(s) from the non-default device list previously set with
1888 * {@link #setDeviceAsNonDefaultForStrategy(AudioProductStrategy, AudioDeviceAttributes)}
1889 * @param strategy the audio strategy whose routing will be affected
1890 * @param device the audio device to remove from the non-default device list
1891 * @return true if the operation was successful, false otherwise (invalid strategy, or no
1892 * device set for example)
1893 */
1894 @SystemApi
1895 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
1896 public boolean removeDeviceAsNonDefaultForStrategy(@NonNull AudioProductStrategy strategy,
1897 @NonNull AudioDeviceAttributes device) {
1898 Objects.requireNonNull(strategy);
1899 Objects.requireNonNull(device);
1900 try {
1901 final int status =
1902 getService().removeDeviceAsNonDefaultForStrategy(strategy.getId(), device);
1903 return status == AudioSystem.SUCCESS;
1904 } catch (RemoteException e) {
1905 throw e.rethrowFromSystemServer();
1906 }
1907 }
1908
1909 /**
1910 * @hide
1911 * Gets the audio device(s) from the non-default device list previously set with
1912 * {@link #setDeviceAsNonDefaultForStrategy(AudioProductStrategy, AudioDeviceAttributes)}
1913 * @param strategy the audio strategy to query
1914 * @return list of non-default devices for the strategy
1915 */
1916 @SystemApi
1917 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
1918 @NonNull
1919 public List<AudioDeviceAttributes> getNonDefaultDevicesForStrategy(
1920 @NonNull AudioProductStrategy strategy) {
1921 Objects.requireNonNull(strategy);
1922 try {
1923 return getService().getNonDefaultDevicesForStrategy(strategy.getId());
1924 } catch (RemoteException e) {
1925 throw e.rethrowFromSystemServer();
1926 }
1927 }
1928
1929 /**
1930 * @hide
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08001931 * Interface to be notified of changes in the preferred audio device set for a given audio
1932 * strategy.
Jean-Michel Trivicf9c5d32020-02-20 10:45:35 -08001933 * <p>Note that this listener will only be invoked whenever
1934 * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
jiabinf40141d2020-08-07 17:27:48 -07001935 * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}
Jean-Michel Trivicf9c5d32020-02-20 10:45:35 -08001936 * {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} causes a change in
1937 * preferred device. It will not be invoked directly after registration with
1938 * {@link #addOnPreferredDeviceForStrategyChangedListener(Executor, OnPreferredDeviceForStrategyChangedListener)}
1939 * to indicate which strategies had preferred devices at the time of registration.</p>
Jean-Michel Trivi0e87bca2020-02-18 14:30:29 -08001940 * @see #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08001941 * @see #removePreferredDeviceForStrategy(AudioProductStrategy)
1942 * @see #getPreferredDeviceForStrategy(AudioProductStrategy)
jiabinf40141d2020-08-07 17:27:48 -07001943 * @deprecated use #OnPreferredDevicesForStrategyChangedListener
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08001944 */
1945 @SystemApi
jiabinf40141d2020-08-07 17:27:48 -07001946 @Deprecated
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08001947 public interface OnPreferredDeviceForStrategyChangedListener {
1948 /**
1949 * Called on the listener to indicate that the preferred audio device for the given
1950 * strategy has changed.
1951 * @param strategy the {@link AudioProductStrategy} whose preferred device changed
1952 * @param device <code>null</code> if the preferred device was removed, or the newly set
1953 * preferred audio device
1954 */
1955 void onPreferredDeviceForStrategyChanged(@NonNull AudioProductStrategy strategy,
Jean-Michel Trivi0e87bca2020-02-18 14:30:29 -08001956 @Nullable AudioDeviceAttributes device);
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08001957 }
1958
1959 /**
1960 * @hide
jiabinf40141d2020-08-07 17:27:48 -07001961 * Interface to be notified of changes in the preferred audio devices set for a given audio
1962 * strategy.
1963 * <p>Note that this listener will only be invoked whenever
Paul Wangee4774a2022-08-23 09:41:03 +00001964 * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)},
1965 * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)},
1966 * {@link #setDeviceAsNonDefaultForStrategy(AudioProductStrategy, AudioDeviceAttributes)},
1967 * {@link #removeDeviceAsNonDefaultForStrategy(AudioProductStrategy, AudioDeviceAttributes)}
1968 * or {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} causes a change in
jiabinf40141d2020-08-07 17:27:48 -07001969 * preferred device(s). It will not be invoked directly after registration with
1970 * {@link #addOnPreferredDevicesForStrategyChangedListener(
1971 * Executor, OnPreferredDevicesForStrategyChangedListener)}
1972 * to indicate which strategies had preferred devices at the time of registration.</p>
1973 * @see #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)
1974 * @see #setPreferredDevicesForStrategy(AudioProductStrategy, List)
1975 * @see #removePreferredDeviceForStrategy(AudioProductStrategy)
jiabinf40141d2020-08-07 17:27:48 -07001976 * @see #getPreferredDevicesForStrategy(AudioProductStrategy)
1977 */
1978 @SystemApi
1979 public interface OnPreferredDevicesForStrategyChangedListener {
1980 /**
1981 * Called on the listener to indicate that the preferred audio devices for the given
1982 * strategy has changed.
1983 * @param strategy the {@link AudioProductStrategy} whose preferred device changed
1984 * @param devices a list of newly set preferred audio devices
1985 */
1986 void onPreferredDevicesForStrategyChanged(@NonNull AudioProductStrategy strategy,
1987 @NonNull List<AudioDeviceAttributes> devices);
1988 }
1989
1990 /**
1991 * @hide
1992 * Adds a listener for being notified of changes to the strategy-preferred audio device.
1993 * @param executor
1994 * @param listener
1995 * @throws SecurityException if the caller doesn't hold the required permission
1996 * @deprecated use {@link #addOnPreferredDevicesForStrategyChangedListener(
1997 * Executor, AudioManager.OnPreferredDevicesForStrategyChangedListener)} instead
1998 */
1999 @SystemApi
2000 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
2001 @Deprecated
2002 public void addOnPreferredDeviceForStrategyChangedListener(
2003 @NonNull @CallbackExecutor Executor executor,
2004 @NonNull OnPreferredDeviceForStrategyChangedListener listener)
2005 throws SecurityException {
2006 // No-op, the method is deprecated.
2007 }
2008
2009 /**
2010 * @hide
2011 * Removes a previously added listener of changes to the strategy-preferred audio device.
2012 * @param listener
2013 * @deprecated use {@link #removeOnPreferredDevicesForStrategyChangedListener(
2014 * AudioManager.OnPreferredDevicesForStrategyChangedListener)} instead
2015 */
2016 @SystemApi
2017 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
2018 @Deprecated
2019 public void removeOnPreferredDeviceForStrategyChangedListener(
2020 @NonNull OnPreferredDeviceForStrategyChangedListener listener) {
2021 // No-op, the method is deprecated.
2022 }
2023
2024 /**
2025 * @hide
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002026 * Adds a listener for being notified of changes to the strategy-preferred audio device.
2027 * @param executor
2028 * @param listener
2029 * @throws SecurityException if the caller doesn't hold the required permission
2030 */
2031 @SystemApi
2032 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
jiabinf40141d2020-08-07 17:27:48 -07002033 public void addOnPreferredDevicesForStrategyChangedListener(
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002034 @NonNull @CallbackExecutor Executor executor,
jiabinf40141d2020-08-07 17:27:48 -07002035 @NonNull OnPreferredDevicesForStrategyChangedListener listener)
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002036 throws SecurityException {
2037 Objects.requireNonNull(executor);
2038 Objects.requireNonNull(listener);
Paul Wang8ee29602022-12-22 03:40:19 +00002039 mPrefDevListenerMgr.addListener(
2040 executor, listener, "addOnPreferredDevicesForStrategyChangedListener",
2041 () -> new StrategyPreferredDevicesDispatcherStub());
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002042 }
2043
2044 /**
2045 * @hide
2046 * Removes a previously added listener of changes to the strategy-preferred audio device.
2047 * @param listener
2048 */
2049 @SystemApi
2050 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
jiabinf40141d2020-08-07 17:27:48 -07002051 public void removeOnPreferredDevicesForStrategyChangedListener(
2052 @NonNull OnPreferredDevicesForStrategyChangedListener listener) {
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002053 Objects.requireNonNull(listener);
Paul Wang8ee29602022-12-22 03:40:19 +00002054 mPrefDevListenerMgr.removeListener(
2055 listener, "removeOnPreferredDevicesForStrategyChangedListener");
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002056 }
2057
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002058 /**
Paul Wangee4774a2022-08-23 09:41:03 +00002059 * @hide
2060 * Interface to be notified of changes in the non-default audio devices set for a given audio
2061 * strategy.
2062 * <p>Note that this listener will only be invoked whenever
2063 * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)},
2064 * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)},
2065 * {@link #setDeviceAsNonDefaultForStrategy(AudioProductStrategy, AudioDeviceAttributes)},
2066 * {@link #removeDeviceAsNonDefaultForStrategy(AudioProductStrategy, AudioDeviceAttributes)}
2067 * or {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} causes a change in
2068 * non-default device(s). It will not be invoked directly after registration with
2069 * {@link #addOnNonDefaultDevicesForStrategyChangedListener(
2070 * Executor, OnNonDefaultDevicesForStrategyChangedListener)}
2071 * to indicate which strategies had preferred devices at the time of registration.</p>
2072 * @see #setDeviceAsNonDefaultForStrategy(AudioProductStrategy, AudioDeviceAttributes)
2073 * @see #removeDeviceAsNonDefaultForStrategy(AudioProductStrategy, AudioDeviceAttributes)
2074 */
2075 @SystemApi
2076 public interface OnNonDefaultDevicesForStrategyChangedListener {
2077 /**
2078 * Called on the listener to indicate that the non-default audio devices for the given
2079 * strategy has changed.
2080 * @param strategy the {@link AudioProductStrategy} whose non-default device changed
2081 * @param devices a list of newly set non-default audio devices
2082 */
2083 void onNonDefaultDevicesForStrategyChanged(@NonNull AudioProductStrategy strategy,
2084 @NonNull List<AudioDeviceAttributes> devices);
2085 }
2086
2087 /**
2088 * @hide
2089 * Adds a listener for being notified of changes to the non-default audio devices for
2090 * strategies.
2091 * @param executor
2092 * @param listener
2093 * @throws SecurityException if the caller doesn't hold the required permission
2094 */
2095 @SystemApi
2096 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
2097 public void addOnNonDefaultDevicesForStrategyChangedListener(
2098 @NonNull @CallbackExecutor Executor executor,
2099 @NonNull OnNonDefaultDevicesForStrategyChangedListener listener)
2100 throws SecurityException {
2101 Objects.requireNonNull(executor);
2102 Objects.requireNonNull(listener);
2103
2104 mNonDefDevListenerMgr.addListener(
2105 executor, listener, "addOnNonDefaultDevicesForStrategyChangedListener",
2106 () -> new StrategyNonDefaultDevicesDispatcherStub());
2107 }
2108
2109 /**
2110 * @hide
2111 * Removes a previously added listener of changes to the non-default audio device for
2112 * strategies.
2113 * @param listener
2114 */
2115 @SystemApi
2116 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
2117 public void removeOnNonDefaultDevicesForStrategyChangedListener(
2118 @NonNull OnNonDefaultDevicesForStrategyChangedListener listener) {
2119 Objects.requireNonNull(listener);
2120 mNonDefDevListenerMgr.removeListener(
2121 listener, "removeOnNonDefaultDevicesForStrategyChangedListener");
2122 }
2123
2124 /**
Paul Wang8ee29602022-12-22 03:40:19 +00002125 * Manages the OnPreferredDevicesForStrategyChangedListener listeners and the
2126 * StrategyPreferredDevicesDispatcherStub
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002127 */
Paul Wang8ee29602022-12-22 03:40:19 +00002128 private final CallbackUtil.LazyListenerManager<OnPreferredDevicesForStrategyChangedListener>
2129 mPrefDevListenerMgr = new CallbackUtil.LazyListenerManager();
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002130
Paul Wangee4774a2022-08-23 09:41:03 +00002131 /**
2132 * Manages the OnNonDefaultDevicesForStrategyChangedListener listeners and the
2133 * StrategyNonDefaultDevicesDispatcherStub
2134 */
2135 private final CallbackUtil.LazyListenerManager<OnNonDefaultDevicesForStrategyChangedListener>
2136 mNonDefDevListenerMgr = new CallbackUtil.LazyListenerManager();
2137
jiabinf40141d2020-08-07 17:27:48 -07002138 private final class StrategyPreferredDevicesDispatcherStub
Paul Wang8ee29602022-12-22 03:40:19 +00002139 extends IStrategyPreferredDevicesDispatcher.Stub
2140 implements CallbackUtil.DispatcherStub {
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002141
2142 @Override
jiabinf40141d2020-08-07 17:27:48 -07002143 public void dispatchPrefDevicesChanged(int strategyId,
2144 @NonNull List<AudioDeviceAttributes> devices) {
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002145 final AudioProductStrategy strategy =
2146 AudioProductStrategy.getAudioProductStrategyWithId(strategyId);
Paul Wang8ee29602022-12-22 03:40:19 +00002147
2148 mPrefDevListenerMgr.callListeners(
2149 (listener) -> listener.onPreferredDevicesForStrategyChanged(strategy, devices));
2150 }
2151
2152 @Override
2153 public void register(boolean register) {
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002154 try {
Paul Wang8ee29602022-12-22 03:40:19 +00002155 if (register) {
2156 getService().registerStrategyPreferredDevicesDispatcher(this);
2157 } else {
2158 getService().unregisterStrategyPreferredDevicesDispatcher(this);
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002159 }
Paul Wang8ee29602022-12-22 03:40:19 +00002160 } catch (RemoteException e) {
2161 e.rethrowFromSystemServer();
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002162 }
2163 }
2164 }
2165
Paul Wangee4774a2022-08-23 09:41:03 +00002166 private final class StrategyNonDefaultDevicesDispatcherStub
2167 extends IStrategyNonDefaultDevicesDispatcher.Stub
2168 implements CallbackUtil.DispatcherStub {
2169
2170 @Override
2171 public void dispatchNonDefDevicesChanged(int strategyId,
2172 @NonNull List<AudioDeviceAttributes> devices) {
2173 final AudioProductStrategy strategy =
2174 AudioProductStrategy.getAudioProductStrategyWithId(strategyId);
2175
2176 mNonDefDevListenerMgr.callListeners(
2177 (listener) -> listener.onNonDefaultDevicesForStrategyChanged(
2178 strategy, devices));
2179 }
2180
2181 @Override
2182 public void register(boolean register) {
2183 try {
2184 if (register) {
2185 getService().registerStrategyNonDefaultDevicesDispatcher(this);
2186 } else {
2187 getService().unregisterStrategyNonDefaultDevicesDispatcher(this);
2188 }
2189 } catch (RemoteException e) {
2190 e.rethrowFromSystemServer();
2191 }
2192 }
2193 }
2194
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07002195 //====================================================================
Jiabin Huangb55305f2020-09-03 17:54:16 +00002196 // Audio Capture Preset routing
2197
2198 /**
2199 * @hide
2200 * Set the preferred device for a given capture preset, i.e. the audio routing to be used by
2201 * this capture preset. Note that the device may not be available at the time the preferred
2202 * device is set, but it will be used once made available.
2203 * <p>Use {@link #clearPreferredDevicesForCapturePreset(int)} to cancel setting this preference
2204 * for this capture preset.</p>
2205 * @param capturePreset the audio capture preset whose routing will be affected
2206 * @param device the audio device to route to when available
2207 * @return true if the operation was successful, false otherwise
2208 */
2209 @SystemApi
2210 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
jiabin958faf92021-03-12 20:00:05 +00002211 public boolean setPreferredDeviceForCapturePreset(@MediaRecorder.SystemSource int capturePreset,
Jiabin Huangb55305f2020-09-03 17:54:16 +00002212 @NonNull AudioDeviceAttributes device) {
2213 return setPreferredDevicesForCapturePreset(capturePreset, Arrays.asList(device));
2214 }
2215
2216 /**
2217 * @hide
2218 * Remove all the preferred audio devices previously set
2219 * @param capturePreset the audio capture preset whose routing will be affected
2220 * @return true if the operation was successful, false otherwise (invalid capture preset, or no
2221 * device set for example)
2222 */
2223 @SystemApi
2224 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
jiabin958faf92021-03-12 20:00:05 +00002225 public boolean clearPreferredDevicesForCapturePreset(
2226 @MediaRecorder.SystemSource int capturePreset) {
Jiabin Huangb55305f2020-09-03 17:54:16 +00002227 if (!MediaRecorder.isValidAudioSource(capturePreset)) {
2228 return false;
2229 }
2230 try {
2231 final int status = getService().clearPreferredDevicesForCapturePreset(capturePreset);
2232 return status == AudioSystem.SUCCESS;
2233 } catch (RemoteException e) {
2234 throw e.rethrowFromSystemServer();
2235 }
2236 }
2237
2238 /**
2239 * @hide
2240 * Return the preferred devices for an audio capture preset, previously set with
2241 * {@link #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)}
2242 * @param capturePreset the capture preset to query
2243 * @return a list that contains preferred devices for that capture preset.
2244 */
2245 @NonNull
2246 @SystemApi
2247 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
jiabin958faf92021-03-12 20:00:05 +00002248 public List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(
2249 @MediaRecorder.SystemSource int capturePreset) {
Jiabin Huangb55305f2020-09-03 17:54:16 +00002250 if (!MediaRecorder.isValidAudioSource(capturePreset)) {
2251 return new ArrayList<AudioDeviceAttributes>();
2252 }
2253 try {
2254 return getService().getPreferredDevicesForCapturePreset(capturePreset);
2255 } catch (RemoteException e) {
2256 throw e.rethrowFromSystemServer();
2257 }
2258 }
2259
2260 private boolean setPreferredDevicesForCapturePreset(
jiabin958faf92021-03-12 20:00:05 +00002261 @MediaRecorder.SystemSource int capturePreset,
2262 @NonNull List<AudioDeviceAttributes> devices) {
Jiabin Huangb55305f2020-09-03 17:54:16 +00002263 Objects.requireNonNull(devices);
2264 if (!MediaRecorder.isValidAudioSource(capturePreset)) {
2265 return false;
2266 }
2267 if (devices.size() != 1) {
2268 throw new IllegalArgumentException(
2269 "Only support setting one preferred devices for capture preset");
2270 }
2271 for (AudioDeviceAttributes device : devices) {
2272 Objects.requireNonNull(device);
2273 }
2274 try {
2275 final int status =
2276 getService().setPreferredDevicesForCapturePreset(capturePreset, devices);
2277 return status == AudioSystem.SUCCESS;
2278 } catch (RemoteException e) {
2279 throw e.rethrowFromSystemServer();
2280 }
2281 }
2282
2283 /**
2284 * @hide
2285 * Interface to be notified of changes in the preferred audio devices set for a given capture
2286 * preset.
2287 * <p>Note that this listener will only be invoked whenever
2288 * {@link #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)} or
2289 * {@link #clearPreferredDevicesForCapturePreset(int)} causes a change in
2290 * preferred device. It will not be invoked directly after registration with
2291 * {@link #addOnPreferredDevicesForCapturePresetChangedListener(
2292 * Executor, OnPreferredDevicesForCapturePresetChangedListener)}
2293 * to indicate which strategies had preferred devices at the time of registration.</p>
2294 * @see #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)
2295 * @see #clearPreferredDevicesForCapturePreset(int)
2296 * @see #getPreferredDevicesForCapturePreset(int)
2297 */
2298 @SystemApi
2299 public interface OnPreferredDevicesForCapturePresetChangedListener {
2300 /**
2301 * Called on the listener to indicate that the preferred audio devices for the given
2302 * capture preset has changed.
2303 * @param capturePreset the capture preset whose preferred device changed
2304 * @param devices a list of newly set preferred audio devices
2305 */
2306 void onPreferredDevicesForCapturePresetChanged(
jiabin958faf92021-03-12 20:00:05 +00002307 @MediaRecorder.SystemSource int capturePreset,
2308 @NonNull List<AudioDeviceAttributes> devices);
Jiabin Huangb55305f2020-09-03 17:54:16 +00002309 }
2310
2311 /**
2312 * @hide
2313 * Adds a listener for being notified of changes to the capture-preset-preferred audio device.
2314 * @param executor
2315 * @param listener
2316 * @throws SecurityException if the caller doesn't hold the required permission
2317 */
2318 @SystemApi
2319 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
2320 public void addOnPreferredDevicesForCapturePresetChangedListener(
2321 @NonNull @CallbackExecutor Executor executor,
2322 @NonNull OnPreferredDevicesForCapturePresetChangedListener listener)
2323 throws SecurityException {
2324 Objects.requireNonNull(executor);
2325 Objects.requireNonNull(listener);
2326 int status = addOnDevRoleForCapturePresetChangedListener(
2327 executor, listener, AudioSystem.DEVICE_ROLE_PREFERRED);
2328 if (status == AudioSystem.ERROR) {
2329 // This must not happen
2330 throw new RuntimeException("Unknown error happened");
2331 }
2332 if (status == AudioSystem.BAD_VALUE) {
2333 throw new IllegalArgumentException(
2334 "attempt to call addOnPreferredDevicesForCapturePresetChangedListener() "
2335 + "on a previously registered listener");
2336 }
2337 }
2338
2339 /**
2340 * @hide
2341 * Removes a previously added listener of changes to the capture-preset-preferred audio device.
2342 * @param listener
2343 */
2344 @SystemApi
2345 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
2346 public void removeOnPreferredDevicesForCapturePresetChangedListener(
2347 @NonNull OnPreferredDevicesForCapturePresetChangedListener listener) {
2348 Objects.requireNonNull(listener);
2349 int status = removeOnDevRoleForCapturePresetChangedListener(
2350 listener, AudioSystem.DEVICE_ROLE_PREFERRED);
2351 if (status == AudioSystem.ERROR) {
2352 // This must not happen
2353 throw new RuntimeException("Unknown error happened");
2354 }
2355 if (status == AudioSystem.BAD_VALUE) {
2356 throw new IllegalArgumentException(
2357 "attempt to call removeOnPreferredDevicesForCapturePresetChangedListener() "
2358 + "on an unregistered listener");
2359 }
2360 }
2361
2362 private <T> int addOnDevRoleForCapturePresetChangedListener(
2363 @NonNull @CallbackExecutor Executor executor,
2364 @NonNull T listener, int deviceRole) {
2365 Objects.requireNonNull(executor);
2366 Objects.requireNonNull(listener);
2367 DevRoleListeners<T> devRoleListeners =
2368 (DevRoleListeners<T>) mDevRoleForCapturePresetListeners.get(deviceRole);
2369 if (devRoleListeners == null) {
2370 return AudioSystem.ERROR;
2371 }
2372 synchronized (devRoleListeners.mDevRoleListenersLock) {
2373 if (devRoleListeners.hasDevRoleListener(listener)) {
2374 return AudioSystem.BAD_VALUE;
2375 }
2376 // lazy initialization of the list of device role listener
2377 if (devRoleListeners.mListenerInfos == null) {
2378 devRoleListeners.mListenerInfos = new ArrayList<>();
2379 }
2380 final int oldCbCount = devRoleListeners.mListenerInfos.size();
2381 devRoleListeners.mListenerInfos.add(new DevRoleListenerInfo<T>(executor, listener));
2382 if (oldCbCount == 0 && devRoleListeners.mListenerInfos.size() > 0) {
2383 // register binder for callbacks
2384 synchronized (mDevRoleForCapturePresetListenersLock) {
2385 int deviceRoleListenerStatus = mDeviceRoleListenersStatus;
2386 mDeviceRoleListenersStatus |= (1 << deviceRole);
2387 if (deviceRoleListenerStatus != 0) {
2388 // There are already device role changed listeners active.
2389 return AudioSystem.SUCCESS;
2390 }
2391 if (mDevicesRoleForCapturePresetDispatcherStub == null) {
2392 mDevicesRoleForCapturePresetDispatcherStub =
2393 new CapturePresetDevicesRoleDispatcherStub();
2394 }
2395 try {
2396 getService().registerCapturePresetDevicesRoleDispatcher(
2397 mDevicesRoleForCapturePresetDispatcherStub);
2398 } catch (RemoteException e) {
2399 throw e.rethrowFromSystemServer();
2400 }
2401 }
2402 }
2403 }
2404 return AudioSystem.SUCCESS;
2405 }
2406
2407 private <T> int removeOnDevRoleForCapturePresetChangedListener(
2408 @NonNull T listener, int deviceRole) {
2409 Objects.requireNonNull(listener);
2410 DevRoleListeners<T> devRoleListeners =
2411 (DevRoleListeners<T>) mDevRoleForCapturePresetListeners.get(deviceRole);
2412 if (devRoleListeners == null) {
2413 return AudioSystem.ERROR;
2414 }
2415 synchronized (devRoleListeners.mDevRoleListenersLock) {
2416 if (!devRoleListeners.removeDevRoleListener(listener)) {
2417 return AudioSystem.BAD_VALUE;
2418 }
2419 if (devRoleListeners.mListenerInfos.size() == 0) {
2420 // unregister binder for callbacks
2421 synchronized (mDevRoleForCapturePresetListenersLock) {
2422 mDeviceRoleListenersStatus ^= (1 << deviceRole);
2423 if (mDeviceRoleListenersStatus != 0) {
2424 // There are some other device role changed listeners active.
2425 return AudioSystem.SUCCESS;
2426 }
2427 try {
2428 getService().unregisterCapturePresetDevicesRoleDispatcher(
2429 mDevicesRoleForCapturePresetDispatcherStub);
2430 } catch (RemoteException e) {
2431 throw e.rethrowFromSystemServer();
2432 }
2433 }
2434 }
2435 }
2436 return AudioSystem.SUCCESS;
2437 }
2438
Cole Faust7da659b2022-10-15 21:33:29 -07002439 private final Map<Integer, Object> mDevRoleForCapturePresetListeners = Map.of(
2440 AudioSystem.DEVICE_ROLE_PREFERRED,
2441 new DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>());
Jiabin Huangb55305f2020-09-03 17:54:16 +00002442
2443 private class DevRoleListenerInfo<T> {
2444 final @NonNull Executor mExecutor;
2445 final @NonNull T mListener;
2446 DevRoleListenerInfo(Executor executor, T listener) {
2447 mExecutor = executor;
2448 mListener = listener;
2449 }
2450 }
2451
2452 private class DevRoleListeners<T> {
2453 private final Object mDevRoleListenersLock = new Object();
2454 @GuardedBy("mDevRoleListenersLock")
2455 private @Nullable ArrayList<DevRoleListenerInfo<T>> mListenerInfos;
2456
2457 @GuardedBy("mDevRoleListenersLock")
2458 private @Nullable DevRoleListenerInfo<T> getDevRoleListenerInfo(T listener) {
2459 if (mListenerInfos == null) {
2460 return null;
2461 }
2462 for (DevRoleListenerInfo<T> listenerInfo : mListenerInfos) {
2463 if (listenerInfo.mListener == listener) {
2464 return listenerInfo;
2465 }
2466 }
2467 return null;
2468 }
2469
2470 @GuardedBy("mDevRoleListenersLock")
2471 private boolean hasDevRoleListener(T listener) {
2472 return getDevRoleListenerInfo(listener) != null;
2473 }
2474
2475 @GuardedBy("mDevRoleListenersLock")
2476 private boolean removeDevRoleListener(T listener) {
2477 final DevRoleListenerInfo<T> infoToRemove = getDevRoleListenerInfo(listener);
2478 if (infoToRemove != null) {
2479 mListenerInfos.remove(infoToRemove);
2480 return true;
2481 }
2482 return false;
2483 }
2484 }
2485
2486 private final Object mDevRoleForCapturePresetListenersLock = new Object();
2487 /**
2488 * Record if there is a listener added for device role change. If there is a listener added for
2489 * a specified device role change, the bit at position `1 << device_role` is set.
2490 */
2491 @GuardedBy("mDevRoleForCapturePresetListenersLock")
2492 private int mDeviceRoleListenersStatus = 0;
2493 @GuardedBy("mDevRoleForCapturePresetListenersLock")
2494 private CapturePresetDevicesRoleDispatcherStub mDevicesRoleForCapturePresetDispatcherStub;
2495
2496 private final class CapturePresetDevicesRoleDispatcherStub
2497 extends ICapturePresetDevicesRoleDispatcher.Stub {
2498
2499 @Override
2500 public void dispatchDevicesRoleChanged(
2501 int capturePreset, int role, List<AudioDeviceAttributes> devices) {
2502 final Object listenersObj = mDevRoleForCapturePresetListeners.get(role);
2503 if (listenersObj == null) {
2504 return;
2505 }
2506 switch (role) {
2507 case AudioSystem.DEVICE_ROLE_PREFERRED: {
2508 final DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>
2509 listeners =
2510 (DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>)
2511 listenersObj;
2512 final ArrayList<DevRoleListenerInfo<
2513 OnPreferredDevicesForCapturePresetChangedListener>> prefDevListeners;
2514 synchronized (listeners.mDevRoleListenersLock) {
2515 if (listeners.mListenerInfos.isEmpty()) {
2516 return;
2517 }
2518 prefDevListeners = (ArrayList<DevRoleListenerInfo<
2519 OnPreferredDevicesForCapturePresetChangedListener>>)
2520 listeners.mListenerInfos.clone();
2521 }
2522 final long ident = Binder.clearCallingIdentity();
2523 try {
2524 for (DevRoleListenerInfo<
2525 OnPreferredDevicesForCapturePresetChangedListener> info :
2526 prefDevListeners) {
2527 info.mExecutor.execute(() ->
2528 info.mListener.onPreferredDevicesForCapturePresetChanged(
2529 capturePreset, devices));
2530 }
2531 } finally {
2532 Binder.restoreCallingIdentity(ident);
2533 }
2534 } break;
2535 default:
2536 break;
2537 }
2538 }
2539 }
2540
2541 //====================================================================
jiabine22f6aa2021-12-10 01:09:02 +00002542 // Direct playback query
2543
2544 /** Return value for {@link #getDirectPlaybackSupport(AudioFormat, AudioAttributes)}:
2545 direct playback not supported. */
2546 public static final int DIRECT_PLAYBACK_NOT_SUPPORTED = AudioSystem.DIRECT_NOT_SUPPORTED;
2547 /** Return value for {@link #getDirectPlaybackSupport(AudioFormat, AudioAttributes)}:
2548 direct offload playback supported. Compressed offload is a variant of direct playback.
2549 It is the feature that allows audio processing tasks to be done on the Android device but
2550 not on the application processor, instead, it is handled by dedicated hardware such as audio
2551 DSPs. That will allow the application processor to be idle as much as possible, which is
2552 good for power saving. Compressed offload playback supports
2553 {@link AudioTrack.StreamEventCallback} for event notifications. */
2554 public static final int DIRECT_PLAYBACK_OFFLOAD_SUPPORTED =
2555 AudioSystem.DIRECT_OFFLOAD_SUPPORTED;
2556 /** Return value for {@link #getDirectPlaybackSupport(AudioFormat, AudioAttributes)}:
2557 direct offload playback supported with gapless transitions. Compressed offload is a variant
2558 of direct playback. It is the feature that allows audio processing tasks to be done on the
2559 Android device but not on the application processor, instead, it is handled by dedicated
2560 hardware such as audio DSPs. That will allow the application processor to be idle as much as
2561 possible, which is good for power saving. Compressed offload playback supports
2562 {@link AudioTrack.StreamEventCallback} for event notifications. Gapless transitions
2563 indicates the ability to play consecutive audio tracks without an audio silence in
2564 between. */
2565 public static final int DIRECT_PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED =
2566 AudioSystem.DIRECT_OFFLOAD_GAPLESS_SUPPORTED;
2567 /** Return value for {@link #getDirectPlaybackSupport(AudioFormat, AudioAttributes)}:
2568 direct playback supported. This value covers direct playback that is bitstream pass-through
2569 such as compressed pass-through. */
2570 public static final int DIRECT_PLAYBACK_BITSTREAM_SUPPORTED =
2571 AudioSystem.DIRECT_BITSTREAM_SUPPORTED;
2572
2573 /** @hide */
2574 @IntDef(flag = true, prefix = "DIRECT_PLAYBACK_", value = {
2575 DIRECT_PLAYBACK_NOT_SUPPORTED,
2576 DIRECT_PLAYBACK_OFFLOAD_SUPPORTED,
2577 DIRECT_PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED,
2578 DIRECT_PLAYBACK_BITSTREAM_SUPPORTED}
2579 )
2580 @Retention(RetentionPolicy.SOURCE)
2581 public @interface AudioDirectPlaybackMode {}
2582
2583 /**
2584 * Returns a bitfield representing the different forms of direct playback currently available
2585 * for a given audio format.
2586 * <p>Direct playback means that the audio stream is not altered by the framework. The audio
2587 * stream will not be resampled, volume scaled, downmixed or mixed with other content by
2588 * the framework. But it may be wrapped in a higher level protocol such as IEC61937 for
2589 * passthrough.
2590 * <p>Checking for direct support can help the app select the representation of audio content
2591 * that most closely matches the capabilities of the device and peripherals (e.g. A/V receiver)
2592 * connected to it. Note that the provided stream can still be re-encoded or mixed with other
2593 * streams, if needed.
2594 * @param format the audio format (codec, sample rate, channels) being checked.
2595 * @param attributes the {@link AudioAttributes} to be used for playback
2596 * @return the direct playback mode available with given format and attributes. The returned
2597 * value will be {@link #DIRECT_PLAYBACK_NOT_SUPPORTED} or a combination of
2598 * {@link #DIRECT_PLAYBACK_OFFLOAD_SUPPORTED},
2599 * {@link #DIRECT_PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED} and
2600 * {@link #DIRECT_PLAYBACK_BITSTREAM_SUPPORTED}. Note that if
2601 * {@link #DIRECT_PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED} is present in the returned value,
2602 * then {@link #DIRECT_PLAYBACK_OFFLOAD_SUPPORTED} will be too.
2603 */
2604 @AudioDirectPlaybackMode
2605 public static int getDirectPlaybackSupport(@NonNull AudioFormat format,
2606 @NonNull AudioAttributes attributes) {
2607 Objects.requireNonNull(format);
2608 Objects.requireNonNull(attributes);
2609 return AudioSystem.getDirectPlaybackSupport(format, attributes);
2610 }
2611
2612 //====================================================================
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -08002613 // Offload query
2614 /**
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -08002615 * Returns whether offloaded playback of an audio format is supported on the device.
Jean-Michel Trivi03f51392018-08-31 15:47:13 -07002616 * <p>Offloaded playback is the feature where the decoding and playback of an audio stream
2617 * is not competing with other software resources. In general, it is supported by dedicated
2618 * hardware, such as audio DSPs.
2619 * <p>Note that this query only provides information about the support of an audio format,
2620 * it does not indicate whether the resources necessary for the offloaded playback are
2621 * available at that instant.
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -08002622 * @param format the audio format (codec, sample rate, channels) being checked.
Jean-Michel Trivif3fd84a2019-03-21 16:08:50 -07002623 * @param attributes the {@link AudioAttributes} to be used for playback
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -08002624 * @return true if the given audio format can be offloaded.
2625 */
Jean-Michel Trivif3fd84a2019-03-21 16:08:50 -07002626 public static boolean isOffloadedPlaybackSupported(@NonNull AudioFormat format,
2627 @NonNull AudioAttributes attributes) {
Jean-Michel Trivi03f51392018-08-31 15:47:13 -07002628 if (format == null) {
Jean-Michel Trivif3fd84a2019-03-21 16:08:50 -07002629 throw new NullPointerException("Illegal null AudioFormat");
Jean-Michel Trivi03f51392018-08-31 15:47:13 -07002630 }
Jean-Michel Trivif3fd84a2019-03-21 16:08:50 -07002631 if (attributes == null) {
2632 throw new NullPointerException("Illegal null AudioAttributes");
2633 }
Eric Laurentba3b3a62020-11-26 20:10:51 +01002634 return AudioSystem.getOffloadSupport(format, attributes) != PLAYBACK_OFFLOAD_NOT_SUPPORTED;
2635 }
2636
2637 /** Return value for {@link #getPlaybackOffloadSupport(AudioFormat, AudioAttributes)}:
2638 offload playback not supported */
2639 public static final int PLAYBACK_OFFLOAD_NOT_SUPPORTED = AudioSystem.OFFLOAD_NOT_SUPPORTED;
2640 /** Return value for {@link #getPlaybackOffloadSupport(AudioFormat, AudioAttributes)}:
2641 offload playback supported */
2642 public static final int PLAYBACK_OFFLOAD_SUPPORTED = AudioSystem.OFFLOAD_SUPPORTED;
2643 /** Return value for {@link #getPlaybackOffloadSupport(AudioFormat, AudioAttributes)}:
2644 offload playback supported with gapless transitions */
2645 public static final int PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED =
2646 AudioSystem.OFFLOAD_GAPLESS_SUPPORTED;
2647
2648 /** @hide */
2649 @IntDef(flag = false, prefix = "PLAYBACK_OFFLOAD_", value = {
2650 PLAYBACK_OFFLOAD_NOT_SUPPORTED,
2651 PLAYBACK_OFFLOAD_SUPPORTED,
2652 PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED }
2653 )
2654 @Retention(RetentionPolicy.SOURCE)
2655 public @interface AudioOffloadMode {}
2656
2657 /**
2658 * Returns whether offloaded playback of an audio format is supported on the device or not and
2659 * when supported whether gapless transitions are possible or not.
2660 * <p>Offloaded playback is the feature where the decoding and playback of an audio stream
2661 * is not competing with other software resources. In general, it is supported by dedicated
2662 * hardware, such as audio DSPs.
2663 * <p>Note that this query only provides information about the support of an audio format,
2664 * it does not indicate whether the resources necessary for the offloaded playback are
2665 * available at that instant.
2666 * @param format the audio format (codec, sample rate, channels) being checked.
2667 * @param attributes the {@link AudioAttributes} to be used for playback
2668 * @return {@link #PLAYBACK_OFFLOAD_NOT_SUPPORTED} if offload playback if not supported,
2669 * {@link #PLAYBACK_OFFLOAD_SUPPORTED} if offload playback is supported or
2670 * {@link #PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED} if gapless transitions are
2671 * also supported.
jiabine22f6aa2021-12-10 01:09:02 +00002672 * @deprecated Use {@link #getDirectPlaybackSupport(AudioFormat, AudioAttributes)} instead
Eric Laurentba3b3a62020-11-26 20:10:51 +01002673 */
jiabine22f6aa2021-12-10 01:09:02 +00002674 @Deprecated
Eric Laurentba3b3a62020-11-26 20:10:51 +01002675 @AudioOffloadMode
2676 public static int getPlaybackOffloadSupport(@NonNull AudioFormat format,
2677 @NonNull AudioAttributes attributes) {
2678 if (format == null) {
2679 throw new NullPointerException("Illegal null AudioFormat");
2680 }
2681 if (attributes == null) {
2682 throw new NullPointerException("Illegal null AudioAttributes");
2683 }
2684 return AudioSystem.getOffloadSupport(format, attributes);
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -08002685 }
2686
2687 //====================================================================
Jean-Michel Trivie9dd14e2021-07-12 11:10:18 -07002688 // Immersive audio
2689
2690 /**
2691 * Return a handle to the optional platform's {@link Spatializer}
Jean-Michel Trivi838913c2021-09-02 20:55:44 -07002692 * @return the {@code Spatializer} instance.
Jean-Michel Trivic5f69572021-09-14 10:46:55 -07002693 * @see Spatializer#getImmersiveAudioLevel() to check for the level of support of the effect
2694 * on the platform
Jean-Michel Trivie9dd14e2021-07-12 11:10:18 -07002695 */
Jean-Michel Trivic5f69572021-09-14 10:46:55 -07002696 public @NonNull Spatializer getSpatializer() {
Jean-Michel Trivie9dd14e2021-07-12 11:10:18 -07002697 return new Spatializer(this);
2698 }
2699
2700 //====================================================================
Eric Laurent3def1ee2010-03-17 23:26:26 -07002701 // Bluetooth SCO control
2702 /**
Glenn Kastena24e9912017-08-25 08:14:08 -07002703 * Sticky broadcast intent action indicating that the Bluetooth SCO audio
Eric Laurent95b88fb2010-03-18 20:35:49 -07002704 * connection state has changed. The intent contains on extra {@link #EXTRA_SCO_AUDIO_STATE}
Eric Laurent3def1ee2010-03-17 23:26:26 -07002705 * indicating the new state which is either {@link #SCO_AUDIO_STATE_DISCONNECTED}
2706 * or {@link #SCO_AUDIO_STATE_CONNECTED}
2707 *
2708 * @see #startBluetoothSco()
Eric Laurentdc03c612011-04-01 10:59:41 -07002709 * @deprecated Use {@link #ACTION_SCO_AUDIO_STATE_UPDATED} instead
Eric Laurent3def1ee2010-03-17 23:26:26 -07002710 */
Eric Laurentdc03c612011-04-01 10:59:41 -07002711 @Deprecated
Eric Laurent3def1ee2010-03-17 23:26:26 -07002712 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
2713 public static final String ACTION_SCO_AUDIO_STATE_CHANGED =
2714 "android.media.SCO_AUDIO_STATE_CHANGED";
Eric Laurentdc03c612011-04-01 10:59:41 -07002715
2716 /**
Glenn Kastena24e9912017-08-25 08:14:08 -07002717 * Sticky broadcast intent action indicating that the Bluetooth SCO audio
Eric Laurentdc03c612011-04-01 10:59:41 -07002718 * connection state has been updated.
2719 * <p>This intent has two extras:
2720 * <ul>
2721 * <li> {@link #EXTRA_SCO_AUDIO_STATE} - The new SCO audio state. </li>
2722 * <li> {@link #EXTRA_SCO_AUDIO_PREVIOUS_STATE}- The previous SCO audio state. </li>
2723 * </ul>
2724 * <p> EXTRA_SCO_AUDIO_STATE or EXTRA_SCO_AUDIO_PREVIOUS_STATE can be any of:
2725 * <ul>
2726 * <li> {@link #SCO_AUDIO_STATE_DISCONNECTED}, </li>
2727 * <li> {@link #SCO_AUDIO_STATE_CONNECTING} or </li>
2728 * <li> {@link #SCO_AUDIO_STATE_CONNECTED}, </li>
2729 * </ul>
2730 * @see #startBluetoothSco()
2731 */
2732 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
2733 public static final String ACTION_SCO_AUDIO_STATE_UPDATED =
2734 "android.media.ACTION_SCO_AUDIO_STATE_UPDATED";
2735
Eric Laurent3def1ee2010-03-17 23:26:26 -07002736 /**
Eric Laurentdc03c612011-04-01 10:59:41 -07002737 * Extra for intent {@link #ACTION_SCO_AUDIO_STATE_CHANGED} or
2738 * {@link #ACTION_SCO_AUDIO_STATE_UPDATED} containing the new bluetooth SCO connection state.
Eric Laurent3def1ee2010-03-17 23:26:26 -07002739 */
2740 public static final String EXTRA_SCO_AUDIO_STATE =
2741 "android.media.extra.SCO_AUDIO_STATE";
2742
2743 /**
Eric Laurentdc03c612011-04-01 10:59:41 -07002744 * Extra for intent {@link #ACTION_SCO_AUDIO_STATE_UPDATED} containing the previous
2745 * bluetooth SCO connection state.
2746 */
2747 public static final String EXTRA_SCO_AUDIO_PREVIOUS_STATE =
2748 "android.media.extra.SCO_AUDIO_PREVIOUS_STATE";
2749
2750 /**
2751 * Value for extra EXTRA_SCO_AUDIO_STATE or EXTRA_SCO_AUDIO_PREVIOUS_STATE
2752 * indicating that the SCO audio channel is not established
Eric Laurent3def1ee2010-03-17 23:26:26 -07002753 */
2754 public static final int SCO_AUDIO_STATE_DISCONNECTED = 0;
2755 /**
Eric Laurentdc03c612011-04-01 10:59:41 -07002756 * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} or {@link #EXTRA_SCO_AUDIO_PREVIOUS_STATE}
2757 * indicating that the SCO audio channel is established
Eric Laurent3def1ee2010-03-17 23:26:26 -07002758 */
2759 public static final int SCO_AUDIO_STATE_CONNECTED = 1;
2760 /**
Eric Laurentdc03c612011-04-01 10:59:41 -07002761 * Value for extra EXTRA_SCO_AUDIO_STATE or EXTRA_SCO_AUDIO_PREVIOUS_STATE
2762 * indicating that the SCO audio channel is being established
2763 */
2764 public static final int SCO_AUDIO_STATE_CONNECTING = 2;
2765 /**
2766 * Value for extra EXTRA_SCO_AUDIO_STATE indicating that
Eric Laurent3def1ee2010-03-17 23:26:26 -07002767 * there was an error trying to obtain the state
2768 */
2769 public static final int SCO_AUDIO_STATE_ERROR = -1;
2770
2771
2772 /**
Eric Laurent3def1ee2010-03-17 23:26:26 -07002773 * Indicates if current platform supports use of SCO for off call use cases.
2774 * Application wanted to use bluetooth SCO audio when the phone is not in call
Jean-Michel Trivi2ac2afe2012-08-21 11:16:55 -07002775 * must first call this method to make sure that the platform supports this
Eric Laurent3def1ee2010-03-17 23:26:26 -07002776 * feature.
2777 * @return true if bluetooth SCO can be used for audio when not in call
2778 * false otherwise
2779 * @see #startBluetoothSco()
2780 */
2781 public boolean isBluetoothScoAvailableOffCall() {
Marco Nelissen29f16932015-04-17 09:50:56 -07002782 return getContext().getResources().getBoolean(
Eric Laurent3def1ee2010-03-17 23:26:26 -07002783 com.android.internal.R.bool.config_bluetooth_sco_off_call);
2784 }
2785
2786 /**
Eric Laurent3def1ee2010-03-17 23:26:26 -07002787 * Start bluetooth SCO audio connection.
2788 * <p>Requires Permission:
2789 * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
2790 * <p>This method can be used by applications wanting to send and received audio
2791 * to/from a bluetooth SCO headset while the phone is not in call.
2792 * <p>As the SCO connection establishment can take several seconds,
2793 * applications should not rely on the connection to be available when the method
Eric Laurentdc03c612011-04-01 10:59:41 -07002794 * returns but instead register to receive the intent {@link #ACTION_SCO_AUDIO_STATE_UPDATED}
Eric Laurent3def1ee2010-03-17 23:26:26 -07002795 * and wait for the state to be {@link #SCO_AUDIO_STATE_CONNECTED}.
Eric Laurentdc03c612011-04-01 10:59:41 -07002796 * <p>As the ACTION_SCO_AUDIO_STATE_UPDATED intent is sticky, the application can check the SCO
2797 * audio state before calling startBluetoothSco() by reading the intent returned by the receiver
2798 * registration. If the state is already CONNECTED, no state change will be received via the
2799 * intent after calling startBluetoothSco(). It is however useful to call startBluetoothSco()
2800 * so that the connection stays active in case the current initiator stops the connection.
2801 * <p>Unless the connection is already active as described above, the state will always
2802 * transition from DISCONNECTED to CONNECTING and then either to CONNECTED if the connection
2803 * succeeds or back to DISCONNECTED if the connection fails (e.g no headset is connected).
2804 * <p>When finished with the SCO connection or if the establishment fails, the application must
2805 * call {@link #stopBluetoothSco()} to clear the request and turn down the bluetooth connection.
Eric Laurent3def1ee2010-03-17 23:26:26 -07002806 * <p>Even if a SCO connection is established, the following restrictions apply on audio
2807 * output streams so that they can be routed to SCO headset:
Eric Laurentdc03c612011-04-01 10:59:41 -07002808 * <ul>
2809 * <li> the stream type must be {@link #STREAM_VOICE_CALL} </li>
2810 * <li> the format must be mono </li>
2811 * <li> the sampling must be 16kHz or 8kHz </li>
2812 * </ul>
Eric Laurent3def1ee2010-03-17 23:26:26 -07002813 * <p>The following restrictions apply on input streams:
Eric Laurentdc03c612011-04-01 10:59:41 -07002814 * <ul>
2815 * <li> the format must be mono </li>
2816 * <li> the sampling must be 8kHz </li>
2817 * </ul>
Eric Laurent3def1ee2010-03-17 23:26:26 -07002818 * <p>Note that the phone application always has the priority on the usage of the SCO
2819 * connection for telephony. If this method is called while the phone is in call
2820 * it will be ignored. Similarly, if a call is received or sent while an application
2821 * is using the SCO connection, the connection will be lost for the application and NOT
2822 * returned automatically when the call ends.
Eric Laurent83900752014-05-15 15:14:22 -07002823 * <p>NOTE: up to and including API version
2824 * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method initiates a virtual
2825 * voice call to the bluetooth headset.
2826 * After API version {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} only a raw SCO audio
2827 * connection is established.
Eric Laurent3def1ee2010-03-17 23:26:26 -07002828 * @see #stopBluetoothSco()
Eric Laurentdc03c612011-04-01 10:59:41 -07002829 * @see #ACTION_SCO_AUDIO_STATE_UPDATED
Eric Laurentf23f1b72022-02-18 10:57:54 +01002830 * @deprecated Use {@link AudioManager#setCommunicationDevice(AudioDeviceInfo)} instead.
Eric Laurent3def1ee2010-03-17 23:26:26 -07002831 */
Eric Laurentf23f1b72022-02-18 10:57:54 +01002832 @Deprecated public void startBluetoothSco() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07002833 final IAudioService service = getService();
Eric Laurent3def1ee2010-03-17 23:26:26 -07002834 try {
Marco Nelissen926ebb82015-03-11 09:59:49 -07002835 service.startBluetoothSco(mICallBack,
Marco Nelissen29f16932015-04-17 09:50:56 -07002836 getContext().getApplicationInfo().targetSdkVersion);
Eric Laurent3def1ee2010-03-17 23:26:26 -07002837 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07002838 throw e.rethrowFromSystemServer();
Eric Laurent3def1ee2010-03-17 23:26:26 -07002839 }
2840 }
2841
2842 /**
Jean-Michel Trivi0daab222014-07-18 15:31:29 -07002843 * @hide
Eric Laurent83900752014-05-15 15:14:22 -07002844 * Start bluetooth SCO audio connection in virtual call mode.
2845 * <p>Requires Permission:
2846 * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
2847 * <p>Similar to {@link #startBluetoothSco()} with explicit selection of virtual call mode.
2848 * Telephony and communication applications (VoIP, Video Chat) should preferably select
2849 * virtual call mode.
2850 * Applications using voice input for search or commands should first try raw audio connection
2851 * with {@link #startBluetoothSco()} and fall back to startBluetoothScoVirtualCall() in case of
2852 * failure.
2853 * @see #startBluetoothSco()
2854 * @see #stopBluetoothSco()
2855 * @see #ACTION_SCO_AUDIO_STATE_UPDATED
2856 */
Mathew Inwood8e742f92020-10-27 11:47:29 +00002857 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Eric Laurent83900752014-05-15 15:14:22 -07002858 public void startBluetoothScoVirtualCall() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07002859 final IAudioService service = getService();
Eric Laurent83900752014-05-15 15:14:22 -07002860 try {
2861 service.startBluetoothScoVirtualCall(mICallBack);
2862 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07002863 throw e.rethrowFromSystemServer();
Eric Laurent83900752014-05-15 15:14:22 -07002864 }
2865 }
2866
2867 /**
Eric Laurent3def1ee2010-03-17 23:26:26 -07002868 * Stop bluetooth SCO audio connection.
2869 * <p>Requires Permission:
2870 * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
2871 * <p>This method must be called by applications having requested the use of
Jean-Michel Trivi0daab222014-07-18 15:31:29 -07002872 * bluetooth SCO audio with {@link #startBluetoothSco()} when finished with the SCO
2873 * connection or if connection fails.
Eric Laurent3def1ee2010-03-17 23:26:26 -07002874 * @see #startBluetoothSco()
Eric Laurentf23f1b72022-02-18 10:57:54 +01002875 * @deprecated Use {@link AudioManager#clearCommunicationDevice()} instead.
Eric Laurent3def1ee2010-03-17 23:26:26 -07002876 */
Jean-Michel Trivi0daab222014-07-18 15:31:29 -07002877 // Also used for connections started with {@link #startBluetoothScoVirtualCall()}
Eric Laurentf23f1b72022-02-18 10:57:54 +01002878 @Deprecated public void stopBluetoothSco() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07002879 final IAudioService service = getService();
Eric Laurent3def1ee2010-03-17 23:26:26 -07002880 try {
2881 service.stopBluetoothSco(mICallBack);
2882 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07002883 throw e.rethrowFromSystemServer();
Eric Laurent3def1ee2010-03-17 23:26:26 -07002884 }
2885 }
2886
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002887 /**
Eric Laurenta553c252009-07-17 12:17:14 -07002888 * Request use of Bluetooth SCO headset for communications.
Jean-Michel Trivifccb25d2009-09-15 16:06:10 -07002889 * <p>
2890 * This method should only be used by applications that replace the platform-wide
2891 * management of audio settings or the main telephony application.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002892 *
Eric Laurenta553c252009-07-17 12:17:14 -07002893 * @param on set <var>true</var> to use bluetooth SCO for communications;
2894 * <var>false</var> to not use bluetooth SCO for communications
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002895 */
2896 public void setBluetoothScoOn(boolean on){
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07002897 final IAudioService service = getService();
Eric Laurentc42ac9d2009-07-29 08:53:03 -07002898 try {
2899 service.setBluetoothScoOn(on);
2900 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07002901 throw e.rethrowFromSystemServer();
Eric Laurenta553c252009-07-17 12:17:14 -07002902 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002903 }
2904
2905 /**
Eric Laurenta553c252009-07-17 12:17:14 -07002906 * Checks whether communications use Bluetooth SCO.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002907 *
Eric Laurenta553c252009-07-17 12:17:14 -07002908 * @return true if SCO is used for communications;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002909 * false if otherwise
Eric Laurentf23f1b72022-02-18 10:57:54 +01002910 * @deprecated Use {@link AudioManager#getCommunicationDevice()} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002911 */
Eric Laurentf23f1b72022-02-18 10:57:54 +01002912 @Deprecated public boolean isBluetoothScoOn() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07002913 final IAudioService service = getService();
Eric Laurentc42ac9d2009-07-29 08:53:03 -07002914 try {
2915 return service.isBluetoothScoOn();
2916 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07002917 throw e.rethrowFromSystemServer();
Eric Laurenta553c252009-07-17 12:17:14 -07002918 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002919 }
2920
2921 /**
Santiago Seifert1d8f6172022-08-25 14:34:44 +00002922 * @deprecated Use {@link MediaRouter#selectRoute} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002923 */
Eric Laurenta553c252009-07-17 12:17:14 -07002924 @Deprecated public void setBluetoothA2dpOn(boolean on){
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002925 }
2926
2927 /**
Eric Laurentc117bea2017-02-07 11:04:18 -08002928 * Checks whether a Bluetooth A2DP audio peripheral is connected or not.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002929 *
Eric Laurentc117bea2017-02-07 11:04:18 -08002930 * @return true if a Bluetooth A2DP peripheral is connected
Eric Laurent242b3382012-06-15 11:48:50 -07002931 * false if otherwise
Eric Laurentc117bea2017-02-07 11:04:18 -08002932 * @deprecated Use {@link AudioManager#getDevices(int)} instead to list available audio devices.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002933 */
2934 public boolean isBluetoothA2dpOn() {
Eric Laurent242b3382012-06-15 11:48:50 -07002935 if (AudioSystem.getDeviceConnectionState(DEVICE_OUT_BLUETOOTH_A2DP,"")
Eric Laurent9656df22016-04-20 16:42:28 -07002936 == AudioSystem.DEVICE_STATE_AVAILABLE) {
2937 return true;
2938 } else if (AudioSystem.getDeviceConnectionState(DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES,"")
2939 == AudioSystem.DEVICE_STATE_AVAILABLE) {
2940 return true;
2941 } else if (AudioSystem.getDeviceConnectionState(DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER,"")
2942 == AudioSystem.DEVICE_STATE_AVAILABLE) {
Eric Laurent242b3382012-06-15 11:48:50 -07002943 return true;
Eric Laurenta553c252009-07-17 12:17:14 -07002944 }
Eric Laurent9656df22016-04-20 16:42:28 -07002945 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002946 }
2947
2948 /**
2949 * Sets audio routing to the wired headset on or off.
2950 *
2951 * @param on set <var>true</var> to route audio to/from wired
2952 * headset; <var>false</var> disable wired headset audio
Eric Laurenta553c252009-07-17 12:17:14 -07002953 * @deprecated Do not use.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002954 */
Eric Laurenta553c252009-07-17 12:17:14 -07002955 @Deprecated public void setWiredHeadsetOn(boolean on){
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002956 }
2957
2958 /**
Eric Laurent497b3fe2011-08-02 17:41:11 -07002959 * Checks whether a wired headset is connected or not.
2960 * <p>This is not a valid indication that audio playback is
2961 * actually over the wired headset as audio routing depends on other conditions.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002962 *
Eric Laurent497b3fe2011-08-02 17:41:11 -07002963 * @return true if a wired headset is connected.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002964 * false if otherwise
Eric Laurentc117bea2017-02-07 11:04:18 -08002965 * @deprecated Use {@link AudioManager#getDevices(int)} instead to list available audio devices.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002966 */
2967 public boolean isWiredHeadsetOn() {
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08002968 if (AudioSystem.getDeviceConnectionState(DEVICE_OUT_WIRED_HEADSET,"")
Eric Laurent6015a972010-02-12 07:41:14 -08002969 == AudioSystem.DEVICE_STATE_UNAVAILABLE &&
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08002970 AudioSystem.getDeviceConnectionState(DEVICE_OUT_WIRED_HEADPHONE,"")
Paul McLean145c9532017-08-04 11:12:19 -06002971 == AudioSystem.DEVICE_STATE_UNAVAILABLE &&
2972 AudioSystem.getDeviceConnectionState(DEVICE_OUT_USB_HEADSET, "")
2973 == AudioSystem.DEVICE_STATE_UNAVAILABLE) {
Eric Laurenta553c252009-07-17 12:17:14 -07002974 return false;
2975 } else {
2976 return true;
2977 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002978 }
2979
2980 /**
2981 * Sets the microphone mute on or off.
Jean-Michel Trivifccb25d2009-09-15 16:06:10 -07002982 * <p>
2983 * This method should only be used by applications that replace the platform-wide
2984 * management of audio settings or the main telephony application.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002985 *
2986 * @param on set <var>true</var> to mute the microphone;
2987 * <var>false</var> to turn mute off
2988 */
Kenny Guy70e0c582015-06-30 19:18:28 +01002989 public void setMicrophoneMute(boolean on) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07002990 final IAudioService service = getService();
Emily Bernier22c921a2014-05-28 11:01:32 -04002991 try {
Kenny Guy70e0c582015-06-30 19:18:28 +01002992 service.setMicrophoneMute(on, getContext().getOpPackageName(),
John Wu4f7e5102021-06-22 17:29:11 +00002993 UserHandle.getCallingUserId(), getContext().getAttributionTag());
Emily Bernier22c921a2014-05-28 11:01:32 -04002994 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07002995 throw e.rethrowFromSystemServer();
Emily Bernier22c921a2014-05-28 11:01:32 -04002996 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002997 }
2998
2999 /**
Dmitry Shmidt3d233932019-09-20 15:52:03 -07003000 * @hide
3001 * Sets the microphone from switch mute on or off.
3002 * <p>
3003 * This method should only be used by InputManager to notify
3004 * Audio Subsystem about Microphone Mute switch state.
3005 *
3006 * @param on set <var>true</var> to mute the microphone;
3007 * <var>false</var> to turn mute off
3008 */
Mathew Inwood8e742f92020-10-27 11:47:29 +00003009 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Dmitry Shmidt3d233932019-09-20 15:52:03 -07003010 public void setMicrophoneMuteFromSwitch(boolean on) {
3011 final IAudioService service = getService();
3012 try {
3013 service.setMicrophoneMuteFromSwitch(on);
3014 } catch (RemoteException e) {
3015 throw e.rethrowFromSystemServer();
3016 }
3017 }
3018
3019 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003020 * Checks whether the microphone mute is on or off.
3021 *
3022 * @return true if microphone is muted, false if it's not
3023 */
3024 public boolean isMicrophoneMute() {
Dmitry Shmidt3d233932019-09-20 15:52:03 -07003025 final IAudioService service = getService();
3026 try {
3027 return service.isMicrophoneMuted();
3028 } catch (RemoteException e) {
3029 throw e.rethrowFromSystemServer();
3030 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003031 }
3032
3033 /**
Jean-Michel Trivi7be17d22017-12-21 18:09:21 -08003034 * Broadcast Action: microphone muting state changed.
3035 *
3036 * You <em>cannot</em> receive this through components declared
3037 * in manifests, only by explicitly registering for it with
3038 * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
3039 * Context.registerReceiver()}.
3040 *
3041 * <p>The intent has no extra values, use {@link #isMicrophoneMute} to check whether the
3042 * microphone is muted.
3043 */
3044 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
3045 public static final String ACTION_MICROPHONE_MUTE_CHANGED =
3046 "android.media.action.MICROPHONE_MUTE_CHANGED";
3047
3048 /**
Jean-Michel Trivi90682ff2019-03-18 15:52:00 -07003049 * Broadcast Action: speakerphone state changed.
3050 *
3051 * You <em>cannot</em> receive this through components declared
3052 * in manifests, only by explicitly registering for it with
3053 * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
3054 * Context.registerReceiver()}.
3055 *
3056 * <p>The intent has no extra values, use {@link #isSpeakerphoneOn} to check whether the
3057 * speakerphone functionality is enabled or not.
3058 */
3059 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
3060 public static final String ACTION_SPEAKERPHONE_STATE_CHANGED =
3061 "android.media.action.SPEAKERPHONE_STATE_CHANGED";
3062
3063 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003064 * Sets the audio mode.
Jean-Michel Trivifccb25d2009-09-15 16:06:10 -07003065 * <p>
3066 * The audio mode encompasses audio routing AND the behavior of
3067 * the telephony layer. Therefore this method should only be used by applications that
3068 * replace the platform-wide management of audio settings or the main telephony application.
3069 * In particular, the {@link #MODE_IN_CALL} mode should only be used by the telephony
3070 * application when it places a phone call, as it will cause signals from the radio layer
3071 * to feed the platform mixer.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003072 *
Eric Laurent2d7197fc2019-11-06 10:17:06 -08003073 * @param mode the requested audio mode.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003074 * Informs the HAL about the current audio state so that
3075 * it can route the audio appropriately.
3076 */
Eric Laurent2d7197fc2019-11-06 10:17:06 -08003077 public void setMode(@AudioMode int mode) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07003078 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003079 try {
Marco Nelissena80ac052015-03-12 16:17:45 -07003080 service.setMode(mode, mICallBack, mApplicationContext.getOpPackageName());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003081 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07003082 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003083 }
3084 }
3085
3086 /**
Eric Laurent1c3408f2021-11-09 12:09:54 +01003087 * This change id controls use of audio modes for call audio redirection.
3088 * @hide
3089 */
3090 @ChangeId
3091 @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
3092 public static final long CALL_REDIRECTION_AUDIO_MODES = 189472651L; // buganizer id
3093
3094 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003095 * Returns the current audio mode.
3096 *
Eric Laurent2d7197fc2019-11-06 10:17:06 -08003097 * @return the current audio mode.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003098 */
Eric Laurent2d7197fc2019-11-06 10:17:06 -08003099 @AudioMode
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003100 public int getMode() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07003101 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003102 try {
Eric Laurent2d7197fc2019-11-06 10:17:06 -08003103 int mode = service.getMode();
3104 int sdk;
3105 try {
3106 sdk = getContext().getApplicationInfo().targetSdkVersion;
3107 } catch (NullPointerException e) {
3108 // some tests don't have a Context
3109 sdk = Build.VERSION.SDK_INT;
3110 }
3111 if (mode == MODE_CALL_SCREENING && sdk <= Build.VERSION_CODES.Q) {
3112 mode = MODE_IN_CALL;
Eric Laurent1c3408f2021-11-09 12:09:54 +01003113 } else if (mode == MODE_CALL_REDIRECT
3114 && !CompatChanges.isChangeEnabled(CALL_REDIRECTION_AUDIO_MODES)) {
3115 mode = MODE_IN_CALL;
3116 } else if (mode == MODE_COMMUNICATION_REDIRECT
3117 && !CompatChanges.isChangeEnabled(CALL_REDIRECTION_AUDIO_MODES)) {
3118 mode = MODE_IN_COMMUNICATION;
Eric Laurent2d7197fc2019-11-06 10:17:06 -08003119 }
3120 return mode;
3121 } catch (RemoteException e) {
3122 throw e.rethrowFromSystemServer();
3123 }
3124 }
3125
3126 /**
Nate Myren08635fe2021-04-20 12:04:39 -07003127 * Interface definition of a callback that is notified when the audio mode changes
3128 */
3129 public interface OnModeChangedListener {
3130 /**
3131 * Called on the listener to indicate that the audio mode has changed
3132 *
3133 * @param mode The current audio mode
3134 */
3135 void onModeChanged(@AudioMode int mode);
3136 }
3137
Nate Myren08635fe2021-04-20 12:04:39 -07003138 /**
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08003139 * manages the OnModeChangedListener listeners and the ModeDispatcherStub
Nate Myren08635fe2021-04-20 12:04:39 -07003140 */
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08003141 private final CallbackUtil.LazyListenerManager<OnModeChangedListener> mModeChangedListenerMgr =
3142 new CallbackUtil.LazyListenerManager();
Nate Myren08635fe2021-04-20 12:04:39 -07003143
Nate Myren08635fe2021-04-20 12:04:39 -07003144
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08003145 final class ModeDispatcherStub extends IAudioModeDispatcher.Stub
3146 implements CallbackUtil.DispatcherStub {
Jean-Michel Trivi91f0baa2021-11-11 16:31:32 -08003147
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08003148 @Override
Jean-Michel Trivi91f0baa2021-11-11 16:31:32 -08003149 public void register(boolean register) {
3150 try {
3151 if (register) {
3152 getService().registerModeDispatcher(this);
3153 } else {
3154 getService().unregisterModeDispatcher(this);
3155 }
3156 } catch (RemoteException e) {
3157 e.rethrowFromSystemServer();
3158 }
3159 }
Nate Myren08635fe2021-04-20 12:04:39 -07003160
3161 @Override
3162 public void dispatchAudioModeChanged(int mode) {
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08003163 mModeChangedListenerMgr.callListeners((listener) -> listener.onModeChanged(mode));
Nate Myren08635fe2021-04-20 12:04:39 -07003164 }
3165 }
3166
Nate Myren08635fe2021-04-20 12:04:39 -07003167 /**
3168 * Adds a listener to be notified of changes to the audio mode.
3169 * See {@link #getMode()}
3170 * @param executor
3171 * @param listener
3172 */
3173 public void addOnModeChangedListener(
3174 @NonNull @CallbackExecutor Executor executor,
3175 @NonNull OnModeChangedListener listener) {
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08003176 mModeChangedListenerMgr.addListener(executor, listener, "addOnModeChangedListener",
3177 () -> new ModeDispatcherStub());
Nate Myren08635fe2021-04-20 12:04:39 -07003178 }
3179
3180 /**
3181 * Removes a previously added listener for changes to audio mode.
3182 * See {@link #getMode()}
3183 * @param listener
3184 */
3185 public void removeOnModeChangedListener(@NonNull OnModeChangedListener listener) {
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08003186 mModeChangedListenerMgr.removeListener(listener, "removeOnModeChangedListener");
Nate Myren08635fe2021-04-20 12:04:39 -07003187 }
3188
3189 /**
Eric Laurent2d7197fc2019-11-06 10:17:06 -08003190 * Indicates if the platform supports a special call screening and call monitoring mode.
3191 * <p>
3192 * When this mode is supported, it is possible to perform call screening and monitoring
3193 * functions while other use cases like music or movie playback are active.
3194 * <p>
3195 * Use {@link #setMode(int)} with mode {@link #MODE_CALL_SCREENING} to place the platform in
3196 * call screening mode.
3197 * <p>
3198 * If call screening mode is not supported, setting mode to
3199 * MODE_CALL_SCREENING will be ignored and will not change current mode reported by
3200 * {@link #getMode()}.
3201 * @return true if call screening mode is supported, false otherwise.
3202 */
3203 public boolean isCallScreeningModeSupported() {
3204 final IAudioService service = getService();
3205 try {
3206 return service.isCallScreeningModeSupported();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003207 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07003208 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003209 }
3210 }
3211
3212 /* modes for setMode/getMode/setRoute/getRoute */
3213 /**
3214 * Audio harware modes.
3215 */
3216 /**
3217 * Invalid audio mode.
3218 */
3219 public static final int MODE_INVALID = AudioSystem.MODE_INVALID;
3220 /**
3221 * Current audio mode. Used to apply audio routing to current mode.
3222 */
3223 public static final int MODE_CURRENT = AudioSystem.MODE_CURRENT;
3224 /**
3225 * Normal audio mode: not ringing and no call established.
3226 */
3227 public static final int MODE_NORMAL = AudioSystem.MODE_NORMAL;
3228 /**
3229 * Ringing audio mode. An incoming is being signaled.
3230 */
3231 public static final int MODE_RINGTONE = AudioSystem.MODE_RINGTONE;
3232 /**
Jean-Michel Trivi8f677d62010-11-15 12:11:32 -08003233 * In call audio mode. A telephony call is established.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003234 */
3235 public static final int MODE_IN_CALL = AudioSystem.MODE_IN_CALL;
Jean-Michel Trivi8f677d62010-11-15 12:11:32 -08003236 /**
Jean-Michel Trivi8f677d62010-11-15 12:11:32 -08003237 * In communication audio mode. An audio/video chat or VoIP call is established.
3238 */
3239 public static final int MODE_IN_COMMUNICATION = AudioSystem.MODE_IN_COMMUNICATION;
Eric Laurent2d7197fc2019-11-06 10:17:06 -08003240 /**
3241 * Call screening in progress. Call is connected and audio is accessible to call
3242 * screening applications but other audio use cases are still possible.
3243 */
3244 public static final int MODE_CALL_SCREENING = AudioSystem.MODE_CALL_SCREENING;
3245
Eric Laurent1c3408f2021-11-09 12:09:54 +01003246 /**
3247 * A telephony call is established and its audio is being redirected to another device.
3248 */
3249 public static final int MODE_CALL_REDIRECT = AudioSystem.MODE_CALL_REDIRECT;
3250
3251 /**
Eric Laurent961cd3a2021-11-17 15:02:24 +01003252 * An audio/video chat or VoIP call is established and its audio is being redirected to another
Eric Laurent1c3408f2021-11-09 12:09:54 +01003253 * device.
3254 */
3255 public static final int MODE_COMMUNICATION_REDIRECT = AudioSystem.MODE_COMMUNICATION_REDIRECT;
3256
Eric Laurent2d7197fc2019-11-06 10:17:06 -08003257 /** @hide */
3258 @IntDef(flag = false, prefix = "MODE_", value = {
3259 MODE_NORMAL,
3260 MODE_RINGTONE,
3261 MODE_IN_CALL,
3262 MODE_IN_COMMUNICATION,
Eric Laurent1c3408f2021-11-09 12:09:54 +01003263 MODE_CALL_SCREENING,
3264 MODE_CALL_REDIRECT,
3265 MODE_COMMUNICATION_REDIRECT}
Eric Laurent2d7197fc2019-11-06 10:17:06 -08003266 )
3267 @Retention(RetentionPolicy.SOURCE)
3268 public @interface AudioMode {}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003269
3270 /* Routing bits for setRouting/getRouting API */
3271 /**
3272 * Routing audio output to earpiece
Dianne Hackborn4a51c202009-08-21 15:14:02 -07003273 * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
3274 * setBluetoothScoOn() methods instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003275 */
Eric Laurenta553c252009-07-17 12:17:14 -07003276 @Deprecated public static final int ROUTE_EARPIECE = AudioSystem.ROUTE_EARPIECE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003277 /**
Dianne Hackborn4a51c202009-08-21 15:14:02 -07003278 * Routing audio output to speaker
3279 * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
3280 * setBluetoothScoOn() methods instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003281 */
Eric Laurenta553c252009-07-17 12:17:14 -07003282 @Deprecated public static final int ROUTE_SPEAKER = AudioSystem.ROUTE_SPEAKER;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003283 /**
3284 * @deprecated use {@link #ROUTE_BLUETOOTH_SCO}
Dianne Hackborn4a51c202009-08-21 15:14:02 -07003285 * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
3286 * setBluetoothScoOn() methods instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003287 */
3288 @Deprecated public static final int ROUTE_BLUETOOTH = AudioSystem.ROUTE_BLUETOOTH_SCO;
3289 /**
3290 * Routing audio output to bluetooth SCO
Dianne Hackborn4a51c202009-08-21 15:14:02 -07003291 * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
3292 * setBluetoothScoOn() methods instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003293 */
Eric Laurenta553c252009-07-17 12:17:14 -07003294 @Deprecated public static final int ROUTE_BLUETOOTH_SCO = AudioSystem.ROUTE_BLUETOOTH_SCO;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003295 /**
3296 * Routing audio output to headset
Dianne Hackborn4a51c202009-08-21 15:14:02 -07003297 * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
3298 * setBluetoothScoOn() methods instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003299 */
Eric Laurenta553c252009-07-17 12:17:14 -07003300 @Deprecated public static final int ROUTE_HEADSET = AudioSystem.ROUTE_HEADSET;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003301 /**
3302 * Routing audio output to bluetooth A2DP
Dianne Hackborn4a51c202009-08-21 15:14:02 -07003303 * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
3304 * setBluetoothScoOn() methods instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003305 */
Eric Laurenta553c252009-07-17 12:17:14 -07003306 @Deprecated public static final int ROUTE_BLUETOOTH_A2DP = AudioSystem.ROUTE_BLUETOOTH_A2DP;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003307 /**
3308 * Used for mask parameter of {@link #setRouting(int,int,int)}.
Dianne Hackborn4a51c202009-08-21 15:14:02 -07003309 * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
3310 * setBluetoothScoOn() methods instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003311 */
Eric Laurenta553c252009-07-17 12:17:14 -07003312 @Deprecated public static final int ROUTE_ALL = AudioSystem.ROUTE_ALL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003313
3314 /**
3315 * Sets the audio routing for a specified mode
3316 *
3317 * @param mode audio mode to change route. E.g., MODE_RINGTONE.
3318 * @param routes bit vector of routes requested, created from one or
3319 * more of ROUTE_xxx types. Set bits indicate that route should be on
3320 * @param mask bit vector of routes to change, created from one or more of
3321 * ROUTE_xxx types. Unset bits indicate the route should be left unchanged
Eric Laurentb9c9d262009-05-06 08:13:20 -07003322 *
3323 * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
Eric Laurenta553c252009-07-17 12:17:14 -07003324 * setBluetoothScoOn() methods instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003325 */
Eric Laurenta553c252009-07-17 12:17:14 -07003326 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003327 public void setRouting(int mode, int routes, int mask) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003328 }
3329
3330 /**
3331 * Returns the current audio routing bit vector for a specified mode.
3332 *
3333 * @param mode audio mode to get route (e.g., MODE_RINGTONE)
3334 * @return an audio route bit vector that can be compared with ROUTE_xxx
3335 * bits
Eric Laurentb9c9d262009-05-06 08:13:20 -07003336 * @deprecated Do not query audio routing directly, use isSpeakerphoneOn(),
3337 * isBluetoothScoOn(), isBluetoothA2dpOn() and isWiredHeadsetOn() methods instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003338 */
Eric Laurentb9c9d262009-05-06 08:13:20 -07003339 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003340 public int getRouting(int mode) {
Eric Laurenta553c252009-07-17 12:17:14 -07003341 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003342 }
3343
3344 /**
3345 * Checks whether any music is active.
3346 *
3347 * @return true if any music tracks are active.
3348 */
3349 public boolean isMusicActive() {
Jean-Michel Trivi15e30052020-12-09 11:54:00 -08003350 final IAudioService service = getService();
3351 try {
Eric Laurent89c3a972020-12-16 15:57:56 +01003352 return service.isMusicActive(false /*remotely*/);
Jean-Michel Trivi15e30052020-12-09 11:54:00 -08003353 } catch (RemoteException e) {
3354 throw e.rethrowFromSystemServer();
3355 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003356 }
3357
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07003358 /**
3359 * @hide
Jean-Michel Trivi679d5042013-02-04 16:24:09 -08003360 * Checks whether any music or media is actively playing on a remote device (e.g. wireless
3361 * display). Note that BT audio sinks are not considered remote devices.
3362 * @return true if {@link AudioManager#STREAM_MUSIC} is active on a remote device
3363 */
Mathew Inwood8e742f92020-10-27 11:47:29 +00003364 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Jean-Michel Trivi679d5042013-02-04 16:24:09 -08003365 public boolean isMusicActiveRemotely() {
Eric Laurent89c3a972020-12-16 15:57:56 +01003366 final IAudioService service = getService();
3367 try {
3368 return service.isMusicActive(true /*remotely*/);
3369 } catch (RemoteException e) {
3370 throw e.rethrowFromSystemServer();
3371 }
Jean-Michel Trivi679d5042013-02-04 16:24:09 -08003372 }
3373
3374 /**
3375 * @hide
Jean-Michel Trivi23805662013-07-31 14:19:18 -07003376 * Checks whether the current audio focus is exclusive.
3377 * @return true if the top of the audio focus stack requested focus
3378 * with {@link #AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}
3379 */
3380 public boolean isAudioFocusExclusive() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07003381 final IAudioService service = getService();
Jean-Michel Trivi23805662013-07-31 14:19:18 -07003382 try {
3383 return service.getCurrentAudioFocus() == AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE;
3384 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07003385 throw e.rethrowFromSystemServer();
Jean-Michel Trivi23805662013-07-31 14:19:18 -07003386 }
3387 }
3388
3389 /**
Jean-Michel Trivi8a21f5d2014-06-05 15:03:52 -07003390 * Return a new audio session identifier not associated with any player or effect.
Jean-Michel Trivi9e477e42014-08-25 17:44:28 -07003391 * An audio session identifier is a system wide unique identifier for a set of audio streams
3392 * (one or more mixed together).
3393 * <p>The primary use of the audio session ID is to associate audio effects to audio players,
3394 * such as {@link MediaPlayer} or {@link AudioTrack}: all audio effects sharing the same audio
3395 * session ID will be applied to the mixed audio content of the players that share the same
3396 * audio session.
3397 * <p>This method can for instance be used when creating one of the
3398 * {@link android.media.audiofx.AudioEffect} objects to define the audio session of the effect,
3399 * or to specify a session for a speech synthesis utterance
3400 * in {@link android.speech.tts.TextToSpeech.Engine}.
Jean-Michel Trivi8a21f5d2014-06-05 15:03:52 -07003401 * @return a new unclaimed and unused audio session identifier, or {@link #ERROR} when the
Jean-Michel Trivi9e477e42014-08-25 17:44:28 -07003402 * system failed to generate a new session, a condition in which audio playback or recording
3403 * will subsequently fail as well.
Jean-Michel Trivi8a21f5d2014-06-05 15:03:52 -07003404 */
Dichen Zhangf50b2362018-11-26 13:18:58 -08003405 public int generateAudioSessionId() {
Jean-Michel Trivi8a21f5d2014-06-05 15:03:52 -07003406 int session = AudioSystem.newAudioSessionId();
3407 if (session > 0) {
3408 return session;
3409 } else {
Jean-Michel Trivi289cc8e2014-07-18 18:45:32 -07003410 Log.e(TAG, "Failure to generate a new audio session ID");
Jean-Michel Trivi8a21f5d2014-06-05 15:03:52 -07003411 return ERROR;
3412 }
3413 }
3414
Jean-Michel Trivi289cc8e2014-07-18 18:45:32 -07003415 /**
3416 * A special audio session ID to indicate that the audio session ID isn't known and the
3417 * framework should generate a new value. This can be used when building a new
3418 * {@link AudioTrack} instance with
3419 * {@link AudioTrack#AudioTrack(AudioAttributes, AudioFormat, int, int, int)}.
3420 */
3421 public static final int AUDIO_SESSION_ID_GENERATE = AudioSystem.AUDIO_SESSION_ALLOCATE;
3422
Jean-Michel Trivi8a21f5d2014-06-05 15:03:52 -07003423
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003424 /*
3425 * Sets a generic audio configuration parameter. The use of these parameters
3426 * are platform dependant, see libaudio
3427 *
3428 * ** Temporary interface - DO NOT USE
3429 *
3430 * TODO: Replace with a more generic key:value get/set mechanism
3431 *
3432 * param key name of parameter to set. Must not be null.
3433 * param value value of parameter. Must not be null.
3434 */
3435 /**
3436 * @hide
Dianne Hackbornba50b97c2013-04-30 15:04:46 -07003437 * @deprecated Use {@link #setParameters(String)} instead
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003438 */
Eric Laurenta553c252009-07-17 12:17:14 -07003439 @Deprecated public void setParameter(String key, String value) {
3440 setParameters(key+"="+value);
3441 }
3442
3443 /**
3444 * Sets a variable number of parameter values to audio hardware.
3445 *
3446 * @param keyValuePairs list of parameters key value pairs in the form:
3447 * key1=value1;key2=value2;...
3448 *
3449 */
3450 public void setParameters(String keyValuePairs) {
3451 AudioSystem.setParameters(keyValuePairs);
3452 }
3453
3454 /**
William Escandeef429b62021-10-15 18:37:40 +02003455 * @hide
3456 */
3457 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
3458 @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
3459 public void setHfpEnabled(boolean enable) {
3460 AudioSystem.setParameters("hfp_enable=" + enable);
3461 }
3462
3463 /**
3464 * @hide
3465 */
3466 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
3467 @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
3468 public void setHfpVolume(int volume) {
3469 AudioSystem.setParameters("hfp_volume=" + volume);
3470 }
3471
3472 /**
3473 * @hide
3474 */
3475 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
3476 @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
3477 public void setHfpSamplingRate(int rate) {
3478 AudioSystem.setParameters("hfp_set_sampling_rate=" + rate);
3479 }
3480
3481 /**
3482 * @hide
3483 */
3484 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
3485 @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
3486 public void setBluetoothHeadsetProperties(@NonNull String name, boolean hasNrecEnabled,
3487 boolean hasWbsEnabled) {
3488 AudioSystem.setParameters("bt_headset_name=" + name
3489 + ";bt_headset_nrec=" + (hasNrecEnabled ? "on" : "off")
3490 + ";bt_wbs=" + (hasWbsEnabled ? "on" : "off"));
3491 }
3492
3493 /**
3494 * @hide
3495 */
3496 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
3497 @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
3498 public void setA2dpSuspended(boolean enable) {
3499 AudioSystem.setParameters("A2dpSuspended=" + enable);
3500 }
3501
3502 /**
Rahul Sabnis215e5412022-12-21 16:03:07 -08003503 * Suspends the use of LE Audio.
3504 *
3505 * @param enable {@code true} to suspend le audio, {@code false} to unsuspend
3506 *
3507 * @hide
3508 */
3509 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
3510 @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
3511 public void setLeAudioSuspended(boolean enable) {
3512 AudioSystem.setParameters("LeAudioSuspended=" + enable);
3513 }
3514
3515 /**
John Spurlockaac753d2013-02-22 16:33:32 -05003516 * Gets a variable number of parameter values from audio hardware.
Eric Laurenta553c252009-07-17 12:17:14 -07003517 *
3518 * @param keys list of parameters
3519 * @return list of parameters key value pairs in the form:
3520 * key1=value1;key2=value2;...
3521 */
3522 public String getParameters(String keys) {
3523 return AudioSystem.getParameters(keys);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003524 }
3525
3526 /* Sound effect identifiers */
3527 /**
3528 * Keyboard and direction pad click sound
3529 * @see #playSoundEffect(int)
3530 */
3531 public static final int FX_KEY_CLICK = 0;
3532 /**
3533 * Focus has moved up
3534 * @see #playSoundEffect(int)
3535 */
3536 public static final int FX_FOCUS_NAVIGATION_UP = 1;
3537 /**
3538 * Focus has moved down
3539 * @see #playSoundEffect(int)
3540 */
3541 public static final int FX_FOCUS_NAVIGATION_DOWN = 2;
3542 /**
3543 * Focus has moved left
3544 * @see #playSoundEffect(int)
3545 */
3546 public static final int FX_FOCUS_NAVIGATION_LEFT = 3;
3547 /**
3548 * Focus has moved right
3549 * @see #playSoundEffect(int)
3550 */
3551 public static final int FX_FOCUS_NAVIGATION_RIGHT = 4;
3552 /**
3553 * IME standard keypress sound
3554 * @see #playSoundEffect(int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003555 */
3556 public static final int FX_KEYPRESS_STANDARD = 5;
3557 /**
3558 * IME spacebar keypress sound
3559 * @see #playSoundEffect(int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003560 */
3561 public static final int FX_KEYPRESS_SPACEBAR = 6;
3562 /**
3563 * IME delete keypress sound
3564 * @see #playSoundEffect(int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003565 */
3566 public static final int FX_KEYPRESS_DELETE = 7;
3567 /**
3568 * IME return_keypress sound
3569 * @see #playSoundEffect(int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003570 */
3571 public static final int FX_KEYPRESS_RETURN = 8;
Justin Kohcacfe692013-07-11 17:16:53 -07003572
3573 /**
3574 * Invalid keypress sound
3575 * @see #playSoundEffect(int)
3576 */
3577 public static final int FX_KEYPRESS_INVALID = 9;
Philip Junker7f1fdff2020-12-03 16:10:41 +01003578
3579 /**
3580 * Back sound
3581 * @see #playSoundEffect(int)
3582 */
3583 public static final int FX_BACK = 10;
3584
3585 /**
3586 * @hide Home sound
Philip Junker6d9a0962021-02-18 13:55:54 +01003587 * <p>
3588 * To be played by the framework when the home app becomes active if config_enableHomeSound is
3589 * set to true. This is currently only used on TV devices.
Philip Junker7f1fdff2020-12-03 16:10:41 +01003590 * Note that this sound is only available if a sound file is specified in audio_assets.xml.
3591 * @see #playSoundEffect(int)
3592 */
3593 public static final int FX_HOME = 11;
3594
3595 /**
Philip Junker6d9a0962021-02-18 13:55:54 +01003596 * @hide Navigation repeat sound 1
3597 * <p>
3598 * To be played by the framework when a focus navigation is repeatedly triggered
3599 * (e.g. due to long-pressing) and {@link #areNavigationRepeatSoundEffectsEnabled()} is true.
Philip Junker7f1fdff2020-12-03 16:10:41 +01003600 * This is currently only used on TV devices.
3601 * Note that this sound is only available if a sound file is specified in audio_assets.xml
3602 * @see #playSoundEffect(int)
3603 */
Philip Junker6d9a0962021-02-18 13:55:54 +01003604 public static final int FX_FOCUS_NAVIGATION_REPEAT_1 = 12;
Philip Junker7f1fdff2020-12-03 16:10:41 +01003605
3606 /**
Philip Junker6d9a0962021-02-18 13:55:54 +01003607 * @hide Navigation repeat sound 2
3608 * <p>
3609 * To be played by the framework when a focus navigation is repeatedly triggered
3610 * (e.g. due to long-pressing) and {@link #areNavigationRepeatSoundEffectsEnabled()} is true.
Philip Junker7f1fdff2020-12-03 16:10:41 +01003611 * This is currently only used on TV devices.
3612 * Note that this sound is only available if a sound file is specified in audio_assets.xml
3613 * @see #playSoundEffect(int)
3614 */
Philip Junker6d9a0962021-02-18 13:55:54 +01003615 public static final int FX_FOCUS_NAVIGATION_REPEAT_2 = 13;
Philip Junker7f1fdff2020-12-03 16:10:41 +01003616
3617 /**
Philip Junker6d9a0962021-02-18 13:55:54 +01003618 * @hide Navigation repeat sound 3
3619 * <p>
3620 * To be played by the framework when a focus navigation is repeatedly triggered
3621 * (e.g. due to long-pressing) and {@link #areNavigationRepeatSoundEffectsEnabled()} is true.
Philip Junker7f1fdff2020-12-03 16:10:41 +01003622 * This is currently only used on TV devices.
3623 * Note that this sound is only available if a sound file is specified in audio_assets.xml
3624 * @see #playSoundEffect(int)
3625 */
Philip Junker6d9a0962021-02-18 13:55:54 +01003626 public static final int FX_FOCUS_NAVIGATION_REPEAT_3 = 14;
Philip Junker7f1fdff2020-12-03 16:10:41 +01003627
3628 /**
Philip Junker6d9a0962021-02-18 13:55:54 +01003629 * @hide Navigation repeat sound 4
3630 * <p>
3631 * To be played by the framework when a focus navigation is repeatedly triggered
3632 * (e.g. due to long-pressing) and {@link #areNavigationRepeatSoundEffectsEnabled()} is true.
Philip Junker7f1fdff2020-12-03 16:10:41 +01003633 * This is currently only used on TV devices.
3634 * Note that this sound is only available if a sound file is specified in audio_assets.xml
3635 * @see #playSoundEffect(int)
3636 */
Philip Junker6d9a0962021-02-18 13:55:54 +01003637 public static final int FX_FOCUS_NAVIGATION_REPEAT_4 = 15;
Philip Junker7f1fdff2020-12-03 16:10:41 +01003638
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003639 /**
3640 * @hide Number of sound effects
3641 */
Mathew Inwood8e742f92020-10-27 11:47:29 +00003642 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Philip Junker7f1fdff2020-12-03 16:10:41 +01003643 public static final int NUM_SOUND_EFFECTS = 16;
3644
Philip Junkerdfcb12b2021-03-17 19:38:50 +01003645 /** @hide */
3646 @IntDef(prefix = { "FX_" }, value = {
3647 FX_KEY_CLICK,
3648 FX_FOCUS_NAVIGATION_UP,
3649 FX_FOCUS_NAVIGATION_DOWN,
3650 FX_FOCUS_NAVIGATION_LEFT,
3651 FX_FOCUS_NAVIGATION_RIGHT,
3652 FX_KEYPRESS_STANDARD,
3653 FX_KEYPRESS_SPACEBAR,
3654 FX_KEYPRESS_DELETE,
3655 FX_KEYPRESS_RETURN,
3656 FX_KEYPRESS_INVALID,
3657 FX_BACK
3658 })
3659 @Retention(RetentionPolicy.SOURCE)
3660 public @interface SystemSoundEffect {}
3661
Philip Junker7f1fdff2020-12-03 16:10:41 +01003662 /**
Philip Junker6d9a0962021-02-18 13:55:54 +01003663 * @hide Number of FX_FOCUS_NAVIGATION_REPEAT_* sound effects
Philip Junker7f1fdff2020-12-03 16:10:41 +01003664 */
Philip Junker6d9a0962021-02-18 13:55:54 +01003665 public static final int NUM_NAVIGATION_REPEAT_SOUND_EFFECTS = 4;
Philip Junker7f1fdff2020-12-03 16:10:41 +01003666
3667 /**
3668 * @hide
Philip Junker6d9a0962021-02-18 13:55:54 +01003669 * @param n a value in [0, {@link #NUM_NAVIGATION_REPEAT_SOUND_EFFECTS}[
3670 * @return The id of a navigation repeat sound effect or -1 if out of bounds
Philip Junker7f1fdff2020-12-03 16:10:41 +01003671 */
Philip Junker6d9a0962021-02-18 13:55:54 +01003672 public static int getNthNavigationRepeatSoundEffect(int n) {
Philip Junker7f1fdff2020-12-03 16:10:41 +01003673 switch (n) {
3674 case 0:
Philip Junker6d9a0962021-02-18 13:55:54 +01003675 return FX_FOCUS_NAVIGATION_REPEAT_1;
Philip Junker7f1fdff2020-12-03 16:10:41 +01003676 case 1:
Philip Junker6d9a0962021-02-18 13:55:54 +01003677 return FX_FOCUS_NAVIGATION_REPEAT_2;
Philip Junker7f1fdff2020-12-03 16:10:41 +01003678 case 2:
Philip Junker6d9a0962021-02-18 13:55:54 +01003679 return FX_FOCUS_NAVIGATION_REPEAT_3;
Philip Junker7f1fdff2020-12-03 16:10:41 +01003680 case 3:
Philip Junker6d9a0962021-02-18 13:55:54 +01003681 return FX_FOCUS_NAVIGATION_REPEAT_4;
Philip Junker7f1fdff2020-12-03 16:10:41 +01003682 default:
Philip Junker6d9a0962021-02-18 13:55:54 +01003683 Log.w(TAG, "Invalid navigation repeat sound effect id: " + n);
Philip Junker7f1fdff2020-12-03 16:10:41 +01003684 return -1;
3685 }
3686 }
3687
3688 /**
3689 * @hide
3690 */
Philip Junker6d9a0962021-02-18 13:55:54 +01003691 public void setNavigationRepeatSoundEffectsEnabled(boolean enabled) {
Philip Junker7f1fdff2020-12-03 16:10:41 +01003692 try {
Philip Junker6d9a0962021-02-18 13:55:54 +01003693 getService().setNavigationRepeatSoundEffectsEnabled(enabled);
Philip Junker7f1fdff2020-12-03 16:10:41 +01003694 } catch (RemoteException e) {
3695
3696 }
3697 }
3698
3699 /**
3700 * @hide
Philip Junker6d9a0962021-02-18 13:55:54 +01003701 * @return true if the navigation repeat sound effects are enabled
Philip Junker7f1fdff2020-12-03 16:10:41 +01003702 */
Philip Junker6d9a0962021-02-18 13:55:54 +01003703 public boolean areNavigationRepeatSoundEffectsEnabled() {
Philip Junker7f1fdff2020-12-03 16:10:41 +01003704 try {
Philip Junker6d9a0962021-02-18 13:55:54 +01003705 return getService().areNavigationRepeatSoundEffectsEnabled();
Philip Junker7f1fdff2020-12-03 16:10:41 +01003706 } catch (RemoteException e) {
3707 throw e.rethrowFromSystemServer();
3708 }
3709 }
3710
3711 /**
3712 * @hide
3713 * @param enabled
3714 */
3715 public void setHomeSoundEffectEnabled(boolean enabled) {
3716 try {
3717 getService().setHomeSoundEffectEnabled(enabled);
3718 } catch (RemoteException e) {
3719
3720 }
3721 }
3722
3723 /**
3724 * @hide
3725 * @return true if the home sound effect is enabled
3726 */
3727 public boolean isHomeSoundEffectEnabled() {
3728 try {
3729 return getService().isHomeSoundEffectEnabled();
3730 } catch (RemoteException e) {
3731 throw e.rethrowFromSystemServer();
3732 }
3733 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003734
3735 /**
3736 * Plays a sound effect (Key clicks, lid open/close...)
Philip Junkerdfcb12b2021-03-17 19:38:50 +01003737 * @param effectType The type of sound effect.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003738 * NOTE: This version uses the UI settings to determine
3739 * whether sounds are heard or not.
3740 */
Philip Junkerdfcb12b2021-03-17 19:38:50 +01003741 public void playSoundEffect(@SystemSoundEffect int effectType) {
Oscar Azucena21f2c4d2021-10-26 18:06:54 -07003742 playSoundEffect(effectType, UserHandle.USER_CURRENT);
Jason Monk0c37ba32014-09-08 15:34:23 -04003743 }
3744
3745 /**
3746 * Plays a sound effect (Key clicks, lid open/close...)
Philip Junkerdfcb12b2021-03-17 19:38:50 +01003747 * @param effectType The type of sound effect.
Jason Monk0c37ba32014-09-08 15:34:23 -04003748 * @param userId The current user to pull sound settings from
3749 * NOTE: This version uses the UI settings to determine
3750 * whether sounds are heard or not.
3751 * @hide
3752 */
Jan Sebechlebskyfb48c022022-12-16 16:26:47 +01003753 public void playSoundEffect(@SystemSoundEffect int effectType, int userId) {
Jason Monk0c37ba32014-09-08 15:34:23 -04003754 if (effectType < 0 || effectType >= NUM_SOUND_EFFECTS) {
3755 return;
3756 }
3757
Jan Sebechlebskyfb48c022022-12-16 16:26:47 +01003758 if (delegateSoundEffectToVdm(effectType)) {
3759 return;
3760 }
3761
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07003762 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003763 try {
Oscar Azucena21f2c4d2021-10-26 18:06:54 -07003764 service.playSoundEffect(effectType, userId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003765 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07003766 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003767 }
3768 }
3769
3770 /**
3771 * Plays a sound effect (Key clicks, lid open/close...)
Philip Junkerdfcb12b2021-03-17 19:38:50 +01003772 * @param effectType The type of sound effect.
Eric Laurenta2ef57d2009-09-28 04:46:10 -07003773 * @param volume Sound effect volume.
3774 * The volume value is a raw scalar so UI controls should be scaled logarithmically.
3775 * If a volume of -1 is specified, the AudioManager.STREAM_MUSIC stream volume minus 3dB will be used.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003776 * NOTE: This version is for applications that have their own
3777 * settings panel for enabling and controlling volume.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003778 */
Jan Sebechlebskyfb48c022022-12-16 16:26:47 +01003779 public void playSoundEffect(@SystemSoundEffect int effectType, float volume) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003780 if (effectType < 0 || effectType >= NUM_SOUND_EFFECTS) {
3781 return;
3782 }
3783
Jan Sebechlebskyfb48c022022-12-16 16:26:47 +01003784 if (delegateSoundEffectToVdm(effectType)) {
3785 return;
3786 }
3787
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07003788 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003789 try {
3790 service.playSoundEffectVolume(effectType, volume);
3791 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07003792 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003793 }
3794 }
3795
3796 /**
Jan Sebechlebskyfb48c022022-12-16 16:26:47 +01003797 * Checks whether this {@link AudioManager} instance is asociated with {@link VirtualDevice}
3798 * configured with custom device policy for audio. If there is such device, request to play
3799 * sound effect is forwarded to {@link VirtualDeviceManager}.
3800 *
3801 * @param effectType - The type of sound effect.
3802 * @return true if the request was forwarded to {@link VirtualDeviceManager} instance,
3803 * false otherwise.
3804 */
3805 private boolean delegateSoundEffectToVdm(@SystemSoundEffect int effectType) {
3806 int deviceId = getContext().getDeviceId();
3807 if (deviceId != DEVICE_ID_DEFAULT) {
3808 VirtualDeviceManager vdm = getVirtualDeviceManager();
3809 if (vdm != null && vdm.getDevicePolicy(deviceId, POLICY_TYPE_AUDIO)
3810 != DEVICE_POLICY_DEFAULT) {
3811 vdm.playSoundEffect(deviceId, effectType);
3812 return true;
3813 }
3814 }
3815 return false;
3816 }
3817
3818 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003819 * Load Sound effects.
3820 * This method must be called when sound effects are enabled.
3821 */
3822 public void loadSoundEffects() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07003823 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003824 try {
3825 service.loadSoundEffects();
3826 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07003827 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003828 }
3829 }
3830
3831 /**
3832 * Unload Sound effects.
3833 * This method can be called to free some memory when
3834 * sound effects are disabled.
3835 */
3836 public void unloadSoundEffects() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07003837 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003838 try {
3839 service.unloadSoundEffects();
3840 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07003841 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003842 }
3843 }
3844
Eric Laurent4050c932009-07-08 02:52:14 -07003845 /**
Andy Hung69836952020-03-26 01:00:15 -07003846 * @hide
3847 */
3848 public static String audioFocusToString(int focus) {
3849 switch (focus) {
3850 case AUDIOFOCUS_NONE:
3851 return "AUDIOFOCUS_NONE";
3852 case AUDIOFOCUS_GAIN:
3853 return "AUDIOFOCUS_GAIN";
3854 case AUDIOFOCUS_GAIN_TRANSIENT:
3855 return "AUDIOFOCUS_GAIN_TRANSIENT";
3856 case AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
3857 return "AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK";
3858 case AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
3859 return "AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE";
3860 case AUDIOFOCUS_LOSS:
3861 return "AUDIOFOCUS_LOSS";
3862 case AUDIOFOCUS_LOSS_TRANSIENT:
3863 return "AUDIOFOCUS_LOSS_TRANSIENT";
3864 case AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: // Note CAN_DUCK not MAY_DUCK.
3865 return "AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK";
3866 default:
3867 return "AUDIO_FOCUS_UNKNOWN(" + focus + ")";
3868 }
3869 }
3870
3871 /**
Jean-Michel Trivi0f49f822017-02-16 14:36:43 -08003872 * Used to indicate no audio focus has been gained or lost, or requested.
Jean-Michel Trivi23805662013-07-31 14:19:18 -07003873 */
3874 public static final int AUDIOFOCUS_NONE = 0;
3875
3876 /**
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003877 * Used to indicate a gain of audio focus, or a request of audio focus, of unknown duration.
Jean-Michel Trivi7f7e67f2010-03-31 11:30:24 -07003878 * @see OnAudioFocusChangeListener#onAudioFocusChange(int)
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003879 * @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003880 */
3881 public static final int AUDIOFOCUS_GAIN = 1;
3882 /**
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003883 * Used to indicate a temporary gain or request of audio focus, anticipated to last a short
3884 * amount of time. Examples of temporary changes are the playback of driving directions, or an
3885 * event notification.
Jean-Michel Trivi7f7e67f2010-03-31 11:30:24 -07003886 * @see OnAudioFocusChangeListener#onAudioFocusChange(int)
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003887 * @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003888 */
3889 public static final int AUDIOFOCUS_GAIN_TRANSIENT = 2;
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07003890 /**
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07003891 * Used to indicate a temporary request of audio focus, anticipated to last a short
Jean-Michel Trivi983ac2b2010-03-19 12:09:25 -07003892 * amount of time, and where it is acceptable for other audio applications to keep playing
3893 * after having lowered their output level (also referred to as "ducking").
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07003894 * Examples of temporary changes are the playback of driving directions where playback of music
3895 * in the background is acceptable.
Jean-Michel Trivi7f7e67f2010-03-31 11:30:24 -07003896 * @see OnAudioFocusChangeListener#onAudioFocusChange(int)
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07003897 * @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
3898 */
3899 public static final int AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK = 3;
3900 /**
Jean-Michel Trivi23805662013-07-31 14:19:18 -07003901 * Used to indicate a temporary request of audio focus, anticipated to last a short
3902 * amount of time, during which no other applications, or system components, should play
3903 * anything. Examples of exclusive and transient audio focus requests are voice
3904 * memo recording and speech recognition, during which the system shouldn't play any
3905 * notifications, and media playback should have paused.
3906 * @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
3907 */
3908 public static final int AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE = 4;
3909 /**
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07003910 * Used to indicate a loss of audio focus of unknown duration.
Jean-Michel Trivi7f7e67f2010-03-31 11:30:24 -07003911 * @see OnAudioFocusChangeListener#onAudioFocusChange(int)
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07003912 */
3913 public static final int AUDIOFOCUS_LOSS = -1 * AUDIOFOCUS_GAIN;
3914 /**
3915 * Used to indicate a transient loss of audio focus.
Jean-Michel Trivi7f7e67f2010-03-31 11:30:24 -07003916 * @see OnAudioFocusChangeListener#onAudioFocusChange(int)
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07003917 */
3918 public static final int AUDIOFOCUS_LOSS_TRANSIENT = -1 * AUDIOFOCUS_GAIN_TRANSIENT;
3919 /**
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07003920 * Used to indicate a transient loss of audio focus where the loser of the audio focus can
Jean-Michel Trivi983ac2b2010-03-19 12:09:25 -07003921 * lower its output volume if it wants to continue playing (also referred to as "ducking"), as
3922 * the new focus owner doesn't require others to be silent.
Jean-Michel Trivi7f7e67f2010-03-31 11:30:24 -07003923 * @see OnAudioFocusChangeListener#onAudioFocusChange(int)
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07003924 */
3925 public static final int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK =
3926 -1 * AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003927
3928 /**
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003929 * Interface definition for a callback to be invoked when the audio focus of the system is
3930 * updated.
3931 */
3932 public interface OnAudioFocusChangeListener {
3933 /**
3934 * Called on the listener to notify it the audio focus for this listener has been changed.
3935 * The focusChange value indicates whether the focus was gained,
3936 * whether the focus was lost, and whether that loss is transient, or whether the new focus
3937 * holder will hold it for an unknown amount of time.
Jean-Michel Trivi983ac2b2010-03-19 12:09:25 -07003938 * When losing focus, listeners can use the focus change information to decide what
3939 * behavior to adopt when losing focus. A music player could for instance elect to lower
3940 * the volume of its music stream (duck) for transient focus losses, and pause otherwise.
3941 * @param focusChange the type of focus change, one of {@link AudioManager#AUDIOFOCUS_GAIN},
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07003942 * {@link AudioManager#AUDIOFOCUS_LOSS}, {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT}
Jean-Michel Trivi983ac2b2010-03-19 12:09:25 -07003943 * and {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}.
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003944 */
Jean-Michel Trivi7f7e67f2010-03-31 11:30:24 -07003945 public void onAudioFocusChange(int focusChange);
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003946 }
3947
3948 /**
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07003949 * Internal class to hold the AudioFocusRequest as well as the Handler for the callback
3950 */
3951 private static class FocusRequestInfo {
3952 @NonNull final AudioFocusRequest mRequest;
3953 @Nullable final Handler mHandler;
3954 FocusRequestInfo(@NonNull AudioFocusRequest afr, @Nullable Handler handler) {
3955 mRequest = afr;
3956 mHandler = handler;
3957 }
3958 }
3959
3960 /**
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003961 * Map to convert focus event listener IDs, as used in the AudioService audio focus stack,
3962 * to actual listener objects.
3963 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01003964 @UnsupportedAppUsage
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07003965 private final ConcurrentHashMap<String, FocusRequestInfo> mAudioFocusIdListenerMap =
3966 new ConcurrentHashMap<String, FocusRequestInfo>();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003967
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07003968 private FocusRequestInfo findFocusRequestInfo(String id) {
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003969 return mAudioFocusIdListenerMap.get(id);
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003970 }
3971
3972 /**
Jean-Michel Trivi3b61d2d2016-01-07 10:50:09 -08003973 * Handler for events (audio focus change, recording config change) coming from the
3974 * audio service.
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003975 */
Jean-Michel Trivi3b61d2d2016-01-07 10:50:09 -08003976 private final ServiceEventHandlerDelegate mServiceEventHandlerDelegate =
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08003977 new ServiceEventHandlerDelegate(null);
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07003978
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003979 /**
Jean-Michel Trivi3b61d2d2016-01-07 10:50:09 -08003980 * Event types
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003981 */
Jean-Michel Trivi3b61d2d2016-01-07 10:50:09 -08003982 private final static int MSSG_FOCUS_CHANGE = 0;
3983 private final static int MSSG_RECORDING_CONFIG_CHANGE = 1;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -08003984 private final static int MSSG_PLAYBACK_CONFIG_CHANGE = 2;
Jean-Michel Trivi3b61d2d2016-01-07 10:50:09 -08003985
3986 /**
3987 * Helper class to handle the forwarding of audio service events to the appropriate listener
3988 */
3989 private class ServiceEventHandlerDelegate {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003990 private final Handler mHandler;
3991
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08003992 ServiceEventHandlerDelegate(Handler handler) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003993 Looper looper;
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08003994 if (handler == null) {
3995 if ((looper = Looper.myLooper()) == null) {
3996 looper = Looper.getMainLooper();
3997 }
3998 } else {
3999 looper = handler.getLooper();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004000 }
4001
4002 if (looper != null) {
Jean-Michel Trivi3b61d2d2016-01-07 10:50:09 -08004003 // implement the event handler delegate to receive events from audio service
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004004 mHandler = new Handler(looper) {
4005 @Override
4006 public void handleMessage(Message msg) {
Jean-Michel Trivi3b61d2d2016-01-07 10:50:09 -08004007 switch (msg.what) {
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -08004008 case MSSG_FOCUS_CHANGE: {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004009 final FocusRequestInfo fri = findFocusRequestInfo((String)msg.obj);
4010 if (fri != null) {
4011 final OnAudioFocusChangeListener listener =
4012 fri.mRequest.getOnAudioFocusChangeListener();
4013 if (listener != null) {
4014 Log.d(TAG, "dispatching onAudioFocusChange("
4015 + msg.arg1 + ") to " + msg.obj);
4016 listener.onAudioFocusChange(msg.arg1);
4017 }
Jean-Michel Trivi3b61d2d2016-01-07 10:50:09 -08004018 }
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -08004019 } break;
4020 case MSSG_RECORDING_CONFIG_CHANGE: {
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -08004021 final RecordConfigChangeCallbackData cbData =
4022 (RecordConfigChangeCallbackData) msg.obj;
4023 if (cbData.mCb != null) {
Jean-Michel Trivie6a505b2016-04-01 09:56:28 -07004024 cbData.mCb.onRecordingConfigChanged(cbData.mConfigs);
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08004025 }
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -08004026 } break;
4027 case MSSG_PLAYBACK_CONFIG_CHANGE: {
4028 final PlaybackConfigChangeCallbackData cbData =
4029 (PlaybackConfigChangeCallbackData) msg.obj;
4030 if (cbData.mCb != null) {
4031 if (DEBUG) {
4032 Log.d(TAG, "dispatching onPlaybackConfigChanged()");
4033 }
4034 cbData.mCb.onPlaybackConfigChanged(cbData.mConfigs);
4035 }
4036 } break;
Jean-Michel Trivi3b61d2d2016-01-07 10:50:09 -08004037 default:
4038 Log.e(TAG, "Unknown event " + msg.what);
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004039 }
4040 }
4041 };
4042 } else {
4043 mHandler = null;
4044 }
4045 }
4046
4047 Handler getHandler() {
4048 return mHandler;
4049 }
4050 }
4051
Glenn Kasten30c918c2011-11-10 17:56:41 -08004052 private final IAudioFocusDispatcher mAudioFocusDispatcher = new IAudioFocusDispatcher.Stub() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004053 @Override
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004054 public void dispatchAudioFocusChange(int focusChange, String id) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004055 final FocusRequestInfo fri = findFocusRequestInfo(id);
4056 if (fri != null) {
4057 final OnAudioFocusChangeListener listener =
4058 fri.mRequest.getOnAudioFocusChangeListener();
4059 if (listener != null) {
4060 final Handler h = (fri.mHandler == null) ?
4061 mServiceEventHandlerDelegate.getHandler() : fri.mHandler;
4062 final Message m = h.obtainMessage(
4063 MSSG_FOCUS_CHANGE/*what*/, focusChange/*arg1*/, 0/*arg2 ignored*/,
4064 id/*obj*/);
4065 h.sendMessage(m);
4066 }
4067 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004068 }
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004069
4070 @Override
4071 public void dispatchFocusResultFromExtPolicy(int requestResult, String clientId) {
4072 synchronized (mFocusRequestsLock) {
4073 // TODO use generation counter as the key instead
4074 final BlockingFocusResultReceiver focusReceiver =
4075 mFocusRequestsAwaitingResult.remove(clientId);
4076 if (focusReceiver != null) {
4077 focusReceiver.notifyResult(requestResult);
4078 } else {
4079 Log.e(TAG, "dispatchFocusResultFromExtPolicy found no result receiver");
4080 }
4081 }
4082 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004083 };
4084
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004085 private String getIdForAudioFocusListener(OnAudioFocusChangeListener l) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004086 if (l == null) {
Jean-Michel Trivi308e9a52010-03-17 15:04:20 -07004087 return new String(this.toString());
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004088 } else {
4089 return new String(this.toString() + l.toString());
4090 }
4091 }
4092
4093 /**
Xavier Ducrohet5f53f082010-05-11 14:19:40 -07004094 * @hide
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004095 * Registers a listener to be called when audio focus changes and keeps track of the associated
4096 * focus request (including Handler to use for the listener).
4097 * @param afr the full request parameters
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004098 */
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004099 public void registerAudioFocusRequest(@NonNull AudioFocusRequest afr) {
4100 final Handler h = afr.getOnAudioFocusChangeListenerHandler();
4101 final FocusRequestInfo fri = new FocusRequestInfo(afr, (h == null) ? null :
4102 new ServiceEventHandlerDelegate(h).getHandler());
4103 final String key = getIdForAudioFocusListener(afr.getOnAudioFocusChangeListener());
4104 mAudioFocusIdListenerMap.put(key, fri);
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004105 }
4106
4107 /**
Xavier Ducrohet5f53f082010-05-11 14:19:40 -07004108 * @hide
Jean-Michel Trivi46b3a182010-05-11 11:22:54 -07004109 * Causes the specified listener to not be called anymore when focus is gained or lost.
4110 * @param l the listener to unregister.
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004111 */
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004112 public void unregisterAudioFocusRequest(OnAudioFocusChangeListener l) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004113 // remove locally
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004114 mAudioFocusIdListenerMap.remove(getIdForAudioFocusListener(l));
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004115 }
4116
4117
4118 /**
Jean-Michel Trivi46b3a182010-05-11 11:22:54 -07004119 * A failed focus change request.
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004120 */
4121 public static final int AUDIOFOCUS_REQUEST_FAILED = 0;
4122 /**
Jean-Michel Trivi46b3a182010-05-11 11:22:54 -07004123 * A successful focus change request.
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004124 */
4125 public static final int AUDIOFOCUS_REQUEST_GRANTED = 1;
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004126 /**
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004127 * A focus change request whose granting is delayed: the request was successful, but the
4128 * requester will only be granted audio focus once the condition that prevented immediate
4129 * granting has ended.
Jean-Michel Trivi3db31ac2017-02-24 11:40:37 -08004130 * See {@link #requestAudioFocus(AudioFocusRequest)} and
4131 * {@link AudioFocusRequest.Builder#setAcceptsDelayedFocusGain(boolean)}
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004132 */
4133 public static final int AUDIOFOCUS_REQUEST_DELAYED = 2;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004134
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004135 /** @hide */
4136 @IntDef(flag = false, prefix = "AUDIOFOCUS_REQUEST", value = {
4137 AUDIOFOCUS_REQUEST_FAILED,
4138 AUDIOFOCUS_REQUEST_GRANTED,
4139 AUDIOFOCUS_REQUEST_DELAYED }
4140 )
4141 @Retention(RetentionPolicy.SOURCE)
4142 public @interface FocusRequestResult {}
4143
4144 /**
4145 * @hide
4146 * code returned when a synchronous focus request on the client-side is to be blocked
4147 * until the external audio focus policy decides on the response for the client
4148 */
4149 public static final int AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY = 100;
4150
4151 /**
4152 * Timeout duration in ms when waiting on an external focus policy for the result for a
4153 * focus request
4154 */
Weilin Xu1017d432022-06-08 00:27:52 +00004155 private static final int EXT_FOCUS_POLICY_TIMEOUT_MS = 250;
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004156
4157 private static final String FOCUS_CLIENT_ID_STRING = "android_audio_focus_client_id";
4158
4159 private final Object mFocusRequestsLock = new Object();
4160 /**
4161 * Map of all receivers of focus request results, one per unresolved focus request.
4162 * Receivers are added before sending the request to the external focus policy,
4163 * and are removed either after receiving the result, or after the timeout.
4164 * This variable is lazily initialized.
4165 */
4166 @GuardedBy("mFocusRequestsLock")
4167 private HashMap<String, BlockingFocusResultReceiver> mFocusRequestsAwaitingResult;
4168
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004169
4170 /**
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004171 * Request audio focus.
Jean-Michel Trivi46b3a182010-05-11 11:22:54 -07004172 * Send a request to obtain the audio focus
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004173 * @param l the listener to be notified of audio focus changes
4174 * @param streamType the main audio stream type affected by the focus request
4175 * @param durationHint use {@link #AUDIOFOCUS_GAIN_TRANSIENT} to indicate this focus request
4176 * is temporary, and focus will be abandonned shortly. Examples of transient requests are
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07004177 * for the playback of driving directions, or notifications sounds.
Jean-Michel Trivi983ac2b2010-03-19 12:09:25 -07004178 * Use {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} to indicate also that it's ok for
4179 * the previous focus owner to keep playing if it ducks its audio output.
Jean-Michel Trivi9171db22013-07-31 17:11:12 -07004180 * Alternatively use {@link #AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE} for a temporary request
4181 * that benefits from the system not playing disruptive sounds like notifications, for
4182 * usecases such as voice memo recording, or speech recognition.
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07004183 * Use {@link #AUDIOFOCUS_GAIN} for a focus request of unknown duration such
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004184 * as the playback of a song or a video.
4185 * @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
Jean-Michel Trivi36bdb662017-03-09 11:56:51 -08004186 * @deprecated use {@link #requestAudioFocus(AudioFocusRequest)}
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004187 */
4188 public int requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint) {
Jean-Michel Trivi36bdb662017-03-09 11:56:51 -08004189 PlayerBase.deprecateStreamTypeForPlayback(streamType,
4190 "AudioManager", "requestAudioFocus()");
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004191 int status = AUDIOFOCUS_REQUEST_FAILED;
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004192
4193 try {
4194 // status is guaranteed to be either AUDIOFOCUS_REQUEST_FAILED or
4195 // AUDIOFOCUS_REQUEST_GRANTED as focus is requested without the
4196 // AUDIOFOCUS_FLAG_DELAY_OK flag
4197 status = requestAudioFocus(l,
4198 new AudioAttributes.Builder()
4199 .setInternalLegacyStreamType(streamType).build(),
4200 durationHint,
4201 0 /* flags, legacy behavior */);
4202 } catch (IllegalArgumentException e) {
4203 Log.e(TAG, "Audio focus request denied due to ", e);
4204 }
4205
4206 return status;
4207 }
4208
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004209 // when adding new flags, add them to the relevant AUDIOFOCUS_FLAGS_APPS or SYSTEM masks
Jean-Michel Trivi0212be52014-11-24 14:43:10 -08004210 /**
4211 * @hide
4212 * Use this flag when requesting audio focus to indicate it is ok for the requester to not be
4213 * granted audio focus immediately (as indicated by {@link #AUDIOFOCUS_REQUEST_DELAYED}) when
4214 * the system is in a state where focus cannot change, but be granted focus later when
4215 * this condition ends.
4216 */
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004217 @SystemApi
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004218 public static final int AUDIOFOCUS_FLAG_DELAY_OK = 0x1 << 0;
Jean-Michel Trivi0212be52014-11-24 14:43:10 -08004219 /**
4220 * @hide
4221 * Use this flag when requesting audio focus to indicate that the requester
4222 * will pause its media playback (if applicable) when losing audio focus with
4223 * {@link #AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}, rather than ducking.
4224 * <br>On some platforms, the ducking may be handled without the application being aware of it
4225 * (i.e. it will not transiently lose focus). For applications that for instance play spoken
4226 * content, such as audio book or podcast players, ducking may never be acceptable, and will
4227 * thus always pause. This flag enables them to be declared as such whenever they request focus.
4228 */
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004229 @SystemApi
Jean-Michel Trivi0212be52014-11-24 14:43:10 -08004230 public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 0x1 << 1;
4231 /**
4232 * @hide
4233 * Use this flag to lock audio focus so granting is temporarily disabled.
4234 * <br>This flag can only be used by owners of a registered
4235 * {@link android.media.audiopolicy.AudioPolicy} in
4236 * {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int, AudioPolicy)}
4237 */
4238 @SystemApi
4239 public static final int AUDIOFOCUS_FLAG_LOCK = 0x1 << 2;
Jean-Michel Trivi28602092021-03-24 11:14:47 -07004240
4241 /**
4242 * @hide
4243 * flag set on test API calls,
4244 * see {@link #requestAudioFocusForTest(AudioFocusRequest, String, int, int)},
Jean-Michel Trivibec223b2021-12-08 18:40:08 -08004245 */
Jean-Michel Trivi28602092021-03-24 11:14:47 -07004246 public static final int AUDIOFOCUS_FLAG_TEST = 0x1 << 3;
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004247 /** @hide */
Jean-Michel Trivi0212be52014-11-24 14:43:10 -08004248 public static final int AUDIOFOCUS_FLAGS_APPS = AUDIOFOCUS_FLAG_DELAY_OK
4249 | AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS;
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004250 /** @hide */
4251 public static final int AUDIOFOCUS_FLAGS_SYSTEM = AUDIOFOCUS_FLAG_DELAY_OK
Jean-Michel Trivi0212be52014-11-24 14:43:10 -08004252 | AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS | AUDIOFOCUS_FLAG_LOCK;
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004253
4254 /**
Jean-Michel Trivi3db31ac2017-02-24 11:40:37 -08004255 * Request audio focus.
4256 * See the {@link AudioFocusRequest} for information about the options available to configure
4257 * your request, and notification of focus gain and loss.
4258 * @param focusRequest a {@link AudioFocusRequest} instance used to configure how focus is
4259 * requested.
4260 * @return {@link #AUDIOFOCUS_REQUEST_FAILED}, {@link #AUDIOFOCUS_REQUEST_GRANTED}
4261 * or {@link #AUDIOFOCUS_REQUEST_DELAYED}.
4262 * <br>Note that the return value is never {@link #AUDIOFOCUS_REQUEST_DELAYED} when focus
4263 * is requested without building the {@link AudioFocusRequest} with
4264 * {@link AudioFocusRequest.Builder#setAcceptsDelayedFocusGain(boolean)} set to
4265 * {@code true}.
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004266 * @throws NullPointerException if passed a null argument
Jean-Michel Trivi3db31ac2017-02-24 11:40:37 -08004267 */
4268 public int requestAudioFocus(@NonNull AudioFocusRequest focusRequest) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004269 return requestAudioFocus(focusRequest, null /* no AudioPolicy*/);
Jean-Michel Trivi3db31ac2017-02-24 11:40:37 -08004270 }
4271
4272 /**
4273 * Abandon audio focus. Causes the previous focus owner, if any, to receive focus.
4274 * @param focusRequest the {@link AudioFocusRequest} that was used when requesting focus
4275 * with {@link #requestAudioFocus(AudioFocusRequest)}.
4276 * @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
4277 * @throws IllegalArgumentException if passed a null argument
4278 */
4279 public int abandonAudioFocusRequest(@NonNull AudioFocusRequest focusRequest) {
4280 if (focusRequest == null) {
4281 throw new IllegalArgumentException("Illegal null AudioFocusRequest");
4282 }
4283 return abandonAudioFocus(focusRequest.getOnAudioFocusChangeListener(),
4284 focusRequest.getAudioAttributes());
4285 }
4286
4287 /**
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004288 * @hide
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004289 * Request audio focus.
4290 * Send a request to obtain the audio focus. This method differs from
4291 * {@link #requestAudioFocus(OnAudioFocusChangeListener, int, int)} in that it can express
4292 * that the requester accepts delayed grants of audio focus.
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004293 * @param l the listener to be notified of audio focus changes. It is not allowed to be null
4294 * when the request is flagged with {@link #AUDIOFOCUS_FLAG_DELAY_OK}.
4295 * @param requestAttributes non null {@link AudioAttributes} describing the main reason for
4296 * requesting audio focus.
4297 * @param durationHint use {@link #AUDIOFOCUS_GAIN_TRANSIENT} to indicate this focus request
4298 * is temporary, and focus will be abandonned shortly. Examples of transient requests are
4299 * for the playback of driving directions, or notifications sounds.
4300 * Use {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} to indicate also that it's ok for
4301 * the previous focus owner to keep playing if it ducks its audio output.
4302 * Alternatively use {@link #AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE} for a temporary request
4303 * that benefits from the system not playing disruptive sounds like notifications, for
4304 * usecases such as voice memo recording, or speech recognition.
4305 * Use {@link #AUDIOFOCUS_GAIN} for a focus request of unknown duration such
4306 * as the playback of a song or a video.
Jean-Michel Trivi26ae15f2017-08-25 19:20:49 -07004307 * @param flags 0 or a combination of {link #AUDIOFOCUS_FLAG_DELAY_OK},
4308 * {@link #AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS} and {@link #AUDIOFOCUS_FLAG_LOCK}.
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004309 * <br>Use 0 when not using any flags for the request, which behaves like
4310 * {@link #requestAudioFocus(OnAudioFocusChangeListener, int, int)}, where either audio
4311 * focus is granted immediately, or the grant request fails because the system is in a
4312 * state where focus cannot change (e.g. a phone call).
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004313 * @return {@link #AUDIOFOCUS_REQUEST_FAILED}, {@link #AUDIOFOCUS_REQUEST_GRANTED}
4314 * or {@link #AUDIOFOCUS_REQUEST_DELAYED}.
4315 * The return value is never {@link #AUDIOFOCUS_REQUEST_DELAYED} when focus is requested
4316 * without the {@link #AUDIOFOCUS_FLAG_DELAY_OK} flag.
4317 * @throws IllegalArgumentException
4318 */
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004319 @SystemApi
Jean-Michel Trivi26ae15f2017-08-25 19:20:49 -07004320 @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004321 public int requestAudioFocus(OnAudioFocusChangeListener l,
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004322 @NonNull AudioAttributes requestAttributes,
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004323 int durationHint,
4324 int flags) throws IllegalArgumentException {
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004325 if (flags != (flags & AUDIOFOCUS_FLAGS_APPS)) {
4326 throw new IllegalArgumentException("Invalid flags 0x"
4327 + Integer.toHexString(flags).toUpperCase());
4328 }
4329 return requestAudioFocus(l, requestAttributes, durationHint,
4330 flags & AUDIOFOCUS_FLAGS_APPS,
4331 null /* no AudioPolicy*/);
4332 }
4333
4334 /**
4335 * @hide
4336 * Request or lock audio focus.
4337 * This method is to be used by system components that have registered an
4338 * {@link android.media.audiopolicy.AudioPolicy} to request audio focus, but also to "lock" it
4339 * so focus granting is temporarily disabled.
4340 * @param l see the description of the same parameter in
4341 * {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int)}
4342 * @param requestAttributes non null {@link AudioAttributes} describing the main reason for
4343 * requesting audio focus.
4344 * @param durationHint see the description of the same parameter in
4345 * {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int)}
4346 * @param flags 0 or a combination of {link #AUDIOFOCUS_FLAG_DELAY_OK},
Jean-Michel Trivi0212be52014-11-24 14:43:10 -08004347 * {@link #AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS}, and {@link #AUDIOFOCUS_FLAG_LOCK}.
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004348 * <br>Use 0 when not using any flags for the request, which behaves like
4349 * {@link #requestAudioFocus(OnAudioFocusChangeListener, int, int)}, where either audio
4350 * focus is granted immediately, or the grant request fails because the system is in a
4351 * state where focus cannot change (e.g. a phone call).
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004352 * @param ap a registered {@link android.media.audiopolicy.AudioPolicy} instance when locking
4353 * focus, or null.
4354 * @return see the description of the same return value in
4355 * {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int)}
4356 * @throws IllegalArgumentException
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004357 * @deprecated use {@link #requestAudioFocus(AudioFocusRequest, AudioPolicy)}
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004358 */
Jean-Michel Trivi1b3541d2014-11-25 12:53:41 -08004359 @SystemApi
Jean-Michel Trivi26ae15f2017-08-25 19:20:49 -07004360 @RequiresPermission(anyOf= {
4361 android.Manifest.permission.MODIFY_PHONE_STATE,
4362 android.Manifest.permission.MODIFY_AUDIO_ROUTING
4363 })
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004364 public int requestAudioFocus(OnAudioFocusChangeListener l,
4365 @NonNull AudioAttributes requestAttributes,
4366 int durationHint,
4367 int flags,
4368 AudioPolicy ap) throws IllegalArgumentException {
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004369 // parameter checking
4370 if (requestAttributes == null) {
4371 throw new IllegalArgumentException("Illegal null AudioAttributes argument");
4372 }
Jean-Michel Trivi3db31ac2017-02-24 11:40:37 -08004373 if (!AudioFocusRequest.isValidFocusGain(durationHint)) {
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004374 throw new IllegalArgumentException("Invalid duration hint");
Jean-Michel Trivi55d1bb32010-04-01 17:40:58 -07004375 }
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004376 if (flags != (flags & AUDIOFOCUS_FLAGS_SYSTEM)) {
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004377 throw new IllegalArgumentException("Illegal flags 0x"
4378 + Integer.toHexString(flags).toUpperCase());
4379 }
4380 if (((flags & AUDIOFOCUS_FLAG_DELAY_OK) == AUDIOFOCUS_FLAG_DELAY_OK) && (l == null)) {
4381 throw new IllegalArgumentException(
4382 "Illegal null focus listener when flagged as accepting delayed focus grant");
4383 }
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004384 if (((flags & AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS)
4385 == AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) && (l == null)) {
4386 throw new IllegalArgumentException(
4387 "Illegal null focus listener when flagged as pausing instead of ducking");
4388 }
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004389 if (((flags & AUDIOFOCUS_FLAG_LOCK) == AUDIOFOCUS_FLAG_LOCK) && (ap == null)) {
4390 throw new IllegalArgumentException(
4391 "Illegal null audio policy when locking audio focus");
4392 }
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004393
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004394 final AudioFocusRequest afr = new AudioFocusRequest.Builder(durationHint)
Jean-Michel Trivi36728ce2017-05-01 12:33:40 -07004395 .setOnAudioFocusChangeListenerInt(l, null /* no Handler for this legacy API */)
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004396 .setAudioAttributes(requestAttributes)
4397 .setAcceptsDelayedFocusGain((flags & AUDIOFOCUS_FLAG_DELAY_OK)
4398 == AUDIOFOCUS_FLAG_DELAY_OK)
4399 .setWillPauseWhenDucked((flags & AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS)
4400 == AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS)
4401 .setLocksFocus((flags & AUDIOFOCUS_FLAG_LOCK) == AUDIOFOCUS_FLAG_LOCK)
4402 .build();
4403 return requestAudioFocus(afr, ap);
4404 }
4405
4406 /**
4407 * @hide
Jean-Michel Trivi28602092021-03-24 11:14:47 -07004408 * Test API to request audio focus for an arbitrary client operating from a (fake) given UID.
4409 * Used to simulate conditions of the test, not the behavior of the focus requester under test.
4410 * @param afr the parameters of the request
4411 * @param clientFakeId the identifier of the AudioManager the client would be requesting from
4412 * @param clientFakeUid the UID of the client, here an arbitrary int,
4413 * doesn't have to be a real UID
4414 * @param clientTargetSdk the target SDK used by the client
4415 * @return return code indicating status of the request
4416 */
4417 @TestApi
4418 @RequiresPermission("android.permission.QUERY_AUDIO_STATE")
4419 public @FocusRequestResult int requestAudioFocusForTest(@NonNull AudioFocusRequest afr,
4420 @NonNull String clientFakeId, int clientFakeUid, int clientTargetSdk) {
4421 Objects.requireNonNull(afr);
4422 Objects.requireNonNull(clientFakeId);
Oscar Azucena3209c342022-05-13 19:08:14 -07004423 int status;
Jean-Michel Trivi28602092021-03-24 11:14:47 -07004424 try {
Oscar Azucena3209c342022-05-13 19:08:14 -07004425 status = getService().requestAudioFocusForTest(afr.getAudioAttributes(),
Jean-Michel Trivi28602092021-03-24 11:14:47 -07004426 afr.getFocusGain(),
4427 mICallBack,
4428 mAudioFocusDispatcher,
Jean-Michel Trivibec223b2021-12-08 18:40:08 -08004429 clientFakeId, "com.android.test.fakeclient",
4430 afr.getFlags() | AudioManager.AUDIOFOCUS_FLAG_TEST,
4431 clientFakeUid, clientTargetSdk);
Jean-Michel Trivi28602092021-03-24 11:14:47 -07004432 } catch (RemoteException e) {
4433 throw e.rethrowFromSystemServer();
4434 }
Weilin Xu1017d432022-06-08 00:27:52 +00004435 if (status != AudioManager.AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY) {
4436 // default path with no external focus policy
4437 return status;
4438 }
Oscar Azucena3209c342022-05-13 19:08:14 -07004439
Weilin Xu1017d432022-06-08 00:27:52 +00004440 BlockingFocusResultReceiver focusReceiver;
4441 synchronized (mFocusRequestsLock) {
4442 focusReceiver = addClientIdToFocusReceiverLocked(clientFakeId);
4443 }
4444
4445 return handleExternalAudioPolicyWaitIfNeeded(clientFakeId, focusReceiver);
Jean-Michel Trivi28602092021-03-24 11:14:47 -07004446 }
4447
4448 /**
4449 * @hide
4450 * Test API to abandon audio focus for an arbitrary client.
4451 * Used to simulate conditions of the test, not the behavior of the focus requester under test.
4452 * @param afr the parameters used for the request
4453 * @param clientFakeId clientFakeId the identifier of the AudioManager from which the client
4454 * would be requesting
4455 * @return return code indicating status of the request
4456 */
4457 @TestApi
4458 @RequiresPermission("android.permission.QUERY_AUDIO_STATE")
4459 public @FocusRequestResult int abandonAudioFocusForTest(@NonNull AudioFocusRequest afr,
4460 @NonNull String clientFakeId) {
4461 Objects.requireNonNull(afr);
4462 Objects.requireNonNull(clientFakeId);
4463 try {
4464 return getService().abandonAudioFocusForTest(mAudioFocusDispatcher,
4465 clientFakeId, afr.getAudioAttributes(), "com.android.test.fakeclient");
4466 } catch (RemoteException e) {
4467 throw e.rethrowFromSystemServer();
4468 }
4469 }
4470
4471 /**
4472 * @hide
4473 * Return the duration of the fade out applied when a player of the given AudioAttributes
4474 * is losing audio focus
4475 * @param aa the AudioAttributes of the player losing focus with {@link #AUDIOFOCUS_LOSS}
4476 * @return a duration in ms, 0 indicates no fade out is applied
4477 */
4478 @TestApi
4479 @RequiresPermission("android.permission.QUERY_AUDIO_STATE")
4480 public @IntRange(from = 0) long getFadeOutDurationOnFocusLossMillis(@NonNull AudioAttributes aa)
4481 {
4482 Objects.requireNonNull(aa);
4483 try {
4484 return getService().getFadeOutDurationOnFocusLossMillis(aa);
4485 } catch (RemoteException e) {
4486 throw e.rethrowFromSystemServer();
4487 }
4488 }
4489
4490 /**
4491 * @hide
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004492 * Request or lock audio focus.
4493 * This method is to be used by system components that have registered an
4494 * {@link android.media.audiopolicy.AudioPolicy} to request audio focus, but also to "lock" it
4495 * so focus granting is temporarily disabled.
4496 * @param afr see the description of the same parameter in
4497 * {@link #requestAudioFocus(AudioFocusRequest)}
4498 * @param ap a registered {@link android.media.audiopolicy.AudioPolicy} instance when locking
4499 * focus, or null.
4500 * @return {@link #AUDIOFOCUS_REQUEST_FAILED}, {@link #AUDIOFOCUS_REQUEST_GRANTED}
4501 * or {@link #AUDIOFOCUS_REQUEST_DELAYED}.
4502 * @throws NullPointerException if the AudioFocusRequest is null
4503 * @throws IllegalArgumentException when trying to lock focus without an AudioPolicy
4504 */
4505 @SystemApi
Jean-Michel Trivi26ae15f2017-08-25 19:20:49 -07004506 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004507 public int requestAudioFocus(@NonNull AudioFocusRequest afr, @Nullable AudioPolicy ap) {
4508 if (afr == null) {
4509 throw new NullPointerException("Illegal null AudioFocusRequest");
4510 }
4511 // this can only be checked now, not during the creation of the AudioFocusRequest instance
4512 if (afr.locksFocus() && ap == null) {
4513 throw new IllegalArgumentException(
4514 "Illegal null audio policy when locking audio focus");
4515 }
4516 registerAudioFocusRequest(afr);
4517 final IAudioService service = getService();
4518 final int status;
Jean-Michel Trivi461922f2017-04-25 15:23:17 -07004519 int sdk;
4520 try {
4521 sdk = getContext().getApplicationInfo().targetSdkVersion;
4522 } catch (NullPointerException e) {
4523 // some tests don't have a Context
4524 sdk = Build.VERSION.SDK_INT;
4525 }
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004526
4527 final String clientId = getIdForAudioFocusListener(afr.getOnAudioFocusChangeListener());
Weilin Xu1017d432022-06-08 00:27:52 +00004528 BlockingFocusResultReceiver focusReceiver;
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004529 synchronized (mFocusRequestsLock) {
Weilin Xu1017d432022-06-08 00:27:52 +00004530
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004531 try {
4532 // TODO status contains result and generation counter for ext policy
4533 status = service.requestAudioFocus(afr.getAudioAttributes(),
4534 afr.getFocusGain(), mICallBack,
4535 mAudioFocusDispatcher,
4536 clientId,
John Wu4f7e5102021-06-22 17:29:11 +00004537 getContext().getOpPackageName() /* package name */,
4538 getContext().getAttributionTag(),
4539 afr.getFlags(),
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004540 ap != null ? ap.cb() : null,
4541 sdk);
4542 } catch (RemoteException e) {
4543 throw e.rethrowFromSystemServer();
4544 }
Weilin Xu1017d432022-06-08 00:27:52 +00004545 if (status != AudioManager.AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY) {
4546 // default path with no external focus policy
4547 return status;
4548 }
4549 focusReceiver = addClientIdToFocusReceiverLocked(clientId);
Oscar Azucena3209c342022-05-13 19:08:14 -07004550 }
4551
Weilin Xu1017d432022-06-08 00:27:52 +00004552 return handleExternalAudioPolicyWaitIfNeeded(clientId, focusReceiver);
4553 }
4554
4555 @GuardedBy("mFocusRequestsLock")
4556 private BlockingFocusResultReceiver addClientIdToFocusReceiverLocked(String clientId) {
4557 BlockingFocusResultReceiver focusReceiver;
4558 if (mFocusRequestsAwaitingResult == null) {
4559 mFocusRequestsAwaitingResult =
4560 new HashMap<String, BlockingFocusResultReceiver>(1);
4561 }
4562 focusReceiver = new BlockingFocusResultReceiver(clientId);
4563 mFocusRequestsAwaitingResult.put(clientId, focusReceiver);
4564 return focusReceiver;
Oscar Azucena3209c342022-05-13 19:08:14 -07004565 }
4566
4567 private @FocusRequestResult int handleExternalAudioPolicyWaitIfNeeded(String clientId,
Weilin Xu1017d432022-06-08 00:27:52 +00004568 BlockingFocusResultReceiver focusReceiver) {
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004569 focusReceiver.waitForResult(EXT_FOCUS_POLICY_TIMEOUT_MS);
4570 if (DEBUG && !focusReceiver.receivedResult()) {
Oscar Azucena3209c342022-05-13 19:08:14 -07004571 Log.e(TAG, "handleExternalAudioPolicyWaitIfNeeded"
4572 + " response from ext policy timed out, denying request");
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004573 }
Oscar Azucena3209c342022-05-13 19:08:14 -07004574
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004575 synchronized (mFocusRequestsLock) {
4576 mFocusRequestsAwaitingResult.remove(clientId);
4577 }
4578 return focusReceiver.requestResult();
4579 }
4580
4581 // helper class that abstracts out the handling of spurious wakeups in Object.wait()
4582 private static final class SafeWaitObject {
4583 private boolean mQuit = false;
4584
4585 public void safeNotify() {
4586 synchronized (this) {
4587 mQuit = true;
4588 this.notify();
4589 }
4590 }
4591
4592 public void safeWait(long millis) throws InterruptedException {
4593 final long timeOutTime = java.lang.System.currentTimeMillis() + millis;
4594 synchronized (this) {
4595 while (!mQuit) {
4596 final long timeToWait = timeOutTime - java.lang.System.currentTimeMillis();
4597 if (timeToWait < 0) { break; }
4598 this.wait(timeToWait);
4599 }
4600 }
4601 }
4602 }
4603
4604 private static final class BlockingFocusResultReceiver {
4605 private final SafeWaitObject mLock = new SafeWaitObject();
4606 @GuardedBy("mLock")
4607 private boolean mResultReceived = false;
4608 // request denied by default (e.g. timeout)
4609 private int mFocusRequestResult = AudioManager.AUDIOFOCUS_REQUEST_FAILED;
4610 private final String mFocusClientId;
4611
4612 BlockingFocusResultReceiver(String clientId) {
4613 mFocusClientId = clientId;
4614 }
4615
4616 boolean receivedResult() { return mResultReceived; }
4617 int requestResult() { return mFocusRequestResult; }
4618
4619 void notifyResult(int requestResult) {
4620 synchronized (mLock) {
4621 mResultReceived = true;
4622 mFocusRequestResult = requestResult;
4623 mLock.safeNotify();
4624 }
4625 }
4626
4627 public void waitForResult(long timeOutMs) {
4628 synchronized (mLock) {
4629 if (mResultReceived) {
4630 // the result was received before waiting
4631 return;
4632 }
4633 try {
4634 mLock.safeWait(timeOutMs);
4635 } catch (InterruptedException e) { }
4636 }
4637 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004638 }
4639
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08004640 /**
4641 * @hide
4642 * Used internally by telephony package to request audio focus. Will cause the focus request
4643 * to be associated with the "voice communication" identifier only used in AudioService
4644 * to identify this use case.
4645 * @param streamType use STREAM_RING for focus requests when ringing, VOICE_CALL for
4646 * the establishment of the call
4647 * @param durationHint the type of focus request. AUDIOFOCUS_GAIN_TRANSIENT is recommended so
4648 * media applications resume after a call
4649 */
Mathew Inwood8e742f92020-10-27 11:47:29 +00004650 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08004651 public void requestAudioFocusForCall(int streamType, int durationHint) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004652 final IAudioService service = getService();
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08004653 try {
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004654 service.requestAudioFocus(new AudioAttributes.Builder()
4655 .setInternalLegacyStreamType(streamType).build(),
4656 durationHint, mICallBack, null,
John Spurlock61560172015-02-06 19:46:04 -05004657 AudioSystem.IN_VOICE_COMM_FOCUS_ID,
Marco Nelissen29f16932015-04-17 09:50:56 -07004658 getContext().getOpPackageName(),
John Wu4f7e5102021-06-22 17:29:11 +00004659 getContext().getAttributionTag(),
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004660 AUDIOFOCUS_FLAG_LOCK,
Jean-Michel Trivi461922f2017-04-25 15:23:17 -07004661 null /* policy token */, 0 /* sdk n/a here*/);
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08004662 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07004663 throw e.rethrowFromSystemServer();
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08004664 }
4665 }
4666
4667 /**
4668 * @hide
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -08004669 * Return the volume ramping time for a sound to be played after the given focus request,
4670 * and to play a sound of the given attributes
4671 * @param focusGain
4672 * @param attr
4673 * @return
4674 */
4675 public int getFocusRampTimeMs(int focusGain, AudioAttributes attr) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004676 final IAudioService service = getService();
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -08004677 try {
4678 return service.getFocusRampTimeMs(focusGain, attr);
4679 } catch (RemoteException e) {
4680 throw e.rethrowFromSystemServer();
4681 }
4682 }
4683
4684 /**
4685 * @hide
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004686 * Set the result to the audio focus request received through
4687 * {@link AudioPolicyFocusListener#onAudioFocusRequest(AudioFocusInfo, int)}.
4688 * @param afi the information about the focus requester
4689 * @param requestResult the result to the focus request to be passed to the requester
4690 * @param ap a valid registered {@link AudioPolicy} configured as a focus policy.
4691 */
4692 @SystemApi
4693 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
4694 public void setFocusRequestResult(@NonNull AudioFocusInfo afi,
4695 @FocusRequestResult int requestResult, @NonNull AudioPolicy ap) {
4696 if (afi == null) {
4697 throw new IllegalArgumentException("Illegal null AudioFocusInfo");
4698 }
4699 if (ap == null) {
4700 throw new IllegalArgumentException("Illegal null AudioPolicy");
4701 }
4702 final IAudioService service = getService();
4703 try {
4704 service.setFocusRequestResultFromExtPolicy(afi, requestResult, ap.cb());
4705 } catch (RemoteException e) {
4706 throw e.rethrowFromSystemServer();
4707 }
4708 }
4709
4710 /**
4711 * @hide
Jean-Michel Trivi126cf032017-04-02 23:19:02 -07004712 * Notifies an application with a focus listener of gain or loss of audio focus.
4713 * This method can only be used by owners of an {@link AudioPolicy} configured with
4714 * {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to true.
4715 * @param afi the recipient of the focus change, that has previously requested audio focus, and
4716 * that was received by the {@code AudioPolicy} through
4717 * {@link AudioPolicy.AudioPolicyFocusListener#onAudioFocusRequest(AudioFocusInfo, int)}.
4718 * @param focusChange one of focus gain types ({@link #AUDIOFOCUS_GAIN},
4719 * {@link #AUDIOFOCUS_GAIN_TRANSIENT}, {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} or
4720 * {@link #AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE})
4721 * or one of the focus loss types ({@link AudioManager#AUDIOFOCUS_LOSS},
4722 * {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT},
4723 * or {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}).
4724 * <br>For the focus gain, the change type should be the same as the app requested.
4725 * @param ap a valid registered {@link AudioPolicy} configured as a focus policy.
4726 * @return {@link #AUDIOFOCUS_REQUEST_GRANTED} if the dispatch was successfully sent, or
4727 * {@link #AUDIOFOCUS_REQUEST_FAILED} if the focus client didn't have a listener, or
4728 * if there was an error sending the request.
4729 * @throws NullPointerException if the {@link AudioFocusInfo} or {@link AudioPolicy} are null.
4730 */
4731 @SystemApi
Jean-Michel Trivi26ae15f2017-08-25 19:20:49 -07004732 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
Jean-Michel Trivi126cf032017-04-02 23:19:02 -07004733 public int dispatchAudioFocusChange(@NonNull AudioFocusInfo afi, int focusChange,
4734 @NonNull AudioPolicy ap) {
4735 if (afi == null) {
4736 throw new NullPointerException("Illegal null AudioFocusInfo");
4737 }
4738 if (ap == null) {
4739 throw new NullPointerException("Illegal null AudioPolicy");
4740 }
4741 final IAudioService service = getService();
4742 try {
4743 return service.dispatchFocusChange(afi, focusChange, ap.cb());
4744 } catch (RemoteException e) {
4745 throw e.rethrowFromSystemServer();
4746 }
4747 }
4748
4749 /**
4750 * @hide
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08004751 * Used internally by telephony package to abandon audio focus, typically after a call or
4752 * when ringing ends and the call is rejected or not answered.
4753 * Should match one or more calls to {@link #requestAudioFocusForCall(int, int)}.
4754 */
Mathew Inwood8e742f92020-10-27 11:47:29 +00004755 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08004756 public void abandonAudioFocusForCall() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004757 final IAudioService service = getService();
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08004758 try {
John Spurlock61560172015-02-06 19:46:04 -05004759 service.abandonAudioFocus(null, AudioSystem.IN_VOICE_COMM_FOCUS_ID,
Jean-Michel Trivi126cf032017-04-02 23:19:02 -07004760 null /*AudioAttributes, legacy behavior*/, getContext().getOpPackageName());
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08004761 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07004762 throw e.rethrowFromSystemServer();
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08004763 }
4764 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004765
4766 /**
Jean-Michel Trivi46b3a182010-05-11 11:22:54 -07004767 * Abandon audio focus. Causes the previous focus owner, if any, to receive focus.
4768 * @param l the listener with which focus was requested.
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004769 * @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
Jean-Michel Trivi36bdb662017-03-09 11:56:51 -08004770 * @deprecated use {@link #abandonAudioFocusRequest(AudioFocusRequest)}
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004771 */
4772 public int abandonAudioFocus(OnAudioFocusChangeListener l) {
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004773 return abandonAudioFocus(l, null /*AudioAttributes, legacy behavior*/);
4774 }
4775
4776 /**
4777 * @hide
4778 * Abandon audio focus. Causes the previous focus owner, if any, to receive focus.
4779 * @param l the listener with which focus was requested.
4780 * @param aa the {@link AudioAttributes} with which audio focus was requested
4781 * @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004782 * @deprecated use {@link #abandonAudioFocusRequest(AudioFocusRequest)}
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004783 */
4784 @SystemApi
Aurimas Liutikas1394a012020-11-12 18:26:09 -08004785 @SuppressLint("RequiresPermission") // no permission enforcement, but only "undoes" what would
4786 // have been done by a matching requestAudioFocus
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004787 public int abandonAudioFocus(OnAudioFocusChangeListener l, AudioAttributes aa) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004788 int status = AUDIOFOCUS_REQUEST_FAILED;
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004789 unregisterAudioFocusRequest(l);
4790 final IAudioService service = getService();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004791 try {
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004792 status = service.abandonAudioFocus(mAudioFocusDispatcher,
Jean-Michel Trivi126cf032017-04-02 23:19:02 -07004793 getIdForAudioFocusListener(l), aa, getContext().getOpPackageName());
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004794 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07004795 throw e.rethrowFromSystemServer();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004796 }
4797 return status;
4798 }
4799
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004800 //====================================================================
4801 // Remote Control
4802 /**
Jean-Michel Trivi46b3a182010-05-11 11:22:54 -07004803 * Register a component to be the sole receiver of MEDIA_BUTTON intents.
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004804 * @param eventReceiver identifier of a {@link android.content.BroadcastReceiver}
4805 * that will receive the media button intent. This broadcast receiver must be declared
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004806 * in the application manifest. The package of the component must match that of
4807 * the context you're registering from.
RoboErikb214efb2014-07-24 13:20:30 -07004808 * @deprecated Use {@link MediaSession#setMediaButtonReceiver(PendingIntent)} instead.
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004809 */
RoboErikb214efb2014-07-24 13:20:30 -07004810 @Deprecated
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004811 public void registerMediaButtonEventReceiver(ComponentName eventReceiver) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004812 if (eventReceiver == null) {
4813 return;
4814 }
Marco Nelissen29f16932015-04-17 09:50:56 -07004815 if (!eventReceiver.getPackageName().equals(getContext().getPackageName())) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004816 Log.e(TAG, "registerMediaButtonEventReceiver() error: " +
4817 "receiver and context package names don't match");
4818 return;
4819 }
4820 // construct a PendingIntent for the media button and register it
4821 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
4822 // the associated intent will be handled by the component being registered
4823 mediaButtonIntent.setComponent(eventReceiver);
Marco Nelissen29f16932015-04-17 09:50:56 -07004824 PendingIntent pi = PendingIntent.getBroadcast(getContext(),
Jean-Michel Triviec84dae2020-10-22 17:12:37 -07004825 0/*requestCode, ignored*/, mediaButtonIntent,
4826 PendingIntent.FLAG_IMMUTABLE);
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004827 registerMediaButtonIntent(pi, eventReceiver);
4828 }
4829
4830 /**
Dianne Hackborn961cae92013-03-20 14:59:43 -07004831 * Register a component to be the sole receiver of MEDIA_BUTTON intents. This is like
4832 * {@link #registerMediaButtonEventReceiver(android.content.ComponentName)}, but allows
4833 * the buttons to go to any PendingIntent. Note that you should only use this form if
4834 * you know you will continue running for the full time until unregistering the
4835 * PendingIntent.
4836 * @param eventReceiver target that will receive media button intents. The PendingIntent
RoboErikb214efb2014-07-24 13:20:30 -07004837 * will be sent an {@link Intent#ACTION_MEDIA_BUTTON} event when a media button action
4838 * occurs, with {@link Intent#EXTRA_KEY_EVENT} added and holding the key code of the
4839 * media button that was pressed.
4840 * @deprecated Use {@link MediaSession#setMediaButtonReceiver(PendingIntent)} instead.
Dianne Hackborn961cae92013-03-20 14:59:43 -07004841 */
RoboErikb214efb2014-07-24 13:20:30 -07004842 @Deprecated
Dianne Hackborn961cae92013-03-20 14:59:43 -07004843 public void registerMediaButtonEventReceiver(PendingIntent eventReceiver) {
4844 if (eventReceiver == null) {
4845 return;
4846 }
4847 registerMediaButtonIntent(eventReceiver, null);
4848 }
4849
4850 /**
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004851 * @hide
4852 * no-op if (pi == null) or (eventReceiver == null)
4853 */
4854 public void registerMediaButtonIntent(PendingIntent pi, ComponentName eventReceiver) {
Dianne Hackborn961cae92013-03-20 14:59:43 -07004855 if (pi == null) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004856 Log.e(TAG, "Cannot call registerMediaButtonIntent() with a null parameter");
4857 return;
4858 }
Marco Nelissen29f16932015-04-17 09:50:56 -07004859 MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
4860 helper.addMediaButtonListener(pi, eventReceiver, getContext());
Jean-Michel Trivi722b8082012-05-15 15:18:33 -07004861 }
4862
4863 /**
Jean-Michel Trivi46b3a182010-05-11 11:22:54 -07004864 * Unregister the receiver of MEDIA_BUTTON intents.
4865 * @param eventReceiver identifier of a {@link android.content.BroadcastReceiver}
4866 * that was registered with {@link #registerMediaButtonEventReceiver(ComponentName)}.
RoboErikb214efb2014-07-24 13:20:30 -07004867 * @deprecated Use {@link MediaSession} instead.
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004868 */
RoboErikb214efb2014-07-24 13:20:30 -07004869 @Deprecated
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004870 public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004871 if (eventReceiver == null) {
4872 return;
4873 }
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004874 // construct a PendingIntent for the media button and unregister it
4875 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
4876 // the associated intent will be handled by the component being registered
4877 mediaButtonIntent.setComponent(eventReceiver);
Marco Nelissen29f16932015-04-17 09:50:56 -07004878 PendingIntent pi = PendingIntent.getBroadcast(getContext(),
Jean-Michel Triviec84dae2020-10-22 17:12:37 -07004879 0/*requestCode, ignored*/, mediaButtonIntent,
4880 PendingIntent.FLAG_IMMUTABLE);
Jean-Michel Trivib839b832013-03-30 14:17:45 -07004881 unregisterMediaButtonIntent(pi);
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004882 }
4883
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004884 /**
Dianne Hackborn961cae92013-03-20 14:59:43 -07004885 * Unregister the receiver of MEDIA_BUTTON intents.
4886 * @param eventReceiver same PendingIntent that was registed with
4887 * {@link #registerMediaButtonEventReceiver(PendingIntent)}.
RoboErikb214efb2014-07-24 13:20:30 -07004888 * @deprecated Use {@link MediaSession} instead.
Dianne Hackborn961cae92013-03-20 14:59:43 -07004889 */
RoboErikb214efb2014-07-24 13:20:30 -07004890 @Deprecated
Dianne Hackborn961cae92013-03-20 14:59:43 -07004891 public void unregisterMediaButtonEventReceiver(PendingIntent eventReceiver) {
4892 if (eventReceiver == null) {
4893 return;
4894 }
Jean-Michel Trivib839b832013-03-30 14:17:45 -07004895 unregisterMediaButtonIntent(eventReceiver);
Dianne Hackborn961cae92013-03-20 14:59:43 -07004896 }
4897
4898 /**
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004899 * @hide
4900 */
Jean-Michel Trivib839b832013-03-30 14:17:45 -07004901 public void unregisterMediaButtonIntent(PendingIntent pi) {
Marco Nelissen29f16932015-04-17 09:50:56 -07004902 MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
RoboErik430fc482014-06-12 15:49:20 -07004903 helper.removeMediaButtonListener(pi);
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004904 }
Jean-Michel Trivi178889e2011-08-15 17:17:52 -07004905
4906 /**
Jean-Michel Trivi178889e2011-08-15 17:17:52 -07004907 * Registers the remote control client for providing information to display on the remote
4908 * controls.
Jean-Michel Trivi466ade52011-09-01 10:02:30 -07004909 * @param rcClient The remote control client from which remote controls will receive
4910 * information to display.
4911 * @see RemoteControlClient
RoboErikb214efb2014-07-24 13:20:30 -07004912 * @deprecated Use {@link MediaSession} instead.
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004913 */
RoboErikb214efb2014-07-24 13:20:30 -07004914 @Deprecated
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004915 public void registerRemoteControlClient(RemoteControlClient rcClient) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004916 if ((rcClient == null) || (rcClient.getRcMediaIntent() == null)) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004917 return;
4918 }
Marco Nelissen29f16932015-04-17 09:50:56 -07004919 rcClient.registerWithSession(MediaSessionLegacyHelper.getHelper(getContext()));
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004920 }
4921
4922 /**
Jean-Michel Trivifcd693a2011-08-11 13:53:55 -07004923 * Unregisters the remote control client that was providing information to display on the
Jean-Michel Trivi466ade52011-09-01 10:02:30 -07004924 * remote controls.
4925 * @param rcClient The remote control client to unregister.
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004926 * @see #registerRemoteControlClient(RemoteControlClient)
RoboErikb214efb2014-07-24 13:20:30 -07004927 * @deprecated Use {@link MediaSession} instead.
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004928 */
RoboErikb214efb2014-07-24 13:20:30 -07004929 @Deprecated
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004930 public void unregisterRemoteControlClient(RemoteControlClient rcClient) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004931 if ((rcClient == null) || (rcClient.getRcMediaIntent() == null)) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004932 return;
4933 }
Marco Nelissen29f16932015-04-17 09:50:56 -07004934 rcClient.unregisterWithSession(MediaSessionLegacyHelper.getHelper(getContext()));
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004935 }
4936
Jean-Michel Trivi44413e52011-08-23 18:20:03 -07004937 /**
RoboErika66c40b2014-08-15 15:21:41 -07004938 * Registers a {@link RemoteController} instance for it to receive media
4939 * metadata updates and playback state information from applications using
4940 * {@link RemoteControlClient}, and control their playback.
4941 * <p>
John Spurlockee5ad722015-03-03 16:17:21 -05004942 * Registration requires the {@link RemoteController.OnClientUpdateListener} listener to be
RoboErika66c40b2014-08-15 15:21:41 -07004943 * one of the enabled notification listeners (see
Jean-Michel Trivif108cdd92013-09-27 18:37:36 -07004944 * {@link android.service.notification.NotificationListenerService}).
RoboErika66c40b2014-08-15 15:21:41 -07004945 *
Jean-Michel Trivia83487e2013-09-17 21:19:30 -07004946 * @param rctlr the object to register.
RoboErika66c40b2014-08-15 15:21:41 -07004947 * @return true if the {@link RemoteController} was successfully registered,
4948 * false if an error occurred, due to an internal system error, or
4949 * insufficient permissions.
RoboErikb214efb2014-07-24 13:20:30 -07004950 * @deprecated Use
RoboErika66c40b2014-08-15 15:21:41 -07004951 * {@link MediaSessionManager#addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, ComponentName)}
4952 * and {@link MediaController} instead.
Jean-Michel Trivi7ddd2262013-09-01 18:06:45 -07004953 */
RoboErikb214efb2014-07-24 13:20:30 -07004954 @Deprecated
Jean-Michel Trivi7ddd2262013-09-01 18:06:45 -07004955 public boolean registerRemoteController(RemoteController rctlr) {
4956 if (rctlr == null) {
4957 return false;
4958 }
RoboErik430fc482014-06-12 15:49:20 -07004959 rctlr.startListeningToSessions();
4960 return true;
Jean-Michel Trivi7ddd2262013-09-01 18:06:45 -07004961 }
4962
4963 /**
RoboErika66c40b2014-08-15 15:21:41 -07004964 * Unregisters a {@link RemoteController}, causing it to no longer receive
4965 * media metadata and playback state information, and no longer be capable
4966 * of controlling playback.
4967 *
Jean-Michel Trivia83487e2013-09-17 21:19:30 -07004968 * @param rctlr the object to unregister.
RoboErikb214efb2014-07-24 13:20:30 -07004969 * @deprecated Use
RoboErika66c40b2014-08-15 15:21:41 -07004970 * {@link MediaSessionManager#removeOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener)}
4971 * instead.
Jean-Michel Trivi7ddd2262013-09-01 18:06:45 -07004972 */
RoboErikb214efb2014-07-24 13:20:30 -07004973 @Deprecated
Jean-Michel Trivi7ddd2262013-09-01 18:06:45 -07004974 public void unregisterRemoteController(RemoteController rctlr) {
4975 if (rctlr == null) {
4976 return;
4977 }
RoboErik430fc482014-06-12 15:49:20 -07004978 rctlr.stopListeningToSessions();
Jean-Michel Trivi7ddd2262013-09-01 18:06:45 -07004979 }
4980
Jean-Michel Trivic3c4bab2013-04-19 08:56:50 -07004981
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08004982 //====================================================================
4983 // Audio policy
Jean-Michel Trivic3c4bab2013-04-19 08:56:50 -07004984 /**
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07004985 * @hide
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07004986 * Register the given {@link AudioPolicy}.
4987 * This call is synchronous and blocks until the registration process successfully completed
4988 * or failed to complete.
Jean-Michel Trivi1b3541d2014-11-25 12:53:41 -08004989 * @param policy the non-null {@link AudioPolicy} to register.
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07004990 * @return {@link #ERROR} if there was an error communicating with the registration service
4991 * or if the user doesn't have the required
4992 * {@link android.Manifest.permission#MODIFY_AUDIO_ROUTING} permission,
4993 * {@link #SUCCESS} otherwise.
4994 */
Jean-Michel Trivi1b3541d2014-11-25 12:53:41 -08004995 @SystemApi
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -06004996 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
Jean-Michel Trivi1b3541d2014-11-25 12:53:41 -08004997 public int registerAudioPolicy(@NonNull AudioPolicy policy) {
Robert Berry669be042019-01-22 18:48:35 -05004998 return registerAudioPolicyStatic(policy);
4999 }
5000
5001 static int registerAudioPolicyStatic(@NonNull AudioPolicy policy) {
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005002 if (policy == null) {
5003 throw new IllegalArgumentException("Illegal null AudioPolicy argument");
5004 }
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07005005 final IAudioService service = getService();
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005006 try {
Kevin Rocard2fe8b8b2019-02-20 11:25:23 -08005007 MediaProjection projection = policy.getMediaProjection();
Jean-Michel Trivi0212be52014-11-24 14:43:10 -08005008 String regId = service.registerAudioPolicy(policy.getConfig(), policy.cb(),
Jean-Michel Triviee7d2452019-03-19 12:29:27 -07005009 policy.hasFocusListener(), policy.isFocusPolicy(), policy.isTestFocusPolicy(),
5010 policy.isVolumeController(),
Kevin Rocard2fe8b8b2019-02-20 11:25:23 -08005011 projection == null ? null : projection.getProjection());
Jean-Michel Trivi8fdb0d42014-07-16 19:08:37 -07005012 if (regId == null) {
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005013 return ERROR;
Jean-Michel Trivi8fdb0d42014-07-16 19:08:37 -07005014 } else {
5015 policy.setRegistration(regId);
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005016 }
Jean-Michel Trivi8fdb0d42014-07-16 19:08:37 -07005017 // successful registration
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005018 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07005019 throw e.rethrowFromSystemServer();
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005020 }
5021 return SUCCESS;
5022 }
5023
5024 /**
5025 * @hide
Jean-Michel Trivicada3722019-03-02 16:14:12 -08005026 * Unregisters an {@link AudioPolicy} asynchronously.
Jean-Michel Trivi1b3541d2014-11-25 12:53:41 -08005027 * @param policy the non-null {@link AudioPolicy} to unregister.
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005028 */
Jean-Michel Trivi1b3541d2014-11-25 12:53:41 -08005029 @SystemApi
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -06005030 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
Jean-Michel Trivi1b3541d2014-11-25 12:53:41 -08005031 public void unregisterAudioPolicyAsync(@NonNull AudioPolicy policy) {
Robert Berry669be042019-01-22 18:48:35 -05005032 unregisterAudioPolicyAsyncStatic(policy);
5033 }
5034
5035 static void unregisterAudioPolicyAsyncStatic(@NonNull AudioPolicy policy) {
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005036 if (policy == null) {
5037 throw new IllegalArgumentException("Illegal null AudioPolicy argument");
5038 }
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07005039 final IAudioService service = getService();
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005040 try {
Jean-Michel Trivi0212be52014-11-24 14:43:10 -08005041 service.unregisterAudioPolicyAsync(policy.cb());
Jean-Michel Trivif5f3e682020-07-29 15:45:48 -07005042 policy.reset();
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005043 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07005044 throw e.rethrowFromSystemServer();
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005045 }
5046 }
5047
Jean-Michel Trivicada3722019-03-02 16:14:12 -08005048 /**
5049 * @hide
5050 * Unregisters an {@link AudioPolicy} synchronously.
5051 * This method also invalidates all {@link AudioRecord} and {@link AudioTrack} objects
5052 * associated with mixes of this policy.
5053 * @param policy the non-null {@link AudioPolicy} to unregister.
5054 */
5055 @SystemApi
5056 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
5057 public void unregisterAudioPolicy(@NonNull AudioPolicy policy) {
5058 Preconditions.checkNotNull(policy, "Illegal null AudioPolicy argument");
5059 final IAudioService service = getService();
5060 try {
5061 policy.invalidateCaptorsAndInjectors();
5062 service.unregisterAudioPolicy(policy.cb());
Jean-Michel Trivif5f3e682020-07-29 15:45:48 -07005063 policy.reset();
Jean-Michel Trivicada3722019-03-02 16:14:12 -08005064 } catch (RemoteException e) {
5065 throw e.rethrowFromSystemServer();
5066 }
5067 }
5068
Jean-Michel Trivi0c88f492019-04-12 15:43:56 -07005069 /**
5070 * @hide
5071 * @return true if an AudioPolicy was previously registered
5072 */
5073 @TestApi
5074 public boolean hasRegisteredDynamicPolicy() {
5075 final IAudioService service = getService();
5076 try {
5077 return service.hasRegisteredDynamicPolicy();
5078 } catch (RemoteException e) {
5079 throw e.rethrowFromSystemServer();
5080 }
5081 }
5082
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -08005083 //====================================================================
5084 // Notification of playback activity & playback configuration
5085 /**
5086 * Interface for receiving update notifications about the playback activity on the system.
5087 * Extend this abstract class and register it with
5088 * {@link AudioManager#registerAudioPlaybackCallback(AudioPlaybackCallback, Handler)}
5089 * to be notified.
5090 * Use {@link AudioManager#getActivePlaybackConfigurations()} to query the current
5091 * configuration.
5092 * @see AudioPlaybackConfiguration
5093 */
5094 public static abstract class AudioPlaybackCallback {
5095 /**
5096 * Called whenever the playback activity and configuration has changed.
5097 * @param configs list containing the results of
5098 * {@link AudioManager#getActivePlaybackConfigurations()}.
5099 */
5100 public void onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configs) {}
5101 }
5102
5103 private static class AudioPlaybackCallbackInfo {
5104 final AudioPlaybackCallback mCb;
5105 final Handler mHandler;
5106 AudioPlaybackCallbackInfo(AudioPlaybackCallback cb, Handler handler) {
5107 mCb = cb;
5108 mHandler = handler;
5109 }
5110 }
5111
5112 private final static class PlaybackConfigChangeCallbackData {
5113 final AudioPlaybackCallback mCb;
5114 final List<AudioPlaybackConfiguration> mConfigs;
5115
5116 PlaybackConfigChangeCallbackData(AudioPlaybackCallback cb,
5117 List<AudioPlaybackConfiguration> configs) {
5118 mCb = cb;
5119 mConfigs = configs;
5120 }
5121 }
5122
5123 /**
5124 * Register a callback to be notified of audio playback changes through
5125 * {@link AudioPlaybackCallback}
5126 * @param cb non-null callback to register
5127 * @param handler the {@link Handler} object for the thread on which to execute
5128 * the callback. If <code>null</code>, the {@link Handler} associated with the main
5129 * {@link Looper} will be used.
5130 */
Jean-Michel Trivi1ec66182019-12-30 09:53:04 -08005131 public void registerAudioPlaybackCallback(@NonNull AudioPlaybackCallback cb,
5132 @Nullable Handler handler)
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -08005133 {
5134 if (cb == null) {
5135 throw new IllegalArgumentException("Illegal null AudioPlaybackCallback argument");
5136 }
5137
5138 synchronized(mPlaybackCallbackLock) {
5139 // lazy initialization of the list of playback callbacks
5140 if (mPlaybackCallbackList == null) {
5141 mPlaybackCallbackList = new ArrayList<AudioPlaybackCallbackInfo>();
5142 }
5143 final int oldCbCount = mPlaybackCallbackList.size();
5144 if (!hasPlaybackCallback_sync(cb)) {
5145 mPlaybackCallbackList.add(new AudioPlaybackCallbackInfo(cb,
5146 new ServiceEventHandlerDelegate(handler).getHandler()));
5147 final int newCbCount = mPlaybackCallbackList.size();
5148 if ((oldCbCount == 0) && (newCbCount > 0)) {
5149 // register binder for callbacks
5150 try {
5151 getService().registerPlaybackCallback(mPlayCb);
5152 } catch (RemoteException e) {
5153 throw e.rethrowFromSystemServer();
5154 }
5155 }
5156 } else {
5157 Log.w(TAG, "attempt to call registerAudioPlaybackCallback() on a previously"
5158 + "registered callback");
5159 }
5160 }
5161 }
5162
5163 /**
5164 * Unregister an audio playback callback previously registered with
5165 * {@link #registerAudioPlaybackCallback(AudioPlaybackCallback, Handler)}.
5166 * @param cb non-null callback to unregister
5167 */
5168 public void unregisterAudioPlaybackCallback(@NonNull AudioPlaybackCallback cb) {
5169 if (cb == null) {
5170 throw new IllegalArgumentException("Illegal null AudioPlaybackCallback argument");
5171 }
5172 synchronized(mPlaybackCallbackLock) {
5173 if (mPlaybackCallbackList == null) {
5174 Log.w(TAG, "attempt to call unregisterAudioPlaybackCallback() on a callback"
5175 + " that was never registered");
5176 return;
5177 }
5178 final int oldCbCount = mPlaybackCallbackList.size();
5179 if (removePlaybackCallback_sync(cb)) {
5180 final int newCbCount = mPlaybackCallbackList.size();
5181 if ((oldCbCount > 0) && (newCbCount == 0)) {
5182 // unregister binder for callbacks
5183 try {
5184 getService().unregisterPlaybackCallback(mPlayCb);
5185 } catch (RemoteException e) {
5186 throw e.rethrowFromSystemServer();
5187 }
5188 }
5189 } else {
5190 Log.w(TAG, "attempt to call unregisterAudioPlaybackCallback() on a callback"
5191 + " already unregistered or never registered");
5192 }
5193 }
5194 }
5195
5196 /**
5197 * Returns the current active audio playback configurations of the device
5198 * @return a non-null list of playback configurations. An empty list indicates there is no
5199 * playback active when queried.
5200 * @see AudioPlaybackConfiguration
5201 */
5202 public @NonNull List<AudioPlaybackConfiguration> getActivePlaybackConfigurations() {
5203 final IAudioService service = getService();
5204 try {
5205 return service.getActivePlaybackConfigurations();
5206 } catch (RemoteException e) {
5207 throw e.rethrowFromSystemServer();
5208 }
5209 }
5210
5211 /**
5212 * All operations on this list are sync'd on mPlaybackCallbackLock.
5213 * List is lazy-initialized in
5214 * {@link #registerAudioPlaybackCallback(AudioPlaybackCallback, Handler)}.
5215 * List can be null.
5216 */
5217 private List<AudioPlaybackCallbackInfo> mPlaybackCallbackList;
5218 private final Object mPlaybackCallbackLock = new Object();
5219
5220 /**
5221 * Must be called synchronized on mPlaybackCallbackLock
5222 */
5223 private boolean hasPlaybackCallback_sync(@NonNull AudioPlaybackCallback cb) {
5224 if (mPlaybackCallbackList != null) {
5225 for (int i=0 ; i < mPlaybackCallbackList.size() ; i++) {
5226 if (cb.equals(mPlaybackCallbackList.get(i).mCb)) {
5227 return true;
5228 }
5229 }
5230 }
5231 return false;
5232 }
5233
5234 /**
5235 * Must be called synchronized on mPlaybackCallbackLock
5236 */
5237 private boolean removePlaybackCallback_sync(@NonNull AudioPlaybackCallback cb) {
5238 if (mPlaybackCallbackList != null) {
5239 for (int i=0 ; i < mPlaybackCallbackList.size() ; i++) {
5240 if (cb.equals(mPlaybackCallbackList.get(i).mCb)) {
5241 mPlaybackCallbackList.remove(i);
5242 return true;
5243 }
5244 }
5245 }
5246 return false;
5247 }
5248
5249 private final IPlaybackConfigDispatcher mPlayCb = new IPlaybackConfigDispatcher.Stub() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07005250 @Override
Jean-Michel Trivi776a3992017-09-12 16:45:34 -07005251 public void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs,
5252 boolean flush) {
5253 if (flush) {
5254 Binder.flushPendingCommands();
5255 }
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -08005256 synchronized(mPlaybackCallbackLock) {
5257 if (mPlaybackCallbackList != null) {
5258 for (int i=0 ; i < mPlaybackCallbackList.size() ; i++) {
5259 final AudioPlaybackCallbackInfo arci = mPlaybackCallbackList.get(i);
5260 if (arci.mHandler != null) {
5261 final Message m = arci.mHandler.obtainMessage(
5262 MSSG_PLAYBACK_CONFIG_CHANGE/*what*/,
5263 new PlaybackConfigChangeCallbackData(arci.mCb, configs)/*obj*/);
5264 arci.mHandler.sendMessage(m);
5265 }
5266 }
5267 }
5268 }
5269 }
5270
5271 };
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005272
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005273 //====================================================================
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -08005274 // Notification of recording activity & recording configuration
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005275 /**
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005276 * Interface for receiving update notifications about the recording configuration. Extend
5277 * this abstract class and register it with
5278 * {@link AudioManager#registerAudioRecordingCallback(AudioRecordingCallback, Handler)}
5279 * to be notified.
Jean-Michel Trivi598c0c92016-03-07 14:13:03 -08005280 * Use {@link AudioManager#getActiveRecordingConfigurations()} to query the current
5281 * configuration.
Jean-Michel Trivi7872aff2016-07-06 14:34:52 -07005282 * @see AudioRecordingConfiguration
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005283 */
5284 public static abstract class AudioRecordingCallback {
5285 /**
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005286 * Called whenever the device recording configuration has changed.
Jean-Michel Trivif04fab12016-05-19 10:42:35 -07005287 * @param configs list containing the results of
Jean-Michel Trivi598c0c92016-03-07 14:13:03 -08005288 * {@link AudioManager#getActiveRecordingConfigurations()}.
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005289 */
Jean-Michel Trivif04fab12016-05-19 10:42:35 -07005290 public void onRecordingConfigChanged(List<AudioRecordingConfiguration> configs) {}
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005291 }
5292
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005293 private static class AudioRecordingCallbackInfo {
5294 final AudioRecordingCallback mCb;
5295 final Handler mHandler;
5296 AudioRecordingCallbackInfo(AudioRecordingCallback cb, Handler handler) {
5297 mCb = cb;
5298 mHandler = handler;
5299 }
5300 }
5301
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -08005302 private final static class RecordConfigChangeCallbackData {
5303 final AudioRecordingCallback mCb;
Jean-Michel Trivif04fab12016-05-19 10:42:35 -07005304 final List<AudioRecordingConfiguration> mConfigs;
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -08005305
5306 RecordConfigChangeCallbackData(AudioRecordingCallback cb,
Jean-Michel Trivif04fab12016-05-19 10:42:35 -07005307 List<AudioRecordingConfiguration> configs) {
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -08005308 mCb = cb;
5309 mConfigs = configs;
5310 }
5311 }
5312
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005313 /**
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005314 * Register a callback to be notified of audio recording changes through
5315 * {@link AudioRecordingCallback}
5316 * @param cb non-null callback to register
5317 * @param handler the {@link Handler} object for the thread on which to execute
5318 * the callback. If <code>null</code>, the {@link Handler} associated with the main
5319 * {@link Looper} will be used.
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005320 */
Jean-Michel Trivi1ec66182019-12-30 09:53:04 -08005321 public void registerAudioRecordingCallback(@NonNull AudioRecordingCallback cb,
5322 @Nullable Handler handler)
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005323 {
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005324 if (cb == null) {
5325 throw new IllegalArgumentException("Illegal null AudioRecordingCallback argument");
5326 }
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005327
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005328 synchronized(mRecordCallbackLock) {
5329 // lazy initialization of the list of recording callbacks
5330 if (mRecordCallbackList == null) {
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005331 mRecordCallbackList = new ArrayList<AudioRecordingCallbackInfo>();
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005332 }
5333 final int oldCbCount = mRecordCallbackList.size();
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005334 if (!hasRecordCallback_sync(cb)) {
5335 mRecordCallbackList.add(new AudioRecordingCallbackInfo(cb,
5336 new ServiceEventHandlerDelegate(handler).getHandler()));
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005337 final int newCbCount = mRecordCallbackList.size();
5338 if ((oldCbCount == 0) && (newCbCount > 0)) {
5339 // register binder for callbacks
5340 final IAudioService service = getService();
5341 try {
5342 service.registerRecordingCallback(mRecCb);
5343 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07005344 throw e.rethrowFromSystemServer();
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005345 }
5346 }
5347 } else {
5348 Log.w(TAG, "attempt to call registerAudioRecordingCallback() on a previously"
5349 + "registered callback");
5350 }
5351 }
5352 }
5353
5354 /**
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005355 * Unregister an audio recording callback previously registered with
5356 * {@link #registerAudioRecordingCallback(AudioRecordingCallback, Handler)}.
5357 * @param cb non-null callback to unregister
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005358 */
5359 public void unregisterAudioRecordingCallback(@NonNull AudioRecordingCallback cb) {
5360 if (cb == null) {
5361 throw new IllegalArgumentException("Illegal null AudioRecordingCallback argument");
5362 }
5363 synchronized(mRecordCallbackLock) {
5364 if (mRecordCallbackList == null) {
5365 return;
5366 }
5367 final int oldCbCount = mRecordCallbackList.size();
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005368 if (removeRecordCallback_sync(cb)) {
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005369 final int newCbCount = mRecordCallbackList.size();
5370 if ((oldCbCount > 0) && (newCbCount == 0)) {
5371 // unregister binder for callbacks
5372 final IAudioService service = getService();
5373 try {
5374 service.unregisterRecordingCallback(mRecCb);
5375 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07005376 throw e.rethrowFromSystemServer();
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005377 }
5378 }
5379 } else {
5380 Log.w(TAG, "attempt to call unregisterAudioRecordingCallback() on a callback"
5381 + " already unregistered or never registered");
5382 }
5383 }
5384 }
5385
5386 /**
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005387 * Returns the current active audio recording configurations of the device.
Jean-Michel Trivif04fab12016-05-19 10:42:35 -07005388 * @return a non-null list of recording configurations. An empty list indicates there is
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005389 * no recording active when queried.
Jean-Michel Trivi7872aff2016-07-06 14:34:52 -07005390 * @see AudioRecordingConfiguration
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005391 */
Jean-Michel Trivif04fab12016-05-19 10:42:35 -07005392 public @NonNull List<AudioRecordingConfiguration> getActiveRecordingConfigurations() {
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005393 final IAudioService service = getService();
5394 try {
Jean-Michel Trivi598c0c92016-03-07 14:13:03 -08005395 return service.getActiveRecordingConfigurations();
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005396 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07005397 throw e.rethrowFromSystemServer();
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005398 }
5399 }
5400
5401 /**
5402 * constants for the recording events, to keep in sync
5403 * with frameworks/av/include/media/AudioPolicy.h
5404 */
5405 /** @hide */
Mikhail Naganova00883d2019-04-18 12:36:27 -07005406 public static final int RECORD_CONFIG_EVENT_NONE = -1;
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005407 /** @hide */
Mikhail Naganova00883d2019-04-18 12:36:27 -07005408 public static final int RECORD_CONFIG_EVENT_START = 0;
5409 /** @hide */
5410 public static final int RECORD_CONFIG_EVENT_STOP = 1;
5411 /** @hide */
5412 public static final int RECORD_CONFIG_EVENT_UPDATE = 2;
5413 /** @hide */
Mikhail Naganovcfe4c262019-05-09 09:02:47 -07005414 public static final int RECORD_CONFIG_EVENT_RELEASE = 3;
Mikhail Naganova00883d2019-04-18 12:36:27 -07005415 /**
5416 * keep in sync with frameworks/native/include/audiomanager/AudioManager.h
5417 */
5418 /** @hide */
5419 public static final int RECORD_RIID_INVALID = -1;
5420 /** @hide */
5421 public static final int RECORDER_STATE_STARTED = 0;
5422 /** @hide */
5423 public static final int RECORDER_STATE_STOPPED = 1;
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005424
5425 /**
5426 * All operations on this list are sync'd on mRecordCallbackLock.
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005427 * List is lazy-initialized in
5428 * {@link #registerAudioRecordingCallback(AudioRecordingCallback, Handler)}.
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005429 * List can be null.
5430 */
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005431 private List<AudioRecordingCallbackInfo> mRecordCallbackList;
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005432 private final Object mRecordCallbackLock = new Object();
5433
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005434 /**
5435 * Must be called synchronized on mRecordCallbackLock
5436 */
5437 private boolean hasRecordCallback_sync(@NonNull AudioRecordingCallback cb) {
5438 if (mRecordCallbackList != null) {
5439 for (int i=0 ; i < mRecordCallbackList.size() ; i++) {
5440 if (cb.equals(mRecordCallbackList.get(i).mCb)) {
5441 return true;
5442 }
5443 }
5444 }
5445 return false;
5446 }
5447
5448 /**
5449 * Must be called synchronized on mRecordCallbackLock
5450 */
5451 private boolean removeRecordCallback_sync(@NonNull AudioRecordingCallback cb) {
5452 if (mRecordCallbackList != null) {
5453 for (int i=0 ; i < mRecordCallbackList.size() ; i++) {
5454 if (cb.equals(mRecordCallbackList.get(i).mCb)) {
5455 mRecordCallbackList.remove(i);
5456 return true;
5457 }
5458 }
5459 }
5460 return false;
5461 }
5462
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005463 private final IRecordingConfigDispatcher mRecCb = new IRecordingConfigDispatcher.Stub() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07005464 @Override
Jean-Michel Trivif04fab12016-05-19 10:42:35 -07005465 public void dispatchRecordingConfigChange(List<AudioRecordingConfiguration> configs) {
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005466 synchronized(mRecordCallbackLock) {
5467 if (mRecordCallbackList != null) {
5468 for (int i=0 ; i < mRecordCallbackList.size() ; i++) {
5469 final AudioRecordingCallbackInfo arci = mRecordCallbackList.get(i);
5470 if (arci.mHandler != null) {
5471 final Message m = arci.mHandler.obtainMessage(
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -08005472 MSSG_RECORDING_CONFIG_CHANGE/*what*/,
5473 new RecordConfigChangeCallbackData(arci.mCb, configs)/*obj*/);
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005474 arci.mHandler.sendMessage(m);
5475 }
5476 }
5477 }
5478 }
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005479 }
5480
5481 };
5482
5483 //=====================================================================
5484
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005485 /**
Eric Laurent4050c932009-07-08 02:52:14 -07005486 * @hide
5487 * Reload audio settings. This method is called by Settings backup
5488 * agent when audio settings are restored and causes the AudioService
5489 * to read and apply restored settings.
5490 */
Mathew Inwood8e742f92020-10-27 11:47:29 +00005491 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Eric Laurent4050c932009-07-08 02:52:14 -07005492 public void reloadAudioSettings() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07005493 final IAudioService service = getService();
Eric Laurent4050c932009-07-08 02:52:14 -07005494 try {
5495 service.reloadAudioSettings();
5496 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07005497 throw e.rethrowFromSystemServer();
Eric Laurent4050c932009-07-08 02:52:14 -07005498 }
5499 }
5500
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005501 /**
5502 * {@hide}
5503 */
Glenn Kasten30c918c2011-11-10 17:56:41 -08005504 private final IBinder mICallBack = new Binder();
Glenn Kasten7f9b1fa2010-03-31 11:11:36 -07005505
5506 /**
5507 * Checks whether the phone is in silent mode, with or without vibrate.
5508 *
5509 * @return true if phone is in silent mode, with or without vibrate.
5510 *
5511 * @see #getRingerMode()
5512 *
5513 * @hide pending API Council approval
5514 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005515 @UnsupportedAppUsage
Glenn Kasten7f9b1fa2010-03-31 11:11:36 -07005516 public boolean isSilentMode() {
5517 int ringerMode = getRingerMode();
5518 boolean silentMode =
5519 (ringerMode == RINGER_MODE_SILENT) ||
5520 (ringerMode == RINGER_MODE_VIBRATE);
5521 return silentMode;
5522 }
5523
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005524 // This section re-defines new output device constants from AudioSystem, because the AudioSystem
5525 // class is not used by other parts of the framework, which instead use definitions and methods
5526 // from AudioManager. AudioSystem is an internal class used by AudioManager and AudioService.
5527
Eric Laurent948d3272014-05-16 15:18:45 -07005528 /** @hide
Wonsik Kimd7c29182014-05-27 10:38:21 +09005529 * The audio device code for representing "no device." */
5530 public static final int DEVICE_NONE = AudioSystem.DEVICE_NONE;
5531 /** @hide
Eric Laurent948d3272014-05-16 15:18:45 -07005532 * The audio output device code for the small speaker at the front of the device used
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005533 * when placing calls. Does not refer to an in-ear headphone without attached microphone,
5534 * such as earbuds, earphones, or in-ear monitors (IEM). Those would be handled as a
5535 * {@link #DEVICE_OUT_WIRED_HEADPHONE}.
5536 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005537 @UnsupportedAppUsage
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005538 public static final int DEVICE_OUT_EARPIECE = AudioSystem.DEVICE_OUT_EARPIECE;
Eric Laurent948d3272014-05-16 15:18:45 -07005539 /** @hide
5540 * The audio output device code for the built-in speaker */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005541 @UnsupportedAppUsage
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005542 public static final int DEVICE_OUT_SPEAKER = AudioSystem.DEVICE_OUT_SPEAKER;
Eric Laurent948d3272014-05-16 15:18:45 -07005543 /** @hide
5544 * The audio output device code for a wired headset with attached microphone */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005545 @UnsupportedAppUsage
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005546 public static final int DEVICE_OUT_WIRED_HEADSET = AudioSystem.DEVICE_OUT_WIRED_HEADSET;
Eric Laurent948d3272014-05-16 15:18:45 -07005547 /** @hide
5548 * The audio output device code for a wired headphone without attached microphone */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005549 @UnsupportedAppUsage
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005550 public static final int DEVICE_OUT_WIRED_HEADPHONE = AudioSystem.DEVICE_OUT_WIRED_HEADPHONE;
Eric Laurent948d3272014-05-16 15:18:45 -07005551 /** @hide
Paul McLean145c9532017-08-04 11:12:19 -06005552 * The audio output device code for a USB headphone with attached microphone */
5553 public static final int DEVICE_OUT_USB_HEADSET = AudioSystem.DEVICE_OUT_USB_HEADSET;
5554 /** @hide
Eric Laurent948d3272014-05-16 15:18:45 -07005555 * The audio output device code for generic Bluetooth SCO, for voice */
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005556 public static final int DEVICE_OUT_BLUETOOTH_SCO = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
Eric Laurent948d3272014-05-16 15:18:45 -07005557 /** @hide
5558 * The audio output device code for Bluetooth SCO Headset Profile (HSP) and
5559 * Hands-Free Profile (HFP), for voice
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005560 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005561 @UnsupportedAppUsage
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005562 public static final int DEVICE_OUT_BLUETOOTH_SCO_HEADSET =
5563 AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
Eric Laurent948d3272014-05-16 15:18:45 -07005564 /** @hide
5565 * The audio output device code for Bluetooth SCO car audio, for voice */
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005566 public static final int DEVICE_OUT_BLUETOOTH_SCO_CARKIT =
5567 AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
Eric Laurent948d3272014-05-16 15:18:45 -07005568 /** @hide
5569 * The audio output device code for generic Bluetooth A2DP, for music */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005570 @UnsupportedAppUsage
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005571 public static final int DEVICE_OUT_BLUETOOTH_A2DP = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
Eric Laurent948d3272014-05-16 15:18:45 -07005572 /** @hide
5573 * The audio output device code for Bluetooth A2DP headphones, for music */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005574 @UnsupportedAppUsage
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005575 public static final int DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES =
5576 AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
Eric Laurent948d3272014-05-16 15:18:45 -07005577 /** @hide
5578 * The audio output device code for Bluetooth A2DP external speaker, for music */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005579 @UnsupportedAppUsage
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005580 public static final int DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER =
5581 AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
Eric Laurent948d3272014-05-16 15:18:45 -07005582 /** @hide
5583 * The audio output device code for S/PDIF (legacy) or HDMI
5584 * Deprecated: replaced by {@link #DEVICE_OUT_HDMI} */
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005585 public static final int DEVICE_OUT_AUX_DIGITAL = AudioSystem.DEVICE_OUT_AUX_DIGITAL;
Eric Laurent948d3272014-05-16 15:18:45 -07005586 /** @hide
5587 * The audio output device code for HDMI */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005588 @UnsupportedAppUsage
Eric Laurent948d3272014-05-16 15:18:45 -07005589 public static final int DEVICE_OUT_HDMI = AudioSystem.DEVICE_OUT_HDMI;
5590 /** @hide
5591 * The audio output device code for an analog wired headset attached via a
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005592 * docking station
5593 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005594 @UnsupportedAppUsage
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005595 public static final int DEVICE_OUT_ANLG_DOCK_HEADSET = AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET;
Eric Laurent948d3272014-05-16 15:18:45 -07005596 /** @hide
5597 * The audio output device code for a digital wired headset attached via a
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005598 * docking station
5599 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005600 @UnsupportedAppUsage
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005601 public static final int DEVICE_OUT_DGTL_DOCK_HEADSET = AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET;
Eric Laurent948d3272014-05-16 15:18:45 -07005602 /** @hide
5603 * The audio output device code for a USB audio accessory. The accessory is in USB host
Eric Laurent59f48272012-04-05 19:42:21 -07005604 * mode and the Android device in USB device mode
5605 */
5606 public static final int DEVICE_OUT_USB_ACCESSORY = AudioSystem.DEVICE_OUT_USB_ACCESSORY;
Eric Laurent948d3272014-05-16 15:18:45 -07005607 /** @hide
5608 * The audio output device code for a USB audio device. The device is in USB device
Eric Laurent59f48272012-04-05 19:42:21 -07005609 * mode and the Android device in USB host mode
5610 */
5611 public static final int DEVICE_OUT_USB_DEVICE = AudioSystem.DEVICE_OUT_USB_DEVICE;
Eric Laurent948d3272014-05-16 15:18:45 -07005612 /** @hide
5613 * The audio output device code for projection output.
5614 */
5615 public static final int DEVICE_OUT_REMOTE_SUBMIX = AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
5616 /** @hide
5617 * The audio output device code the telephony voice TX path.
5618 */
5619 public static final int DEVICE_OUT_TELEPHONY_TX = AudioSystem.DEVICE_OUT_TELEPHONY_TX;
5620 /** @hide
5621 * The audio output device code for an analog jack with line impedance detected.
5622 */
5623 public static final int DEVICE_OUT_LINE = AudioSystem.DEVICE_OUT_LINE;
5624 /** @hide
5625 * The audio output device code for HDMI Audio Return Channel.
5626 */
5627 public static final int DEVICE_OUT_HDMI_ARC = AudioSystem.DEVICE_OUT_HDMI_ARC;
5628 /** @hide
Kuowei Lif898eae2020-10-27 16:41:18 +08005629 * The audio output device code for HDMI enhanced Audio Return Channel.
5630 */
5631 public static final int DEVICE_OUT_HDMI_EARC = AudioSystem.DEVICE_OUT_HDMI_EARC;
5632 /** @hide
Eric Laurent948d3272014-05-16 15:18:45 -07005633 * The audio output device code for S/PDIF digital connection.
5634 */
5635 public static final int DEVICE_OUT_SPDIF = AudioSystem.DEVICE_OUT_SPDIF;
5636 /** @hide
5637 * The audio output device code for built-in FM transmitter.
5638 */
5639 public static final int DEVICE_OUT_FM = AudioSystem.DEVICE_OUT_FM;
5640 /** @hide
Eric Laurentca94af02020-08-07 12:30:44 -07005641 * The audio output device code for echo reference injection point.
5642 */
5643 public static final int DEVICE_OUT_ECHO_CANCELLER = AudioSystem.DEVICE_OUT_ECHO_CANCELLER;
5644 /** @hide
Eric Laurent6239d7e2020-08-07 10:58:14 -07005645 * The audio output device code for a BLE audio headset.
5646 */
5647 public static final int DEVICE_OUT_BLE_HEADSET = AudioSystem.DEVICE_OUT_BLE_HEADSET;
5648 /** @hide
5649 * The audio output device code for a BLE audio speaker.
5650 */
5651 public static final int DEVICE_OUT_BLE_SPEAKER = AudioSystem.DEVICE_OUT_BLE_SPEAKER;
5652 /** @hide
Eric Laurent277373e2022-01-20 14:42:22 +01005653 * The audio output device code for a BLE audio brodcast group.
5654 */
5655 public static final int DEVICE_OUT_BLE_BROADCAST = AudioSystem.DEVICE_OUT_BLE_BROADCAST;
5656 /** @hide
Eric Laurent948d3272014-05-16 15:18:45 -07005657 * This is not used as a returned value from {@link #getDevicesForStream}, but could be
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005658 * used in the future in a set method to select whatever default device is chosen by the
5659 * platform-specific implementation.
5660 */
5661 public static final int DEVICE_OUT_DEFAULT = AudioSystem.DEVICE_OUT_DEFAULT;
5662
Eric Laurent948d3272014-05-16 15:18:45 -07005663 /** @hide
5664 * The audio input device code for default built-in microphone
5665 */
5666 public static final int DEVICE_IN_BUILTIN_MIC = AudioSystem.DEVICE_IN_BUILTIN_MIC;
5667 /** @hide
5668 * The audio input device code for a Bluetooth SCO headset
5669 */
5670 public static final int DEVICE_IN_BLUETOOTH_SCO_HEADSET =
5671 AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
5672 /** @hide
5673 * The audio input device code for wired headset microphone
5674 */
5675 public static final int DEVICE_IN_WIRED_HEADSET =
5676 AudioSystem.DEVICE_IN_WIRED_HEADSET;
5677 /** @hide
5678 * The audio input device code for HDMI
5679 */
5680 public static final int DEVICE_IN_HDMI =
5681 AudioSystem.DEVICE_IN_HDMI;
5682 /** @hide
Nick Chalko2e1f76a2018-10-25 10:19:10 -07005683 * The audio input device code for HDMI ARC
5684 */
5685 public static final int DEVICE_IN_HDMI_ARC =
5686 AudioSystem.DEVICE_IN_HDMI_ARC;
5687
5688 /** @hide
Kuowei Lif898eae2020-10-27 16:41:18 +08005689 * The audio input device code for HDMI EARC
5690 */
5691 public static final int DEVICE_IN_HDMI_EARC =
5692 AudioSystem.DEVICE_IN_HDMI_EARC;
5693
5694 /** @hide
Eric Laurent948d3272014-05-16 15:18:45 -07005695 * The audio input device code for telephony voice RX path
5696 */
5697 public static final int DEVICE_IN_TELEPHONY_RX =
5698 AudioSystem.DEVICE_IN_TELEPHONY_RX;
5699 /** @hide
5700 * The audio input device code for built-in microphone pointing to the back
5701 */
5702 public static final int DEVICE_IN_BACK_MIC =
5703 AudioSystem.DEVICE_IN_BACK_MIC;
5704 /** @hide
5705 * The audio input device code for analog from a docking station
5706 */
5707 public static final int DEVICE_IN_ANLG_DOCK_HEADSET =
5708 AudioSystem.DEVICE_IN_ANLG_DOCK_HEADSET;
5709 /** @hide
5710 * The audio input device code for digital from a docking station
5711 */
5712 public static final int DEVICE_IN_DGTL_DOCK_HEADSET =
5713 AudioSystem.DEVICE_IN_DGTL_DOCK_HEADSET;
5714 /** @hide
5715 * The audio input device code for a USB audio accessory. The accessory is in USB host
5716 * mode and the Android device in USB device mode
5717 */
5718 public static final int DEVICE_IN_USB_ACCESSORY =
5719 AudioSystem.DEVICE_IN_USB_ACCESSORY;
5720 /** @hide
5721 * The audio input device code for a USB audio device. The device is in USB device
5722 * mode and the Android device in USB host mode
5723 */
5724 public static final int DEVICE_IN_USB_DEVICE =
5725 AudioSystem.DEVICE_IN_USB_DEVICE;
5726 /** @hide
5727 * The audio input device code for a FM radio tuner
5728 */
5729 public static final int DEVICE_IN_FM_TUNER = AudioSystem.DEVICE_IN_FM_TUNER;
5730 /** @hide
5731 * The audio input device code for a TV tuner
5732 */
5733 public static final int DEVICE_IN_TV_TUNER = AudioSystem.DEVICE_IN_TV_TUNER;
5734 /** @hide
5735 * The audio input device code for an analog jack with line impedance detected
5736 */
5737 public static final int DEVICE_IN_LINE = AudioSystem.DEVICE_IN_LINE;
5738 /** @hide
5739 * The audio input device code for a S/PDIF digital connection
5740 */
5741 public static final int DEVICE_IN_SPDIF = AudioSystem.DEVICE_IN_SPDIF;
Terry Heo112c19e2014-07-07 10:25:38 +09005742 /** @hide
5743 * The audio input device code for audio loopback
5744 */
5745 public static final int DEVICE_IN_LOOPBACK = AudioSystem.DEVICE_IN_LOOPBACK;
Eric Laurent6239d7e2020-08-07 10:58:14 -07005746 /** @hide
Eric Laurentca94af02020-08-07 12:30:44 -07005747 * The audio input device code for an echo reference capture point.
5748 */
5749 public static final int DEVICE_IN_ECHO_REFERENCE = AudioSystem.DEVICE_IN_ECHO_REFERENCE;
5750 /** @hide
Eric Laurent6239d7e2020-08-07 10:58:14 -07005751 * The audio input device code for a BLE audio headset.
5752 */
5753 public static final int DEVICE_IN_BLE_HEADSET = AudioSystem.DEVICE_IN_BLE_HEADSET;
Eric Laurent948d3272014-05-16 15:18:45 -07005754
5755 /**
5756 * Return true if the device code corresponds to an output device.
5757 * @hide
5758 */
5759 public static boolean isOutputDevice(int device)
5760 {
5761 return (device & AudioSystem.DEVICE_BIT_IN) == 0;
5762 }
5763
5764 /**
5765 * Return true if the device code corresponds to an input device.
5766 * @hide
5767 */
5768 public static boolean isInputDevice(int device)
5769 {
5770 return (device & AudioSystem.DEVICE_BIT_IN) == AudioSystem.DEVICE_BIT_IN;
5771 }
5772
5773
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005774 /**
5775 * Return the enabled devices for the specified output stream type.
5776 *
5777 * @param streamType The stream type to query. One of
5778 * {@link #STREAM_VOICE_CALL},
5779 * {@link #STREAM_SYSTEM},
5780 * {@link #STREAM_RING},
5781 * {@link #STREAM_MUSIC},
5782 * {@link #STREAM_ALARM},
5783 * {@link #STREAM_NOTIFICATION},
Jean-Michel Trivi8e0bc782016-11-25 10:22:23 -08005784 * {@link #STREAM_DTMF},
5785 * {@link #STREAM_ACCESSIBILITY}.
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005786 *
5787 * @return The bit-mask "or" of audio output device codes for all enabled devices on this
5788 * stream. Zero or more of
5789 * {@link #DEVICE_OUT_EARPIECE},
5790 * {@link #DEVICE_OUT_SPEAKER},
5791 * {@link #DEVICE_OUT_WIRED_HEADSET},
5792 * {@link #DEVICE_OUT_WIRED_HEADPHONE},
5793 * {@link #DEVICE_OUT_BLUETOOTH_SCO},
5794 * {@link #DEVICE_OUT_BLUETOOTH_SCO_HEADSET},
5795 * {@link #DEVICE_OUT_BLUETOOTH_SCO_CARKIT},
5796 * {@link #DEVICE_OUT_BLUETOOTH_A2DP},
5797 * {@link #DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES},
5798 * {@link #DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER},
Eric Laurent948d3272014-05-16 15:18:45 -07005799 * {@link #DEVICE_OUT_HDMI},
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005800 * {@link #DEVICE_OUT_ANLG_DOCK_HEADSET},
5801 * {@link #DEVICE_OUT_DGTL_DOCK_HEADSET}.
Eric Laurent948d3272014-05-16 15:18:45 -07005802 * {@link #DEVICE_OUT_USB_ACCESSORY}.
5803 * {@link #DEVICE_OUT_USB_DEVICE}.
5804 * {@link #DEVICE_OUT_REMOTE_SUBMIX}.
5805 * {@link #DEVICE_OUT_TELEPHONY_TX}.
5806 * {@link #DEVICE_OUT_LINE}.
5807 * {@link #DEVICE_OUT_HDMI_ARC}.
Kuowei Lif898eae2020-10-27 16:41:18 +08005808 * {@link #DEVICE_OUT_HDMI_EARC}.
Eric Laurent948d3272014-05-16 15:18:45 -07005809 * {@link #DEVICE_OUT_SPDIF}.
5810 * {@link #DEVICE_OUT_FM}.
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005811 * {@link #DEVICE_OUT_DEFAULT} is not used here.
5812 *
5813 * The implementation may support additional device codes beyond those listed, so
5814 * the application should ignore any bits which it does not recognize.
5815 * Note that the information may be imprecise when the implementation
5816 * cannot distinguish whether a particular device is enabled.
5817 *
Andy Hungb11e4c72021-04-13 19:31:00 -07005818 * @deprecated on {@link android.os.Build.VERSION_CODES#T} as new devices
5819 * will have multi-bit device types.
5820 * Prefer to use {@link #getDevicesForAttributes()} instead,
5821 * noting that getDevicesForStream() has a few small discrepancies
5822 * for better volume handling.
5823 * @hide
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005824 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005825 @UnsupportedAppUsage
Andy Hungb11e4c72021-04-13 19:31:00 -07005826 @Deprecated
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005827 public int getDevicesForStream(int streamType) {
5828 switch (streamType) {
Andy Hungb11e4c72021-04-13 19:31:00 -07005829 case STREAM_VOICE_CALL:
5830 case STREAM_SYSTEM:
5831 case STREAM_RING:
5832 case STREAM_MUSIC:
5833 case STREAM_ALARM:
5834 case STREAM_NOTIFICATION:
5835 case STREAM_DTMF:
5836 case STREAM_ACCESSIBILITY:
5837 final IAudioService service = getService();
5838 try {
5839 return service.getDeviceMaskForStream(streamType);
5840 } catch (RemoteException e) {
5841 throw e.rethrowFromSystemServer();
5842 }
5843 default:
5844 return 0;
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005845 }
5846 }
5847
Jean-Michel Trivi56b97b72020-01-07 14:18:05 -08005848 /**
5849 * @hide
5850 * Get the audio devices that would be used for the routing of the given audio attributes.
5851 * @param attributes the {@link AudioAttributes} for which the routing is being queried
5852 * @return an empty list if there was an issue with the request, a list of audio devices
5853 * otherwise (typically one device, except for duplicated paths).
5854 */
5855 @SystemApi
kholoud mohamed37839212021-03-15 16:49:06 +00005856 @RequiresPermission(anyOf = {
5857 android.Manifest.permission.MODIFY_AUDIO_ROUTING,
5858 android.Manifest.permission.QUERY_AUDIO_STATE
5859 })
Jean-Michel Trivi0e87bca2020-02-18 14:30:29 -08005860 public @NonNull List<AudioDeviceAttributes> getDevicesForAttributes(
Jean-Michel Trivi56b97b72020-01-07 14:18:05 -08005861 @NonNull AudioAttributes attributes) {
5862 Objects.requireNonNull(attributes);
5863 final IAudioService service = getService();
5864 try {
5865 return service.getDevicesForAttributes(attributes);
5866 } catch (RemoteException e) {
5867 throw e.rethrowFromSystemServer();
5868 }
5869 }
5870
Paul Wangebadb692022-12-15 20:40:19 +00005871 // Each listener corresponds to a unique callback stub because each listener can subscribe to
5872 // different AudioAttributes.
5873 private final ConcurrentHashMap<OnDevicesForAttributesChangedListener,
5874 IDevicesForAttributesCallbackStub> mDevicesForAttributesListenerToStub =
5875 new ConcurrentHashMap<>();
5876
5877 private static final class IDevicesForAttributesCallbackStub
5878 extends IDevicesForAttributesCallback.Stub {
5879 ListenerInfo<OnDevicesForAttributesChangedListener> mInfo;
5880
5881 IDevicesForAttributesCallbackStub(@NonNull OnDevicesForAttributesChangedListener listener,
5882 @NonNull Executor executor) {
5883 mInfo = new ListenerInfo<>(listener, executor);
5884 }
5885
5886 public void register(boolean register, AudioAttributes attributes) {
5887 try {
5888 if (register) {
5889 getService().addOnDevicesForAttributesChangedListener(attributes, this);
5890 } else {
5891 getService().removeOnDevicesForAttributesChangedListener(this);
5892 }
5893 } catch (RemoteException e) {
5894 throw e.rethrowFromSystemServer();
5895 }
5896 }
5897
5898 @Override
5899 public void onDevicesForAttributesChanged(AudioAttributes attributes, boolean forVolume,
5900 List<AudioDeviceAttributes> devices) {
5901 // forVolume is ignored. The case where it is `true` is not handled.
5902 mInfo.mExecutor.execute(() ->
5903 mInfo.mListener.onDevicesForAttributesChanged(
5904 attributes, devices));
5905 }
5906 }
5907
5908 /**
5909 * @hide
5910 * Interface to be notified of when routing changes for the registered audio attributes.
5911 */
5912 @SystemApi
5913 public interface OnDevicesForAttributesChangedListener {
5914 /**
5915 * Called on the listener to indicate that the audio devices for the given audio
5916 * attributes have changed.
5917 * @param attributes the {@link AudioAttributes} whose routing changed
5918 * @param devices a list of newly routed audio devices
5919 */
5920 void onDevicesForAttributesChanged(@NonNull AudioAttributes attributes,
5921 @NonNull List<AudioDeviceAttributes> devices);
5922 }
5923
5924 /**
5925 * @hide
5926 * Adds a listener for being notified of routing changes for the given {@link AudioAttributes}.
5927 * @param attributes the {@link AudioAttributes} to listen for routing changes
5928 * @param executor
5929 * @param listener
5930 */
5931 @SystemApi
5932 @RequiresPermission(anyOf = {
5933 android.Manifest.permission.MODIFY_AUDIO_ROUTING,
5934 android.Manifest.permission.QUERY_AUDIO_STATE
5935 })
5936 public void addOnDevicesForAttributesChangedListener(@NonNull AudioAttributes attributes,
5937 @NonNull @CallbackExecutor Executor executor,
5938 @NonNull OnDevicesForAttributesChangedListener listener) {
5939 Objects.requireNonNull(attributes);
5940 Objects.requireNonNull(executor);
5941 Objects.requireNonNull(listener);
5942
5943 synchronized (mDevicesForAttributesListenerToStub) {
5944 IDevicesForAttributesCallbackStub callbackStub =
5945 mDevicesForAttributesListenerToStub.get(listener);
5946
5947 if (callbackStub == null) {
5948 callbackStub = new IDevicesForAttributesCallbackStub(listener, executor);
5949 mDevicesForAttributesListenerToStub.put(listener, callbackStub);
5950 }
5951
5952 callbackStub.register(true, attributes);
5953 }
5954 }
5955
5956 /**
5957 * @hide
5958 * Removes a previously registered listener for being notified of routing changes for the given
5959 * {@link AudioAttributes}.
5960 * @param listener
5961 */
5962 @SystemApi
5963 @RequiresPermission(anyOf = {
5964 android.Manifest.permission.MODIFY_AUDIO_ROUTING,
5965 android.Manifest.permission.QUERY_AUDIO_STATE
5966 })
5967 public void removeOnDevicesForAttributesChangedListener(
5968 @NonNull OnDevicesForAttributesChangedListener listener) {
5969 Objects.requireNonNull(listener);
5970
5971 synchronized (mDevicesForAttributesListenerToStub) {
5972 IDevicesForAttributesCallbackStub callbackStub =
5973 mDevicesForAttributesListenerToStub.get(listener);
5974 if (callbackStub != null) {
5975 callbackStub.register(false, null /* attributes */);
5976 }
5977
5978 mDevicesForAttributesListenerToStub.remove(listener);
5979 }
5980 }
5981
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07005982 /**
Dorin Drimusdaeb6a92021-12-22 11:46:26 +01005983 * Get the audio devices that would be used for the routing of the given audio attributes.
5984 * These are the devices anticipated to play sound from an {@link AudioTrack} created with
5985 * the specified {@link AudioAttributes}.
5986 * The audio routing can change if audio devices are physically connected or disconnected or
5987 * concurrently through {@link AudioRouting} or {@link MediaRouter}.
5988 * @param attributes the {@link AudioAttributes} for which the routing is being queried
5989 * @return an empty list if there was an issue with the request, a list of
5990 * {@link AudioDeviceInfo} otherwise (typically one device, except for duplicated paths).
5991 */
5992 public @NonNull List<AudioDeviceInfo> getAudioDevicesForAttributes(
5993 @NonNull AudioAttributes attributes) {
5994 final List<AudioDeviceAttributes> devicesForAttributes;
5995 try {
5996 Objects.requireNonNull(attributes);
5997 final IAudioService service = getService();
5998 devicesForAttributes = service.getDevicesForAttributesUnprotected(attributes);
5999 } catch (Exception e) {
6000 Log.i(TAG, "No audio devices available for specified attributes.");
6001 return Collections.emptyList();
6002 }
6003
6004 // Map from AudioDeviceAttributes to AudioDeviceInfo
6005 AudioDeviceInfo[] outputDeviceInfos = getDevicesStatic(GET_DEVICES_OUTPUTS);
6006 List<AudioDeviceInfo> deviceInfosForAttributes = new ArrayList<>();
6007 for (AudioDeviceAttributes deviceForAttributes : devicesForAttributes) {
6008 for (AudioDeviceInfo deviceInfo : outputDeviceInfos) {
6009 if (deviceForAttributes.getType() == deviceInfo.getType()
6010 && TextUtils.equals(deviceForAttributes.getAddress(),
6011 deviceInfo.getAddress())) {
6012 deviceInfosForAttributes.add(deviceInfo);
6013 }
6014 }
6015 }
6016 return Collections.unmodifiableList(deviceInfosForAttributes);
6017 }
6018
6019 /**
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006020 * @hide
Madhava Srinivasanb5ed6002020-06-11 21:49:24 +00006021 * Volume behavior for an audio device that has no particular volume behavior set. Invalid as
Marvin Ramin5495cc92020-07-23 11:58:33 +02006022 * an argument to {@link #setDeviceVolumeBehavior(AudioDeviceAttributes, int)} and should not
6023 * be returned by {@link #getDeviceVolumeBehavior(AudioDeviceAttributes)}.
Madhava Srinivasanb5ed6002020-06-11 21:49:24 +00006024 */
6025 public static final int DEVICE_VOLUME_BEHAVIOR_UNSET = -1;
6026 /**
6027 * @hide
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006028 * Volume behavior for an audio device where a software attenuation is applied
Marvin Ramin5495cc92020-07-23 11:58:33 +02006029 * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006030 */
Marvin Ramin5495cc92020-07-23 11:58:33 +02006031 @SystemApi
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006032 public static final int DEVICE_VOLUME_BEHAVIOR_VARIABLE = 0;
6033 /**
6034 * @hide
6035 * Volume behavior for an audio device where the volume is always set to provide no attenuation
6036 * nor gain (e.g. unit gain).
Marvin Ramin5495cc92020-07-23 11:58:33 +02006037 * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006038 */
Marvin Ramin5495cc92020-07-23 11:58:33 +02006039 @SystemApi
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006040 public static final int DEVICE_VOLUME_BEHAVIOR_FULL = 1;
6041 /**
6042 * @hide
6043 * Volume behavior for an audio device where the volume is either set to muted, or to provide
6044 * no attenuation nor gain (e.g. unit gain).
Marvin Ramin5495cc92020-07-23 11:58:33 +02006045 * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006046 */
Marvin Ramin5495cc92020-07-23 11:58:33 +02006047 @SystemApi
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006048 public static final int DEVICE_VOLUME_BEHAVIOR_FIXED = 2;
6049 /**
6050 * @hide
6051 * Volume behavior for an audio device where no software attenuation is applied, and
6052 * the volume is kept synchronized between the host and the device itself through a
6053 * device-specific protocol such as BT AVRCP.
Marvin Ramin5495cc92020-07-23 11:58:33 +02006054 * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006055 */
Marvin Ramin5495cc92020-07-23 11:58:33 +02006056 @SystemApi
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006057 public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3;
6058 /**
6059 * @hide
6060 * Volume behavior for an audio device where no software attenuation is applied, and
6061 * the volume is kept synchronized between the host and the device itself through a
6062 * device-specific protocol (such as for hearing aids), based on the audio mode (e.g.
6063 * normal vs in phone call).
6064 * @see #setMode(int)
Marvin Ramin5495cc92020-07-23 11:58:33 +02006065 * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006066 */
Marvin Ramin5495cc92020-07-23 11:58:33 +02006067 @SystemApi
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006068 public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4;
6069
6070 /** @hide */
6071 @IntDef({
6072 DEVICE_VOLUME_BEHAVIOR_VARIABLE,
6073 DEVICE_VOLUME_BEHAVIOR_FULL,
6074 DEVICE_VOLUME_BEHAVIOR_FIXED,
6075 DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
6076 DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE,
6077 })
6078 @Retention(RetentionPolicy.SOURCE)
6079 public @interface DeviceVolumeBehavior {}
6080
Madhava Srinivasanb5ed6002020-06-11 21:49:24 +00006081 /** @hide */
6082 @IntDef({
6083 DEVICE_VOLUME_BEHAVIOR_UNSET,
6084 DEVICE_VOLUME_BEHAVIOR_VARIABLE,
6085 DEVICE_VOLUME_BEHAVIOR_FULL,
6086 DEVICE_VOLUME_BEHAVIOR_FIXED,
6087 DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
6088 DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE,
6089 })
6090 @Retention(RetentionPolicy.SOURCE)
6091 public @interface DeviceVolumeBehaviorState {}
6092
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006093 /**
6094 * @hide
6095 * Throws IAE on an invalid volume behavior value
6096 * @param volumeBehavior behavior value to check
6097 */
6098 public static void enforceValidVolumeBehavior(int volumeBehavior) {
6099 switch (volumeBehavior) {
6100 case DEVICE_VOLUME_BEHAVIOR_VARIABLE:
6101 case DEVICE_VOLUME_BEHAVIOR_FULL:
6102 case DEVICE_VOLUME_BEHAVIOR_FIXED:
6103 case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
6104 case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE:
6105 return;
6106 default:
6107 throw new IllegalArgumentException("Illegal volume behavior " + volumeBehavior);
6108 }
6109 }
6110
6111 /**
6112 * @hide
6113 * Sets the volume behavior for an audio output device.
Marvin Ramin5495cc92020-07-23 11:58:33 +02006114 * @see #DEVICE_VOLUME_BEHAVIOR_VARIABLE
6115 * @see #DEVICE_VOLUME_BEHAVIOR_FULL
6116 * @see #DEVICE_VOLUME_BEHAVIOR_FIXED
6117 * @see #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE
6118 * @see #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE
6119 * @param device the device to be affected
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006120 * @param deviceVolumeBehavior one of the device behaviors
6121 */
Marvin Ramin5495cc92020-07-23 11:58:33 +02006122 @SystemApi
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006123 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
6124 public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
6125 @DeviceVolumeBehavior int deviceVolumeBehavior) {
6126 // verify arguments (validity of device type is enforced in server)
6127 Objects.requireNonNull(device);
6128 enforceValidVolumeBehavior(deviceVolumeBehavior);
6129 // communicate with service
6130 final IAudioService service = getService();
6131 try {
6132 service.setDeviceVolumeBehavior(device, deviceVolumeBehavior,
6133 mApplicationContext.getOpPackageName());
6134 } catch (RemoteException e) {
6135 throw e.rethrowFromSystemServer();
6136 }
6137 }
6138
6139 /**
6140 * @hide
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006141 * Returns the volume device behavior for the given audio device
6142 * @param device the audio device
6143 * @return the volume behavior for the device
6144 */
Marvin Ramin5495cc92020-07-23 11:58:33 +02006145 @SystemApi
kholoud mohamed37839212021-03-15 16:49:06 +00006146 @RequiresPermission(anyOf = {
6147 android.Manifest.permission.MODIFY_AUDIO_ROUTING,
6148 android.Manifest.permission.QUERY_AUDIO_STATE
6149 })
Marvin Ramin5495cc92020-07-23 11:58:33 +02006150 public @DeviceVolumeBehavior
6151 int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006152 // verify arguments (validity of device type is enforced in server)
6153 Objects.requireNonNull(device);
6154 // communicate with service
6155 final IAudioService service = getService();
6156 try {
6157 return service.getDeviceVolumeBehavior(device);
6158 } catch (RemoteException e) {
6159 throw e.rethrowFromSystemServer();
6160 }
6161 }
6162
kholoud mohamed37839212021-03-15 16:49:06 +00006163 /**
6164 * @hide
6165 * Returns {@code true} if the volume device behavior is {@link #DEVICE_VOLUME_BEHAVIOR_FULL}.
6166 */
6167 @TestApi
6168 @RequiresPermission(anyOf = {
6169 android.Manifest.permission.MODIFY_AUDIO_ROUTING,
6170 android.Manifest.permission.QUERY_AUDIO_STATE
6171 })
6172 public boolean isFullVolumeDevice() {
6173 final AudioAttributes attributes = new AudioAttributes.Builder()
6174 .setUsage(AudioAttributes.USAGE_MEDIA)
6175 .build();
6176 final List<AudioDeviceAttributes> devices = getDevicesForAttributes(attributes);
6177 for (AudioDeviceAttributes device : devices) {
6178 if (getDeviceVolumeBehavior(device) == DEVICE_VOLUME_BEHAVIOR_FULL) {
6179 return true;
6180 }
6181 }
6182 return false;
6183 }
6184
Nathalie Le Clair517a1322021-10-15 14:22:41 +02006185 /**
Eric Laurentb1fbaac2012-05-29 09:24:28 -07006186 * Indicate wired accessory connection state change.
6187 * @param device type of device connected/disconnected (AudioManager.DEVICE_OUT_xxx)
6188 * @param state new connection state: 1 connected, 0 disconnected
6189 * @param name device name
6190 * {@hide}
6191 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01006192 @UnsupportedAppUsage
Jean-Michel Trivif0491972020-03-24 09:20:50 -07006193 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
Nathalie Le Clair7b72c392022-04-04 13:25:46 +02006194 public void setWiredDeviceConnectionState(int device, int state, String address, String name) {
6195 AudioDeviceAttributes attributes = new AudioDeviceAttributes(device, address, name);
Nathalie Le Clair517a1322021-10-15 14:22:41 +02006196 setWiredDeviceConnectionState(attributes, state);
6197 }
6198
6199 /**
6200 * Indicate wired accessory connection state change and attributes.
6201 * @param state new connection state: 1 connected, 0 disconnected
6202 * @param attributes attributes of the connected device
6203 * {@hide}
6204 */
6205 @UnsupportedAppUsage
6206 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
6207 public void setWiredDeviceConnectionState(AudioDeviceAttributes attributes, int state) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07006208 final IAudioService service = getService();
Eric Laurentb1fbaac2012-05-29 09:24:28 -07006209 try {
Nathalie Le Clair517a1322021-10-15 14:22:41 +02006210 service.setWiredDeviceConnectionState(attributes, state,
Marco Nelissena80ac052015-03-12 16:17:45 -07006211 mApplicationContext.getOpPackageName());
Eric Laurentb1fbaac2012-05-29 09:24:28 -07006212 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006213 throw e.rethrowFromSystemServer();
Eric Laurentb1fbaac2012-05-29 09:24:28 -07006214 }
6215 }
6216
Grzegorz Kołodziejczyk59b2baa2021-05-14 12:19:07 +00006217 /**
Jean-Michel Trivi4da775d2021-12-03 15:33:46 -08006218 * Indicate wired accessory connection state change.
6219 * @param device {@link AudioDeviceAttributes} of the device to "fake-connect"
6220 * @param connected true for connected, false for disconnected
6221 * {@hide}
6222 */
6223 @TestApi
6224 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
6225 public void setTestDeviceConnectionState(@NonNull AudioDeviceAttributes device,
6226 boolean connected) {
6227 try {
6228 getService().setTestDeviceConnectionState(device, connected);
6229 } catch (RemoteException e) {
6230 throw e.rethrowFromSystemServer();
6231 }
6232 }
6233
6234 /**
wescande7c17ba0c2021-07-30 16:46:14 +02006235 * Indicate Bluetooth profile connection state change.
6236 * Configuration changes for A2DP are indicated by having the same <code>newDevice</code> and
6237 * <code>previousDevice</code>
6238 * This operation is asynchronous.
6239 *
6240 * @param newDevice Bluetooth device connected or null if there is no new devices
6241 * @param previousDevice Bluetooth device disconnected or null if there is no disconnected
6242 * devices
William Escandeac11d772022-01-25 18:01:15 +01006243 * @param info contain all info related to the device. {@link BluetoothProfileConnectionInfo}
Pavlin Radoslavov82e06462018-01-19 18:20:04 -08006244 * {@hide}
6245 */
wescande7c17ba0c2021-07-30 16:46:14 +02006246 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
6247 @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
6248 public void handleBluetoothActiveDeviceChanged(@Nullable BluetoothDevice newDevice,
William Escandeac11d772022-01-25 18:01:15 +01006249 @Nullable BluetoothDevice previousDevice,
6250 @NonNull BluetoothProfileConnectionInfo info) {
Pavlin Radoslavov82e06462018-01-19 18:20:04 -08006251 final IAudioService service = getService();
Pavlin Radoslavov82e06462018-01-19 18:20:04 -08006252 try {
wescande7c17ba0c2021-07-30 16:46:14 +02006253 service.handleBluetoothActiveDeviceChanged(newDevice, previousDevice, info);
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -08006254 } catch (RemoteException e) {
6255 throw e.rethrowFromSystemServer();
6256 }
6257 }
6258
Jeff Sharkey098d5802012-04-26 17:30:34 -07006259 /** {@hide} */
6260 public IRingtonePlayer getRingtonePlayer() {
6261 try {
6262 return getService().getRingtonePlayer();
6263 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006264 throw e.rethrowFromSystemServer();
Jeff Sharkey098d5802012-04-26 17:30:34 -07006265 }
6266 }
Glenn Kasten228c9842012-09-14 08:48:47 -07006267
6268 /**
Glenn Kasten62796aa2012-09-20 08:08:07 -07006269 * Used as a key for {@link #getProperty} to request the native or optimal output sample rate
Glenn Kastenc3de5142016-07-15 12:14:24 -07006270 * for this device's low latency output stream, in decimal Hz. Latency-sensitive apps
6271 * should use this value as a default, and offer the user the option to override it.
6272 * The low latency output stream is typically either the device's primary output stream,
6273 * or another output stream with smaller buffers.
Glenn Kasten228c9842012-09-14 08:48:47 -07006274 */
Glenn Kasten1cbf9b32016-02-02 12:04:09 -08006275 // FIXME Deprecate
Glenn Kasten228c9842012-09-14 08:48:47 -07006276 public static final String PROPERTY_OUTPUT_SAMPLE_RATE =
6277 "android.media.property.OUTPUT_SAMPLE_RATE";
6278
6279 /**
Glenn Kasten62796aa2012-09-20 08:08:07 -07006280 * Used as a key for {@link #getProperty} to request the native or optimal output buffer size
Glenn Kastenc3de5142016-07-15 12:14:24 -07006281 * for this device's low latency output stream, in decimal PCM frames. Latency-sensitive apps
6282 * should use this value as a minimum, and offer the user the option to override it.
6283 * The low latency output stream is typically either the device's primary output stream,
6284 * or another output stream with smaller buffers.
Glenn Kasten228c9842012-09-14 08:48:47 -07006285 */
Glenn Kasten1cbf9b32016-02-02 12:04:09 -08006286 // FIXME Deprecate
Glenn Kasten228c9842012-09-14 08:48:47 -07006287 public static final String PROPERTY_OUTPUT_FRAMES_PER_BUFFER =
6288 "android.media.property.OUTPUT_FRAMES_PER_BUFFER";
6289
6290 /**
Arunesh Mishrad08715e52015-04-23 22:39:40 -07006291 * Used as a key for {@link #getProperty} to determine if the default microphone audio source
6292 * supports near-ultrasound frequencies (range of 18 - 21 kHz).
6293 */
6294 public static final String PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND =
6295 "android.media.property.SUPPORT_MIC_NEAR_ULTRASOUND";
6296
6297 /**
6298 * Used as a key for {@link #getProperty} to determine if the default speaker audio path
6299 * supports near-ultrasound frequencies (range of 18 - 21 kHz).
6300 */
6301 public static final String PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND =
6302 "android.media.property.SUPPORT_SPEAKER_NEAR_ULTRASOUND";
6303
6304 /**
ragoa7cc59c2015-12-02 11:31:15 -08006305 * Used as a key for {@link #getProperty} to determine if the unprocessed audio source is
6306 * available and supported with the expected frequency range and level response.
6307 */
6308 public static final String PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED =
6309 "android.media.property.SUPPORT_AUDIO_SOURCE_UNPROCESSED";
6310 /**
Glenn Kasten62796aa2012-09-20 08:08:07 -07006311 * Returns the value of the property with the specified key.
Glenn Kasten228c9842012-09-14 08:48:47 -07006312 * @param key One of the strings corresponding to a property key: either
Glenn Kasten0b986af2015-10-30 18:24:04 -07006313 * {@link #PROPERTY_OUTPUT_SAMPLE_RATE},
6314 * {@link #PROPERTY_OUTPUT_FRAMES_PER_BUFFER},
ragoa7cc59c2015-12-02 11:31:15 -08006315 * {@link #PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND},
6316 * {@link #PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND}, or
6317 * {@link #PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED}.
Glenn Kasten228c9842012-09-14 08:48:47 -07006318 * @return A string representing the associated value for that property key,
6319 * or null if there is no value for that key.
Glenn Kasten228c9842012-09-14 08:48:47 -07006320 */
6321 public String getProperty(String key) {
Glenn Kastenc6c43652012-09-24 17:32:30 -07006322 if (PROPERTY_OUTPUT_SAMPLE_RATE.equals(key)) {
6323 int outputSampleRate = AudioSystem.getPrimaryOutputSamplingRate();
6324 return outputSampleRate > 0 ? Integer.toString(outputSampleRate) : null;
6325 } else if (PROPERTY_OUTPUT_FRAMES_PER_BUFFER.equals(key)) {
6326 int outputFramesPerBuffer = AudioSystem.getPrimaryOutputFrameCount();
6327 return outputFramesPerBuffer > 0 ? Integer.toString(outputFramesPerBuffer) : null;
Arunesh Mishrabc922272015-04-27 09:39:00 -07006328 } else if (PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND.equals(key)) {
Arunesh Mishra6e4fb342015-06-02 13:23:41 -07006329 // Will throw a RuntimeException Resources.NotFoundException if this config value is
6330 // not found.
6331 return String.valueOf(getContext().getResources().getBoolean(
6332 com.android.internal.R.bool.config_supportMicNearUltrasound));
Arunesh Mishrabc922272015-04-27 09:39:00 -07006333 } else if (PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND.equals(key)) {
Arunesh Mishra6e4fb342015-06-02 13:23:41 -07006334 return String.valueOf(getContext().getResources().getBoolean(
6335 com.android.internal.R.bool.config_supportSpeakerNearUltrasound));
ragoa7cc59c2015-12-02 11:31:15 -08006336 } else if (PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED.equals(key)) {
6337 return String.valueOf(getContext().getResources().getBoolean(
6338 com.android.internal.R.bool.config_supportAudioSourceUnprocessed));
Glenn Kastenc6c43652012-09-24 17:32:30 -07006339 } else {
6340 // null or unknown key
6341 return null;
6342 }
Glenn Kasten228c9842012-09-14 08:48:47 -07006343 }
6344
Oliver Woodman61dcdf32013-06-26 12:43:36 +01006345 /**
Andy Hung97aa07f82020-01-17 14:05:06 -08006346 * @hide
6347 * Sets an additional audio output device delay in milliseconds.
6348 *
6349 * The additional output delay is a request to the output device to
6350 * delay audio presentation (generally with respect to video presentation for better
6351 * synchronization).
6352 * It may not be supported by all output devices,
6353 * and typically increases the audio latency by the amount of additional
6354 * audio delay requested.
6355 *
6356 * If additional audio delay is supported by an audio output device,
6357 * it is expected to be supported for all output streams (and configurations)
6358 * opened on that device.
6359 *
6360 * @param device an instance of {@link AudioDeviceInfo} returned from {@link getDevices()}.
Andy Hung120c1c42020-03-26 12:01:33 -07006361 * @param delayMillis delay in milliseconds desired. This should be in range of {@code 0}
Andy Hung97aa07f82020-01-17 14:05:06 -08006362 * to the value returned by {@link #getMaxAdditionalOutputDeviceDelay()}.
6363 * @return true if successful, false if the device does not support output device delay
6364 * or the delay is not in range of {@link #getMaxAdditionalOutputDeviceDelay()}.
6365 */
6366 @SystemApi
6367 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
6368 public boolean setAdditionalOutputDeviceDelay(
Andy Hung120c1c42020-03-26 12:01:33 -07006369 @NonNull AudioDeviceInfo device, @IntRange(from = 0) long delayMillis) {
Andy Hung97aa07f82020-01-17 14:05:06 -08006370 Objects.requireNonNull(device);
Kuowei Lif50ac2e32020-08-13 14:34:17 +08006371 try {
6372 return getService().setAdditionalOutputDeviceDelay(
6373 new AudioDeviceAttributes(device), delayMillis);
6374 } catch (RemoteException e) {
6375 throw e.rethrowFromSystemServer();
6376 }
Andy Hung97aa07f82020-01-17 14:05:06 -08006377 }
6378
6379 /**
6380 * @hide
6381 * Returns the current additional audio output device delay in milliseconds.
6382 *
6383 * @param device an instance of {@link AudioDeviceInfo} returned from {@link getDevices()}.
6384 * @return the additional output device delay. This is a non-negative number.
6385 * {@code 0} is returned if unsupported.
6386 */
6387 @SystemApi
6388 @IntRange(from = 0)
Andy Hung120c1c42020-03-26 12:01:33 -07006389 public long getAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) {
Andy Hung97aa07f82020-01-17 14:05:06 -08006390 Objects.requireNonNull(device);
Kuowei Lif50ac2e32020-08-13 14:34:17 +08006391 try {
6392 return getService().getAdditionalOutputDeviceDelay(new AudioDeviceAttributes(device));
6393 } catch (RemoteException e) {
6394 throw e.rethrowFromSystemServer();
6395 }
Andy Hung97aa07f82020-01-17 14:05:06 -08006396 }
6397
6398 /**
6399 * @hide
6400 * Returns the maximum additional audio output device delay in milliseconds.
6401 *
6402 * @param device an instance of {@link AudioDeviceInfo} returned from {@link getDevices()}.
6403 * @return the maximum output device delay in milliseconds that can be set.
6404 * This is a non-negative number
6405 * representing the additional audio delay supported for the device.
6406 * {@code 0} is returned if unsupported.
6407 */
6408 @SystemApi
6409 @IntRange(from = 0)
Andy Hung120c1c42020-03-26 12:01:33 -07006410 public long getMaxAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) {
Andy Hung97aa07f82020-01-17 14:05:06 -08006411 Objects.requireNonNull(device);
Kuowei Lif50ac2e32020-08-13 14:34:17 +08006412 try {
6413 return getService().getMaxAdditionalOutputDeviceDelay(
6414 new AudioDeviceAttributes(device));
6415 } catch (RemoteException e) {
6416 throw e.rethrowFromSystemServer();
6417 }
Andy Hung97aa07f82020-01-17 14:05:06 -08006418 }
6419
6420 /**
Oliver Woodman61dcdf32013-06-26 12:43:36 +01006421 * Returns the estimated latency for the given stream type in milliseconds.
6422 *
6423 * DO NOT UNHIDE. The existing approach for doing A/V sync has too many problems. We need
6424 * a better solution.
6425 * @hide
6426 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01006427 @UnsupportedAppUsage
Oliver Woodman61dcdf32013-06-26 12:43:36 +01006428 public int getOutputLatency(int streamType) {
6429 return AudioSystem.getOutputLatency(streamType);
6430 }
6431
John Spurlock3346a802014-05-20 16:25:37 -04006432 /**
6433 * Registers a global volume controller interface. Currently limited to SystemUI.
6434 *
6435 * @hide
6436 */
6437 public void setVolumeController(IVolumeController controller) {
6438 try {
6439 getService().setVolumeController(controller);
6440 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006441 throw e.rethrowFromSystemServer();
John Spurlock3346a802014-05-20 16:25:37 -04006442 }
6443 }
6444
6445 /**
John Spurlock33f4e042014-07-11 13:10:58 -04006446 * Notify audio manager about volume controller visibility changes.
6447 * Currently limited to SystemUI.
6448 *
6449 * @hide
6450 */
6451 public void notifyVolumeControllerVisible(IVolumeController controller, boolean visible) {
6452 try {
6453 getService().notifyVolumeControllerVisible(controller, visible);
6454 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006455 throw e.rethrowFromSystemServer();
John Spurlock33f4e042014-07-11 13:10:58 -04006456 }
6457 }
6458
6459 /**
John Spurlock3346a802014-05-20 16:25:37 -04006460 * Only useful for volume controllers.
6461 * @hide
6462 */
John Spurlock3346a802014-05-20 16:25:37 -04006463 public boolean isStreamAffectedByRingerMode(int streamType) {
6464 try {
6465 return getService().isStreamAffectedByRingerMode(streamType);
6466 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006467 throw e.rethrowFromSystemServer();
John Spurlock3346a802014-05-20 16:25:37 -04006468 }
6469 }
6470
6471 /**
6472 * Only useful for volume controllers.
6473 * @hide
6474 */
John Spurlocka9dfbe8b2015-02-17 11:01:51 -05006475 public boolean isStreamAffectedByMute(int streamType) {
6476 try {
6477 return getService().isStreamAffectedByMute(streamType);
6478 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006479 throw e.rethrowFromSystemServer();
John Spurlocka9dfbe8b2015-02-17 11:01:51 -05006480 }
6481 }
6482
6483 /**
6484 * Only useful for volume controllers.
6485 * @hide
6486 */
John Spurlock3346a802014-05-20 16:25:37 -04006487 public void disableSafeMediaVolume() {
6488 try {
Marco Nelissena80ac052015-03-12 16:17:45 -07006489 getService().disableSafeMediaVolume(mApplicationContext.getOpPackageName());
John Spurlock3346a802014-05-20 16:25:37 -04006490 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006491 throw e.rethrowFromSystemServer();
John Spurlock3346a802014-05-20 16:25:37 -04006492 }
6493 }
Eric Laurenta198a292014-02-18 16:26:17 -08006494
6495 /**
Jean-Michel Trivia5321ff2022-12-14 22:11:38 +00006496 * @hide
6497 * Lower media volume to RS1
6498 */
6499 public void lowerVolumeToRs1() {
6500 try {
6501 getService().lowerVolumeToRs1(mApplicationContext.getOpPackageName());
6502 } catch (RemoteException e) {
6503 throw e.rethrowFromSystemServer();
6504 }
6505 }
6506
6507 /**
6508 * @hide
6509 * Sound dose warning at every 100% of dose during integration window
6510 */
6511 public static final int CSD_WARNING_DOSE_REACHED_1X = 1;
6512 /**
6513 * @hide
6514 * Sound dose warning when 500% of dose is reached during integration window
6515 */
6516 public static final int CSD_WARNING_DOSE_REPEATED_5X = 2;
6517 /**
6518 * @hide
6519 * Sound dose warning after a momentary exposure event
6520 */
6521 public static final int CSD_WARNING_MOMENTARY_EXPOSURE = 3;
6522 /**
6523 * @hide
6524 * Sound dose warning at every 100% of dose during integration window
6525 */
6526 public static final int CSD_WARNING_ACCUMULATION_START = 4;
6527
6528 /** @hide */
6529 @IntDef(flag = false, value = {
6530 CSD_WARNING_DOSE_REACHED_1X,
6531 CSD_WARNING_DOSE_REPEATED_5X,
6532 CSD_WARNING_MOMENTARY_EXPOSURE,
6533 CSD_WARNING_ACCUMULATION_START }
6534 )
6535 @Retention(RetentionPolicy.SOURCE)
6536 public @interface CsdWarning {}
6537
6538 /**
John Spurlock661f2cf42014-11-17 10:29:10 -05006539 * Only useful for volume controllers.
6540 * @hide
6541 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01006542 @UnsupportedAppUsage
John Spurlock661f2cf42014-11-17 10:29:10 -05006543 public void setRingerModeInternal(int ringerMode) {
6544 try {
Marco Nelissen29f16932015-04-17 09:50:56 -07006545 getService().setRingerModeInternal(ringerMode, getContext().getOpPackageName());
John Spurlock661f2cf42014-11-17 10:29:10 -05006546 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006547 throw e.rethrowFromSystemServer();
John Spurlock661f2cf42014-11-17 10:29:10 -05006548 }
6549 }
6550
6551 /**
6552 * Only useful for volume controllers.
6553 * @hide
6554 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01006555 @UnsupportedAppUsage
John Spurlock661f2cf42014-11-17 10:29:10 -05006556 public int getRingerModeInternal() {
6557 try {
6558 return getService().getRingerModeInternal();
6559 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006560 throw e.rethrowFromSystemServer();
John Spurlock661f2cf42014-11-17 10:29:10 -05006561 }
6562 }
6563
6564 /**
John Spurlocka48d7792015-03-03 17:35:57 -05006565 * Only useful for volume controllers.
6566 * @hide
6567 */
6568 public void setVolumePolicy(VolumePolicy policy) {
6569 try {
6570 getService().setVolumePolicy(policy);
6571 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006572 throw e.rethrowFromSystemServer();
John Spurlocka48d7792015-03-03 17:35:57 -05006573 }
6574 }
6575
6576 /**
Jungshik Jang41d97462014-06-30 22:26:29 +09006577 * Set Hdmi Cec system audio mode.
6578 *
6579 * @param on whether to be on system audio mode
Jungshik Jang6f34f5a2014-07-08 21:17:29 +09006580 * @return output device type. 0 (DEVICE_NONE) if failed to set device.
Jungshik Jang41d97462014-06-30 22:26:29 +09006581 * @hide
6582 */
Jungshik Jang12307ca2014-07-15 19:27:56 +09006583 public int setHdmiSystemAudioSupported(boolean on) {
Jungshik Jang41d97462014-06-30 22:26:29 +09006584 try {
Jungshik Jang12307ca2014-07-15 19:27:56 +09006585 return getService().setHdmiSystemAudioSupported(on);
Jungshik Jang41d97462014-06-30 22:26:29 +09006586 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006587 throw e.rethrowFromSystemServer();
Jungshik Jang41d97462014-06-30 22:26:29 +09006588 }
6589 }
6590
6591 /**
Terry Heoe7d6d972014-09-04 21:05:28 +09006592 * Returns true if Hdmi Cec system audio mode is supported.
6593 *
6594 * @hide
6595 */
6596 @SystemApi
Aurimas Liutikas1394a012020-11-12 18:26:09 -08006597 @SuppressLint("RequiresPermission") // FIXME is this still used?
Terry Heoe7d6d972014-09-04 21:05:28 +09006598 public boolean isHdmiSystemAudioSupported() {
6599 try {
6600 return getService().isHdmiSystemAudioSupported();
6601 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006602 throw e.rethrowFromSystemServer();
Terry Heoe7d6d972014-09-04 21:05:28 +09006603 }
6604 }
6605
6606 /**
Eric Laurenta198a292014-02-18 16:26:17 -08006607 * Return codes for listAudioPorts(), createAudioPatch() ...
6608 */
6609
Jean-Michel Trivid6f65de2018-12-18 18:49:14 -08006610 /** @hide */
6611 @SystemApi
Eric Laurenta198a292014-02-18 16:26:17 -08006612 public static final int SUCCESS = AudioSystem.SUCCESS;
Jean-Michel Trivi8a21f5d2014-06-05 15:03:52 -07006613 /**
6614 * A default error code.
Eric Laurenta198a292014-02-18 16:26:17 -08006615 */
6616 public static final int ERROR = AudioSystem.ERROR;
6617 /** @hide
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07006618 * CANDIDATE FOR PUBLIC API
Eric Laurenta198a292014-02-18 16:26:17 -08006619 */
6620 public static final int ERROR_BAD_VALUE = AudioSystem.BAD_VALUE;
6621 /** @hide
6622 */
6623 public static final int ERROR_INVALID_OPERATION = AudioSystem.INVALID_OPERATION;
6624 /** @hide
6625 */
6626 public static final int ERROR_PERMISSION_DENIED = AudioSystem.PERMISSION_DENIED;
6627 /** @hide
6628 */
6629 public static final int ERROR_NO_INIT = AudioSystem.NO_INIT;
Eric Laurentff0d9f02014-06-09 17:23:02 -07006630 /**
6631 * An error code indicating that the object reporting it is no longer valid and needs to
6632 * be recreated.
Eric Laurenta198a292014-02-18 16:26:17 -08006633 */
6634 public static final int ERROR_DEAD_OBJECT = AudioSystem.DEAD_OBJECT;
6635
6636 /**
6637 * Returns a list of descriptors for all audio ports managed by the audio framework.
6638 * Audio ports are nodes in the audio framework or audio hardware that can be configured
6639 * or connected and disconnected with createAudioPatch() or releaseAudioPatch().
6640 * See AudioPort for a list of attributes of each audio port.
6641 * @param ports An AudioPort ArrayList where the list will be returned.
6642 * @hide
6643 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01006644 @UnsupportedAppUsage
Eric Laurent4bcdba82015-05-01 11:37:49 -07006645 public static int listAudioPorts(ArrayList<AudioPort> ports) {
Eric Laurent8a1e7a82015-05-08 11:43:05 -07006646 return updateAudioPortCache(ports, null, null);
6647 }
6648
6649 /**
6650 * Returns a list of descriptors for all audio ports managed by the audio framework as
6651 * it was before the last update calback.
6652 * @param ports An AudioPort ArrayList where the list will be returned.
6653 * @hide
6654 */
6655 public static int listPreviousAudioPorts(ArrayList<AudioPort> ports) {
6656 return updateAudioPortCache(null, null, ports);
Eric Laurenta198a292014-02-18 16:26:17 -08006657 }
6658
6659 /**
6660 * Specialized version of listAudioPorts() listing only audio devices (AudioDevicePort)
6661 * @see listAudioPorts(ArrayList<AudioPort>)
6662 * @hide
6663 */
Eric Laurent4bcdba82015-05-01 11:37:49 -07006664 public static int listAudioDevicePorts(ArrayList<AudioDevicePort> devices) {
Eric Laurent8a1e7a82015-05-08 11:43:05 -07006665 if (devices == null) {
6666 return ERROR_BAD_VALUE;
6667 }
Eric Laurentb69681c2014-05-19 19:02:51 -07006668 ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
Eric Laurent8a1e7a82015-05-08 11:43:05 -07006669 int status = updateAudioPortCache(ports, null, null);
Eric Laurentb69681c2014-05-19 19:02:51 -07006670 if (status == SUCCESS) {
Eric Laurent8a1e7a82015-05-08 11:43:05 -07006671 filterDevicePorts(ports, devices);
Eric Laurentb69681c2014-05-19 19:02:51 -07006672 }
6673 return status;
Eric Laurenta198a292014-02-18 16:26:17 -08006674 }
6675
6676 /**
Eric Laurent8a1e7a82015-05-08 11:43:05 -07006677 * Specialized version of listPreviousAudioPorts() listing only audio devices (AudioDevicePort)
6678 * @see listPreviousAudioPorts(ArrayList<AudioPort>)
6679 * @hide
6680 */
6681 public static int listPreviousAudioDevicePorts(ArrayList<AudioDevicePort> devices) {
6682 if (devices == null) {
6683 return ERROR_BAD_VALUE;
6684 }
6685 ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
6686 int status = updateAudioPortCache(null, null, ports);
6687 if (status == SUCCESS) {
6688 filterDevicePorts(ports, devices);
6689 }
6690 return status;
6691 }
6692
6693 private static void filterDevicePorts(ArrayList<AudioPort> ports,
6694 ArrayList<AudioDevicePort> devices) {
6695 devices.clear();
6696 for (int i = 0; i < ports.size(); i++) {
6697 if (ports.get(i) instanceof AudioDevicePort) {
6698 devices.add((AudioDevicePort)ports.get(i));
6699 }
6700 }
6701 }
6702
6703 /**
Eric Laurenta198a292014-02-18 16:26:17 -08006704 * Create a connection between two or more devices. The framework will reject the request if
6705 * device types are not compatible or the implementation does not support the requested
6706 * configuration.
6707 * NOTE: current implementation is limited to one source and one sink per patch.
6708 * @param patch AudioPatch array where the newly created patch will be returned.
6709 * As input, if patch[0] is not null, the specified patch will be replaced by the
6710 * new patch created. This avoids calling releaseAudioPatch() when modifying a
6711 * patch and allows the implementation to optimize transitions.
6712 * @param sources List of source audio ports. All must be AudioPort.ROLE_SOURCE.
6713 * @param sinks List of sink audio ports. All must be AudioPort.ROLE_SINK.
6714 *
6715 * @return - {@link #SUCCESS} if connection is successful.
6716 * - {@link #ERROR_BAD_VALUE} if incompatible device types are passed.
6717 * - {@link #ERROR_INVALID_OPERATION} if the requested connection is not supported.
6718 * - {@link #ERROR_PERMISSION_DENIED} if the client does not have permission to create
6719 * a patch.
6720 * - {@link #ERROR_DEAD_OBJECT} if the server process is dead
6721 * - {@link #ERROR} if patch cannot be connected for any other reason.
6722 *
6723 * patch[0] contains the newly created patch
6724 * @hide
6725 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01006726 @UnsupportedAppUsage
Eric Laurent4bcdba82015-05-01 11:37:49 -07006727 public static int createAudioPatch(AudioPatch[] patch,
Eric Laurenta198a292014-02-18 16:26:17 -08006728 AudioPortConfig[] sources,
6729 AudioPortConfig[] sinks) {
Eric Laurentb69681c2014-05-19 19:02:51 -07006730 return AudioSystem.createAudioPatch(patch, sources, sinks);
Eric Laurenta198a292014-02-18 16:26:17 -08006731 }
6732
6733 /**
6734 * Releases an existing audio patch connection.
6735 * @param patch The audio patch to disconnect.
6736 * @return - {@link #SUCCESS} if disconnection is successful.
6737 * - {@link #ERROR_BAD_VALUE} if the specified patch does not exist.
6738 * - {@link #ERROR_PERMISSION_DENIED} if the client does not have permission to release
6739 * a patch.
6740 * - {@link #ERROR_DEAD_OBJECT} if the server process is dead
6741 * - {@link #ERROR} if patch cannot be released for any other reason.
6742 * @hide
6743 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01006744 @UnsupportedAppUsage
Eric Laurent4bcdba82015-05-01 11:37:49 -07006745 public static int releaseAudioPatch(AudioPatch patch) {
Eric Laurentb69681c2014-05-19 19:02:51 -07006746 return AudioSystem.releaseAudioPatch(patch);
Eric Laurenta198a292014-02-18 16:26:17 -08006747 }
6748
6749 /**
6750 * List all existing connections between audio ports.
6751 * @param patches An AudioPatch array where the list will be returned.
6752 * @hide
6753 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01006754 @UnsupportedAppUsage
Eric Laurent4bcdba82015-05-01 11:37:49 -07006755 public static int listAudioPatches(ArrayList<AudioPatch> patches) {
Eric Laurent8a1e7a82015-05-08 11:43:05 -07006756 return updateAudioPortCache(null, patches, null);
Eric Laurenta198a292014-02-18 16:26:17 -08006757 }
6758
6759 /**
6760 * Set the gain on the specified AudioPort. The AudioGainConfig config is build by
6761 * AudioGain.buildConfig()
6762 * @hide
6763 */
Eric Laurent4bcdba82015-05-01 11:37:49 -07006764 public static int setAudioPortGain(AudioPort port, AudioGainConfig gain) {
Eric Laurent3a241992014-05-19 19:33:26 -07006765 if (port == null || gain == null) {
6766 return ERROR_BAD_VALUE;
6767 }
6768 AudioPortConfig activeConfig = port.activeConfig();
6769 AudioPortConfig config = new AudioPortConfig(port, activeConfig.samplingRate(),
6770 activeConfig.channelMask(), activeConfig.format(), gain);
6771 config.mConfigMask = AudioPortConfig.GAIN;
6772 return AudioSystem.setAudioPortConfig(config);
Eric Laurenta198a292014-02-18 16:26:17 -08006773 }
6774
6775 /**
6776 * Listener registered by client to be notified upon new audio port connections,
6777 * disconnections or attributes update.
6778 * @hide
6779 */
6780 public interface OnAudioPortUpdateListener {
6781 /**
6782 * Callback method called upon audio port list update.
6783 * @param portList the updated list of audio ports
6784 */
Eric Laurentfdaed9d2014-07-29 09:29:30 -07006785 public void onAudioPortListUpdate(AudioPort[] portList);
Eric Laurenta198a292014-02-18 16:26:17 -08006786
6787 /**
6788 * Callback method called upon audio patch list update.
6789 * @param patchList the updated list of audio patches
6790 */
Eric Laurentfdaed9d2014-07-29 09:29:30 -07006791 public void onAudioPatchListUpdate(AudioPatch[] patchList);
Eric Laurenta198a292014-02-18 16:26:17 -08006792
6793 /**
6794 * Callback method called when the mediaserver dies
6795 */
Eric Laurentfdaed9d2014-07-29 09:29:30 -07006796 public void onServiceDied();
Eric Laurenta198a292014-02-18 16:26:17 -08006797 }
6798
6799 /**
Eric Laurent700e7342014-05-02 18:33:15 -07006800 * Register an audio port list update listener.
Eric Laurenta198a292014-02-18 16:26:17 -08006801 * @hide
6802 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01006803 @UnsupportedAppUsage
Eric Laurenta198a292014-02-18 16:26:17 -08006804 public void registerAudioPortUpdateListener(OnAudioPortUpdateListener l) {
Eric Laurentc573bc52015-06-26 10:01:12 -07006805 sAudioPortEventHandler.init();
Eric Laurentf076db42015-01-14 13:23:27 -08006806 sAudioPortEventHandler.registerListener(l);
Eric Laurenta198a292014-02-18 16:26:17 -08006807 }
6808
6809 /**
Eric Laurent700e7342014-05-02 18:33:15 -07006810 * Unregister an audio port list update listener.
Eric Laurenta198a292014-02-18 16:26:17 -08006811 * @hide
6812 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01006813 @UnsupportedAppUsage
Eric Laurenta198a292014-02-18 16:26:17 -08006814 public void unregisterAudioPortUpdateListener(OnAudioPortUpdateListener l) {
Eric Laurentf076db42015-01-14 13:23:27 -08006815 sAudioPortEventHandler.unregisterListener(l);
Eric Laurenta198a292014-02-18 16:26:17 -08006816 }
Eric Laurentb69681c2014-05-19 19:02:51 -07006817
6818 //
6819 // AudioPort implementation
6820 //
6821
Cole Faust7da659b2022-10-15 21:33:29 -07006822 private static final int AUDIOPORT_GENERATION_INIT = 0;
6823 private static Object sAudioPortGenerationLock = new Object();
6824 @GuardedBy("sAudioPortGenerationLock")
6825 private static int sAudioPortGeneration = AUDIOPORT_GENERATION_INIT;
6826 private static ArrayList<AudioPort> sAudioPortsCached = new ArrayList<AudioPort>();
6827 private static ArrayList<AudioPort> sPreviousAudioPortsCached = new ArrayList<AudioPort>();
6828 private static ArrayList<AudioPatch> sAudioPatchesCached = new ArrayList<AudioPatch>();
Eric Laurentb69681c2014-05-19 19:02:51 -07006829
Eric Laurentf076db42015-01-14 13:23:27 -08006830 static int resetAudioPortGeneration() {
Eric Laurentb69681c2014-05-19 19:02:51 -07006831 int generation;
Cole Faust7da659b2022-10-15 21:33:29 -07006832 synchronized (sAudioPortGenerationLock) {
Eric Laurentf076db42015-01-14 13:23:27 -08006833 generation = sAudioPortGeneration;
6834 sAudioPortGeneration = AUDIOPORT_GENERATION_INIT;
Eric Laurentb69681c2014-05-19 19:02:51 -07006835 }
6836 return generation;
6837 }
6838
Eric Laurent8a1e7a82015-05-08 11:43:05 -07006839 static int updateAudioPortCache(ArrayList<AudioPort> ports, ArrayList<AudioPatch> patches,
6840 ArrayList<AudioPort> previousPorts) {
Eric Laurentc573bc52015-06-26 10:01:12 -07006841 sAudioPortEventHandler.init();
Cole Faust7da659b2022-10-15 21:33:29 -07006842 synchronized (sAudioPortGenerationLock) {
Eric Laurentb69681c2014-05-19 19:02:51 -07006843
Eric Laurentf076db42015-01-14 13:23:27 -08006844 if (sAudioPortGeneration == AUDIOPORT_GENERATION_INIT) {
Eric Laurentb69681c2014-05-19 19:02:51 -07006845 int[] patchGeneration = new int[1];
6846 int[] portGeneration = new int[1];
6847 int status;
6848 ArrayList<AudioPort> newPorts = new ArrayList<AudioPort>();
6849 ArrayList<AudioPatch> newPatches = new ArrayList<AudioPatch>();
6850
6851 do {
6852 newPorts.clear();
6853 status = AudioSystem.listAudioPorts(newPorts, portGeneration);
Eric Laurentb69681c2014-05-19 19:02:51 -07006854 if (status != SUCCESS) {
Wonsik Kimb561cce2015-01-30 17:48:51 +09006855 Log.w(TAG, "updateAudioPortCache: listAudioPorts failed");
Eric Laurentb69681c2014-05-19 19:02:51 -07006856 return status;
6857 }
6858 newPatches.clear();
6859 status = AudioSystem.listAudioPatches(newPatches, patchGeneration);
Eric Laurentb69681c2014-05-19 19:02:51 -07006860 if (status != SUCCESS) {
Wonsik Kimb561cce2015-01-30 17:48:51 +09006861 Log.w(TAG, "updateAudioPortCache: listAudioPatches failed");
Eric Laurentb69681c2014-05-19 19:02:51 -07006862 return status;
6863 }
jiabinc4ecaa52017-09-26 14:28:41 -07006864 // Loop until patch generation is the same as port generation unless audio ports
6865 // and audio patches are not null.
6866 } while (patchGeneration[0] != portGeneration[0]
6867 && (ports == null || patches == null));
6868 // If the patch generation doesn't equal port generation, return ERROR here in case
6869 // of mismatch between audio ports and audio patches.
6870 if (patchGeneration[0] != portGeneration[0]) {
6871 return ERROR;
6872 }
Eric Laurentb69681c2014-05-19 19:02:51 -07006873
6874 for (int i = 0; i < newPatches.size(); i++) {
6875 for (int j = 0; j < newPatches.get(i).sources().length; j++) {
Eric Laurentb4e09092014-06-02 19:18:43 -07006876 AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sources()[j],
6877 newPorts);
Eric Laurentb69681c2014-05-19 19:02:51 -07006878 newPatches.get(i).sources()[j] = portCfg;
6879 }
6880 for (int j = 0; j < newPatches.get(i).sinks().length; j++) {
Eric Laurentb4e09092014-06-02 19:18:43 -07006881 AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sinks()[j],
6882 newPorts);
Eric Laurentb69681c2014-05-19 19:02:51 -07006883 newPatches.get(i).sinks()[j] = portCfg;
6884 }
6885 }
Wonsik Kimb561cce2015-01-30 17:48:51 +09006886 for (Iterator<AudioPatch> i = newPatches.iterator(); i.hasNext(); ) {
6887 AudioPatch newPatch = i.next();
6888 boolean hasInvalidPort = false;
6889 for (AudioPortConfig portCfg : newPatch.sources()) {
6890 if (portCfg == null) {
6891 hasInvalidPort = true;
6892 break;
6893 }
6894 }
6895 for (AudioPortConfig portCfg : newPatch.sinks()) {
6896 if (portCfg == null) {
6897 hasInvalidPort = true;
6898 break;
6899 }
6900 }
6901 if (hasInvalidPort) {
6902 // Temporarily remove patches with invalid ports. One who created the patch
6903 // is responsible for dealing with the port change.
6904 i.remove();
6905 }
6906 }
Eric Laurentb69681c2014-05-19 19:02:51 -07006907
Eric Laurent8a1e7a82015-05-08 11:43:05 -07006908 sPreviousAudioPortsCached = sAudioPortsCached;
Eric Laurentf076db42015-01-14 13:23:27 -08006909 sAudioPortsCached = newPorts;
6910 sAudioPatchesCached = newPatches;
6911 sAudioPortGeneration = portGeneration[0];
Eric Laurentb69681c2014-05-19 19:02:51 -07006912 }
6913 if (ports != null) {
6914 ports.clear();
Eric Laurentf076db42015-01-14 13:23:27 -08006915 ports.addAll(sAudioPortsCached);
Eric Laurentb69681c2014-05-19 19:02:51 -07006916 }
6917 if (patches != null) {
6918 patches.clear();
Eric Laurentf076db42015-01-14 13:23:27 -08006919 patches.addAll(sAudioPatchesCached);
Eric Laurentb69681c2014-05-19 19:02:51 -07006920 }
Eric Laurent8a1e7a82015-05-08 11:43:05 -07006921 if (previousPorts != null) {
6922 previousPorts.clear();
6923 previousPorts.addAll(sPreviousAudioPortsCached);
6924 }
Eric Laurentb69681c2014-05-19 19:02:51 -07006925 }
6926 return SUCCESS;
6927 }
6928
Eric Laurentf076db42015-01-14 13:23:27 -08006929 static AudioPortConfig updatePortConfig(AudioPortConfig portCfg, ArrayList<AudioPort> ports) {
Eric Laurentb69681c2014-05-19 19:02:51 -07006930 AudioPort port = portCfg.port();
6931 int k;
6932 for (k = 0; k < ports.size(); k++) {
6933 // compare handles because the port returned by JNI is not of the correct
6934 // subclass
6935 if (ports.get(k).handle().equals(port.handle())) {
Eric Laurentb69681c2014-05-19 19:02:51 -07006936 port = ports.get(k);
6937 break;
6938 }
6939 }
6940 if (k == ports.size()) {
Darwin Huangbb111732022-10-21 13:14:32 +00006941 // This can happen in case of stale audio patch referring to a removed device and is
6942 // handled by the caller.
Eric Laurentb69681c2014-05-19 19:02:51 -07006943 return null;
6944 }
6945 AudioGainConfig gainCfg = portCfg.gain();
6946 if (gainCfg != null) {
6947 AudioGain gain = port.gain(gainCfg.index());
6948 gainCfg = gain.buildConfig(gainCfg.mode(),
6949 gainCfg.channelMask(),
6950 gainCfg.values(),
6951 gainCfg.rampDurationMs());
6952 }
6953 return port.buildConfig(portCfg.samplingRate(),
6954 portCfg.channelMask(),
6955 portCfg.format(),
6956 gainCfg);
6957 }
Paul McLeane3383cc2015-05-08 11:41:20 -07006958
6959 private OnAmPortUpdateListener mPortListener = null;
6960
6961 /**
6962 * The message sent to apps when the contents of the device list changes if they provide
Paul Wang9b2d0482022-05-17 10:18:01 +00006963 * a {@link Handler} object to {@link registerAudioDeviceCallback}.
Paul McLeane3383cc2015-05-08 11:41:20 -07006964 */
Paul McLeancbeb8a22015-06-10 08:21:27 -07006965 private final static int MSG_DEVICES_CALLBACK_REGISTERED = 0;
6966 private final static int MSG_DEVICES_DEVICES_ADDED = 1;
6967 private final static int MSG_DEVICES_DEVICES_REMOVED = 2;
Paul McLeane3383cc2015-05-08 11:41:20 -07006968
Paul McLean8e6c9f42015-05-19 11:13:41 -07006969 /**
6970 * The list of {@link AudioDeviceCallback} objects to receive add/remove notifications.
6971 */
Jack He89f97982018-05-02 19:10:56 -07006972 private final ArrayMap<AudioDeviceCallback, NativeEventHandlerDelegate> mDeviceCallbacks =
Paul McLean03346882015-05-12 15:36:56 -07006973 new ArrayMap<AudioDeviceCallback, NativeEventHandlerDelegate>();
Paul McLeane3383cc2015-05-08 11:41:20 -07006974
6975 /**
Paul McLean8e6c9f42015-05-19 11:13:41 -07006976 * The following are flags to allow users of {@link AudioManager#getDevices(int)} to filter
6977 * the results list to only those device types they are interested in.
6978 */
6979 /**
Paul McLeane3383cc2015-05-08 11:41:20 -07006980 * Specifies to the {@link AudioManager#getDevices(int)} method to include
6981 * source (i.e. input) audio devices.
6982 */
6983 public static final int GET_DEVICES_INPUTS = 0x0001;
6984
6985 /**
6986 * Specifies to the {@link AudioManager#getDevices(int)} method to include
6987 * sink (i.e. output) audio devices.
6988 */
6989 public static final int GET_DEVICES_OUTPUTS = 0x0002;
6990
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07006991 /** @hide */
6992 @IntDef(flag = true, prefix = "GET_DEVICES", value = {
6993 GET_DEVICES_INPUTS,
6994 GET_DEVICES_OUTPUTS }
6995 )
6996 @Retention(RetentionPolicy.SOURCE)
6997 public @interface AudioDeviceRole {}
6998
Paul McLeane3383cc2015-05-08 11:41:20 -07006999 /**
7000 * Specifies to the {@link AudioManager#getDevices(int)} method to include both
7001 * source and sink devices.
7002 */
7003 public static final int GET_DEVICES_ALL = GET_DEVICES_OUTPUTS | GET_DEVICES_INPUTS;
7004
7005 /**
7006 * Determines if a given AudioDevicePort meets the specified filter criteria.
7007 * @param port The port to test.
7008 * @param flags A set of bitflags specifying the criteria to test.
7009 * @see {@link GET_DEVICES_OUTPUTS} and {@link GET_DEVICES_INPUTS}
7010 **/
7011 private static boolean checkFlags(AudioDevicePort port, int flags) {
7012 return port.role() == AudioPort.ROLE_SINK && (flags & GET_DEVICES_OUTPUTS) != 0 ||
7013 port.role() == AudioPort.ROLE_SOURCE && (flags & GET_DEVICES_INPUTS) != 0;
7014 }
7015
Paul McLean11354572015-08-07 12:50:48 -06007016 private static boolean checkTypes(AudioDevicePort port) {
7017 return AudioDeviceInfo.convertInternalDeviceToDeviceType(port.type()) !=
jiabin9697c6c2018-03-20 17:13:04 -07007018 AudioDeviceInfo.TYPE_UNKNOWN;
Paul McLean11354572015-08-07 12:50:48 -06007019 }
7020
Paul McLeane3383cc2015-05-08 11:41:20 -07007021 /**
Paul McLean8e6c9f42015-05-19 11:13:41 -07007022 * Returns an array of {@link AudioDeviceInfo} objects corresponding to the audio devices
7023 * currently connected to the system and meeting the criteria specified in the
7024 * <code>flags</code> parameter.
Paul McLeane3383cc2015-05-08 11:41:20 -07007025 * @param flags A set of bitflags specifying the criteria to test.
Elliot Waite54de77472017-01-11 15:30:35 -08007026 * @see #GET_DEVICES_OUTPUTS
7027 * @see #GET_DEVICES_INPUTS
7028 * @see #GET_DEVICES_ALL
Paul McLeane3383cc2015-05-08 11:41:20 -07007029 * @return A (possibly zero-length) array of AudioDeviceInfo objects.
7030 */
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07007031 public AudioDeviceInfo[] getDevices(@AudioDeviceRole int flags) {
Paul McLeane3383cc2015-05-08 11:41:20 -07007032 return getDevicesStatic(flags);
7033 }
7034
Paul McLean8e6c9f42015-05-19 11:13:41 -07007035 /**
7036 * Does the actual computation to generate an array of (externally-visible) AudioDeviceInfo
7037 * objects from the current (internal) AudioDevicePort list.
7038 */
Paul McLean03346882015-05-12 15:36:56 -07007039 private static AudioDeviceInfo[]
7040 infoListFromPortList(ArrayList<AudioDevicePort> ports, int flags) {
Paul McLeane3383cc2015-05-08 11:41:20 -07007041
Paul McLean8e6c9f42015-05-19 11:13:41 -07007042 // figure out how many AudioDeviceInfo we need space for...
Paul McLeane3383cc2015-05-08 11:41:20 -07007043 int numRecs = 0;
7044 for (AudioDevicePort port : ports) {
Paul McLean11354572015-08-07 12:50:48 -06007045 if (checkTypes(port) && checkFlags(port, flags)) {
Paul McLeane3383cc2015-05-08 11:41:20 -07007046 numRecs++;
7047 }
7048 }
7049
Paul McLean8e6c9f42015-05-19 11:13:41 -07007050 // Now load them up...
Paul McLeane3383cc2015-05-08 11:41:20 -07007051 AudioDeviceInfo[] deviceList = new AudioDeviceInfo[numRecs];
7052 int slot = 0;
7053 for (AudioDevicePort port : ports) {
Paul McLean11354572015-08-07 12:50:48 -06007054 if (checkTypes(port) && checkFlags(port, flags)) {
Paul McLeane3383cc2015-05-08 11:41:20 -07007055 deviceList[slot++] = new AudioDeviceInfo(port);
7056 }
7057 }
7058
7059 return deviceList;
7060 }
7061
Paul McLean03346882015-05-12 15:36:56 -07007062 /*
Paul McLean8e6c9f42015-05-19 11:13:41 -07007063 * Calculate the list of ports that are in ports_B, but not in ports_A. This is used by
7064 * the add/remove callback mechanism to provide a list of the newly added or removed devices
7065 * rather than the whole list and make the app figure it out.
7066 * Note that calling this method with:
7067 * ports_A == PREVIOUS_ports and ports_B == CURRENT_ports will calculated ADDED ports.
7068 * ports_A == CURRENT_ports and ports_B == PREVIOUS_ports will calculated REMOVED ports.
Paul McLean03346882015-05-12 15:36:56 -07007069 */
7070 private static AudioDeviceInfo[] calcListDeltas(
7071 ArrayList<AudioDevicePort> ports_A, ArrayList<AudioDevicePort> ports_B, int flags) {
7072
7073 ArrayList<AudioDevicePort> delta_ports = new ArrayList<AudioDevicePort>();
7074
7075 AudioDevicePort cur_port = null;
7076 for (int cur_index = 0; cur_index < ports_B.size(); cur_index++) {
7077 boolean cur_port_found = false;
7078 cur_port = ports_B.get(cur_index);
7079 for (int prev_index = 0;
7080 prev_index < ports_A.size() && !cur_port_found;
7081 prev_index++) {
7082 cur_port_found = (cur_port.id() == ports_A.get(prev_index).id());
7083 }
7084
7085 if (!cur_port_found) {
7086 delta_ports.add(cur_port);
7087 }
7088 }
7089
7090 return infoListFromPortList(delta_ports, flags);
7091 }
7092
Paul McLeane3383cc2015-05-08 11:41:20 -07007093 /**
Paul McLean03346882015-05-12 15:36:56 -07007094 * Generates a list of AudioDeviceInfo objects corresponding to the audio devices currently
7095 * connected to the system and meeting the criteria specified in the <code>flags</code>
7096 * parameter.
Paul McLean8e6c9f42015-05-19 11:13:41 -07007097 * This is an internal function. The public API front is getDevices(int).
Paul McLean03346882015-05-12 15:36:56 -07007098 * @param flags A set of bitflags specifying the criteria to test.
Elliot Waite54de77472017-01-11 15:30:35 -08007099 * @see #GET_DEVICES_OUTPUTS
7100 * @see #GET_DEVICES_INPUTS
7101 * @see #GET_DEVICES_ALL
Paul McLean03346882015-05-12 15:36:56 -07007102 * @return A (possibly zero-length) array of AudioDeviceInfo objects.
7103 * @hide
7104 */
7105 public static AudioDeviceInfo[] getDevicesStatic(int flags) {
7106 ArrayList<AudioDevicePort> ports = new ArrayList<AudioDevicePort>();
7107 int status = AudioManager.listAudioDevicePorts(ports);
7108 if (status != AudioManager.SUCCESS) {
7109 // fail and bail!
Paul McLean8e6c9f42015-05-19 11:13:41 -07007110 return new AudioDeviceInfo[0]; // Always return an array.
Paul McLean03346882015-05-12 15:36:56 -07007111 }
7112
7113 return infoListFromPortList(ports, flags);
7114 }
7115
7116 /**
Eric Laurentb36d4a12020-10-09 09:52:49 -07007117 * Returns an {@link AudioDeviceInfo} corresponding to the specified {@link AudioPort} ID.
7118 * @param portId The audio port ID to look up for.
7119 * @param flags A set of bitflags specifying the criteria to test.
7120 * @see #GET_DEVICES_OUTPUTS
7121 * @see #GET_DEVICES_INPUTS
7122 * @see #GET_DEVICES_ALL
7123 * @return An AudioDeviceInfo or null if no device with matching port ID is found.
7124 * @hide
7125 */
7126 public static AudioDeviceInfo getDeviceForPortId(int portId, int flags) {
7127 if (portId == 0) {
7128 return null;
7129 }
7130 AudioDeviceInfo[] devices = getDevicesStatic(flags);
7131 for (AudioDeviceInfo device : devices) {
7132 if (device.getId() == portId) {
7133 return device;
7134 }
7135 }
7136 return null;
7137 }
7138
7139 /**
Paul McLean8e6c9f42015-05-19 11:13:41 -07007140 * Registers an {@link AudioDeviceCallback} object to receive notifications of changes
Paul McLeane3383cc2015-05-08 11:41:20 -07007141 * to the set of connected audio devices.
Paul McLean8e6c9f42015-05-19 11:13:41 -07007142 * @param callback The {@link AudioDeviceCallback} object to receive connect/disconnect
7143 * notifications.
7144 * @param handler Specifies the {@link Handler} object for the thread on which to execute
7145 * the callback. If <code>null</code>, the {@link Handler} associated with the main
7146 * {@link Looper} will be used.
Paul McLeane3383cc2015-05-08 11:41:20 -07007147 */
Paul McLean03346882015-05-12 15:36:56 -07007148 public void registerAudioDeviceCallback(AudioDeviceCallback callback,
Jean-Michel Trivi1ec66182019-12-30 09:53:04 -08007149 @Nullable Handler handler) {
Eric Laurent1691f732015-07-14 16:27:54 -07007150 synchronized (mDeviceCallbacks) {
7151 if (callback != null && !mDeviceCallbacks.containsKey(callback)) {
Eric Laurentc573bc52015-06-26 10:01:12 -07007152 if (mDeviceCallbacks.size() == 0) {
7153 if (mPortListener == null) {
7154 mPortListener = new OnAmPortUpdateListener();
7155 }
7156 registerAudioPortUpdateListener(mPortListener);
7157 }
Paul McLeancbeb8a22015-06-10 08:21:27 -07007158 NativeEventHandlerDelegate delegate =
7159 new NativeEventHandlerDelegate(callback, handler);
7160 mDeviceCallbacks.put(callback, delegate);
jiabin8c3a7672018-05-22 15:44:21 -07007161 broadcastDeviceListChange_sync(delegate.getHandler());
Paul McLeane3383cc2015-05-08 11:41:20 -07007162 }
7163 }
7164 }
7165
7166 /**
Paul McLean8e6c9f42015-05-19 11:13:41 -07007167 * Unregisters an {@link AudioDeviceCallback} object which has been previously registered
Paul McLeane3383cc2015-05-08 11:41:20 -07007168 * to receive notifications of changes to the set of connected audio devices.
Paul McLean8e6c9f42015-05-19 11:13:41 -07007169 * @param callback The {@link AudioDeviceCallback} object that was previously registered
Elliot Waite54de77472017-01-11 15:30:35 -08007170 * with {@link AudioManager#registerAudioDeviceCallback} to be unregistered.
Paul McLeane3383cc2015-05-08 11:41:20 -07007171 */
Paul McLean03346882015-05-12 15:36:56 -07007172 public void unregisterAudioDeviceCallback(AudioDeviceCallback callback) {
7173 synchronized (mDeviceCallbacks) {
7174 if (mDeviceCallbacks.containsKey(callback)) {
7175 mDeviceCallbacks.remove(callback);
Eric Laurentc573bc52015-06-26 10:01:12 -07007176 if (mDeviceCallbacks.size() == 0) {
7177 unregisterAudioPortUpdateListener(mPortListener);
7178 }
Paul McLeane3383cc2015-05-08 11:41:20 -07007179 }
7180 }
7181 }
7182
jiabinc0f49442018-01-05 10:23:50 -08007183 /**
7184 * Set port id for microphones by matching device type and address.
7185 * @hide
7186 */
7187 public static void setPortIdForMicrophones(ArrayList<MicrophoneInfo> microphones) {
7188 AudioDeviceInfo[] devices = getDevicesStatic(AudioManager.GET_DEVICES_INPUTS);
7189 for (int i = microphones.size() - 1; i >= 0; i--) {
7190 boolean foundPortId = false;
7191 for (AudioDeviceInfo device : devices) {
7192 if (device.getPort().type() == microphones.get(i).getInternalDeviceType()
7193 && TextUtils.equals(device.getAddress(), microphones.get(i).getAddress())) {
7194 microphones.get(i).setId(device.getId());
7195 foundPortId = true;
7196 break;
7197 }
7198 }
7199 if (!foundPortId) {
7200 Log.i(TAG, "Failed to find port id for device with type:"
7201 + microphones.get(i).getType() + " address:"
7202 + microphones.get(i).getAddress());
7203 microphones.remove(i);
7204 }
7205 }
7206 }
7207
7208 /**
jiabin589a2362018-02-22 16:21:53 -08007209 * Convert {@link AudioDeviceInfo} to {@link MicrophoneInfo}.
7210 * @hide
7211 */
7212 public static MicrophoneInfo microphoneInfoFromAudioDeviceInfo(AudioDeviceInfo deviceInfo) {
7213 int deviceType = deviceInfo.getType();
7214 int micLocation = (deviceType == AudioDeviceInfo.TYPE_BUILTIN_MIC
7215 || deviceType == AudioDeviceInfo.TYPE_TELEPHONY) ? MicrophoneInfo.LOCATION_MAINBODY
7216 : deviceType == AudioDeviceInfo.TYPE_UNKNOWN ? MicrophoneInfo.LOCATION_UNKNOWN
7217 : MicrophoneInfo.LOCATION_PERIPHERAL;
7218 MicrophoneInfo microphone = new MicrophoneInfo(
7219 deviceInfo.getPort().name() + deviceInfo.getId(),
7220 deviceInfo.getPort().type(), deviceInfo.getAddress(), micLocation,
7221 MicrophoneInfo.GROUP_UNKNOWN, MicrophoneInfo.INDEX_IN_THE_GROUP_UNKNOWN,
7222 MicrophoneInfo.POSITION_UNKNOWN, MicrophoneInfo.ORIENTATION_UNKNOWN,
7223 new ArrayList<Pair<Float, Float>>(), new ArrayList<Pair<Integer, Integer>>(),
7224 MicrophoneInfo.SENSITIVITY_UNKNOWN, MicrophoneInfo.SPL_UNKNOWN,
7225 MicrophoneInfo.SPL_UNKNOWN, MicrophoneInfo.DIRECTIONALITY_UNKNOWN);
7226 microphone.setId(deviceInfo.getId());
7227 return microphone;
7228 }
7229
7230 /**
jiabind0be5b22018-04-10 14:10:04 -07007231 * Add {@link MicrophoneInfo} by device information while filtering certain types.
7232 */
7233 private void addMicrophonesFromAudioDeviceInfo(ArrayList<MicrophoneInfo> microphones,
7234 HashSet<Integer> filterTypes) {
7235 AudioDeviceInfo[] devices = getDevicesStatic(GET_DEVICES_INPUTS);
7236 for (AudioDeviceInfo device : devices) {
7237 if (filterTypes.contains(device.getType())) {
7238 continue;
7239 }
7240 MicrophoneInfo microphone = microphoneInfoFromAudioDeviceInfo(device);
7241 microphones.add(microphone);
7242 }
7243 }
7244
7245 /**
jiabinc0f49442018-01-05 10:23:50 -08007246 * Returns a list of {@link MicrophoneInfo} that corresponds to the characteristics
7247 * of all available microphones. The list is empty when no microphones are available
7248 * on the device. An error during the query will result in an IOException being thrown.
7249 *
7250 * @return a list that contains all microphones' characteristics
7251 * @throws IOException if an error occurs.
7252 */
7253 public List<MicrophoneInfo> getMicrophones() throws IOException {
7254 ArrayList<MicrophoneInfo> microphones = new ArrayList<MicrophoneInfo>();
7255 int status = AudioSystem.getMicrophones(microphones);
jiabind0be5b22018-04-10 14:10:04 -07007256 HashSet<Integer> filterTypes = new HashSet<>();
7257 filterTypes.add(AudioDeviceInfo.TYPE_TELEPHONY);
jiabinc0f49442018-01-05 10:23:50 -08007258 if (status != AudioManager.SUCCESS) {
jiabind0be5b22018-04-10 14:10:04 -07007259 // fail and populate microphones with unknown characteristics by device information.
jiabina26a7622018-04-11 15:38:46 -07007260 if (status != AudioManager.ERROR_INVALID_OPERATION) {
7261 Log.e(TAG, "getMicrophones failed:" + status);
7262 }
7263 Log.i(TAG, "fallback on device info");
jiabind0be5b22018-04-10 14:10:04 -07007264 addMicrophonesFromAudioDeviceInfo(microphones, filterTypes);
7265 return microphones;
jiabinc0f49442018-01-05 10:23:50 -08007266 }
7267 setPortIdForMicrophones(microphones);
jiabind0be5b22018-04-10 14:10:04 -07007268 filterTypes.add(AudioDeviceInfo.TYPE_BUILTIN_MIC);
7269 addMicrophonesFromAudioDeviceInfo(microphones, filterTypes);
jiabinc0f49442018-01-05 10:23:50 -08007270 return microphones;
7271 }
7272
Arun Mirpuricb102fa2019-01-11 18:39:21 -08007273 /**
7274 * Returns a list of audio formats that corresponds to encoding formats
William Escandea05cb452021-12-08 14:14:19 +01007275 * supported on offload path for A2DP playback.
Arun Mirpuricb102fa2019-01-11 18:39:21 -08007276 *
7277 * @return a list of {@link BluetoothCodecConfig} objects containing encoding formats
William Escandea05cb452021-12-08 14:14:19 +01007278 * supported for offload A2DP playback
Arun Mirpuricb102fa2019-01-11 18:39:21 -08007279 * @hide
7280 */
William Escandea05cb452021-12-08 14:14:19 +01007281 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
7282 public @NonNull List<BluetoothCodecConfig> getHwOffloadFormatsSupportedForA2dp() {
7283 ArrayList<Integer> formatsList = new ArrayList<>();
7284 ArrayList<BluetoothCodecConfig> codecConfigList = new ArrayList<>();
Arun Mirpuricb102fa2019-01-11 18:39:21 -08007285
William Escandea05cb452021-12-08 14:14:19 +01007286 int status = AudioSystem.getHwOffloadFormatsSupportedForBluetoothMedia(
7287 AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, formatsList);
Arun Mirpuricb102fa2019-01-11 18:39:21 -08007288 if (status != AudioManager.SUCCESS) {
William Escandea05cb452021-12-08 14:14:19 +01007289 Log.e(TAG, "getHwOffloadEncodingFormatsSupportedForA2DP failed:" + status);
7290 return codecConfigList;
Arun Mirpuricb102fa2019-01-11 18:39:21 -08007291 }
7292
William Escandea05cb452021-12-08 14:14:19 +01007293 for (Integer format : formatsList) {
7294 int btSourceCodec = AudioSystem.audioFormatToBluetoothSourceCodec(format);
7295 if (btSourceCodec != BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID) {
Etienne Ruffieux2c5180a2022-03-08 13:31:41 +00007296 codecConfigList.add(
7297 new BluetoothCodecConfig.Builder().setCodecType(btSourceCodec).build());
Arun Mirpuricb102fa2019-01-11 18:39:21 -08007298 }
William Escandea05cb452021-12-08 14:14:19 +01007299 }
7300 return codecConfigList;
7301 }
7302
7303 /**
7304 * Returns a list of audio formats that corresponds to encoding formats
7305 * supported on offload path for Le audio playback.
7306 *
7307 * @return a list of {@link BluetoothLeAudioCodecConfig} objects containing encoding formats
7308 * supported for offload Le Audio playback
7309 * @hide
7310 */
7311 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
7312 @NonNull
7313 public List<BluetoothLeAudioCodecConfig> getHwOffloadFormatsSupportedForLeAudio() {
7314 ArrayList<Integer> formatsList = new ArrayList<>();
7315 ArrayList<BluetoothLeAudioCodecConfig> leAudioCodecConfigList = new ArrayList<>();
7316
7317 int status = AudioSystem.getHwOffloadFormatsSupportedForBluetoothMedia(
7318 AudioSystem.DEVICE_OUT_BLE_HEADSET, formatsList);
7319 if (status != AudioManager.SUCCESS) {
7320 Log.e(TAG, "getHwOffloadEncodingFormatsSupportedForLeAudio failed:" + status);
Patty46694212021-11-04 21:03:32 +08007321 return leAudioCodecConfigList;
Arun Mirpuricb102fa2019-01-11 18:39:21 -08007322 }
William Escandea05cb452021-12-08 14:14:19 +01007323
7324 for (Integer format : formatsList) {
7325 int btLeAudioCodec = AudioSystem.audioFormatToBluetoothLeAudioSourceCodec(format);
7326 if (btLeAudioCodec != BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID) {
7327 leAudioCodecConfigList.add(new BluetoothLeAudioCodecConfig.Builder()
7328 .setCodecType(btLeAudioCodec)
7329 .build());
7330 }
7331 }
7332 return leAudioCodecConfigList;
Arun Mirpuricb102fa2019-01-11 18:39:21 -08007333 }
7334
Paul McLeancbeb8a22015-06-10 08:21:27 -07007335 // Since we need to calculate the changes since THE LAST NOTIFICATION, and not since the
7336 // (unpredictable) last time updateAudioPortCache() was called by someone, keep a list
7337 // of the ports that exist at the time of the last notification.
7338 private ArrayList<AudioDevicePort> mPreviousPorts = new ArrayList<AudioDevicePort>();
7339
Paul McLeane3383cc2015-05-08 11:41:20 -07007340 /**
Paul McLean8e6c9f42015-05-19 11:13:41 -07007341 * Internal method to compute and generate add/remove messages and then send to any
jiabin8c3a7672018-05-22 15:44:21 -07007342 * registered callbacks. Must be called synchronized on mDeviceCallbacks.
Paul McLeane3383cc2015-05-08 11:41:20 -07007343 */
jiabin8c3a7672018-05-22 15:44:21 -07007344 private void broadcastDeviceListChange_sync(Handler handler) {
Paul McLean03346882015-05-12 15:36:56 -07007345 int status;
7346
Paul McLeancbeb8a22015-06-10 08:21:27 -07007347 // Get the new current set of ports
Paul McLean03346882015-05-12 15:36:56 -07007348 ArrayList<AudioDevicePort> current_ports = new ArrayList<AudioDevicePort>();
7349 status = AudioManager.listAudioDevicePorts(current_ports);
7350 if (status != AudioManager.SUCCESS) {
7351 return;
7352 }
7353
Paul McLeancbeb8a22015-06-10 08:21:27 -07007354 if (handler != null) {
7355 // This is the callback for the registration, so send the current list
7356 AudioDeviceInfo[] deviceList =
7357 infoListFromPortList(current_ports, GET_DEVICES_ALL);
7358 handler.sendMessage(
7359 Message.obtain(handler, MSG_DEVICES_CALLBACK_REGISTERED, deviceList));
7360 } else {
7361 AudioDeviceInfo[] added_devices =
7362 calcListDeltas(mPreviousPorts, current_ports, GET_DEVICES_ALL);
7363 AudioDeviceInfo[] removed_devices =
7364 calcListDeltas(current_ports, mPreviousPorts, GET_DEVICES_ALL);
Paul McLeancbeb8a22015-06-10 08:21:27 -07007365 if (added_devices.length != 0 || removed_devices.length != 0) {
jiabin8c3a7672018-05-22 15:44:21 -07007366 for (int i = 0; i < mDeviceCallbacks.size(); i++) {
7367 handler = mDeviceCallbacks.valueAt(i).getHandler();
7368 if (handler != null) {
7369 if (removed_devices.length != 0) {
7370 handler.sendMessage(Message.obtain(handler,
7371 MSG_DEVICES_DEVICES_REMOVED,
7372 removed_devices));
7373 }
7374 if (added_devices.length != 0) {
7375 handler.sendMessage(Message.obtain(handler,
7376 MSG_DEVICES_DEVICES_ADDED,
7377 added_devices));
Paul McLeancbeb8a22015-06-10 08:21:27 -07007378 }
Paul McLean03346882015-05-12 15:36:56 -07007379 }
7380 }
Paul McLeane3383cc2015-05-08 11:41:20 -07007381 }
7382 }
Paul McLeancbeb8a22015-06-10 08:21:27 -07007383
7384 mPreviousPorts = current_ports;
Paul McLeane3383cc2015-05-08 11:41:20 -07007385 }
7386
7387 /**
7388 * Handles Port list update notifications from the AudioManager
7389 */
7390 private class OnAmPortUpdateListener implements AudioManager.OnAudioPortUpdateListener {
7391 static final String TAG = "OnAmPortUpdateListener";
7392 public void onAudioPortListUpdate(AudioPort[] portList) {
jiabin8c3a7672018-05-22 15:44:21 -07007393 synchronized (mDeviceCallbacks) {
7394 broadcastDeviceListChange_sync(null);
7395 }
Paul McLeane3383cc2015-05-08 11:41:20 -07007396 }
7397
7398 /**
7399 * Callback method called upon audio patch list update.
Paul McLean8e6c9f42015-05-19 11:13:41 -07007400 * Note: We don't do anything with Patches at this time, so ignore this notification.
7401 * @param patchList the updated list of audio patches.
Paul McLeane3383cc2015-05-08 11:41:20 -07007402 */
7403 public void onAudioPatchListUpdate(AudioPatch[] patchList) {}
7404
7405 /**
7406 * Callback method called when the mediaserver dies
7407 */
7408 public void onServiceDied() {
jiabin8c3a7672018-05-22 15:44:21 -07007409 synchronized (mDeviceCallbacks) {
7410 broadcastDeviceListChange_sync(null);
7411 }
Paul McLeane3383cc2015-05-08 11:41:20 -07007412 }
7413 }
7414
Eric Laurent1d3cdce2018-01-20 10:31:21 -08007415
7416 /**
7417 * @hide
7418 * Abstract class to receive event notification about audioserver process state.
7419 */
7420 @SystemApi
7421 public abstract static class AudioServerStateCallback {
7422 public void onAudioServerDown() { }
7423 public void onAudioServerUp() { }
7424 }
7425
7426 private Executor mAudioServerStateExec;
7427 private AudioServerStateCallback mAudioServerStateCb;
7428 private final Object mAudioServerStateCbLock = new Object();
7429
7430 private final IAudioServerStateDispatcher mAudioServerStateDispatcher =
7431 new IAudioServerStateDispatcher.Stub() {
7432 @Override
7433 public void dispatchAudioServerStateChange(boolean state) {
7434 Executor exec;
7435 AudioServerStateCallback cb;
7436
7437 synchronized (mAudioServerStateCbLock) {
7438 exec = mAudioServerStateExec;
7439 cb = mAudioServerStateCb;
7440 }
7441
7442 if ((exec == null) || (cb == null)) {
7443 return;
7444 }
7445 if (state) {
7446 exec.execute(() -> cb.onAudioServerUp());
7447 } else {
7448 exec.execute(() -> cb.onAudioServerDown());
7449 }
7450 }
7451 };
7452
7453 /**
7454 * @hide
7455 * Registers a callback for notification of audio server state changes.
7456 * @param executor {@link Executor} to handle the callbacks
7457 * @param stateCallback the callback to receive the audio server state changes
7458 * To remove the callabck, pass a null reference for both executor and stateCallback.
7459 */
7460 @SystemApi
7461 public void setAudioServerStateCallback(@NonNull Executor executor,
7462 @NonNull AudioServerStateCallback stateCallback) {
7463 if (stateCallback == null) {
7464 throw new IllegalArgumentException("Illegal null AudioServerStateCallback");
7465 }
7466 if (executor == null) {
7467 throw new IllegalArgumentException(
7468 "Illegal null Executor for the AudioServerStateCallback");
7469 }
7470
7471 synchronized (mAudioServerStateCbLock) {
7472 if (mAudioServerStateCb != null) {
7473 throw new IllegalStateException(
7474 "setAudioServerStateCallback called with already registered callabck");
7475 }
7476 final IAudioService service = getService();
7477 try {
7478 service.registerAudioServerStateDispatcher(mAudioServerStateDispatcher);
7479 } catch (RemoteException e) {
7480 throw e.rethrowFromSystemServer();
7481 }
7482 mAudioServerStateExec = executor;
7483 mAudioServerStateCb = stateCallback;
7484 }
7485 }
7486
7487 /**
7488 * @hide
7489 * Unregisters the callback for notification of audio server state changes.
7490 */
7491 @SystemApi
7492 public void clearAudioServerStateCallback() {
7493 synchronized (mAudioServerStateCbLock) {
7494 if (mAudioServerStateCb != null) {
7495 final IAudioService service = getService();
7496 try {
7497 service.unregisterAudioServerStateDispatcher(
7498 mAudioServerStateDispatcher);
7499 } catch (RemoteException e) {
7500 throw e.rethrowFromSystemServer();
7501 }
7502 }
7503 mAudioServerStateExec = null;
7504 mAudioServerStateCb = null;
7505 }
7506 }
7507
7508 /**
7509 * @hide
7510 * Checks if native audioservice is running or not.
7511 * @return true if native audioservice runs, false otherwise.
7512 */
7513 @SystemApi
7514 public boolean isAudioServerRunning() {
7515 final IAudioService service = getService();
7516 try {
7517 return service.isAudioServerRunning();
7518 } catch (RemoteException e) {
7519 throw e.rethrowFromSystemServer();
7520 }
7521 }
7522
jiabin39940752018-04-02 18:18:45 -07007523 /**
Kriti Dang527e66c2021-03-04 10:37:22 +01007524 * Sets the surround sound mode.
7525 *
7526 * @return true if successful, otherwise false
7527 */
7528 @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS)
7529 public boolean setEncodedSurroundMode(@EncodedSurroundOutputMode int mode) {
7530 try {
7531 return getService().setEncodedSurroundMode(mode);
7532 } catch (RemoteException e) {
7533 throw e.rethrowFromSystemServer();
7534 }
7535 }
7536
7537 /**
7538 * Gets the surround sound mode.
7539 *
7540 * @return true if successful, otherwise false
7541 */
Kriti Dang527e66c2021-03-04 10:37:22 +01007542 public @EncodedSurroundOutputMode int getEncodedSurroundMode() {
7543 try {
Kriti Dang98fdb262021-04-01 13:26:00 +02007544 return getService().getEncodedSurroundMode(
7545 getContext().getApplicationInfo().targetSdkVersion);
Kriti Dang527e66c2021-03-04 10:37:22 +01007546 } catch (RemoteException e) {
7547 throw e.rethrowFromSystemServer();
7548 }
7549 }
7550
7551 /**
jiabin39940752018-04-02 18:18:45 -07007552 * @hide
7553 * Returns all surround formats.
7554 * @return a map where the key is a surround format and
7555 * the value indicates the surround format is enabled or not
7556 */
Marin Shalamanov49e778e2021-06-02 14:12:41 +02007557 @TestApi
7558 @NonNull
jiabin39940752018-04-02 18:18:45 -07007559 public Map<Integer, Boolean> getSurroundFormats() {
Kriti Dang1380c0e2021-06-04 14:51:48 +02007560 try {
7561 return getService().getSurroundFormats();
7562 } catch (RemoteException e) {
7563 throw e.rethrowFromSystemServer();
jiabin39940752018-04-02 18:18:45 -07007564 }
jiabin39940752018-04-02 18:18:45 -07007565 }
7566
7567 /**
Kriti Dang3f296bd2021-05-31 15:54:44 +02007568 * Sets and persists a certain surround format as enabled or not.
7569 * <p>
7570 * This API is called by TvSettings surround sound menu when user enables or disables a
7571 * surround sound format. This setting is persisted as global user setting.
7572 * Applications should revert their changes to surround sound settings unless they intend to
7573 * modify the global user settings across all apps. The framework does not auto-revert an
7574 * application's settings after a lifecycle event. Audio focus is not required to apply these
7575 * settings.
Kriti Dang1985c452021-05-10 17:06:44 +02007576 *
jiabin39940752018-04-02 18:18:45 -07007577 * @param enabled the required surround format state, true for enabled, false for disabled
7578 * @return true if successful, otherwise false
7579 */
Kriti Dang527e66c2021-03-04 10:37:22 +01007580 @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS)
jiabin39940752018-04-02 18:18:45 -07007581 public boolean setSurroundFormatEnabled(
7582 @AudioFormat.SurroundSoundEncoding int audioFormat, boolean enabled) {
Kriti Dang527e66c2021-03-04 10:37:22 +01007583 try {
7584 return getService().setSurroundFormatEnabled(audioFormat, enabled);
7585 } catch (RemoteException e) {
7586 throw e.rethrowFromSystemServer();
7587 }
7588 }
7589
7590 /**
7591 * Gets whether a certain surround format is enabled or not.
7592 * @param audioFormat a surround format
7593 *
7594 * @return whether the required surround format is enabled
7595 */
Kriti Dang527e66c2021-03-04 10:37:22 +01007596 public boolean isSurroundFormatEnabled(@AudioFormat.SurroundSoundEncoding int audioFormat) {
7597 try {
7598 return getService().isSurroundFormatEnabled(audioFormat);
7599 } catch (RemoteException e) {
7600 throw e.rethrowFromSystemServer();
7601 }
jiabin39940752018-04-02 18:18:45 -07007602 }
7603
7604 /**
7605 * @hide
7606 * Returns all surround formats that are reported by the connected HDMI device.
Kriti Dang01924232021-03-02 13:51:09 +01007607 * The return values are not affected by calling setSurroundFormatEnabled.
jiabin39940752018-04-02 18:18:45 -07007608 *
Kriti Dang01924232021-03-02 13:51:09 +01007609 * @return a list of surround formats
jiabin39940752018-04-02 18:18:45 -07007610 */
Kriti Dang1380c0e2021-06-04 14:51:48 +02007611 @TestApi
7612 @NonNull
7613 public List<Integer> getReportedSurroundFormats() {
7614 try {
7615 return getService().getReportedSurroundFormats();
7616 } catch (RemoteException e) {
7617 throw e.rethrowFromSystemServer();
jiabin39940752018-04-02 18:18:45 -07007618 }
jiabin39940752018-04-02 18:18:45 -07007619 }
7620
jiabin66f9e722018-11-02 16:20:19 -07007621 /**
7622 * Return if audio haptic coupled playback is supported or not.
7623 *
7624 * @return whether audio haptic playback supported.
7625 */
7626 public static boolean isHapticPlaybackSupported() {
7627 return AudioSystem.isHapticPlaybackSupported();
7628 }
7629
François Gaffie0699fec2018-07-09 14:35:10 +02007630 /**
7631 * @hide
Carter Hsu2065d1e2022-01-19 19:54:50 +08007632 * Indicates whether a platform supports the Ultrasound feature which covers the playback
7633 * and recording of 20kHz~ sounds. If platform supports Ultrasound, then the
7634 * usage will be
7635 * To start the Ultrasound playback:
7636 * - Create an AudioTrack with {@link AudioAttributes.CONTENT_TYPE_ULTRASOUND}.
7637 * To start the Ultrasound capture:
7638 * - Create an AudioRecord with {@link MediaRecorder.AudioSource.ULTRASOUND}.
7639 *
7640 * @return whether the ultrasound feature is supported, true when platform supports both
7641 * Ultrasound playback and capture, false otherwise.
7642 */
7643 @SystemApi
Carter Hsu3ea30de42022-02-15 15:59:00 +08007644 @RequiresPermission(android.Manifest.permission.ACCESS_ULTRASOUND)
7645 public boolean isUltrasoundSupported() {
7646 try {
7647 return getService().isUltrasoundSupported();
7648 } catch (RemoteException e) {
7649 throw e.rethrowFromSystemServer();
7650 }
Carter Hsu2065d1e2022-01-19 19:54:50 +08007651 }
7652
7653 /**
7654 * @hide
Atneya Naireeab0a72022-12-15 09:32:51 -08007655 * Indicates whether the platform supports capturing content from the hotword recognition
7656 * pipeline. To capture content of this type, create an AudioRecord with
7657 * {@link AudioRecord.Builder.setRequestHotwordStream(boolean, boolean)}.
7658 * @param lookbackAudio Query if the hotword stream additionally supports providing buffered
7659 * audio prior to stream open.
7660 * @return True if the platform supports capturing hotword content, and if lookbackAudio
7661 * is true, if it additionally supports capturing buffered hotword content prior to stream
7662 * open. False otherwise.
7663 */
7664 @SystemApi
7665 @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD)
7666 public boolean isHotwordStreamSupported(boolean lookbackAudio) {
7667 try {
7668 return getService().isHotwordStreamSupported(lookbackAudio);
7669 } catch (RemoteException e) {
7670 return false;
7671 }
7672 }
7673
7674 /**
7675 * @hide
François Gaffie0699fec2018-07-09 14:35:10 +02007676 * Introspection API to retrieve audio product strategies.
7677 * When implementing {Car|Oem}AudioManager, use this method to retrieve the collection of
7678 * audio product strategies, which is indexed by a weakly typed index in order to be extended
7679 * by OEM without any needs of AOSP patches.
7680 * The {Car|Oem}AudioManager can expose API to build {@link AudioAttributes} for a given product
7681 * strategy refered either by its index or human readable string. It will allow clients
7682 * application to start streaming data using these {@link AudioAttributes} on the selected
7683 * device by Audio Policy Engine.
7684 * @return a (possibly zero-length) array of
7685 * {@see android.media.audiopolicy.AudioProductStrategy} objects.
7686 */
7687 @SystemApi
Hayden Gomes6d69bde2019-04-04 13:10:13 -07007688 @NonNull
François Gaffie0699fec2018-07-09 14:35:10 +02007689 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
Hayden Gomes6d69bde2019-04-04 13:10:13 -07007690 public static List<AudioProductStrategy> getAudioProductStrategies() {
François Gaffie0699fec2018-07-09 14:35:10 +02007691 final IAudioService service = getService();
7692 try {
7693 return service.getAudioProductStrategies();
7694 } catch (RemoteException e) {
7695 throw e.rethrowFromSystemServer();
7696 }
7697 }
7698
François Gaffieadcd00a2018-09-18 17:06:26 +02007699 /**
7700 * @hide
7701 * Introspection API to retrieve audio volume groups.
7702 * When implementing {Car|Oem}AudioManager, use this method to retrieve the collection of
7703 * audio volume groups.
Hayden Gomesebd6aaa2019-04-04 13:14:21 -07007704 * @return a (possibly zero-length) List of
7705 * {@see android.media.audiopolicy.AudioVolumeGroup} objects.
François Gaffieadcd00a2018-09-18 17:06:26 +02007706 */
7707 @SystemApi
Hayden Gomesebd6aaa2019-04-04 13:14:21 -07007708 @NonNull
François Gaffieadcd00a2018-09-18 17:06:26 +02007709 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
Hayden Gomesebd6aaa2019-04-04 13:14:21 -07007710 public static List<AudioVolumeGroup> getAudioVolumeGroups() {
François Gaffie9c362102018-09-21 17:43:52 +02007711 final IAudioService service = getService();
7712 try {
Hayden Gomesebd6aaa2019-04-04 13:14:21 -07007713 return service.getAudioVolumeGroups();
François Gaffie9c362102018-09-21 17:43:52 +02007714 } catch (RemoteException e) {
7715 throw e.rethrowFromSystemServer();
7716 }
François Gaffieadcd00a2018-09-18 17:06:26 +02007717 }
7718
7719 /**
7720 * @hide
7721 * Callback registered by client to be notified upon volume group change.
7722 */
7723 @SystemApi
7724 public abstract static class VolumeGroupCallback {
7725 /**
7726 * Callback method called upon audio volume group change.
7727 * @param group the group for which the volume has changed
7728 */
7729 public void onAudioVolumeGroupChanged(int group, int flags) {}
7730 }
7731
7732 /**
7733 * @hide
7734 * Register an audio volume group change listener.
7735 * @param callback the {@link VolumeGroupCallback} to register
7736 */
7737 @SystemApi
7738 public void registerVolumeGroupCallback(
7739 @NonNull Executor executor,
7740 @NonNull VolumeGroupCallback callback) {
7741 Preconditions.checkNotNull(executor, "executor must not be null");
7742 Preconditions.checkNotNull(callback, "volume group change cb must not be null");
7743 sAudioAudioVolumeGroupChangedHandler.init();
7744 // TODO: make use of executor
7745 sAudioAudioVolumeGroupChangedHandler.registerListener(callback);
7746 }
7747
7748 /**
7749 * @hide
7750 * Unregister an audio volume group change listener.
7751 * @param callback the {@link VolumeGroupCallback} to unregister
7752 */
7753 @SystemApi
7754 public void unregisterVolumeGroupCallback(
7755 @NonNull VolumeGroupCallback callback) {
7756 Preconditions.checkNotNull(callback, "volume group change cb must not be null");
7757 sAudioAudioVolumeGroupChangedHandler.unregisterListener(callback);
7758 }
jiabin39940752018-04-02 18:18:45 -07007759
jiabinad225202019-03-20 15:22:50 -07007760 /**
7761 * Return if an asset contains haptic channels or not.
jiabincfcf1032021-07-01 16:30:50 -07007762 *
7763 * @param context the {@link Context} to resolve the uri.
jiabinad225202019-03-20 15:22:50 -07007764 * @param uri the {@link Uri} of the asset.
7765 * @return true if the assert contains haptic channels.
7766 * @hide
7767 */
jiabincfcf1032021-07-01 16:30:50 -07007768 public static boolean hasHapticChannelsImpl(@NonNull Context context, @NonNull Uri uri) {
7769 MediaExtractor extractor = new MediaExtractor();
jiabinad225202019-03-20 15:22:50 -07007770 try {
jiabincfcf1032021-07-01 16:30:50 -07007771 extractor.setDataSource(context, uri, null);
7772 for (int i = 0; i < extractor.getTrackCount(); i++) {
7773 MediaFormat format = extractor.getTrackFormat(i);
7774 if (format.containsKey(MediaFormat.KEY_HAPTIC_CHANNEL_COUNT)
7775 && format.getInteger(MediaFormat.KEY_HAPTIC_CHANNEL_COUNT) > 0) {
7776 return true;
7777 }
7778 }
7779 } catch (IOException e) {
7780 Log.e(TAG, "hasHapticChannels failure:" + e);
7781 }
7782 return false;
7783 }
7784
7785 /**
7786 * Return if an asset contains haptic channels or not.
7787 *
7788 * @param context the {@link Context} to resolve the uri.
7789 * @param uri the {@link Uri} of the asset.
7790 * @return true if the assert contains haptic channels.
7791 * @hide
7792 */
7793 public static boolean hasHapticChannels(@Nullable Context context, @NonNull Uri uri) {
7794 Objects.requireNonNull(uri);
jiabin0f3339c2021-07-09 11:50:07 -07007795
jiabincfcf1032021-07-01 16:30:50 -07007796 if (context != null) {
7797 return hasHapticChannelsImpl(context, uri);
jiabin0f3339c2021-07-09 11:50:07 -07007798 }
7799
7800 Context cachedContext = sContext.get();
7801 if (cachedContext != null) {
jiabincfcf1032021-07-01 16:30:50 -07007802 if (DEBUG) {
7803 Log.d(TAG, "Try to use static context to query if having haptic channels");
7804 }
jiabin0f3339c2021-07-09 11:50:07 -07007805 return hasHapticChannelsImpl(cachedContext, uri);
7806 }
7807
7808 // Try with audio service context, this may fail to get correct result.
7809 if (DEBUG) {
7810 Log.d(TAG, "Try to use audio service context to query if having haptic channels");
7811 }
7812 try {
7813 return getService().hasHapticChannels(uri);
7814 } catch (RemoteException e) {
7815 throw e.rethrowFromSystemServer();
jiabinad225202019-03-20 15:22:50 -07007816 }
7817 }
7818
Kohsuke Yatoh900e1f12020-03-25 08:05:49 -07007819 /**
7820 * Set whether or not there is an active RTT call.
7821 * This method should be called by Telecom service.
7822 * @hide
7823 * TODO: make this a @SystemApi
7824 */
7825 public static void setRttEnabled(boolean rttEnabled) {
7826 try {
7827 getService().setRttEnabled(rttEnabled);
7828 } catch (RemoteException e) {
7829 throw e.rethrowFromSystemServer();
7830 }
7831 }
7832
Jin Seok Park16aeba382020-08-06 12:52:54 +09007833 /**
7834 * Adjusts the volume of the most relevant stream, or the given fallback
7835 * stream.
7836 * <p>
7837 * This method should only be used by applications that replace the
7838 * platform-wide management of audio settings or the main telephony
7839 * application.
7840 * <p>
7841 * This method has no effect if the device implements a fixed volume policy
7842 * as indicated by {@link #isVolumeFixed()}.
7843 * <p>This API checks if the caller has the necessary permissions based on the provided
7844 * component name, uid, and pid values.
7845 * See {@link #adjustSuggestedStreamVolume(int, int, int)}.
7846 *
7847 * @param suggestedStreamType The stream type that will be used if there
7848 * isn't a relevant stream. {@link #USE_DEFAULT_STREAM_TYPE} is
7849 * valid here.
7850 * @param direction The direction to adjust the volume. One of
7851 * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE},
7852 * {@link #ADJUST_SAME}, {@link #ADJUST_MUTE},
7853 * {@link #ADJUST_UNMUTE}, or {@link #ADJUST_TOGGLE_MUTE}.
7854 * @param flags One or more flags.
7855 * @param packageName the package name of client application
7856 * @param uid the uid of client application
7857 * @param pid the pid of client application
7858 * @param targetSdkVersion the target sdk version of client application
7859 * @see #adjustVolume(int, int)
7860 * @see #adjustStreamVolume(int, int, int)
7861 * @see #setStreamVolume(int, int, int)
7862 * @see #isVolumeFixed()
7863 *
7864 * @hide
7865 */
7866 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
7867 public void adjustSuggestedStreamVolumeForUid(int suggestedStreamType, int direction, int flags,
7868 @NonNull String packageName, int uid, int pid, int targetSdkVersion) {
7869 try {
7870 getService().adjustSuggestedStreamVolumeForUid(suggestedStreamType, direction, flags,
7871 packageName, uid, pid, UserHandle.getUserHandleForUid(uid), targetSdkVersion);
7872 } catch (RemoteException e) {
7873 throw e.rethrowFromSystemServer();
7874 }
7875 }
7876
7877 /**
7878 * Adjusts the volume of a particular stream by one step in a direction.
7879 * <p>
7880 * This method should only be used by applications that replace the platform-wide
7881 * management of audio settings or the main telephony application.
7882 * <p>This method has no effect if the device implements a fixed volume policy
7883 * as indicated by {@link #isVolumeFixed()}.
7884 * <p>From N onward, ringer mode adjustments that would toggle Do Not Disturb are not allowed
7885 * unless the app has been granted Do Not Disturb Access.
7886 * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
7887 * <p>This API checks if the caller has the necessary permissions based on the provided
7888 * component name, uid, and pid values.
7889 * See {@link #adjustStreamVolume(int, int, int)}.
7890 *
7891 * @param streamType The stream type to adjust. One of {@link #STREAM_VOICE_CALL},
7892 * {@link #STREAM_SYSTEM}, {@link #STREAM_RING}, {@link #STREAM_MUSIC},
7893 * {@link #STREAM_ALARM} or {@link #STREAM_ACCESSIBILITY}.
7894 * @param direction The direction to adjust the volume. One of
7895 * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
7896 * {@link #ADJUST_SAME}.
7897 * @param flags One or more flags.
7898 * @param packageName the package name of client application
7899 * @param uid the uid of client application
7900 * @param pid the pid of client application
7901 * @param targetSdkVersion the target sdk version of client application
7902 * @see #adjustVolume(int, int)
7903 * @see #setStreamVolume(int, int, int)
7904 * @throws SecurityException if the adjustment triggers a Do Not Disturb change
7905 * and the caller is not granted notification policy access.
7906 *
7907 * @hide
7908 */
7909 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
7910 public void adjustStreamVolumeForUid(int streamType, int direction, int flags,
7911 @NonNull String packageName, int uid, int pid, int targetSdkVersion) {
7912 try {
7913 getService().adjustStreamVolumeForUid(streamType, direction, flags, packageName, uid,
7914 pid, UserHandle.getUserHandleForUid(uid), targetSdkVersion);
7915 } catch (RemoteException e) {
7916 throw e.rethrowFromSystemServer();
7917 }
7918 }
7919
7920 /**
7921 * Sets the volume index for a particular stream.
7922 * <p>This method has no effect if the device implements a fixed volume policy
7923 * as indicated by {@link #isVolumeFixed()}.
7924 * <p>From N onward, volume adjustments that would toggle Do Not Disturb are not allowed unless
7925 * the app has been granted Do Not Disturb Access.
7926 * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
7927 * <p>This API checks if the caller has the necessary permissions based on the provided
7928 * component name, uid, and pid values.
7929 * See {@link #setStreamVolume(int, int, int)}.
7930 *
7931 * @param streamType The stream whose volume index should be set.
7932 * @param index The volume index to set. See
7933 * {@link #getStreamMaxVolume(int)} for the largest valid value.
7934 * @param flags One or more flags.
7935 * @param packageName the package name of client application
7936 * @param uid the uid of client application
7937 * @param pid the pid of client application
7938 * @param targetSdkVersion the target sdk version of client application
7939 * @see #getStreamMaxVolume(int)
7940 * @see #getStreamVolume(int)
7941 * @see #isVolumeFixed()
7942 * @throws SecurityException if the volume change triggers a Do Not Disturb change
7943 * and the caller is not granted notification policy access.
7944 *
7945 * @hide
7946 */
7947 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
7948 public void setStreamVolumeForUid(int streamType, int index, int flags,
7949 @NonNull String packageName, int uid, int pid, int targetSdkVersion) {
7950 try {
7951 getService().setStreamVolumeForUid(streamType, index, flags, packageName, uid, pid,
7952 UserHandle.getUserHandleForUid(uid), targetSdkVersion);
7953 } catch (RemoteException e) {
7954 throw e.rethrowFromSystemServer();
7955 }
7956 }
7957
7958
hjin81.lee4e984e52019-12-05 14:34:52 +09007959 /** @hide
7960 * TODO: make this a @SystemApi */
7961 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
7962 public void setMultiAudioFocusEnabled(boolean enabled) {
7963 try {
7964 getService().setMultiAudioFocusEnabled(enabled);
7965 } catch (RemoteException e) {
7966 throw e.rethrowFromSystemServer();
7967 }
7968 }
7969
Eric Laurent43a78de2020-07-24 17:11:15 -07007970
7971 /**
7972 * Retrieves the Hardware A/V synchronization ID corresponding to the given audio session ID.
7973 * For more details on Hardware A/V synchronization please refer to
7974 * <a href="https://source.android.com/devices/tv/multimedia-tunneling/">
7975 * media tunneling documentation</a>.
7976 * @param sessionId the audio session ID for which the HW A/V sync ID is retrieved.
7977 * @return the HW A/V sync ID for this audio session (an integer different from 0).
7978 * @throws UnsupportedOperationException if HW A/V synchronization is not supported.
7979 */
7980 public int getAudioHwSyncForSession(int sessionId) {
7981 int hwSyncId = AudioSystem.getAudioHwSyncForSession(sessionId);
7982 if (hwSyncId == AudioSystem.AUDIO_HW_SYNC_INVALID) {
7983 throw new UnsupportedOperationException("HW A/V synchronization is not supported.");
7984 }
7985 return hwSyncId;
7986 }
7987
Eric Laurentb36d4a12020-10-09 09:52:49 -07007988 /**
7989 * Selects the audio device that should be used for communication use cases, for instance voice
7990 * or video calls. This method can be used by voice or video chat applications to select a
7991 * different audio device than the one selected by default by the platform.
Eric Laurent7412f572021-02-11 15:10:31 +01007992 * <p>The device selection is expressed as an {@link AudioDeviceInfo} among devices returned by
Eric Laurent6aa23612022-11-18 16:08:20 +01007993 * {@link #getAvailableCommunicationDevices()}. Note that only devices in a sink role
7994 * (AKA output devices, see {@link AudioDeviceInfo#isSink()}) can be specified. The matching
7995 * source device is selected automatically by the platform.
7996 * <p>The selection is active as long as the requesting application process lives, until
Eric Laurent7412f572021-02-11 15:10:31 +01007997 * {@link #clearCommunicationDevice} is called or until the device is disconnected.
Eric Laurentb36d4a12020-10-09 09:52:49 -07007998 * It is therefore important for applications to clear the request when a call ends or the
Eric Laurent7412f572021-02-11 15:10:31 +01007999 * the requesting activity or service is stopped or destroyed.
Eric Laurentb36d4a12020-10-09 09:52:49 -07008000 * <p>In case of simultaneous requests by multiple applications the priority is given to the
8001 * application currently controlling the audio mode (see {@link #setMode(int)}). This is the
8002 * latest application having selected mode {@link #MODE_IN_COMMUNICATION} or mode
8003 * {@link #MODE_IN_CALL}. Note that <code>MODE_IN_CALL</code> can only be selected by the main
8004 * telephony application with permission
8005 * {@link android.Manifest.permission#MODIFY_PHONE_STATE}.
8006 * <p> If the requested devices is not currently available, the request will be rejected and
8007 * the method will return false.
8008 * <p>This API replaces the following deprecated APIs:
8009 * <ul>
8010 * <li> {@link #startBluetoothSco()}
8011 * <li> {@link #stopBluetoothSco()}
8012 * <li> {@link #setSpeakerphoneOn(boolean)}
8013 * </ul>
8014 * <h4>Example</h4>
8015 * <p>The example below shows how to enable and disable speakerphone mode.
8016 * <pre class="prettyprint">
8017 * // Get an AudioManager instance
8018 * AudioManager audioManager = Context.getSystemService(AudioManager.class);
Eric Laurent9a404482021-03-09 19:58:39 +01008019 * AudioDeviceInfo speakerDevice = null;
8020 * List<AudioDeviceInfo> devices = audioManager.getAvailableCommunicationDevices();
8021 * for (AudioDeviceInfo device : devices) {
8022 * if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
8023 * speakerDevice = device;
8024 * break;
Eric Laurentb36d4a12020-10-09 09:52:49 -07008025 * }
Eric Laurent9a404482021-03-09 19:58:39 +01008026 * }
8027 * if (speakerDevice != null) {
8028 * // Turn speakerphone ON.
8029 * boolean result = audioManager.setCommunicationDevice(speakerDevice);
8030 * if (!result) {
8031 * // Handle error.
Eric Laurentb36d4a12020-10-09 09:52:49 -07008032 * }
Eric Laurent9a404482021-03-09 19:58:39 +01008033 * // Turn speakerphone OFF.
8034 * audioManager.clearCommunicationDevice();
Eric Laurentb36d4a12020-10-09 09:52:49 -07008035 * }
8036 * </pre>
8037 * @param device the requested audio device.
8038 * @return <code>true</code> if the request was accepted, <code>false</code> otherwise.
8039 * @throws IllegalArgumentException If an invalid device is specified.
8040 */
Eric Laurent7412f572021-02-11 15:10:31 +01008041 public boolean setCommunicationDevice(@NonNull AudioDeviceInfo device) {
Eric Laurentb36d4a12020-10-09 09:52:49 -07008042 Objects.requireNonNull(device);
8043 try {
8044 if (device.getId() == 0) {
8045 throw new IllegalArgumentException("In valid device: " + device);
8046 }
Eric Laurent7412f572021-02-11 15:10:31 +01008047 return getService().setCommunicationDevice(mICallBack, device.getId());
Eric Laurentb36d4a12020-10-09 09:52:49 -07008048 } catch (RemoteException e) {
8049 throw e.rethrowFromSystemServer();
8050 }
8051 }
8052
8053 /**
8054 * Cancels previous communication device selection made with
Eric Laurent7412f572021-02-11 15:10:31 +01008055 * {@link #setCommunicationDevice(AudioDeviceInfo)}.
Eric Laurentb36d4a12020-10-09 09:52:49 -07008056 */
Eric Laurent7412f572021-02-11 15:10:31 +01008057 public void clearCommunicationDevice() {
Eric Laurentb36d4a12020-10-09 09:52:49 -07008058 try {
Eric Laurent7412f572021-02-11 15:10:31 +01008059 getService().setCommunicationDevice(mICallBack, 0);
Eric Laurentb36d4a12020-10-09 09:52:49 -07008060 } catch (RemoteException e) {
8061 throw e.rethrowFromSystemServer();
8062 }
8063 }
8064
8065 /**
8066 * Returns currently selected audio device for communication.
8067 * <p>This API replaces the following deprecated APIs:
8068 * <ul>
8069 * <li> {@link #isBluetoothScoOn()}
8070 * <li> {@link #isSpeakerphoneOn()}
8071 * </ul>
8072 * @return an {@link AudioDeviceInfo} indicating which audio device is
Eric Laurent7412f572021-02-11 15:10:31 +01008073 * currently selected for communication use cases. Can be null on platforms
8074 * not supporting {@link android.content.pm.PackageManager#FEATURE_TELEPHONY}.
Eric Laurentb36d4a12020-10-09 09:52:49 -07008075 * is used.
8076 */
8077 @Nullable
Eric Laurent7412f572021-02-11 15:10:31 +01008078 public AudioDeviceInfo getCommunicationDevice() {
Eric Laurentb36d4a12020-10-09 09:52:49 -07008079 try {
8080 return getDeviceForPortId(
Eric Laurent7412f572021-02-11 15:10:31 +01008081 getService().getCommunicationDevice(), GET_DEVICES_OUTPUTS);
8082 } catch (RemoteException e) {
8083 throw e.rethrowFromSystemServer();
8084 }
8085 }
8086
8087 /**
8088 * Returns a list of audio devices that can be selected for communication use cases via
8089 * {@link #setCommunicationDevice(AudioDeviceInfo)}.
8090 * @return a list of {@link AudioDeviceInfo} suitable for use with setCommunicationDevice().
8091 */
8092 @NonNull
8093 public List<AudioDeviceInfo> getAvailableCommunicationDevices() {
8094 try {
8095 ArrayList<AudioDeviceInfo> devices = new ArrayList<>();
8096 int[] portIds = getService().getAvailableCommunicationDeviceIds();
8097 for (int portId : portIds) {
8098 AudioDeviceInfo device = getDeviceForPortId(portId, GET_DEVICES_OUTPUTS);
8099 if (device == null) {
8100 continue;
8101 }
8102 devices.add(device);
8103 }
8104 return devices;
Eric Laurentb36d4a12020-10-09 09:52:49 -07008105 } catch (RemoteException e) {
8106 throw e.rethrowFromSystemServer();
8107 }
8108 }
8109
8110 /**
Dorin Drimuseb9bf642022-01-03 12:05:37 +01008111 * Returns a list of direct {@link AudioProfile} that are supported for the specified
8112 * {@link AudioAttributes}. This can be empty in case of an error or if no direct playback
8113 * is possible.
8114 *
8115 * <p>Direct playback means that the audio stream is not resampled or downmixed
8116 * by the framework. Checking for direct support can help the app select the representation
8117 * of audio content that most closely matches the capabilities of the device and peripherals
8118 * (e.g. A/V receiver) connected to it. Note that the provided stream can still be re-encoded
8119 * or mixed with other streams, if needed.
8120 * <p>When using this information to inform your application which audio format to play,
8121 * query again whenever audio output devices change (see {@link AudioDeviceCallback}).
8122 * @param attributes a non-null {@link AudioAttributes} instance.
8123 * @return a list of {@link AudioProfile}
8124 */
8125 @NonNull
8126 public List<AudioProfile> getDirectProfilesForAttributes(@NonNull AudioAttributes attributes) {
8127 Objects.requireNonNull(attributes);
8128 ArrayList<AudioProfile> audioProfilesList = new ArrayList<>();
8129 int status = AudioSystem.getDirectProfilesForAttributes(attributes, audioProfilesList);
8130 if (status != SUCCESS) {
8131 Log.w(TAG, "getDirectProfilesForAttributes failed.");
8132 return new ArrayList<>();
8133 }
8134 return audioProfilesList;
8135 }
8136
8137 /**
Eric Laurentb36d4a12020-10-09 09:52:49 -07008138 * @hide
8139 * Returns an {@link AudioDeviceInfo} corresponding to a connected device of the type provided.
8140 * The type must be a valid output type defined in <code>AudioDeviceInfo</code> class,
8141 * for instance {@link AudioDeviceInfo#TYPE_BUILTIN_SPEAKER}.
8142 * The method will return null if no device of the provided type is connected.
8143 * If more than one device of the provided type is connected, an object corresponding to the
8144 * first device encountered in the enumeration list will be returned.
8145 * @param deviceType The device device for which an <code>AudioDeviceInfo</code>
Eric Laurent7412f572021-02-11 15:10:31 +01008146 * object is queried.
Eric Laurentb36d4a12020-10-09 09:52:49 -07008147 * @return An AudioDeviceInfo object or null if no device with the requested type is connected.
8148 * @throws IllegalArgumentException If an invalid device type is specified.
8149 */
8150 @TestApi
8151 @Nullable
8152 public static AudioDeviceInfo getDeviceInfoFromType(
8153 @AudioDeviceInfo.AudioDeviceTypeOut int deviceType) {
Eric Laurent7412f572021-02-11 15:10:31 +01008154 return getDeviceInfoFromTypeAndAddress(deviceType, null);
8155 }
8156
Eric Laurent78eef3a2021-11-09 16:10:42 +01008157 /**
Eric Laurent7412f572021-02-11 15:10:31 +01008158 * @hide
8159 * Returns an {@link AudioDeviceInfo} corresponding to a connected device of the type and
8160 * address provided.
8161 * The type must be a valid output type defined in <code>AudioDeviceInfo</code> class,
8162 * for instance {@link AudioDeviceInfo#TYPE_BUILTIN_SPEAKER}.
8163 * If a null address is provided, the matching will happen on the type only.
8164 * The method will return null if no device of the provided type and address is connected.
8165 * If more than one device of the provided type is connected, an object corresponding to the
8166 * first device encountered in the enumeration list will be returned.
8167 * @param type The device device for which an <code>AudioDeviceInfo</code>
8168 * object is queried.
8169 * @param address The device address for which an <code>AudioDeviceInfo</code>
8170 * object is queried or null if requesting match on type only.
8171 * @return An AudioDeviceInfo object or null if no matching device is connected.
8172 * @throws IllegalArgumentException If an invalid device type is specified.
8173 */
8174 @Nullable
8175 public static AudioDeviceInfo getDeviceInfoFromTypeAndAddress(
8176 @AudioDeviceInfo.AudioDeviceTypeOut int type, @Nullable String address) {
Eric Laurentb36d4a12020-10-09 09:52:49 -07008177 AudioDeviceInfo[] devices = getDevicesStatic(GET_DEVICES_OUTPUTS);
Eric Laurent7412f572021-02-11 15:10:31 +01008178 AudioDeviceInfo deviceForType = null;
Eric Laurentb36d4a12020-10-09 09:52:49 -07008179 for (AudioDeviceInfo device : devices) {
Eric Laurent7412f572021-02-11 15:10:31 +01008180 if (device.getType() == type) {
8181 deviceForType = device;
8182 if (address == null || address.equals(device.getAddress())) {
8183 return device;
8184 }
Eric Laurentb36d4a12020-10-09 09:52:49 -07008185 }
8186 }
Eric Laurent7412f572021-02-11 15:10:31 +01008187 return deviceForType;
Eric Laurentb36d4a12020-10-09 09:52:49 -07008188 }
8189
8190 /**
8191 * Listener registered by client to be notified upon communication audio device change.
Eric Laurent7412f572021-02-11 15:10:31 +01008192 * See {@link #setCommunicationDevice(AudioDeviceInfo)}.
Eric Laurentb36d4a12020-10-09 09:52:49 -07008193 */
8194 public interface OnCommunicationDeviceChangedListener {
8195 /**
8196 * Callback method called upon communication audio device change.
Eric Laurent7412f572021-02-11 15:10:31 +01008197 * @param device the audio device requested for communication use cases.
8198 * Can be null on platforms not supporting
8199 * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY}.
Eric Laurentb36d4a12020-10-09 09:52:49 -07008200 */
8201 void onCommunicationDeviceChanged(@Nullable AudioDeviceInfo device);
8202 }
8203
8204 /**
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08008205 * manages the OnCommunicationDeviceChangedListener listeners and the
8206 * CommunicationDeviceDispatcherStub
8207 */
8208 private final CallbackUtil.LazyListenerManager<OnCommunicationDeviceChangedListener>
8209 mCommDeviceChangedListenerMgr = new CallbackUtil.LazyListenerManager();
8210 /**
Eric Laurentb36d4a12020-10-09 09:52:49 -07008211 * Adds a listener for being notified of changes to the communication audio device.
Eric Laurent7412f572021-02-11 15:10:31 +01008212 * See {@link #setCommunicationDevice(AudioDeviceInfo)}.
Eric Laurentb36d4a12020-10-09 09:52:49 -07008213 * @param executor
8214 * @param listener
8215 */
8216 public void addOnCommunicationDeviceChangedListener(
8217 @NonNull @CallbackExecutor Executor executor,
8218 @NonNull OnCommunicationDeviceChangedListener listener) {
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08008219 mCommDeviceChangedListenerMgr.addListener(
8220 executor, listener, "addOnCommunicationDeviceChangedListener",
8221 () -> new CommunicationDeviceDispatcherStub());
Eric Laurentb36d4a12020-10-09 09:52:49 -07008222 }
8223
8224 /**
8225 * Removes a previously added listener of changes to the communication audio device.
Eric Laurent7412f572021-02-11 15:10:31 +01008226 * See {@link #setCommunicationDevice(AudioDeviceInfo)}.
Eric Laurentb36d4a12020-10-09 09:52:49 -07008227 * @param listener
8228 */
8229 public void removeOnCommunicationDeviceChangedListener(
8230 @NonNull OnCommunicationDeviceChangedListener listener) {
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08008231 mCommDeviceChangedListenerMgr.removeListener(listener,
8232 "removeOnCommunicationDeviceChangedListener");
Eric Laurentb36d4a12020-10-09 09:52:49 -07008233 }
8234
Eric Laurentb36d4a12020-10-09 09:52:49 -07008235 private final class CommunicationDeviceDispatcherStub
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08008236 extends ICommunicationDeviceDispatcher.Stub implements CallbackUtil.DispatcherStub {
Eric Laurentb36d4a12020-10-09 09:52:49 -07008237
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08008238 @Override
Jean-Michel Trivi91f0baa2021-11-11 16:31:32 -08008239 public void register(boolean register) {
Eric Laurentb36d4a12020-10-09 09:52:49 -07008240 try {
Jean-Michel Trivi91f0baa2021-11-11 16:31:32 -08008241 if (register) {
8242 getService().registerCommunicationDeviceDispatcher(this);
8243 } else {
8244 getService().unregisterCommunicationDeviceDispatcher(this);
Eric Laurentb36d4a12020-10-09 09:52:49 -07008245 }
Jean-Michel Trivi91f0baa2021-11-11 16:31:32 -08008246 } catch (RemoteException e) {
8247 e.rethrowFromSystemServer();
Eric Laurentb36d4a12020-10-09 09:52:49 -07008248 }
8249 }
Eric Laurentb36d4a12020-10-09 09:52:49 -07008250
Jean-Michel Trivi91f0baa2021-11-11 16:31:32 -08008251 @Override
Jean-Michel Trivi91f0baa2021-11-11 16:31:32 -08008252 public void dispatchCommunicationDeviceChanged(int portId) {
8253 AudioDeviceInfo device = getDeviceForPortId(portId, GET_DEVICES_OUTPUTS);
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08008254 mCommDeviceChangedListenerMgr.callListeners(
Jean-Michel Trivi91f0baa2021-11-11 16:31:32 -08008255 (listener) -> listener.onCommunicationDeviceChanged(device));
Eric Laurentb36d4a12020-10-09 09:52:49 -07008256 }
Eric Laurentb36d4a12020-10-09 09:52:49 -07008257 }
8258
Eric Laurent78eef3a2021-11-09 16:10:42 +01008259
8260 /**
8261 * @hide
8262 * Indicates if the platform allows accessing the uplink and downlink audio of an ongoing
8263 * PSTN call.
8264 * When true, {@link getCallUplinkInjectionAudioTrack(AudioFormat)} can be used to obtain
8265 * an AudioTrack for call uplink audio injection and
8266 * {@link getCallDownlinkExtractionAudioRecord(AudioFormat)} can be used to obtain
8267 * an AudioRecord for call downlink audio extraction.
8268 * @return true if PSTN call audio is accessible, false otherwise.
8269 */
8270 @TestApi
8271 @SystemApi
8272 @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION)
8273 public boolean isPstnCallAudioInterceptable() {
8274 final IAudioService service = getService();
8275 try {
8276 return service.isPstnCallAudioInterceptable();
8277 } catch (RemoteException e) {
8278 throw e.rethrowFromSystemServer();
8279 }
8280 }
8281
8282 /** @hide */
8283 @IntDef(flag = false, prefix = "CALL_REDIRECT_", value = {
8284 CALL_REDIRECT_NONE,
8285 CALL_REDIRECT_PSTN,
8286 CALL_REDIRECT_VOIP }
8287 )
8288 @Retention(RetentionPolicy.SOURCE)
8289 public @interface CallRedirectionMode {}
8290
8291 /**
8292 * Not used for call redirection
8293 * @hide
8294 */
8295 public static final int CALL_REDIRECT_NONE = 0;
8296 /**
8297 * Used to redirect PSTN call
8298 * @hide
8299 */
8300 public static final int CALL_REDIRECT_PSTN = 1;
8301 /**
8302 * Used to redirect VoIP call
8303 * @hide
8304 */
8305 public static final int CALL_REDIRECT_VOIP = 2;
8306
8307
8308 private @CallRedirectionMode int getCallRedirectMode() {
8309 int mode = getMode();
8310 if (mode == MODE_IN_CALL || mode == MODE_CALL_SCREENING
8311 || mode == MODE_CALL_REDIRECT) {
8312 return CALL_REDIRECT_PSTN;
8313 } else if (mode == MODE_IN_COMMUNICATION || mode == MODE_COMMUNICATION_REDIRECT) {
8314 return CALL_REDIRECT_VOIP;
8315 }
8316 return CALL_REDIRECT_NONE;
8317 }
8318
8319 private void checkCallRedirectionFormat(AudioFormat format, boolean isOutput) {
8320 if (format.getEncoding() != AudioFormat.ENCODING_PCM_16BIT
8321 && format.getEncoding() != AudioFormat.ENCODING_PCM_FLOAT) {
8322 throw new UnsupportedOperationException(" Unsupported encoding ");
8323 }
8324 if (format.getSampleRate() < 8000
8325 || format.getSampleRate() > 48000) {
8326 throw new UnsupportedOperationException(" Unsupported sample rate ");
8327 }
8328 if (isOutput && format.getChannelMask() != AudioFormat.CHANNEL_OUT_MONO
8329 && format.getChannelMask() != AudioFormat.CHANNEL_OUT_STEREO) {
8330 throw new UnsupportedOperationException(" Unsupported output channel mask ");
8331 }
8332 if (!isOutput && format.getChannelMask() != AudioFormat.CHANNEL_IN_MONO
8333 && format.getChannelMask() != AudioFormat.CHANNEL_IN_STEREO) {
8334 throw new UnsupportedOperationException(" Unsupported input channel mask ");
8335 }
8336 }
8337
8338 class CallIRedirectionClientInfo {
8339 public WeakReference trackOrRecord;
8340 public int redirectMode;
8341 }
8342
8343 private Object mCallRedirectionLock = new Object();
8344 @GuardedBy("mCallRedirectionLock")
8345 private CallInjectionModeChangedListener mCallRedirectionModeListener;
8346 @GuardedBy("mCallRedirectionLock")
8347 private ArrayList<CallIRedirectionClientInfo> mCallIRedirectionClients;
8348
8349 /**
8350 * @hide
8351 * Returns an AudioTrack that can be used to inject audio to an active call uplink.
8352 * This can be used for functions like call screening or call audio redirection and is reserved
8353 * to system apps with privileged permission.
8354 * @param format the desired audio format for audio playback.
8355 * p>Formats accepted are:
8356 * <ul>
8357 * <li><em>Sampling rate</em> - 8kHz to 48kHz. </li>
8358 * <li><em>Channel mask</em> - Mono or Stereo </li>
8359 * <li><em>Sample format</em> - PCM 16 bit or FLOAT 32 bit </li>
8360 * </ul>
8361 *
8362 * @return The AudioTrack used for audio injection
8363 * @throws NullPointerException if AudioFormat argument is null.
8364 * @throws UnsupportedOperationException if on unsupported AudioFormat is specified.
8365 * @throws IllegalArgumentException if an invalid AudioFormat is specified.
8366 * @throws SecurityException if permission CALL_AUDIO_INTERCEPTION is missing .
8367 * @throws IllegalStateException if current audio mode is not MODE_IN_CALL,
8368 * MODE_IN_COMMUNICATION, MODE_CALL_SCREENING, MODE_CALL_REDIRECT
8369 * or MODE_COMMUNICATION_REDIRECT.
8370 */
8371 @TestApi
8372 @SystemApi
8373 @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION)
8374 public @NonNull AudioTrack getCallUplinkInjectionAudioTrack(@NonNull AudioFormat format) {
8375 Objects.requireNonNull(format);
8376 checkCallRedirectionFormat(format, true /* isOutput */);
8377
8378 AudioTrack track = null;
8379 int redirectMode = getCallRedirectMode();
8380 if (redirectMode == CALL_REDIRECT_NONE) {
8381 throw new IllegalStateException(
8382 " not available in mode " + AudioSystem.modeToString(getMode()));
8383 } else if (redirectMode == CALL_REDIRECT_PSTN && !isPstnCallAudioInterceptable()) {
8384 throw new UnsupportedOperationException(" PSTN Call audio not accessible ");
8385 }
8386
8387 track = new AudioTrack.Builder()
8388 .setAudioAttributes(new AudioAttributes.Builder()
8389 .setSystemUsage(AudioAttributes.USAGE_CALL_ASSISTANT)
8390 .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
8391 .build())
8392 .setAudioFormat(format)
8393 .setCallRedirectionMode(redirectMode)
8394 .build();
8395
8396 if (track != null && track.getState() != AudioTrack.STATE_UNINITIALIZED) {
8397 synchronized (mCallRedirectionLock) {
8398 if (mCallRedirectionModeListener == null) {
8399 mCallRedirectionModeListener = new CallInjectionModeChangedListener();
8400 try {
8401 addOnModeChangedListener(
8402 Executors.newSingleThreadExecutor(), mCallRedirectionModeListener);
8403 } catch (Exception e) {
8404 Log.e(TAG, "addOnModeChangedListener failed with exception: " + e);
8405 mCallRedirectionModeListener = null;
8406 throw new UnsupportedOperationException(" Cannot register mode listener ");
8407 }
8408 mCallIRedirectionClients = new ArrayList<CallIRedirectionClientInfo>();
8409 }
8410 CallIRedirectionClientInfo info = new CallIRedirectionClientInfo();
8411 info.redirectMode = redirectMode;
8412 info.trackOrRecord = new WeakReference<AudioTrack>(track);
8413 mCallIRedirectionClients.add(info);
8414 }
8415 } else {
8416 throw new UnsupportedOperationException(" Cannot create the AudioTrack");
8417 }
8418 return track;
8419 }
8420
8421 /**
8422 * @hide
8423 * Returns an AudioRecord that can be used to extract audio from an active call downlink.
8424 * This can be used for functions like call screening or call audio redirection and is reserved
8425 * to system apps with privileged permission.
8426 * @param format the desired audio format for audio capture.
8427 *<p>Formats accepted are:
8428 * <ul>
8429 * <li><em>Sampling rate</em> - 8kHz to 48kHz. </li>
8430 * <li><em>Channel mask</em> - Mono or Stereo </li>
8431 * <li><em>Sample format</em> - PCM 16 bit or FLOAT 32 bit </li>
8432 * </ul>
8433 *
8434 * @return The AudioRecord used for audio extraction
8435 * @throws UnsupportedOperationException if on unsupported AudioFormat is specified.
8436 * @throws IllegalArgumentException if an invalid AudioFormat is specified.
8437 * @throws NullPointerException if AudioFormat argument is null.
8438 * @throws SecurityException if permission CALL_AUDIO_INTERCEPTION is missing .
8439 * @throws IllegalStateException if current audio mode is not MODE_IN_CALL,
8440 * MODE_IN_COMMUNICATION, MODE_CALL_SCREENING, MODE_CALL_REDIRECT
8441 * or MODE_COMMUNICATION_REDIRECT.
8442 */
8443 @TestApi
8444 @SystemApi
8445 @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION)
8446 public @NonNull AudioRecord getCallDownlinkExtractionAudioRecord(@NonNull AudioFormat format) {
8447 Objects.requireNonNull(format);
8448 checkCallRedirectionFormat(format, false /* isOutput */);
8449
8450 AudioRecord record = null;
8451 int redirectMode = getCallRedirectMode();
8452 if (redirectMode == CALL_REDIRECT_NONE) {
8453 throw new IllegalStateException(
8454 " not available in mode " + AudioSystem.modeToString(getMode()));
8455 } else if (redirectMode == CALL_REDIRECT_PSTN && !isPstnCallAudioInterceptable()) {
8456 throw new UnsupportedOperationException(" PSTN Call audio not accessible ");
8457 }
8458
8459 record = new AudioRecord.Builder()
8460 .setAudioAttributes(new AudioAttributes.Builder()
8461 .setInternalCapturePreset(MediaRecorder.AudioSource.VOICE_DOWNLINK)
8462 .build())
8463 .setAudioFormat(format)
8464 .setCallRedirectionMode(redirectMode)
8465 .build();
8466
8467 if (record != null && record.getState() != AudioRecord.STATE_UNINITIALIZED) {
8468 synchronized (mCallRedirectionLock) {
8469 if (mCallRedirectionModeListener == null) {
8470 mCallRedirectionModeListener = new CallInjectionModeChangedListener();
8471 try {
8472 addOnModeChangedListener(
8473 Executors.newSingleThreadExecutor(), mCallRedirectionModeListener);
8474 } catch (Exception e) {
8475 Log.e(TAG, "addOnModeChangedListener failed with exception: " + e);
8476 mCallRedirectionModeListener = null;
8477 throw new UnsupportedOperationException(" Cannot register mode listener ");
8478 }
8479 mCallIRedirectionClients = new ArrayList<CallIRedirectionClientInfo>();
8480 }
8481 CallIRedirectionClientInfo info = new CallIRedirectionClientInfo();
8482 info.redirectMode = redirectMode;
8483 info.trackOrRecord = new WeakReference<AudioRecord>(record);
8484 mCallIRedirectionClients.add(info);
8485 }
8486 } else {
8487 throw new UnsupportedOperationException(" Cannot create the AudioRecord");
8488 }
8489 return record;
8490 }
8491
8492 class CallInjectionModeChangedListener implements OnModeChangedListener {
8493 @Override
8494 public void onModeChanged(@AudioMode int mode) {
8495 synchronized (mCallRedirectionLock) {
8496 final ArrayList<CallIRedirectionClientInfo> clientInfos =
8497 (ArrayList<CallIRedirectionClientInfo>) mCallIRedirectionClients.clone();
8498 for (CallIRedirectionClientInfo info : clientInfos) {
8499 Object trackOrRecord = info.trackOrRecord.get();
8500 if (trackOrRecord != null) {
8501 if ((info.redirectMode == CALL_REDIRECT_PSTN
8502 && mode != MODE_IN_CALL && mode != MODE_CALL_SCREENING
8503 && mode != MODE_CALL_REDIRECT)
8504 || (info.redirectMode == CALL_REDIRECT_VOIP
8505 && mode != MODE_IN_COMMUNICATION
8506 && mode != MODE_COMMUNICATION_REDIRECT)) {
8507 if (trackOrRecord instanceof AudioTrack) {
8508 AudioTrack track = (AudioTrack) trackOrRecord;
8509 track.release();
8510 } else {
8511 AudioRecord record = (AudioRecord) trackOrRecord;
8512 record.release();
8513 }
8514 mCallIRedirectionClients.remove(info);
8515 }
8516 }
8517 }
8518 if (mCallIRedirectionClients.isEmpty()) {
8519 try {
8520 if (mCallRedirectionModeListener != null) {
8521 removeOnModeChangedListener(mCallRedirectionModeListener);
8522 }
8523 } catch (Exception e) {
8524 Log.e(TAG, "removeOnModeChangedListener failed with exception: " + e);
8525 } finally {
8526 mCallRedirectionModeListener = null;
8527 mCallIRedirectionClients = null;
8528 }
8529 }
8530 }
8531 }
8532 }
8533
Paul McLeane3383cc2015-05-08 11:41:20 -07008534 //---------------------------------------------------------
Jean-Michel Trivi933bf142021-11-19 16:18:52 -08008535 // audio device connection-dependent muting
8536 /**
8537 * @hide
8538 * Mute a set of playback use cases until a given audio device is connected.
8539 * Automatically unmute upon connection of the device, or after the given timeout, whichever
8540 * happens first.
8541 * @param usagesToMute non-empty array of {@link AudioAttributes} usages (for example
8542 * {@link AudioAttributes#USAGE_MEDIA}) to mute until the
8543 * device connects
8544 * @param device the audio device expected to connect within the timeout duration
8545 * @param timeout the maximum amount of time to wait for the device connection
8546 * @param timeUnit the unit for the timeout
8547 * @throws IllegalStateException when trying to issue the command while another is already in
8548 * progress and hasn't been cancelled by
8549 * {@link #cancelMuteAwaitConnection(AudioDeviceAttributes)}. See
8550 * {@link #getMutingExpectedDevice()} to check if a muting command is active.
8551 * @see #registerMuteAwaitConnectionCallback(Executor, AudioManager.MuteAwaitConnectionCallback)
8552 */
8553 @SystemApi
8554 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
8555 public void muteAwaitConnection(@NonNull int[] usagesToMute,
8556 @NonNull AudioDeviceAttributes device,
8557 long timeout, @NonNull TimeUnit timeUnit) throws IllegalStateException {
8558 if (timeout <= 0) {
8559 throw new IllegalArgumentException("Timeout must be greater than 0");
8560 }
8561 Objects.requireNonNull(usagesToMute);
8562 if (usagesToMute.length == 0) {
8563 throw new IllegalArgumentException("Array of usages to mute cannot be empty");
8564 }
8565 Objects.requireNonNull(device);
8566 Objects.requireNonNull(timeUnit);
8567 try {
8568 getService().muteAwaitConnection(usagesToMute, device, timeUnit.toMillis(timeout));
8569 } catch (RemoteException e) {
8570 throw e.rethrowFromSystemServer();
8571 }
8572 }
8573
8574 /**
8575 * @hide
8576 * Query which audio device, if any, is causing some playback use cases to be muted until it
8577 * connects.
8578 * @return the audio device used in
8579 * {@link #muteAwaitConnection(int[], AudioDeviceAttributes, long, TimeUnit)}, or null
8580 * if there is no active muting command (either because the muting command was not issued
8581 * or because it timed out)
8582 */
8583 @SystemApi
8584 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
8585 public @Nullable AudioDeviceAttributes getMutingExpectedDevice() {
8586 try {
8587 return getService().getMutingExpectedDevice();
8588 } catch (RemoteException e) {
8589 throw e.rethrowFromSystemServer();
8590 }
8591 }
8592
8593 /**
8594 * @hide
8595 * Cancel a {@link #muteAwaitConnection(int[], AudioDeviceAttributes, long, TimeUnit)}
8596 * command.
8597 * @param device the device whose connection was expected when the {@code muteAwaitConnection}
8598 * command was issued.
8599 * @throws IllegalStateException when trying to issue the command for a device whose connection
8600 * is not anticipated by a previous call to
8601 * {@link #muteAwaitConnection(int[], AudioDeviceAttributes, long, TimeUnit)}
8602 */
8603 @SystemApi
8604 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
8605 public void cancelMuteAwaitConnection(@NonNull AudioDeviceAttributes device)
8606 throws IllegalStateException {
8607 Objects.requireNonNull(device);
8608 try {
8609 getService().cancelMuteAwaitConnection(device);
8610 } catch (RemoteException e) {
8611 throw e.rethrowFromSystemServer();
8612 }
8613 }
8614
8615 /**
8616 * @hide
8617 * A callback class to receive events about the muting and unmuting of playback use cases
8618 * conditional on the upcoming connection of an audio device.
8619 * @see #registerMuteAwaitConnectionCallback(Executor, AudioManager.MuteAwaitConnectionCallback)
8620 */
8621 @SystemApi
8622 public abstract static class MuteAwaitConnectionCallback {
8623
8624 /**
8625 * An event where the expected audio device connected
8626 * @see MuteAwaitConnectionCallback#onUnmutedEvent(int, AudioDeviceAttributes, int[])
8627 */
8628 public static final int EVENT_CONNECTION = 1;
8629 /**
8630 * An event where the expected audio device failed connect before the timeout happened
8631 * @see MuteAwaitConnectionCallback#onUnmutedEvent(int, AudioDeviceAttributes, int[])
8632 */
8633 public static final int EVENT_TIMEOUT = 2;
8634 /**
8635 * An event where the {@code muteAwaitConnection()} command
8636 * was cancelled with {@link #cancelMuteAwaitConnection(AudioDeviceAttributes)}
8637 * @see MuteAwaitConnectionCallback#onUnmutedEvent(int, AudioDeviceAttributes, int[])
8638 */
8639 public static final int EVENT_CANCEL = 3;
8640
8641 /** @hide */
8642 @IntDef(flag = false, prefix = "EVENT_", value = {
8643 EVENT_CONNECTION,
8644 EVENT_TIMEOUT,
8645 EVENT_CANCEL }
8646 )
8647 @Retention(RetentionPolicy.SOURCE)
8648 public @interface UnmuteEvent {}
8649
8650 /**
8651 * Called when a number of playback use cases are muted in response to a call to
8652 * {@link #muteAwaitConnection(int[], AudioDeviceAttributes, long, TimeUnit)}.
8653 * @param device the audio device whose connection is expected. Playback use cases are
8654 * unmuted when that device connects
8655 * @param mutedUsages an array of {@link AudioAttributes} usages that describe the affected
8656 * playback use cases.
8657 */
8658 public void onMutedUntilConnection(
8659 @NonNull AudioDeviceAttributes device,
8660 @NonNull int[] mutedUsages) {}
8661
8662 /**
8663 * Called when an event occurred that caused playback uses cases to be unmuted
8664 * @param unmuteEvent the nature of the event
8665 * @param device the device that was expected to connect
8666 * @param mutedUsages the array of {@link AudioAttributes} usages that were muted until
8667 * the event occurred
8668 */
8669 public void onUnmutedEvent(
8670 @UnmuteEvent int unmuteEvent,
8671 @NonNull AudioDeviceAttributes device, @NonNull int[] mutedUsages) {}
8672 }
8673
8674
8675 /**
8676 * @hide
8677 * Register a callback to receive updates on the playback muting conditional on a specific
8678 * audio device connection.
8679 * @param executor the {@link Executor} handling the callback
8680 * @param callback the callback to register
8681 */
8682 @SystemApi
8683 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
8684 public void registerMuteAwaitConnectionCallback(
8685 @NonNull @CallbackExecutor Executor executor,
8686 @NonNull MuteAwaitConnectionCallback callback) {
8687 synchronized (mMuteAwaitConnectionListenerLock) {
8688 final Pair<ArrayList<ListenerInfo<MuteAwaitConnectionCallback>>,
8689 MuteAwaitConnectionDispatcherStub> res =
8690 CallbackUtil.addListener("registerMuteAwaitConnectionCallback",
8691 executor, callback, mMuteAwaitConnectionListeners,
8692 mMuteAwaitConnDispatcherStub,
8693 () -> new MuteAwaitConnectionDispatcherStub(),
8694 stub -> stub.register(true));
8695 mMuteAwaitConnectionListeners = res.first;
8696 mMuteAwaitConnDispatcherStub = res.second;
8697 }
8698 }
8699
8700 /**
8701 * @hide
8702 * Unregister a previously registered callback for playback muting conditional on device
8703 * connection.
8704 * @param callback the callback to unregister
8705 */
8706 @SystemApi
8707 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
8708 public void unregisterMuteAwaitConnectionCallback(
8709 @NonNull MuteAwaitConnectionCallback callback) {
8710 synchronized (mMuteAwaitConnectionListenerLock) {
8711 final Pair<ArrayList<ListenerInfo<MuteAwaitConnectionCallback>>,
8712 MuteAwaitConnectionDispatcherStub> res =
8713 CallbackUtil.removeListener("unregisterMuteAwaitConnectionCallback",
8714 callback, mMuteAwaitConnectionListeners, mMuteAwaitConnDispatcherStub,
8715 stub -> stub.register(false));
8716 mMuteAwaitConnectionListeners = res.first;
8717 mMuteAwaitConnDispatcherStub = res.second;
8718 }
8719 }
8720
Oscar Azucena3ef3f582022-01-31 19:07:14 -08008721 /**
Oscar Azucena88467fe2022-02-15 21:55:11 +00008722 * Add UIDs that can be considered as assistant.
Oscar Azucena3ef3f582022-01-31 19:07:14 -08008723 *
Oscar Azucena88467fe2022-02-15 21:55:11 +00008724 * @param assistantUids UIDs of the services that can be considered as assistant.
Oscar Azucena3ef3f582022-01-31 19:07:14 -08008725 *
8726 * @hide
8727 */
8728 @SystemApi
8729 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
Oscar Azucena88467fe2022-02-15 21:55:11 +00008730 public void addAssistantServicesUids(@NonNull int[] assistantUids) {
Oscar Azucena3ef3f582022-01-31 19:07:14 -08008731 try {
Oscar Azucena88467fe2022-02-15 21:55:11 +00008732 getService().addAssistantServicesUids(assistantUids);
Oscar Azucena3ef3f582022-01-31 19:07:14 -08008733 } catch (RemoteException e) {
8734 throw e.rethrowFromSystemServer();
8735 }
8736 }
8737
8738 /**
Oscar Azucena88467fe2022-02-15 21:55:11 +00008739 * Remove UIDs that can be considered as assistant.
Oscar Azucena3ef3f582022-01-31 19:07:14 -08008740 *
Oscar Azucena88467fe2022-02-15 21:55:11 +00008741 * @param assistantUids UIDs of the services that should be remove.
Oscar Azucena3ef3f582022-01-31 19:07:14 -08008742 *
8743 * @hide
8744 */
8745 @SystemApi
8746 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
Oscar Azucena88467fe2022-02-15 21:55:11 +00008747 public void removeAssistantServicesUids(@NonNull int[] assistantUids) {
Oscar Azucena3ef3f582022-01-31 19:07:14 -08008748 try {
Oscar Azucena88467fe2022-02-15 21:55:11 +00008749 getService().removeAssistantServicesUids(assistantUids);
Oscar Azucena3ef3f582022-01-31 19:07:14 -08008750 } catch (RemoteException e) {
8751 throw e.rethrowFromSystemServer();
8752 }
8753 }
8754
8755 /**
Oscar Azucena88467fe2022-02-15 21:55:11 +00008756 * Get the assistants UIDs that been added with the
8757 * {@link #addAssistantServicesUids(int[])} and not yet removed with
8758 * {@link #removeAssistantServicesUids(int[])}
Oscar Azucena3ef3f582022-01-31 19:07:14 -08008759 *
Oscar Azucena3ae33762022-03-04 04:44:59 +00008760 * <p> Note that during native audioserver crash and after boot up the list of assistant
8761 * UIDs will be reset to an empty list (i.e. no UID will be considered as assistant)
8762 * Just after user switch, the list of assistant will also reset to empty.
8763 * In both cases,The component's UID of the assistiant role or assistant setting will be
8764 * automitically added to the list by the audio service.
8765 *
Oscar Azucena88467fe2022-02-15 21:55:11 +00008766 * @return array of assistants UIDs
Oscar Azucena3ef3f582022-01-31 19:07:14 -08008767 *
8768 * @hide
8769 */
8770 @SystemApi
8771 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
Oscar Azucena88467fe2022-02-15 21:55:11 +00008772 public @NonNull int[] getAssistantServicesUids() {
Oscar Azucena3ef3f582022-01-31 19:07:14 -08008773 try {
8774 int[] uids = getService().getAssistantServicesUids();
Oscar Azucena88467fe2022-02-15 21:55:11 +00008775 return Arrays.copyOf(uids, uids.length);
Oscar Azucena3ef3f582022-01-31 19:07:14 -08008776 } catch (RemoteException e) {
8777 throw e.rethrowFromSystemServer();
8778 }
8779 }
8780
8781 /**
Oscar Azucena88467fe2022-02-15 21:55:11 +00008782 * Sets UIDs that can be considered as active assistant. Calling the API with a new array will
8783 * overwrite previous UIDs. If the array of UIDs is empty then no UID will be considered active.
8784 * In this manner calling the API with an empty array will remove all UIDs previously set.
Oscar Azucena3ef3f582022-01-31 19:07:14 -08008785 *
Oscar Azucena88467fe2022-02-15 21:55:11 +00008786 * @param assistantUids UIDs of the services that can be considered active assistant. Can be
8787 * an empty array, for this no UID will be considered active.
Oscar Azucena3ef3f582022-01-31 19:07:14 -08008788 *
8789 * <p> Note that during audio service crash reset and after boot up the list of active assistant
Oscar Azucena88467fe2022-02-15 21:55:11 +00008790 * UIDs will be reset to an empty list (i.e. no UID will be considered as an active assistant).
Oscar Azucena3ef3f582022-01-31 19:07:14 -08008791 * Just after user switch the list of active assistant will also reset to empty.
8792 *
8793 * @hide
8794 */
8795 @SystemApi
8796 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
Oscar Azucena88467fe2022-02-15 21:55:11 +00008797 public void setActiveAssistantServiceUids(@NonNull int[] assistantUids) {
Oscar Azucena3ef3f582022-01-31 19:07:14 -08008798 try {
Oscar Azucena88467fe2022-02-15 21:55:11 +00008799 getService().setActiveAssistantServiceUids(assistantUids);
Oscar Azucena3ef3f582022-01-31 19:07:14 -08008800 } catch (RemoteException e) {
8801 throw e.rethrowFromSystemServer();
8802 }
8803 }
8804
8805 /**
Oscar Azucena88467fe2022-02-15 21:55:11 +00008806 * Get active assistant UIDs last set with the
8807 * {@link #setActiveAssistantServiceUids(int[])}
Oscar Azucena3ef3f582022-01-31 19:07:14 -08008808 *
Oscar Azucena88467fe2022-02-15 21:55:11 +00008809 * @return array of active assistants UIDs
Oscar Azucena3ef3f582022-01-31 19:07:14 -08008810 *
8811 * @hide
8812 */
8813 @SystemApi
8814 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
Oscar Azucena88467fe2022-02-15 21:55:11 +00008815 public @NonNull int[] getActiveAssistantServicesUids() {
Oscar Azucena3ef3f582022-01-31 19:07:14 -08008816 try {
8817 int[] uids = getService().getActiveAssistantServiceUids();
Oscar Azucena88467fe2022-02-15 21:55:11 +00008818 return Arrays.copyOf(uids, uids.length);
Oscar Azucena3ef3f582022-01-31 19:07:14 -08008819 } catch (RemoteException e) {
8820 throw e.rethrowFromSystemServer();
8821 }
8822 }
8823
jiabinfe6b7f12022-02-04 18:45:44 +00008824 /**
Shunkai Yao601b6132022-11-22 01:47:48 +00008825 * Returns an {@link AudioHalVersionInfo} indicating the Audio Hal Version. If there is no audio
8826 * HAL found, null will be returned.
jiabinfe6b7f12022-02-04 18:45:44 +00008827 *
Shunkai Yao601b6132022-11-22 01:47:48 +00008828 * @return @see @link #AudioHalVersionInfo The version of Audio HAL.
jiabinfe6b7f12022-02-04 18:45:44 +00008829 * @hide
8830 */
8831 @TestApi
Shunkai Yao601b6132022-11-22 01:47:48 +00008832 public static @Nullable AudioHalVersionInfo getHalVersion() {
jiabinfe6b7f12022-02-04 18:45:44 +00008833 try {
8834 return getService().getHalVersion();
8835 } catch (RemoteException e) {
8836 Log.e(TAG, "Error querying getHalVersion", e);
8837 throw e.rethrowFromSystemServer();
8838 }
8839 }
8840
jiabin89f87ed2022-12-01 22:55:05 +00008841 //====================================================================
8842 // Preferred mixer attributes
8843
8844 /**
8845 * Returns the {@link AudioMixerAttributes} that can be used to set as preferred mixe
8846 * attributes via {@link #setPreferredMixerAttributes(
8847 * AudioAttributes, AudioDeviceInfo, AudioMixerAttributes)}.
8848 * <p>Note that only USB devices are guaranteed to expose configurable mixer attributes, the
8849 * returned list may be empty when devices do not allow dynamic configuration.
8850 *
8851 * @param device the device to query
8852 * @return a list of {@link AudioMixerAttributes} that can be used as preferred mixer attributes
8853 * for the given device.
8854 * @see #setPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo, AudioMixerAttributes)
8855 */
8856 @NonNull
8857 public List<AudioMixerAttributes> getSupportedMixerAttributes(@NonNull AudioDeviceInfo device) {
8858 Objects.requireNonNull(device);
8859 List<AudioMixerAttributes> mixerAttrs = new ArrayList<>();
8860 return (AudioSystem.getSupportedMixerAttributes(device.getId(), mixerAttrs)
8861 == AudioSystem.SUCCESS) ? mixerAttrs : new ArrayList<>();
8862 }
8863
8864 /**
8865 * Configures the mixer attributes for a particular {@link AudioAttributes} over a given
8866 * {@link AudioDeviceInfo}.
8867 * <p>When constructing an {@link AudioMixerAttributes} for setting preferred mixer attributes,
8868 * the mixer format must be constructed from an {@link AudioProfile} that can be used to set
8869 * preferred mixer attributes.
8870 * <p>The ownership of preferred mixer attributes is recognized by uid. When a playback from the
8871 * same uid is routed to the given audio device when calling this API, the output mixer/stream
8872 * will be configured with the values previously set via this API.
8873 * <p>Use {@link #clearPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo)}
8874 * to cancel setting mixer attributes for this {@link AudioAttributes}.
8875 *
8876 * @param attributes the {@link AudioAttributes} whose mixer attributes should be set.
8877 * Currently, only {@link AudioAttributes#USAGE_MEDIA} is supported. When
8878 * playing audio targeted at the given device, use the same attributes for
8879 * playback.
8880 * @param device the device to be routed. Currently, only USB device will be allowed.
8881 * @param mixerAttributes the preferred mixer attributes. When playing audio targeted at the
8882 * given device, use the same {@link AudioFormat} for both playback
8883 * and the mixer attributes.
8884 * @return true only if the preferred mixer attributes are set successfully.
8885 * @see #getPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo)
8886 * @see #clearPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo)
8887 */
8888 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS)
8889 public boolean setPreferredMixerAttributes(@NonNull AudioAttributes attributes,
8890 @NonNull AudioDeviceInfo device,
8891 @NonNull AudioMixerAttributes mixerAttributes) {
8892 Objects.requireNonNull(attributes);
8893 Objects.requireNonNull(device);
8894 Objects.requireNonNull(mixerAttributes);
8895 try {
8896 final int status = getService().setPreferredMixerAttributes(
8897 attributes, device.getId(), mixerAttributes);
8898 return status == AudioSystem.SUCCESS;
8899 } catch (RemoteException e) {
8900 throw e.rethrowFromSystemServer();
8901 }
8902 }
8903
8904 /**
8905 * Returns current preferred mixer attributes that is set via
8906 * {@link #setPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo, AudioMixerAttributes)}
8907 *
8908 * @param attributes the {@link AudioAttributes} whose mixer attributes should be set.
8909 * @param device the expected routing device
8910 * @return the preferred mixer attributes, which will be null when no preferred mixer attributes
8911 * have been set, or when they have been cleared.
8912 * @see #setPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo, AudioMixerAttributes)
8913 * @see #clearPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo)
8914 */
8915 @Nullable
8916 public AudioMixerAttributes getPreferredMixerAttributes(
8917 @NonNull AudioAttributes attributes,
8918 @NonNull AudioDeviceInfo device) {
8919 Objects.requireNonNull(attributes);
8920 Objects.requireNonNull(device);
8921 List<AudioMixerAttributes> mixerAttrList = new ArrayList<>();
8922 int ret = AudioSystem.getPreferredMixerAttributes(
8923 attributes, device.getId(), mixerAttrList);
8924 if (ret == AudioSystem.SUCCESS) {
8925 return mixerAttrList.isEmpty() ? null : mixerAttrList.get(0);
8926 } else {
8927 Log.e(TAG, "Failed calling getPreferredMixerAttributes, ret=" + ret);
8928 return null;
8929 }
8930 }
8931
8932 /**
8933 * Clears the current preferred mixer attributes that were previously set via
8934 * {@link #setPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo, AudioMixerAttributes)}
8935 *
8936 * @param attributes the {@link AudioAttributes} whose mixer attributes should be cleared.
8937 * @param device the expected routing device
8938 * @return true only if the preferred mixer attributes are removed successfully.
8939 * @see #setPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo, AudioMixerAttributes)
8940 * @see #getPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo)
8941 */
8942 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS)
8943 public boolean clearPreferredMixerAttributes(
8944 @NonNull AudioAttributes attributes,
8945 @NonNull AudioDeviceInfo device) {
8946 Objects.requireNonNull(attributes);
8947 Objects.requireNonNull(device);
8948 try {
8949 final int status = getService().clearPreferredMixerAttributes(
8950 attributes, device.getId());
8951 return status == AudioSystem.SUCCESS;
8952 } catch (RemoteException e) {
8953 throw e.rethrowFromSystemServer();
8954 }
8955 }
8956
8957 /**
8958 * Interface to be notified of changes in the preferred mixer attributes.
8959 * <p>Note that this listener will only be invoked whenever
8960 * {@link #setPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo, AudioMixerAttributes)}
8961 * or {@link #clearPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo)} or device
8962 * disconnection causes a change in preferred mixer attributes.
8963 * @see #setPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo, AudioMixerAttributes)
8964 * @see #clearPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo)
8965 */
8966 public interface OnPreferredMixerAttributesChangedListener {
8967 /**
8968 * Called on the listener to indicate that the preferred mixer attributes for the audio
8969 * attributes over the given device has changed.
8970 *
8971 * @param attributes the audio attributes for playback
8972 * @param device the targeted device
8973 * @param mixerAttributes the {@link AudioMixerAttributes} that contains information for
8974 * preferred mixer attributes or null if preferred mixer attributes
8975 * is cleared
8976 */
8977 void onPreferredMixerAttributesChanged(
8978 @NonNull AudioAttributes attributes,
8979 @NonNull AudioDeviceInfo device,
8980 @Nullable AudioMixerAttributes mixerAttributes);
8981 }
8982
8983 /**
8984 * Manage the {@link OnPreferredMixerAttributesChangedListener} listeners and the
8985 * {@link PreferredMixerAttributesDispatcherStub}.
8986 */
8987 private final CallbackUtil.LazyListenerManager<OnPreferredMixerAttributesChangedListener>
8988 mPrefMixerAttributesListenerMgr = new CallbackUtil.LazyListenerManager();
8989
8990 /**
8991 * Adds a listener for being notified of changes to the preferred mixer attributes.
8992 * @param executor the executor to execute the callback
8993 * @param listener the listener to be notified of changes in the preferred mixer attributes.
8994 */
8995 public void addOnPreferredMixerAttributesChangedListener(
8996 @NonNull @CallbackExecutor Executor executor,
8997 @NonNull OnPreferredMixerAttributesChangedListener listener) {
8998 Objects.requireNonNull(executor);
8999 Objects.requireNonNull(listener);
9000 mPrefMixerAttributesListenerMgr.addListener(executor, listener,
9001 "addOnPreferredMixerAttributesChangedListener",
9002 () -> new PreferredMixerAttributesDispatcherStub());
9003 }
9004
9005 /**
9006 * Removes a previously added listener of changes to the preferred mixer attributes.
9007 * @param listener the listener to be notified of changes in the preferred mixer attributes,
9008 * which were added via {@link #addOnPreferredMixerAttributesChangedListener(
9009 * Executor, OnPreferredMixerAttributesChangedListener)}.
9010 */
9011 public void removeOnPreferredMixerAttributesChangedListener(
9012 @NonNull OnPreferredMixerAttributesChangedListener listener) {
9013 Objects.requireNonNull(listener);
9014 mPrefMixerAttributesListenerMgr.removeListener(listener,
9015 "removeOnPreferredMixerAttributesChangedListener");
9016 }
9017
9018 private final class PreferredMixerAttributesDispatcherStub
9019 extends IPreferredMixerAttributesDispatcher.Stub
9020 implements CallbackUtil.DispatcherStub {
9021
9022 @Override
9023 public void register(boolean register) {
9024 try {
9025 if (register) {
9026 getService().registerPreferredMixerAttributesDispatcher(this);
9027 } else {
9028 getService().unregisterPreferredMixerAttributesDispatcher(this);
9029 }
9030 } catch (RemoteException e) {
9031 e.rethrowFromSystemServer();
9032 }
9033 }
9034
9035 @Override
9036 public void dispatchPrefMixerAttributesChanged(@NonNull AudioAttributes attr,
9037 int deviceId,
9038 @Nullable AudioMixerAttributes mixerAttr) {
9039 // TODO: If the device is disconnected, we may not be able to find the device with
9040 // given device id. We need a better to carry the device information via binder.
9041 AudioDeviceInfo device = getDeviceForPortId(deviceId, GET_DEVICES_OUTPUTS);
9042 if (device == null) {
9043 Log.d(TAG, "Drop preferred mixer attributes changed as the device("
9044 + deviceId + ") is disconnected");
9045 return;
9046 }
9047 mPrefMixerAttributesListenerMgr.callListeners(
9048 (listener) -> listener.onPreferredMixerAttributesChanged(
9049 attr, device, mixerAttr));
9050 }
9051 }
9052
Eric Laurent3c474bc2022-12-16 17:24:32 +01009053 /**
9054 * Requests if the implementation supports controlling the latency modes
9055 * over the Bluetooth A2DP or LE Audio links.
9056 *
9057 * @return true if supported, false otherwise
9058 *
9059 * @hide
9060 */
9061 @SystemApi
9062 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
9063 public boolean supportsBluetoothVariableLatency() {
9064 try {
9065 return getService().supportsBluetoothVariableLatency();
9066 } catch (RemoteException e) {
9067 throw e.rethrowFromSystemServer();
9068 }
9069 }
9070
9071 /**
9072 * Enables or disables the variable Bluetooth latency control mechanism in the
9073 * audio framework and the audio HAL. This does not apply to the latency mode control
9074 * on the spatializer output as this is a built-in feature.
9075 *
9076 * @hide
9077 */
9078 @SystemApi
9079 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
9080 public void setBluetoothVariableLatencyEnabled(boolean enabled) {
9081 try {
9082 getService().setBluetoothVariableLatencyEnabled(enabled);
9083 } catch (RemoteException e) {
9084 throw e.rethrowFromSystemServer();
9085 }
9086 }
9087
9088 /**
9089 * Indicates if the variable Bluetooth latency control mechanism is enabled or disabled.
9090 * @hide
9091 */
9092 @SystemApi
9093 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
9094 public boolean isBluetoothVariableLatencyEnabled() {
9095 try {
9096 return getService().isBluetoothVariableLatencyEnabled();
9097 } catch (RemoteException e) {
9098 throw e.rethrowFromSystemServer();
9099 }
9100 }
9101
jiabin89f87ed2022-12-01 22:55:05 +00009102 //====================================================================
9103 // Mute await connection
9104
Jean-Michel Trivi933bf142021-11-19 16:18:52 -08009105 private final Object mMuteAwaitConnectionListenerLock = new Object();
9106
9107 @GuardedBy("mMuteAwaitConnectionListenerLock")
9108 private @Nullable ArrayList<ListenerInfo<MuteAwaitConnectionCallback>>
9109 mMuteAwaitConnectionListeners;
9110
9111 @GuardedBy("mMuteAwaitConnectionListenerLock")
9112 private MuteAwaitConnectionDispatcherStub mMuteAwaitConnDispatcherStub;
9113
9114 private final class MuteAwaitConnectionDispatcherStub
9115 extends IMuteAwaitConnectionCallback.Stub {
9116 public void register(boolean register) {
9117 try {
9118 getService().registerMuteAwaitConnectionDispatcher(this, register);
9119 } catch (RemoteException e) {
9120 throw e.rethrowFromSystemServer();
9121 }
9122 }
9123
9124 @Override
9125 @SuppressLint("GuardedBy") // lock applied inside callListeners method
9126 public void dispatchOnMutedUntilConnection(AudioDeviceAttributes device,
9127 int[] mutedUsages) {
9128 CallbackUtil.callListeners(mMuteAwaitConnectionListeners,
9129 mMuteAwaitConnectionListenerLock,
9130 (listener) -> listener.onMutedUntilConnection(device, mutedUsages));
9131 }
9132
9133 @Override
9134 @SuppressLint("GuardedBy") // lock applied inside callListeners method
9135 public void dispatchOnUnmutedEvent(int event, AudioDeviceAttributes device,
9136 int[] mutedUsages) {
9137 CallbackUtil.callListeners(mMuteAwaitConnectionListeners,
9138 mMuteAwaitConnectionListenerLock,
9139 (listener) -> listener.onUnmutedEvent(event, device, mutedUsages));
9140 }
9141 }
9142
9143 //---------------------------------------------------------
Paul McLeane3383cc2015-05-08 11:41:20 -07009144 // Inner classes
9145 //--------------------
9146 /**
9147 * Helper class to handle the forwarding of native events to the appropriate listener
9148 * (potentially) handled in a different thread.
9149 */
9150 private class NativeEventHandlerDelegate {
9151 private final Handler mHandler;
9152
Paul McLean03346882015-05-12 15:36:56 -07009153 NativeEventHandlerDelegate(final AudioDeviceCallback callback,
Paul McLeane3383cc2015-05-08 11:41:20 -07009154 Handler handler) {
9155 // find the looper for our new event handler
9156 Looper looper;
9157 if (handler != null) {
9158 looper = handler.getLooper();
9159 } else {
9160 // no given handler, use the looper the addListener call was called in
9161 looper = Looper.getMainLooper();
9162 }
9163
9164 // construct the event handler with this looper
9165 if (looper != null) {
9166 // implement the event handler delegate
9167 mHandler = new Handler(looper) {
9168 @Override
9169 public void handleMessage(Message msg) {
9170 switch(msg.what) {
Paul McLeancbeb8a22015-06-10 08:21:27 -07009171 case MSG_DEVICES_CALLBACK_REGISTERED:
Paul McLean03346882015-05-12 15:36:56 -07009172 case MSG_DEVICES_DEVICES_ADDED:
Paul McLean03346882015-05-12 15:36:56 -07009173 if (callback != null) {
9174 callback.onAudioDevicesAdded((AudioDeviceInfo[])msg.obj);
Paul McLeane3383cc2015-05-08 11:41:20 -07009175 }
9176 break;
Paul McLean03346882015-05-12 15:36:56 -07009177
9178 case MSG_DEVICES_DEVICES_REMOVED:
9179 if (callback != null) {
9180 callback.onAudioDevicesRemoved((AudioDeviceInfo[])msg.obj);
9181 }
9182 break;
9183
Paul McLeane3383cc2015-05-08 11:41:20 -07009184 default:
9185 Log.e(TAG, "Unknown native event type: " + msg.what);
9186 break;
9187 }
9188 }
9189 };
9190 } else {
9191 mHandler = null;
9192 }
9193 }
9194
9195 Handler getHandler() {
9196 return mHandler;
9197 }
9198 }
Carter Hsu2065d1e2022-01-19 19:54:50 +08009199}