blob: a4d5606a8aac39660931ada61f540ec86919e87c [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
Jack Diver4f2f43d2023-01-23 10:40:14 +000019import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_DEFAULT;
Jan Sebechlebskyfb48c022022-12-16 16:26:47 +010020import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
21import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
22
Jean-Michel Trivic4557822023-01-23 18:19:52 +000023import android.Manifest;
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -080024import android.annotation.CallbackExecutor;
Jean-Michel Trivi8e7aca32017-11-28 18:12:38 -080025import android.annotation.IntDef;
Hayden Gomes695f8022019-04-11 10:44:18 -070026import android.annotation.IntRange;
Jean-Michel Trivi958876f2014-11-16 15:40:22 -080027import android.annotation.NonNull;
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -070028import android.annotation.Nullable;
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060029import android.annotation.RequiresPermission;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.annotation.SdkConstant;
31import android.annotation.SdkConstant.SdkConstantType;
Jean-Michel Trivi26ae15f2017-08-25 19:20:49 -070032import android.annotation.SuppressLint;
Terry Heoe7d6d972014-09-04 21:05:28 +090033import android.annotation.SystemApi;
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060034import android.annotation.SystemService;
Jean-Michel Triviec977322019-04-12 11:20:35 -070035import android.annotation.TestApi;
Julia Reynolds48034f82016-03-09 10:15:16 -050036import android.app.NotificationManager;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -070037import android.app.PendingIntent;
Eric Laurent1c3408f2021-11-09 12:09:54 +010038import android.app.compat.CompatChanges;
Arun Mirpuricb102fa2019-01-11 18:39:21 -080039import android.bluetooth.BluetoothCodecConfig;
Eric Laurentb1fbaac2012-05-29 09:24:28 -070040import android.bluetooth.BluetoothDevice;
Patty46694212021-11-04 21:03:32 +080041import android.bluetooth.BluetoothLeAudioCodecConfig;
Jan Sebechlebskyfb48c022022-12-16 16:26:47 +010042import android.companion.virtual.VirtualDeviceManager;
Eric Laurent1c3408f2021-11-09 12:09:54 +010043import android.compat.annotation.ChangeId;
44import android.compat.annotation.EnabledSince;
Artur Satayev53fe9662019-12-10 17:47:55 +000045import android.compat.annotation.UnsupportedAppUsage;
Jean-Michel Trivid327f212010-03-16 21:44:33 -070046import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.content.Context;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -070048import android.content.Intent;
Hayden Gomes62812aa2019-12-23 11:40:27 -080049import android.media.AudioAttributes.AttributeSystemUsage;
Jean-Michel Trivi91f0baa2021-11-11 16:31:32 -080050import android.media.CallbackUtil.ListenerInfo;
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -070051import android.media.audiopolicy.AudioPolicy;
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -080052import android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener;
Hayden Gomes6d69bde2019-04-04 13:10:13 -070053import android.media.audiopolicy.AudioProductStrategy;
Hayden Gomesebd6aaa2019-04-04 13:14:21 -070054import android.media.audiopolicy.AudioVolumeGroup;
François Gaffieadcd00a2018-09-18 17:06:26 +020055import android.media.audiopolicy.AudioVolumeGroupChangeHandler;
Kevin Rocard2fe8b8b2019-02-20 11:25:23 -080056import android.media.projection.MediaProjection;
RoboErikb214efb2014-07-24 13:20:30 -070057import android.media.session.MediaController;
58import android.media.session.MediaSession;
RoboErikf1372422014-04-23 14:38:17 -070059import android.media.session.MediaSessionLegacyHelper;
RoboErikb214efb2014-07-24 13:20:30 -070060import android.media.session.MediaSessionManager;
jiabinad225202019-03-20 15:22:50 -070061import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062import android.os.Binder;
Jean-Michel Trivi461922f2017-04-25 15:23:17 -070063import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064import android.os.Handler;
65import android.os.IBinder;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -080066import android.os.Looper;
67import android.os.Message;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068import android.os.RemoteException;
69import android.os.ServiceManager;
Beverlye2d9a232017-11-08 18:14:59 -050070import android.os.SystemClock;
Kenny Guy70e0c582015-06-30 19:18:28 +010071import android.os.UserHandle;
Lais Andrade724d0cd2021-11-03 19:46:21 +000072import android.provider.Settings;
jiabinc0f49442018-01-05 10:23:50 -080073import android.text.TextUtils;
Paul McLeane3383cc2015-05-08 11:41:20 -070074import android.util.ArrayMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075import android.util.Log;
jiabin589a2362018-02-22 16:21:53 -080076import android.util.Pair;
Jean-Michel Trivid327f212010-03-16 21:44:33 -070077import android.view.KeyEvent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -080079import com.android.internal.annotations.GuardedBy;
François Gaffieadcd00a2018-09-18 17:06:26 +020080import com.android.internal.util.Preconditions;
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -080081
jiabinc0f49442018-01-05 10:23:50 -080082import java.io.IOException;
Jean-Michel Trivi8e7aca32017-11-28 18:12:38 -080083import java.lang.annotation.Retention;
84import java.lang.annotation.RetentionPolicy;
jiabin0f3339c2021-07-09 11:50:07 -070085import java.lang.ref.WeakReference;
Eric Laurenta198a292014-02-18 16:26:17 -080086import java.util.ArrayList;
jiabinf40141d2020-08-07 17:27:48 -070087import java.util.Arrays;
Dorin Drimusdaeb6a92021-12-22 11:46:26 +010088import java.util.Collections;
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -080089import java.util.HashMap;
jiabind0be5b22018-04-10 14:10:04 -070090import java.util.HashSet;
Wonsik Kimb561cce2015-01-30 17:48:51 +090091import java.util.Iterator;
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -080092import java.util.List;
jiabin39940752018-04-02 18:18:45 -070093import java.util.Map;
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -070094import java.util.Objects;
Hyundo Moonca0080d2018-12-26 16:16:55 +090095import java.util.TreeMap;
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -070096import java.util.concurrent.ConcurrentHashMap;
Eric Laurent1d3cdce2018-01-20 10:31:21 -080097import java.util.concurrent.Executor;
Eric Laurent78eef3a2021-11-09 16:10:42 +010098import java.util.concurrent.Executors;
Jean-Michel Trivi933bf142021-11-19 16:18:52 -080099import java.util.concurrent.TimeUnit;
Eric Laurent700e7342014-05-02 18:33:15 -0700100
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101/**
102 * AudioManager provides access to volume and ringer mode control.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -0600104@SystemService(Context.AUDIO_SERVICE)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105public class AudioManager {
106
Marco Nelissen29f16932015-04-17 09:50:56 -0700107 private Context mOriginalContext;
108 private Context mApplicationContext;
Jan Sebechlebskyfb48c022022-12-16 16:26:47 +0100109 private @Nullable VirtualDeviceManager mVirtualDeviceManager; // Lazy initialized.
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
Jean-Michel Trivic4557822023-01-23 18:19:52 +0000411 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Kim Baekgyeongb64fac72019-12-09 10:35:58 +0000412 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
Santiago Seifert75969912023-01-18 11:11:30 +0000914 && 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 Trivic4557822023-01-23 18:19:52 +00001042 @RequiresPermission(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
Jean-Michel Trivic4557822023-01-23 18:19:52 +00001350 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
François Gaffie9c362102018-09-21 17:43:52 +02001351 public void setVolumeIndexForAttributes(@NonNull AudioAttributes attr, int index, int flags) {
1352 Preconditions.checkNotNull(attr, "attr must not be null");
1353 final IAudioService service = getService();
Francois Gaffie32a2b5f2021-11-10 13:45:42 +01001354 int groupId = getVolumeGroupIdForAttributes(attr);
1355 setVolumeGroupVolumeIndex(groupId, index, flags);
François Gaffie9c362102018-09-21 17:43:52 +02001356 }
1357
1358 /**
1359 * Returns the current volume index for a particular {@link AudioAttributes}.
1360 *
1361 * @param attr The {@link AudioAttributes} whose volume index is returned.
1362 * @return The current volume index for the stream.
1363 * @see #getMaxVolumeIndexForAttributes(AudioAttributes)
1364 * @see #getMinVolumeIndexForAttributes(AudioAttributes)
1365 * @see #setVolumeForAttributes(AudioAttributes, int, int)
1366 * @hide
1367 */
1368 @SystemApi
Hayden Gomes695f8022019-04-11 10:44:18 -07001369 @IntRange(from = 0)
Jean-Michel Trivic4557822023-01-23 18:19:52 +00001370 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
François Gaffie9c362102018-09-21 17:43:52 +02001371 public int getVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
1372 Preconditions.checkNotNull(attr, "attr must not be null");
1373 final IAudioService service = getService();
Francois Gaffie32a2b5f2021-11-10 13:45:42 +01001374 int groupId = getVolumeGroupIdForAttributes(attr);
1375 return getVolumeGroupVolumeIndex(groupId);
François Gaffie9c362102018-09-21 17:43:52 +02001376 }
1377
1378 /**
1379 * Returns the maximum volume index for a particular {@link AudioAttributes}.
1380 *
1381 * @param attr The {@link AudioAttributes} whose maximum volume index is returned.
1382 * @return The maximum valid volume index for the {@link AudioAttributes}.
1383 * @see #getVolumeIndexForAttributes(AudioAttributes)
1384 * @hide
1385 */
1386 @SystemApi
Hayden Gomes695f8022019-04-11 10:44:18 -07001387 @IntRange(from = 0)
Jean-Michel Trivic4557822023-01-23 18:19:52 +00001388 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
François Gaffie9c362102018-09-21 17:43:52 +02001389 public int getMaxVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
1390 Preconditions.checkNotNull(attr, "attr must not be null");
1391 final IAudioService service = getService();
Francois Gaffie32a2b5f2021-11-10 13:45:42 +01001392 int groupId = getVolumeGroupIdForAttributes(attr);
1393 return getVolumeGroupMaxVolumeIndex(groupId);
François Gaffie9c362102018-09-21 17:43:52 +02001394 }
1395
1396 /**
1397 * Returns the minimum volume index for a particular {@link AudioAttributes}.
1398 *
1399 * @param attr The {@link AudioAttributes} whose minimum volume index is returned.
1400 * @return The minimum valid volume index for the {@link AudioAttributes}.
1401 * @see #getVolumeIndexForAttributes(AudioAttributes)
1402 * @hide
1403 */
1404 @SystemApi
Hayden Gomes695f8022019-04-11 10:44:18 -07001405 @IntRange(from = 0)
Jean-Michel Trivic4557822023-01-23 18:19:52 +00001406 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
François Gaffie9c362102018-09-21 17:43:52 +02001407 public int getMinVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
1408 Preconditions.checkNotNull(attr, "attr must not be null");
1409 final IAudioService service = getService();
Francois Gaffie32a2b5f2021-11-10 13:45:42 +01001410 int groupId = getVolumeGroupIdForAttributes(attr);
1411 return getVolumeGroupMinVolumeIndex(groupId);
1412 }
1413
1414 /**
1415 * Returns the volume group id associated to the given {@link AudioAttributes}.
1416 *
1417 * @param attributes The {@link AudioAttributes} to consider.
1418 * @return {@link android.media.audiopolicy.AudioVolumeGroup} id supporting the given
1419 * {@link AudioAttributes} if found,
1420 * {@code android.media.audiopolicy.AudioVolumeGroup.DEFAULT_VOLUME_GROUP} otherwise.
1421 */
1422 public int getVolumeGroupIdForAttributes(@NonNull AudioAttributes attributes) {
1423 Preconditions.checkNotNull(attributes, "Audio Attributes must not be null");
1424 return AudioProductStrategy.getVolumeGroupIdForAudioAttributes(attributes,
1425 /* fallbackOnDefault= */ false);
1426 }
1427
1428 /**
1429 * Sets the volume index for a particular group associated to given id.
1430 * <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)}
1431 * to retrieve the volume group id supporting the given {@link AudioAttributes}.
1432 *
1433 * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
1434 * @param index The volume index to set. See
1435 * {@link #getVolumeGroupMaxVolumeIndex(id)} for the largest valid value
1436 * {@link #getVolumeGroupMinVolumeIndex(id)} for the lowest valid value.
1437 * @param flags One or more flags.
1438 * @hide
1439 */
1440 @SystemApi
1441 @RequiresPermission(anyOf = {
1442 android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS,
1443 android.Manifest.permission.MODIFY_AUDIO_ROUTING
1444 })
1445 public void setVolumeGroupVolumeIndex(int groupId, int index, int flags) {
1446 final IAudioService service = getService();
François Gaffie9c362102018-09-21 17:43:52 +02001447 try {
Francois Gaffie32a2b5f2021-11-10 13:45:42 +01001448 service.setVolumeGroupVolumeIndex(groupId, index, flags,
1449 getContext().getOpPackageName(), getContext().getAttributionTag());
1450 } catch (RemoteException e) {
1451 throw e.rethrowFromSystemServer();
1452 }
1453 }
1454
1455 /**
1456 * Returns the current volume index for a particular group associated to given id.
1457 * <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)}
1458 * to retrieve the volume group id supporting the given {@link AudioAttributes}.
1459 *
1460 * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
1461 * @return The current volume index for the stream.
1462 * @hide
1463 */
1464 @SystemApi
1465 @IntRange(from = 0)
1466 @RequiresPermission(anyOf = {
1467 android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS,
1468 android.Manifest.permission.MODIFY_AUDIO_ROUTING
1469 })
1470 public int getVolumeGroupVolumeIndex(int groupId) {
1471 final IAudioService service = getService();
1472 try {
1473 return service.getVolumeGroupVolumeIndex(groupId);
1474 } catch (RemoteException e) {
1475 throw e.rethrowFromSystemServer();
1476 }
1477 }
1478
1479 /**
1480 * Returns the maximum volume index for a particular group associated to given id.
1481 * <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)}
1482 * to retrieve the volume group id supporting the given {@link AudioAttributes}.
1483 *
1484 * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
1485 * @return The maximum valid volume index for the {@link AudioAttributes}.
1486 * @hide
1487 */
1488 @SystemApi
1489 @IntRange(from = 0)
1490 @RequiresPermission(anyOf = {
1491 android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS,
1492 android.Manifest.permission.MODIFY_AUDIO_ROUTING
1493 })
1494 public int getVolumeGroupMaxVolumeIndex(int groupId) {
1495 final IAudioService service = getService();
1496 try {
1497 return service.getVolumeGroupMaxVolumeIndex(groupId);
1498 } catch (RemoteException e) {
1499 throw e.rethrowFromSystemServer();
1500 }
1501 }
1502
1503 /**
1504 * Returns the minimum volume index for a particular group associated to given id.
1505 * <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)}
1506 * to retrieve the volume group id supporting the given {@link AudioAttributes}.
1507 *
1508 * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
1509 * @return The minimum valid volume index for the {@link AudioAttributes}.
1510 * @hide
1511 */
1512 @SystemApi
1513 @IntRange(from = 0)
1514 @RequiresPermission(anyOf = {
1515 android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS,
1516 android.Manifest.permission.MODIFY_AUDIO_ROUTING
1517 })
1518 public int getVolumeGroupMinVolumeIndex(int groupId) {
1519 final IAudioService service = getService();
1520 try {
1521 return service.getVolumeGroupMinVolumeIndex(groupId);
1522 } catch (RemoteException e) {
1523 throw e.rethrowFromSystemServer();
1524 }
1525 }
1526
1527 /**
1528 * Adjusts the volume of a particular group associated to given id by one step in a direction.
1529 * <p> If the volume group is associated to a stream type, it fallbacks on
1530 * {@link #adjustStreamVolume(int, int, int)} for compatibility reason.
1531 * <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)} to retrieve
1532 * the volume group id supporting the given {@link AudioAttributes}.
1533 *
1534 * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
1535 * @param direction The direction to adjust the volume. One of
1536 * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
1537 * {@link #ADJUST_SAME}.
1538 * @param flags One or more flags.
1539 * @throws SecurityException if the adjustment triggers a Do Not Disturb change and the caller
1540 * is not granted notification policy access.
1541 */
1542 public void adjustVolumeGroupVolume(int groupId, int direction, int flags) {
1543 IAudioService service = getService();
1544 try {
1545 service.adjustVolumeGroupVolume(groupId, direction, flags,
1546 getContext().getOpPackageName());
1547 } catch (RemoteException e) {
1548 throw e.rethrowFromSystemServer();
1549 }
1550 }
1551
1552 /**
1553 * Get last audible volume of the group associated to given id before it was muted.
1554 * <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)} to retrieve
1555 * the volume group id supporting the given {@link AudioAttributes}.
1556 *
1557 * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
1558 * @return current volume if not muted, volume before muted otherwise.
1559 * @hide
1560 */
1561 @SystemApi
1562 @RequiresPermission("android.permission.QUERY_AUDIO_STATE")
1563 @IntRange(from = 0)
1564 public int getLastAudibleVolumeGroupVolume(int groupId) {
1565 IAudioService service = getService();
1566 try {
1567 return service.getLastAudibleVolumeGroupVolume(groupId);
1568 } catch (RemoteException e) {
1569 throw e.rethrowFromSystemServer();
1570 }
1571 }
1572
1573 /**
1574 * Returns the current mute state for a particular volume group associated to the given id.
1575 * <p> Call first in prior {@link #getVolumeGroupIdForAttributes(AudioAttributes)} to retrieve
1576 * the volume group id supporting the given {@link AudioAttributes}.
1577 *
1578 * @param groupId of the {@link android.media.audiopolicy.AudioVolumeGroup} to consider.
1579 * @return The mute state for the given {@link android.media.audiopolicy.AudioVolumeGroup} id.
1580 * @see #adjustVolumeGroupVolume(int, int, int)
1581 */
1582 public boolean isVolumeGroupMuted(int groupId) {
1583 IAudioService service = getService();
1584 try {
1585 return service.isVolumeGroupMuted(groupId);
François Gaffie9c362102018-09-21 17:43:52 +02001586 } catch (RemoteException e) {
1587 throw e.rethrowFromSystemServer();
1588 }
1589 }
1590
1591 /**
Hayden Gomes62812aa2019-12-23 11:40:27 -08001592 * Set the system usages to be supported on this device.
1593 * @param systemUsages array of system usages to support {@link AttributeSystemUsage}
1594 * @hide
1595 */
1596 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00001597 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Hayden Gomes62812aa2019-12-23 11:40:27 -08001598 public void setSupportedSystemUsages(@NonNull @AttributeSystemUsage int[] systemUsages) {
1599 Objects.requireNonNull(systemUsages, "systemUsages must not be null");
1600 final IAudioService service = getService();
1601 try {
1602 service.setSupportedSystemUsages(systemUsages);
1603 } catch (RemoteException e) {
1604 throw e.rethrowFromSystemServer();
1605 }
1606 }
1607
1608 /**
1609 * Get the system usages supported on this device.
1610 * @return array of supported system usages {@link AttributeSystemUsage}
1611 * @hide
1612 */
1613 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00001614 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Hayden Gomes62812aa2019-12-23 11:40:27 -08001615 public @NonNull @AttributeSystemUsage int[] getSupportedSystemUsages() {
1616 final IAudioService service = getService();
1617 try {
1618 return service.getSupportedSystemUsages();
1619 } catch (RemoteException e) {
1620 throw e.rethrowFromSystemServer();
1621 }
1622 }
1623
1624 /**
RoboErik4197cb62015-01-21 15:45:32 -08001625 * Solo or unsolo a particular stream.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001626 * <p>
RoboErik4197cb62015-01-21 15:45:32 -08001627 * Do not use. This method has been deprecated and is now a no-op.
1628 * {@link #requestAudioFocus} should be used for exclusive audio playback.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001629 *
1630 * @param streamType The stream to be soloed/unsoloed.
RoboErik4197cb62015-01-21 15:45:32 -08001631 * @param state The required solo state: true for solo ON, false for solo
1632 * OFF
Eric Laurentba207e72014-05-15 17:08:16 -07001633 * @see #isVolumeFixed()
RoboErik4197cb62015-01-21 15:45:32 -08001634 * @deprecated Do not use. If you need exclusive audio playback use
1635 * {@link #requestAudioFocus}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001636 */
RoboErik4197cb62015-01-21 15:45:32 -08001637 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001638 public void setStreamSolo(int streamType, boolean state) {
RoboErik4197cb62015-01-21 15:45:32 -08001639 Log.w(TAG, "setStreamSolo has been deprecated. Do not use.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001640 }
1641
1642 /**
1643 * Mute or unmute an audio stream.
1644 * <p>
RoboErik4197cb62015-01-21 15:45:32 -08001645 * This method should only be used by applications that replace the
1646 * platform-wide management of audio settings or the main telephony
1647 * application.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001648 * <p>
RoboErik4197cb62015-01-21 15:45:32 -08001649 * This method has no effect if the device implements a fixed volume policy
Eric Laurentba207e72014-05-15 17:08:16 -07001650 * as indicated by {@link #isVolumeFixed()}.
RoboErik4197cb62015-01-21 15:45:32 -08001651 * <p>
1652 * This method was deprecated in API level 22. Prior to API level 22 this
1653 * method had significantly different behavior and should be used carefully.
1654 * The following applies only to pre-22 platforms:
1655 * <ul>
1656 * <li>The mute command is protected against client process death: if a
1657 * process with an active mute request on a stream dies, this stream will be
1658 * unmuted automatically.</li>
1659 * <li>The mute requests for a given stream are cumulative: the AudioManager
1660 * can receive several mute requests from one or more clients and the stream
1661 * will be unmuted only when the same number of unmute requests are
1662 * received.</li>
1663 * <li>For a better user experience, applications MUST unmute a muted stream
1664 * in onPause() and mute is again in onResume() if appropriate.</li>
1665 * </ul>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001666 *
1667 * @param streamType The stream to be muted/unmuted.
RoboErik4197cb62015-01-21 15:45:32 -08001668 * @param state The required mute state: true for mute ON, false for mute
1669 * OFF
Eric Laurentba207e72014-05-15 17:08:16 -07001670 * @see #isVolumeFixed()
RoboErik4197cb62015-01-21 15:45:32 -08001671 * @deprecated Use {@link #adjustStreamVolume(int, int, int)} with
1672 * {@link #ADJUST_MUTE} or {@link #ADJUST_UNMUTE} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001673 */
RoboErik4197cb62015-01-21 15:45:32 -08001674 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001675 public void setStreamMute(int streamType, boolean state) {
RoboErik4197cb62015-01-21 15:45:32 -08001676 Log.w(TAG, "setStreamMute is deprecated. adjustStreamVolume should be used instead.");
1677 int direction = state ? ADJUST_MUTE : ADJUST_UNMUTE;
1678 if (streamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
1679 adjustSuggestedStreamVolume(direction, streamType, 0);
1680 } else {
1681 adjustStreamVolume(streamType, direction, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001682 }
1683 }
1684
1685 /**
RoboErik4197cb62015-01-21 15:45:32 -08001686 * Returns the current mute state for a particular stream.
Eric Laurent25101b02011-02-02 09:33:30 -08001687 *
RoboErik4197cb62015-01-21 15:45:32 -08001688 * @param streamType The stream to get mute state for.
1689 * @return The mute state for the given stream.
1690 * @see #adjustStreamVolume(int, int, int)
Eric Laurent25101b02011-02-02 09:33:30 -08001691 */
1692 public boolean isStreamMute(int streamType) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001693 final IAudioService service = getService();
Eric Laurent25101b02011-02-02 09:33:30 -08001694 try {
John Spurlockee5ad722015-03-03 16:17:21 -05001695 return service.isStreamMute(streamType);
Eric Laurent25101b02011-02-02 09:33:30 -08001696 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001697 throw e.rethrowFromSystemServer();
Eric Laurent25101b02011-02-02 09:33:30 -08001698 }
1699 }
1700
1701 /**
Mike Lockwoodce952c82011-11-14 10:47:42 -08001702 * get master mute state.
1703 *
1704 * @hide
1705 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01001706 @UnsupportedAppUsage
Mike Lockwoodce952c82011-11-14 10:47:42 -08001707 public boolean isMasterMute() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001708 final IAudioService service = getService();
Mike Lockwoodce952c82011-11-14 10:47:42 -08001709 try {
1710 return service.isMasterMute();
1711 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001712 throw e.rethrowFromSystemServer();
Mike Lockwoodce952c82011-11-14 10:47:42 -08001713 }
1714 }
1715
1716 /**
Eric Laurent402f7f22011-02-04 12:30:32 -08001717 * forces the stream controlled by hard volume keys
1718 * specifying streamType == -1 releases control to the
1719 * logic.
1720 *
1721 * @hide
1722 */
Jean-Michel Trivic4557822023-01-23 18:19:52 +00001723 @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
Mathew Inwood31a792a2018-08-17 08:54:26 +01001724 @UnsupportedAppUsage
Eric Laurent402f7f22011-02-04 12:30:32 -08001725 public void forceVolumeControlStream(int streamType) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001726 final IAudioService service = getService();
Eric Laurent45c90ce2012-04-24 18:44:22 -07001727 try {
1728 service.forceVolumeControlStream(streamType, mICallBack);
1729 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001730 throw e.rethrowFromSystemServer();
Eric Laurent45c90ce2012-04-24 18:44:22 -07001731 }
Eric Laurent402f7f22011-02-04 12:30:32 -08001732 }
1733
1734 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001735 * Returns whether a particular type should vibrate according to user
1736 * settings and the current ringer mode.
1737 * <p>
1738 * This shouldn't be needed by most clients that use notifications to
1739 * vibrate. The notification manager will not vibrate if the policy doesn't
1740 * allow it, so the client should always set a vibrate pattern and let the
1741 * notification manager control whether or not to actually vibrate.
1742 *
1743 * @param vibrateType The type of vibrate. One of
1744 * {@link #VIBRATE_TYPE_NOTIFICATION} or
1745 * {@link #VIBRATE_TYPE_RINGER}.
1746 * @return Whether the type should vibrate at the instant this method is
1747 * called.
1748 * @see #setVibrateSetting(int, int)
1749 * @see #getVibrateSetting(int)
Eric Laurentcd1cd732012-05-01 11:23:07 -07001750 * @deprecated Applications should maintain their own vibrate policy based on
1751 * current ringer mode that can be queried via {@link #getRingerMode()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001752 */
1753 public boolean shouldVibrate(int vibrateType) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001754 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001755 try {
1756 return service.shouldVibrate(vibrateType);
1757 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001758 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001759 }
1760 }
1761
1762 /**
1763 * Returns whether the user's vibrate setting for a vibrate type.
1764 * <p>
1765 * This shouldn't be needed by most clients that want to vibrate, instead
1766 * see {@link #shouldVibrate(int)}.
1767 *
1768 * @param vibrateType The type of vibrate. One of
1769 * {@link #VIBRATE_TYPE_NOTIFICATION} or
1770 * {@link #VIBRATE_TYPE_RINGER}.
1771 * @return The vibrate setting, one of {@link #VIBRATE_SETTING_ON},
1772 * {@link #VIBRATE_SETTING_OFF}, or
1773 * {@link #VIBRATE_SETTING_ONLY_SILENT}.
1774 * @see #setVibrateSetting(int, int)
1775 * @see #shouldVibrate(int)
Eric Laurentcd1cd732012-05-01 11:23:07 -07001776 * @deprecated Applications should maintain their own vibrate policy based on
1777 * current ringer mode that can be queried via {@link #getRingerMode()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001778 */
1779 public int getVibrateSetting(int vibrateType) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001780 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001781 try {
1782 return service.getVibrateSetting(vibrateType);
1783 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001784 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001785 }
1786 }
1787
1788 /**
1789 * Sets the setting for when the vibrate type should vibrate.
Jean-Michel Trivifccb25d2009-09-15 16:06:10 -07001790 * <p>
1791 * This method should only be used by applications that replace the platform-wide
1792 * management of audio settings or the main telephony application.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001793 *
1794 * @param vibrateType The type of vibrate. One of
1795 * {@link #VIBRATE_TYPE_NOTIFICATION} or
1796 * {@link #VIBRATE_TYPE_RINGER}.
1797 * @param vibrateSetting The vibrate setting, one of
1798 * {@link #VIBRATE_SETTING_ON},
1799 * {@link #VIBRATE_SETTING_OFF}, or
1800 * {@link #VIBRATE_SETTING_ONLY_SILENT}.
1801 * @see #getVibrateSetting(int)
1802 * @see #shouldVibrate(int)
Eric Laurentcd1cd732012-05-01 11:23:07 -07001803 * @deprecated Applications should maintain their own vibrate policy based on
1804 * current ringer mode that can be queried via {@link #getRingerMode()}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001805 */
1806 public void setVibrateSetting(int vibrateType, int vibrateSetting) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001807 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001808 try {
1809 service.setVibrateSetting(vibrateType, vibrateSetting);
1810 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001811 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001812 }
1813 }
1814
1815 /**
1816 * Sets the speakerphone on or off.
Jean-Michel Trivifccb25d2009-09-15 16:06:10 -07001817 * <p>
1818 * This method should only be used by applications that replace the platform-wide
1819 * management of audio settings or the main telephony application.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001820 *
1821 * @param on set <var>true</var> to turn on speakerphone;
1822 * <var>false</var> to turn it off
Eric Laurentf23f1b72022-02-18 10:57:54 +01001823 * @deprecated Use {@link AudioManager#setCommunicationDevice(AudioDeviceInfo)} or
1824 * {@link AudioManager#clearCommunicationDevice()} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001825 */
Eric Laurentf23f1b72022-02-18 10:57:54 +01001826 @Deprecated public void setSpeakerphoneOn(boolean on) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001827 final IAudioService service = getService();
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001828 try {
Eric Laurent3aad0ad2020-05-14 12:45:18 -07001829 service.setSpeakerphoneOn(mICallBack, on);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001830 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001831 throw e.rethrowFromSystemServer();
Eric Laurenta553c252009-07-17 12:17:14 -07001832 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001833 }
1834
1835 /**
1836 * Checks whether the speakerphone is on or off.
1837 *
1838 * @return true if speakerphone is on, false if it's off
Eric Laurentf23f1b72022-02-18 10:57:54 +01001839 * @deprecated Use {@link AudioManager#getCommunicationDevice()} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001840 */
Eric Laurentf23f1b72022-02-18 10:57:54 +01001841 @Deprecated public boolean isSpeakerphoneOn() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07001842 final IAudioService service = getService();
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001843 try {
1844 return service.isSpeakerphoneOn();
1845 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07001846 throw e.rethrowFromSystemServer();
Eric Laurenta553c252009-07-17 12:17:14 -07001847 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001848 }
1849
Kevin Rocard7919c2a2019-03-21 10:28:01 -07001850 /**
Kevin Rocarddb0ff412019-05-02 17:55:35 -07001851 * 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 -07001852 * the system.
Kevin Rocard7919c2a2019-03-21 10:28:01 -07001853 *
1854 * The default is {@link AudioAttributes#ALLOW_CAPTURE_BY_ALL}.
1855 *
Kevin Rocard654b3cb2019-04-03 16:51:11 -07001856 * There are multiple ways to set this policy:
Kevin Rocarddb0ff412019-05-02 17:55:35 -07001857 * <ul>
1858 * <li> for each track independently, see
1859 * {@link AudioAttributes.Builder#setAllowedCapturePolicy(int)} </li>
1860 * <li> application-wide at runtime, with this method </li>
1861 * <li> application-wide at build time, see {@code allowAudioPlaybackCapture} in the application
1862 * manifest. </li>
1863 * </ul>
Kevin Rocard654b3cb2019-04-03 16:51:11 -07001864 * The most restrictive policy is always applied.
1865 *
Kevin Rocarddb0ff412019-05-02 17:55:35 -07001866 * See {@link AudioPlaybackCaptureConfiguration} for more details on
Kevin Rocard654b3cb2019-04-03 16:51:11 -07001867 * which audio signals can be captured.
Kevin Rocard7919c2a2019-03-21 10:28:01 -07001868 *
1869 * @param capturePolicy one of
1870 * {@link AudioAttributes#ALLOW_CAPTURE_BY_ALL},
1871 * {@link AudioAttributes#ALLOW_CAPTURE_BY_SYSTEM},
1872 * {@link AudioAttributes#ALLOW_CAPTURE_BY_NONE}.
jiabinb33f3692019-12-23 13:09:58 -08001873 * @throws RuntimeException if the argument is not a valid value.
Kevin Rocard7919c2a2019-03-21 10:28:01 -07001874 */
1875 public void setAllowedCapturePolicy(@AudioAttributes.CapturePolicy int capturePolicy) {
Kevin Rocard7919c2a2019-03-21 10:28:01 -07001876 // TODO: also pass the package in case multiple packages have the same UID
jiabinb33f3692019-12-23 13:09:58 -08001877 final IAudioService service = getService();
1878 try {
1879 int result = service.setAllowedCapturePolicy(capturePolicy);
1880 if (result != AudioSystem.AUDIO_STATUS_OK) {
1881 Log.e(TAG, "Could not setAllowedCapturePolicy: " + result);
1882 return;
1883 }
1884 } catch (RemoteException e) {
1885 throw e.rethrowFromSystemServer();
Kevin Rocard7919c2a2019-03-21 10:28:01 -07001886 }
Kevin Rocard019f60d2019-04-09 16:25:26 -07001887 }
1888
Kevin Rocard019f60d2019-04-09 16:25:26 -07001889 /**
1890 * Return the capture policy.
1891 * @return the capture policy set by {@link #setAllowedCapturePolicy(int)} or
1892 * the default if it was not called.
1893 */
1894 @AudioAttributes.CapturePolicy
1895 public int getAllowedCapturePolicy() {
jiabinb33f3692019-12-23 13:09:58 -08001896 int result = AudioAttributes.ALLOW_CAPTURE_BY_ALL;
1897 try {
1898 result = getService().getAllowedCapturePolicy();
1899 } catch (RemoteException e) {
1900 Log.e(TAG, "Failed to query allowed capture policy: " + e);
1901 }
1902 return result;
Kevin Rocard7919c2a2019-03-21 10:28:01 -07001903 }
1904
Eric Laurent3def1ee2010-03-17 23:26:26 -07001905 //====================================================================
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07001906 // Audio Product Strategy routing
1907
1908 /**
1909 * @hide
1910 * Set the preferred device for a given strategy, i.e. the audio routing to be used by
1911 * this audio strategy. Note that the device may not be available at the time the preferred
1912 * device is set, but it will be used once made available.
1913 * <p>Use {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} to cancel setting
1914 * this preference for this strategy.</p>
1915 * @param strategy the audio strategy whose routing will be affected
1916 * @param device the audio device to route to when available
1917 * @return true if the operation was successful, false otherwise
1918 */
1919 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00001920 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07001921 public boolean setPreferredDeviceForStrategy(@NonNull AudioProductStrategy strategy,
Jean-Michel Trivi0e87bca2020-02-18 14:30:29 -08001922 @NonNull AudioDeviceAttributes device) {
jiabinf40141d2020-08-07 17:27:48 -07001923 return setPreferredDevicesForStrategy(strategy, Arrays.asList(device));
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07001924 }
1925
1926 /**
1927 * @hide
jiabinf40141d2020-08-07 17:27:48 -07001928 * Removes the preferred audio device(s) previously set with
1929 * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
1930 * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}.
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07001931 * @param strategy the audio strategy whose routing will be affected
1932 * @return true if the operation was successful, false otherwise (invalid strategy, or no
1933 * device set for example)
1934 */
1935 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00001936 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07001937 public boolean removePreferredDeviceForStrategy(@NonNull AudioProductStrategy strategy) {
1938 Objects.requireNonNull(strategy);
1939 try {
1940 final int status =
jiabinf40141d2020-08-07 17:27:48 -07001941 getService().removePreferredDevicesForStrategy(strategy.getId());
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07001942 return status == AudioSystem.SUCCESS;
1943 } catch (RemoteException e) {
1944 throw e.rethrowFromSystemServer();
1945 }
1946 }
1947
1948 /**
1949 * @hide
1950 * Return the preferred device for an audio strategy, previously set with
jiabinf40141d2020-08-07 17:27:48 -07001951 * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
1952 * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}
1953 * @param strategy the strategy to query
1954 * @return the preferred device for that strategy, if multiple devices are set as preferred
1955 * devices, the first one in the list will be returned. Null will be returned if none was
1956 * ever set or if the strategy is invalid
1957 */
1958 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00001959 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
jiabinf40141d2020-08-07 17:27:48 -07001960 @Nullable
1961 public AudioDeviceAttributes getPreferredDeviceForStrategy(
1962 @NonNull AudioProductStrategy strategy) {
1963 List<AudioDeviceAttributes> devices = getPreferredDevicesForStrategy(strategy);
1964 return devices.isEmpty() ? null : devices.get(0);
1965 }
1966
1967 /**
1968 * @hide
1969 * Set the preferred devices for a given strategy, i.e. the audio routing to be used by
1970 * this audio strategy. Note that the devices may not be available at the time the preferred
1971 * devices is set, but it will be used once made available.
1972 * <p>Use {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} to cancel setting
1973 * this preference for this strategy.</p>
1974 * Note that the list of devices is not a list ranked by preference, but a list of one or more
1975 * devices used simultaneously to output the same audio signal.
1976 * @param strategy the audio strategy whose routing will be affected
1977 * @param devices a non-empty list of the audio devices to route to when available
1978 * @return true if the operation was successful, false otherwise
1979 */
1980 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00001981 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
jiabinf40141d2020-08-07 17:27:48 -07001982 public boolean setPreferredDevicesForStrategy(@NonNull AudioProductStrategy strategy,
1983 @NonNull List<AudioDeviceAttributes> devices) {
1984 Objects.requireNonNull(strategy);
1985 Objects.requireNonNull(devices);
1986 if (devices.isEmpty()) {
1987 throw new IllegalArgumentException(
1988 "Tried to set preferred devices for strategy with a empty list");
1989 }
1990 for (AudioDeviceAttributes device : devices) {
1991 Objects.requireNonNull(device);
1992 }
1993 try {
1994 final int status =
1995 getService().setPreferredDevicesForStrategy(strategy.getId(), devices);
1996 return status == AudioSystem.SUCCESS;
1997 } catch (RemoteException e) {
1998 throw e.rethrowFromSystemServer();
1999 }
2000 }
2001
2002 /**
2003 * @hide
2004 * Return the preferred devices for an audio strategy, previously set with
Jean-Michel Trivi0e87bca2020-02-18 14:30:29 -08002005 * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)}
jiabinf40141d2020-08-07 17:27:48 -07002006 * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07002007 * @param strategy the strategy to query
Paul Wang8ee29602022-12-22 03:40:19 +00002008 * @return list of the preferred devices for that strategy
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07002009 */
2010 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00002011 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
jiabinf40141d2020-08-07 17:27:48 -07002012 @NonNull
2013 public List<AudioDeviceAttributes> getPreferredDevicesForStrategy(
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07002014 @NonNull AudioProductStrategy strategy) {
2015 Objects.requireNonNull(strategy);
2016 try {
jiabinf40141d2020-08-07 17:27:48 -07002017 return getService().getPreferredDevicesForStrategy(strategy.getId());
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07002018 } catch (RemoteException e) {
2019 throw e.rethrowFromSystemServer();
2020 }
2021 }
2022
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002023 /**
2024 * @hide
Paul Wangee4774a2022-08-23 09:41:03 +00002025 * Set a device as non-default for a given strategy, i.e. the audio routing to be avoided by
2026 * this audio strategy.
2027 * <p>Use
2028 * {@link #removeDeviceAsNonDefaultForStrategy(AudioProductStrategy, AudioDeviceAttributes)}
2029 * to cancel setting this preference for this strategy.</p>
2030 * @param strategy the audio strategy whose routing will be affected
2031 * @param device the audio device to not route to when available
2032 * @return true if the operation was successful, false otherwise
2033 */
2034 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00002035 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Paul Wangee4774a2022-08-23 09:41:03 +00002036 public boolean setDeviceAsNonDefaultForStrategy(@NonNull AudioProductStrategy strategy,
2037 @NonNull AudioDeviceAttributes device) {
2038 Objects.requireNonNull(strategy);
2039 Objects.requireNonNull(device);
2040 try {
2041 final int status =
2042 getService().setDeviceAsNonDefaultForStrategy(strategy.getId(), device);
2043 return status == AudioSystem.SUCCESS;
2044 } catch (RemoteException e) {
2045 throw e.rethrowFromSystemServer();
2046 }
2047 }
2048
2049 /**
2050 * @hide
2051 * Removes the audio device(s) from the non-default device list previously set with
2052 * {@link #setDeviceAsNonDefaultForStrategy(AudioProductStrategy, AudioDeviceAttributes)}
2053 * @param strategy the audio strategy whose routing will be affected
2054 * @param device the audio device to remove from the non-default device list
2055 * @return true if the operation was successful, false otherwise (invalid strategy, or no
2056 * device set for example)
2057 */
2058 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00002059 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Paul Wangee4774a2022-08-23 09:41:03 +00002060 public boolean removeDeviceAsNonDefaultForStrategy(@NonNull AudioProductStrategy strategy,
2061 @NonNull AudioDeviceAttributes device) {
2062 Objects.requireNonNull(strategy);
2063 Objects.requireNonNull(device);
2064 try {
2065 final int status =
2066 getService().removeDeviceAsNonDefaultForStrategy(strategy.getId(), device);
2067 return status == AudioSystem.SUCCESS;
2068 } catch (RemoteException e) {
2069 throw e.rethrowFromSystemServer();
2070 }
2071 }
2072
2073 /**
2074 * @hide
2075 * Gets the audio device(s) from the non-default device list previously set with
2076 * {@link #setDeviceAsNonDefaultForStrategy(AudioProductStrategy, AudioDeviceAttributes)}
2077 * @param strategy the audio strategy to query
2078 * @return list of non-default devices for the strategy
2079 */
2080 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00002081 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Paul Wangee4774a2022-08-23 09:41:03 +00002082 @NonNull
2083 public List<AudioDeviceAttributes> getNonDefaultDevicesForStrategy(
2084 @NonNull AudioProductStrategy strategy) {
2085 Objects.requireNonNull(strategy);
2086 try {
2087 return getService().getNonDefaultDevicesForStrategy(strategy.getId());
2088 } catch (RemoteException e) {
2089 throw e.rethrowFromSystemServer();
2090 }
2091 }
2092
2093 /**
2094 * @hide
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002095 * Interface to be notified of changes in the preferred audio device set for a given audio
2096 * strategy.
Jean-Michel Trivicf9c5d32020-02-20 10:45:35 -08002097 * <p>Note that this listener will only be invoked whenever
2098 * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
jiabinf40141d2020-08-07 17:27:48 -07002099 * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)}
Jean-Michel Trivicf9c5d32020-02-20 10:45:35 -08002100 * {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} causes a change in
2101 * preferred device. It will not be invoked directly after registration with
2102 * {@link #addOnPreferredDeviceForStrategyChangedListener(Executor, OnPreferredDeviceForStrategyChangedListener)}
2103 * to indicate which strategies had preferred devices at the time of registration.</p>
Jean-Michel Trivi0e87bca2020-02-18 14:30:29 -08002104 * @see #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002105 * @see #removePreferredDeviceForStrategy(AudioProductStrategy)
2106 * @see #getPreferredDeviceForStrategy(AudioProductStrategy)
jiabinf40141d2020-08-07 17:27:48 -07002107 * @deprecated use #OnPreferredDevicesForStrategyChangedListener
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002108 */
2109 @SystemApi
jiabinf40141d2020-08-07 17:27:48 -07002110 @Deprecated
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002111 public interface OnPreferredDeviceForStrategyChangedListener {
2112 /**
2113 * Called on the listener to indicate that the preferred audio device for the given
2114 * strategy has changed.
2115 * @param strategy the {@link AudioProductStrategy} whose preferred device changed
2116 * @param device <code>null</code> if the preferred device was removed, or the newly set
2117 * preferred audio device
2118 */
2119 void onPreferredDeviceForStrategyChanged(@NonNull AudioProductStrategy strategy,
Jean-Michel Trivi0e87bca2020-02-18 14:30:29 -08002120 @Nullable AudioDeviceAttributes device);
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002121 }
2122
2123 /**
2124 * @hide
jiabinf40141d2020-08-07 17:27:48 -07002125 * Interface to be notified of changes in the preferred audio devices set for a given audio
2126 * strategy.
2127 * <p>Note that this listener will only be invoked whenever
Paul Wangee4774a2022-08-23 09:41:03 +00002128 * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)},
2129 * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)},
2130 * {@link #setDeviceAsNonDefaultForStrategy(AudioProductStrategy, AudioDeviceAttributes)},
2131 * {@link #removeDeviceAsNonDefaultForStrategy(AudioProductStrategy, AudioDeviceAttributes)}
2132 * or {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} causes a change in
jiabinf40141d2020-08-07 17:27:48 -07002133 * preferred device(s). It will not be invoked directly after registration with
2134 * {@link #addOnPreferredDevicesForStrategyChangedListener(
2135 * Executor, OnPreferredDevicesForStrategyChangedListener)}
2136 * to indicate which strategies had preferred devices at the time of registration.</p>
2137 * @see #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)
2138 * @see #setPreferredDevicesForStrategy(AudioProductStrategy, List)
2139 * @see #removePreferredDeviceForStrategy(AudioProductStrategy)
jiabinf40141d2020-08-07 17:27:48 -07002140 * @see #getPreferredDevicesForStrategy(AudioProductStrategy)
2141 */
2142 @SystemApi
2143 public interface OnPreferredDevicesForStrategyChangedListener {
2144 /**
2145 * Called on the listener to indicate that the preferred audio devices for the given
2146 * strategy has changed.
2147 * @param strategy the {@link AudioProductStrategy} whose preferred device changed
2148 * @param devices a list of newly set preferred audio devices
2149 */
2150 void onPreferredDevicesForStrategyChanged(@NonNull AudioProductStrategy strategy,
2151 @NonNull List<AudioDeviceAttributes> devices);
2152 }
2153
2154 /**
2155 * @hide
2156 * Adds a listener for being notified of changes to the strategy-preferred audio device.
2157 * @param executor
2158 * @param listener
2159 * @throws SecurityException if the caller doesn't hold the required permission
2160 * @deprecated use {@link #addOnPreferredDevicesForStrategyChangedListener(
2161 * Executor, AudioManager.OnPreferredDevicesForStrategyChangedListener)} instead
2162 */
2163 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00002164 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
jiabinf40141d2020-08-07 17:27:48 -07002165 @Deprecated
2166 public void addOnPreferredDeviceForStrategyChangedListener(
2167 @NonNull @CallbackExecutor Executor executor,
2168 @NonNull OnPreferredDeviceForStrategyChangedListener listener)
2169 throws SecurityException {
2170 // No-op, the method is deprecated.
2171 }
2172
2173 /**
2174 * @hide
2175 * Removes a previously added listener of changes to the strategy-preferred audio device.
2176 * @param listener
2177 * @deprecated use {@link #removeOnPreferredDevicesForStrategyChangedListener(
2178 * AudioManager.OnPreferredDevicesForStrategyChangedListener)} instead
2179 */
2180 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00002181 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
jiabinf40141d2020-08-07 17:27:48 -07002182 @Deprecated
2183 public void removeOnPreferredDeviceForStrategyChangedListener(
2184 @NonNull OnPreferredDeviceForStrategyChangedListener listener) {
2185 // No-op, the method is deprecated.
2186 }
2187
2188 /**
2189 * @hide
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002190 * Adds a listener for being notified of changes to the strategy-preferred audio device.
2191 * @param executor
2192 * @param listener
2193 * @throws SecurityException if the caller doesn't hold the required permission
2194 */
2195 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00002196 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
jiabinf40141d2020-08-07 17:27:48 -07002197 public void addOnPreferredDevicesForStrategyChangedListener(
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002198 @NonNull @CallbackExecutor Executor executor,
jiabinf40141d2020-08-07 17:27:48 -07002199 @NonNull OnPreferredDevicesForStrategyChangedListener listener)
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002200 throws SecurityException {
2201 Objects.requireNonNull(executor);
2202 Objects.requireNonNull(listener);
Paul Wang8ee29602022-12-22 03:40:19 +00002203 mPrefDevListenerMgr.addListener(
2204 executor, listener, "addOnPreferredDevicesForStrategyChangedListener",
2205 () -> new StrategyPreferredDevicesDispatcherStub());
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002206 }
2207
2208 /**
2209 * @hide
2210 * Removes a previously added listener of changes to the strategy-preferred audio device.
2211 * @param listener
2212 */
2213 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00002214 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
jiabinf40141d2020-08-07 17:27:48 -07002215 public void removeOnPreferredDevicesForStrategyChangedListener(
2216 @NonNull OnPreferredDevicesForStrategyChangedListener listener) {
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002217 Objects.requireNonNull(listener);
Paul Wang8ee29602022-12-22 03:40:19 +00002218 mPrefDevListenerMgr.removeListener(
2219 listener, "removeOnPreferredDevicesForStrategyChangedListener");
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002220 }
2221
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002222 /**
Paul Wangee4774a2022-08-23 09:41:03 +00002223 * @hide
2224 * Interface to be notified of changes in the non-default audio devices set for a given audio
2225 * strategy.
2226 * <p>Note that this listener will only be invoked whenever
2227 * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)},
2228 * {@link #setPreferredDevicesForStrategy(AudioProductStrategy, List<AudioDeviceAttributes>)},
2229 * {@link #setDeviceAsNonDefaultForStrategy(AudioProductStrategy, AudioDeviceAttributes)},
2230 * {@link #removeDeviceAsNonDefaultForStrategy(AudioProductStrategy, AudioDeviceAttributes)}
2231 * or {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} causes a change in
2232 * non-default device(s). It will not be invoked directly after registration with
2233 * {@link #addOnNonDefaultDevicesForStrategyChangedListener(
2234 * Executor, OnNonDefaultDevicesForStrategyChangedListener)}
2235 * to indicate which strategies had preferred devices at the time of registration.</p>
2236 * @see #setDeviceAsNonDefaultForStrategy(AudioProductStrategy, AudioDeviceAttributes)
2237 * @see #removeDeviceAsNonDefaultForStrategy(AudioProductStrategy, AudioDeviceAttributes)
2238 */
2239 @SystemApi
2240 public interface OnNonDefaultDevicesForStrategyChangedListener {
2241 /**
2242 * Called on the listener to indicate that the non-default audio devices for the given
2243 * strategy has changed.
2244 * @param strategy the {@link AudioProductStrategy} whose non-default device changed
2245 * @param devices a list of newly set non-default audio devices
2246 */
2247 void onNonDefaultDevicesForStrategyChanged(@NonNull AudioProductStrategy strategy,
2248 @NonNull List<AudioDeviceAttributes> devices);
2249 }
2250
2251 /**
2252 * @hide
2253 * Adds a listener for being notified of changes to the non-default audio devices for
2254 * strategies.
2255 * @param executor
2256 * @param listener
2257 * @throws SecurityException if the caller doesn't hold the required permission
2258 */
2259 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00002260 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Paul Wangee4774a2022-08-23 09:41:03 +00002261 public void addOnNonDefaultDevicesForStrategyChangedListener(
2262 @NonNull @CallbackExecutor Executor executor,
2263 @NonNull OnNonDefaultDevicesForStrategyChangedListener listener)
2264 throws SecurityException {
2265 Objects.requireNonNull(executor);
2266 Objects.requireNonNull(listener);
2267
2268 mNonDefDevListenerMgr.addListener(
2269 executor, listener, "addOnNonDefaultDevicesForStrategyChangedListener",
2270 () -> new StrategyNonDefaultDevicesDispatcherStub());
2271 }
2272
2273 /**
2274 * @hide
2275 * Removes a previously added listener of changes to the non-default audio device for
2276 * strategies.
2277 * @param listener
2278 */
2279 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00002280 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Paul Wangee4774a2022-08-23 09:41:03 +00002281 public void removeOnNonDefaultDevicesForStrategyChangedListener(
2282 @NonNull OnNonDefaultDevicesForStrategyChangedListener listener) {
2283 Objects.requireNonNull(listener);
2284 mNonDefDevListenerMgr.removeListener(
2285 listener, "removeOnNonDefaultDevicesForStrategyChangedListener");
2286 }
2287
2288 /**
Paul Wang8ee29602022-12-22 03:40:19 +00002289 * Manages the OnPreferredDevicesForStrategyChangedListener listeners and the
2290 * StrategyPreferredDevicesDispatcherStub
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002291 */
Paul Wang8ee29602022-12-22 03:40:19 +00002292 private final CallbackUtil.LazyListenerManager<OnPreferredDevicesForStrategyChangedListener>
2293 mPrefDevListenerMgr = new CallbackUtil.LazyListenerManager();
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002294
Paul Wangee4774a2022-08-23 09:41:03 +00002295 /**
2296 * Manages the OnNonDefaultDevicesForStrategyChangedListener listeners and the
2297 * StrategyNonDefaultDevicesDispatcherStub
2298 */
2299 private final CallbackUtil.LazyListenerManager<OnNonDefaultDevicesForStrategyChangedListener>
2300 mNonDefDevListenerMgr = new CallbackUtil.LazyListenerManager();
2301
jiabinf40141d2020-08-07 17:27:48 -07002302 private final class StrategyPreferredDevicesDispatcherStub
Paul Wang8ee29602022-12-22 03:40:19 +00002303 extends IStrategyPreferredDevicesDispatcher.Stub
2304 implements CallbackUtil.DispatcherStub {
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002305
2306 @Override
jiabinf40141d2020-08-07 17:27:48 -07002307 public void dispatchPrefDevicesChanged(int strategyId,
2308 @NonNull List<AudioDeviceAttributes> devices) {
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002309 final AudioProductStrategy strategy =
2310 AudioProductStrategy.getAudioProductStrategyWithId(strategyId);
Paul Wang8ee29602022-12-22 03:40:19 +00002311
2312 mPrefDevListenerMgr.callListeners(
2313 (listener) -> listener.onPreferredDevicesForStrategyChanged(strategy, devices));
2314 }
2315
2316 @Override
2317 public void register(boolean register) {
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002318 try {
Paul Wang8ee29602022-12-22 03:40:19 +00002319 if (register) {
2320 getService().registerStrategyPreferredDevicesDispatcher(this);
2321 } else {
2322 getService().unregisterStrategyPreferredDevicesDispatcher(this);
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002323 }
Paul Wang8ee29602022-12-22 03:40:19 +00002324 } catch (RemoteException e) {
2325 e.rethrowFromSystemServer();
Jean-Michel Trivi8d64ebb2020-02-05 15:44:42 -08002326 }
2327 }
2328 }
2329
Paul Wangee4774a2022-08-23 09:41:03 +00002330 private final class StrategyNonDefaultDevicesDispatcherStub
2331 extends IStrategyNonDefaultDevicesDispatcher.Stub
2332 implements CallbackUtil.DispatcherStub {
2333
2334 @Override
2335 public void dispatchNonDefDevicesChanged(int strategyId,
2336 @NonNull List<AudioDeviceAttributes> devices) {
2337 final AudioProductStrategy strategy =
2338 AudioProductStrategy.getAudioProductStrategyWithId(strategyId);
2339
2340 mNonDefDevListenerMgr.callListeners(
2341 (listener) -> listener.onNonDefaultDevicesForStrategyChanged(
2342 strategy, devices));
2343 }
2344
2345 @Override
2346 public void register(boolean register) {
2347 try {
2348 if (register) {
2349 getService().registerStrategyNonDefaultDevicesDispatcher(this);
2350 } else {
2351 getService().unregisterStrategyNonDefaultDevicesDispatcher(this);
2352 }
2353 } catch (RemoteException e) {
2354 e.rethrowFromSystemServer();
2355 }
2356 }
2357 }
2358
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07002359 //====================================================================
Jiabin Huangb55305f2020-09-03 17:54:16 +00002360 // Audio Capture Preset routing
2361
2362 /**
2363 * @hide
2364 * Set the preferred device for a given capture preset, i.e. the audio routing to be used by
2365 * this capture preset. Note that the device may not be available at the time the preferred
2366 * device is set, but it will be used once made available.
2367 * <p>Use {@link #clearPreferredDevicesForCapturePreset(int)} to cancel setting this preference
2368 * for this capture preset.</p>
2369 * @param capturePreset the audio capture preset whose routing will be affected
2370 * @param device the audio device to route to when available
2371 * @return true if the operation was successful, false otherwise
2372 */
2373 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00002374 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
jiabin958faf92021-03-12 20:00:05 +00002375 public boolean setPreferredDeviceForCapturePreset(@MediaRecorder.SystemSource int capturePreset,
Jiabin Huangb55305f2020-09-03 17:54:16 +00002376 @NonNull AudioDeviceAttributes device) {
2377 return setPreferredDevicesForCapturePreset(capturePreset, Arrays.asList(device));
2378 }
2379
2380 /**
2381 * @hide
2382 * Remove all the preferred audio devices previously set
2383 * @param capturePreset the audio capture preset whose routing will be affected
2384 * @return true if the operation was successful, false otherwise (invalid capture preset, or no
2385 * device set for example)
2386 */
2387 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00002388 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
jiabin958faf92021-03-12 20:00:05 +00002389 public boolean clearPreferredDevicesForCapturePreset(
2390 @MediaRecorder.SystemSource int capturePreset) {
Jiabin Huangb55305f2020-09-03 17:54:16 +00002391 if (!MediaRecorder.isValidAudioSource(capturePreset)) {
2392 return false;
2393 }
2394 try {
2395 final int status = getService().clearPreferredDevicesForCapturePreset(capturePreset);
2396 return status == AudioSystem.SUCCESS;
2397 } catch (RemoteException e) {
2398 throw e.rethrowFromSystemServer();
2399 }
2400 }
2401
2402 /**
2403 * @hide
2404 * Return the preferred devices for an audio capture preset, previously set with
2405 * {@link #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)}
2406 * @param capturePreset the capture preset to query
2407 * @return a list that contains preferred devices for that capture preset.
2408 */
2409 @NonNull
2410 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00002411 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
jiabin958faf92021-03-12 20:00:05 +00002412 public List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(
2413 @MediaRecorder.SystemSource int capturePreset) {
Jiabin Huangb55305f2020-09-03 17:54:16 +00002414 if (!MediaRecorder.isValidAudioSource(capturePreset)) {
2415 return new ArrayList<AudioDeviceAttributes>();
2416 }
2417 try {
2418 return getService().getPreferredDevicesForCapturePreset(capturePreset);
2419 } catch (RemoteException e) {
2420 throw e.rethrowFromSystemServer();
2421 }
2422 }
2423
2424 private boolean setPreferredDevicesForCapturePreset(
jiabin958faf92021-03-12 20:00:05 +00002425 @MediaRecorder.SystemSource int capturePreset,
2426 @NonNull List<AudioDeviceAttributes> devices) {
Jiabin Huangb55305f2020-09-03 17:54:16 +00002427 Objects.requireNonNull(devices);
2428 if (!MediaRecorder.isValidAudioSource(capturePreset)) {
2429 return false;
2430 }
2431 if (devices.size() != 1) {
2432 throw new IllegalArgumentException(
2433 "Only support setting one preferred devices for capture preset");
2434 }
2435 for (AudioDeviceAttributes device : devices) {
2436 Objects.requireNonNull(device);
2437 }
2438 try {
2439 final int status =
2440 getService().setPreferredDevicesForCapturePreset(capturePreset, devices);
2441 return status == AudioSystem.SUCCESS;
2442 } catch (RemoteException e) {
2443 throw e.rethrowFromSystemServer();
2444 }
2445 }
2446
2447 /**
2448 * @hide
2449 * Interface to be notified of changes in the preferred audio devices set for a given capture
2450 * preset.
2451 * <p>Note that this listener will only be invoked whenever
2452 * {@link #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)} or
2453 * {@link #clearPreferredDevicesForCapturePreset(int)} causes a change in
2454 * preferred device. It will not be invoked directly after registration with
2455 * {@link #addOnPreferredDevicesForCapturePresetChangedListener(
2456 * Executor, OnPreferredDevicesForCapturePresetChangedListener)}
2457 * to indicate which strategies had preferred devices at the time of registration.</p>
2458 * @see #setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)
2459 * @see #clearPreferredDevicesForCapturePreset(int)
2460 * @see #getPreferredDevicesForCapturePreset(int)
2461 */
2462 @SystemApi
2463 public interface OnPreferredDevicesForCapturePresetChangedListener {
2464 /**
2465 * Called on the listener to indicate that the preferred audio devices for the given
2466 * capture preset has changed.
2467 * @param capturePreset the capture preset whose preferred device changed
2468 * @param devices a list of newly set preferred audio devices
2469 */
2470 void onPreferredDevicesForCapturePresetChanged(
jiabin958faf92021-03-12 20:00:05 +00002471 @MediaRecorder.SystemSource int capturePreset,
2472 @NonNull List<AudioDeviceAttributes> devices);
Jiabin Huangb55305f2020-09-03 17:54:16 +00002473 }
2474
2475 /**
2476 * @hide
2477 * Adds a listener for being notified of changes to the capture-preset-preferred audio device.
2478 * @param executor
2479 * @param listener
2480 * @throws SecurityException if the caller doesn't hold the required permission
2481 */
2482 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00002483 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Jiabin Huangb55305f2020-09-03 17:54:16 +00002484 public void addOnPreferredDevicesForCapturePresetChangedListener(
2485 @NonNull @CallbackExecutor Executor executor,
2486 @NonNull OnPreferredDevicesForCapturePresetChangedListener listener)
2487 throws SecurityException {
2488 Objects.requireNonNull(executor);
2489 Objects.requireNonNull(listener);
2490 int status = addOnDevRoleForCapturePresetChangedListener(
2491 executor, listener, AudioSystem.DEVICE_ROLE_PREFERRED);
2492 if (status == AudioSystem.ERROR) {
2493 // This must not happen
2494 throw new RuntimeException("Unknown error happened");
2495 }
2496 if (status == AudioSystem.BAD_VALUE) {
2497 throw new IllegalArgumentException(
2498 "attempt to call addOnPreferredDevicesForCapturePresetChangedListener() "
2499 + "on a previously registered listener");
2500 }
2501 }
2502
2503 /**
2504 * @hide
2505 * Removes a previously added listener of changes to the capture-preset-preferred audio device.
2506 * @param listener
2507 */
2508 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00002509 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Jiabin Huangb55305f2020-09-03 17:54:16 +00002510 public void removeOnPreferredDevicesForCapturePresetChangedListener(
2511 @NonNull OnPreferredDevicesForCapturePresetChangedListener listener) {
2512 Objects.requireNonNull(listener);
2513 int status = removeOnDevRoleForCapturePresetChangedListener(
2514 listener, AudioSystem.DEVICE_ROLE_PREFERRED);
2515 if (status == AudioSystem.ERROR) {
2516 // This must not happen
2517 throw new RuntimeException("Unknown error happened");
2518 }
2519 if (status == AudioSystem.BAD_VALUE) {
2520 throw new IllegalArgumentException(
2521 "attempt to call removeOnPreferredDevicesForCapturePresetChangedListener() "
2522 + "on an unregistered listener");
2523 }
2524 }
2525
2526 private <T> int addOnDevRoleForCapturePresetChangedListener(
2527 @NonNull @CallbackExecutor Executor executor,
2528 @NonNull T listener, int deviceRole) {
2529 Objects.requireNonNull(executor);
2530 Objects.requireNonNull(listener);
2531 DevRoleListeners<T> devRoleListeners =
2532 (DevRoleListeners<T>) mDevRoleForCapturePresetListeners.get(deviceRole);
2533 if (devRoleListeners == null) {
2534 return AudioSystem.ERROR;
2535 }
2536 synchronized (devRoleListeners.mDevRoleListenersLock) {
2537 if (devRoleListeners.hasDevRoleListener(listener)) {
2538 return AudioSystem.BAD_VALUE;
2539 }
2540 // lazy initialization of the list of device role listener
2541 if (devRoleListeners.mListenerInfos == null) {
2542 devRoleListeners.mListenerInfos = new ArrayList<>();
2543 }
2544 final int oldCbCount = devRoleListeners.mListenerInfos.size();
2545 devRoleListeners.mListenerInfos.add(new DevRoleListenerInfo<T>(executor, listener));
2546 if (oldCbCount == 0 && devRoleListeners.mListenerInfos.size() > 0) {
2547 // register binder for callbacks
2548 synchronized (mDevRoleForCapturePresetListenersLock) {
2549 int deviceRoleListenerStatus = mDeviceRoleListenersStatus;
2550 mDeviceRoleListenersStatus |= (1 << deviceRole);
2551 if (deviceRoleListenerStatus != 0) {
2552 // There are already device role changed listeners active.
2553 return AudioSystem.SUCCESS;
2554 }
2555 if (mDevicesRoleForCapturePresetDispatcherStub == null) {
2556 mDevicesRoleForCapturePresetDispatcherStub =
2557 new CapturePresetDevicesRoleDispatcherStub();
2558 }
2559 try {
2560 getService().registerCapturePresetDevicesRoleDispatcher(
2561 mDevicesRoleForCapturePresetDispatcherStub);
2562 } catch (RemoteException e) {
2563 throw e.rethrowFromSystemServer();
2564 }
2565 }
2566 }
2567 }
2568 return AudioSystem.SUCCESS;
2569 }
2570
2571 private <T> int removeOnDevRoleForCapturePresetChangedListener(
2572 @NonNull T listener, int deviceRole) {
2573 Objects.requireNonNull(listener);
2574 DevRoleListeners<T> devRoleListeners =
2575 (DevRoleListeners<T>) mDevRoleForCapturePresetListeners.get(deviceRole);
2576 if (devRoleListeners == null) {
2577 return AudioSystem.ERROR;
2578 }
2579 synchronized (devRoleListeners.mDevRoleListenersLock) {
2580 if (!devRoleListeners.removeDevRoleListener(listener)) {
2581 return AudioSystem.BAD_VALUE;
2582 }
2583 if (devRoleListeners.mListenerInfos.size() == 0) {
2584 // unregister binder for callbacks
2585 synchronized (mDevRoleForCapturePresetListenersLock) {
2586 mDeviceRoleListenersStatus ^= (1 << deviceRole);
2587 if (mDeviceRoleListenersStatus != 0) {
2588 // There are some other device role changed listeners active.
2589 return AudioSystem.SUCCESS;
2590 }
2591 try {
2592 getService().unregisterCapturePresetDevicesRoleDispatcher(
2593 mDevicesRoleForCapturePresetDispatcherStub);
2594 } catch (RemoteException e) {
2595 throw e.rethrowFromSystemServer();
2596 }
2597 }
2598 }
2599 }
2600 return AudioSystem.SUCCESS;
2601 }
2602
Cole Faust7da659b2022-10-15 21:33:29 -07002603 private final Map<Integer, Object> mDevRoleForCapturePresetListeners = Map.of(
2604 AudioSystem.DEVICE_ROLE_PREFERRED,
2605 new DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>());
Jiabin Huangb55305f2020-09-03 17:54:16 +00002606
2607 private class DevRoleListenerInfo<T> {
2608 final @NonNull Executor mExecutor;
2609 final @NonNull T mListener;
2610 DevRoleListenerInfo(Executor executor, T listener) {
2611 mExecutor = executor;
2612 mListener = listener;
2613 }
2614 }
2615
2616 private class DevRoleListeners<T> {
2617 private final Object mDevRoleListenersLock = new Object();
2618 @GuardedBy("mDevRoleListenersLock")
2619 private @Nullable ArrayList<DevRoleListenerInfo<T>> mListenerInfos;
2620
2621 @GuardedBy("mDevRoleListenersLock")
2622 private @Nullable DevRoleListenerInfo<T> getDevRoleListenerInfo(T listener) {
2623 if (mListenerInfos == null) {
2624 return null;
2625 }
2626 for (DevRoleListenerInfo<T> listenerInfo : mListenerInfos) {
2627 if (listenerInfo.mListener == listener) {
2628 return listenerInfo;
2629 }
2630 }
2631 return null;
2632 }
2633
2634 @GuardedBy("mDevRoleListenersLock")
2635 private boolean hasDevRoleListener(T listener) {
2636 return getDevRoleListenerInfo(listener) != null;
2637 }
2638
2639 @GuardedBy("mDevRoleListenersLock")
2640 private boolean removeDevRoleListener(T listener) {
2641 final DevRoleListenerInfo<T> infoToRemove = getDevRoleListenerInfo(listener);
2642 if (infoToRemove != null) {
2643 mListenerInfos.remove(infoToRemove);
2644 return true;
2645 }
2646 return false;
2647 }
2648 }
2649
2650 private final Object mDevRoleForCapturePresetListenersLock = new Object();
2651 /**
2652 * Record if there is a listener added for device role change. If there is a listener added for
2653 * a specified device role change, the bit at position `1 << device_role` is set.
2654 */
2655 @GuardedBy("mDevRoleForCapturePresetListenersLock")
2656 private int mDeviceRoleListenersStatus = 0;
2657 @GuardedBy("mDevRoleForCapturePresetListenersLock")
2658 private CapturePresetDevicesRoleDispatcherStub mDevicesRoleForCapturePresetDispatcherStub;
2659
2660 private final class CapturePresetDevicesRoleDispatcherStub
2661 extends ICapturePresetDevicesRoleDispatcher.Stub {
2662
2663 @Override
2664 public void dispatchDevicesRoleChanged(
2665 int capturePreset, int role, List<AudioDeviceAttributes> devices) {
2666 final Object listenersObj = mDevRoleForCapturePresetListeners.get(role);
2667 if (listenersObj == null) {
2668 return;
2669 }
2670 switch (role) {
2671 case AudioSystem.DEVICE_ROLE_PREFERRED: {
2672 final DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>
2673 listeners =
2674 (DevRoleListeners<OnPreferredDevicesForCapturePresetChangedListener>)
2675 listenersObj;
2676 final ArrayList<DevRoleListenerInfo<
2677 OnPreferredDevicesForCapturePresetChangedListener>> prefDevListeners;
2678 synchronized (listeners.mDevRoleListenersLock) {
2679 if (listeners.mListenerInfos.isEmpty()) {
2680 return;
2681 }
2682 prefDevListeners = (ArrayList<DevRoleListenerInfo<
2683 OnPreferredDevicesForCapturePresetChangedListener>>)
2684 listeners.mListenerInfos.clone();
2685 }
2686 final long ident = Binder.clearCallingIdentity();
2687 try {
2688 for (DevRoleListenerInfo<
2689 OnPreferredDevicesForCapturePresetChangedListener> info :
2690 prefDevListeners) {
2691 info.mExecutor.execute(() ->
2692 info.mListener.onPreferredDevicesForCapturePresetChanged(
2693 capturePreset, devices));
2694 }
2695 } finally {
2696 Binder.restoreCallingIdentity(ident);
2697 }
2698 } break;
2699 default:
2700 break;
2701 }
2702 }
2703 }
2704
2705 //====================================================================
jiabine22f6aa2021-12-10 01:09:02 +00002706 // Direct playback query
2707
2708 /** Return value for {@link #getDirectPlaybackSupport(AudioFormat, AudioAttributes)}:
2709 direct playback not supported. */
2710 public static final int DIRECT_PLAYBACK_NOT_SUPPORTED = AudioSystem.DIRECT_NOT_SUPPORTED;
2711 /** Return value for {@link #getDirectPlaybackSupport(AudioFormat, AudioAttributes)}:
2712 direct offload playback supported. Compressed offload is a variant of direct playback.
2713 It is the feature that allows audio processing tasks to be done on the Android device but
2714 not on the application processor, instead, it is handled by dedicated hardware such as audio
2715 DSPs. That will allow the application processor to be idle as much as possible, which is
2716 good for power saving. Compressed offload playback supports
2717 {@link AudioTrack.StreamEventCallback} for event notifications. */
2718 public static final int DIRECT_PLAYBACK_OFFLOAD_SUPPORTED =
2719 AudioSystem.DIRECT_OFFLOAD_SUPPORTED;
2720 /** Return value for {@link #getDirectPlaybackSupport(AudioFormat, AudioAttributes)}:
2721 direct offload playback supported with gapless transitions. Compressed offload is a variant
2722 of direct playback. It is the feature that allows audio processing tasks to be done on the
2723 Android device but not on the application processor, instead, it is handled by dedicated
2724 hardware such as audio DSPs. That will allow the application processor to be idle as much as
2725 possible, which is good for power saving. Compressed offload playback supports
2726 {@link AudioTrack.StreamEventCallback} for event notifications. Gapless transitions
2727 indicates the ability to play consecutive audio tracks without an audio silence in
2728 between. */
2729 public static final int DIRECT_PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED =
2730 AudioSystem.DIRECT_OFFLOAD_GAPLESS_SUPPORTED;
2731 /** Return value for {@link #getDirectPlaybackSupport(AudioFormat, AudioAttributes)}:
2732 direct playback supported. This value covers direct playback that is bitstream pass-through
2733 such as compressed pass-through. */
2734 public static final int DIRECT_PLAYBACK_BITSTREAM_SUPPORTED =
2735 AudioSystem.DIRECT_BITSTREAM_SUPPORTED;
2736
2737 /** @hide */
2738 @IntDef(flag = true, prefix = "DIRECT_PLAYBACK_", value = {
2739 DIRECT_PLAYBACK_NOT_SUPPORTED,
2740 DIRECT_PLAYBACK_OFFLOAD_SUPPORTED,
2741 DIRECT_PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED,
2742 DIRECT_PLAYBACK_BITSTREAM_SUPPORTED}
2743 )
2744 @Retention(RetentionPolicy.SOURCE)
2745 public @interface AudioDirectPlaybackMode {}
2746
2747 /**
2748 * Returns a bitfield representing the different forms of direct playback currently available
2749 * for a given audio format.
2750 * <p>Direct playback means that the audio stream is not altered by the framework. The audio
2751 * stream will not be resampled, volume scaled, downmixed or mixed with other content by
2752 * the framework. But it may be wrapped in a higher level protocol such as IEC61937 for
2753 * passthrough.
2754 * <p>Checking for direct support can help the app select the representation of audio content
2755 * that most closely matches the capabilities of the device and peripherals (e.g. A/V receiver)
2756 * connected to it. Note that the provided stream can still be re-encoded or mixed with other
2757 * streams, if needed.
2758 * @param format the audio format (codec, sample rate, channels) being checked.
2759 * @param attributes the {@link AudioAttributes} to be used for playback
2760 * @return the direct playback mode available with given format and attributes. The returned
2761 * value will be {@link #DIRECT_PLAYBACK_NOT_SUPPORTED} or a combination of
2762 * {@link #DIRECT_PLAYBACK_OFFLOAD_SUPPORTED},
2763 * {@link #DIRECT_PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED} and
2764 * {@link #DIRECT_PLAYBACK_BITSTREAM_SUPPORTED}. Note that if
2765 * {@link #DIRECT_PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED} is present in the returned value,
2766 * then {@link #DIRECT_PLAYBACK_OFFLOAD_SUPPORTED} will be too.
2767 */
2768 @AudioDirectPlaybackMode
2769 public static int getDirectPlaybackSupport(@NonNull AudioFormat format,
2770 @NonNull AudioAttributes attributes) {
2771 Objects.requireNonNull(format);
2772 Objects.requireNonNull(attributes);
2773 return AudioSystem.getDirectPlaybackSupport(format, attributes);
2774 }
2775
2776 //====================================================================
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -08002777 // Offload query
2778 /**
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -08002779 * Returns whether offloaded playback of an audio format is supported on the device.
Jean-Michel Trivi03f51392018-08-31 15:47:13 -07002780 * <p>Offloaded playback is the feature where the decoding and playback of an audio stream
2781 * is not competing with other software resources. In general, it is supported by dedicated
2782 * hardware, such as audio DSPs.
2783 * <p>Note that this query only provides information about the support of an audio format,
2784 * it does not indicate whether the resources necessary for the offloaded playback are
2785 * available at that instant.
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -08002786 * @param format the audio format (codec, sample rate, channels) being checked.
Jean-Michel Trivif3fd84a2019-03-21 16:08:50 -07002787 * @param attributes the {@link AudioAttributes} to be used for playback
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -08002788 * @return true if the given audio format can be offloaded.
2789 */
Jean-Michel Trivif3fd84a2019-03-21 16:08:50 -07002790 public static boolean isOffloadedPlaybackSupported(@NonNull AudioFormat format,
2791 @NonNull AudioAttributes attributes) {
Jean-Michel Trivi03f51392018-08-31 15:47:13 -07002792 if (format == null) {
Jean-Michel Trivif3fd84a2019-03-21 16:08:50 -07002793 throw new NullPointerException("Illegal null AudioFormat");
Jean-Michel Trivi03f51392018-08-31 15:47:13 -07002794 }
Jean-Michel Trivif3fd84a2019-03-21 16:08:50 -07002795 if (attributes == null) {
2796 throw new NullPointerException("Illegal null AudioAttributes");
2797 }
Eric Laurentba3b3a62020-11-26 20:10:51 +01002798 return AudioSystem.getOffloadSupport(format, attributes) != PLAYBACK_OFFLOAD_NOT_SUPPORTED;
2799 }
2800
2801 /** Return value for {@link #getPlaybackOffloadSupport(AudioFormat, AudioAttributes)}:
2802 offload playback not supported */
2803 public static final int PLAYBACK_OFFLOAD_NOT_SUPPORTED = AudioSystem.OFFLOAD_NOT_SUPPORTED;
2804 /** Return value for {@link #getPlaybackOffloadSupport(AudioFormat, AudioAttributes)}:
2805 offload playback supported */
2806 public static final int PLAYBACK_OFFLOAD_SUPPORTED = AudioSystem.OFFLOAD_SUPPORTED;
2807 /** Return value for {@link #getPlaybackOffloadSupport(AudioFormat, AudioAttributes)}:
2808 offload playback supported with gapless transitions */
2809 public static final int PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED =
2810 AudioSystem.OFFLOAD_GAPLESS_SUPPORTED;
2811
2812 /** @hide */
2813 @IntDef(flag = false, prefix = "PLAYBACK_OFFLOAD_", value = {
2814 PLAYBACK_OFFLOAD_NOT_SUPPORTED,
2815 PLAYBACK_OFFLOAD_SUPPORTED,
2816 PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED }
2817 )
2818 @Retention(RetentionPolicy.SOURCE)
2819 public @interface AudioOffloadMode {}
2820
2821 /**
2822 * Returns whether offloaded playback of an audio format is supported on the device or not and
2823 * when supported whether gapless transitions are possible or not.
2824 * <p>Offloaded playback is the feature where the decoding and playback of an audio stream
2825 * is not competing with other software resources. In general, it is supported by dedicated
2826 * hardware, such as audio DSPs.
2827 * <p>Note that this query only provides information about the support of an audio format,
2828 * it does not indicate whether the resources necessary for the offloaded playback are
2829 * available at that instant.
2830 * @param format the audio format (codec, sample rate, channels) being checked.
2831 * @param attributes the {@link AudioAttributes} to be used for playback
2832 * @return {@link #PLAYBACK_OFFLOAD_NOT_SUPPORTED} if offload playback if not supported,
2833 * {@link #PLAYBACK_OFFLOAD_SUPPORTED} if offload playback is supported or
2834 * {@link #PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED} if gapless transitions are
2835 * also supported.
jiabine22f6aa2021-12-10 01:09:02 +00002836 * @deprecated Use {@link #getDirectPlaybackSupport(AudioFormat, AudioAttributes)} instead
Eric Laurentba3b3a62020-11-26 20:10:51 +01002837 */
jiabine22f6aa2021-12-10 01:09:02 +00002838 @Deprecated
Eric Laurentba3b3a62020-11-26 20:10:51 +01002839 @AudioOffloadMode
2840 public static int getPlaybackOffloadSupport(@NonNull AudioFormat format,
2841 @NonNull AudioAttributes attributes) {
2842 if (format == null) {
2843 throw new NullPointerException("Illegal null AudioFormat");
2844 }
2845 if (attributes == null) {
2846 throw new NullPointerException("Illegal null AudioAttributes");
2847 }
2848 return AudioSystem.getOffloadSupport(format, attributes);
Jean-Michel Trivi980d38f2018-01-08 15:43:35 -08002849 }
2850
2851 //====================================================================
Jean-Michel Trivie9dd14e2021-07-12 11:10:18 -07002852 // Immersive audio
2853
2854 /**
2855 * Return a handle to the optional platform's {@link Spatializer}
Jean-Michel Trivi838913c2021-09-02 20:55:44 -07002856 * @return the {@code Spatializer} instance.
Jean-Michel Trivic5f69572021-09-14 10:46:55 -07002857 * @see Spatializer#getImmersiveAudioLevel() to check for the level of support of the effect
2858 * on the platform
Jean-Michel Trivie9dd14e2021-07-12 11:10:18 -07002859 */
Jean-Michel Trivic5f69572021-09-14 10:46:55 -07002860 public @NonNull Spatializer getSpatializer() {
Jean-Michel Trivie9dd14e2021-07-12 11:10:18 -07002861 return new Spatializer(this);
2862 }
2863
2864 //====================================================================
Eric Laurent3def1ee2010-03-17 23:26:26 -07002865 // Bluetooth SCO control
2866 /**
Glenn Kastena24e9912017-08-25 08:14:08 -07002867 * Sticky broadcast intent action indicating that the Bluetooth SCO audio
Eric Laurent95b88fb2010-03-18 20:35:49 -07002868 * connection state has changed. The intent contains on extra {@link #EXTRA_SCO_AUDIO_STATE}
Eric Laurent3def1ee2010-03-17 23:26:26 -07002869 * indicating the new state which is either {@link #SCO_AUDIO_STATE_DISCONNECTED}
2870 * or {@link #SCO_AUDIO_STATE_CONNECTED}
2871 *
2872 * @see #startBluetoothSco()
Eric Laurentdc03c612011-04-01 10:59:41 -07002873 * @deprecated Use {@link #ACTION_SCO_AUDIO_STATE_UPDATED} instead
Eric Laurent3def1ee2010-03-17 23:26:26 -07002874 */
Eric Laurentdc03c612011-04-01 10:59:41 -07002875 @Deprecated
Eric Laurent3def1ee2010-03-17 23:26:26 -07002876 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
2877 public static final String ACTION_SCO_AUDIO_STATE_CHANGED =
2878 "android.media.SCO_AUDIO_STATE_CHANGED";
Eric Laurentdc03c612011-04-01 10:59:41 -07002879
2880 /**
Glenn Kastena24e9912017-08-25 08:14:08 -07002881 * Sticky broadcast intent action indicating that the Bluetooth SCO audio
Eric Laurentdc03c612011-04-01 10:59:41 -07002882 * connection state has been updated.
2883 * <p>This intent has two extras:
2884 * <ul>
2885 * <li> {@link #EXTRA_SCO_AUDIO_STATE} - The new SCO audio state. </li>
2886 * <li> {@link #EXTRA_SCO_AUDIO_PREVIOUS_STATE}- The previous SCO audio state. </li>
2887 * </ul>
2888 * <p> EXTRA_SCO_AUDIO_STATE or EXTRA_SCO_AUDIO_PREVIOUS_STATE can be any of:
2889 * <ul>
2890 * <li> {@link #SCO_AUDIO_STATE_DISCONNECTED}, </li>
2891 * <li> {@link #SCO_AUDIO_STATE_CONNECTING} or </li>
2892 * <li> {@link #SCO_AUDIO_STATE_CONNECTED}, </li>
2893 * </ul>
2894 * @see #startBluetoothSco()
2895 */
2896 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
2897 public static final String ACTION_SCO_AUDIO_STATE_UPDATED =
2898 "android.media.ACTION_SCO_AUDIO_STATE_UPDATED";
2899
Eric Laurent3def1ee2010-03-17 23:26:26 -07002900 /**
Eric Laurentdc03c612011-04-01 10:59:41 -07002901 * Extra for intent {@link #ACTION_SCO_AUDIO_STATE_CHANGED} or
2902 * {@link #ACTION_SCO_AUDIO_STATE_UPDATED} containing the new bluetooth SCO connection state.
Eric Laurent3def1ee2010-03-17 23:26:26 -07002903 */
2904 public static final String EXTRA_SCO_AUDIO_STATE =
2905 "android.media.extra.SCO_AUDIO_STATE";
2906
2907 /**
Eric Laurentdc03c612011-04-01 10:59:41 -07002908 * Extra for intent {@link #ACTION_SCO_AUDIO_STATE_UPDATED} containing the previous
2909 * bluetooth SCO connection state.
2910 */
2911 public static final String EXTRA_SCO_AUDIO_PREVIOUS_STATE =
2912 "android.media.extra.SCO_AUDIO_PREVIOUS_STATE";
2913
2914 /**
2915 * Value for extra EXTRA_SCO_AUDIO_STATE or EXTRA_SCO_AUDIO_PREVIOUS_STATE
2916 * indicating that the SCO audio channel is not established
Eric Laurent3def1ee2010-03-17 23:26:26 -07002917 */
2918 public static final int SCO_AUDIO_STATE_DISCONNECTED = 0;
2919 /**
Eric Laurentdc03c612011-04-01 10:59:41 -07002920 * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} or {@link #EXTRA_SCO_AUDIO_PREVIOUS_STATE}
2921 * indicating that the SCO audio channel is established
Eric Laurent3def1ee2010-03-17 23:26:26 -07002922 */
2923 public static final int SCO_AUDIO_STATE_CONNECTED = 1;
2924 /**
Eric Laurentdc03c612011-04-01 10:59:41 -07002925 * Value for extra EXTRA_SCO_AUDIO_STATE or EXTRA_SCO_AUDIO_PREVIOUS_STATE
2926 * indicating that the SCO audio channel is being established
2927 */
2928 public static final int SCO_AUDIO_STATE_CONNECTING = 2;
2929 /**
2930 * Value for extra EXTRA_SCO_AUDIO_STATE indicating that
Eric Laurent3def1ee2010-03-17 23:26:26 -07002931 * there was an error trying to obtain the state
2932 */
2933 public static final int SCO_AUDIO_STATE_ERROR = -1;
2934
2935
2936 /**
Eric Laurent3def1ee2010-03-17 23:26:26 -07002937 * Indicates if current platform supports use of SCO for off call use cases.
2938 * Application wanted to use bluetooth SCO audio when the phone is not in call
Jean-Michel Trivi2ac2afe2012-08-21 11:16:55 -07002939 * must first call this method to make sure that the platform supports this
Eric Laurent3def1ee2010-03-17 23:26:26 -07002940 * feature.
2941 * @return true if bluetooth SCO can be used for audio when not in call
2942 * false otherwise
2943 * @see #startBluetoothSco()
2944 */
2945 public boolean isBluetoothScoAvailableOffCall() {
Marco Nelissen29f16932015-04-17 09:50:56 -07002946 return getContext().getResources().getBoolean(
Eric Laurent3def1ee2010-03-17 23:26:26 -07002947 com.android.internal.R.bool.config_bluetooth_sco_off_call);
2948 }
2949
2950 /**
Eric Laurent3def1ee2010-03-17 23:26:26 -07002951 * Start bluetooth SCO audio connection.
2952 * <p>Requires Permission:
Jean-Michel Trivic4557822023-01-23 18:19:52 +00002953 * {@link Manifest.permission#MODIFY_AUDIO_SETTINGS}.
Eric Laurent3def1ee2010-03-17 23:26:26 -07002954 * <p>This method can be used by applications wanting to send and received audio
2955 * to/from a bluetooth SCO headset while the phone is not in call.
2956 * <p>As the SCO connection establishment can take several seconds,
2957 * applications should not rely on the connection to be available when the method
Eric Laurentdc03c612011-04-01 10:59:41 -07002958 * returns but instead register to receive the intent {@link #ACTION_SCO_AUDIO_STATE_UPDATED}
Eric Laurent3def1ee2010-03-17 23:26:26 -07002959 * and wait for the state to be {@link #SCO_AUDIO_STATE_CONNECTED}.
Eric Laurentdc03c612011-04-01 10:59:41 -07002960 * <p>As the ACTION_SCO_AUDIO_STATE_UPDATED intent is sticky, the application can check the SCO
2961 * audio state before calling startBluetoothSco() by reading the intent returned by the receiver
2962 * registration. If the state is already CONNECTED, no state change will be received via the
2963 * intent after calling startBluetoothSco(). It is however useful to call startBluetoothSco()
2964 * so that the connection stays active in case the current initiator stops the connection.
2965 * <p>Unless the connection is already active as described above, the state will always
2966 * transition from DISCONNECTED to CONNECTING and then either to CONNECTED if the connection
2967 * succeeds or back to DISCONNECTED if the connection fails (e.g no headset is connected).
2968 * <p>When finished with the SCO connection or if the establishment fails, the application must
2969 * call {@link #stopBluetoothSco()} to clear the request and turn down the bluetooth connection.
Eric Laurent3def1ee2010-03-17 23:26:26 -07002970 * <p>Even if a SCO connection is established, the following restrictions apply on audio
2971 * output streams so that they can be routed to SCO headset:
Eric Laurentdc03c612011-04-01 10:59:41 -07002972 * <ul>
2973 * <li> the stream type must be {@link #STREAM_VOICE_CALL} </li>
2974 * <li> the format must be mono </li>
2975 * <li> the sampling must be 16kHz or 8kHz </li>
2976 * </ul>
Eric Laurent3def1ee2010-03-17 23:26:26 -07002977 * <p>The following restrictions apply on input streams:
Eric Laurentdc03c612011-04-01 10:59:41 -07002978 * <ul>
2979 * <li> the format must be mono </li>
2980 * <li> the sampling must be 8kHz </li>
2981 * </ul>
Eric Laurent3def1ee2010-03-17 23:26:26 -07002982 * <p>Note that the phone application always has the priority on the usage of the SCO
2983 * connection for telephony. If this method is called while the phone is in call
2984 * it will be ignored. Similarly, if a call is received or sent while an application
2985 * is using the SCO connection, the connection will be lost for the application and NOT
2986 * returned automatically when the call ends.
Eric Laurent83900752014-05-15 15:14:22 -07002987 * <p>NOTE: up to and including API version
2988 * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method initiates a virtual
2989 * voice call to the bluetooth headset.
2990 * After API version {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} only a raw SCO audio
2991 * connection is established.
Eric Laurent3def1ee2010-03-17 23:26:26 -07002992 * @see #stopBluetoothSco()
Eric Laurentdc03c612011-04-01 10:59:41 -07002993 * @see #ACTION_SCO_AUDIO_STATE_UPDATED
Eric Laurentf23f1b72022-02-18 10:57:54 +01002994 * @deprecated Use {@link AudioManager#setCommunicationDevice(AudioDeviceInfo)} instead.
Eric Laurent3def1ee2010-03-17 23:26:26 -07002995 */
Eric Laurentf23f1b72022-02-18 10:57:54 +01002996 @Deprecated public void startBluetoothSco() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07002997 final IAudioService service = getService();
Eric Laurent3def1ee2010-03-17 23:26:26 -07002998 try {
Marco Nelissen926ebb82015-03-11 09:59:49 -07002999 service.startBluetoothSco(mICallBack,
Marco Nelissen29f16932015-04-17 09:50:56 -07003000 getContext().getApplicationInfo().targetSdkVersion);
Eric Laurent3def1ee2010-03-17 23:26:26 -07003001 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07003002 throw e.rethrowFromSystemServer();
Eric Laurent3def1ee2010-03-17 23:26:26 -07003003 }
3004 }
3005
3006 /**
Jean-Michel Trivi0daab222014-07-18 15:31:29 -07003007 * @hide
Eric Laurent83900752014-05-15 15:14:22 -07003008 * Start bluetooth SCO audio connection in virtual call mode.
3009 * <p>Requires Permission:
Jean-Michel Trivic4557822023-01-23 18:19:52 +00003010 * {@link Manifest.permission#MODIFY_AUDIO_SETTINGS}.
Eric Laurent83900752014-05-15 15:14:22 -07003011 * <p>Similar to {@link #startBluetoothSco()} with explicit selection of virtual call mode.
3012 * Telephony and communication applications (VoIP, Video Chat) should preferably select
3013 * virtual call mode.
3014 * Applications using voice input for search or commands should first try raw audio connection
3015 * with {@link #startBluetoothSco()} and fall back to startBluetoothScoVirtualCall() in case of
3016 * failure.
3017 * @see #startBluetoothSco()
3018 * @see #stopBluetoothSco()
3019 * @see #ACTION_SCO_AUDIO_STATE_UPDATED
3020 */
Mathew Inwood8e742f92020-10-27 11:47:29 +00003021 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Eric Laurent83900752014-05-15 15:14:22 -07003022 public void startBluetoothScoVirtualCall() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07003023 final IAudioService service = getService();
Eric Laurent83900752014-05-15 15:14:22 -07003024 try {
3025 service.startBluetoothScoVirtualCall(mICallBack);
3026 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07003027 throw e.rethrowFromSystemServer();
Eric Laurent83900752014-05-15 15:14:22 -07003028 }
3029 }
3030
3031 /**
Eric Laurent3def1ee2010-03-17 23:26:26 -07003032 * Stop bluetooth SCO audio connection.
3033 * <p>Requires Permission:
Jean-Michel Trivic4557822023-01-23 18:19:52 +00003034 * {@link Manifest.permission#MODIFY_AUDIO_SETTINGS}.
Eric Laurent3def1ee2010-03-17 23:26:26 -07003035 * <p>This method must be called by applications having requested the use of
Jean-Michel Trivi0daab222014-07-18 15:31:29 -07003036 * bluetooth SCO audio with {@link #startBluetoothSco()} when finished with the SCO
3037 * connection or if connection fails.
Eric Laurent3def1ee2010-03-17 23:26:26 -07003038 * @see #startBluetoothSco()
Eric Laurentf23f1b72022-02-18 10:57:54 +01003039 * @deprecated Use {@link AudioManager#clearCommunicationDevice()} instead.
Eric Laurent3def1ee2010-03-17 23:26:26 -07003040 */
Jean-Michel Trivi0daab222014-07-18 15:31:29 -07003041 // Also used for connections started with {@link #startBluetoothScoVirtualCall()}
Eric Laurentf23f1b72022-02-18 10:57:54 +01003042 @Deprecated public void stopBluetoothSco() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07003043 final IAudioService service = getService();
Eric Laurent3def1ee2010-03-17 23:26:26 -07003044 try {
3045 service.stopBluetoothSco(mICallBack);
3046 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07003047 throw e.rethrowFromSystemServer();
Eric Laurent3def1ee2010-03-17 23:26:26 -07003048 }
3049 }
3050
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003051 /**
Eric Laurenta553c252009-07-17 12:17:14 -07003052 * Request use of Bluetooth SCO headset for communications.
Jean-Michel Trivifccb25d2009-09-15 16:06:10 -07003053 * <p>
3054 * This method should only be used by applications that replace the platform-wide
3055 * management of audio settings or the main telephony application.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003056 *
Eric Laurenta553c252009-07-17 12:17:14 -07003057 * @param on set <var>true</var> to use bluetooth SCO for communications;
3058 * <var>false</var> to not use bluetooth SCO for communications
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003059 */
3060 public void setBluetoothScoOn(boolean on){
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07003061 final IAudioService service = getService();
Eric Laurentc42ac9d2009-07-29 08:53:03 -07003062 try {
3063 service.setBluetoothScoOn(on);
3064 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07003065 throw e.rethrowFromSystemServer();
Eric Laurenta553c252009-07-17 12:17:14 -07003066 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003067 }
3068
3069 /**
Eric Laurenta553c252009-07-17 12:17:14 -07003070 * Checks whether communications use Bluetooth SCO.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003071 *
Eric Laurenta553c252009-07-17 12:17:14 -07003072 * @return true if SCO is used for communications;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003073 * false if otherwise
Eric Laurentf23f1b72022-02-18 10:57:54 +01003074 * @deprecated Use {@link AudioManager#getCommunicationDevice()} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003075 */
Eric Laurentf23f1b72022-02-18 10:57:54 +01003076 @Deprecated public boolean isBluetoothScoOn() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07003077 final IAudioService service = getService();
Eric Laurentc42ac9d2009-07-29 08:53:03 -07003078 try {
3079 return service.isBluetoothScoOn();
3080 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07003081 throw e.rethrowFromSystemServer();
Eric Laurenta553c252009-07-17 12:17:14 -07003082 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003083 }
3084
3085 /**
Santiago Seifert1d8f6172022-08-25 14:34:44 +00003086 * @deprecated Use {@link MediaRouter#selectRoute} instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003087 */
Eric Laurenta553c252009-07-17 12:17:14 -07003088 @Deprecated public void setBluetoothA2dpOn(boolean on){
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003089 }
3090
3091 /**
Eric Laurentc117bea2017-02-07 11:04:18 -08003092 * Checks whether a Bluetooth A2DP audio peripheral is connected or not.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003093 *
Eric Laurentc117bea2017-02-07 11:04:18 -08003094 * @return true if a Bluetooth A2DP peripheral is connected
Eric Laurent242b3382012-06-15 11:48:50 -07003095 * false if otherwise
Eric Laurentc117bea2017-02-07 11:04:18 -08003096 * @deprecated Use {@link AudioManager#getDevices(int)} instead to list available audio devices.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003097 */
3098 public boolean isBluetoothA2dpOn() {
Eric Laurent242b3382012-06-15 11:48:50 -07003099 if (AudioSystem.getDeviceConnectionState(DEVICE_OUT_BLUETOOTH_A2DP,"")
Eric Laurent9656df22016-04-20 16:42:28 -07003100 == AudioSystem.DEVICE_STATE_AVAILABLE) {
3101 return true;
3102 } else if (AudioSystem.getDeviceConnectionState(DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES,"")
3103 == AudioSystem.DEVICE_STATE_AVAILABLE) {
3104 return true;
3105 } else if (AudioSystem.getDeviceConnectionState(DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER,"")
3106 == AudioSystem.DEVICE_STATE_AVAILABLE) {
Eric Laurent242b3382012-06-15 11:48:50 -07003107 return true;
Eric Laurenta553c252009-07-17 12:17:14 -07003108 }
Eric Laurent9656df22016-04-20 16:42:28 -07003109 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003110 }
3111
3112 /**
3113 * Sets audio routing to the wired headset on or off.
3114 *
3115 * @param on set <var>true</var> to route audio to/from wired
3116 * headset; <var>false</var> disable wired headset audio
Eric Laurenta553c252009-07-17 12:17:14 -07003117 * @deprecated Do not use.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003118 */
Eric Laurenta553c252009-07-17 12:17:14 -07003119 @Deprecated public void setWiredHeadsetOn(boolean on){
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003120 }
3121
3122 /**
Eric Laurent497b3fe2011-08-02 17:41:11 -07003123 * Checks whether a wired headset is connected or not.
3124 * <p>This is not a valid indication that audio playback is
3125 * actually over the wired headset as audio routing depends on other conditions.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003126 *
Eric Laurent497b3fe2011-08-02 17:41:11 -07003127 * @return true if a wired headset is connected.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003128 * false if otherwise
Eric Laurentc117bea2017-02-07 11:04:18 -08003129 * @deprecated Use {@link AudioManager#getDevices(int)} instead to list available audio devices.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003130 */
3131 public boolean isWiredHeadsetOn() {
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08003132 if (AudioSystem.getDeviceConnectionState(DEVICE_OUT_WIRED_HEADSET,"")
Eric Laurent6015a972010-02-12 07:41:14 -08003133 == AudioSystem.DEVICE_STATE_UNAVAILABLE &&
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08003134 AudioSystem.getDeviceConnectionState(DEVICE_OUT_WIRED_HEADPHONE,"")
Paul McLean145c9532017-08-04 11:12:19 -06003135 == AudioSystem.DEVICE_STATE_UNAVAILABLE &&
3136 AudioSystem.getDeviceConnectionState(DEVICE_OUT_USB_HEADSET, "")
3137 == AudioSystem.DEVICE_STATE_UNAVAILABLE) {
Eric Laurenta553c252009-07-17 12:17:14 -07003138 return false;
3139 } else {
3140 return true;
3141 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003142 }
3143
3144 /**
3145 * Sets the microphone mute on or off.
Jean-Michel Trivifccb25d2009-09-15 16:06:10 -07003146 * <p>
3147 * This method should only be used by applications that replace the platform-wide
3148 * management of audio settings or the main telephony application.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003149 *
3150 * @param on set <var>true</var> to mute the microphone;
3151 * <var>false</var> to turn mute off
3152 */
Kenny Guy70e0c582015-06-30 19:18:28 +01003153 public void setMicrophoneMute(boolean on) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07003154 final IAudioService service = getService();
Emily Bernier22c921a2014-05-28 11:01:32 -04003155 try {
Kenny Guy70e0c582015-06-30 19:18:28 +01003156 service.setMicrophoneMute(on, getContext().getOpPackageName(),
John Wu4f7e5102021-06-22 17:29:11 +00003157 UserHandle.getCallingUserId(), getContext().getAttributionTag());
Emily Bernier22c921a2014-05-28 11:01:32 -04003158 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07003159 throw e.rethrowFromSystemServer();
Emily Bernier22c921a2014-05-28 11:01:32 -04003160 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003161 }
3162
3163 /**
Dmitry Shmidt3d233932019-09-20 15:52:03 -07003164 * @hide
3165 * Sets the microphone from switch mute on or off.
3166 * <p>
3167 * This method should only be used by InputManager to notify
3168 * Audio Subsystem about Microphone Mute switch state.
3169 *
3170 * @param on set <var>true</var> to mute the microphone;
3171 * <var>false</var> to turn mute off
3172 */
Mathew Inwood8e742f92020-10-27 11:47:29 +00003173 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Dmitry Shmidt3d233932019-09-20 15:52:03 -07003174 public void setMicrophoneMuteFromSwitch(boolean on) {
3175 final IAudioService service = getService();
3176 try {
3177 service.setMicrophoneMuteFromSwitch(on);
3178 } catch (RemoteException e) {
3179 throw e.rethrowFromSystemServer();
3180 }
3181 }
3182
3183 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003184 * Checks whether the microphone mute is on or off.
3185 *
3186 * @return true if microphone is muted, false if it's not
3187 */
3188 public boolean isMicrophoneMute() {
Dmitry Shmidt3d233932019-09-20 15:52:03 -07003189 final IAudioService service = getService();
3190 try {
3191 return service.isMicrophoneMuted();
3192 } catch (RemoteException e) {
3193 throw e.rethrowFromSystemServer();
3194 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003195 }
3196
3197 /**
Jean-Michel Trivi7be17d22017-12-21 18:09:21 -08003198 * Broadcast Action: microphone muting state changed.
3199 *
3200 * You <em>cannot</em> receive this through components declared
3201 * in manifests, only by explicitly registering for it with
3202 * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
3203 * Context.registerReceiver()}.
3204 *
3205 * <p>The intent has no extra values, use {@link #isMicrophoneMute} to check whether the
3206 * microphone is muted.
3207 */
3208 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
3209 public static final String ACTION_MICROPHONE_MUTE_CHANGED =
3210 "android.media.action.MICROPHONE_MUTE_CHANGED";
3211
3212 /**
Jean-Michel Trivi90682ff2019-03-18 15:52:00 -07003213 * Broadcast Action: speakerphone state changed.
3214 *
3215 * You <em>cannot</em> receive this through components declared
3216 * in manifests, only by explicitly registering for it with
3217 * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
3218 * Context.registerReceiver()}.
3219 *
3220 * <p>The intent has no extra values, use {@link #isSpeakerphoneOn} to check whether the
3221 * speakerphone functionality is enabled or not.
3222 */
3223 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
3224 public static final String ACTION_SPEAKERPHONE_STATE_CHANGED =
3225 "android.media.action.SPEAKERPHONE_STATE_CHANGED";
3226
3227 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003228 * Sets the audio mode.
Jean-Michel Trivifccb25d2009-09-15 16:06:10 -07003229 * <p>
3230 * The audio mode encompasses audio routing AND the behavior of
3231 * the telephony layer. Therefore this method should only be used by applications that
3232 * replace the platform-wide management of audio settings or the main telephony application.
3233 * In particular, the {@link #MODE_IN_CALL} mode should only be used by the telephony
3234 * application when it places a phone call, as it will cause signals from the radio layer
3235 * to feed the platform mixer.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003236 *
Eric Laurent2d7197fc2019-11-06 10:17:06 -08003237 * @param mode the requested audio mode.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003238 * Informs the HAL about the current audio state so that
3239 * it can route the audio appropriately.
3240 */
Eric Laurent2d7197fc2019-11-06 10:17:06 -08003241 public void setMode(@AudioMode int mode) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07003242 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003243 try {
Marco Nelissena80ac052015-03-12 16:17:45 -07003244 service.setMode(mode, mICallBack, mApplicationContext.getOpPackageName());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003245 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07003246 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003247 }
3248 }
3249
3250 /**
Eric Laurent1c3408f2021-11-09 12:09:54 +01003251 * This change id controls use of audio modes for call audio redirection.
3252 * @hide
3253 */
3254 @ChangeId
3255 @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
3256 public static final long CALL_REDIRECTION_AUDIO_MODES = 189472651L; // buganizer id
3257
3258 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003259 * Returns the current audio mode.
3260 *
Eric Laurent2d7197fc2019-11-06 10:17:06 -08003261 * @return the current audio mode.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003262 */
Eric Laurent2d7197fc2019-11-06 10:17:06 -08003263 @AudioMode
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003264 public int getMode() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07003265 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003266 try {
Eric Laurent2d7197fc2019-11-06 10:17:06 -08003267 int mode = service.getMode();
3268 int sdk;
3269 try {
3270 sdk = getContext().getApplicationInfo().targetSdkVersion;
3271 } catch (NullPointerException e) {
3272 // some tests don't have a Context
3273 sdk = Build.VERSION.SDK_INT;
3274 }
3275 if (mode == MODE_CALL_SCREENING && sdk <= Build.VERSION_CODES.Q) {
3276 mode = MODE_IN_CALL;
Eric Laurent1c3408f2021-11-09 12:09:54 +01003277 } else if (mode == MODE_CALL_REDIRECT
3278 && !CompatChanges.isChangeEnabled(CALL_REDIRECTION_AUDIO_MODES)) {
3279 mode = MODE_IN_CALL;
3280 } else if (mode == MODE_COMMUNICATION_REDIRECT
3281 && !CompatChanges.isChangeEnabled(CALL_REDIRECTION_AUDIO_MODES)) {
3282 mode = MODE_IN_COMMUNICATION;
Eric Laurent2d7197fc2019-11-06 10:17:06 -08003283 }
3284 return mode;
3285 } catch (RemoteException e) {
3286 throw e.rethrowFromSystemServer();
3287 }
3288 }
3289
3290 /**
Nate Myren08635fe2021-04-20 12:04:39 -07003291 * Interface definition of a callback that is notified when the audio mode changes
3292 */
3293 public interface OnModeChangedListener {
3294 /**
3295 * Called on the listener to indicate that the audio mode has changed
3296 *
3297 * @param mode The current audio mode
3298 */
3299 void onModeChanged(@AudioMode int mode);
3300 }
3301
Nate Myren08635fe2021-04-20 12:04:39 -07003302 /**
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08003303 * manages the OnModeChangedListener listeners and the ModeDispatcherStub
Nate Myren08635fe2021-04-20 12:04:39 -07003304 */
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08003305 private final CallbackUtil.LazyListenerManager<OnModeChangedListener> mModeChangedListenerMgr =
3306 new CallbackUtil.LazyListenerManager();
Nate Myren08635fe2021-04-20 12:04:39 -07003307
Nate Myren08635fe2021-04-20 12:04:39 -07003308
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08003309 final class ModeDispatcherStub extends IAudioModeDispatcher.Stub
3310 implements CallbackUtil.DispatcherStub {
Jean-Michel Trivi91f0baa2021-11-11 16:31:32 -08003311
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08003312 @Override
Jean-Michel Trivi91f0baa2021-11-11 16:31:32 -08003313 public void register(boolean register) {
3314 try {
3315 if (register) {
3316 getService().registerModeDispatcher(this);
3317 } else {
3318 getService().unregisterModeDispatcher(this);
3319 }
3320 } catch (RemoteException e) {
3321 e.rethrowFromSystemServer();
3322 }
3323 }
Nate Myren08635fe2021-04-20 12:04:39 -07003324
3325 @Override
3326 public void dispatchAudioModeChanged(int mode) {
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08003327 mModeChangedListenerMgr.callListeners((listener) -> listener.onModeChanged(mode));
Nate Myren08635fe2021-04-20 12:04:39 -07003328 }
3329 }
3330
Nate Myren08635fe2021-04-20 12:04:39 -07003331 /**
3332 * Adds a listener to be notified of changes to the audio mode.
3333 * See {@link #getMode()}
3334 * @param executor
3335 * @param listener
3336 */
3337 public void addOnModeChangedListener(
3338 @NonNull @CallbackExecutor Executor executor,
3339 @NonNull OnModeChangedListener listener) {
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08003340 mModeChangedListenerMgr.addListener(executor, listener, "addOnModeChangedListener",
3341 () -> new ModeDispatcherStub());
Nate Myren08635fe2021-04-20 12:04:39 -07003342 }
3343
3344 /**
3345 * Removes a previously added listener for changes to audio mode.
3346 * See {@link #getMode()}
3347 * @param listener
3348 */
3349 public void removeOnModeChangedListener(@NonNull OnModeChangedListener listener) {
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08003350 mModeChangedListenerMgr.removeListener(listener, "removeOnModeChangedListener");
Nate Myren08635fe2021-04-20 12:04:39 -07003351 }
3352
3353 /**
Eric Laurent2d7197fc2019-11-06 10:17:06 -08003354 * Indicates if the platform supports a special call screening and call monitoring mode.
3355 * <p>
3356 * When this mode is supported, it is possible to perform call screening and monitoring
3357 * functions while other use cases like music or movie playback are active.
3358 * <p>
3359 * Use {@link #setMode(int)} with mode {@link #MODE_CALL_SCREENING} to place the platform in
3360 * call screening mode.
3361 * <p>
3362 * If call screening mode is not supported, setting mode to
3363 * MODE_CALL_SCREENING will be ignored and will not change current mode reported by
3364 * {@link #getMode()}.
3365 * @return true if call screening mode is supported, false otherwise.
3366 */
3367 public boolean isCallScreeningModeSupported() {
3368 final IAudioService service = getService();
3369 try {
3370 return service.isCallScreeningModeSupported();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003371 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07003372 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003373 }
3374 }
3375
3376 /* modes for setMode/getMode/setRoute/getRoute */
3377 /**
3378 * Audio harware modes.
3379 */
3380 /**
3381 * Invalid audio mode.
3382 */
3383 public static final int MODE_INVALID = AudioSystem.MODE_INVALID;
3384 /**
3385 * Current audio mode. Used to apply audio routing to current mode.
3386 */
3387 public static final int MODE_CURRENT = AudioSystem.MODE_CURRENT;
3388 /**
3389 * Normal audio mode: not ringing and no call established.
3390 */
3391 public static final int MODE_NORMAL = AudioSystem.MODE_NORMAL;
3392 /**
3393 * Ringing audio mode. An incoming is being signaled.
3394 */
3395 public static final int MODE_RINGTONE = AudioSystem.MODE_RINGTONE;
3396 /**
Jean-Michel Trivi8f677d62010-11-15 12:11:32 -08003397 * In call audio mode. A telephony call is established.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003398 */
3399 public static final int MODE_IN_CALL = AudioSystem.MODE_IN_CALL;
Jean-Michel Trivi8f677d62010-11-15 12:11:32 -08003400 /**
Jean-Michel Trivi8f677d62010-11-15 12:11:32 -08003401 * In communication audio mode. An audio/video chat or VoIP call is established.
3402 */
3403 public static final int MODE_IN_COMMUNICATION = AudioSystem.MODE_IN_COMMUNICATION;
Eric Laurent2d7197fc2019-11-06 10:17:06 -08003404 /**
3405 * Call screening in progress. Call is connected and audio is accessible to call
3406 * screening applications but other audio use cases are still possible.
3407 */
3408 public static final int MODE_CALL_SCREENING = AudioSystem.MODE_CALL_SCREENING;
3409
Eric Laurent1c3408f2021-11-09 12:09:54 +01003410 /**
3411 * A telephony call is established and its audio is being redirected to another device.
3412 */
3413 public static final int MODE_CALL_REDIRECT = AudioSystem.MODE_CALL_REDIRECT;
3414
3415 /**
Eric Laurent961cd3a2021-11-17 15:02:24 +01003416 * An audio/video chat or VoIP call is established and its audio is being redirected to another
Eric Laurent1c3408f2021-11-09 12:09:54 +01003417 * device.
3418 */
3419 public static final int MODE_COMMUNICATION_REDIRECT = AudioSystem.MODE_COMMUNICATION_REDIRECT;
3420
Eric Laurent2d7197fc2019-11-06 10:17:06 -08003421 /** @hide */
3422 @IntDef(flag = false, prefix = "MODE_", value = {
3423 MODE_NORMAL,
3424 MODE_RINGTONE,
3425 MODE_IN_CALL,
3426 MODE_IN_COMMUNICATION,
Eric Laurent1c3408f2021-11-09 12:09:54 +01003427 MODE_CALL_SCREENING,
3428 MODE_CALL_REDIRECT,
3429 MODE_COMMUNICATION_REDIRECT}
Eric Laurent2d7197fc2019-11-06 10:17:06 -08003430 )
3431 @Retention(RetentionPolicy.SOURCE)
3432 public @interface AudioMode {}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003433
3434 /* Routing bits for setRouting/getRouting API */
3435 /**
3436 * Routing audio output to earpiece
Dianne Hackborn4a51c202009-08-21 15:14:02 -07003437 * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
3438 * setBluetoothScoOn() methods instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003439 */
Eric Laurenta553c252009-07-17 12:17:14 -07003440 @Deprecated public static final int ROUTE_EARPIECE = AudioSystem.ROUTE_EARPIECE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003441 /**
Dianne Hackborn4a51c202009-08-21 15:14:02 -07003442 * Routing audio output to speaker
3443 * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
3444 * setBluetoothScoOn() methods instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003445 */
Eric Laurenta553c252009-07-17 12:17:14 -07003446 @Deprecated public static final int ROUTE_SPEAKER = AudioSystem.ROUTE_SPEAKER;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003447 /**
3448 * @deprecated use {@link #ROUTE_BLUETOOTH_SCO}
Dianne Hackborn4a51c202009-08-21 15:14:02 -07003449 * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
3450 * setBluetoothScoOn() methods instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003451 */
3452 @Deprecated public static final int ROUTE_BLUETOOTH = AudioSystem.ROUTE_BLUETOOTH_SCO;
3453 /**
3454 * Routing audio output to bluetooth SCO
Dianne Hackborn4a51c202009-08-21 15:14:02 -07003455 * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
3456 * setBluetoothScoOn() methods instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003457 */
Eric Laurenta553c252009-07-17 12:17:14 -07003458 @Deprecated public static final int ROUTE_BLUETOOTH_SCO = AudioSystem.ROUTE_BLUETOOTH_SCO;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003459 /**
3460 * Routing audio output to headset
Dianne Hackborn4a51c202009-08-21 15:14:02 -07003461 * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
3462 * setBluetoothScoOn() methods instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003463 */
Eric Laurenta553c252009-07-17 12:17:14 -07003464 @Deprecated public static final int ROUTE_HEADSET = AudioSystem.ROUTE_HEADSET;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003465 /**
3466 * Routing audio output to bluetooth A2DP
Dianne Hackborn4a51c202009-08-21 15:14:02 -07003467 * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
3468 * setBluetoothScoOn() methods instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003469 */
Eric Laurenta553c252009-07-17 12:17:14 -07003470 @Deprecated public static final int ROUTE_BLUETOOTH_A2DP = AudioSystem.ROUTE_BLUETOOTH_A2DP;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003471 /**
3472 * Used for mask parameter of {@link #setRouting(int,int,int)}.
Dianne Hackborn4a51c202009-08-21 15:14:02 -07003473 * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
3474 * setBluetoothScoOn() methods instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003475 */
Eric Laurenta553c252009-07-17 12:17:14 -07003476 @Deprecated public static final int ROUTE_ALL = AudioSystem.ROUTE_ALL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003477
3478 /**
3479 * Sets the audio routing for a specified mode
3480 *
3481 * @param mode audio mode to change route. E.g., MODE_RINGTONE.
3482 * @param routes bit vector of routes requested, created from one or
3483 * more of ROUTE_xxx types. Set bits indicate that route should be on
3484 * @param mask bit vector of routes to change, created from one or more of
3485 * ROUTE_xxx types. Unset bits indicate the route should be left unchanged
Eric Laurentb9c9d262009-05-06 08:13:20 -07003486 *
3487 * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
Eric Laurenta553c252009-07-17 12:17:14 -07003488 * setBluetoothScoOn() methods instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003489 */
Eric Laurenta553c252009-07-17 12:17:14 -07003490 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003491 public void setRouting(int mode, int routes, int mask) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003492 }
3493
3494 /**
3495 * Returns the current audio routing bit vector for a specified mode.
3496 *
3497 * @param mode audio mode to get route (e.g., MODE_RINGTONE)
3498 * @return an audio route bit vector that can be compared with ROUTE_xxx
3499 * bits
Eric Laurentb9c9d262009-05-06 08:13:20 -07003500 * @deprecated Do not query audio routing directly, use isSpeakerphoneOn(),
3501 * isBluetoothScoOn(), isBluetoothA2dpOn() and isWiredHeadsetOn() methods instead.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003502 */
Eric Laurentb9c9d262009-05-06 08:13:20 -07003503 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003504 public int getRouting(int mode) {
Eric Laurenta553c252009-07-17 12:17:14 -07003505 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003506 }
3507
3508 /**
3509 * Checks whether any music is active.
3510 *
3511 * @return true if any music tracks are active.
3512 */
3513 public boolean isMusicActive() {
Jean-Michel Trivi15e30052020-12-09 11:54:00 -08003514 final IAudioService service = getService();
3515 try {
Eric Laurent89c3a972020-12-16 15:57:56 +01003516 return service.isMusicActive(false /*remotely*/);
Jean-Michel Trivi15e30052020-12-09 11:54:00 -08003517 } catch (RemoteException e) {
3518 throw e.rethrowFromSystemServer();
3519 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003520 }
3521
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07003522 /**
3523 * @hide
Jean-Michel Trivi679d5042013-02-04 16:24:09 -08003524 * Checks whether any music or media is actively playing on a remote device (e.g. wireless
3525 * display). Note that BT audio sinks are not considered remote devices.
3526 * @return true if {@link AudioManager#STREAM_MUSIC} is active on a remote device
3527 */
Mathew Inwood8e742f92020-10-27 11:47:29 +00003528 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Jean-Michel Trivi679d5042013-02-04 16:24:09 -08003529 public boolean isMusicActiveRemotely() {
Eric Laurent89c3a972020-12-16 15:57:56 +01003530 final IAudioService service = getService();
3531 try {
3532 return service.isMusicActive(true /*remotely*/);
3533 } catch (RemoteException e) {
3534 throw e.rethrowFromSystemServer();
3535 }
Jean-Michel Trivi679d5042013-02-04 16:24:09 -08003536 }
3537
3538 /**
3539 * @hide
Jean-Michel Trivi23805662013-07-31 14:19:18 -07003540 * Checks whether the current audio focus is exclusive.
3541 * @return true if the top of the audio focus stack requested focus
3542 * with {@link #AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}
3543 */
3544 public boolean isAudioFocusExclusive() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07003545 final IAudioService service = getService();
Jean-Michel Trivi23805662013-07-31 14:19:18 -07003546 try {
3547 return service.getCurrentAudioFocus() == AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE;
3548 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07003549 throw e.rethrowFromSystemServer();
Jean-Michel Trivi23805662013-07-31 14:19:18 -07003550 }
3551 }
3552
3553 /**
Jean-Michel Trivi8a21f5d2014-06-05 15:03:52 -07003554 * Return a new audio session identifier not associated with any player or effect.
Jean-Michel Trivi9e477e42014-08-25 17:44:28 -07003555 * An audio session identifier is a system wide unique identifier for a set of audio streams
3556 * (one or more mixed together).
3557 * <p>The primary use of the audio session ID is to associate audio effects to audio players,
3558 * such as {@link MediaPlayer} or {@link AudioTrack}: all audio effects sharing the same audio
3559 * session ID will be applied to the mixed audio content of the players that share the same
3560 * audio session.
3561 * <p>This method can for instance be used when creating one of the
3562 * {@link android.media.audiofx.AudioEffect} objects to define the audio session of the effect,
3563 * or to specify a session for a speech synthesis utterance
3564 * in {@link android.speech.tts.TextToSpeech.Engine}.
Jean-Michel Trivi8a21f5d2014-06-05 15:03:52 -07003565 * @return a new unclaimed and unused audio session identifier, or {@link #ERROR} when the
Jean-Michel Trivi9e477e42014-08-25 17:44:28 -07003566 * system failed to generate a new session, a condition in which audio playback or recording
3567 * will subsequently fail as well.
Jean-Michel Trivi8a21f5d2014-06-05 15:03:52 -07003568 */
Dichen Zhangf50b2362018-11-26 13:18:58 -08003569 public int generateAudioSessionId() {
Jean-Michel Trivi8a21f5d2014-06-05 15:03:52 -07003570 int session = AudioSystem.newAudioSessionId();
3571 if (session > 0) {
3572 return session;
3573 } else {
Jean-Michel Trivi289cc8e2014-07-18 18:45:32 -07003574 Log.e(TAG, "Failure to generate a new audio session ID");
Jean-Michel Trivi8a21f5d2014-06-05 15:03:52 -07003575 return ERROR;
3576 }
3577 }
3578
Jean-Michel Trivi289cc8e2014-07-18 18:45:32 -07003579 /**
3580 * A special audio session ID to indicate that the audio session ID isn't known and the
3581 * framework should generate a new value. This can be used when building a new
3582 * {@link AudioTrack} instance with
3583 * {@link AudioTrack#AudioTrack(AudioAttributes, AudioFormat, int, int, int)}.
3584 */
3585 public static final int AUDIO_SESSION_ID_GENERATE = AudioSystem.AUDIO_SESSION_ALLOCATE;
3586
Jean-Michel Trivi8a21f5d2014-06-05 15:03:52 -07003587
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003588 /*
3589 * Sets a generic audio configuration parameter. The use of these parameters
3590 * are platform dependant, see libaudio
3591 *
3592 * ** Temporary interface - DO NOT USE
3593 *
3594 * TODO: Replace with a more generic key:value get/set mechanism
3595 *
3596 * param key name of parameter to set. Must not be null.
3597 * param value value of parameter. Must not be null.
3598 */
3599 /**
3600 * @hide
Dianne Hackbornba50b97c2013-04-30 15:04:46 -07003601 * @deprecated Use {@link #setParameters(String)} instead
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003602 */
Eric Laurenta553c252009-07-17 12:17:14 -07003603 @Deprecated public void setParameter(String key, String value) {
3604 setParameters(key+"="+value);
3605 }
3606
3607 /**
3608 * Sets a variable number of parameter values to audio hardware.
3609 *
3610 * @param keyValuePairs list of parameters key value pairs in the form:
3611 * key1=value1;key2=value2;...
3612 *
3613 */
3614 public void setParameters(String keyValuePairs) {
3615 AudioSystem.setParameters(keyValuePairs);
3616 }
3617
3618 /**
William Escandeef429b62021-10-15 18:37:40 +02003619 * @hide
3620 */
3621 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
Jean-Michel Trivic4557822023-01-23 18:19:52 +00003622 @RequiresPermission(Manifest.permission.BLUETOOTH_STACK)
William Escandeef429b62021-10-15 18:37:40 +02003623 public void setHfpEnabled(boolean enable) {
3624 AudioSystem.setParameters("hfp_enable=" + enable);
3625 }
3626
3627 /**
3628 * @hide
3629 */
3630 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
Jean-Michel Trivic4557822023-01-23 18:19:52 +00003631 @RequiresPermission(Manifest.permission.BLUETOOTH_STACK)
William Escandeef429b62021-10-15 18:37:40 +02003632 public void setHfpVolume(int volume) {
3633 AudioSystem.setParameters("hfp_volume=" + volume);
3634 }
3635
3636 /**
3637 * @hide
3638 */
3639 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
Jean-Michel Trivic4557822023-01-23 18:19:52 +00003640 @RequiresPermission(Manifest.permission.BLUETOOTH_STACK)
William Escandeef429b62021-10-15 18:37:40 +02003641 public void setHfpSamplingRate(int rate) {
3642 AudioSystem.setParameters("hfp_set_sampling_rate=" + rate);
3643 }
3644
3645 /**
3646 * @hide
3647 */
3648 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
Jean-Michel Trivic4557822023-01-23 18:19:52 +00003649 @RequiresPermission(Manifest.permission.BLUETOOTH_STACK)
William Escandeef429b62021-10-15 18:37:40 +02003650 public void setBluetoothHeadsetProperties(@NonNull String name, boolean hasNrecEnabled,
3651 boolean hasWbsEnabled) {
3652 AudioSystem.setParameters("bt_headset_name=" + name
3653 + ";bt_headset_nrec=" + (hasNrecEnabled ? "on" : "off")
3654 + ";bt_wbs=" + (hasWbsEnabled ? "on" : "off"));
3655 }
3656
3657 /**
3658 * @hide
3659 */
3660 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
Jean-Michel Trivic4557822023-01-23 18:19:52 +00003661 @RequiresPermission(Manifest.permission.BLUETOOTH_STACK)
William Escandeef429b62021-10-15 18:37:40 +02003662 public void setA2dpSuspended(boolean enable) {
3663 AudioSystem.setParameters("A2dpSuspended=" + enable);
3664 }
3665
3666 /**
Rahul Sabnis215e5412022-12-21 16:03:07 -08003667 * Suspends the use of LE Audio.
3668 *
3669 * @param enable {@code true} to suspend le audio, {@code false} to unsuspend
3670 *
3671 * @hide
3672 */
3673 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
Jean-Michel Trivic4557822023-01-23 18:19:52 +00003674 @RequiresPermission(Manifest.permission.BLUETOOTH_STACK)
Rahul Sabnis215e5412022-12-21 16:03:07 -08003675 public void setLeAudioSuspended(boolean enable) {
3676 AudioSystem.setParameters("LeAudioSuspended=" + enable);
3677 }
3678
3679 /**
John Spurlockaac753d2013-02-22 16:33:32 -05003680 * Gets a variable number of parameter values from audio hardware.
Eric Laurenta553c252009-07-17 12:17:14 -07003681 *
3682 * @param keys list of parameters
3683 * @return list of parameters key value pairs in the form:
3684 * key1=value1;key2=value2;...
3685 */
3686 public String getParameters(String keys) {
3687 return AudioSystem.getParameters(keys);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003688 }
3689
3690 /* Sound effect identifiers */
3691 /**
3692 * Keyboard and direction pad click sound
3693 * @see #playSoundEffect(int)
3694 */
3695 public static final int FX_KEY_CLICK = 0;
3696 /**
3697 * Focus has moved up
3698 * @see #playSoundEffect(int)
3699 */
3700 public static final int FX_FOCUS_NAVIGATION_UP = 1;
3701 /**
3702 * Focus has moved down
3703 * @see #playSoundEffect(int)
3704 */
3705 public static final int FX_FOCUS_NAVIGATION_DOWN = 2;
3706 /**
3707 * Focus has moved left
3708 * @see #playSoundEffect(int)
3709 */
3710 public static final int FX_FOCUS_NAVIGATION_LEFT = 3;
3711 /**
3712 * Focus has moved right
3713 * @see #playSoundEffect(int)
3714 */
3715 public static final int FX_FOCUS_NAVIGATION_RIGHT = 4;
3716 /**
3717 * IME standard keypress sound
3718 * @see #playSoundEffect(int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003719 */
3720 public static final int FX_KEYPRESS_STANDARD = 5;
3721 /**
3722 * IME spacebar keypress sound
3723 * @see #playSoundEffect(int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003724 */
3725 public static final int FX_KEYPRESS_SPACEBAR = 6;
3726 /**
3727 * IME delete keypress sound
3728 * @see #playSoundEffect(int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003729 */
3730 public static final int FX_KEYPRESS_DELETE = 7;
3731 /**
3732 * IME return_keypress sound
3733 * @see #playSoundEffect(int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003734 */
3735 public static final int FX_KEYPRESS_RETURN = 8;
Justin Kohcacfe692013-07-11 17:16:53 -07003736
3737 /**
3738 * Invalid keypress sound
3739 * @see #playSoundEffect(int)
3740 */
3741 public static final int FX_KEYPRESS_INVALID = 9;
Philip Junker7f1fdff2020-12-03 16:10:41 +01003742
3743 /**
3744 * Back sound
3745 * @see #playSoundEffect(int)
3746 */
3747 public static final int FX_BACK = 10;
3748
3749 /**
3750 * @hide Home sound
Philip Junker6d9a0962021-02-18 13:55:54 +01003751 * <p>
3752 * To be played by the framework when the home app becomes active if config_enableHomeSound is
3753 * set to true. This is currently only used on TV devices.
Philip Junker7f1fdff2020-12-03 16:10:41 +01003754 * Note that this sound is only available if a sound file is specified in audio_assets.xml.
3755 * @see #playSoundEffect(int)
3756 */
3757 public static final int FX_HOME = 11;
3758
3759 /**
Philip Junker6d9a0962021-02-18 13:55:54 +01003760 * @hide Navigation repeat sound 1
3761 * <p>
3762 * To be played by the framework when a focus navigation is repeatedly triggered
3763 * (e.g. due to long-pressing) and {@link #areNavigationRepeatSoundEffectsEnabled()} is true.
Philip Junker7f1fdff2020-12-03 16:10:41 +01003764 * This is currently only used on TV devices.
3765 * Note that this sound is only available if a sound file is specified in audio_assets.xml
3766 * @see #playSoundEffect(int)
3767 */
Philip Junker6d9a0962021-02-18 13:55:54 +01003768 public static final int FX_FOCUS_NAVIGATION_REPEAT_1 = 12;
Philip Junker7f1fdff2020-12-03 16:10:41 +01003769
3770 /**
Philip Junker6d9a0962021-02-18 13:55:54 +01003771 * @hide Navigation repeat sound 2
3772 * <p>
3773 * To be played by the framework when a focus navigation is repeatedly triggered
3774 * (e.g. due to long-pressing) and {@link #areNavigationRepeatSoundEffectsEnabled()} is true.
Philip Junker7f1fdff2020-12-03 16:10:41 +01003775 * This is currently only used on TV devices.
3776 * Note that this sound is only available if a sound file is specified in audio_assets.xml
3777 * @see #playSoundEffect(int)
3778 */
Philip Junker6d9a0962021-02-18 13:55:54 +01003779 public static final int FX_FOCUS_NAVIGATION_REPEAT_2 = 13;
Philip Junker7f1fdff2020-12-03 16:10:41 +01003780
3781 /**
Philip Junker6d9a0962021-02-18 13:55:54 +01003782 * @hide Navigation repeat sound 3
3783 * <p>
3784 * To be played by the framework when a focus navigation is repeatedly triggered
3785 * (e.g. due to long-pressing) and {@link #areNavigationRepeatSoundEffectsEnabled()} is true.
Philip Junker7f1fdff2020-12-03 16:10:41 +01003786 * This is currently only used on TV devices.
3787 * Note that this sound is only available if a sound file is specified in audio_assets.xml
3788 * @see #playSoundEffect(int)
3789 */
Philip Junker6d9a0962021-02-18 13:55:54 +01003790 public static final int FX_FOCUS_NAVIGATION_REPEAT_3 = 14;
Philip Junker7f1fdff2020-12-03 16:10:41 +01003791
3792 /**
Philip Junker6d9a0962021-02-18 13:55:54 +01003793 * @hide Navigation repeat sound 4
3794 * <p>
3795 * To be played by the framework when a focus navigation is repeatedly triggered
3796 * (e.g. due to long-pressing) and {@link #areNavigationRepeatSoundEffectsEnabled()} is true.
Philip Junker7f1fdff2020-12-03 16:10:41 +01003797 * This is currently only used on TV devices.
3798 * Note that this sound is only available if a sound file is specified in audio_assets.xml
3799 * @see #playSoundEffect(int)
3800 */
Philip Junker6d9a0962021-02-18 13:55:54 +01003801 public static final int FX_FOCUS_NAVIGATION_REPEAT_4 = 15;
Philip Junker7f1fdff2020-12-03 16:10:41 +01003802
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003803 /**
3804 * @hide Number of sound effects
3805 */
Mathew Inwood8e742f92020-10-27 11:47:29 +00003806 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Philip Junker7f1fdff2020-12-03 16:10:41 +01003807 public static final int NUM_SOUND_EFFECTS = 16;
3808
Philip Junkerdfcb12b2021-03-17 19:38:50 +01003809 /** @hide */
3810 @IntDef(prefix = { "FX_" }, value = {
3811 FX_KEY_CLICK,
3812 FX_FOCUS_NAVIGATION_UP,
3813 FX_FOCUS_NAVIGATION_DOWN,
3814 FX_FOCUS_NAVIGATION_LEFT,
3815 FX_FOCUS_NAVIGATION_RIGHT,
3816 FX_KEYPRESS_STANDARD,
3817 FX_KEYPRESS_SPACEBAR,
3818 FX_KEYPRESS_DELETE,
3819 FX_KEYPRESS_RETURN,
3820 FX_KEYPRESS_INVALID,
3821 FX_BACK
3822 })
3823 @Retention(RetentionPolicy.SOURCE)
3824 public @interface SystemSoundEffect {}
3825
Philip Junker7f1fdff2020-12-03 16:10:41 +01003826 /**
Philip Junker6d9a0962021-02-18 13:55:54 +01003827 * @hide Number of FX_FOCUS_NAVIGATION_REPEAT_* sound effects
Philip Junker7f1fdff2020-12-03 16:10:41 +01003828 */
Philip Junker6d9a0962021-02-18 13:55:54 +01003829 public static final int NUM_NAVIGATION_REPEAT_SOUND_EFFECTS = 4;
Philip Junker7f1fdff2020-12-03 16:10:41 +01003830
3831 /**
3832 * @hide
Philip Junker6d9a0962021-02-18 13:55:54 +01003833 * @param n a value in [0, {@link #NUM_NAVIGATION_REPEAT_SOUND_EFFECTS}[
3834 * @return The id of a navigation repeat sound effect or -1 if out of bounds
Philip Junker7f1fdff2020-12-03 16:10:41 +01003835 */
Philip Junker6d9a0962021-02-18 13:55:54 +01003836 public static int getNthNavigationRepeatSoundEffect(int n) {
Philip Junker7f1fdff2020-12-03 16:10:41 +01003837 switch (n) {
3838 case 0:
Philip Junker6d9a0962021-02-18 13:55:54 +01003839 return FX_FOCUS_NAVIGATION_REPEAT_1;
Philip Junker7f1fdff2020-12-03 16:10:41 +01003840 case 1:
Philip Junker6d9a0962021-02-18 13:55:54 +01003841 return FX_FOCUS_NAVIGATION_REPEAT_2;
Philip Junker7f1fdff2020-12-03 16:10:41 +01003842 case 2:
Philip Junker6d9a0962021-02-18 13:55:54 +01003843 return FX_FOCUS_NAVIGATION_REPEAT_3;
Philip Junker7f1fdff2020-12-03 16:10:41 +01003844 case 3:
Philip Junker6d9a0962021-02-18 13:55:54 +01003845 return FX_FOCUS_NAVIGATION_REPEAT_4;
Philip Junker7f1fdff2020-12-03 16:10:41 +01003846 default:
Philip Junker6d9a0962021-02-18 13:55:54 +01003847 Log.w(TAG, "Invalid navigation repeat sound effect id: " + n);
Philip Junker7f1fdff2020-12-03 16:10:41 +01003848 return -1;
3849 }
3850 }
3851
3852 /**
3853 * @hide
3854 */
Philip Junker6d9a0962021-02-18 13:55:54 +01003855 public void setNavigationRepeatSoundEffectsEnabled(boolean enabled) {
Philip Junker7f1fdff2020-12-03 16:10:41 +01003856 try {
Philip Junker6d9a0962021-02-18 13:55:54 +01003857 getService().setNavigationRepeatSoundEffectsEnabled(enabled);
Philip Junker7f1fdff2020-12-03 16:10:41 +01003858 } catch (RemoteException e) {
3859
3860 }
3861 }
3862
3863 /**
3864 * @hide
Philip Junker6d9a0962021-02-18 13:55:54 +01003865 * @return true if the navigation repeat sound effects are enabled
Philip Junker7f1fdff2020-12-03 16:10:41 +01003866 */
Philip Junker6d9a0962021-02-18 13:55:54 +01003867 public boolean areNavigationRepeatSoundEffectsEnabled() {
Philip Junker7f1fdff2020-12-03 16:10:41 +01003868 try {
Philip Junker6d9a0962021-02-18 13:55:54 +01003869 return getService().areNavigationRepeatSoundEffectsEnabled();
Philip Junker7f1fdff2020-12-03 16:10:41 +01003870 } catch (RemoteException e) {
3871 throw e.rethrowFromSystemServer();
3872 }
3873 }
3874
3875 /**
3876 * @hide
3877 * @param enabled
3878 */
3879 public void setHomeSoundEffectEnabled(boolean enabled) {
3880 try {
3881 getService().setHomeSoundEffectEnabled(enabled);
3882 } catch (RemoteException e) {
3883
3884 }
3885 }
3886
3887 /**
3888 * @hide
3889 * @return true if the home sound effect is enabled
3890 */
3891 public boolean isHomeSoundEffectEnabled() {
3892 try {
3893 return getService().isHomeSoundEffectEnabled();
3894 } catch (RemoteException e) {
3895 throw e.rethrowFromSystemServer();
3896 }
3897 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003898
3899 /**
3900 * Plays a sound effect (Key clicks, lid open/close...)
Philip Junkerdfcb12b2021-03-17 19:38:50 +01003901 * @param effectType The type of sound effect.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003902 * NOTE: This version uses the UI settings to determine
3903 * whether sounds are heard or not.
3904 */
Philip Junkerdfcb12b2021-03-17 19:38:50 +01003905 public void playSoundEffect(@SystemSoundEffect int effectType) {
Oscar Azucena21f2c4d2021-10-26 18:06:54 -07003906 playSoundEffect(effectType, UserHandle.USER_CURRENT);
Jason Monk0c37ba32014-09-08 15:34:23 -04003907 }
3908
3909 /**
3910 * Plays a sound effect (Key clicks, lid open/close...)
Philip Junkerdfcb12b2021-03-17 19:38:50 +01003911 * @param effectType The type of sound effect.
Jason Monk0c37ba32014-09-08 15:34:23 -04003912 * @param userId The current user to pull sound settings from
3913 * NOTE: This version uses the UI settings to determine
3914 * whether sounds are heard or not.
3915 * @hide
3916 */
Jan Sebechlebskyfb48c022022-12-16 16:26:47 +01003917 public void playSoundEffect(@SystemSoundEffect int effectType, int userId) {
Jason Monk0c37ba32014-09-08 15:34:23 -04003918 if (effectType < 0 || effectType >= NUM_SOUND_EFFECTS) {
3919 return;
3920 }
3921
Jan Sebechlebskyfb48c022022-12-16 16:26:47 +01003922 if (delegateSoundEffectToVdm(effectType)) {
3923 return;
3924 }
3925
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07003926 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003927 try {
Oscar Azucena21f2c4d2021-10-26 18:06:54 -07003928 service.playSoundEffect(effectType, userId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003929 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07003930 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003931 }
3932 }
3933
3934 /**
3935 * Plays a sound effect (Key clicks, lid open/close...)
Philip Junkerdfcb12b2021-03-17 19:38:50 +01003936 * @param effectType The type of sound effect.
Eric Laurenta2ef57d2009-09-28 04:46:10 -07003937 * @param volume Sound effect volume.
3938 * The volume value is a raw scalar so UI controls should be scaled logarithmically.
3939 * 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 -08003940 * NOTE: This version is for applications that have their own
3941 * settings panel for enabling and controlling volume.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003942 */
Jan Sebechlebskyfb48c022022-12-16 16:26:47 +01003943 public void playSoundEffect(@SystemSoundEffect int effectType, float volume) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003944 if (effectType < 0 || effectType >= NUM_SOUND_EFFECTS) {
3945 return;
3946 }
3947
Jan Sebechlebskyfb48c022022-12-16 16:26:47 +01003948 if (delegateSoundEffectToVdm(effectType)) {
3949 return;
3950 }
3951
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07003952 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003953 try {
3954 service.playSoundEffectVolume(effectType, volume);
3955 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07003956 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003957 }
3958 }
3959
3960 /**
Jan Sebechlebskyfb48c022022-12-16 16:26:47 +01003961 * Checks whether this {@link AudioManager} instance is asociated with {@link VirtualDevice}
3962 * configured with custom device policy for audio. If there is such device, request to play
3963 * sound effect is forwarded to {@link VirtualDeviceManager}.
3964 *
3965 * @param effectType - The type of sound effect.
3966 * @return true if the request was forwarded to {@link VirtualDeviceManager} instance,
3967 * false otherwise.
3968 */
3969 private boolean delegateSoundEffectToVdm(@SystemSoundEffect int effectType) {
3970 int deviceId = getContext().getDeviceId();
3971 if (deviceId != DEVICE_ID_DEFAULT) {
3972 VirtualDeviceManager vdm = getVirtualDeviceManager();
3973 if (vdm != null && vdm.getDevicePolicy(deviceId, POLICY_TYPE_AUDIO)
3974 != DEVICE_POLICY_DEFAULT) {
3975 vdm.playSoundEffect(deviceId, effectType);
3976 return true;
3977 }
3978 }
3979 return false;
3980 }
3981
3982 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003983 * Load Sound effects.
3984 * This method must be called when sound effects are enabled.
3985 */
3986 public void loadSoundEffects() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07003987 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003988 try {
3989 service.loadSoundEffects();
3990 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07003991 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003992 }
3993 }
3994
3995 /**
3996 * Unload Sound effects.
3997 * This method can be called to free some memory when
3998 * sound effects are disabled.
3999 */
4000 public void unloadSoundEffects() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004001 final IAudioService service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004002 try {
4003 service.unloadSoundEffects();
4004 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07004005 throw e.rethrowFromSystemServer();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004006 }
4007 }
4008
Eric Laurent4050c932009-07-08 02:52:14 -07004009 /**
Andy Hung69836952020-03-26 01:00:15 -07004010 * @hide
4011 */
4012 public static String audioFocusToString(int focus) {
4013 switch (focus) {
4014 case AUDIOFOCUS_NONE:
4015 return "AUDIOFOCUS_NONE";
4016 case AUDIOFOCUS_GAIN:
4017 return "AUDIOFOCUS_GAIN";
4018 case AUDIOFOCUS_GAIN_TRANSIENT:
4019 return "AUDIOFOCUS_GAIN_TRANSIENT";
4020 case AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
4021 return "AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK";
4022 case AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
4023 return "AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE";
4024 case AUDIOFOCUS_LOSS:
4025 return "AUDIOFOCUS_LOSS";
4026 case AUDIOFOCUS_LOSS_TRANSIENT:
4027 return "AUDIOFOCUS_LOSS_TRANSIENT";
4028 case AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: // Note CAN_DUCK not MAY_DUCK.
4029 return "AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK";
4030 default:
4031 return "AUDIO_FOCUS_UNKNOWN(" + focus + ")";
4032 }
4033 }
4034
4035 /**
Jean-Michel Trivi0f49f822017-02-16 14:36:43 -08004036 * Used to indicate no audio focus has been gained or lost, or requested.
Jean-Michel Trivi23805662013-07-31 14:19:18 -07004037 */
4038 public static final int AUDIOFOCUS_NONE = 0;
4039
4040 /**
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004041 * 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 -07004042 * @see OnAudioFocusChangeListener#onAudioFocusChange(int)
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004043 * @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004044 */
4045 public static final int AUDIOFOCUS_GAIN = 1;
4046 /**
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004047 * Used to indicate a temporary gain or request of audio focus, anticipated to last a short
4048 * amount of time. Examples of temporary changes are the playback of driving directions, or an
4049 * event notification.
Jean-Michel Trivi7f7e67f2010-03-31 11:30:24 -07004050 * @see OnAudioFocusChangeListener#onAudioFocusChange(int)
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004051 * @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004052 */
4053 public static final int AUDIOFOCUS_GAIN_TRANSIENT = 2;
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07004054 /**
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07004055 * Used to indicate a temporary request of audio focus, anticipated to last a short
Jean-Michel Trivi983ac2b2010-03-19 12:09:25 -07004056 * amount of time, and where it is acceptable for other audio applications to keep playing
4057 * after having lowered their output level (also referred to as "ducking").
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07004058 * Examples of temporary changes are the playback of driving directions where playback of music
4059 * in the background is acceptable.
Jean-Michel Trivi7f7e67f2010-03-31 11:30:24 -07004060 * @see OnAudioFocusChangeListener#onAudioFocusChange(int)
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07004061 * @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
4062 */
4063 public static final int AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK = 3;
4064 /**
Jean-Michel Trivi23805662013-07-31 14:19:18 -07004065 * Used to indicate a temporary request of audio focus, anticipated to last a short
4066 * amount of time, during which no other applications, or system components, should play
4067 * anything. Examples of exclusive and transient audio focus requests are voice
4068 * memo recording and speech recognition, during which the system shouldn't play any
4069 * notifications, and media playback should have paused.
4070 * @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
4071 */
4072 public static final int AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE = 4;
4073 /**
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07004074 * Used to indicate a loss of audio focus of unknown duration.
Jean-Michel Trivi7f7e67f2010-03-31 11:30:24 -07004075 * @see OnAudioFocusChangeListener#onAudioFocusChange(int)
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07004076 */
4077 public static final int AUDIOFOCUS_LOSS = -1 * AUDIOFOCUS_GAIN;
4078 /**
4079 * Used to indicate a transient loss of audio focus.
Jean-Michel Trivi7f7e67f2010-03-31 11:30:24 -07004080 * @see OnAudioFocusChangeListener#onAudioFocusChange(int)
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07004081 */
4082 public static final int AUDIOFOCUS_LOSS_TRANSIENT = -1 * AUDIOFOCUS_GAIN_TRANSIENT;
4083 /**
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07004084 * 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 -07004085 * lower its output volume if it wants to continue playing (also referred to as "ducking"), as
4086 * the new focus owner doesn't require others to be silent.
Jean-Michel Trivi7f7e67f2010-03-31 11:30:24 -07004087 * @see OnAudioFocusChangeListener#onAudioFocusChange(int)
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07004088 */
4089 public static final int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK =
4090 -1 * AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004091
4092 /**
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004093 * Interface definition for a callback to be invoked when the audio focus of the system is
4094 * updated.
4095 */
4096 public interface OnAudioFocusChangeListener {
4097 /**
4098 * Called on the listener to notify it the audio focus for this listener has been changed.
4099 * The focusChange value indicates whether the focus was gained,
4100 * whether the focus was lost, and whether that loss is transient, or whether the new focus
4101 * holder will hold it for an unknown amount of time.
Jean-Michel Trivi983ac2b2010-03-19 12:09:25 -07004102 * When losing focus, listeners can use the focus change information to decide what
4103 * behavior to adopt when losing focus. A music player could for instance elect to lower
4104 * the volume of its music stream (duck) for transient focus losses, and pause otherwise.
4105 * @param focusChange the type of focus change, one of {@link AudioManager#AUDIOFOCUS_GAIN},
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07004106 * {@link AudioManager#AUDIOFOCUS_LOSS}, {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT}
Jean-Michel Trivi983ac2b2010-03-19 12:09:25 -07004107 * and {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}.
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004108 */
Jean-Michel Trivi7f7e67f2010-03-31 11:30:24 -07004109 public void onAudioFocusChange(int focusChange);
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004110 }
4111
4112 /**
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004113 * Internal class to hold the AudioFocusRequest as well as the Handler for the callback
4114 */
4115 private static class FocusRequestInfo {
4116 @NonNull final AudioFocusRequest mRequest;
4117 @Nullable final Handler mHandler;
4118 FocusRequestInfo(@NonNull AudioFocusRequest afr, @Nullable Handler handler) {
4119 mRequest = afr;
4120 mHandler = handler;
4121 }
4122 }
4123
4124 /**
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004125 * Map to convert focus event listener IDs, as used in the AudioService audio focus stack,
4126 * to actual listener objects.
4127 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01004128 @UnsupportedAppUsage
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004129 private final ConcurrentHashMap<String, FocusRequestInfo> mAudioFocusIdListenerMap =
4130 new ConcurrentHashMap<String, FocusRequestInfo>();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004131
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004132 private FocusRequestInfo findFocusRequestInfo(String id) {
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004133 return mAudioFocusIdListenerMap.get(id);
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004134 }
4135
4136 /**
Jean-Michel Trivi3b61d2d2016-01-07 10:50:09 -08004137 * Handler for events (audio focus change, recording config change) coming from the
4138 * audio service.
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004139 */
Jean-Michel Trivi3b61d2d2016-01-07 10:50:09 -08004140 private final ServiceEventHandlerDelegate mServiceEventHandlerDelegate =
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08004141 new ServiceEventHandlerDelegate(null);
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07004142
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004143 /**
Jean-Michel Trivi3b61d2d2016-01-07 10:50:09 -08004144 * Event types
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004145 */
Jean-Michel Trivi3b61d2d2016-01-07 10:50:09 -08004146 private final static int MSSG_FOCUS_CHANGE = 0;
4147 private final static int MSSG_RECORDING_CONFIG_CHANGE = 1;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -08004148 private final static int MSSG_PLAYBACK_CONFIG_CHANGE = 2;
Jean-Michel Trivi3b61d2d2016-01-07 10:50:09 -08004149
4150 /**
4151 * Helper class to handle the forwarding of audio service events to the appropriate listener
4152 */
4153 private class ServiceEventHandlerDelegate {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004154 private final Handler mHandler;
4155
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08004156 ServiceEventHandlerDelegate(Handler handler) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004157 Looper looper;
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08004158 if (handler == null) {
4159 if ((looper = Looper.myLooper()) == null) {
4160 looper = Looper.getMainLooper();
4161 }
4162 } else {
4163 looper = handler.getLooper();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004164 }
4165
4166 if (looper != null) {
Jean-Michel Trivi3b61d2d2016-01-07 10:50:09 -08004167 // implement the event handler delegate to receive events from audio service
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004168 mHandler = new Handler(looper) {
4169 @Override
4170 public void handleMessage(Message msg) {
Jean-Michel Trivi3b61d2d2016-01-07 10:50:09 -08004171 switch (msg.what) {
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -08004172 case MSSG_FOCUS_CHANGE: {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004173 final FocusRequestInfo fri = findFocusRequestInfo((String)msg.obj);
4174 if (fri != null) {
4175 final OnAudioFocusChangeListener listener =
4176 fri.mRequest.getOnAudioFocusChangeListener();
4177 if (listener != null) {
4178 Log.d(TAG, "dispatching onAudioFocusChange("
4179 + msg.arg1 + ") to " + msg.obj);
4180 listener.onAudioFocusChange(msg.arg1);
4181 }
Jean-Michel Trivi3b61d2d2016-01-07 10:50:09 -08004182 }
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -08004183 } break;
4184 case MSSG_RECORDING_CONFIG_CHANGE: {
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -08004185 final RecordConfigChangeCallbackData cbData =
4186 (RecordConfigChangeCallbackData) msg.obj;
4187 if (cbData.mCb != null) {
Jean-Michel Trivie6a505b2016-04-01 09:56:28 -07004188 cbData.mCb.onRecordingConfigChanged(cbData.mConfigs);
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08004189 }
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -08004190 } break;
4191 case MSSG_PLAYBACK_CONFIG_CHANGE: {
4192 final PlaybackConfigChangeCallbackData cbData =
4193 (PlaybackConfigChangeCallbackData) msg.obj;
4194 if (cbData.mCb != null) {
4195 if (DEBUG) {
4196 Log.d(TAG, "dispatching onPlaybackConfigChanged()");
4197 }
4198 cbData.mCb.onPlaybackConfigChanged(cbData.mConfigs);
4199 }
4200 } break;
Jean-Michel Trivi3b61d2d2016-01-07 10:50:09 -08004201 default:
4202 Log.e(TAG, "Unknown event " + msg.what);
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004203 }
4204 }
4205 };
4206 } else {
4207 mHandler = null;
4208 }
4209 }
4210
4211 Handler getHandler() {
4212 return mHandler;
4213 }
4214 }
4215
Glenn Kasten30c918c2011-11-10 17:56:41 -08004216 private final IAudioFocusDispatcher mAudioFocusDispatcher = new IAudioFocusDispatcher.Stub() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004217 @Override
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004218 public void dispatchAudioFocusChange(int focusChange, String id) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004219 final FocusRequestInfo fri = findFocusRequestInfo(id);
4220 if (fri != null) {
4221 final OnAudioFocusChangeListener listener =
4222 fri.mRequest.getOnAudioFocusChangeListener();
4223 if (listener != null) {
4224 final Handler h = (fri.mHandler == null) ?
4225 mServiceEventHandlerDelegate.getHandler() : fri.mHandler;
4226 final Message m = h.obtainMessage(
4227 MSSG_FOCUS_CHANGE/*what*/, focusChange/*arg1*/, 0/*arg2 ignored*/,
4228 id/*obj*/);
4229 h.sendMessage(m);
4230 }
4231 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004232 }
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004233
4234 @Override
4235 public void dispatchFocusResultFromExtPolicy(int requestResult, String clientId) {
4236 synchronized (mFocusRequestsLock) {
4237 // TODO use generation counter as the key instead
4238 final BlockingFocusResultReceiver focusReceiver =
4239 mFocusRequestsAwaitingResult.remove(clientId);
4240 if (focusReceiver != null) {
4241 focusReceiver.notifyResult(requestResult);
4242 } else {
4243 Log.e(TAG, "dispatchFocusResultFromExtPolicy found no result receiver");
4244 }
4245 }
4246 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004247 };
4248
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004249 private String getIdForAudioFocusListener(OnAudioFocusChangeListener l) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004250 if (l == null) {
Jean-Michel Trivi308e9a52010-03-17 15:04:20 -07004251 return new String(this.toString());
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004252 } else {
4253 return new String(this.toString() + l.toString());
4254 }
4255 }
4256
4257 /**
Xavier Ducrohet5f53f082010-05-11 14:19:40 -07004258 * @hide
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004259 * Registers a listener to be called when audio focus changes and keeps track of the associated
4260 * focus request (including Handler to use for the listener).
4261 * @param afr the full request parameters
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004262 */
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004263 public void registerAudioFocusRequest(@NonNull AudioFocusRequest afr) {
4264 final Handler h = afr.getOnAudioFocusChangeListenerHandler();
4265 final FocusRequestInfo fri = new FocusRequestInfo(afr, (h == null) ? null :
4266 new ServiceEventHandlerDelegate(h).getHandler());
4267 final String key = getIdForAudioFocusListener(afr.getOnAudioFocusChangeListener());
4268 mAudioFocusIdListenerMap.put(key, fri);
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004269 }
4270
4271 /**
Xavier Ducrohet5f53f082010-05-11 14:19:40 -07004272 * @hide
Jean-Michel Trivi46b3a182010-05-11 11:22:54 -07004273 * Causes the specified listener to not be called anymore when focus is gained or lost.
4274 * @param l the listener to unregister.
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004275 */
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004276 public void unregisterAudioFocusRequest(OnAudioFocusChangeListener l) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004277 // remove locally
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004278 mAudioFocusIdListenerMap.remove(getIdForAudioFocusListener(l));
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004279 }
4280
4281
4282 /**
Jean-Michel Trivi46b3a182010-05-11 11:22:54 -07004283 * A failed focus change request.
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004284 */
4285 public static final int AUDIOFOCUS_REQUEST_FAILED = 0;
4286 /**
Jean-Michel Trivi46b3a182010-05-11 11:22:54 -07004287 * A successful focus change request.
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004288 */
4289 public static final int AUDIOFOCUS_REQUEST_GRANTED = 1;
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004290 /**
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004291 * A focus change request whose granting is delayed: the request was successful, but the
4292 * requester will only be granted audio focus once the condition that prevented immediate
4293 * granting has ended.
Jean-Michel Trivi3db31ac2017-02-24 11:40:37 -08004294 * See {@link #requestAudioFocus(AudioFocusRequest)} and
4295 * {@link AudioFocusRequest.Builder#setAcceptsDelayedFocusGain(boolean)}
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004296 */
4297 public static final int AUDIOFOCUS_REQUEST_DELAYED = 2;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004298
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004299 /** @hide */
4300 @IntDef(flag = false, prefix = "AUDIOFOCUS_REQUEST", value = {
4301 AUDIOFOCUS_REQUEST_FAILED,
4302 AUDIOFOCUS_REQUEST_GRANTED,
4303 AUDIOFOCUS_REQUEST_DELAYED }
4304 )
4305 @Retention(RetentionPolicy.SOURCE)
4306 public @interface FocusRequestResult {}
4307
4308 /**
4309 * @hide
4310 * code returned when a synchronous focus request on the client-side is to be blocked
4311 * until the external audio focus policy decides on the response for the client
4312 */
4313 public static final int AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY = 100;
4314
4315 /**
4316 * Timeout duration in ms when waiting on an external focus policy for the result for a
4317 * focus request
4318 */
Weilin Xu1017d432022-06-08 00:27:52 +00004319 private static final int EXT_FOCUS_POLICY_TIMEOUT_MS = 250;
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004320
4321 private static final String FOCUS_CLIENT_ID_STRING = "android_audio_focus_client_id";
4322
4323 private final Object mFocusRequestsLock = new Object();
4324 /**
4325 * Map of all receivers of focus request results, one per unresolved focus request.
4326 * Receivers are added before sending the request to the external focus policy,
4327 * and are removed either after receiving the result, or after the timeout.
4328 * This variable is lazily initialized.
4329 */
4330 @GuardedBy("mFocusRequestsLock")
4331 private HashMap<String, BlockingFocusResultReceiver> mFocusRequestsAwaitingResult;
4332
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004333
4334 /**
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004335 * Request audio focus.
Jean-Michel Trivi46b3a182010-05-11 11:22:54 -07004336 * Send a request to obtain the audio focus
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004337 * @param l the listener to be notified of audio focus changes
4338 * @param streamType the main audio stream type affected by the focus request
4339 * @param durationHint use {@link #AUDIOFOCUS_GAIN_TRANSIENT} to indicate this focus request
4340 * is temporary, and focus will be abandonned shortly. Examples of transient requests are
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07004341 * for the playback of driving directions, or notifications sounds.
Jean-Michel Trivi983ac2b2010-03-19 12:09:25 -07004342 * Use {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} to indicate also that it's ok for
4343 * the previous focus owner to keep playing if it ducks its audio output.
Jean-Michel Trivi9171db22013-07-31 17:11:12 -07004344 * Alternatively use {@link #AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE} for a temporary request
4345 * that benefits from the system not playing disruptive sounds like notifications, for
4346 * usecases such as voice memo recording, or speech recognition.
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07004347 * Use {@link #AUDIOFOCUS_GAIN} for a focus request of unknown duration such
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004348 * as the playback of a song or a video.
4349 * @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
Jean-Michel Trivi36bdb662017-03-09 11:56:51 -08004350 * @deprecated use {@link #requestAudioFocus(AudioFocusRequest)}
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004351 */
4352 public int requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint) {
Jean-Michel Trivi36bdb662017-03-09 11:56:51 -08004353 PlayerBase.deprecateStreamTypeForPlayback(streamType,
4354 "AudioManager", "requestAudioFocus()");
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004355 int status = AUDIOFOCUS_REQUEST_FAILED;
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004356
4357 try {
4358 // status is guaranteed to be either AUDIOFOCUS_REQUEST_FAILED or
4359 // AUDIOFOCUS_REQUEST_GRANTED as focus is requested without the
4360 // AUDIOFOCUS_FLAG_DELAY_OK flag
4361 status = requestAudioFocus(l,
4362 new AudioAttributes.Builder()
4363 .setInternalLegacyStreamType(streamType).build(),
4364 durationHint,
4365 0 /* flags, legacy behavior */);
4366 } catch (IllegalArgumentException e) {
4367 Log.e(TAG, "Audio focus request denied due to ", e);
4368 }
4369
4370 return status;
4371 }
4372
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004373 // when adding new flags, add them to the relevant AUDIOFOCUS_FLAGS_APPS or SYSTEM masks
Jean-Michel Trivi0212be52014-11-24 14:43:10 -08004374 /**
4375 * @hide
4376 * Use this flag when requesting audio focus to indicate it is ok for the requester to not be
4377 * granted audio focus immediately (as indicated by {@link #AUDIOFOCUS_REQUEST_DELAYED}) when
4378 * the system is in a state where focus cannot change, but be granted focus later when
4379 * this condition ends.
4380 */
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004381 @SystemApi
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004382 public static final int AUDIOFOCUS_FLAG_DELAY_OK = 0x1 << 0;
Jean-Michel Trivi0212be52014-11-24 14:43:10 -08004383 /**
4384 * @hide
4385 * Use this flag when requesting audio focus to indicate that the requester
4386 * will pause its media playback (if applicable) when losing audio focus with
4387 * {@link #AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}, rather than ducking.
4388 * <br>On some platforms, the ducking may be handled without the application being aware of it
4389 * (i.e. it will not transiently lose focus). For applications that for instance play spoken
4390 * content, such as audio book or podcast players, ducking may never be acceptable, and will
4391 * thus always pause. This flag enables them to be declared as such whenever they request focus.
4392 */
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004393 @SystemApi
Jean-Michel Trivi0212be52014-11-24 14:43:10 -08004394 public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 0x1 << 1;
4395 /**
4396 * @hide
4397 * Use this flag to lock audio focus so granting is temporarily disabled.
4398 * <br>This flag can only be used by owners of a registered
4399 * {@link android.media.audiopolicy.AudioPolicy} in
4400 * {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int, AudioPolicy)}
4401 */
4402 @SystemApi
4403 public static final int AUDIOFOCUS_FLAG_LOCK = 0x1 << 2;
Jean-Michel Trivi28602092021-03-24 11:14:47 -07004404
4405 /**
4406 * @hide
4407 * flag set on test API calls,
4408 * see {@link #requestAudioFocusForTest(AudioFocusRequest, String, int, int)},
Jean-Michel Trivibec223b2021-12-08 18:40:08 -08004409 */
Jean-Michel Trivi28602092021-03-24 11:14:47 -07004410 public static final int AUDIOFOCUS_FLAG_TEST = 0x1 << 3;
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004411 /** @hide */
Jean-Michel Trivi0212be52014-11-24 14:43:10 -08004412 public static final int AUDIOFOCUS_FLAGS_APPS = AUDIOFOCUS_FLAG_DELAY_OK
4413 | AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS;
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004414 /** @hide */
4415 public static final int AUDIOFOCUS_FLAGS_SYSTEM = AUDIOFOCUS_FLAG_DELAY_OK
Jean-Michel Trivi0212be52014-11-24 14:43:10 -08004416 | AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS | AUDIOFOCUS_FLAG_LOCK;
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004417
4418 /**
Jean-Michel Trivi3db31ac2017-02-24 11:40:37 -08004419 * Request audio focus.
4420 * See the {@link AudioFocusRequest} for information about the options available to configure
4421 * your request, and notification of focus gain and loss.
4422 * @param focusRequest a {@link AudioFocusRequest} instance used to configure how focus is
4423 * requested.
4424 * @return {@link #AUDIOFOCUS_REQUEST_FAILED}, {@link #AUDIOFOCUS_REQUEST_GRANTED}
4425 * or {@link #AUDIOFOCUS_REQUEST_DELAYED}.
4426 * <br>Note that the return value is never {@link #AUDIOFOCUS_REQUEST_DELAYED} when focus
4427 * is requested without building the {@link AudioFocusRequest} with
4428 * {@link AudioFocusRequest.Builder#setAcceptsDelayedFocusGain(boolean)} set to
4429 * {@code true}.
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004430 * @throws NullPointerException if passed a null argument
Jean-Michel Trivi3db31ac2017-02-24 11:40:37 -08004431 */
4432 public int requestAudioFocus(@NonNull AudioFocusRequest focusRequest) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004433 return requestAudioFocus(focusRequest, null /* no AudioPolicy*/);
Jean-Michel Trivi3db31ac2017-02-24 11:40:37 -08004434 }
4435
4436 /**
4437 * Abandon audio focus. Causes the previous focus owner, if any, to receive focus.
4438 * @param focusRequest the {@link AudioFocusRequest} that was used when requesting focus
4439 * with {@link #requestAudioFocus(AudioFocusRequest)}.
4440 * @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
4441 * @throws IllegalArgumentException if passed a null argument
4442 */
4443 public int abandonAudioFocusRequest(@NonNull AudioFocusRequest focusRequest) {
4444 if (focusRequest == null) {
4445 throw new IllegalArgumentException("Illegal null AudioFocusRequest");
4446 }
4447 return abandonAudioFocus(focusRequest.getOnAudioFocusChangeListener(),
4448 focusRequest.getAudioAttributes());
4449 }
4450
4451 /**
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004452 * @hide
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004453 * Request audio focus.
4454 * Send a request to obtain the audio focus. This method differs from
4455 * {@link #requestAudioFocus(OnAudioFocusChangeListener, int, int)} in that it can express
4456 * that the requester accepts delayed grants of audio focus.
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004457 * @param l the listener to be notified of audio focus changes. It is not allowed to be null
4458 * when the request is flagged with {@link #AUDIOFOCUS_FLAG_DELAY_OK}.
4459 * @param requestAttributes non null {@link AudioAttributes} describing the main reason for
4460 * requesting audio focus.
4461 * @param durationHint use {@link #AUDIOFOCUS_GAIN_TRANSIENT} to indicate this focus request
4462 * is temporary, and focus will be abandonned shortly. Examples of transient requests are
4463 * for the playback of driving directions, or notifications sounds.
4464 * Use {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} to indicate also that it's ok for
4465 * the previous focus owner to keep playing if it ducks its audio output.
4466 * Alternatively use {@link #AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE} for a temporary request
4467 * that benefits from the system not playing disruptive sounds like notifications, for
4468 * usecases such as voice memo recording, or speech recognition.
4469 * Use {@link #AUDIOFOCUS_GAIN} for a focus request of unknown duration such
4470 * as the playback of a song or a video.
Jean-Michel Trivi26ae15f2017-08-25 19:20:49 -07004471 * @param flags 0 or a combination of {link #AUDIOFOCUS_FLAG_DELAY_OK},
4472 * {@link #AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS} and {@link #AUDIOFOCUS_FLAG_LOCK}.
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004473 * <br>Use 0 when not using any flags for the request, which behaves like
4474 * {@link #requestAudioFocus(OnAudioFocusChangeListener, int, int)}, where either audio
4475 * focus is granted immediately, or the grant request fails because the system is in a
4476 * state where focus cannot change (e.g. a phone call).
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004477 * @return {@link #AUDIOFOCUS_REQUEST_FAILED}, {@link #AUDIOFOCUS_REQUEST_GRANTED}
4478 * or {@link #AUDIOFOCUS_REQUEST_DELAYED}.
4479 * The return value is never {@link #AUDIOFOCUS_REQUEST_DELAYED} when focus is requested
4480 * without the {@link #AUDIOFOCUS_FLAG_DELAY_OK} flag.
4481 * @throws IllegalArgumentException
4482 */
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004483 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00004484 @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004485 public int requestAudioFocus(OnAudioFocusChangeListener l,
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004486 @NonNull AudioAttributes requestAttributes,
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004487 int durationHint,
4488 int flags) throws IllegalArgumentException {
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004489 if (flags != (flags & AUDIOFOCUS_FLAGS_APPS)) {
4490 throw new IllegalArgumentException("Invalid flags 0x"
4491 + Integer.toHexString(flags).toUpperCase());
4492 }
4493 return requestAudioFocus(l, requestAttributes, durationHint,
4494 flags & AUDIOFOCUS_FLAGS_APPS,
4495 null /* no AudioPolicy*/);
4496 }
4497
4498 /**
4499 * @hide
4500 * Request or lock audio focus.
4501 * This method is to be used by system components that have registered an
4502 * {@link android.media.audiopolicy.AudioPolicy} to request audio focus, but also to "lock" it
4503 * so focus granting is temporarily disabled.
4504 * @param l see the description of the same parameter in
4505 * {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int)}
4506 * @param requestAttributes non null {@link AudioAttributes} describing the main reason for
4507 * requesting audio focus.
4508 * @param durationHint see the description of the same parameter in
4509 * {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int)}
4510 * @param flags 0 or a combination of {link #AUDIOFOCUS_FLAG_DELAY_OK},
Jean-Michel Trivi0212be52014-11-24 14:43:10 -08004511 * {@link #AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS}, and {@link #AUDIOFOCUS_FLAG_LOCK}.
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004512 * <br>Use 0 when not using any flags for the request, which behaves like
4513 * {@link #requestAudioFocus(OnAudioFocusChangeListener, int, int)}, where either audio
4514 * focus is granted immediately, or the grant request fails because the system is in a
4515 * state where focus cannot change (e.g. a phone call).
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004516 * @param ap a registered {@link android.media.audiopolicy.AudioPolicy} instance when locking
4517 * focus, or null.
4518 * @return see the description of the same return value in
4519 * {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int)}
4520 * @throws IllegalArgumentException
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004521 * @deprecated use {@link #requestAudioFocus(AudioFocusRequest, AudioPolicy)}
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004522 */
Jean-Michel Trivi1b3541d2014-11-25 12:53:41 -08004523 @SystemApi
Jean-Michel Trivi26ae15f2017-08-25 19:20:49 -07004524 @RequiresPermission(anyOf= {
Jean-Michel Trivic4557822023-01-23 18:19:52 +00004525 Manifest.permission.MODIFY_PHONE_STATE,
4526 Manifest.permission.MODIFY_AUDIO_ROUTING
Jean-Michel Trivi26ae15f2017-08-25 19:20:49 -07004527 })
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004528 public int requestAudioFocus(OnAudioFocusChangeListener l,
4529 @NonNull AudioAttributes requestAttributes,
4530 int durationHint,
4531 int flags,
4532 AudioPolicy ap) throws IllegalArgumentException {
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004533 // parameter checking
4534 if (requestAttributes == null) {
4535 throw new IllegalArgumentException("Illegal null AudioAttributes argument");
4536 }
Jean-Michel Trivi3db31ac2017-02-24 11:40:37 -08004537 if (!AudioFocusRequest.isValidFocusGain(durationHint)) {
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004538 throw new IllegalArgumentException("Invalid duration hint");
Jean-Michel Trivi55d1bb32010-04-01 17:40:58 -07004539 }
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004540 if (flags != (flags & AUDIOFOCUS_FLAGS_SYSTEM)) {
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004541 throw new IllegalArgumentException("Illegal flags 0x"
4542 + Integer.toHexString(flags).toUpperCase());
4543 }
4544 if (((flags & AUDIOFOCUS_FLAG_DELAY_OK) == AUDIOFOCUS_FLAG_DELAY_OK) && (l == null)) {
4545 throw new IllegalArgumentException(
4546 "Illegal null focus listener when flagged as accepting delayed focus grant");
4547 }
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004548 if (((flags & AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS)
4549 == AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) && (l == null)) {
4550 throw new IllegalArgumentException(
4551 "Illegal null focus listener when flagged as pausing instead of ducking");
4552 }
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004553 if (((flags & AUDIOFOCUS_FLAG_LOCK) == AUDIOFOCUS_FLAG_LOCK) && (ap == null)) {
4554 throw new IllegalArgumentException(
4555 "Illegal null audio policy when locking audio focus");
4556 }
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004557
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004558 final AudioFocusRequest afr = new AudioFocusRequest.Builder(durationHint)
Jean-Michel Trivi36728ce2017-05-01 12:33:40 -07004559 .setOnAudioFocusChangeListenerInt(l, null /* no Handler for this legacy API */)
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004560 .setAudioAttributes(requestAttributes)
4561 .setAcceptsDelayedFocusGain((flags & AUDIOFOCUS_FLAG_DELAY_OK)
4562 == AUDIOFOCUS_FLAG_DELAY_OK)
4563 .setWillPauseWhenDucked((flags & AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS)
4564 == AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS)
4565 .setLocksFocus((flags & AUDIOFOCUS_FLAG_LOCK) == AUDIOFOCUS_FLAG_LOCK)
4566 .build();
4567 return requestAudioFocus(afr, ap);
4568 }
4569
4570 /**
4571 * @hide
Jean-Michel Trivi28602092021-03-24 11:14:47 -07004572 * Test API to request audio focus for an arbitrary client operating from a (fake) given UID.
4573 * Used to simulate conditions of the test, not the behavior of the focus requester under test.
4574 * @param afr the parameters of the request
4575 * @param clientFakeId the identifier of the AudioManager the client would be requesting from
4576 * @param clientFakeUid the UID of the client, here an arbitrary int,
4577 * doesn't have to be a real UID
4578 * @param clientTargetSdk the target SDK used by the client
4579 * @return return code indicating status of the request
4580 */
4581 @TestApi
4582 @RequiresPermission("android.permission.QUERY_AUDIO_STATE")
4583 public @FocusRequestResult int requestAudioFocusForTest(@NonNull AudioFocusRequest afr,
4584 @NonNull String clientFakeId, int clientFakeUid, int clientTargetSdk) {
4585 Objects.requireNonNull(afr);
4586 Objects.requireNonNull(clientFakeId);
Oscar Azucena3209c342022-05-13 19:08:14 -07004587 int status;
Jean-Michel Trivi28602092021-03-24 11:14:47 -07004588 try {
Oscar Azucena3209c342022-05-13 19:08:14 -07004589 status = getService().requestAudioFocusForTest(afr.getAudioAttributes(),
Jean-Michel Trivi28602092021-03-24 11:14:47 -07004590 afr.getFocusGain(),
4591 mICallBack,
4592 mAudioFocusDispatcher,
Jean-Michel Trivibec223b2021-12-08 18:40:08 -08004593 clientFakeId, "com.android.test.fakeclient",
4594 afr.getFlags() | AudioManager.AUDIOFOCUS_FLAG_TEST,
4595 clientFakeUid, clientTargetSdk);
Jean-Michel Trivi28602092021-03-24 11:14:47 -07004596 } catch (RemoteException e) {
4597 throw e.rethrowFromSystemServer();
4598 }
Weilin Xu1017d432022-06-08 00:27:52 +00004599 if (status != AudioManager.AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY) {
4600 // default path with no external focus policy
4601 return status;
4602 }
Oscar Azucena3209c342022-05-13 19:08:14 -07004603
Weilin Xu1017d432022-06-08 00:27:52 +00004604 BlockingFocusResultReceiver focusReceiver;
4605 synchronized (mFocusRequestsLock) {
4606 focusReceiver = addClientIdToFocusReceiverLocked(clientFakeId);
4607 }
4608
4609 return handleExternalAudioPolicyWaitIfNeeded(clientFakeId, focusReceiver);
Jean-Michel Trivi28602092021-03-24 11:14:47 -07004610 }
4611
4612 /**
4613 * @hide
4614 * Test API to abandon audio focus for an arbitrary client.
4615 * Used to simulate conditions of the test, not the behavior of the focus requester under test.
4616 * @param afr the parameters used for the request
4617 * @param clientFakeId clientFakeId the identifier of the AudioManager from which the client
4618 * would be requesting
4619 * @return return code indicating status of the request
4620 */
4621 @TestApi
4622 @RequiresPermission("android.permission.QUERY_AUDIO_STATE")
4623 public @FocusRequestResult int abandonAudioFocusForTest(@NonNull AudioFocusRequest afr,
4624 @NonNull String clientFakeId) {
4625 Objects.requireNonNull(afr);
4626 Objects.requireNonNull(clientFakeId);
4627 try {
4628 return getService().abandonAudioFocusForTest(mAudioFocusDispatcher,
4629 clientFakeId, afr.getAudioAttributes(), "com.android.test.fakeclient");
4630 } catch (RemoteException e) {
4631 throw e.rethrowFromSystemServer();
4632 }
4633 }
4634
4635 /**
4636 * @hide
4637 * Return the duration of the fade out applied when a player of the given AudioAttributes
4638 * is losing audio focus
4639 * @param aa the AudioAttributes of the player losing focus with {@link #AUDIOFOCUS_LOSS}
4640 * @return a duration in ms, 0 indicates no fade out is applied
4641 */
4642 @TestApi
4643 @RequiresPermission("android.permission.QUERY_AUDIO_STATE")
4644 public @IntRange(from = 0) long getFadeOutDurationOnFocusLossMillis(@NonNull AudioAttributes aa)
4645 {
4646 Objects.requireNonNull(aa);
4647 try {
4648 return getService().getFadeOutDurationOnFocusLossMillis(aa);
4649 } catch (RemoteException e) {
4650 throw e.rethrowFromSystemServer();
4651 }
4652 }
4653
4654 /**
4655 * @hide
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004656 * Request or lock audio focus.
4657 * This method is to be used by system components that have registered an
4658 * {@link android.media.audiopolicy.AudioPolicy} to request audio focus, but also to "lock" it
4659 * so focus granting is temporarily disabled.
4660 * @param afr see the description of the same parameter in
4661 * {@link #requestAudioFocus(AudioFocusRequest)}
4662 * @param ap a registered {@link android.media.audiopolicy.AudioPolicy} instance when locking
4663 * focus, or null.
4664 * @return {@link #AUDIOFOCUS_REQUEST_FAILED}, {@link #AUDIOFOCUS_REQUEST_GRANTED}
4665 * or {@link #AUDIOFOCUS_REQUEST_DELAYED}.
4666 * @throws NullPointerException if the AudioFocusRequest is null
4667 * @throws IllegalArgumentException when trying to lock focus without an AudioPolicy
4668 */
4669 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00004670 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004671 public int requestAudioFocus(@NonNull AudioFocusRequest afr, @Nullable AudioPolicy ap) {
4672 if (afr == null) {
4673 throw new NullPointerException("Illegal null AudioFocusRequest");
4674 }
4675 // this can only be checked now, not during the creation of the AudioFocusRequest instance
4676 if (afr.locksFocus() && ap == null) {
4677 throw new IllegalArgumentException(
4678 "Illegal null audio policy when locking audio focus");
4679 }
4680 registerAudioFocusRequest(afr);
4681 final IAudioService service = getService();
4682 final int status;
Jean-Michel Trivi461922f2017-04-25 15:23:17 -07004683 int sdk;
4684 try {
4685 sdk = getContext().getApplicationInfo().targetSdkVersion;
4686 } catch (NullPointerException e) {
4687 // some tests don't have a Context
4688 sdk = Build.VERSION.SDK_INT;
4689 }
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004690
4691 final String clientId = getIdForAudioFocusListener(afr.getOnAudioFocusChangeListener());
Weilin Xu1017d432022-06-08 00:27:52 +00004692 BlockingFocusResultReceiver focusReceiver;
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004693 synchronized (mFocusRequestsLock) {
Weilin Xu1017d432022-06-08 00:27:52 +00004694
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004695 try {
4696 // TODO status contains result and generation counter for ext policy
4697 status = service.requestAudioFocus(afr.getAudioAttributes(),
4698 afr.getFocusGain(), mICallBack,
4699 mAudioFocusDispatcher,
4700 clientId,
John Wu4f7e5102021-06-22 17:29:11 +00004701 getContext().getOpPackageName() /* package name */,
4702 getContext().getAttributionTag(),
4703 afr.getFlags(),
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004704 ap != null ? ap.cb() : null,
4705 sdk);
4706 } catch (RemoteException e) {
4707 throw e.rethrowFromSystemServer();
4708 }
Weilin Xu1017d432022-06-08 00:27:52 +00004709 if (status != AudioManager.AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY) {
4710 // default path with no external focus policy
4711 return status;
4712 }
4713 focusReceiver = addClientIdToFocusReceiverLocked(clientId);
Oscar Azucena3209c342022-05-13 19:08:14 -07004714 }
4715
Weilin Xu1017d432022-06-08 00:27:52 +00004716 return handleExternalAudioPolicyWaitIfNeeded(clientId, focusReceiver);
4717 }
4718
4719 @GuardedBy("mFocusRequestsLock")
4720 private BlockingFocusResultReceiver addClientIdToFocusReceiverLocked(String clientId) {
4721 BlockingFocusResultReceiver focusReceiver;
4722 if (mFocusRequestsAwaitingResult == null) {
4723 mFocusRequestsAwaitingResult =
4724 new HashMap<String, BlockingFocusResultReceiver>(1);
4725 }
4726 focusReceiver = new BlockingFocusResultReceiver(clientId);
4727 mFocusRequestsAwaitingResult.put(clientId, focusReceiver);
4728 return focusReceiver;
Oscar Azucena3209c342022-05-13 19:08:14 -07004729 }
4730
4731 private @FocusRequestResult int handleExternalAudioPolicyWaitIfNeeded(String clientId,
Weilin Xu1017d432022-06-08 00:27:52 +00004732 BlockingFocusResultReceiver focusReceiver) {
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004733 focusReceiver.waitForResult(EXT_FOCUS_POLICY_TIMEOUT_MS);
4734 if (DEBUG && !focusReceiver.receivedResult()) {
Oscar Azucena3209c342022-05-13 19:08:14 -07004735 Log.e(TAG, "handleExternalAudioPolicyWaitIfNeeded"
4736 + " response from ext policy timed out, denying request");
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004737 }
Oscar Azucena3209c342022-05-13 19:08:14 -07004738
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004739 synchronized (mFocusRequestsLock) {
4740 mFocusRequestsAwaitingResult.remove(clientId);
4741 }
4742 return focusReceiver.requestResult();
4743 }
4744
4745 // helper class that abstracts out the handling of spurious wakeups in Object.wait()
4746 private static final class SafeWaitObject {
4747 private boolean mQuit = false;
4748
4749 public void safeNotify() {
4750 synchronized (this) {
4751 mQuit = true;
4752 this.notify();
4753 }
4754 }
4755
4756 public void safeWait(long millis) throws InterruptedException {
4757 final long timeOutTime = java.lang.System.currentTimeMillis() + millis;
4758 synchronized (this) {
4759 while (!mQuit) {
4760 final long timeToWait = timeOutTime - java.lang.System.currentTimeMillis();
4761 if (timeToWait < 0) { break; }
4762 this.wait(timeToWait);
4763 }
4764 }
4765 }
4766 }
4767
4768 private static final class BlockingFocusResultReceiver {
4769 private final SafeWaitObject mLock = new SafeWaitObject();
4770 @GuardedBy("mLock")
4771 private boolean mResultReceived = false;
4772 // request denied by default (e.g. timeout)
4773 private int mFocusRequestResult = AudioManager.AUDIOFOCUS_REQUEST_FAILED;
4774 private final String mFocusClientId;
4775
4776 BlockingFocusResultReceiver(String clientId) {
4777 mFocusClientId = clientId;
4778 }
4779
4780 boolean receivedResult() { return mResultReceived; }
4781 int requestResult() { return mFocusRequestResult; }
4782
4783 void notifyResult(int requestResult) {
4784 synchronized (mLock) {
4785 mResultReceived = true;
4786 mFocusRequestResult = requestResult;
4787 mLock.safeNotify();
4788 }
4789 }
4790
4791 public void waitForResult(long timeOutMs) {
4792 synchronized (mLock) {
4793 if (mResultReceived) {
4794 // the result was received before waiting
4795 return;
4796 }
4797 try {
4798 mLock.safeWait(timeOutMs);
4799 } catch (InterruptedException e) { }
4800 }
4801 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004802 }
4803
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08004804 /**
4805 * @hide
4806 * Used internally by telephony package to request audio focus. Will cause the focus request
4807 * to be associated with the "voice communication" identifier only used in AudioService
4808 * to identify this use case.
4809 * @param streamType use STREAM_RING for focus requests when ringing, VOICE_CALL for
4810 * the establishment of the call
4811 * @param durationHint the type of focus request. AUDIOFOCUS_GAIN_TRANSIENT is recommended so
4812 * media applications resume after a call
4813 */
Mathew Inwood8e742f92020-10-27 11:47:29 +00004814 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08004815 public void requestAudioFocusForCall(int streamType, int durationHint) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004816 final IAudioService service = getService();
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08004817 try {
Jean-Michel Trivifd6ad742014-11-10 14:38:30 -08004818 service.requestAudioFocus(new AudioAttributes.Builder()
4819 .setInternalLegacyStreamType(streamType).build(),
4820 durationHint, mICallBack, null,
John Spurlock61560172015-02-06 19:46:04 -05004821 AudioSystem.IN_VOICE_COMM_FOCUS_ID,
Marco Nelissen29f16932015-04-17 09:50:56 -07004822 getContext().getOpPackageName(),
John Wu4f7e5102021-06-22 17:29:11 +00004823 getContext().getAttributionTag(),
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004824 AUDIOFOCUS_FLAG_LOCK,
Jean-Michel Trivi461922f2017-04-25 15:23:17 -07004825 null /* policy token */, 0 /* sdk n/a here*/);
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08004826 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07004827 throw e.rethrowFromSystemServer();
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08004828 }
4829 }
4830
4831 /**
4832 * @hide
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -08004833 * Return the volume ramping time for a sound to be played after the given focus request,
4834 * and to play a sound of the given attributes
4835 * @param focusGain
4836 * @param attr
4837 * @return
4838 */
4839 public int getFocusRampTimeMs(int focusGain, AudioAttributes attr) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004840 final IAudioService service = getService();
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -08004841 try {
4842 return service.getFocusRampTimeMs(focusGain, attr);
4843 } catch (RemoteException e) {
4844 throw e.rethrowFromSystemServer();
4845 }
4846 }
4847
4848 /**
4849 * @hide
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004850 * Set the result to the audio focus request received through
4851 * {@link AudioPolicyFocusListener#onAudioFocusRequest(AudioFocusInfo, int)}.
4852 * @param afi the information about the focus requester
4853 * @param requestResult the result to the focus request to be passed to the requester
4854 * @param ap a valid registered {@link AudioPolicy} configured as a focus policy.
4855 */
4856 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00004857 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Jean-Michel Trivie2d8aae2018-01-30 15:09:47 -08004858 public void setFocusRequestResult(@NonNull AudioFocusInfo afi,
4859 @FocusRequestResult int requestResult, @NonNull AudioPolicy ap) {
4860 if (afi == null) {
4861 throw new IllegalArgumentException("Illegal null AudioFocusInfo");
4862 }
4863 if (ap == null) {
4864 throw new IllegalArgumentException("Illegal null AudioPolicy");
4865 }
4866 final IAudioService service = getService();
4867 try {
4868 service.setFocusRequestResultFromExtPolicy(afi, requestResult, ap.cb());
4869 } catch (RemoteException e) {
4870 throw e.rethrowFromSystemServer();
4871 }
4872 }
4873
4874 /**
4875 * @hide
Jean-Michel Trivi126cf032017-04-02 23:19:02 -07004876 * Notifies an application with a focus listener of gain or loss of audio focus.
4877 * This method can only be used by owners of an {@link AudioPolicy} configured with
4878 * {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to true.
4879 * @param afi the recipient of the focus change, that has previously requested audio focus, and
4880 * that was received by the {@code AudioPolicy} through
4881 * {@link AudioPolicy.AudioPolicyFocusListener#onAudioFocusRequest(AudioFocusInfo, int)}.
4882 * @param focusChange one of focus gain types ({@link #AUDIOFOCUS_GAIN},
4883 * {@link #AUDIOFOCUS_GAIN_TRANSIENT}, {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} or
4884 * {@link #AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE})
4885 * or one of the focus loss types ({@link AudioManager#AUDIOFOCUS_LOSS},
4886 * {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT},
4887 * or {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}).
4888 * <br>For the focus gain, the change type should be the same as the app requested.
4889 * @param ap a valid registered {@link AudioPolicy} configured as a focus policy.
4890 * @return {@link #AUDIOFOCUS_REQUEST_GRANTED} if the dispatch was successfully sent, or
4891 * {@link #AUDIOFOCUS_REQUEST_FAILED} if the focus client didn't have a listener, or
4892 * if there was an error sending the request.
4893 * @throws NullPointerException if the {@link AudioFocusInfo} or {@link AudioPolicy} are null.
4894 */
4895 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00004896 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Jean-Michel Trivi126cf032017-04-02 23:19:02 -07004897 public int dispatchAudioFocusChange(@NonNull AudioFocusInfo afi, int focusChange,
4898 @NonNull AudioPolicy ap) {
4899 if (afi == null) {
4900 throw new NullPointerException("Illegal null AudioFocusInfo");
4901 }
4902 if (ap == null) {
4903 throw new NullPointerException("Illegal null AudioPolicy");
4904 }
4905 final IAudioService service = getService();
4906 try {
4907 return service.dispatchFocusChange(afi, focusChange, ap.cb());
4908 } catch (RemoteException e) {
4909 throw e.rethrowFromSystemServer();
4910 }
4911 }
4912
4913 /**
4914 * @hide
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08004915 * Used internally by telephony package to abandon audio focus, typically after a call or
4916 * when ringing ends and the call is rejected or not answered.
4917 * Should match one or more calls to {@link #requestAudioFocusForCall(int, int)}.
4918 */
Mathew Inwood8e742f92020-10-27 11:47:29 +00004919 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08004920 public void abandonAudioFocusForCall() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004921 final IAudioService service = getService();
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08004922 try {
John Spurlock61560172015-02-06 19:46:04 -05004923 service.abandonAudioFocus(null, AudioSystem.IN_VOICE_COMM_FOCUS_ID,
Jean-Michel Trivi126cf032017-04-02 23:19:02 -07004924 null /*AudioAttributes, legacy behavior*/, getContext().getOpPackageName());
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08004925 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07004926 throw e.rethrowFromSystemServer();
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08004927 }
4928 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004929
4930 /**
Jean-Michel Trivi46b3a182010-05-11 11:22:54 -07004931 * Abandon audio focus. Causes the previous focus owner, if any, to receive focus.
4932 * @param l the listener with which focus was requested.
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004933 * @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
Jean-Michel Trivi36bdb662017-03-09 11:56:51 -08004934 * @deprecated use {@link #abandonAudioFocusRequest(AudioFocusRequest)}
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004935 */
4936 public int abandonAudioFocus(OnAudioFocusChangeListener l) {
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004937 return abandonAudioFocus(l, null /*AudioAttributes, legacy behavior*/);
4938 }
4939
4940 /**
4941 * @hide
4942 * Abandon audio focus. Causes the previous focus owner, if any, to receive focus.
4943 * @param l the listener with which focus was requested.
4944 * @param aa the {@link AudioAttributes} with which audio focus was requested
4945 * @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004946 * @deprecated use {@link #abandonAudioFocusRequest(AudioFocusRequest)}
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004947 */
4948 @SystemApi
Aurimas Liutikas1394a012020-11-12 18:26:09 -08004949 @SuppressLint("RequiresPermission") // no permission enforcement, but only "undoes" what would
4950 // have been done by a matching requestAudioFocus
Jean-Michel Trivi958876f2014-11-16 15:40:22 -08004951 public int abandonAudioFocus(OnAudioFocusChangeListener l, AudioAttributes aa) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004952 int status = AUDIOFOCUS_REQUEST_FAILED;
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07004953 unregisterAudioFocusRequest(l);
4954 final IAudioService service = getService();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004955 try {
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004956 status = service.abandonAudioFocus(mAudioFocusDispatcher,
Jean-Michel Trivi126cf032017-04-02 23:19:02 -07004957 getIdForAudioFocusListener(l), aa, getContext().getOpPackageName());
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004958 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07004959 throw e.rethrowFromSystemServer();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004960 }
4961 return status;
4962 }
4963
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004964 //====================================================================
4965 // Remote Control
4966 /**
Jean-Michel Trivi46b3a182010-05-11 11:22:54 -07004967 * Register a component to be the sole receiver of MEDIA_BUTTON intents.
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004968 * @param eventReceiver identifier of a {@link android.content.BroadcastReceiver}
4969 * that will receive the media button intent. This broadcast receiver must be declared
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004970 * in the application manifest. The package of the component must match that of
4971 * the context you're registering from.
RoboErikb214efb2014-07-24 13:20:30 -07004972 * @deprecated Use {@link MediaSession#setMediaButtonReceiver(PendingIntent)} instead.
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004973 */
RoboErikb214efb2014-07-24 13:20:30 -07004974 @Deprecated
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004975 public void registerMediaButtonEventReceiver(ComponentName eventReceiver) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004976 if (eventReceiver == null) {
4977 return;
4978 }
Marco Nelissen29f16932015-04-17 09:50:56 -07004979 if (!eventReceiver.getPackageName().equals(getContext().getPackageName())) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004980 Log.e(TAG, "registerMediaButtonEventReceiver() error: " +
4981 "receiver and context package names don't match");
4982 return;
4983 }
4984 // construct a PendingIntent for the media button and register it
4985 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
4986 // the associated intent will be handled by the component being registered
4987 mediaButtonIntent.setComponent(eventReceiver);
Marco Nelissen29f16932015-04-17 09:50:56 -07004988 PendingIntent pi = PendingIntent.getBroadcast(getContext(),
Jean-Michel Triviec84dae2020-10-22 17:12:37 -07004989 0/*requestCode, ignored*/, mediaButtonIntent,
4990 PendingIntent.FLAG_IMMUTABLE);
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004991 registerMediaButtonIntent(pi, eventReceiver);
4992 }
4993
4994 /**
Dianne Hackborn961cae92013-03-20 14:59:43 -07004995 * Register a component to be the sole receiver of MEDIA_BUTTON intents. This is like
4996 * {@link #registerMediaButtonEventReceiver(android.content.ComponentName)}, but allows
4997 * the buttons to go to any PendingIntent. Note that you should only use this form if
4998 * you know you will continue running for the full time until unregistering the
4999 * PendingIntent.
5000 * @param eventReceiver target that will receive media button intents. The PendingIntent
RoboErikb214efb2014-07-24 13:20:30 -07005001 * will be sent an {@link Intent#ACTION_MEDIA_BUTTON} event when a media button action
5002 * occurs, with {@link Intent#EXTRA_KEY_EVENT} added and holding the key code of the
5003 * media button that was pressed.
5004 * @deprecated Use {@link MediaSession#setMediaButtonReceiver(PendingIntent)} instead.
Dianne Hackborn961cae92013-03-20 14:59:43 -07005005 */
RoboErikb214efb2014-07-24 13:20:30 -07005006 @Deprecated
Dianne Hackborn961cae92013-03-20 14:59:43 -07005007 public void registerMediaButtonEventReceiver(PendingIntent eventReceiver) {
5008 if (eventReceiver == null) {
5009 return;
5010 }
5011 registerMediaButtonIntent(eventReceiver, null);
5012 }
5013
5014 /**
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005015 * @hide
5016 * no-op if (pi == null) or (eventReceiver == null)
5017 */
5018 public void registerMediaButtonIntent(PendingIntent pi, ComponentName eventReceiver) {
Dianne Hackborn961cae92013-03-20 14:59:43 -07005019 if (pi == null) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005020 Log.e(TAG, "Cannot call registerMediaButtonIntent() with a null parameter");
5021 return;
5022 }
Marco Nelissen29f16932015-04-17 09:50:56 -07005023 MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
5024 helper.addMediaButtonListener(pi, eventReceiver, getContext());
Jean-Michel Trivi722b8082012-05-15 15:18:33 -07005025 }
5026
5027 /**
Jean-Michel Trivi46b3a182010-05-11 11:22:54 -07005028 * Unregister the receiver of MEDIA_BUTTON intents.
5029 * @param eventReceiver identifier of a {@link android.content.BroadcastReceiver}
5030 * that was registered with {@link #registerMediaButtonEventReceiver(ComponentName)}.
RoboErikb214efb2014-07-24 13:20:30 -07005031 * @deprecated Use {@link MediaSession} instead.
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005032 */
RoboErikb214efb2014-07-24 13:20:30 -07005033 @Deprecated
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005034 public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005035 if (eventReceiver == null) {
5036 return;
5037 }
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005038 // construct a PendingIntent for the media button and unregister it
5039 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
5040 // the associated intent will be handled by the component being registered
5041 mediaButtonIntent.setComponent(eventReceiver);
Marco Nelissen29f16932015-04-17 09:50:56 -07005042 PendingIntent pi = PendingIntent.getBroadcast(getContext(),
Jean-Michel Triviec84dae2020-10-22 17:12:37 -07005043 0/*requestCode, ignored*/, mediaButtonIntent,
5044 PendingIntent.FLAG_IMMUTABLE);
Jean-Michel Trivib839b832013-03-30 14:17:45 -07005045 unregisterMediaButtonIntent(pi);
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005046 }
5047
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005048 /**
Dianne Hackborn961cae92013-03-20 14:59:43 -07005049 * Unregister the receiver of MEDIA_BUTTON intents.
5050 * @param eventReceiver same PendingIntent that was registed with
5051 * {@link #registerMediaButtonEventReceiver(PendingIntent)}.
RoboErikb214efb2014-07-24 13:20:30 -07005052 * @deprecated Use {@link MediaSession} instead.
Dianne Hackborn961cae92013-03-20 14:59:43 -07005053 */
RoboErikb214efb2014-07-24 13:20:30 -07005054 @Deprecated
Dianne Hackborn961cae92013-03-20 14:59:43 -07005055 public void unregisterMediaButtonEventReceiver(PendingIntent eventReceiver) {
5056 if (eventReceiver == null) {
5057 return;
5058 }
Jean-Michel Trivib839b832013-03-30 14:17:45 -07005059 unregisterMediaButtonIntent(eventReceiver);
Dianne Hackborn961cae92013-03-20 14:59:43 -07005060 }
5061
5062 /**
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005063 * @hide
5064 */
Jean-Michel Trivib839b832013-03-30 14:17:45 -07005065 public void unregisterMediaButtonIntent(PendingIntent pi) {
Marco Nelissen29f16932015-04-17 09:50:56 -07005066 MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(getContext());
RoboErik430fc482014-06-12 15:49:20 -07005067 helper.removeMediaButtonListener(pi);
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005068 }
Jean-Michel Trivi178889e2011-08-15 17:17:52 -07005069
5070 /**
Jean-Michel Trivi178889e2011-08-15 17:17:52 -07005071 * Registers the remote control client for providing information to display on the remote
5072 * controls.
Jean-Michel Trivi466ade52011-09-01 10:02:30 -07005073 * @param rcClient The remote control client from which remote controls will receive
5074 * information to display.
5075 * @see RemoteControlClient
RoboErikb214efb2014-07-24 13:20:30 -07005076 * @deprecated Use {@link MediaSession} instead.
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005077 */
RoboErikb214efb2014-07-24 13:20:30 -07005078 @Deprecated
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005079 public void registerRemoteControlClient(RemoteControlClient rcClient) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005080 if ((rcClient == null) || (rcClient.getRcMediaIntent() == null)) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005081 return;
5082 }
Marco Nelissen29f16932015-04-17 09:50:56 -07005083 rcClient.registerWithSession(MediaSessionLegacyHelper.getHelper(getContext()));
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005084 }
5085
5086 /**
Jean-Michel Trivifcd693a2011-08-11 13:53:55 -07005087 * Unregisters the remote control client that was providing information to display on the
Jean-Michel Trivi466ade52011-09-01 10:02:30 -07005088 * remote controls.
5089 * @param rcClient The remote control client to unregister.
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005090 * @see #registerRemoteControlClient(RemoteControlClient)
RoboErikb214efb2014-07-24 13:20:30 -07005091 * @deprecated Use {@link MediaSession} instead.
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005092 */
RoboErikb214efb2014-07-24 13:20:30 -07005093 @Deprecated
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005094 public void unregisterRemoteControlClient(RemoteControlClient rcClient) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005095 if ((rcClient == null) || (rcClient.getRcMediaIntent() == null)) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005096 return;
5097 }
Marco Nelissen29f16932015-04-17 09:50:56 -07005098 rcClient.unregisterWithSession(MediaSessionLegacyHelper.getHelper(getContext()));
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005099 }
5100
Jean-Michel Trivi44413e52011-08-23 18:20:03 -07005101 /**
RoboErika66c40b2014-08-15 15:21:41 -07005102 * Registers a {@link RemoteController} instance for it to receive media
5103 * metadata updates and playback state information from applications using
5104 * {@link RemoteControlClient}, and control their playback.
5105 * <p>
John Spurlockee5ad722015-03-03 16:17:21 -05005106 * Registration requires the {@link RemoteController.OnClientUpdateListener} listener to be
RoboErika66c40b2014-08-15 15:21:41 -07005107 * one of the enabled notification listeners (see
Jean-Michel Trivif108cdd92013-09-27 18:37:36 -07005108 * {@link android.service.notification.NotificationListenerService}).
RoboErika66c40b2014-08-15 15:21:41 -07005109 *
Jean-Michel Trivia83487e2013-09-17 21:19:30 -07005110 * @param rctlr the object to register.
RoboErika66c40b2014-08-15 15:21:41 -07005111 * @return true if the {@link RemoteController} was successfully registered,
5112 * false if an error occurred, due to an internal system error, or
5113 * insufficient permissions.
RoboErikb214efb2014-07-24 13:20:30 -07005114 * @deprecated Use
RoboErika66c40b2014-08-15 15:21:41 -07005115 * {@link MediaSessionManager#addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, ComponentName)}
5116 * and {@link MediaController} instead.
Jean-Michel Trivi7ddd2262013-09-01 18:06:45 -07005117 */
RoboErikb214efb2014-07-24 13:20:30 -07005118 @Deprecated
Jean-Michel Trivi7ddd2262013-09-01 18:06:45 -07005119 public boolean registerRemoteController(RemoteController rctlr) {
5120 if (rctlr == null) {
5121 return false;
5122 }
RoboErik430fc482014-06-12 15:49:20 -07005123 rctlr.startListeningToSessions();
5124 return true;
Jean-Michel Trivi7ddd2262013-09-01 18:06:45 -07005125 }
5126
5127 /**
RoboErika66c40b2014-08-15 15:21:41 -07005128 * Unregisters a {@link RemoteController}, causing it to no longer receive
5129 * media metadata and playback state information, and no longer be capable
5130 * of controlling playback.
5131 *
Jean-Michel Trivia83487e2013-09-17 21:19:30 -07005132 * @param rctlr the object to unregister.
RoboErikb214efb2014-07-24 13:20:30 -07005133 * @deprecated Use
RoboErika66c40b2014-08-15 15:21:41 -07005134 * {@link MediaSessionManager#removeOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener)}
5135 * instead.
Jean-Michel Trivi7ddd2262013-09-01 18:06:45 -07005136 */
RoboErikb214efb2014-07-24 13:20:30 -07005137 @Deprecated
Jean-Michel Trivi7ddd2262013-09-01 18:06:45 -07005138 public void unregisterRemoteController(RemoteController rctlr) {
5139 if (rctlr == null) {
5140 return;
5141 }
RoboErik430fc482014-06-12 15:49:20 -07005142 rctlr.stopListeningToSessions();
Jean-Michel Trivi7ddd2262013-09-01 18:06:45 -07005143 }
5144
Jean-Michel Trivic3c4bab2013-04-19 08:56:50 -07005145
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005146 //====================================================================
5147 // Audio policy
Jean-Michel Trivic3c4bab2013-04-19 08:56:50 -07005148 /**
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005149 * @hide
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005150 * Register the given {@link AudioPolicy}.
5151 * This call is synchronous and blocks until the registration process successfully completed
5152 * or failed to complete.
Jean-Michel Trivi1b3541d2014-11-25 12:53:41 -08005153 * @param policy the non-null {@link AudioPolicy} to register.
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005154 * @return {@link #ERROR} if there was an error communicating with the registration service
5155 * or if the user doesn't have the required
Jean-Michel Trivic4557822023-01-23 18:19:52 +00005156 * {@link Manifest.permission#MODIFY_AUDIO_ROUTING} permission,
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005157 * {@link #SUCCESS} otherwise.
5158 */
Jean-Michel Trivi1b3541d2014-11-25 12:53:41 -08005159 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00005160 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Jean-Michel Trivi1b3541d2014-11-25 12:53:41 -08005161 public int registerAudioPolicy(@NonNull AudioPolicy policy) {
Robert Berry669be042019-01-22 18:48:35 -05005162 return registerAudioPolicyStatic(policy);
5163 }
5164
5165 static int registerAudioPolicyStatic(@NonNull AudioPolicy policy) {
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005166 if (policy == null) {
5167 throw new IllegalArgumentException("Illegal null AudioPolicy argument");
5168 }
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07005169 final IAudioService service = getService();
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005170 try {
Kevin Rocard2fe8b8b2019-02-20 11:25:23 -08005171 MediaProjection projection = policy.getMediaProjection();
Jean-Michel Trivi0212be52014-11-24 14:43:10 -08005172 String regId = service.registerAudioPolicy(policy.getConfig(), policy.cb(),
Jean-Michel Triviee7d2452019-03-19 12:29:27 -07005173 policy.hasFocusListener(), policy.isFocusPolicy(), policy.isTestFocusPolicy(),
5174 policy.isVolumeController(),
Kevin Rocard2fe8b8b2019-02-20 11:25:23 -08005175 projection == null ? null : projection.getProjection());
Jean-Michel Trivi8fdb0d42014-07-16 19:08:37 -07005176 if (regId == null) {
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005177 return ERROR;
Jean-Michel Trivi8fdb0d42014-07-16 19:08:37 -07005178 } else {
5179 policy.setRegistration(regId);
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005180 }
Jean-Michel Trivi8fdb0d42014-07-16 19:08:37 -07005181 // successful registration
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005182 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07005183 throw e.rethrowFromSystemServer();
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005184 }
5185 return SUCCESS;
5186 }
5187
5188 /**
5189 * @hide
Jean-Michel Trivicada3722019-03-02 16:14:12 -08005190 * Unregisters an {@link AudioPolicy} asynchronously.
Jean-Michel Trivi1b3541d2014-11-25 12:53:41 -08005191 * @param policy the non-null {@link AudioPolicy} to unregister.
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005192 */
Jean-Michel Trivi1b3541d2014-11-25 12:53:41 -08005193 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00005194 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Jean-Michel Trivi1b3541d2014-11-25 12:53:41 -08005195 public void unregisterAudioPolicyAsync(@NonNull AudioPolicy policy) {
Robert Berry669be042019-01-22 18:48:35 -05005196 unregisterAudioPolicyAsyncStatic(policy);
5197 }
5198
5199 static void unregisterAudioPolicyAsyncStatic(@NonNull AudioPolicy policy) {
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005200 if (policy == null) {
5201 throw new IllegalArgumentException("Illegal null AudioPolicy argument");
5202 }
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07005203 final IAudioService service = getService();
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005204 try {
Jean-Michel Trivi0212be52014-11-24 14:43:10 -08005205 service.unregisterAudioPolicyAsync(policy.cb());
Jean-Michel Trivif5f3e682020-07-29 15:45:48 -07005206 policy.reset();
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005207 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07005208 throw e.rethrowFromSystemServer();
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005209 }
5210 }
5211
Jean-Michel Trivicada3722019-03-02 16:14:12 -08005212 /**
5213 * @hide
5214 * Unregisters an {@link AudioPolicy} synchronously.
5215 * This method also invalidates all {@link AudioRecord} and {@link AudioTrack} objects
5216 * associated with mixes of this policy.
5217 * @param policy the non-null {@link AudioPolicy} to unregister.
5218 */
5219 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00005220 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Jean-Michel Trivicada3722019-03-02 16:14:12 -08005221 public void unregisterAudioPolicy(@NonNull AudioPolicy policy) {
5222 Preconditions.checkNotNull(policy, "Illegal null AudioPolicy argument");
5223 final IAudioService service = getService();
5224 try {
5225 policy.invalidateCaptorsAndInjectors();
5226 service.unregisterAudioPolicy(policy.cb());
Jean-Michel Trivif5f3e682020-07-29 15:45:48 -07005227 policy.reset();
Jean-Michel Trivicada3722019-03-02 16:14:12 -08005228 } catch (RemoteException e) {
5229 throw e.rethrowFromSystemServer();
5230 }
5231 }
5232
Jean-Michel Trivi0c88f492019-04-12 15:43:56 -07005233 /**
5234 * @hide
5235 * @return true if an AudioPolicy was previously registered
5236 */
5237 @TestApi
5238 public boolean hasRegisteredDynamicPolicy() {
5239 final IAudioService service = getService();
5240 try {
5241 return service.hasRegisteredDynamicPolicy();
5242 } catch (RemoteException e) {
5243 throw e.rethrowFromSystemServer();
5244 }
5245 }
5246
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -08005247 //====================================================================
5248 // Notification of playback activity & playback configuration
5249 /**
5250 * Interface for receiving update notifications about the playback activity on the system.
5251 * Extend this abstract class and register it with
5252 * {@link AudioManager#registerAudioPlaybackCallback(AudioPlaybackCallback, Handler)}
5253 * to be notified.
5254 * Use {@link AudioManager#getActivePlaybackConfigurations()} to query the current
5255 * configuration.
5256 * @see AudioPlaybackConfiguration
5257 */
5258 public static abstract class AudioPlaybackCallback {
5259 /**
5260 * Called whenever the playback activity and configuration has changed.
5261 * @param configs list containing the results of
5262 * {@link AudioManager#getActivePlaybackConfigurations()}.
5263 */
5264 public void onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configs) {}
5265 }
5266
5267 private static class AudioPlaybackCallbackInfo {
5268 final AudioPlaybackCallback mCb;
5269 final Handler mHandler;
5270 AudioPlaybackCallbackInfo(AudioPlaybackCallback cb, Handler handler) {
5271 mCb = cb;
5272 mHandler = handler;
5273 }
5274 }
5275
5276 private final static class PlaybackConfigChangeCallbackData {
5277 final AudioPlaybackCallback mCb;
5278 final List<AudioPlaybackConfiguration> mConfigs;
5279
5280 PlaybackConfigChangeCallbackData(AudioPlaybackCallback cb,
5281 List<AudioPlaybackConfiguration> configs) {
5282 mCb = cb;
5283 mConfigs = configs;
5284 }
5285 }
5286
5287 /**
5288 * Register a callback to be notified of audio playback changes through
5289 * {@link AudioPlaybackCallback}
5290 * @param cb non-null callback to register
5291 * @param handler the {@link Handler} object for the thread on which to execute
5292 * the callback. If <code>null</code>, the {@link Handler} associated with the main
5293 * {@link Looper} will be used.
5294 */
Jean-Michel Trivi1ec66182019-12-30 09:53:04 -08005295 public void registerAudioPlaybackCallback(@NonNull AudioPlaybackCallback cb,
5296 @Nullable Handler handler)
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -08005297 {
5298 if (cb == null) {
5299 throw new IllegalArgumentException("Illegal null AudioPlaybackCallback argument");
5300 }
5301
5302 synchronized(mPlaybackCallbackLock) {
5303 // lazy initialization of the list of playback callbacks
5304 if (mPlaybackCallbackList == null) {
5305 mPlaybackCallbackList = new ArrayList<AudioPlaybackCallbackInfo>();
5306 }
5307 final int oldCbCount = mPlaybackCallbackList.size();
5308 if (!hasPlaybackCallback_sync(cb)) {
5309 mPlaybackCallbackList.add(new AudioPlaybackCallbackInfo(cb,
5310 new ServiceEventHandlerDelegate(handler).getHandler()));
5311 final int newCbCount = mPlaybackCallbackList.size();
5312 if ((oldCbCount == 0) && (newCbCount > 0)) {
5313 // register binder for callbacks
5314 try {
5315 getService().registerPlaybackCallback(mPlayCb);
5316 } catch (RemoteException e) {
5317 throw e.rethrowFromSystemServer();
5318 }
5319 }
5320 } else {
5321 Log.w(TAG, "attempt to call registerAudioPlaybackCallback() on a previously"
5322 + "registered callback");
5323 }
5324 }
5325 }
5326
5327 /**
5328 * Unregister an audio playback callback previously registered with
5329 * {@link #registerAudioPlaybackCallback(AudioPlaybackCallback, Handler)}.
5330 * @param cb non-null callback to unregister
5331 */
5332 public void unregisterAudioPlaybackCallback(@NonNull AudioPlaybackCallback cb) {
5333 if (cb == null) {
5334 throw new IllegalArgumentException("Illegal null AudioPlaybackCallback argument");
5335 }
5336 synchronized(mPlaybackCallbackLock) {
5337 if (mPlaybackCallbackList == null) {
5338 Log.w(TAG, "attempt to call unregisterAudioPlaybackCallback() on a callback"
5339 + " that was never registered");
5340 return;
5341 }
5342 final int oldCbCount = mPlaybackCallbackList.size();
5343 if (removePlaybackCallback_sync(cb)) {
5344 final int newCbCount = mPlaybackCallbackList.size();
5345 if ((oldCbCount > 0) && (newCbCount == 0)) {
5346 // unregister binder for callbacks
5347 try {
5348 getService().unregisterPlaybackCallback(mPlayCb);
5349 } catch (RemoteException e) {
5350 throw e.rethrowFromSystemServer();
5351 }
5352 }
5353 } else {
5354 Log.w(TAG, "attempt to call unregisterAudioPlaybackCallback() on a callback"
5355 + " already unregistered or never registered");
5356 }
5357 }
5358 }
5359
5360 /**
5361 * Returns the current active audio playback configurations of the device
5362 * @return a non-null list of playback configurations. An empty list indicates there is no
5363 * playback active when queried.
5364 * @see AudioPlaybackConfiguration
5365 */
5366 public @NonNull List<AudioPlaybackConfiguration> getActivePlaybackConfigurations() {
5367 final IAudioService service = getService();
5368 try {
5369 return service.getActivePlaybackConfigurations();
5370 } catch (RemoteException e) {
5371 throw e.rethrowFromSystemServer();
5372 }
5373 }
5374
5375 /**
5376 * All operations on this list are sync'd on mPlaybackCallbackLock.
5377 * List is lazy-initialized in
5378 * {@link #registerAudioPlaybackCallback(AudioPlaybackCallback, Handler)}.
5379 * List can be null.
5380 */
5381 private List<AudioPlaybackCallbackInfo> mPlaybackCallbackList;
5382 private final Object mPlaybackCallbackLock = new Object();
5383
5384 /**
5385 * Must be called synchronized on mPlaybackCallbackLock
5386 */
5387 private boolean hasPlaybackCallback_sync(@NonNull AudioPlaybackCallback cb) {
5388 if (mPlaybackCallbackList != null) {
5389 for (int i=0 ; i < mPlaybackCallbackList.size() ; i++) {
5390 if (cb.equals(mPlaybackCallbackList.get(i).mCb)) {
5391 return true;
5392 }
5393 }
5394 }
5395 return false;
5396 }
5397
5398 /**
5399 * Must be called synchronized on mPlaybackCallbackLock
5400 */
5401 private boolean removePlaybackCallback_sync(@NonNull AudioPlaybackCallback cb) {
5402 if (mPlaybackCallbackList != null) {
5403 for (int i=0 ; i < mPlaybackCallbackList.size() ; i++) {
5404 if (cb.equals(mPlaybackCallbackList.get(i).mCb)) {
5405 mPlaybackCallbackList.remove(i);
5406 return true;
5407 }
5408 }
5409 }
5410 return false;
5411 }
5412
5413 private final IPlaybackConfigDispatcher mPlayCb = new IPlaybackConfigDispatcher.Stub() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07005414 @Override
Jean-Michel Trivi776a3992017-09-12 16:45:34 -07005415 public void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs,
5416 boolean flush) {
5417 if (flush) {
5418 Binder.flushPendingCommands();
5419 }
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -08005420 synchronized(mPlaybackCallbackLock) {
5421 if (mPlaybackCallbackList != null) {
5422 for (int i=0 ; i < mPlaybackCallbackList.size() ; i++) {
5423 final AudioPlaybackCallbackInfo arci = mPlaybackCallbackList.get(i);
5424 if (arci.mHandler != null) {
5425 final Message m = arci.mHandler.obtainMessage(
5426 MSSG_PLAYBACK_CONFIG_CHANGE/*what*/,
5427 new PlaybackConfigChangeCallbackData(arci.mCb, configs)/*obj*/);
5428 arci.mHandler.sendMessage(m);
5429 }
5430 }
5431 }
5432 }
5433 }
5434
5435 };
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005436
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005437 //====================================================================
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -08005438 // Notification of recording activity & recording configuration
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005439 /**
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005440 * Interface for receiving update notifications about the recording configuration. Extend
5441 * this abstract class and register it with
5442 * {@link AudioManager#registerAudioRecordingCallback(AudioRecordingCallback, Handler)}
5443 * to be notified.
Jean-Michel Trivi598c0c92016-03-07 14:13:03 -08005444 * Use {@link AudioManager#getActiveRecordingConfigurations()} to query the current
5445 * configuration.
Jean-Michel Trivi7872aff2016-07-06 14:34:52 -07005446 * @see AudioRecordingConfiguration
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005447 */
5448 public static abstract class AudioRecordingCallback {
5449 /**
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005450 * Called whenever the device recording configuration has changed.
Jean-Michel Trivif04fab12016-05-19 10:42:35 -07005451 * @param configs list containing the results of
Jean-Michel Trivi598c0c92016-03-07 14:13:03 -08005452 * {@link AudioManager#getActiveRecordingConfigurations()}.
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005453 */
Jean-Michel Trivif04fab12016-05-19 10:42:35 -07005454 public void onRecordingConfigChanged(List<AudioRecordingConfiguration> configs) {}
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005455 }
5456
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005457 private static class AudioRecordingCallbackInfo {
5458 final AudioRecordingCallback mCb;
5459 final Handler mHandler;
5460 AudioRecordingCallbackInfo(AudioRecordingCallback cb, Handler handler) {
5461 mCb = cb;
5462 mHandler = handler;
5463 }
5464 }
5465
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -08005466 private final static class RecordConfigChangeCallbackData {
5467 final AudioRecordingCallback mCb;
Jean-Michel Trivif04fab12016-05-19 10:42:35 -07005468 final List<AudioRecordingConfiguration> mConfigs;
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -08005469
5470 RecordConfigChangeCallbackData(AudioRecordingCallback cb,
Jean-Michel Trivif04fab12016-05-19 10:42:35 -07005471 List<AudioRecordingConfiguration> configs) {
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -08005472 mCb = cb;
5473 mConfigs = configs;
5474 }
5475 }
5476
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005477 /**
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005478 * Register a callback to be notified of audio recording changes through
5479 * {@link AudioRecordingCallback}
5480 * @param cb non-null callback to register
5481 * @param handler the {@link Handler} object for the thread on which to execute
5482 * the callback. If <code>null</code>, the {@link Handler} associated with the main
5483 * {@link Looper} will be used.
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005484 */
Jean-Michel Trivi1ec66182019-12-30 09:53:04 -08005485 public void registerAudioRecordingCallback(@NonNull AudioRecordingCallback cb,
5486 @Nullable Handler handler)
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005487 {
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005488 if (cb == null) {
5489 throw new IllegalArgumentException("Illegal null AudioRecordingCallback argument");
5490 }
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005491
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005492 synchronized(mRecordCallbackLock) {
5493 // lazy initialization of the list of recording callbacks
5494 if (mRecordCallbackList == null) {
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005495 mRecordCallbackList = new ArrayList<AudioRecordingCallbackInfo>();
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005496 }
5497 final int oldCbCount = mRecordCallbackList.size();
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005498 if (!hasRecordCallback_sync(cb)) {
5499 mRecordCallbackList.add(new AudioRecordingCallbackInfo(cb,
5500 new ServiceEventHandlerDelegate(handler).getHandler()));
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005501 final int newCbCount = mRecordCallbackList.size();
5502 if ((oldCbCount == 0) && (newCbCount > 0)) {
5503 // register binder for callbacks
5504 final IAudioService service = getService();
5505 try {
5506 service.registerRecordingCallback(mRecCb);
5507 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07005508 throw e.rethrowFromSystemServer();
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005509 }
5510 }
5511 } else {
5512 Log.w(TAG, "attempt to call registerAudioRecordingCallback() on a previously"
5513 + "registered callback");
5514 }
5515 }
5516 }
5517
5518 /**
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005519 * Unregister an audio recording callback previously registered with
5520 * {@link #registerAudioRecordingCallback(AudioRecordingCallback, Handler)}.
5521 * @param cb non-null callback to unregister
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005522 */
5523 public void unregisterAudioRecordingCallback(@NonNull AudioRecordingCallback cb) {
5524 if (cb == null) {
5525 throw new IllegalArgumentException("Illegal null AudioRecordingCallback argument");
5526 }
5527 synchronized(mRecordCallbackLock) {
5528 if (mRecordCallbackList == null) {
5529 return;
5530 }
5531 final int oldCbCount = mRecordCallbackList.size();
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005532 if (removeRecordCallback_sync(cb)) {
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005533 final int newCbCount = mRecordCallbackList.size();
5534 if ((oldCbCount > 0) && (newCbCount == 0)) {
5535 // unregister binder for callbacks
5536 final IAudioService service = getService();
5537 try {
5538 service.unregisterRecordingCallback(mRecCb);
5539 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07005540 throw e.rethrowFromSystemServer();
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005541 }
5542 }
5543 } else {
5544 Log.w(TAG, "attempt to call unregisterAudioRecordingCallback() on a callback"
5545 + " already unregistered or never registered");
5546 }
5547 }
5548 }
5549
5550 /**
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005551 * Returns the current active audio recording configurations of the device.
Jean-Michel Trivif04fab12016-05-19 10:42:35 -07005552 * @return a non-null list of recording configurations. An empty list indicates there is
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005553 * no recording active when queried.
Jean-Michel Trivi7872aff2016-07-06 14:34:52 -07005554 * @see AudioRecordingConfiguration
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005555 */
Jean-Michel Trivif04fab12016-05-19 10:42:35 -07005556 public @NonNull List<AudioRecordingConfiguration> getActiveRecordingConfigurations() {
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005557 final IAudioService service = getService();
5558 try {
Jean-Michel Trivi598c0c92016-03-07 14:13:03 -08005559 return service.getActiveRecordingConfigurations();
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005560 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07005561 throw e.rethrowFromSystemServer();
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005562 }
5563 }
5564
5565 /**
5566 * constants for the recording events, to keep in sync
5567 * with frameworks/av/include/media/AudioPolicy.h
5568 */
5569 /** @hide */
Mikhail Naganova00883d2019-04-18 12:36:27 -07005570 public static final int RECORD_CONFIG_EVENT_NONE = -1;
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005571 /** @hide */
Mikhail Naganova00883d2019-04-18 12:36:27 -07005572 public static final int RECORD_CONFIG_EVENT_START = 0;
5573 /** @hide */
5574 public static final int RECORD_CONFIG_EVENT_STOP = 1;
5575 /** @hide */
5576 public static final int RECORD_CONFIG_EVENT_UPDATE = 2;
5577 /** @hide */
Mikhail Naganovcfe4c262019-05-09 09:02:47 -07005578 public static final int RECORD_CONFIG_EVENT_RELEASE = 3;
Mikhail Naganova00883d2019-04-18 12:36:27 -07005579 /**
5580 * keep in sync with frameworks/native/include/audiomanager/AudioManager.h
5581 */
5582 /** @hide */
5583 public static final int RECORD_RIID_INVALID = -1;
5584 /** @hide */
5585 public static final int RECORDER_STATE_STARTED = 0;
5586 /** @hide */
5587 public static final int RECORDER_STATE_STOPPED = 1;
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005588
5589 /**
5590 * All operations on this list are sync'd on mRecordCallbackLock.
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005591 * List is lazy-initialized in
5592 * {@link #registerAudioRecordingCallback(AudioRecordingCallback, Handler)}.
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005593 * List can be null.
5594 */
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005595 private List<AudioRecordingCallbackInfo> mRecordCallbackList;
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005596 private final Object mRecordCallbackLock = new Object();
5597
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005598 /**
5599 * Must be called synchronized on mRecordCallbackLock
5600 */
5601 private boolean hasRecordCallback_sync(@NonNull AudioRecordingCallback cb) {
5602 if (mRecordCallbackList != null) {
5603 for (int i=0 ; i < mRecordCallbackList.size() ; i++) {
5604 if (cb.equals(mRecordCallbackList.get(i).mCb)) {
5605 return true;
5606 }
5607 }
5608 }
5609 return false;
5610 }
5611
5612 /**
5613 * Must be called synchronized on mRecordCallbackLock
5614 */
5615 private boolean removeRecordCallback_sync(@NonNull AudioRecordingCallback cb) {
5616 if (mRecordCallbackList != null) {
5617 for (int i=0 ; i < mRecordCallbackList.size() ; i++) {
5618 if (cb.equals(mRecordCallbackList.get(i).mCb)) {
5619 mRecordCallbackList.remove(i);
5620 return true;
5621 }
5622 }
5623 }
5624 return false;
5625 }
5626
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005627 private final IRecordingConfigDispatcher mRecCb = new IRecordingConfigDispatcher.Stub() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07005628 @Override
Jean-Michel Trivif04fab12016-05-19 10:42:35 -07005629 public void dispatchRecordingConfigChange(List<AudioRecordingConfiguration> configs) {
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005630 synchronized(mRecordCallbackLock) {
5631 if (mRecordCallbackList != null) {
5632 for (int i=0 ; i < mRecordCallbackList.size() ; i++) {
5633 final AudioRecordingCallbackInfo arci = mRecordCallbackList.get(i);
5634 if (arci.mHandler != null) {
5635 final Message m = arci.mHandler.obtainMessage(
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -08005636 MSSG_RECORDING_CONFIG_CHANGE/*what*/,
5637 new RecordConfigChangeCallbackData(arci.mCb, configs)/*obj*/);
Jean-Michel Trivib919f6f2016-01-12 18:27:17 -08005638 arci.mHandler.sendMessage(m);
5639 }
5640 }
5641 }
5642 }
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08005643 }
5644
5645 };
5646
5647 //=====================================================================
5648
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07005649 /**
Eric Laurent4050c932009-07-08 02:52:14 -07005650 * @hide
5651 * Reload audio settings. This method is called by Settings backup
5652 * agent when audio settings are restored and causes the AudioService
5653 * to read and apply restored settings.
5654 */
Mathew Inwood8e742f92020-10-27 11:47:29 +00005655 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Eric Laurent4050c932009-07-08 02:52:14 -07005656 public void reloadAudioSettings() {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07005657 final IAudioService service = getService();
Eric Laurent4050c932009-07-08 02:52:14 -07005658 try {
5659 service.reloadAudioSettings();
5660 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07005661 throw e.rethrowFromSystemServer();
Eric Laurent4050c932009-07-08 02:52:14 -07005662 }
5663 }
5664
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005665 /**
5666 * {@hide}
5667 */
Glenn Kasten30c918c2011-11-10 17:56:41 -08005668 private final IBinder mICallBack = new Binder();
Glenn Kasten7f9b1fa2010-03-31 11:11:36 -07005669
5670 /**
5671 * Checks whether the phone is in silent mode, with or without vibrate.
5672 *
5673 * @return true if phone is in silent mode, with or without vibrate.
5674 *
5675 * @see #getRingerMode()
5676 *
5677 * @hide pending API Council approval
5678 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005679 @UnsupportedAppUsage
Glenn Kasten7f9b1fa2010-03-31 11:11:36 -07005680 public boolean isSilentMode() {
5681 int ringerMode = getRingerMode();
5682 boolean silentMode =
5683 (ringerMode == RINGER_MODE_SILENT) ||
5684 (ringerMode == RINGER_MODE_VIBRATE);
5685 return silentMode;
5686 }
5687
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005688 // This section re-defines new output device constants from AudioSystem, because the AudioSystem
5689 // class is not used by other parts of the framework, which instead use definitions and methods
5690 // from AudioManager. AudioSystem is an internal class used by AudioManager and AudioService.
5691
Eric Laurent948d3272014-05-16 15:18:45 -07005692 /** @hide
Wonsik Kimd7c29182014-05-27 10:38:21 +09005693 * The audio device code for representing "no device." */
5694 public static final int DEVICE_NONE = AudioSystem.DEVICE_NONE;
5695 /** @hide
Eric Laurent948d3272014-05-16 15:18:45 -07005696 * The audio output device code for the small speaker at the front of the device used
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005697 * when placing calls. Does not refer to an in-ear headphone without attached microphone,
5698 * such as earbuds, earphones, or in-ear monitors (IEM). Those would be handled as a
5699 * {@link #DEVICE_OUT_WIRED_HEADPHONE}.
5700 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005701 @UnsupportedAppUsage
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005702 public static final int DEVICE_OUT_EARPIECE = AudioSystem.DEVICE_OUT_EARPIECE;
Eric Laurent948d3272014-05-16 15:18:45 -07005703 /** @hide
5704 * The audio output device code for the built-in speaker */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005705 @UnsupportedAppUsage
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005706 public static final int DEVICE_OUT_SPEAKER = AudioSystem.DEVICE_OUT_SPEAKER;
Eric Laurent948d3272014-05-16 15:18:45 -07005707 /** @hide
5708 * The audio output device code for a wired headset with attached microphone */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005709 @UnsupportedAppUsage
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005710 public static final int DEVICE_OUT_WIRED_HEADSET = AudioSystem.DEVICE_OUT_WIRED_HEADSET;
Eric Laurent948d3272014-05-16 15:18:45 -07005711 /** @hide
5712 * The audio output device code for a wired headphone without attached microphone */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005713 @UnsupportedAppUsage
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005714 public static final int DEVICE_OUT_WIRED_HEADPHONE = AudioSystem.DEVICE_OUT_WIRED_HEADPHONE;
Eric Laurent948d3272014-05-16 15:18:45 -07005715 /** @hide
Paul McLean145c9532017-08-04 11:12:19 -06005716 * The audio output device code for a USB headphone with attached microphone */
5717 public static final int DEVICE_OUT_USB_HEADSET = AudioSystem.DEVICE_OUT_USB_HEADSET;
5718 /** @hide
Eric Laurent948d3272014-05-16 15:18:45 -07005719 * The audio output device code for generic Bluetooth SCO, for voice */
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005720 public static final int DEVICE_OUT_BLUETOOTH_SCO = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
Eric Laurent948d3272014-05-16 15:18:45 -07005721 /** @hide
5722 * The audio output device code for Bluetooth SCO Headset Profile (HSP) and
5723 * Hands-Free Profile (HFP), for voice
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005724 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005725 @UnsupportedAppUsage
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005726 public static final int DEVICE_OUT_BLUETOOTH_SCO_HEADSET =
5727 AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
Eric Laurent948d3272014-05-16 15:18:45 -07005728 /** @hide
5729 * The audio output device code for Bluetooth SCO car audio, for voice */
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005730 public static final int DEVICE_OUT_BLUETOOTH_SCO_CARKIT =
5731 AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
Eric Laurent948d3272014-05-16 15:18:45 -07005732 /** @hide
5733 * The audio output device code for generic Bluetooth A2DP, for music */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005734 @UnsupportedAppUsage
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005735 public static final int DEVICE_OUT_BLUETOOTH_A2DP = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
Eric Laurent948d3272014-05-16 15:18:45 -07005736 /** @hide
5737 * The audio output device code for Bluetooth A2DP headphones, for music */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005738 @UnsupportedAppUsage
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005739 public static final int DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES =
5740 AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
Eric Laurent948d3272014-05-16 15:18:45 -07005741 /** @hide
5742 * The audio output device code for Bluetooth A2DP external speaker, for music */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005743 @UnsupportedAppUsage
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005744 public static final int DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER =
5745 AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
Eric Laurent948d3272014-05-16 15:18:45 -07005746 /** @hide
5747 * The audio output device code for S/PDIF (legacy) or HDMI
5748 * Deprecated: replaced by {@link #DEVICE_OUT_HDMI} */
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005749 public static final int DEVICE_OUT_AUX_DIGITAL = AudioSystem.DEVICE_OUT_AUX_DIGITAL;
Eric Laurent948d3272014-05-16 15:18:45 -07005750 /** @hide
5751 * The audio output device code for HDMI */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005752 @UnsupportedAppUsage
Eric Laurent948d3272014-05-16 15:18:45 -07005753 public static final int DEVICE_OUT_HDMI = AudioSystem.DEVICE_OUT_HDMI;
5754 /** @hide
5755 * The audio output device code for an analog wired headset attached via a
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005756 * docking station
5757 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005758 @UnsupportedAppUsage
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005759 public static final int DEVICE_OUT_ANLG_DOCK_HEADSET = AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET;
Eric Laurent948d3272014-05-16 15:18:45 -07005760 /** @hide
5761 * The audio output device code for a digital wired headset attached via a
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005762 * docking station
5763 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005764 @UnsupportedAppUsage
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005765 public static final int DEVICE_OUT_DGTL_DOCK_HEADSET = AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET;
Eric Laurent948d3272014-05-16 15:18:45 -07005766 /** @hide
5767 * The audio output device code for a USB audio accessory. The accessory is in USB host
Eric Laurent59f48272012-04-05 19:42:21 -07005768 * mode and the Android device in USB device mode
5769 */
5770 public static final int DEVICE_OUT_USB_ACCESSORY = AudioSystem.DEVICE_OUT_USB_ACCESSORY;
Eric Laurent948d3272014-05-16 15:18:45 -07005771 /** @hide
5772 * The audio output device code for a USB audio device. The device is in USB device
Eric Laurent59f48272012-04-05 19:42:21 -07005773 * mode and the Android device in USB host mode
5774 */
5775 public static final int DEVICE_OUT_USB_DEVICE = AudioSystem.DEVICE_OUT_USB_DEVICE;
Eric Laurent948d3272014-05-16 15:18:45 -07005776 /** @hide
5777 * The audio output device code for projection output.
5778 */
5779 public static final int DEVICE_OUT_REMOTE_SUBMIX = AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
5780 /** @hide
5781 * The audio output device code the telephony voice TX path.
5782 */
5783 public static final int DEVICE_OUT_TELEPHONY_TX = AudioSystem.DEVICE_OUT_TELEPHONY_TX;
5784 /** @hide
5785 * The audio output device code for an analog jack with line impedance detected.
5786 */
5787 public static final int DEVICE_OUT_LINE = AudioSystem.DEVICE_OUT_LINE;
5788 /** @hide
5789 * The audio output device code for HDMI Audio Return Channel.
5790 */
5791 public static final int DEVICE_OUT_HDMI_ARC = AudioSystem.DEVICE_OUT_HDMI_ARC;
5792 /** @hide
Kuowei Lif898eae2020-10-27 16:41:18 +08005793 * The audio output device code for HDMI enhanced Audio Return Channel.
5794 */
5795 public static final int DEVICE_OUT_HDMI_EARC = AudioSystem.DEVICE_OUT_HDMI_EARC;
5796 /** @hide
Eric Laurent948d3272014-05-16 15:18:45 -07005797 * The audio output device code for S/PDIF digital connection.
5798 */
5799 public static final int DEVICE_OUT_SPDIF = AudioSystem.DEVICE_OUT_SPDIF;
5800 /** @hide
5801 * The audio output device code for built-in FM transmitter.
5802 */
5803 public static final int DEVICE_OUT_FM = AudioSystem.DEVICE_OUT_FM;
5804 /** @hide
Eric Laurentca94af02020-08-07 12:30:44 -07005805 * The audio output device code for echo reference injection point.
5806 */
5807 public static final int DEVICE_OUT_ECHO_CANCELLER = AudioSystem.DEVICE_OUT_ECHO_CANCELLER;
5808 /** @hide
Eric Laurent6239d7e2020-08-07 10:58:14 -07005809 * The audio output device code for a BLE audio headset.
5810 */
5811 public static final int DEVICE_OUT_BLE_HEADSET = AudioSystem.DEVICE_OUT_BLE_HEADSET;
5812 /** @hide
5813 * The audio output device code for a BLE audio speaker.
5814 */
5815 public static final int DEVICE_OUT_BLE_SPEAKER = AudioSystem.DEVICE_OUT_BLE_SPEAKER;
5816 /** @hide
Eric Laurent277373e2022-01-20 14:42:22 +01005817 * The audio output device code for a BLE audio brodcast group.
5818 */
5819 public static final int DEVICE_OUT_BLE_BROADCAST = AudioSystem.DEVICE_OUT_BLE_BROADCAST;
5820 /** @hide
Eric Laurent948d3272014-05-16 15:18:45 -07005821 * This is not used as a returned value from {@link #getDevicesForStream}, but could be
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005822 * used in the future in a set method to select whatever default device is chosen by the
5823 * platform-specific implementation.
5824 */
5825 public static final int DEVICE_OUT_DEFAULT = AudioSystem.DEVICE_OUT_DEFAULT;
5826
Eric Laurent948d3272014-05-16 15:18:45 -07005827 /** @hide
5828 * The audio input device code for default built-in microphone
5829 */
5830 public static final int DEVICE_IN_BUILTIN_MIC = AudioSystem.DEVICE_IN_BUILTIN_MIC;
5831 /** @hide
5832 * The audio input device code for a Bluetooth SCO headset
5833 */
5834 public static final int DEVICE_IN_BLUETOOTH_SCO_HEADSET =
5835 AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
5836 /** @hide
5837 * The audio input device code for wired headset microphone
5838 */
5839 public static final int DEVICE_IN_WIRED_HEADSET =
5840 AudioSystem.DEVICE_IN_WIRED_HEADSET;
5841 /** @hide
5842 * The audio input device code for HDMI
5843 */
5844 public static final int DEVICE_IN_HDMI =
5845 AudioSystem.DEVICE_IN_HDMI;
5846 /** @hide
Nick Chalko2e1f76a2018-10-25 10:19:10 -07005847 * The audio input device code for HDMI ARC
5848 */
5849 public static final int DEVICE_IN_HDMI_ARC =
5850 AudioSystem.DEVICE_IN_HDMI_ARC;
5851
5852 /** @hide
Kuowei Lif898eae2020-10-27 16:41:18 +08005853 * The audio input device code for HDMI EARC
5854 */
5855 public static final int DEVICE_IN_HDMI_EARC =
5856 AudioSystem.DEVICE_IN_HDMI_EARC;
5857
5858 /** @hide
Eric Laurent948d3272014-05-16 15:18:45 -07005859 * The audio input device code for telephony voice RX path
5860 */
5861 public static final int DEVICE_IN_TELEPHONY_RX =
5862 AudioSystem.DEVICE_IN_TELEPHONY_RX;
5863 /** @hide
5864 * The audio input device code for built-in microphone pointing to the back
5865 */
5866 public static final int DEVICE_IN_BACK_MIC =
5867 AudioSystem.DEVICE_IN_BACK_MIC;
5868 /** @hide
5869 * The audio input device code for analog from a docking station
5870 */
5871 public static final int DEVICE_IN_ANLG_DOCK_HEADSET =
5872 AudioSystem.DEVICE_IN_ANLG_DOCK_HEADSET;
5873 /** @hide
5874 * The audio input device code for digital from a docking station
5875 */
5876 public static final int DEVICE_IN_DGTL_DOCK_HEADSET =
5877 AudioSystem.DEVICE_IN_DGTL_DOCK_HEADSET;
5878 /** @hide
5879 * The audio input device code for a USB audio accessory. The accessory is in USB host
5880 * mode and the Android device in USB device mode
5881 */
5882 public static final int DEVICE_IN_USB_ACCESSORY =
5883 AudioSystem.DEVICE_IN_USB_ACCESSORY;
5884 /** @hide
5885 * The audio input device code for a USB audio device. The device is in USB device
5886 * mode and the Android device in USB host mode
5887 */
5888 public static final int DEVICE_IN_USB_DEVICE =
5889 AudioSystem.DEVICE_IN_USB_DEVICE;
5890 /** @hide
5891 * The audio input device code for a FM radio tuner
5892 */
5893 public static final int DEVICE_IN_FM_TUNER = AudioSystem.DEVICE_IN_FM_TUNER;
5894 /** @hide
5895 * The audio input device code for a TV tuner
5896 */
5897 public static final int DEVICE_IN_TV_TUNER = AudioSystem.DEVICE_IN_TV_TUNER;
5898 /** @hide
5899 * The audio input device code for an analog jack with line impedance detected
5900 */
5901 public static final int DEVICE_IN_LINE = AudioSystem.DEVICE_IN_LINE;
5902 /** @hide
5903 * The audio input device code for a S/PDIF digital connection
5904 */
5905 public static final int DEVICE_IN_SPDIF = AudioSystem.DEVICE_IN_SPDIF;
Terry Heo112c19e2014-07-07 10:25:38 +09005906 /** @hide
5907 * The audio input device code for audio loopback
5908 */
5909 public static final int DEVICE_IN_LOOPBACK = AudioSystem.DEVICE_IN_LOOPBACK;
Eric Laurent6239d7e2020-08-07 10:58:14 -07005910 /** @hide
Eric Laurentca94af02020-08-07 12:30:44 -07005911 * The audio input device code for an echo reference capture point.
5912 */
5913 public static final int DEVICE_IN_ECHO_REFERENCE = AudioSystem.DEVICE_IN_ECHO_REFERENCE;
5914 /** @hide
Eric Laurent6239d7e2020-08-07 10:58:14 -07005915 * The audio input device code for a BLE audio headset.
5916 */
5917 public static final int DEVICE_IN_BLE_HEADSET = AudioSystem.DEVICE_IN_BLE_HEADSET;
Eric Laurent948d3272014-05-16 15:18:45 -07005918
5919 /**
5920 * Return true if the device code corresponds to an output device.
5921 * @hide
5922 */
5923 public static boolean isOutputDevice(int device)
5924 {
5925 return (device & AudioSystem.DEVICE_BIT_IN) == 0;
5926 }
5927
5928 /**
5929 * Return true if the device code corresponds to an input device.
5930 * @hide
5931 */
5932 public static boolean isInputDevice(int device)
5933 {
5934 return (device & AudioSystem.DEVICE_BIT_IN) == AudioSystem.DEVICE_BIT_IN;
5935 }
5936
5937
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005938 /**
5939 * Return the enabled devices for the specified output stream type.
5940 *
5941 * @param streamType The stream type to query. One of
5942 * {@link #STREAM_VOICE_CALL},
5943 * {@link #STREAM_SYSTEM},
5944 * {@link #STREAM_RING},
5945 * {@link #STREAM_MUSIC},
5946 * {@link #STREAM_ALARM},
5947 * {@link #STREAM_NOTIFICATION},
Jean-Michel Trivi8e0bc782016-11-25 10:22:23 -08005948 * {@link #STREAM_DTMF},
5949 * {@link #STREAM_ACCESSIBILITY}.
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005950 *
5951 * @return The bit-mask "or" of audio output device codes for all enabled devices on this
5952 * stream. Zero or more of
5953 * {@link #DEVICE_OUT_EARPIECE},
5954 * {@link #DEVICE_OUT_SPEAKER},
5955 * {@link #DEVICE_OUT_WIRED_HEADSET},
5956 * {@link #DEVICE_OUT_WIRED_HEADPHONE},
5957 * {@link #DEVICE_OUT_BLUETOOTH_SCO},
5958 * {@link #DEVICE_OUT_BLUETOOTH_SCO_HEADSET},
5959 * {@link #DEVICE_OUT_BLUETOOTH_SCO_CARKIT},
5960 * {@link #DEVICE_OUT_BLUETOOTH_A2DP},
5961 * {@link #DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES},
5962 * {@link #DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER},
Eric Laurent948d3272014-05-16 15:18:45 -07005963 * {@link #DEVICE_OUT_HDMI},
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005964 * {@link #DEVICE_OUT_ANLG_DOCK_HEADSET},
5965 * {@link #DEVICE_OUT_DGTL_DOCK_HEADSET}.
Eric Laurent948d3272014-05-16 15:18:45 -07005966 * {@link #DEVICE_OUT_USB_ACCESSORY}.
5967 * {@link #DEVICE_OUT_USB_DEVICE}.
5968 * {@link #DEVICE_OUT_REMOTE_SUBMIX}.
5969 * {@link #DEVICE_OUT_TELEPHONY_TX}.
5970 * {@link #DEVICE_OUT_LINE}.
5971 * {@link #DEVICE_OUT_HDMI_ARC}.
Kuowei Lif898eae2020-10-27 16:41:18 +08005972 * {@link #DEVICE_OUT_HDMI_EARC}.
Eric Laurent948d3272014-05-16 15:18:45 -07005973 * {@link #DEVICE_OUT_SPDIF}.
5974 * {@link #DEVICE_OUT_FM}.
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005975 * {@link #DEVICE_OUT_DEFAULT} is not used here.
5976 *
5977 * The implementation may support additional device codes beyond those listed, so
5978 * the application should ignore any bits which it does not recognize.
5979 * Note that the information may be imprecise when the implementation
5980 * cannot distinguish whether a particular device is enabled.
5981 *
Andy Hungb11e4c72021-04-13 19:31:00 -07005982 * @deprecated on {@link android.os.Build.VERSION_CODES#T} as new devices
5983 * will have multi-bit device types.
5984 * Prefer to use {@link #getDevicesForAttributes()} instead,
5985 * noting that getDevicesForStream() has a few small discrepancies
5986 * for better volume handling.
5987 * @hide
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005988 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01005989 @UnsupportedAppUsage
Andy Hungb11e4c72021-04-13 19:31:00 -07005990 @Deprecated
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08005991 public int getDevicesForStream(int streamType) {
5992 switch (streamType) {
Andy Hungb11e4c72021-04-13 19:31:00 -07005993 case STREAM_VOICE_CALL:
5994 case STREAM_SYSTEM:
5995 case STREAM_RING:
5996 case STREAM_MUSIC:
5997 case STREAM_ALARM:
5998 case STREAM_NOTIFICATION:
5999 case STREAM_DTMF:
6000 case STREAM_ACCESSIBILITY:
6001 final IAudioService service = getService();
6002 try {
6003 return service.getDeviceMaskForStream(streamType);
6004 } catch (RemoteException e) {
6005 throw e.rethrowFromSystemServer();
6006 }
6007 default:
6008 return 0;
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08006009 }
6010 }
6011
Jean-Michel Trivi56b97b72020-01-07 14:18:05 -08006012 /**
6013 * @hide
6014 * Get the audio devices that would be used for the routing of the given audio attributes.
6015 * @param attributes the {@link AudioAttributes} for which the routing is being queried
6016 * @return an empty list if there was an issue with the request, a list of audio devices
6017 * otherwise (typically one device, except for duplicated paths).
6018 */
6019 @SystemApi
kholoud mohamed37839212021-03-15 16:49:06 +00006020 @RequiresPermission(anyOf = {
Jean-Michel Trivic4557822023-01-23 18:19:52 +00006021 Manifest.permission.MODIFY_AUDIO_ROUTING,
6022 Manifest.permission.QUERY_AUDIO_STATE
kholoud mohamed37839212021-03-15 16:49:06 +00006023 })
Jean-Michel Trivi0e87bca2020-02-18 14:30:29 -08006024 public @NonNull List<AudioDeviceAttributes> getDevicesForAttributes(
Jean-Michel Trivi56b97b72020-01-07 14:18:05 -08006025 @NonNull AudioAttributes attributes) {
6026 Objects.requireNonNull(attributes);
6027 final IAudioService service = getService();
6028 try {
6029 return service.getDevicesForAttributes(attributes);
6030 } catch (RemoteException e) {
6031 throw e.rethrowFromSystemServer();
6032 }
6033 }
6034
Paul Wangebadb692022-12-15 20:40:19 +00006035 // Each listener corresponds to a unique callback stub because each listener can subscribe to
6036 // different AudioAttributes.
6037 private final ConcurrentHashMap<OnDevicesForAttributesChangedListener,
6038 IDevicesForAttributesCallbackStub> mDevicesForAttributesListenerToStub =
6039 new ConcurrentHashMap<>();
6040
6041 private static final class IDevicesForAttributesCallbackStub
6042 extends IDevicesForAttributesCallback.Stub {
6043 ListenerInfo<OnDevicesForAttributesChangedListener> mInfo;
6044
6045 IDevicesForAttributesCallbackStub(@NonNull OnDevicesForAttributesChangedListener listener,
6046 @NonNull Executor executor) {
6047 mInfo = new ListenerInfo<>(listener, executor);
6048 }
6049
6050 public void register(boolean register, AudioAttributes attributes) {
6051 try {
6052 if (register) {
6053 getService().addOnDevicesForAttributesChangedListener(attributes, this);
6054 } else {
6055 getService().removeOnDevicesForAttributesChangedListener(this);
6056 }
6057 } catch (RemoteException e) {
6058 throw e.rethrowFromSystemServer();
6059 }
6060 }
6061
6062 @Override
6063 public void onDevicesForAttributesChanged(AudioAttributes attributes, boolean forVolume,
6064 List<AudioDeviceAttributes> devices) {
6065 // forVolume is ignored. The case where it is `true` is not handled.
6066 mInfo.mExecutor.execute(() ->
6067 mInfo.mListener.onDevicesForAttributesChanged(
6068 attributes, devices));
6069 }
6070 }
6071
6072 /**
6073 * @hide
6074 * Interface to be notified of when routing changes for the registered audio attributes.
6075 */
6076 @SystemApi
6077 public interface OnDevicesForAttributesChangedListener {
6078 /**
6079 * Called on the listener to indicate that the audio devices for the given audio
6080 * attributes have changed.
6081 * @param attributes the {@link AudioAttributes} whose routing changed
6082 * @param devices a list of newly routed audio devices
6083 */
6084 void onDevicesForAttributesChanged(@NonNull AudioAttributes attributes,
6085 @NonNull List<AudioDeviceAttributes> devices);
6086 }
6087
6088 /**
6089 * @hide
6090 * Adds a listener for being notified of routing changes for the given {@link AudioAttributes}.
6091 * @param attributes the {@link AudioAttributes} to listen for routing changes
6092 * @param executor
6093 * @param listener
6094 */
6095 @SystemApi
6096 @RequiresPermission(anyOf = {
Jean-Michel Trivic4557822023-01-23 18:19:52 +00006097 Manifest.permission.MODIFY_AUDIO_ROUTING,
6098 Manifest.permission.QUERY_AUDIO_STATE
Paul Wangebadb692022-12-15 20:40:19 +00006099 })
6100 public void addOnDevicesForAttributesChangedListener(@NonNull AudioAttributes attributes,
6101 @NonNull @CallbackExecutor Executor executor,
6102 @NonNull OnDevicesForAttributesChangedListener listener) {
6103 Objects.requireNonNull(attributes);
6104 Objects.requireNonNull(executor);
6105 Objects.requireNonNull(listener);
6106
6107 synchronized (mDevicesForAttributesListenerToStub) {
6108 IDevicesForAttributesCallbackStub callbackStub =
6109 mDevicesForAttributesListenerToStub.get(listener);
6110
6111 if (callbackStub == null) {
6112 callbackStub = new IDevicesForAttributesCallbackStub(listener, executor);
6113 mDevicesForAttributesListenerToStub.put(listener, callbackStub);
6114 }
6115
6116 callbackStub.register(true, attributes);
6117 }
6118 }
6119
6120 /**
6121 * @hide
6122 * Removes a previously registered listener for being notified of routing changes for the given
6123 * {@link AudioAttributes}.
6124 * @param listener
6125 */
6126 @SystemApi
6127 @RequiresPermission(anyOf = {
Jean-Michel Trivic4557822023-01-23 18:19:52 +00006128 Manifest.permission.MODIFY_AUDIO_ROUTING,
6129 Manifest.permission.QUERY_AUDIO_STATE
Paul Wangebadb692022-12-15 20:40:19 +00006130 })
6131 public void removeOnDevicesForAttributesChangedListener(
6132 @NonNull OnDevicesForAttributesChangedListener listener) {
6133 Objects.requireNonNull(listener);
6134
6135 synchronized (mDevicesForAttributesListenerToStub) {
6136 IDevicesForAttributesCallbackStub callbackStub =
6137 mDevicesForAttributesListenerToStub.get(listener);
6138 if (callbackStub != null) {
6139 callbackStub.register(false, null /* attributes */);
6140 }
6141
6142 mDevicesForAttributesListenerToStub.remove(listener);
6143 }
6144 }
6145
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006146 /**
Dorin Drimusdaeb6a92021-12-22 11:46:26 +01006147 * Get the audio devices that would be used for the routing of the given audio attributes.
6148 * These are the devices anticipated to play sound from an {@link AudioTrack} created with
6149 * the specified {@link AudioAttributes}.
6150 * The audio routing can change if audio devices are physically connected or disconnected or
6151 * concurrently through {@link AudioRouting} or {@link MediaRouter}.
6152 * @param attributes the {@link AudioAttributes} for which the routing is being queried
6153 * @return an empty list if there was an issue with the request, a list of
6154 * {@link AudioDeviceInfo} otherwise (typically one device, except for duplicated paths).
6155 */
6156 public @NonNull List<AudioDeviceInfo> getAudioDevicesForAttributes(
6157 @NonNull AudioAttributes attributes) {
6158 final List<AudioDeviceAttributes> devicesForAttributes;
6159 try {
6160 Objects.requireNonNull(attributes);
6161 final IAudioService service = getService();
6162 devicesForAttributes = service.getDevicesForAttributesUnprotected(attributes);
6163 } catch (Exception e) {
6164 Log.i(TAG, "No audio devices available for specified attributes.");
6165 return Collections.emptyList();
6166 }
6167
6168 // Map from AudioDeviceAttributes to AudioDeviceInfo
6169 AudioDeviceInfo[] outputDeviceInfos = getDevicesStatic(GET_DEVICES_OUTPUTS);
6170 List<AudioDeviceInfo> deviceInfosForAttributes = new ArrayList<>();
6171 for (AudioDeviceAttributes deviceForAttributes : devicesForAttributes) {
6172 for (AudioDeviceInfo deviceInfo : outputDeviceInfos) {
6173 if (deviceForAttributes.getType() == deviceInfo.getType()
6174 && TextUtils.equals(deviceForAttributes.getAddress(),
6175 deviceInfo.getAddress())) {
6176 deviceInfosForAttributes.add(deviceInfo);
6177 }
6178 }
6179 }
6180 return Collections.unmodifiableList(deviceInfosForAttributes);
6181 }
6182
6183 /**
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006184 * @hide
Madhava Srinivasanb5ed6002020-06-11 21:49:24 +00006185 * Volume behavior for an audio device that has no particular volume behavior set. Invalid as
Marvin Ramin5495cc92020-07-23 11:58:33 +02006186 * an argument to {@link #setDeviceVolumeBehavior(AudioDeviceAttributes, int)} and should not
6187 * be returned by {@link #getDeviceVolumeBehavior(AudioDeviceAttributes)}.
Madhava Srinivasanb5ed6002020-06-11 21:49:24 +00006188 */
6189 public static final int DEVICE_VOLUME_BEHAVIOR_UNSET = -1;
6190 /**
6191 * @hide
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006192 * Volume behavior for an audio device where a software attenuation is applied
Marvin Ramin5495cc92020-07-23 11:58:33 +02006193 * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006194 */
Marvin Ramin5495cc92020-07-23 11:58:33 +02006195 @SystemApi
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006196 public static final int DEVICE_VOLUME_BEHAVIOR_VARIABLE = 0;
6197 /**
6198 * @hide
6199 * Volume behavior for an audio device where the volume is always set to provide no attenuation
6200 * nor gain (e.g. unit gain).
Marvin Ramin5495cc92020-07-23 11:58:33 +02006201 * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006202 */
Marvin Ramin5495cc92020-07-23 11:58:33 +02006203 @SystemApi
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006204 public static final int DEVICE_VOLUME_BEHAVIOR_FULL = 1;
6205 /**
6206 * @hide
6207 * Volume behavior for an audio device where the volume is either set to muted, or to provide
6208 * no attenuation nor gain (e.g. unit gain).
Marvin Ramin5495cc92020-07-23 11:58:33 +02006209 * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006210 */
Marvin Ramin5495cc92020-07-23 11:58:33 +02006211 @SystemApi
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006212 public static final int DEVICE_VOLUME_BEHAVIOR_FIXED = 2;
6213 /**
6214 * @hide
6215 * Volume behavior for an audio device where no software attenuation is applied, and
6216 * the volume is kept synchronized between the host and the device itself through a
6217 * device-specific protocol such as BT AVRCP.
Marvin Ramin5495cc92020-07-23 11:58:33 +02006218 * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006219 */
Marvin Ramin5495cc92020-07-23 11:58:33 +02006220 @SystemApi
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006221 public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3;
6222 /**
6223 * @hide
6224 * Volume behavior for an audio device where no software attenuation is applied, and
6225 * the volume is kept synchronized between the host and the device itself through a
6226 * device-specific protocol (such as for hearing aids), based on the audio mode (e.g.
6227 * normal vs in phone call).
6228 * @see #setMode(int)
Marvin Ramin5495cc92020-07-23 11:58:33 +02006229 * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006230 */
Marvin Ramin5495cc92020-07-23 11:58:33 +02006231 @SystemApi
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006232 public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4;
6233
6234 /** @hide */
6235 @IntDef({
6236 DEVICE_VOLUME_BEHAVIOR_VARIABLE,
6237 DEVICE_VOLUME_BEHAVIOR_FULL,
6238 DEVICE_VOLUME_BEHAVIOR_FIXED,
6239 DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
6240 DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE,
6241 })
6242 @Retention(RetentionPolicy.SOURCE)
6243 public @interface DeviceVolumeBehavior {}
6244
Madhava Srinivasanb5ed6002020-06-11 21:49:24 +00006245 /** @hide */
6246 @IntDef({
6247 DEVICE_VOLUME_BEHAVIOR_UNSET,
6248 DEVICE_VOLUME_BEHAVIOR_VARIABLE,
6249 DEVICE_VOLUME_BEHAVIOR_FULL,
6250 DEVICE_VOLUME_BEHAVIOR_FIXED,
6251 DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
6252 DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE,
6253 })
6254 @Retention(RetentionPolicy.SOURCE)
6255 public @interface DeviceVolumeBehaviorState {}
6256
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006257 /**
6258 * @hide
6259 * Throws IAE on an invalid volume behavior value
6260 * @param volumeBehavior behavior value to check
6261 */
6262 public static void enforceValidVolumeBehavior(int volumeBehavior) {
6263 switch (volumeBehavior) {
6264 case DEVICE_VOLUME_BEHAVIOR_VARIABLE:
6265 case DEVICE_VOLUME_BEHAVIOR_FULL:
6266 case DEVICE_VOLUME_BEHAVIOR_FIXED:
6267 case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
6268 case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE:
6269 return;
6270 default:
6271 throw new IllegalArgumentException("Illegal volume behavior " + volumeBehavior);
6272 }
6273 }
6274
6275 /**
6276 * @hide
6277 * Sets the volume behavior for an audio output device.
Marvin Ramin5495cc92020-07-23 11:58:33 +02006278 * @see #DEVICE_VOLUME_BEHAVIOR_VARIABLE
6279 * @see #DEVICE_VOLUME_BEHAVIOR_FULL
6280 * @see #DEVICE_VOLUME_BEHAVIOR_FIXED
6281 * @see #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE
6282 * @see #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE
6283 * @param device the device to be affected
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006284 * @param deviceVolumeBehavior one of the device behaviors
6285 */
Marvin Ramin5495cc92020-07-23 11:58:33 +02006286 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00006287 @RequiresPermission(anyOf = {
6288 Manifest.permission.MODIFY_AUDIO_ROUTING,
6289 Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS
6290 })
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006291 public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
6292 @DeviceVolumeBehavior int deviceVolumeBehavior) {
6293 // verify arguments (validity of device type is enforced in server)
6294 Objects.requireNonNull(device);
6295 enforceValidVolumeBehavior(deviceVolumeBehavior);
6296 // communicate with service
6297 final IAudioService service = getService();
6298 try {
6299 service.setDeviceVolumeBehavior(device, deviceVolumeBehavior,
6300 mApplicationContext.getOpPackageName());
6301 } catch (RemoteException e) {
6302 throw e.rethrowFromSystemServer();
6303 }
6304 }
6305
6306 /**
6307 * @hide
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006308 * Returns the volume device behavior for the given audio device
6309 * @param device the audio device
6310 * @return the volume behavior for the device
6311 */
Marvin Ramin5495cc92020-07-23 11:58:33 +02006312 @SystemApi
kholoud mohamed37839212021-03-15 16:49:06 +00006313 @RequiresPermission(anyOf = {
Jean-Michel Trivic4557822023-01-23 18:19:52 +00006314 Manifest.permission.MODIFY_AUDIO_ROUTING,
6315 Manifest.permission.QUERY_AUDIO_STATE,
6316 Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS
kholoud mohamed37839212021-03-15 16:49:06 +00006317 })
Marvin Ramin5495cc92020-07-23 11:58:33 +02006318 public @DeviceVolumeBehavior
6319 int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
Jean-Michel Trivi005c6012020-03-15 20:33:15 -07006320 // verify arguments (validity of device type is enforced in server)
6321 Objects.requireNonNull(device);
6322 // communicate with service
6323 final IAudioService service = getService();
6324 try {
6325 return service.getDeviceVolumeBehavior(device);
6326 } catch (RemoteException e) {
6327 throw e.rethrowFromSystemServer();
6328 }
6329 }
6330
kholoud mohamed37839212021-03-15 16:49:06 +00006331 /**
6332 * @hide
6333 * Returns {@code true} if the volume device behavior is {@link #DEVICE_VOLUME_BEHAVIOR_FULL}.
6334 */
6335 @TestApi
6336 @RequiresPermission(anyOf = {
Jean-Michel Trivic4557822023-01-23 18:19:52 +00006337 Manifest.permission.MODIFY_AUDIO_ROUTING,
6338 Manifest.permission.QUERY_AUDIO_STATE,
6339 Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS
kholoud mohamed37839212021-03-15 16:49:06 +00006340 })
6341 public boolean isFullVolumeDevice() {
6342 final AudioAttributes attributes = new AudioAttributes.Builder()
6343 .setUsage(AudioAttributes.USAGE_MEDIA)
6344 .build();
6345 final List<AudioDeviceAttributes> devices = getDevicesForAttributes(attributes);
6346 for (AudioDeviceAttributes device : devices) {
6347 if (getDeviceVolumeBehavior(device) == DEVICE_VOLUME_BEHAVIOR_FULL) {
6348 return true;
6349 }
6350 }
6351 return false;
6352 }
6353
Nathalie Le Clair517a1322021-10-15 14:22:41 +02006354 /**
Eric Laurentb1fbaac2012-05-29 09:24:28 -07006355 * Indicate wired accessory connection state change.
6356 * @param device type of device connected/disconnected (AudioManager.DEVICE_OUT_xxx)
6357 * @param state new connection state: 1 connected, 0 disconnected
6358 * @param name device name
6359 * {@hide}
6360 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01006361 @UnsupportedAppUsage
Jean-Michel Trivic4557822023-01-23 18:19:52 +00006362 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Nathalie Le Clair7b72c392022-04-04 13:25:46 +02006363 public void setWiredDeviceConnectionState(int device, int state, String address, String name) {
6364 AudioDeviceAttributes attributes = new AudioDeviceAttributes(device, address, name);
Nathalie Le Clair517a1322021-10-15 14:22:41 +02006365 setWiredDeviceConnectionState(attributes, state);
6366 }
6367
6368 /**
6369 * Indicate wired accessory connection state change and attributes.
6370 * @param state new connection state: 1 connected, 0 disconnected
6371 * @param attributes attributes of the connected device
6372 * {@hide}
6373 */
6374 @UnsupportedAppUsage
Jean-Michel Trivic4557822023-01-23 18:19:52 +00006375 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Nathalie Le Clair517a1322021-10-15 14:22:41 +02006376 public void setWiredDeviceConnectionState(AudioDeviceAttributes attributes, int state) {
Jean-Michel Trivi50750ff2017-03-21 11:18:24 -07006377 final IAudioService service = getService();
Eric Laurentb1fbaac2012-05-29 09:24:28 -07006378 try {
Nathalie Le Clair517a1322021-10-15 14:22:41 +02006379 service.setWiredDeviceConnectionState(attributes, state,
Marco Nelissena80ac052015-03-12 16:17:45 -07006380 mApplicationContext.getOpPackageName());
Eric Laurentb1fbaac2012-05-29 09:24:28 -07006381 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006382 throw e.rethrowFromSystemServer();
Eric Laurentb1fbaac2012-05-29 09:24:28 -07006383 }
6384 }
6385
Grzegorz Kołodziejczyk59b2baa2021-05-14 12:19:07 +00006386 /**
Jean-Michel Trivi4da775d2021-12-03 15:33:46 -08006387 * Indicate wired accessory connection state change.
6388 * @param device {@link AudioDeviceAttributes} of the device to "fake-connect"
6389 * @param connected true for connected, false for disconnected
6390 * {@hide}
6391 */
6392 @TestApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00006393 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Jean-Michel Trivi4da775d2021-12-03 15:33:46 -08006394 public void setTestDeviceConnectionState(@NonNull AudioDeviceAttributes device,
6395 boolean connected) {
6396 try {
6397 getService().setTestDeviceConnectionState(device, connected);
6398 } catch (RemoteException e) {
6399 throw e.rethrowFromSystemServer();
6400 }
6401 }
6402
6403 /**
wescande7c17ba0c2021-07-30 16:46:14 +02006404 * Indicate Bluetooth profile connection state change.
6405 * Configuration changes for A2DP are indicated by having the same <code>newDevice</code> and
6406 * <code>previousDevice</code>
6407 * This operation is asynchronous.
6408 *
6409 * @param newDevice Bluetooth device connected or null if there is no new devices
6410 * @param previousDevice Bluetooth device disconnected or null if there is no disconnected
6411 * devices
William Escandeac11d772022-01-25 18:01:15 +01006412 * @param info contain all info related to the device. {@link BluetoothProfileConnectionInfo}
Pavlin Radoslavov82e06462018-01-19 18:20:04 -08006413 * {@hide}
6414 */
wescande7c17ba0c2021-07-30 16:46:14 +02006415 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
Jean-Michel Trivic4557822023-01-23 18:19:52 +00006416 @RequiresPermission(Manifest.permission.BLUETOOTH_STACK)
wescande7c17ba0c2021-07-30 16:46:14 +02006417 public void handleBluetoothActiveDeviceChanged(@Nullable BluetoothDevice newDevice,
William Escandeac11d772022-01-25 18:01:15 +01006418 @Nullable BluetoothDevice previousDevice,
6419 @NonNull BluetoothProfileConnectionInfo info) {
Pavlin Radoslavov82e06462018-01-19 18:20:04 -08006420 final IAudioService service = getService();
Pavlin Radoslavov82e06462018-01-19 18:20:04 -08006421 try {
wescande7c17ba0c2021-07-30 16:46:14 +02006422 service.handleBluetoothActiveDeviceChanged(newDevice, previousDevice, info);
Pavlin Radoslavov44a4ef02016-12-21 12:05:51 -08006423 } catch (RemoteException e) {
6424 throw e.rethrowFromSystemServer();
6425 }
6426 }
6427
Jeff Sharkey098d5802012-04-26 17:30:34 -07006428 /** {@hide} */
6429 public IRingtonePlayer getRingtonePlayer() {
6430 try {
6431 return getService().getRingtonePlayer();
6432 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006433 throw e.rethrowFromSystemServer();
Jeff Sharkey098d5802012-04-26 17:30:34 -07006434 }
6435 }
Glenn Kasten228c9842012-09-14 08:48:47 -07006436
6437 /**
Glenn Kasten62796aa2012-09-20 08:08:07 -07006438 * Used as a key for {@link #getProperty} to request the native or optimal output sample rate
Glenn Kastenc3de5142016-07-15 12:14:24 -07006439 * for this device's low latency output stream, in decimal Hz. Latency-sensitive apps
6440 * should use this value as a default, and offer the user the option to override it.
6441 * The low latency output stream is typically either the device's primary output stream,
6442 * or another output stream with smaller buffers.
Glenn Kasten228c9842012-09-14 08:48:47 -07006443 */
Glenn Kasten1cbf9b32016-02-02 12:04:09 -08006444 // FIXME Deprecate
Glenn Kasten228c9842012-09-14 08:48:47 -07006445 public static final String PROPERTY_OUTPUT_SAMPLE_RATE =
6446 "android.media.property.OUTPUT_SAMPLE_RATE";
6447
6448 /**
Glenn Kasten62796aa2012-09-20 08:08:07 -07006449 * Used as a key for {@link #getProperty} to request the native or optimal output buffer size
Glenn Kastenc3de5142016-07-15 12:14:24 -07006450 * for this device's low latency output stream, in decimal PCM frames. Latency-sensitive apps
6451 * should use this value as a minimum, and offer the user the option to override it.
6452 * The low latency output stream is typically either the device's primary output stream,
6453 * or another output stream with smaller buffers.
Glenn Kasten228c9842012-09-14 08:48:47 -07006454 */
Glenn Kasten1cbf9b32016-02-02 12:04:09 -08006455 // FIXME Deprecate
Glenn Kasten228c9842012-09-14 08:48:47 -07006456 public static final String PROPERTY_OUTPUT_FRAMES_PER_BUFFER =
6457 "android.media.property.OUTPUT_FRAMES_PER_BUFFER";
6458
6459 /**
Arunesh Mishrad08715e52015-04-23 22:39:40 -07006460 * Used as a key for {@link #getProperty} to determine if the default microphone audio source
6461 * supports near-ultrasound frequencies (range of 18 - 21 kHz).
6462 */
6463 public static final String PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND =
6464 "android.media.property.SUPPORT_MIC_NEAR_ULTRASOUND";
6465
6466 /**
6467 * Used as a key for {@link #getProperty} to determine if the default speaker audio path
6468 * supports near-ultrasound frequencies (range of 18 - 21 kHz).
6469 */
6470 public static final String PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND =
6471 "android.media.property.SUPPORT_SPEAKER_NEAR_ULTRASOUND";
6472
6473 /**
ragoa7cc59c2015-12-02 11:31:15 -08006474 * Used as a key for {@link #getProperty} to determine if the unprocessed audio source is
6475 * available and supported with the expected frequency range and level response.
6476 */
6477 public static final String PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED =
6478 "android.media.property.SUPPORT_AUDIO_SOURCE_UNPROCESSED";
6479 /**
Glenn Kasten62796aa2012-09-20 08:08:07 -07006480 * Returns the value of the property with the specified key.
Glenn Kasten228c9842012-09-14 08:48:47 -07006481 * @param key One of the strings corresponding to a property key: either
Glenn Kasten0b986af2015-10-30 18:24:04 -07006482 * {@link #PROPERTY_OUTPUT_SAMPLE_RATE},
6483 * {@link #PROPERTY_OUTPUT_FRAMES_PER_BUFFER},
ragoa7cc59c2015-12-02 11:31:15 -08006484 * {@link #PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND},
6485 * {@link #PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND}, or
6486 * {@link #PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED}.
Glenn Kasten228c9842012-09-14 08:48:47 -07006487 * @return A string representing the associated value for that property key,
6488 * or null if there is no value for that key.
Glenn Kasten228c9842012-09-14 08:48:47 -07006489 */
6490 public String getProperty(String key) {
Glenn Kastenc6c43652012-09-24 17:32:30 -07006491 if (PROPERTY_OUTPUT_SAMPLE_RATE.equals(key)) {
6492 int outputSampleRate = AudioSystem.getPrimaryOutputSamplingRate();
6493 return outputSampleRate > 0 ? Integer.toString(outputSampleRate) : null;
6494 } else if (PROPERTY_OUTPUT_FRAMES_PER_BUFFER.equals(key)) {
6495 int outputFramesPerBuffer = AudioSystem.getPrimaryOutputFrameCount();
6496 return outputFramesPerBuffer > 0 ? Integer.toString(outputFramesPerBuffer) : null;
Arunesh Mishrabc922272015-04-27 09:39:00 -07006497 } else if (PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND.equals(key)) {
Arunesh Mishra6e4fb342015-06-02 13:23:41 -07006498 // Will throw a RuntimeException Resources.NotFoundException if this config value is
6499 // not found.
6500 return String.valueOf(getContext().getResources().getBoolean(
6501 com.android.internal.R.bool.config_supportMicNearUltrasound));
Arunesh Mishrabc922272015-04-27 09:39:00 -07006502 } else if (PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND.equals(key)) {
Arunesh Mishra6e4fb342015-06-02 13:23:41 -07006503 return String.valueOf(getContext().getResources().getBoolean(
6504 com.android.internal.R.bool.config_supportSpeakerNearUltrasound));
ragoa7cc59c2015-12-02 11:31:15 -08006505 } else if (PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED.equals(key)) {
6506 return String.valueOf(getContext().getResources().getBoolean(
6507 com.android.internal.R.bool.config_supportAudioSourceUnprocessed));
Glenn Kastenc6c43652012-09-24 17:32:30 -07006508 } else {
6509 // null or unknown key
6510 return null;
6511 }
Glenn Kasten228c9842012-09-14 08:48:47 -07006512 }
6513
Oliver Woodman61dcdf32013-06-26 12:43:36 +01006514 /**
Andy Hung97aa07f82020-01-17 14:05:06 -08006515 * @hide
6516 * Sets an additional audio output device delay in milliseconds.
6517 *
6518 * The additional output delay is a request to the output device to
6519 * delay audio presentation (generally with respect to video presentation for better
6520 * synchronization).
6521 * It may not be supported by all output devices,
6522 * and typically increases the audio latency by the amount of additional
6523 * audio delay requested.
6524 *
6525 * If additional audio delay is supported by an audio output device,
6526 * it is expected to be supported for all output streams (and configurations)
6527 * opened on that device.
6528 *
6529 * @param device an instance of {@link AudioDeviceInfo} returned from {@link getDevices()}.
Andy Hung120c1c42020-03-26 12:01:33 -07006530 * @param delayMillis delay in milliseconds desired. This should be in range of {@code 0}
Andy Hung97aa07f82020-01-17 14:05:06 -08006531 * to the value returned by {@link #getMaxAdditionalOutputDeviceDelay()}.
6532 * @return true if successful, false if the device does not support output device delay
6533 * or the delay is not in range of {@link #getMaxAdditionalOutputDeviceDelay()}.
6534 */
6535 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00006536 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Andy Hung97aa07f82020-01-17 14:05:06 -08006537 public boolean setAdditionalOutputDeviceDelay(
Andy Hung120c1c42020-03-26 12:01:33 -07006538 @NonNull AudioDeviceInfo device, @IntRange(from = 0) long delayMillis) {
Andy Hung97aa07f82020-01-17 14:05:06 -08006539 Objects.requireNonNull(device);
Kuowei Lif50ac2e32020-08-13 14:34:17 +08006540 try {
6541 return getService().setAdditionalOutputDeviceDelay(
6542 new AudioDeviceAttributes(device), delayMillis);
6543 } catch (RemoteException e) {
6544 throw e.rethrowFromSystemServer();
6545 }
Andy Hung97aa07f82020-01-17 14:05:06 -08006546 }
6547
6548 /**
6549 * @hide
6550 * Returns the current additional audio output device delay in milliseconds.
6551 *
6552 * @param device an instance of {@link AudioDeviceInfo} returned from {@link getDevices()}.
6553 * @return the additional output device delay. This is a non-negative number.
6554 * {@code 0} is returned if unsupported.
6555 */
6556 @SystemApi
6557 @IntRange(from = 0)
Andy Hung120c1c42020-03-26 12:01:33 -07006558 public long getAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) {
Andy Hung97aa07f82020-01-17 14:05:06 -08006559 Objects.requireNonNull(device);
Kuowei Lif50ac2e32020-08-13 14:34:17 +08006560 try {
6561 return getService().getAdditionalOutputDeviceDelay(new AudioDeviceAttributes(device));
6562 } catch (RemoteException e) {
6563 throw e.rethrowFromSystemServer();
6564 }
Andy Hung97aa07f82020-01-17 14:05:06 -08006565 }
6566
6567 /**
6568 * @hide
6569 * Returns the maximum additional audio output device delay in milliseconds.
6570 *
6571 * @param device an instance of {@link AudioDeviceInfo} returned from {@link getDevices()}.
6572 * @return the maximum output device delay in milliseconds that can be set.
6573 * This is a non-negative number
6574 * representing the additional audio delay supported for the device.
6575 * {@code 0} is returned if unsupported.
6576 */
6577 @SystemApi
6578 @IntRange(from = 0)
Andy Hung120c1c42020-03-26 12:01:33 -07006579 public long getMaxAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) {
Andy Hung97aa07f82020-01-17 14:05:06 -08006580 Objects.requireNonNull(device);
Kuowei Lif50ac2e32020-08-13 14:34:17 +08006581 try {
6582 return getService().getMaxAdditionalOutputDeviceDelay(
6583 new AudioDeviceAttributes(device));
6584 } catch (RemoteException e) {
6585 throw e.rethrowFromSystemServer();
6586 }
Andy Hung97aa07f82020-01-17 14:05:06 -08006587 }
6588
6589 /**
Oliver Woodman61dcdf32013-06-26 12:43:36 +01006590 * Returns the estimated latency for the given stream type in milliseconds.
6591 *
6592 * DO NOT UNHIDE. The existing approach for doing A/V sync has too many problems. We need
6593 * a better solution.
6594 * @hide
6595 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01006596 @UnsupportedAppUsage
Oliver Woodman61dcdf32013-06-26 12:43:36 +01006597 public int getOutputLatency(int streamType) {
6598 return AudioSystem.getOutputLatency(streamType);
6599 }
6600
John Spurlock3346a802014-05-20 16:25:37 -04006601 /**
6602 * Registers a global volume controller interface. Currently limited to SystemUI.
6603 *
6604 * @hide
6605 */
6606 public void setVolumeController(IVolumeController controller) {
6607 try {
6608 getService().setVolumeController(controller);
6609 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006610 throw e.rethrowFromSystemServer();
John Spurlock3346a802014-05-20 16:25:37 -04006611 }
6612 }
6613
6614 /**
Vlad Popa4e9d10b2023-01-13 12:03:48 +01006615 * Returns the registered volume controller interface.
6616 *
6617 * @hide
6618 */
6619 @Nullable
6620 public IVolumeController getVolumeController() {
6621 try {
6622 return getService().getVolumeController();
6623 } catch (RemoteException e) {
6624 throw e.rethrowFromSystemServer();
6625 }
6626 }
6627
6628 /**
John Spurlock33f4e042014-07-11 13:10:58 -04006629 * Notify audio manager about volume controller visibility changes.
6630 * Currently limited to SystemUI.
6631 *
6632 * @hide
6633 */
6634 public void notifyVolumeControllerVisible(IVolumeController controller, boolean visible) {
6635 try {
6636 getService().notifyVolumeControllerVisible(controller, visible);
6637 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006638 throw e.rethrowFromSystemServer();
John Spurlock33f4e042014-07-11 13:10:58 -04006639 }
6640 }
6641
6642 /**
John Spurlock3346a802014-05-20 16:25:37 -04006643 * Only useful for volume controllers.
6644 * @hide
6645 */
John Spurlock3346a802014-05-20 16:25:37 -04006646 public boolean isStreamAffectedByRingerMode(int streamType) {
6647 try {
6648 return getService().isStreamAffectedByRingerMode(streamType);
6649 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006650 throw e.rethrowFromSystemServer();
John Spurlock3346a802014-05-20 16:25:37 -04006651 }
6652 }
6653
6654 /**
6655 * Only useful for volume controllers.
6656 * @hide
6657 */
John Spurlocka9dfbe8b2015-02-17 11:01:51 -05006658 public boolean isStreamAffectedByMute(int streamType) {
6659 try {
6660 return getService().isStreamAffectedByMute(streamType);
6661 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006662 throw e.rethrowFromSystemServer();
John Spurlocka9dfbe8b2015-02-17 11:01:51 -05006663 }
6664 }
6665
6666 /**
6667 * Only useful for volume controllers.
6668 * @hide
6669 */
John Spurlock3346a802014-05-20 16:25:37 -04006670 public void disableSafeMediaVolume() {
6671 try {
Marco Nelissena80ac052015-03-12 16:17:45 -07006672 getService().disableSafeMediaVolume(mApplicationContext.getOpPackageName());
John Spurlock3346a802014-05-20 16:25:37 -04006673 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006674 throw e.rethrowFromSystemServer();
John Spurlock3346a802014-05-20 16:25:37 -04006675 }
6676 }
Eric Laurenta198a292014-02-18 16:26:17 -08006677
6678 /**
Jean-Michel Trivia5321ff2022-12-14 22:11:38 +00006679 * @hide
6680 * Lower media volume to RS1
6681 */
6682 public void lowerVolumeToRs1() {
6683 try {
6684 getService().lowerVolumeToRs1(mApplicationContext.getOpPackageName());
6685 } catch (RemoteException e) {
6686 throw e.rethrowFromSystemServer();
6687 }
6688 }
6689
6690 /**
6691 * @hide
Vlad Popa4e9d10b2023-01-13 12:03:48 +01006692 * @return the RS2 value used for momentary exposure warnings
6693 */
6694 @TestApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00006695 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
Vlad Popa4e9d10b2023-01-13 12:03:48 +01006696 public float getRs2Value() {
6697 try {
6698 return getService().getRs2Value();
6699 } catch (RemoteException e) {
6700 throw e.rethrowFromSystemServer();
6701 }
6702 }
6703
6704 /**
6705 * @hide
6706 * Sets the RS2 value used for momentary exposure warnings
6707 */
6708 @TestApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00006709 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
Vlad Popa4e9d10b2023-01-13 12:03:48 +01006710 public void setRs2Value(float rs2Value) {
6711 try {
6712 getService().setRs2Value(rs2Value);
6713 } catch (RemoteException e) {
6714 throw e.rethrowFromSystemServer();
6715 }
6716 }
6717
6718 /**
6719 * @hide
6720 * @return the current computed sound dose value
6721 */
6722 @TestApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00006723 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
Vlad Popa4e9d10b2023-01-13 12:03:48 +01006724 public float getCsd() {
6725 try {
6726 return getService().getCsd();
6727 } catch (RemoteException e) {
6728 throw e.rethrowFromSystemServer();
6729 }
6730 }
6731
6732 /**
6733 * @hide
6734 * Sets the computed sound dose value to {@code csd}
6735 */
6736 @TestApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00006737 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
Vlad Popa4e9d10b2023-01-13 12:03:48 +01006738 public void setCsd(float csd) {
6739 try {
6740 getService().setCsd(csd);
6741 } catch (RemoteException e) {
6742 throw e.rethrowFromSystemServer();
6743 }
6744 }
6745
6746 /**
6747 * @hide
6748 * Forces the computation of MEL values (used for CSD) on framework level. This will have the
6749 * result of ignoring the MEL values computed on HAL level. Should only be used in testing
6750 * since this can affect the certification of a device with EN50332-3 regulation.
6751 */
6752 @TestApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00006753 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
Vlad Popa4e9d10b2023-01-13 12:03:48 +01006754 public void forceUseFrameworkMel(boolean useFrameworkMel) {
6755 try {
6756 getService().forceUseFrameworkMel(useFrameworkMel);
6757 } catch (RemoteException e) {
6758 throw e.rethrowFromSystemServer();
6759 }
6760 }
6761
6762 /**
6763 * @hide
6764 * Forces the computation of CSD on all output devices.
6765 */
6766 @TestApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00006767 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
Vlad Popa4e9d10b2023-01-13 12:03:48 +01006768 public void forceComputeCsdOnAllDevices(boolean computeCsdOnAllDevices) {
6769 try {
6770 getService().forceComputeCsdOnAllDevices(computeCsdOnAllDevices);
6771 } catch (RemoteException e) {
6772 throw e.rethrowFromSystemServer();
6773 }
6774 }
6775
6776 /**
6777 * @hide
6778 * Returns whether CSD is enabled on this device.
6779 */
6780 @TestApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00006781 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
Vlad Popa4e9d10b2023-01-13 12:03:48 +01006782 public boolean isCsdEnabled() {
6783 try {
6784 return getService().isCsdEnabled();
6785 } catch (RemoteException e) {
6786 throw e.rethrowFromSystemServer();
6787 }
6788 }
6789
6790 /**
6791 * @hide
Jean-Michel Trivia5321ff2022-12-14 22:11:38 +00006792 * Sound dose warning at every 100% of dose during integration window
6793 */
6794 public static final int CSD_WARNING_DOSE_REACHED_1X = 1;
6795 /**
6796 * @hide
6797 * Sound dose warning when 500% of dose is reached during integration window
6798 */
6799 public static final int CSD_WARNING_DOSE_REPEATED_5X = 2;
6800 /**
6801 * @hide
6802 * Sound dose warning after a momentary exposure event
6803 */
6804 public static final int CSD_WARNING_MOMENTARY_EXPOSURE = 3;
6805 /**
6806 * @hide
6807 * Sound dose warning at every 100% of dose during integration window
6808 */
6809 public static final int CSD_WARNING_ACCUMULATION_START = 4;
6810
6811 /** @hide */
6812 @IntDef(flag = false, value = {
6813 CSD_WARNING_DOSE_REACHED_1X,
6814 CSD_WARNING_DOSE_REPEATED_5X,
6815 CSD_WARNING_MOMENTARY_EXPOSURE,
6816 CSD_WARNING_ACCUMULATION_START }
6817 )
6818 @Retention(RetentionPolicy.SOURCE)
6819 public @interface CsdWarning {}
6820
6821 /**
John Spurlock661f2cf42014-11-17 10:29:10 -05006822 * Only useful for volume controllers.
6823 * @hide
6824 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01006825 @UnsupportedAppUsage
John Spurlock661f2cf42014-11-17 10:29:10 -05006826 public void setRingerModeInternal(int ringerMode) {
6827 try {
Marco Nelissen29f16932015-04-17 09:50:56 -07006828 getService().setRingerModeInternal(ringerMode, getContext().getOpPackageName());
John Spurlock661f2cf42014-11-17 10:29:10 -05006829 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006830 throw e.rethrowFromSystemServer();
John Spurlock661f2cf42014-11-17 10:29:10 -05006831 }
6832 }
6833
6834 /**
6835 * Only useful for volume controllers.
6836 * @hide
6837 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01006838 @UnsupportedAppUsage
John Spurlock661f2cf42014-11-17 10:29:10 -05006839 public int getRingerModeInternal() {
6840 try {
6841 return getService().getRingerModeInternal();
6842 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006843 throw e.rethrowFromSystemServer();
John Spurlock661f2cf42014-11-17 10:29:10 -05006844 }
6845 }
6846
6847 /**
John Spurlocka48d7792015-03-03 17:35:57 -05006848 * Only useful for volume controllers.
6849 * @hide
6850 */
6851 public void setVolumePolicy(VolumePolicy policy) {
6852 try {
6853 getService().setVolumePolicy(policy);
6854 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006855 throw e.rethrowFromSystemServer();
John Spurlocka48d7792015-03-03 17:35:57 -05006856 }
6857 }
6858
6859 /**
Jungshik Jang41d97462014-06-30 22:26:29 +09006860 * Set Hdmi Cec system audio mode.
6861 *
6862 * @param on whether to be on system audio mode
Jungshik Jang6f34f5a2014-07-08 21:17:29 +09006863 * @return output device type. 0 (DEVICE_NONE) if failed to set device.
Jungshik Jang41d97462014-06-30 22:26:29 +09006864 * @hide
6865 */
Jungshik Jang12307ca2014-07-15 19:27:56 +09006866 public int setHdmiSystemAudioSupported(boolean on) {
Jungshik Jang41d97462014-06-30 22:26:29 +09006867 try {
Jungshik Jang12307ca2014-07-15 19:27:56 +09006868 return getService().setHdmiSystemAudioSupported(on);
Jungshik Jang41d97462014-06-30 22:26:29 +09006869 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006870 throw e.rethrowFromSystemServer();
Jungshik Jang41d97462014-06-30 22:26:29 +09006871 }
6872 }
6873
6874 /**
Terry Heoe7d6d972014-09-04 21:05:28 +09006875 * Returns true if Hdmi Cec system audio mode is supported.
6876 *
6877 * @hide
6878 */
6879 @SystemApi
Aurimas Liutikas1394a012020-11-12 18:26:09 -08006880 @SuppressLint("RequiresPermission") // FIXME is this still used?
Terry Heoe7d6d972014-09-04 21:05:28 +09006881 public boolean isHdmiSystemAudioSupported() {
6882 try {
6883 return getService().isHdmiSystemAudioSupported();
6884 } catch (RemoteException e) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -07006885 throw e.rethrowFromSystemServer();
Terry Heoe7d6d972014-09-04 21:05:28 +09006886 }
6887 }
6888
6889 /**
Eric Laurenta198a292014-02-18 16:26:17 -08006890 * Return codes for listAudioPorts(), createAudioPatch() ...
6891 */
6892
Jean-Michel Trivid6f65de2018-12-18 18:49:14 -08006893 /** @hide */
6894 @SystemApi
Eric Laurenta198a292014-02-18 16:26:17 -08006895 public static final int SUCCESS = AudioSystem.SUCCESS;
Jean-Michel Trivi8a21f5d2014-06-05 15:03:52 -07006896 /**
6897 * A default error code.
Eric Laurenta198a292014-02-18 16:26:17 -08006898 */
6899 public static final int ERROR = AudioSystem.ERROR;
6900 /** @hide
Jean-Michel Trivia8b6bd82014-07-01 09:48:46 -07006901 * CANDIDATE FOR PUBLIC API
Eric Laurenta198a292014-02-18 16:26:17 -08006902 */
6903 public static final int ERROR_BAD_VALUE = AudioSystem.BAD_VALUE;
6904 /** @hide
6905 */
6906 public static final int ERROR_INVALID_OPERATION = AudioSystem.INVALID_OPERATION;
6907 /** @hide
6908 */
6909 public static final int ERROR_PERMISSION_DENIED = AudioSystem.PERMISSION_DENIED;
6910 /** @hide
6911 */
6912 public static final int ERROR_NO_INIT = AudioSystem.NO_INIT;
Eric Laurentff0d9f02014-06-09 17:23:02 -07006913 /**
6914 * An error code indicating that the object reporting it is no longer valid and needs to
6915 * be recreated.
Eric Laurenta198a292014-02-18 16:26:17 -08006916 */
6917 public static final int ERROR_DEAD_OBJECT = AudioSystem.DEAD_OBJECT;
6918
6919 /**
6920 * Returns a list of descriptors for all audio ports managed by the audio framework.
6921 * Audio ports are nodes in the audio framework or audio hardware that can be configured
6922 * or connected and disconnected with createAudioPatch() or releaseAudioPatch().
6923 * See AudioPort for a list of attributes of each audio port.
6924 * @param ports An AudioPort ArrayList where the list will be returned.
6925 * @hide
6926 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01006927 @UnsupportedAppUsage
Eric Laurent4bcdba82015-05-01 11:37:49 -07006928 public static int listAudioPorts(ArrayList<AudioPort> ports) {
Eric Laurent8a1e7a82015-05-08 11:43:05 -07006929 return updateAudioPortCache(ports, null, null);
6930 }
6931
6932 /**
6933 * Returns a list of descriptors for all audio ports managed by the audio framework as
6934 * it was before the last update calback.
6935 * @param ports An AudioPort ArrayList where the list will be returned.
6936 * @hide
6937 */
6938 public static int listPreviousAudioPorts(ArrayList<AudioPort> ports) {
6939 return updateAudioPortCache(null, null, ports);
Eric Laurenta198a292014-02-18 16:26:17 -08006940 }
6941
6942 /**
6943 * Specialized version of listAudioPorts() listing only audio devices (AudioDevicePort)
6944 * @see listAudioPorts(ArrayList<AudioPort>)
6945 * @hide
6946 */
Eric Laurent4bcdba82015-05-01 11:37:49 -07006947 public static int listAudioDevicePorts(ArrayList<AudioDevicePort> devices) {
Eric Laurent8a1e7a82015-05-08 11:43:05 -07006948 if (devices == null) {
6949 return ERROR_BAD_VALUE;
6950 }
Eric Laurentb69681c2014-05-19 19:02:51 -07006951 ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
Eric Laurent8a1e7a82015-05-08 11:43:05 -07006952 int status = updateAudioPortCache(ports, null, null);
Eric Laurentb69681c2014-05-19 19:02:51 -07006953 if (status == SUCCESS) {
Eric Laurent8a1e7a82015-05-08 11:43:05 -07006954 filterDevicePorts(ports, devices);
Eric Laurentb69681c2014-05-19 19:02:51 -07006955 }
6956 return status;
Eric Laurenta198a292014-02-18 16:26:17 -08006957 }
6958
6959 /**
Eric Laurent8a1e7a82015-05-08 11:43:05 -07006960 * Specialized version of listPreviousAudioPorts() listing only audio devices (AudioDevicePort)
6961 * @see listPreviousAudioPorts(ArrayList<AudioPort>)
6962 * @hide
6963 */
6964 public static int listPreviousAudioDevicePorts(ArrayList<AudioDevicePort> devices) {
6965 if (devices == null) {
6966 return ERROR_BAD_VALUE;
6967 }
6968 ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
6969 int status = updateAudioPortCache(null, null, ports);
6970 if (status == SUCCESS) {
6971 filterDevicePorts(ports, devices);
6972 }
6973 return status;
6974 }
6975
6976 private static void filterDevicePorts(ArrayList<AudioPort> ports,
6977 ArrayList<AudioDevicePort> devices) {
6978 devices.clear();
6979 for (int i = 0; i < ports.size(); i++) {
6980 if (ports.get(i) instanceof AudioDevicePort) {
6981 devices.add((AudioDevicePort)ports.get(i));
6982 }
6983 }
6984 }
6985
6986 /**
Eric Laurenta198a292014-02-18 16:26:17 -08006987 * Create a connection between two or more devices. The framework will reject the request if
6988 * device types are not compatible or the implementation does not support the requested
6989 * configuration.
6990 * NOTE: current implementation is limited to one source and one sink per patch.
6991 * @param patch AudioPatch array where the newly created patch will be returned.
6992 * As input, if patch[0] is not null, the specified patch will be replaced by the
6993 * new patch created. This avoids calling releaseAudioPatch() when modifying a
6994 * patch and allows the implementation to optimize transitions.
6995 * @param sources List of source audio ports. All must be AudioPort.ROLE_SOURCE.
6996 * @param sinks List of sink audio ports. All must be AudioPort.ROLE_SINK.
6997 *
6998 * @return - {@link #SUCCESS} if connection is successful.
6999 * - {@link #ERROR_BAD_VALUE} if incompatible device types are passed.
7000 * - {@link #ERROR_INVALID_OPERATION} if the requested connection is not supported.
7001 * - {@link #ERROR_PERMISSION_DENIED} if the client does not have permission to create
7002 * a patch.
7003 * - {@link #ERROR_DEAD_OBJECT} if the server process is dead
7004 * - {@link #ERROR} if patch cannot be connected for any other reason.
7005 *
7006 * patch[0] contains the newly created patch
7007 * @hide
7008 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01007009 @UnsupportedAppUsage
Eric Laurent4bcdba82015-05-01 11:37:49 -07007010 public static int createAudioPatch(AudioPatch[] patch,
Eric Laurenta198a292014-02-18 16:26:17 -08007011 AudioPortConfig[] sources,
7012 AudioPortConfig[] sinks) {
Eric Laurentb69681c2014-05-19 19:02:51 -07007013 return AudioSystem.createAudioPatch(patch, sources, sinks);
Eric Laurenta198a292014-02-18 16:26:17 -08007014 }
7015
7016 /**
7017 * Releases an existing audio patch connection.
7018 * @param patch The audio patch to disconnect.
7019 * @return - {@link #SUCCESS} if disconnection is successful.
7020 * - {@link #ERROR_BAD_VALUE} if the specified patch does not exist.
7021 * - {@link #ERROR_PERMISSION_DENIED} if the client does not have permission to release
7022 * a patch.
7023 * - {@link #ERROR_DEAD_OBJECT} if the server process is dead
7024 * - {@link #ERROR} if patch cannot be released for any other reason.
7025 * @hide
7026 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01007027 @UnsupportedAppUsage
Eric Laurent4bcdba82015-05-01 11:37:49 -07007028 public static int releaseAudioPatch(AudioPatch patch) {
Eric Laurentb69681c2014-05-19 19:02:51 -07007029 return AudioSystem.releaseAudioPatch(patch);
Eric Laurenta198a292014-02-18 16:26:17 -08007030 }
7031
7032 /**
7033 * List all existing connections between audio ports.
7034 * @param patches An AudioPatch array where the list will be returned.
7035 * @hide
7036 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01007037 @UnsupportedAppUsage
Eric Laurent4bcdba82015-05-01 11:37:49 -07007038 public static int listAudioPatches(ArrayList<AudioPatch> patches) {
Eric Laurent8a1e7a82015-05-08 11:43:05 -07007039 return updateAudioPortCache(null, patches, null);
Eric Laurenta198a292014-02-18 16:26:17 -08007040 }
7041
7042 /**
7043 * Set the gain on the specified AudioPort. The AudioGainConfig config is build by
7044 * AudioGain.buildConfig()
7045 * @hide
7046 */
Eric Laurent4bcdba82015-05-01 11:37:49 -07007047 public static int setAudioPortGain(AudioPort port, AudioGainConfig gain) {
Eric Laurent3a241992014-05-19 19:33:26 -07007048 if (port == null || gain == null) {
7049 return ERROR_BAD_VALUE;
7050 }
7051 AudioPortConfig activeConfig = port.activeConfig();
7052 AudioPortConfig config = new AudioPortConfig(port, activeConfig.samplingRate(),
7053 activeConfig.channelMask(), activeConfig.format(), gain);
7054 config.mConfigMask = AudioPortConfig.GAIN;
7055 return AudioSystem.setAudioPortConfig(config);
Eric Laurenta198a292014-02-18 16:26:17 -08007056 }
7057
7058 /**
7059 * Listener registered by client to be notified upon new audio port connections,
7060 * disconnections or attributes update.
7061 * @hide
7062 */
7063 public interface OnAudioPortUpdateListener {
7064 /**
7065 * Callback method called upon audio port list update.
7066 * @param portList the updated list of audio ports
7067 */
Eric Laurentfdaed9d2014-07-29 09:29:30 -07007068 public void onAudioPortListUpdate(AudioPort[] portList);
Eric Laurenta198a292014-02-18 16:26:17 -08007069
7070 /**
7071 * Callback method called upon audio patch list update.
7072 * @param patchList the updated list of audio patches
7073 */
Eric Laurentfdaed9d2014-07-29 09:29:30 -07007074 public void onAudioPatchListUpdate(AudioPatch[] patchList);
Eric Laurenta198a292014-02-18 16:26:17 -08007075
7076 /**
7077 * Callback method called when the mediaserver dies
7078 */
Eric Laurentfdaed9d2014-07-29 09:29:30 -07007079 public void onServiceDied();
Eric Laurenta198a292014-02-18 16:26:17 -08007080 }
7081
7082 /**
Eric Laurent700e7342014-05-02 18:33:15 -07007083 * Register an audio port list update listener.
Eric Laurenta198a292014-02-18 16:26:17 -08007084 * @hide
7085 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01007086 @UnsupportedAppUsage
Eric Laurenta198a292014-02-18 16:26:17 -08007087 public void registerAudioPortUpdateListener(OnAudioPortUpdateListener l) {
Eric Laurentc573bc52015-06-26 10:01:12 -07007088 sAudioPortEventHandler.init();
Eric Laurentf076db42015-01-14 13:23:27 -08007089 sAudioPortEventHandler.registerListener(l);
Eric Laurenta198a292014-02-18 16:26:17 -08007090 }
7091
7092 /**
Eric Laurent700e7342014-05-02 18:33:15 -07007093 * Unregister an audio port list update listener.
Eric Laurenta198a292014-02-18 16:26:17 -08007094 * @hide
7095 */
Mathew Inwood31a792a2018-08-17 08:54:26 +01007096 @UnsupportedAppUsage
Eric Laurenta198a292014-02-18 16:26:17 -08007097 public void unregisterAudioPortUpdateListener(OnAudioPortUpdateListener l) {
Eric Laurentf076db42015-01-14 13:23:27 -08007098 sAudioPortEventHandler.unregisterListener(l);
Eric Laurenta198a292014-02-18 16:26:17 -08007099 }
Eric Laurentb69681c2014-05-19 19:02:51 -07007100
7101 //
7102 // AudioPort implementation
7103 //
7104
Cole Faust7da659b2022-10-15 21:33:29 -07007105 private static final int AUDIOPORT_GENERATION_INIT = 0;
7106 private static Object sAudioPortGenerationLock = new Object();
7107 @GuardedBy("sAudioPortGenerationLock")
7108 private static int sAudioPortGeneration = AUDIOPORT_GENERATION_INIT;
7109 private static ArrayList<AudioPort> sAudioPortsCached = new ArrayList<AudioPort>();
7110 private static ArrayList<AudioPort> sPreviousAudioPortsCached = new ArrayList<AudioPort>();
7111 private static ArrayList<AudioPatch> sAudioPatchesCached = new ArrayList<AudioPatch>();
Eric Laurentb69681c2014-05-19 19:02:51 -07007112
Eric Laurentf076db42015-01-14 13:23:27 -08007113 static int resetAudioPortGeneration() {
Eric Laurentb69681c2014-05-19 19:02:51 -07007114 int generation;
Cole Faust7da659b2022-10-15 21:33:29 -07007115 synchronized (sAudioPortGenerationLock) {
Eric Laurentf076db42015-01-14 13:23:27 -08007116 generation = sAudioPortGeneration;
7117 sAudioPortGeneration = AUDIOPORT_GENERATION_INIT;
Eric Laurentb69681c2014-05-19 19:02:51 -07007118 }
7119 return generation;
7120 }
7121
Eric Laurent8a1e7a82015-05-08 11:43:05 -07007122 static int updateAudioPortCache(ArrayList<AudioPort> ports, ArrayList<AudioPatch> patches,
7123 ArrayList<AudioPort> previousPorts) {
Eric Laurentc573bc52015-06-26 10:01:12 -07007124 sAudioPortEventHandler.init();
Cole Faust7da659b2022-10-15 21:33:29 -07007125 synchronized (sAudioPortGenerationLock) {
Eric Laurentb69681c2014-05-19 19:02:51 -07007126
Eric Laurentf076db42015-01-14 13:23:27 -08007127 if (sAudioPortGeneration == AUDIOPORT_GENERATION_INIT) {
Eric Laurentb69681c2014-05-19 19:02:51 -07007128 int[] patchGeneration = new int[1];
7129 int[] portGeneration = new int[1];
7130 int status;
7131 ArrayList<AudioPort> newPorts = new ArrayList<AudioPort>();
7132 ArrayList<AudioPatch> newPatches = new ArrayList<AudioPatch>();
7133
7134 do {
7135 newPorts.clear();
7136 status = AudioSystem.listAudioPorts(newPorts, portGeneration);
Eric Laurentb69681c2014-05-19 19:02:51 -07007137 if (status != SUCCESS) {
Wonsik Kimb561cce2015-01-30 17:48:51 +09007138 Log.w(TAG, "updateAudioPortCache: listAudioPorts failed");
Eric Laurentb69681c2014-05-19 19:02:51 -07007139 return status;
7140 }
7141 newPatches.clear();
7142 status = AudioSystem.listAudioPatches(newPatches, patchGeneration);
Eric Laurentb69681c2014-05-19 19:02:51 -07007143 if (status != SUCCESS) {
Wonsik Kimb561cce2015-01-30 17:48:51 +09007144 Log.w(TAG, "updateAudioPortCache: listAudioPatches failed");
Eric Laurentb69681c2014-05-19 19:02:51 -07007145 return status;
7146 }
jiabinc4ecaa52017-09-26 14:28:41 -07007147 // Loop until patch generation is the same as port generation unless audio ports
7148 // and audio patches are not null.
7149 } while (patchGeneration[0] != portGeneration[0]
7150 && (ports == null || patches == null));
7151 // If the patch generation doesn't equal port generation, return ERROR here in case
7152 // of mismatch between audio ports and audio patches.
7153 if (patchGeneration[0] != portGeneration[0]) {
7154 return ERROR;
7155 }
Eric Laurentb69681c2014-05-19 19:02:51 -07007156
7157 for (int i = 0; i < newPatches.size(); i++) {
7158 for (int j = 0; j < newPatches.get(i).sources().length; j++) {
Eric Laurentb4e09092014-06-02 19:18:43 -07007159 AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sources()[j],
7160 newPorts);
Eric Laurentb69681c2014-05-19 19:02:51 -07007161 newPatches.get(i).sources()[j] = portCfg;
7162 }
7163 for (int j = 0; j < newPatches.get(i).sinks().length; j++) {
Eric Laurentb4e09092014-06-02 19:18:43 -07007164 AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sinks()[j],
7165 newPorts);
Eric Laurentb69681c2014-05-19 19:02:51 -07007166 newPatches.get(i).sinks()[j] = portCfg;
7167 }
7168 }
Wonsik Kimb561cce2015-01-30 17:48:51 +09007169 for (Iterator<AudioPatch> i = newPatches.iterator(); i.hasNext(); ) {
7170 AudioPatch newPatch = i.next();
7171 boolean hasInvalidPort = false;
7172 for (AudioPortConfig portCfg : newPatch.sources()) {
7173 if (portCfg == null) {
7174 hasInvalidPort = true;
7175 break;
7176 }
7177 }
7178 for (AudioPortConfig portCfg : newPatch.sinks()) {
7179 if (portCfg == null) {
7180 hasInvalidPort = true;
7181 break;
7182 }
7183 }
7184 if (hasInvalidPort) {
7185 // Temporarily remove patches with invalid ports. One who created the patch
7186 // is responsible for dealing with the port change.
7187 i.remove();
7188 }
7189 }
Eric Laurentb69681c2014-05-19 19:02:51 -07007190
Eric Laurent8a1e7a82015-05-08 11:43:05 -07007191 sPreviousAudioPortsCached = sAudioPortsCached;
Eric Laurentf076db42015-01-14 13:23:27 -08007192 sAudioPortsCached = newPorts;
7193 sAudioPatchesCached = newPatches;
7194 sAudioPortGeneration = portGeneration[0];
Eric Laurentb69681c2014-05-19 19:02:51 -07007195 }
7196 if (ports != null) {
7197 ports.clear();
Eric Laurentf076db42015-01-14 13:23:27 -08007198 ports.addAll(sAudioPortsCached);
Eric Laurentb69681c2014-05-19 19:02:51 -07007199 }
7200 if (patches != null) {
7201 patches.clear();
Eric Laurentf076db42015-01-14 13:23:27 -08007202 patches.addAll(sAudioPatchesCached);
Eric Laurentb69681c2014-05-19 19:02:51 -07007203 }
Eric Laurent8a1e7a82015-05-08 11:43:05 -07007204 if (previousPorts != null) {
7205 previousPorts.clear();
7206 previousPorts.addAll(sPreviousAudioPortsCached);
7207 }
Eric Laurentb69681c2014-05-19 19:02:51 -07007208 }
7209 return SUCCESS;
7210 }
7211
Eric Laurentf076db42015-01-14 13:23:27 -08007212 static AudioPortConfig updatePortConfig(AudioPortConfig portCfg, ArrayList<AudioPort> ports) {
Eric Laurentb69681c2014-05-19 19:02:51 -07007213 AudioPort port = portCfg.port();
7214 int k;
7215 for (k = 0; k < ports.size(); k++) {
7216 // compare handles because the port returned by JNI is not of the correct
7217 // subclass
7218 if (ports.get(k).handle().equals(port.handle())) {
Eric Laurentb69681c2014-05-19 19:02:51 -07007219 port = ports.get(k);
7220 break;
7221 }
7222 }
7223 if (k == ports.size()) {
Darwin Huangbb111732022-10-21 13:14:32 +00007224 // This can happen in case of stale audio patch referring to a removed device and is
7225 // handled by the caller.
Eric Laurentb69681c2014-05-19 19:02:51 -07007226 return null;
7227 }
7228 AudioGainConfig gainCfg = portCfg.gain();
7229 if (gainCfg != null) {
7230 AudioGain gain = port.gain(gainCfg.index());
7231 gainCfg = gain.buildConfig(gainCfg.mode(),
7232 gainCfg.channelMask(),
7233 gainCfg.values(),
7234 gainCfg.rampDurationMs());
7235 }
7236 return port.buildConfig(portCfg.samplingRate(),
7237 portCfg.channelMask(),
7238 portCfg.format(),
7239 gainCfg);
7240 }
Paul McLeane3383cc2015-05-08 11:41:20 -07007241
7242 private OnAmPortUpdateListener mPortListener = null;
7243
7244 /**
7245 * The message sent to apps when the contents of the device list changes if they provide
Paul Wang9b2d0482022-05-17 10:18:01 +00007246 * a {@link Handler} object to {@link registerAudioDeviceCallback}.
Paul McLeane3383cc2015-05-08 11:41:20 -07007247 */
Paul McLeancbeb8a22015-06-10 08:21:27 -07007248 private final static int MSG_DEVICES_CALLBACK_REGISTERED = 0;
7249 private final static int MSG_DEVICES_DEVICES_ADDED = 1;
7250 private final static int MSG_DEVICES_DEVICES_REMOVED = 2;
Paul McLeane3383cc2015-05-08 11:41:20 -07007251
Paul McLean8e6c9f42015-05-19 11:13:41 -07007252 /**
7253 * The list of {@link AudioDeviceCallback} objects to receive add/remove notifications.
7254 */
Jack He89f97982018-05-02 19:10:56 -07007255 private final ArrayMap<AudioDeviceCallback, NativeEventHandlerDelegate> mDeviceCallbacks =
Paul McLean03346882015-05-12 15:36:56 -07007256 new ArrayMap<AudioDeviceCallback, NativeEventHandlerDelegate>();
Paul McLeane3383cc2015-05-08 11:41:20 -07007257
7258 /**
Paul McLean8e6c9f42015-05-19 11:13:41 -07007259 * The following are flags to allow users of {@link AudioManager#getDevices(int)} to filter
7260 * the results list to only those device types they are interested in.
7261 */
7262 /**
Paul McLeane3383cc2015-05-08 11:41:20 -07007263 * Specifies to the {@link AudioManager#getDevices(int)} method to include
7264 * source (i.e. input) audio devices.
7265 */
7266 public static final int GET_DEVICES_INPUTS = 0x0001;
7267
7268 /**
7269 * Specifies to the {@link AudioManager#getDevices(int)} method to include
7270 * sink (i.e. output) audio devices.
7271 */
7272 public static final int GET_DEVICES_OUTPUTS = 0x0002;
7273
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07007274 /** @hide */
7275 @IntDef(flag = true, prefix = "GET_DEVICES", value = {
7276 GET_DEVICES_INPUTS,
7277 GET_DEVICES_OUTPUTS }
7278 )
7279 @Retention(RetentionPolicy.SOURCE)
7280 public @interface AudioDeviceRole {}
7281
Paul McLeane3383cc2015-05-08 11:41:20 -07007282 /**
7283 * Specifies to the {@link AudioManager#getDevices(int)} method to include both
7284 * source and sink devices.
7285 */
7286 public static final int GET_DEVICES_ALL = GET_DEVICES_OUTPUTS | GET_DEVICES_INPUTS;
7287
7288 /**
7289 * Determines if a given AudioDevicePort meets the specified filter criteria.
7290 * @param port The port to test.
7291 * @param flags A set of bitflags specifying the criteria to test.
7292 * @see {@link GET_DEVICES_OUTPUTS} and {@link GET_DEVICES_INPUTS}
7293 **/
7294 private static boolean checkFlags(AudioDevicePort port, int flags) {
7295 return port.role() == AudioPort.ROLE_SINK && (flags & GET_DEVICES_OUTPUTS) != 0 ||
7296 port.role() == AudioPort.ROLE_SOURCE && (flags & GET_DEVICES_INPUTS) != 0;
7297 }
7298
Paul McLean11354572015-08-07 12:50:48 -06007299 private static boolean checkTypes(AudioDevicePort port) {
7300 return AudioDeviceInfo.convertInternalDeviceToDeviceType(port.type()) !=
jiabin9697c6c2018-03-20 17:13:04 -07007301 AudioDeviceInfo.TYPE_UNKNOWN;
Paul McLean11354572015-08-07 12:50:48 -06007302 }
7303
Paul McLeane3383cc2015-05-08 11:41:20 -07007304 /**
Paul McLean8e6c9f42015-05-19 11:13:41 -07007305 * Returns an array of {@link AudioDeviceInfo} objects corresponding to the audio devices
7306 * currently connected to the system and meeting the criteria specified in the
7307 * <code>flags</code> parameter.
jiabin83221092023-01-10 00:15:47 +00007308 * Notes that Android audio framework only support one device per device type. In that case,
7309 * if there are multiple audio device with the same device type connected to the Android device,
7310 * only the last reported device will be known by Android audio framework and returned by this
7311 * API.
Paul McLeane3383cc2015-05-08 11:41:20 -07007312 * @param flags A set of bitflags specifying the criteria to test.
Elliot Waite54de77472017-01-11 15:30:35 -08007313 * @see #GET_DEVICES_OUTPUTS
7314 * @see #GET_DEVICES_INPUTS
7315 * @see #GET_DEVICES_ALL
Paul McLeane3383cc2015-05-08 11:41:20 -07007316 * @return A (possibly zero-length) array of AudioDeviceInfo objects.
7317 */
Jean-Michel Trivi21cd5412019-10-30 08:16:51 -07007318 public AudioDeviceInfo[] getDevices(@AudioDeviceRole int flags) {
Paul McLeane3383cc2015-05-08 11:41:20 -07007319 return getDevicesStatic(flags);
7320 }
7321
Paul McLean8e6c9f42015-05-19 11:13:41 -07007322 /**
7323 * Does the actual computation to generate an array of (externally-visible) AudioDeviceInfo
7324 * objects from the current (internal) AudioDevicePort list.
7325 */
Paul McLean03346882015-05-12 15:36:56 -07007326 private static AudioDeviceInfo[]
7327 infoListFromPortList(ArrayList<AudioDevicePort> ports, int flags) {
Paul McLeane3383cc2015-05-08 11:41:20 -07007328
Paul McLean8e6c9f42015-05-19 11:13:41 -07007329 // figure out how many AudioDeviceInfo we need space for...
Paul McLeane3383cc2015-05-08 11:41:20 -07007330 int numRecs = 0;
7331 for (AudioDevicePort port : ports) {
Paul McLean11354572015-08-07 12:50:48 -06007332 if (checkTypes(port) && checkFlags(port, flags)) {
Paul McLeane3383cc2015-05-08 11:41:20 -07007333 numRecs++;
7334 }
7335 }
7336
Paul McLean8e6c9f42015-05-19 11:13:41 -07007337 // Now load them up...
Paul McLeane3383cc2015-05-08 11:41:20 -07007338 AudioDeviceInfo[] deviceList = new AudioDeviceInfo[numRecs];
7339 int slot = 0;
7340 for (AudioDevicePort port : ports) {
Paul McLean11354572015-08-07 12:50:48 -06007341 if (checkTypes(port) && checkFlags(port, flags)) {
Paul McLeane3383cc2015-05-08 11:41:20 -07007342 deviceList[slot++] = new AudioDeviceInfo(port);
7343 }
7344 }
7345
7346 return deviceList;
7347 }
7348
Paul McLean03346882015-05-12 15:36:56 -07007349 /*
Paul McLean8e6c9f42015-05-19 11:13:41 -07007350 * Calculate the list of ports that are in ports_B, but not in ports_A. This is used by
7351 * the add/remove callback mechanism to provide a list of the newly added or removed devices
7352 * rather than the whole list and make the app figure it out.
7353 * Note that calling this method with:
7354 * ports_A == PREVIOUS_ports and ports_B == CURRENT_ports will calculated ADDED ports.
7355 * ports_A == CURRENT_ports and ports_B == PREVIOUS_ports will calculated REMOVED ports.
Paul McLean03346882015-05-12 15:36:56 -07007356 */
7357 private static AudioDeviceInfo[] calcListDeltas(
7358 ArrayList<AudioDevicePort> ports_A, ArrayList<AudioDevicePort> ports_B, int flags) {
7359
7360 ArrayList<AudioDevicePort> delta_ports = new ArrayList<AudioDevicePort>();
7361
7362 AudioDevicePort cur_port = null;
7363 for (int cur_index = 0; cur_index < ports_B.size(); cur_index++) {
7364 boolean cur_port_found = false;
7365 cur_port = ports_B.get(cur_index);
7366 for (int prev_index = 0;
7367 prev_index < ports_A.size() && !cur_port_found;
7368 prev_index++) {
7369 cur_port_found = (cur_port.id() == ports_A.get(prev_index).id());
7370 }
7371
7372 if (!cur_port_found) {
7373 delta_ports.add(cur_port);
7374 }
7375 }
7376
7377 return infoListFromPortList(delta_ports, flags);
7378 }
7379
Paul McLeane3383cc2015-05-08 11:41:20 -07007380 /**
Paul McLean03346882015-05-12 15:36:56 -07007381 * Generates a list of AudioDeviceInfo objects corresponding to the audio devices currently
7382 * connected to the system and meeting the criteria specified in the <code>flags</code>
7383 * parameter.
Paul McLean8e6c9f42015-05-19 11:13:41 -07007384 * This is an internal function. The public API front is getDevices(int).
Paul McLean03346882015-05-12 15:36:56 -07007385 * @param flags A set of bitflags specifying the criteria to test.
Elliot Waite54de77472017-01-11 15:30:35 -08007386 * @see #GET_DEVICES_OUTPUTS
7387 * @see #GET_DEVICES_INPUTS
7388 * @see #GET_DEVICES_ALL
Paul McLean03346882015-05-12 15:36:56 -07007389 * @return A (possibly zero-length) array of AudioDeviceInfo objects.
7390 * @hide
7391 */
7392 public static AudioDeviceInfo[] getDevicesStatic(int flags) {
7393 ArrayList<AudioDevicePort> ports = new ArrayList<AudioDevicePort>();
7394 int status = AudioManager.listAudioDevicePorts(ports);
7395 if (status != AudioManager.SUCCESS) {
7396 // fail and bail!
Paul McLean8e6c9f42015-05-19 11:13:41 -07007397 return new AudioDeviceInfo[0]; // Always return an array.
Paul McLean03346882015-05-12 15:36:56 -07007398 }
7399
7400 return infoListFromPortList(ports, flags);
7401 }
7402
7403 /**
Eric Laurentb36d4a12020-10-09 09:52:49 -07007404 * Returns an {@link AudioDeviceInfo} corresponding to the specified {@link AudioPort} ID.
7405 * @param portId The audio port ID to look up for.
7406 * @param flags A set of bitflags specifying the criteria to test.
7407 * @see #GET_DEVICES_OUTPUTS
7408 * @see #GET_DEVICES_INPUTS
7409 * @see #GET_DEVICES_ALL
7410 * @return An AudioDeviceInfo or null if no device with matching port ID is found.
7411 * @hide
7412 */
7413 public static AudioDeviceInfo getDeviceForPortId(int portId, int flags) {
7414 if (portId == 0) {
7415 return null;
7416 }
7417 AudioDeviceInfo[] devices = getDevicesStatic(flags);
7418 for (AudioDeviceInfo device : devices) {
7419 if (device.getId() == portId) {
7420 return device;
7421 }
7422 }
7423 return null;
7424 }
7425
7426 /**
Paul McLean8e6c9f42015-05-19 11:13:41 -07007427 * Registers an {@link AudioDeviceCallback} object to receive notifications of changes
Paul McLeane3383cc2015-05-08 11:41:20 -07007428 * to the set of connected audio devices.
Paul McLean8e6c9f42015-05-19 11:13:41 -07007429 * @param callback The {@link AudioDeviceCallback} object to receive connect/disconnect
7430 * notifications.
7431 * @param handler Specifies the {@link Handler} object for the thread on which to execute
7432 * the callback. If <code>null</code>, the {@link Handler} associated with the main
7433 * {@link Looper} will be used.
Paul McLeane3383cc2015-05-08 11:41:20 -07007434 */
Paul McLean03346882015-05-12 15:36:56 -07007435 public void registerAudioDeviceCallback(AudioDeviceCallback callback,
Jean-Michel Trivi1ec66182019-12-30 09:53:04 -08007436 @Nullable Handler handler) {
Eric Laurent1691f732015-07-14 16:27:54 -07007437 synchronized (mDeviceCallbacks) {
7438 if (callback != null && !mDeviceCallbacks.containsKey(callback)) {
Eric Laurentc573bc52015-06-26 10:01:12 -07007439 if (mDeviceCallbacks.size() == 0) {
7440 if (mPortListener == null) {
7441 mPortListener = new OnAmPortUpdateListener();
7442 }
7443 registerAudioPortUpdateListener(mPortListener);
7444 }
Paul McLeancbeb8a22015-06-10 08:21:27 -07007445 NativeEventHandlerDelegate delegate =
7446 new NativeEventHandlerDelegate(callback, handler);
7447 mDeviceCallbacks.put(callback, delegate);
jiabin8c3a7672018-05-22 15:44:21 -07007448 broadcastDeviceListChange_sync(delegate.getHandler());
Paul McLeane3383cc2015-05-08 11:41:20 -07007449 }
7450 }
7451 }
7452
7453 /**
Paul McLean8e6c9f42015-05-19 11:13:41 -07007454 * Unregisters an {@link AudioDeviceCallback} object which has been previously registered
Paul McLeane3383cc2015-05-08 11:41:20 -07007455 * to receive notifications of changes to the set of connected audio devices.
Paul McLean8e6c9f42015-05-19 11:13:41 -07007456 * @param callback The {@link AudioDeviceCallback} object that was previously registered
Elliot Waite54de77472017-01-11 15:30:35 -08007457 * with {@link AudioManager#registerAudioDeviceCallback} to be unregistered.
Paul McLeane3383cc2015-05-08 11:41:20 -07007458 */
Paul McLean03346882015-05-12 15:36:56 -07007459 public void unregisterAudioDeviceCallback(AudioDeviceCallback callback) {
7460 synchronized (mDeviceCallbacks) {
7461 if (mDeviceCallbacks.containsKey(callback)) {
7462 mDeviceCallbacks.remove(callback);
Eric Laurentc573bc52015-06-26 10:01:12 -07007463 if (mDeviceCallbacks.size() == 0) {
7464 unregisterAudioPortUpdateListener(mPortListener);
7465 }
Paul McLeane3383cc2015-05-08 11:41:20 -07007466 }
7467 }
7468 }
7469
jiabinc0f49442018-01-05 10:23:50 -08007470 /**
7471 * Set port id for microphones by matching device type and address.
7472 * @hide
7473 */
7474 public static void setPortIdForMicrophones(ArrayList<MicrophoneInfo> microphones) {
7475 AudioDeviceInfo[] devices = getDevicesStatic(AudioManager.GET_DEVICES_INPUTS);
7476 for (int i = microphones.size() - 1; i >= 0; i--) {
7477 boolean foundPortId = false;
7478 for (AudioDeviceInfo device : devices) {
7479 if (device.getPort().type() == microphones.get(i).getInternalDeviceType()
7480 && TextUtils.equals(device.getAddress(), microphones.get(i).getAddress())) {
7481 microphones.get(i).setId(device.getId());
7482 foundPortId = true;
7483 break;
7484 }
7485 }
7486 if (!foundPortId) {
7487 Log.i(TAG, "Failed to find port id for device with type:"
7488 + microphones.get(i).getType() + " address:"
7489 + microphones.get(i).getAddress());
7490 microphones.remove(i);
7491 }
7492 }
7493 }
7494
7495 /**
jiabin589a2362018-02-22 16:21:53 -08007496 * Convert {@link AudioDeviceInfo} to {@link MicrophoneInfo}.
7497 * @hide
7498 */
7499 public static MicrophoneInfo microphoneInfoFromAudioDeviceInfo(AudioDeviceInfo deviceInfo) {
7500 int deviceType = deviceInfo.getType();
7501 int micLocation = (deviceType == AudioDeviceInfo.TYPE_BUILTIN_MIC
7502 || deviceType == AudioDeviceInfo.TYPE_TELEPHONY) ? MicrophoneInfo.LOCATION_MAINBODY
7503 : deviceType == AudioDeviceInfo.TYPE_UNKNOWN ? MicrophoneInfo.LOCATION_UNKNOWN
7504 : MicrophoneInfo.LOCATION_PERIPHERAL;
7505 MicrophoneInfo microphone = new MicrophoneInfo(
7506 deviceInfo.getPort().name() + deviceInfo.getId(),
7507 deviceInfo.getPort().type(), deviceInfo.getAddress(), micLocation,
7508 MicrophoneInfo.GROUP_UNKNOWN, MicrophoneInfo.INDEX_IN_THE_GROUP_UNKNOWN,
7509 MicrophoneInfo.POSITION_UNKNOWN, MicrophoneInfo.ORIENTATION_UNKNOWN,
7510 new ArrayList<Pair<Float, Float>>(), new ArrayList<Pair<Integer, Integer>>(),
7511 MicrophoneInfo.SENSITIVITY_UNKNOWN, MicrophoneInfo.SPL_UNKNOWN,
7512 MicrophoneInfo.SPL_UNKNOWN, MicrophoneInfo.DIRECTIONALITY_UNKNOWN);
7513 microphone.setId(deviceInfo.getId());
7514 return microphone;
7515 }
7516
7517 /**
jiabind0be5b22018-04-10 14:10:04 -07007518 * Add {@link MicrophoneInfo} by device information while filtering certain types.
7519 */
7520 private void addMicrophonesFromAudioDeviceInfo(ArrayList<MicrophoneInfo> microphones,
7521 HashSet<Integer> filterTypes) {
7522 AudioDeviceInfo[] devices = getDevicesStatic(GET_DEVICES_INPUTS);
7523 for (AudioDeviceInfo device : devices) {
7524 if (filterTypes.contains(device.getType())) {
7525 continue;
7526 }
7527 MicrophoneInfo microphone = microphoneInfoFromAudioDeviceInfo(device);
7528 microphones.add(microphone);
7529 }
7530 }
7531
7532 /**
jiabinc0f49442018-01-05 10:23:50 -08007533 * Returns a list of {@link MicrophoneInfo} that corresponds to the characteristics
7534 * of all available microphones. The list is empty when no microphones are available
7535 * on the device. An error during the query will result in an IOException being thrown.
7536 *
7537 * @return a list that contains all microphones' characteristics
7538 * @throws IOException if an error occurs.
7539 */
7540 public List<MicrophoneInfo> getMicrophones() throws IOException {
7541 ArrayList<MicrophoneInfo> microphones = new ArrayList<MicrophoneInfo>();
7542 int status = AudioSystem.getMicrophones(microphones);
jiabind0be5b22018-04-10 14:10:04 -07007543 HashSet<Integer> filterTypes = new HashSet<>();
7544 filterTypes.add(AudioDeviceInfo.TYPE_TELEPHONY);
jiabinc0f49442018-01-05 10:23:50 -08007545 if (status != AudioManager.SUCCESS) {
jiabind0be5b22018-04-10 14:10:04 -07007546 // fail and populate microphones with unknown characteristics by device information.
jiabina26a7622018-04-11 15:38:46 -07007547 if (status != AudioManager.ERROR_INVALID_OPERATION) {
7548 Log.e(TAG, "getMicrophones failed:" + status);
7549 }
7550 Log.i(TAG, "fallback on device info");
jiabind0be5b22018-04-10 14:10:04 -07007551 addMicrophonesFromAudioDeviceInfo(microphones, filterTypes);
7552 return microphones;
jiabinc0f49442018-01-05 10:23:50 -08007553 }
7554 setPortIdForMicrophones(microphones);
jiabind0be5b22018-04-10 14:10:04 -07007555 filterTypes.add(AudioDeviceInfo.TYPE_BUILTIN_MIC);
7556 addMicrophonesFromAudioDeviceInfo(microphones, filterTypes);
jiabinc0f49442018-01-05 10:23:50 -08007557 return microphones;
7558 }
7559
Arun Mirpuricb102fa2019-01-11 18:39:21 -08007560 /**
7561 * Returns a list of audio formats that corresponds to encoding formats
William Escandea05cb452021-12-08 14:14:19 +01007562 * supported on offload path for A2DP playback.
Arun Mirpuricb102fa2019-01-11 18:39:21 -08007563 *
7564 * @return a list of {@link BluetoothCodecConfig} objects containing encoding formats
William Escandea05cb452021-12-08 14:14:19 +01007565 * supported for offload A2DP playback
Arun Mirpuricb102fa2019-01-11 18:39:21 -08007566 * @hide
7567 */
William Escandea05cb452021-12-08 14:14:19 +01007568 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
7569 public @NonNull List<BluetoothCodecConfig> getHwOffloadFormatsSupportedForA2dp() {
7570 ArrayList<Integer> formatsList = new ArrayList<>();
7571 ArrayList<BluetoothCodecConfig> codecConfigList = new ArrayList<>();
Arun Mirpuricb102fa2019-01-11 18:39:21 -08007572
William Escandea05cb452021-12-08 14:14:19 +01007573 int status = AudioSystem.getHwOffloadFormatsSupportedForBluetoothMedia(
7574 AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, formatsList);
Arun Mirpuricb102fa2019-01-11 18:39:21 -08007575 if (status != AudioManager.SUCCESS) {
William Escandea05cb452021-12-08 14:14:19 +01007576 Log.e(TAG, "getHwOffloadEncodingFormatsSupportedForA2DP failed:" + status);
7577 return codecConfigList;
Arun Mirpuricb102fa2019-01-11 18:39:21 -08007578 }
7579
William Escandea05cb452021-12-08 14:14:19 +01007580 for (Integer format : formatsList) {
7581 int btSourceCodec = AudioSystem.audioFormatToBluetoothSourceCodec(format);
7582 if (btSourceCodec != BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID) {
Etienne Ruffieux2c5180a2022-03-08 13:31:41 +00007583 codecConfigList.add(
7584 new BluetoothCodecConfig.Builder().setCodecType(btSourceCodec).build());
Arun Mirpuricb102fa2019-01-11 18:39:21 -08007585 }
William Escandea05cb452021-12-08 14:14:19 +01007586 }
7587 return codecConfigList;
7588 }
7589
7590 /**
7591 * Returns a list of audio formats that corresponds to encoding formats
7592 * supported on offload path for Le audio playback.
7593 *
7594 * @return a list of {@link BluetoothLeAudioCodecConfig} objects containing encoding formats
7595 * supported for offload Le Audio playback
7596 * @hide
7597 */
7598 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
7599 @NonNull
7600 public List<BluetoothLeAudioCodecConfig> getHwOffloadFormatsSupportedForLeAudio() {
7601 ArrayList<Integer> formatsList = new ArrayList<>();
7602 ArrayList<BluetoothLeAudioCodecConfig> leAudioCodecConfigList = new ArrayList<>();
7603
7604 int status = AudioSystem.getHwOffloadFormatsSupportedForBluetoothMedia(
7605 AudioSystem.DEVICE_OUT_BLE_HEADSET, formatsList);
7606 if (status != AudioManager.SUCCESS) {
7607 Log.e(TAG, "getHwOffloadEncodingFormatsSupportedForLeAudio failed:" + status);
Patty46694212021-11-04 21:03:32 +08007608 return leAudioCodecConfigList;
Arun Mirpuricb102fa2019-01-11 18:39:21 -08007609 }
William Escandea05cb452021-12-08 14:14:19 +01007610
7611 for (Integer format : formatsList) {
7612 int btLeAudioCodec = AudioSystem.audioFormatToBluetoothLeAudioSourceCodec(format);
7613 if (btLeAudioCodec != BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID) {
7614 leAudioCodecConfigList.add(new BluetoothLeAudioCodecConfig.Builder()
7615 .setCodecType(btLeAudioCodec)
7616 .build());
7617 }
7618 }
7619 return leAudioCodecConfigList;
Arun Mirpuricb102fa2019-01-11 18:39:21 -08007620 }
7621
Paul McLeancbeb8a22015-06-10 08:21:27 -07007622 // Since we need to calculate the changes since THE LAST NOTIFICATION, and not since the
7623 // (unpredictable) last time updateAudioPortCache() was called by someone, keep a list
7624 // of the ports that exist at the time of the last notification.
7625 private ArrayList<AudioDevicePort> mPreviousPorts = new ArrayList<AudioDevicePort>();
7626
Paul McLeane3383cc2015-05-08 11:41:20 -07007627 /**
Paul McLean8e6c9f42015-05-19 11:13:41 -07007628 * Internal method to compute and generate add/remove messages and then send to any
jiabin8c3a7672018-05-22 15:44:21 -07007629 * registered callbacks. Must be called synchronized on mDeviceCallbacks.
Paul McLeane3383cc2015-05-08 11:41:20 -07007630 */
jiabin8c3a7672018-05-22 15:44:21 -07007631 private void broadcastDeviceListChange_sync(Handler handler) {
Paul McLean03346882015-05-12 15:36:56 -07007632 int status;
7633
Paul McLeancbeb8a22015-06-10 08:21:27 -07007634 // Get the new current set of ports
Paul McLean03346882015-05-12 15:36:56 -07007635 ArrayList<AudioDevicePort> current_ports = new ArrayList<AudioDevicePort>();
7636 status = AudioManager.listAudioDevicePorts(current_ports);
7637 if (status != AudioManager.SUCCESS) {
7638 return;
7639 }
7640
Paul McLeancbeb8a22015-06-10 08:21:27 -07007641 if (handler != null) {
7642 // This is the callback for the registration, so send the current list
7643 AudioDeviceInfo[] deviceList =
7644 infoListFromPortList(current_ports, GET_DEVICES_ALL);
7645 handler.sendMessage(
7646 Message.obtain(handler, MSG_DEVICES_CALLBACK_REGISTERED, deviceList));
7647 } else {
7648 AudioDeviceInfo[] added_devices =
7649 calcListDeltas(mPreviousPorts, current_ports, GET_DEVICES_ALL);
7650 AudioDeviceInfo[] removed_devices =
7651 calcListDeltas(current_ports, mPreviousPorts, GET_DEVICES_ALL);
Paul McLeancbeb8a22015-06-10 08:21:27 -07007652 if (added_devices.length != 0 || removed_devices.length != 0) {
jiabin8c3a7672018-05-22 15:44:21 -07007653 for (int i = 0; i < mDeviceCallbacks.size(); i++) {
7654 handler = mDeviceCallbacks.valueAt(i).getHandler();
7655 if (handler != null) {
7656 if (removed_devices.length != 0) {
7657 handler.sendMessage(Message.obtain(handler,
7658 MSG_DEVICES_DEVICES_REMOVED,
7659 removed_devices));
7660 }
7661 if (added_devices.length != 0) {
7662 handler.sendMessage(Message.obtain(handler,
7663 MSG_DEVICES_DEVICES_ADDED,
7664 added_devices));
Paul McLeancbeb8a22015-06-10 08:21:27 -07007665 }
Paul McLean03346882015-05-12 15:36:56 -07007666 }
7667 }
Paul McLeane3383cc2015-05-08 11:41:20 -07007668 }
7669 }
Paul McLeancbeb8a22015-06-10 08:21:27 -07007670
7671 mPreviousPorts = current_ports;
Paul McLeane3383cc2015-05-08 11:41:20 -07007672 }
7673
7674 /**
7675 * Handles Port list update notifications from the AudioManager
7676 */
7677 private class OnAmPortUpdateListener implements AudioManager.OnAudioPortUpdateListener {
7678 static final String TAG = "OnAmPortUpdateListener";
7679 public void onAudioPortListUpdate(AudioPort[] portList) {
jiabin8c3a7672018-05-22 15:44:21 -07007680 synchronized (mDeviceCallbacks) {
7681 broadcastDeviceListChange_sync(null);
7682 }
Paul McLeane3383cc2015-05-08 11:41:20 -07007683 }
7684
7685 /**
7686 * Callback method called upon audio patch list update.
Paul McLean8e6c9f42015-05-19 11:13:41 -07007687 * Note: We don't do anything with Patches at this time, so ignore this notification.
7688 * @param patchList the updated list of audio patches.
Paul McLeane3383cc2015-05-08 11:41:20 -07007689 */
7690 public void onAudioPatchListUpdate(AudioPatch[] patchList) {}
7691
7692 /**
7693 * Callback method called when the mediaserver dies
7694 */
7695 public void onServiceDied() {
jiabin8c3a7672018-05-22 15:44:21 -07007696 synchronized (mDeviceCallbacks) {
7697 broadcastDeviceListChange_sync(null);
7698 }
Paul McLeane3383cc2015-05-08 11:41:20 -07007699 }
7700 }
7701
Eric Laurent1d3cdce2018-01-20 10:31:21 -08007702
7703 /**
7704 * @hide
7705 * Abstract class to receive event notification about audioserver process state.
7706 */
7707 @SystemApi
7708 public abstract static class AudioServerStateCallback {
7709 public void onAudioServerDown() { }
7710 public void onAudioServerUp() { }
7711 }
7712
7713 private Executor mAudioServerStateExec;
7714 private AudioServerStateCallback mAudioServerStateCb;
7715 private final Object mAudioServerStateCbLock = new Object();
7716
7717 private final IAudioServerStateDispatcher mAudioServerStateDispatcher =
7718 new IAudioServerStateDispatcher.Stub() {
7719 @Override
7720 public void dispatchAudioServerStateChange(boolean state) {
7721 Executor exec;
7722 AudioServerStateCallback cb;
7723
7724 synchronized (mAudioServerStateCbLock) {
7725 exec = mAudioServerStateExec;
7726 cb = mAudioServerStateCb;
7727 }
7728
7729 if ((exec == null) || (cb == null)) {
7730 return;
7731 }
7732 if (state) {
7733 exec.execute(() -> cb.onAudioServerUp());
7734 } else {
7735 exec.execute(() -> cb.onAudioServerDown());
7736 }
7737 }
7738 };
7739
7740 /**
7741 * @hide
7742 * Registers a callback for notification of audio server state changes.
7743 * @param executor {@link Executor} to handle the callbacks
7744 * @param stateCallback the callback to receive the audio server state changes
7745 * To remove the callabck, pass a null reference for both executor and stateCallback.
7746 */
7747 @SystemApi
7748 public void setAudioServerStateCallback(@NonNull Executor executor,
7749 @NonNull AudioServerStateCallback stateCallback) {
7750 if (stateCallback == null) {
7751 throw new IllegalArgumentException("Illegal null AudioServerStateCallback");
7752 }
7753 if (executor == null) {
7754 throw new IllegalArgumentException(
7755 "Illegal null Executor for the AudioServerStateCallback");
7756 }
7757
7758 synchronized (mAudioServerStateCbLock) {
7759 if (mAudioServerStateCb != null) {
7760 throw new IllegalStateException(
7761 "setAudioServerStateCallback called with already registered callabck");
7762 }
7763 final IAudioService service = getService();
7764 try {
7765 service.registerAudioServerStateDispatcher(mAudioServerStateDispatcher);
7766 } catch (RemoteException e) {
7767 throw e.rethrowFromSystemServer();
7768 }
7769 mAudioServerStateExec = executor;
7770 mAudioServerStateCb = stateCallback;
7771 }
7772 }
7773
7774 /**
7775 * @hide
7776 * Unregisters the callback for notification of audio server state changes.
7777 */
7778 @SystemApi
7779 public void clearAudioServerStateCallback() {
7780 synchronized (mAudioServerStateCbLock) {
7781 if (mAudioServerStateCb != null) {
7782 final IAudioService service = getService();
7783 try {
7784 service.unregisterAudioServerStateDispatcher(
7785 mAudioServerStateDispatcher);
7786 } catch (RemoteException e) {
7787 throw e.rethrowFromSystemServer();
7788 }
7789 }
7790 mAudioServerStateExec = null;
7791 mAudioServerStateCb = null;
7792 }
7793 }
7794
7795 /**
7796 * @hide
7797 * Checks if native audioservice is running or not.
7798 * @return true if native audioservice runs, false otherwise.
7799 */
7800 @SystemApi
7801 public boolean isAudioServerRunning() {
7802 final IAudioService service = getService();
7803 try {
7804 return service.isAudioServerRunning();
7805 } catch (RemoteException e) {
7806 throw e.rethrowFromSystemServer();
7807 }
7808 }
7809
jiabin39940752018-04-02 18:18:45 -07007810 /**
Kriti Dang527e66c2021-03-04 10:37:22 +01007811 * Sets the surround sound mode.
7812 *
7813 * @return true if successful, otherwise false
7814 */
Jean-Michel Trivic4557822023-01-23 18:19:52 +00007815 @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
Kriti Dang527e66c2021-03-04 10:37:22 +01007816 public boolean setEncodedSurroundMode(@EncodedSurroundOutputMode int mode) {
7817 try {
7818 return getService().setEncodedSurroundMode(mode);
7819 } catch (RemoteException e) {
7820 throw e.rethrowFromSystemServer();
7821 }
7822 }
7823
7824 /**
7825 * Gets the surround sound mode.
7826 *
7827 * @return true if successful, otherwise false
7828 */
Kriti Dang527e66c2021-03-04 10:37:22 +01007829 public @EncodedSurroundOutputMode int getEncodedSurroundMode() {
7830 try {
Kriti Dang98fdb262021-04-01 13:26:00 +02007831 return getService().getEncodedSurroundMode(
7832 getContext().getApplicationInfo().targetSdkVersion);
Kriti Dang527e66c2021-03-04 10:37:22 +01007833 } catch (RemoteException e) {
7834 throw e.rethrowFromSystemServer();
7835 }
7836 }
7837
7838 /**
jiabin39940752018-04-02 18:18:45 -07007839 * @hide
7840 * Returns all surround formats.
7841 * @return a map where the key is a surround format and
7842 * the value indicates the surround format is enabled or not
7843 */
Marin Shalamanov49e778e2021-06-02 14:12:41 +02007844 @TestApi
7845 @NonNull
jiabin39940752018-04-02 18:18:45 -07007846 public Map<Integer, Boolean> getSurroundFormats() {
Kriti Dang1380c0e2021-06-04 14:51:48 +02007847 try {
7848 return getService().getSurroundFormats();
7849 } catch (RemoteException e) {
7850 throw e.rethrowFromSystemServer();
jiabin39940752018-04-02 18:18:45 -07007851 }
jiabin39940752018-04-02 18:18:45 -07007852 }
7853
7854 /**
Kriti Dang3f296bd2021-05-31 15:54:44 +02007855 * Sets and persists a certain surround format as enabled or not.
7856 * <p>
7857 * This API is called by TvSettings surround sound menu when user enables or disables a
7858 * surround sound format. This setting is persisted as global user setting.
7859 * Applications should revert their changes to surround sound settings unless they intend to
7860 * modify the global user settings across all apps. The framework does not auto-revert an
7861 * application's settings after a lifecycle event. Audio focus is not required to apply these
7862 * settings.
Kriti Dang1985c452021-05-10 17:06:44 +02007863 *
jiabin39940752018-04-02 18:18:45 -07007864 * @param enabled the required surround format state, true for enabled, false for disabled
7865 * @return true if successful, otherwise false
7866 */
Jean-Michel Trivic4557822023-01-23 18:19:52 +00007867 @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
jiabin39940752018-04-02 18:18:45 -07007868 public boolean setSurroundFormatEnabled(
7869 @AudioFormat.SurroundSoundEncoding int audioFormat, boolean enabled) {
Kriti Dang527e66c2021-03-04 10:37:22 +01007870 try {
7871 return getService().setSurroundFormatEnabled(audioFormat, enabled);
7872 } catch (RemoteException e) {
7873 throw e.rethrowFromSystemServer();
7874 }
7875 }
7876
7877 /**
7878 * Gets whether a certain surround format is enabled or not.
7879 * @param audioFormat a surround format
7880 *
7881 * @return whether the required surround format is enabled
7882 */
Kriti Dang527e66c2021-03-04 10:37:22 +01007883 public boolean isSurroundFormatEnabled(@AudioFormat.SurroundSoundEncoding int audioFormat) {
7884 try {
7885 return getService().isSurroundFormatEnabled(audioFormat);
7886 } catch (RemoteException e) {
7887 throw e.rethrowFromSystemServer();
7888 }
jiabin39940752018-04-02 18:18:45 -07007889 }
7890
7891 /**
7892 * @hide
7893 * Returns all surround formats that are reported by the connected HDMI device.
Kriti Dang01924232021-03-02 13:51:09 +01007894 * The return values are not affected by calling setSurroundFormatEnabled.
jiabin39940752018-04-02 18:18:45 -07007895 *
Kriti Dang01924232021-03-02 13:51:09 +01007896 * @return a list of surround formats
jiabin39940752018-04-02 18:18:45 -07007897 */
Kriti Dang1380c0e2021-06-04 14:51:48 +02007898 @TestApi
7899 @NonNull
7900 public List<Integer> getReportedSurroundFormats() {
7901 try {
7902 return getService().getReportedSurroundFormats();
7903 } catch (RemoteException e) {
7904 throw e.rethrowFromSystemServer();
jiabin39940752018-04-02 18:18:45 -07007905 }
jiabin39940752018-04-02 18:18:45 -07007906 }
7907
jiabin66f9e722018-11-02 16:20:19 -07007908 /**
7909 * Return if audio haptic coupled playback is supported or not.
7910 *
7911 * @return whether audio haptic playback supported.
7912 */
7913 public static boolean isHapticPlaybackSupported() {
7914 return AudioSystem.isHapticPlaybackSupported();
7915 }
7916
François Gaffie0699fec2018-07-09 14:35:10 +02007917 /**
7918 * @hide
Carter Hsu2065d1e2022-01-19 19:54:50 +08007919 * Indicates whether a platform supports the Ultrasound feature which covers the playback
7920 * and recording of 20kHz~ sounds. If platform supports Ultrasound, then the
7921 * usage will be
7922 * To start the Ultrasound playback:
7923 * - Create an AudioTrack with {@link AudioAttributes.CONTENT_TYPE_ULTRASOUND}.
7924 * To start the Ultrasound capture:
7925 * - Create an AudioRecord with {@link MediaRecorder.AudioSource.ULTRASOUND}.
7926 *
7927 * @return whether the ultrasound feature is supported, true when platform supports both
7928 * Ultrasound playback and capture, false otherwise.
7929 */
7930 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00007931 @RequiresPermission(Manifest.permission.ACCESS_ULTRASOUND)
Carter Hsu3ea30de42022-02-15 15:59:00 +08007932 public boolean isUltrasoundSupported() {
7933 try {
7934 return getService().isUltrasoundSupported();
7935 } catch (RemoteException e) {
7936 throw e.rethrowFromSystemServer();
7937 }
Carter Hsu2065d1e2022-01-19 19:54:50 +08007938 }
7939
7940 /**
7941 * @hide
Atneya Naireeab0a72022-12-15 09:32:51 -08007942 * Indicates whether the platform supports capturing content from the hotword recognition
7943 * pipeline. To capture content of this type, create an AudioRecord with
7944 * {@link AudioRecord.Builder.setRequestHotwordStream(boolean, boolean)}.
7945 * @param lookbackAudio Query if the hotword stream additionally supports providing buffered
7946 * audio prior to stream open.
7947 * @return True if the platform supports capturing hotword content, and if lookbackAudio
7948 * is true, if it additionally supports capturing buffered hotword content prior to stream
7949 * open. False otherwise.
7950 */
7951 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00007952 @RequiresPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD)
Atneya Naireeab0a72022-12-15 09:32:51 -08007953 public boolean isHotwordStreamSupported(boolean lookbackAudio) {
7954 try {
7955 return getService().isHotwordStreamSupported(lookbackAudio);
7956 } catch (RemoteException e) {
7957 return false;
7958 }
7959 }
7960
7961 /**
7962 * @hide
François Gaffie0699fec2018-07-09 14:35:10 +02007963 * Introspection API to retrieve audio product strategies.
7964 * When implementing {Car|Oem}AudioManager, use this method to retrieve the collection of
7965 * audio product strategies, which is indexed by a weakly typed index in order to be extended
7966 * by OEM without any needs of AOSP patches.
7967 * The {Car|Oem}AudioManager can expose API to build {@link AudioAttributes} for a given product
7968 * strategy refered either by its index or human readable string. It will allow clients
7969 * application to start streaming data using these {@link AudioAttributes} on the selected
7970 * device by Audio Policy Engine.
7971 * @return a (possibly zero-length) array of
7972 * {@see android.media.audiopolicy.AudioProductStrategy} objects.
7973 */
7974 @SystemApi
Hayden Gomes6d69bde2019-04-04 13:10:13 -07007975 @NonNull
Jean-Michel Trivic4557822023-01-23 18:19:52 +00007976 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Hayden Gomes6d69bde2019-04-04 13:10:13 -07007977 public static List<AudioProductStrategy> getAudioProductStrategies() {
François Gaffie0699fec2018-07-09 14:35:10 +02007978 final IAudioService service = getService();
7979 try {
7980 return service.getAudioProductStrategies();
7981 } catch (RemoteException e) {
7982 throw e.rethrowFromSystemServer();
7983 }
7984 }
7985
François Gaffieadcd00a2018-09-18 17:06:26 +02007986 /**
7987 * @hide
7988 * Introspection API to retrieve audio volume groups.
7989 * When implementing {Car|Oem}AudioManager, use this method to retrieve the collection of
7990 * audio volume groups.
Hayden Gomesebd6aaa2019-04-04 13:14:21 -07007991 * @return a (possibly zero-length) List of
7992 * {@see android.media.audiopolicy.AudioVolumeGroup} objects.
François Gaffieadcd00a2018-09-18 17:06:26 +02007993 */
7994 @SystemApi
Hayden Gomesebd6aaa2019-04-04 13:14:21 -07007995 @NonNull
Jean-Michel Trivic4557822023-01-23 18:19:52 +00007996 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Hayden Gomesebd6aaa2019-04-04 13:14:21 -07007997 public static List<AudioVolumeGroup> getAudioVolumeGroups() {
François Gaffie9c362102018-09-21 17:43:52 +02007998 final IAudioService service = getService();
7999 try {
Hayden Gomesebd6aaa2019-04-04 13:14:21 -07008000 return service.getAudioVolumeGroups();
François Gaffie9c362102018-09-21 17:43:52 +02008001 } catch (RemoteException e) {
8002 throw e.rethrowFromSystemServer();
8003 }
François Gaffieadcd00a2018-09-18 17:06:26 +02008004 }
8005
8006 /**
8007 * @hide
8008 * Callback registered by client to be notified upon volume group change.
8009 */
8010 @SystemApi
8011 public abstract static class VolumeGroupCallback {
8012 /**
8013 * Callback method called upon audio volume group change.
8014 * @param group the group for which the volume has changed
8015 */
8016 public void onAudioVolumeGroupChanged(int group, int flags) {}
8017 }
8018
8019 /**
8020 * @hide
8021 * Register an audio volume group change listener.
8022 * @param callback the {@link VolumeGroupCallback} to register
8023 */
8024 @SystemApi
8025 public void registerVolumeGroupCallback(
8026 @NonNull Executor executor,
8027 @NonNull VolumeGroupCallback callback) {
8028 Preconditions.checkNotNull(executor, "executor must not be null");
8029 Preconditions.checkNotNull(callback, "volume group change cb must not be null");
8030 sAudioAudioVolumeGroupChangedHandler.init();
8031 // TODO: make use of executor
8032 sAudioAudioVolumeGroupChangedHandler.registerListener(callback);
8033 }
8034
8035 /**
8036 * @hide
8037 * Unregister an audio volume group change listener.
8038 * @param callback the {@link VolumeGroupCallback} to unregister
8039 */
8040 @SystemApi
8041 public void unregisterVolumeGroupCallback(
8042 @NonNull VolumeGroupCallback callback) {
8043 Preconditions.checkNotNull(callback, "volume group change cb must not be null");
8044 sAudioAudioVolumeGroupChangedHandler.unregisterListener(callback);
8045 }
jiabin39940752018-04-02 18:18:45 -07008046
jiabinad225202019-03-20 15:22:50 -07008047 /**
8048 * Return if an asset contains haptic channels or not.
jiabincfcf1032021-07-01 16:30:50 -07008049 *
8050 * @param context the {@link Context} to resolve the uri.
jiabinad225202019-03-20 15:22:50 -07008051 * @param uri the {@link Uri} of the asset.
8052 * @return true if the assert contains haptic channels.
8053 * @hide
8054 */
jiabincfcf1032021-07-01 16:30:50 -07008055 public static boolean hasHapticChannelsImpl(@NonNull Context context, @NonNull Uri uri) {
8056 MediaExtractor extractor = new MediaExtractor();
jiabinad225202019-03-20 15:22:50 -07008057 try {
jiabincfcf1032021-07-01 16:30:50 -07008058 extractor.setDataSource(context, uri, null);
8059 for (int i = 0; i < extractor.getTrackCount(); i++) {
8060 MediaFormat format = extractor.getTrackFormat(i);
8061 if (format.containsKey(MediaFormat.KEY_HAPTIC_CHANNEL_COUNT)
8062 && format.getInteger(MediaFormat.KEY_HAPTIC_CHANNEL_COUNT) > 0) {
8063 return true;
8064 }
8065 }
8066 } catch (IOException e) {
8067 Log.e(TAG, "hasHapticChannels failure:" + e);
8068 }
8069 return false;
8070 }
8071
8072 /**
8073 * Return if an asset contains haptic channels or not.
8074 *
8075 * @param context the {@link Context} to resolve the uri.
8076 * @param uri the {@link Uri} of the asset.
8077 * @return true if the assert contains haptic channels.
8078 * @hide
8079 */
8080 public static boolean hasHapticChannels(@Nullable Context context, @NonNull Uri uri) {
8081 Objects.requireNonNull(uri);
jiabin0f3339c2021-07-09 11:50:07 -07008082
jiabincfcf1032021-07-01 16:30:50 -07008083 if (context != null) {
8084 return hasHapticChannelsImpl(context, uri);
jiabin0f3339c2021-07-09 11:50:07 -07008085 }
8086
8087 Context cachedContext = sContext.get();
8088 if (cachedContext != null) {
jiabincfcf1032021-07-01 16:30:50 -07008089 if (DEBUG) {
8090 Log.d(TAG, "Try to use static context to query if having haptic channels");
8091 }
jiabin0f3339c2021-07-09 11:50:07 -07008092 return hasHapticChannelsImpl(cachedContext, uri);
8093 }
8094
8095 // Try with audio service context, this may fail to get correct result.
8096 if (DEBUG) {
8097 Log.d(TAG, "Try to use audio service context to query if having haptic channels");
8098 }
8099 try {
8100 return getService().hasHapticChannels(uri);
8101 } catch (RemoteException e) {
8102 throw e.rethrowFromSystemServer();
jiabinad225202019-03-20 15:22:50 -07008103 }
8104 }
8105
Kohsuke Yatoh900e1f12020-03-25 08:05:49 -07008106 /**
8107 * Set whether or not there is an active RTT call.
8108 * This method should be called by Telecom service.
8109 * @hide
8110 * TODO: make this a @SystemApi
8111 */
8112 public static void setRttEnabled(boolean rttEnabled) {
8113 try {
8114 getService().setRttEnabled(rttEnabled);
8115 } catch (RemoteException e) {
8116 throw e.rethrowFromSystemServer();
8117 }
8118 }
8119
Jin Seok Park16aeba382020-08-06 12:52:54 +09008120 /**
8121 * Adjusts the volume of the most relevant stream, or the given fallback
8122 * stream.
8123 * <p>
8124 * This method should only be used by applications that replace the
8125 * platform-wide management of audio settings or the main telephony
8126 * application.
8127 * <p>
8128 * This method has no effect if the device implements a fixed volume policy
8129 * as indicated by {@link #isVolumeFixed()}.
8130 * <p>This API checks if the caller has the necessary permissions based on the provided
8131 * component name, uid, and pid values.
8132 * See {@link #adjustSuggestedStreamVolume(int, int, int)}.
8133 *
8134 * @param suggestedStreamType The stream type that will be used if there
8135 * isn't a relevant stream. {@link #USE_DEFAULT_STREAM_TYPE} is
8136 * valid here.
8137 * @param direction The direction to adjust the volume. One of
8138 * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE},
8139 * {@link #ADJUST_SAME}, {@link #ADJUST_MUTE},
8140 * {@link #ADJUST_UNMUTE}, or {@link #ADJUST_TOGGLE_MUTE}.
8141 * @param flags One or more flags.
8142 * @param packageName the package name of client application
8143 * @param uid the uid of client application
8144 * @param pid the pid of client application
8145 * @param targetSdkVersion the target sdk version of client application
8146 * @see #adjustVolume(int, int)
8147 * @see #adjustStreamVolume(int, int, int)
8148 * @see #setStreamVolume(int, int, int)
8149 * @see #isVolumeFixed()
8150 *
8151 * @hide
8152 */
8153 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
8154 public void adjustSuggestedStreamVolumeForUid(int suggestedStreamType, int direction, int flags,
8155 @NonNull String packageName, int uid, int pid, int targetSdkVersion) {
8156 try {
8157 getService().adjustSuggestedStreamVolumeForUid(suggestedStreamType, direction, flags,
8158 packageName, uid, pid, UserHandle.getUserHandleForUid(uid), targetSdkVersion);
8159 } catch (RemoteException e) {
8160 throw e.rethrowFromSystemServer();
8161 }
8162 }
8163
8164 /**
8165 * Adjusts the volume of a particular stream by one step in a direction.
8166 * <p>
8167 * This method should only be used by applications that replace the platform-wide
8168 * management of audio settings or the main telephony application.
8169 * <p>This method has no effect if the device implements a fixed volume policy
8170 * as indicated by {@link #isVolumeFixed()}.
8171 * <p>From N onward, ringer mode adjustments that would toggle Do Not Disturb are not allowed
8172 * unless the app has been granted Do Not Disturb Access.
8173 * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
8174 * <p>This API checks if the caller has the necessary permissions based on the provided
8175 * component name, uid, and pid values.
8176 * See {@link #adjustStreamVolume(int, int, int)}.
8177 *
8178 * @param streamType The stream type to adjust. One of {@link #STREAM_VOICE_CALL},
8179 * {@link #STREAM_SYSTEM}, {@link #STREAM_RING}, {@link #STREAM_MUSIC},
8180 * {@link #STREAM_ALARM} or {@link #STREAM_ACCESSIBILITY}.
8181 * @param direction The direction to adjust the volume. One of
8182 * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
8183 * {@link #ADJUST_SAME}.
8184 * @param flags One or more flags.
8185 * @param packageName the package name of client application
8186 * @param uid the uid of client application
8187 * @param pid the pid of client application
8188 * @param targetSdkVersion the target sdk version of client application
8189 * @see #adjustVolume(int, int)
8190 * @see #setStreamVolume(int, int, int)
8191 * @throws SecurityException if the adjustment triggers a Do Not Disturb change
8192 * and the caller is not granted notification policy access.
8193 *
8194 * @hide
8195 */
8196 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
8197 public void adjustStreamVolumeForUid(int streamType, int direction, int flags,
8198 @NonNull String packageName, int uid, int pid, int targetSdkVersion) {
8199 try {
8200 getService().adjustStreamVolumeForUid(streamType, direction, flags, packageName, uid,
8201 pid, UserHandle.getUserHandleForUid(uid), targetSdkVersion);
8202 } catch (RemoteException e) {
8203 throw e.rethrowFromSystemServer();
8204 }
8205 }
8206
8207 /**
8208 * Sets the volume index for a particular stream.
8209 * <p>This method has no effect if the device implements a fixed volume policy
8210 * as indicated by {@link #isVolumeFixed()}.
8211 * <p>From N onward, volume adjustments that would toggle Do Not Disturb are not allowed unless
8212 * the app has been granted Do Not Disturb Access.
8213 * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
8214 * <p>This API checks if the caller has the necessary permissions based on the provided
8215 * component name, uid, and pid values.
8216 * See {@link #setStreamVolume(int, int, int)}.
8217 *
8218 * @param streamType The stream whose volume index should be set.
8219 * @param index The volume index to set. See
8220 * {@link #getStreamMaxVolume(int)} for the largest valid value.
8221 * @param flags One or more flags.
8222 * @param packageName the package name of client application
8223 * @param uid the uid of client application
8224 * @param pid the pid of client application
8225 * @param targetSdkVersion the target sdk version of client application
8226 * @see #getStreamMaxVolume(int)
8227 * @see #getStreamVolume(int)
8228 * @see #isVolumeFixed()
8229 * @throws SecurityException if the volume change triggers a Do Not Disturb change
8230 * and the caller is not granted notification policy access.
8231 *
8232 * @hide
8233 */
8234 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
8235 public void setStreamVolumeForUid(int streamType, int index, int flags,
8236 @NonNull String packageName, int uid, int pid, int targetSdkVersion) {
8237 try {
8238 getService().setStreamVolumeForUid(streamType, index, flags, packageName, uid, pid,
8239 UserHandle.getUserHandleForUid(uid), targetSdkVersion);
8240 } catch (RemoteException e) {
8241 throw e.rethrowFromSystemServer();
8242 }
8243 }
8244
8245
hjin81.lee4e984e52019-12-05 14:34:52 +09008246 /** @hide
8247 * TODO: make this a @SystemApi */
Jean-Michel Trivic4557822023-01-23 18:19:52 +00008248 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
hjin81.lee4e984e52019-12-05 14:34:52 +09008249 public void setMultiAudioFocusEnabled(boolean enabled) {
8250 try {
8251 getService().setMultiAudioFocusEnabled(enabled);
8252 } catch (RemoteException e) {
8253 throw e.rethrowFromSystemServer();
8254 }
8255 }
8256
Eric Laurent43a78de2020-07-24 17:11:15 -07008257
8258 /**
8259 * Retrieves the Hardware A/V synchronization ID corresponding to the given audio session ID.
8260 * For more details on Hardware A/V synchronization please refer to
8261 * <a href="https://source.android.com/devices/tv/multimedia-tunneling/">
8262 * media tunneling documentation</a>.
8263 * @param sessionId the audio session ID for which the HW A/V sync ID is retrieved.
8264 * @return the HW A/V sync ID for this audio session (an integer different from 0).
8265 * @throws UnsupportedOperationException if HW A/V synchronization is not supported.
8266 */
8267 public int getAudioHwSyncForSession(int sessionId) {
8268 int hwSyncId = AudioSystem.getAudioHwSyncForSession(sessionId);
8269 if (hwSyncId == AudioSystem.AUDIO_HW_SYNC_INVALID) {
8270 throw new UnsupportedOperationException("HW A/V synchronization is not supported.");
8271 }
8272 return hwSyncId;
8273 }
8274
Eric Laurentb36d4a12020-10-09 09:52:49 -07008275 /**
8276 * Selects the audio device that should be used for communication use cases, for instance voice
8277 * or video calls. This method can be used by voice or video chat applications to select a
8278 * different audio device than the one selected by default by the platform.
Eric Laurent7412f572021-02-11 15:10:31 +01008279 * <p>The device selection is expressed as an {@link AudioDeviceInfo} among devices returned by
Eric Laurent6aa23612022-11-18 16:08:20 +01008280 * {@link #getAvailableCommunicationDevices()}. Note that only devices in a sink role
8281 * (AKA output devices, see {@link AudioDeviceInfo#isSink()}) can be specified. The matching
8282 * source device is selected automatically by the platform.
8283 * <p>The selection is active as long as the requesting application process lives, until
Eric Laurent7412f572021-02-11 15:10:31 +01008284 * {@link #clearCommunicationDevice} is called or until the device is disconnected.
Eric Laurentb36d4a12020-10-09 09:52:49 -07008285 * It is therefore important for applications to clear the request when a call ends or the
Eric Laurent7412f572021-02-11 15:10:31 +01008286 * the requesting activity or service is stopped or destroyed.
Eric Laurentb36d4a12020-10-09 09:52:49 -07008287 * <p>In case of simultaneous requests by multiple applications the priority is given to the
8288 * application currently controlling the audio mode (see {@link #setMode(int)}). This is the
8289 * latest application having selected mode {@link #MODE_IN_COMMUNICATION} or mode
8290 * {@link #MODE_IN_CALL}. Note that <code>MODE_IN_CALL</code> can only be selected by the main
8291 * telephony application with permission
Jean-Michel Trivic4557822023-01-23 18:19:52 +00008292 * {@link Manifest.permission#MODIFY_PHONE_STATE}.
Eric Laurentb36d4a12020-10-09 09:52:49 -07008293 * <p> If the requested devices is not currently available, the request will be rejected and
8294 * the method will return false.
8295 * <p>This API replaces the following deprecated APIs:
8296 * <ul>
8297 * <li> {@link #startBluetoothSco()}
8298 * <li> {@link #stopBluetoothSco()}
8299 * <li> {@link #setSpeakerphoneOn(boolean)}
8300 * </ul>
8301 * <h4>Example</h4>
8302 * <p>The example below shows how to enable and disable speakerphone mode.
8303 * <pre class="prettyprint">
8304 * // Get an AudioManager instance
8305 * AudioManager audioManager = Context.getSystemService(AudioManager.class);
Eric Laurent9a404482021-03-09 19:58:39 +01008306 * AudioDeviceInfo speakerDevice = null;
8307 * List<AudioDeviceInfo> devices = audioManager.getAvailableCommunicationDevices();
8308 * for (AudioDeviceInfo device : devices) {
8309 * if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
8310 * speakerDevice = device;
8311 * break;
Eric Laurentb36d4a12020-10-09 09:52:49 -07008312 * }
Eric Laurent9a404482021-03-09 19:58:39 +01008313 * }
8314 * if (speakerDevice != null) {
8315 * // Turn speakerphone ON.
8316 * boolean result = audioManager.setCommunicationDevice(speakerDevice);
8317 * if (!result) {
8318 * // Handle error.
Eric Laurentb36d4a12020-10-09 09:52:49 -07008319 * }
Eric Laurent9a404482021-03-09 19:58:39 +01008320 * // Turn speakerphone OFF.
8321 * audioManager.clearCommunicationDevice();
Eric Laurentb36d4a12020-10-09 09:52:49 -07008322 * }
8323 * </pre>
8324 * @param device the requested audio device.
8325 * @return <code>true</code> if the request was accepted, <code>false</code> otherwise.
8326 * @throws IllegalArgumentException If an invalid device is specified.
8327 */
Eric Laurent7412f572021-02-11 15:10:31 +01008328 public boolean setCommunicationDevice(@NonNull AudioDeviceInfo device) {
Eric Laurentb36d4a12020-10-09 09:52:49 -07008329 Objects.requireNonNull(device);
8330 try {
8331 if (device.getId() == 0) {
8332 throw new IllegalArgumentException("In valid device: " + device);
8333 }
Eric Laurent7412f572021-02-11 15:10:31 +01008334 return getService().setCommunicationDevice(mICallBack, device.getId());
Eric Laurentb36d4a12020-10-09 09:52:49 -07008335 } catch (RemoteException e) {
8336 throw e.rethrowFromSystemServer();
8337 }
8338 }
8339
8340 /**
8341 * Cancels previous communication device selection made with
Eric Laurent7412f572021-02-11 15:10:31 +01008342 * {@link #setCommunicationDevice(AudioDeviceInfo)}.
Eric Laurentb36d4a12020-10-09 09:52:49 -07008343 */
Eric Laurent7412f572021-02-11 15:10:31 +01008344 public void clearCommunicationDevice() {
Eric Laurentb36d4a12020-10-09 09:52:49 -07008345 try {
Eric Laurent7412f572021-02-11 15:10:31 +01008346 getService().setCommunicationDevice(mICallBack, 0);
Eric Laurentb36d4a12020-10-09 09:52:49 -07008347 } catch (RemoteException e) {
8348 throw e.rethrowFromSystemServer();
8349 }
8350 }
8351
8352 /**
8353 * Returns currently selected audio device for communication.
8354 * <p>This API replaces the following deprecated APIs:
8355 * <ul>
8356 * <li> {@link #isBluetoothScoOn()}
8357 * <li> {@link #isSpeakerphoneOn()}
8358 * </ul>
8359 * @return an {@link AudioDeviceInfo} indicating which audio device is
Eric Laurent7412f572021-02-11 15:10:31 +01008360 * currently selected for communication use cases. Can be null on platforms
8361 * not supporting {@link android.content.pm.PackageManager#FEATURE_TELEPHONY}.
Eric Laurentb36d4a12020-10-09 09:52:49 -07008362 * is used.
8363 */
8364 @Nullable
Eric Laurent7412f572021-02-11 15:10:31 +01008365 public AudioDeviceInfo getCommunicationDevice() {
Eric Laurentb36d4a12020-10-09 09:52:49 -07008366 try {
8367 return getDeviceForPortId(
Eric Laurent7412f572021-02-11 15:10:31 +01008368 getService().getCommunicationDevice(), GET_DEVICES_OUTPUTS);
8369 } catch (RemoteException e) {
8370 throw e.rethrowFromSystemServer();
8371 }
8372 }
8373
8374 /**
8375 * Returns a list of audio devices that can be selected for communication use cases via
8376 * {@link #setCommunicationDevice(AudioDeviceInfo)}.
8377 * @return a list of {@link AudioDeviceInfo} suitable for use with setCommunicationDevice().
8378 */
8379 @NonNull
8380 public List<AudioDeviceInfo> getAvailableCommunicationDevices() {
8381 try {
8382 ArrayList<AudioDeviceInfo> devices = new ArrayList<>();
8383 int[] portIds = getService().getAvailableCommunicationDeviceIds();
8384 for (int portId : portIds) {
8385 AudioDeviceInfo device = getDeviceForPortId(portId, GET_DEVICES_OUTPUTS);
8386 if (device == null) {
8387 continue;
8388 }
8389 devices.add(device);
8390 }
8391 return devices;
Eric Laurentb36d4a12020-10-09 09:52:49 -07008392 } catch (RemoteException e) {
8393 throw e.rethrowFromSystemServer();
8394 }
8395 }
8396
8397 /**
Dorin Drimuseb9bf642022-01-03 12:05:37 +01008398 * Returns a list of direct {@link AudioProfile} that are supported for the specified
8399 * {@link AudioAttributes}. This can be empty in case of an error or if no direct playback
8400 * is possible.
8401 *
8402 * <p>Direct playback means that the audio stream is not resampled or downmixed
8403 * by the framework. Checking for direct support can help the app select the representation
8404 * of audio content that most closely matches the capabilities of the device and peripherals
8405 * (e.g. A/V receiver) connected to it. Note that the provided stream can still be re-encoded
8406 * or mixed with other streams, if needed.
8407 * <p>When using this information to inform your application which audio format to play,
8408 * query again whenever audio output devices change (see {@link AudioDeviceCallback}).
8409 * @param attributes a non-null {@link AudioAttributes} instance.
8410 * @return a list of {@link AudioProfile}
8411 */
8412 @NonNull
8413 public List<AudioProfile> getDirectProfilesForAttributes(@NonNull AudioAttributes attributes) {
8414 Objects.requireNonNull(attributes);
8415 ArrayList<AudioProfile> audioProfilesList = new ArrayList<>();
8416 int status = AudioSystem.getDirectProfilesForAttributes(attributes, audioProfilesList);
8417 if (status != SUCCESS) {
8418 Log.w(TAG, "getDirectProfilesForAttributes failed.");
8419 return new ArrayList<>();
8420 }
8421 return audioProfilesList;
8422 }
8423
8424 /**
Eric Laurentb36d4a12020-10-09 09:52:49 -07008425 * @hide
8426 * Returns an {@link AudioDeviceInfo} corresponding to a connected device of the type provided.
8427 * The type must be a valid output type defined in <code>AudioDeviceInfo</code> class,
8428 * for instance {@link AudioDeviceInfo#TYPE_BUILTIN_SPEAKER}.
8429 * The method will return null if no device of the provided type is connected.
8430 * If more than one device of the provided type is connected, an object corresponding to the
8431 * first device encountered in the enumeration list will be returned.
8432 * @param deviceType The device device for which an <code>AudioDeviceInfo</code>
Eric Laurent7412f572021-02-11 15:10:31 +01008433 * object is queried.
Eric Laurentb36d4a12020-10-09 09:52:49 -07008434 * @return An AudioDeviceInfo object or null if no device with the requested type is connected.
8435 * @throws IllegalArgumentException If an invalid device type is specified.
8436 */
8437 @TestApi
8438 @Nullable
8439 public static AudioDeviceInfo getDeviceInfoFromType(
8440 @AudioDeviceInfo.AudioDeviceTypeOut int deviceType) {
Eric Laurent7412f572021-02-11 15:10:31 +01008441 return getDeviceInfoFromTypeAndAddress(deviceType, null);
8442 }
8443
Eric Laurent78eef3a2021-11-09 16:10:42 +01008444 /**
Eric Laurent7412f572021-02-11 15:10:31 +01008445 * @hide
8446 * Returns an {@link AudioDeviceInfo} corresponding to a connected device of the type and
8447 * address provided.
8448 * The type must be a valid output type defined in <code>AudioDeviceInfo</code> class,
8449 * for instance {@link AudioDeviceInfo#TYPE_BUILTIN_SPEAKER}.
8450 * If a null address is provided, the matching will happen on the type only.
8451 * The method will return null if no device of the provided type and address is connected.
8452 * If more than one device of the provided type is connected, an object corresponding to the
8453 * first device encountered in the enumeration list will be returned.
8454 * @param type The device device for which an <code>AudioDeviceInfo</code>
8455 * object is queried.
8456 * @param address The device address for which an <code>AudioDeviceInfo</code>
8457 * object is queried or null if requesting match on type only.
8458 * @return An AudioDeviceInfo object or null if no matching device is connected.
8459 * @throws IllegalArgumentException If an invalid device type is specified.
8460 */
8461 @Nullable
8462 public static AudioDeviceInfo getDeviceInfoFromTypeAndAddress(
8463 @AudioDeviceInfo.AudioDeviceTypeOut int type, @Nullable String address) {
Eric Laurentb36d4a12020-10-09 09:52:49 -07008464 AudioDeviceInfo[] devices = getDevicesStatic(GET_DEVICES_OUTPUTS);
Eric Laurent7412f572021-02-11 15:10:31 +01008465 AudioDeviceInfo deviceForType = null;
Eric Laurentb36d4a12020-10-09 09:52:49 -07008466 for (AudioDeviceInfo device : devices) {
Eric Laurent7412f572021-02-11 15:10:31 +01008467 if (device.getType() == type) {
8468 deviceForType = device;
8469 if (address == null || address.equals(device.getAddress())) {
8470 return device;
8471 }
Eric Laurentb36d4a12020-10-09 09:52:49 -07008472 }
8473 }
Eric Laurent7412f572021-02-11 15:10:31 +01008474 return deviceForType;
Eric Laurentb36d4a12020-10-09 09:52:49 -07008475 }
8476
8477 /**
8478 * Listener registered by client to be notified upon communication audio device change.
Eric Laurent7412f572021-02-11 15:10:31 +01008479 * See {@link #setCommunicationDevice(AudioDeviceInfo)}.
Eric Laurentb36d4a12020-10-09 09:52:49 -07008480 */
8481 public interface OnCommunicationDeviceChangedListener {
8482 /**
8483 * Callback method called upon communication audio device change.
Eric Laurent7412f572021-02-11 15:10:31 +01008484 * @param device the audio device requested for communication use cases.
8485 * Can be null on platforms not supporting
8486 * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY}.
Eric Laurentb36d4a12020-10-09 09:52:49 -07008487 */
8488 void onCommunicationDeviceChanged(@Nullable AudioDeviceInfo device);
8489 }
8490
8491 /**
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08008492 * manages the OnCommunicationDeviceChangedListener listeners and the
8493 * CommunicationDeviceDispatcherStub
8494 */
8495 private final CallbackUtil.LazyListenerManager<OnCommunicationDeviceChangedListener>
8496 mCommDeviceChangedListenerMgr = new CallbackUtil.LazyListenerManager();
8497 /**
Eric Laurentb36d4a12020-10-09 09:52:49 -07008498 * Adds a listener for being notified of changes to the communication audio device.
Eric Laurent7412f572021-02-11 15:10:31 +01008499 * See {@link #setCommunicationDevice(AudioDeviceInfo)}.
Eric Laurentb36d4a12020-10-09 09:52:49 -07008500 * @param executor
8501 * @param listener
8502 */
8503 public void addOnCommunicationDeviceChangedListener(
8504 @NonNull @CallbackExecutor Executor executor,
8505 @NonNull OnCommunicationDeviceChangedListener listener) {
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08008506 mCommDeviceChangedListenerMgr.addListener(
8507 executor, listener, "addOnCommunicationDeviceChangedListener",
8508 () -> new CommunicationDeviceDispatcherStub());
Eric Laurentb36d4a12020-10-09 09:52:49 -07008509 }
8510
8511 /**
8512 * Removes a previously added listener of changes to the communication audio device.
Eric Laurent7412f572021-02-11 15:10:31 +01008513 * See {@link #setCommunicationDevice(AudioDeviceInfo)}.
Eric Laurentb36d4a12020-10-09 09:52:49 -07008514 * @param listener
8515 */
8516 public void removeOnCommunicationDeviceChangedListener(
8517 @NonNull OnCommunicationDeviceChangedListener listener) {
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08008518 mCommDeviceChangedListenerMgr.removeListener(listener,
8519 "removeOnCommunicationDeviceChangedListener");
Eric Laurentb36d4a12020-10-09 09:52:49 -07008520 }
8521
Eric Laurentb36d4a12020-10-09 09:52:49 -07008522 private final class CommunicationDeviceDispatcherStub
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08008523 extends ICommunicationDeviceDispatcher.Stub implements CallbackUtil.DispatcherStub {
Eric Laurentb36d4a12020-10-09 09:52:49 -07008524
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08008525 @Override
Jean-Michel Trivi91f0baa2021-11-11 16:31:32 -08008526 public void register(boolean register) {
Eric Laurentb36d4a12020-10-09 09:52:49 -07008527 try {
Jean-Michel Trivi91f0baa2021-11-11 16:31:32 -08008528 if (register) {
8529 getService().registerCommunicationDeviceDispatcher(this);
8530 } else {
8531 getService().unregisterCommunicationDeviceDispatcher(this);
Eric Laurentb36d4a12020-10-09 09:52:49 -07008532 }
Jean-Michel Trivi91f0baa2021-11-11 16:31:32 -08008533 } catch (RemoteException e) {
8534 e.rethrowFromSystemServer();
Eric Laurentb36d4a12020-10-09 09:52:49 -07008535 }
8536 }
Eric Laurentb36d4a12020-10-09 09:52:49 -07008537
Jean-Michel Trivi91f0baa2021-11-11 16:31:32 -08008538 @Override
Jean-Michel Trivi91f0baa2021-11-11 16:31:32 -08008539 public void dispatchCommunicationDeviceChanged(int portId) {
8540 AudioDeviceInfo device = getDeviceForPortId(portId, GET_DEVICES_OUTPUTS);
Jean-Michel Trivi54d129f2021-12-30 17:43:47 -08008541 mCommDeviceChangedListenerMgr.callListeners(
Jean-Michel Trivi91f0baa2021-11-11 16:31:32 -08008542 (listener) -> listener.onCommunicationDeviceChanged(device));
Eric Laurentb36d4a12020-10-09 09:52:49 -07008543 }
Eric Laurentb36d4a12020-10-09 09:52:49 -07008544 }
8545
Eric Laurent78eef3a2021-11-09 16:10:42 +01008546
8547 /**
8548 * @hide
8549 * Indicates if the platform allows accessing the uplink and downlink audio of an ongoing
8550 * PSTN call.
8551 * When true, {@link getCallUplinkInjectionAudioTrack(AudioFormat)} can be used to obtain
8552 * an AudioTrack for call uplink audio injection and
8553 * {@link getCallDownlinkExtractionAudioRecord(AudioFormat)} can be used to obtain
8554 * an AudioRecord for call downlink audio extraction.
8555 * @return true if PSTN call audio is accessible, false otherwise.
8556 */
8557 @TestApi
8558 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00008559 @RequiresPermission(Manifest.permission.CALL_AUDIO_INTERCEPTION)
Eric Laurent78eef3a2021-11-09 16:10:42 +01008560 public boolean isPstnCallAudioInterceptable() {
8561 final IAudioService service = getService();
8562 try {
8563 return service.isPstnCallAudioInterceptable();
8564 } catch (RemoteException e) {
8565 throw e.rethrowFromSystemServer();
8566 }
8567 }
8568
8569 /** @hide */
8570 @IntDef(flag = false, prefix = "CALL_REDIRECT_", value = {
8571 CALL_REDIRECT_NONE,
8572 CALL_REDIRECT_PSTN,
8573 CALL_REDIRECT_VOIP }
8574 )
8575 @Retention(RetentionPolicy.SOURCE)
8576 public @interface CallRedirectionMode {}
8577
8578 /**
8579 * Not used for call redirection
8580 * @hide
8581 */
8582 public static final int CALL_REDIRECT_NONE = 0;
8583 /**
8584 * Used to redirect PSTN call
8585 * @hide
8586 */
8587 public static final int CALL_REDIRECT_PSTN = 1;
8588 /**
8589 * Used to redirect VoIP call
8590 * @hide
8591 */
8592 public static final int CALL_REDIRECT_VOIP = 2;
8593
8594
8595 private @CallRedirectionMode int getCallRedirectMode() {
8596 int mode = getMode();
8597 if (mode == MODE_IN_CALL || mode == MODE_CALL_SCREENING
8598 || mode == MODE_CALL_REDIRECT) {
8599 return CALL_REDIRECT_PSTN;
8600 } else if (mode == MODE_IN_COMMUNICATION || mode == MODE_COMMUNICATION_REDIRECT) {
8601 return CALL_REDIRECT_VOIP;
8602 }
8603 return CALL_REDIRECT_NONE;
8604 }
8605
8606 private void checkCallRedirectionFormat(AudioFormat format, boolean isOutput) {
8607 if (format.getEncoding() != AudioFormat.ENCODING_PCM_16BIT
8608 && format.getEncoding() != AudioFormat.ENCODING_PCM_FLOAT) {
8609 throw new UnsupportedOperationException(" Unsupported encoding ");
8610 }
8611 if (format.getSampleRate() < 8000
8612 || format.getSampleRate() > 48000) {
8613 throw new UnsupportedOperationException(" Unsupported sample rate ");
8614 }
8615 if (isOutput && format.getChannelMask() != AudioFormat.CHANNEL_OUT_MONO
8616 && format.getChannelMask() != AudioFormat.CHANNEL_OUT_STEREO) {
8617 throw new UnsupportedOperationException(" Unsupported output channel mask ");
8618 }
8619 if (!isOutput && format.getChannelMask() != AudioFormat.CHANNEL_IN_MONO
8620 && format.getChannelMask() != AudioFormat.CHANNEL_IN_STEREO) {
8621 throw new UnsupportedOperationException(" Unsupported input channel mask ");
8622 }
8623 }
8624
8625 class CallIRedirectionClientInfo {
8626 public WeakReference trackOrRecord;
8627 public int redirectMode;
8628 }
8629
8630 private Object mCallRedirectionLock = new Object();
8631 @GuardedBy("mCallRedirectionLock")
8632 private CallInjectionModeChangedListener mCallRedirectionModeListener;
8633 @GuardedBy("mCallRedirectionLock")
8634 private ArrayList<CallIRedirectionClientInfo> mCallIRedirectionClients;
8635
8636 /**
8637 * @hide
8638 * Returns an AudioTrack that can be used to inject audio to an active call uplink.
8639 * This can be used for functions like call screening or call audio redirection and is reserved
8640 * to system apps with privileged permission.
8641 * @param format the desired audio format for audio playback.
8642 * p>Formats accepted are:
8643 * <ul>
8644 * <li><em>Sampling rate</em> - 8kHz to 48kHz. </li>
8645 * <li><em>Channel mask</em> - Mono or Stereo </li>
8646 * <li><em>Sample format</em> - PCM 16 bit or FLOAT 32 bit </li>
8647 * </ul>
8648 *
8649 * @return The AudioTrack used for audio injection
8650 * @throws NullPointerException if AudioFormat argument is null.
8651 * @throws UnsupportedOperationException if on unsupported AudioFormat is specified.
8652 * @throws IllegalArgumentException if an invalid AudioFormat is specified.
8653 * @throws SecurityException if permission CALL_AUDIO_INTERCEPTION is missing .
8654 * @throws IllegalStateException if current audio mode is not MODE_IN_CALL,
8655 * MODE_IN_COMMUNICATION, MODE_CALL_SCREENING, MODE_CALL_REDIRECT
8656 * or MODE_COMMUNICATION_REDIRECT.
8657 */
8658 @TestApi
8659 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00008660 @RequiresPermission(Manifest.permission.CALL_AUDIO_INTERCEPTION)
Eric Laurent78eef3a2021-11-09 16:10:42 +01008661 public @NonNull AudioTrack getCallUplinkInjectionAudioTrack(@NonNull AudioFormat format) {
8662 Objects.requireNonNull(format);
8663 checkCallRedirectionFormat(format, true /* isOutput */);
8664
8665 AudioTrack track = null;
8666 int redirectMode = getCallRedirectMode();
8667 if (redirectMode == CALL_REDIRECT_NONE) {
8668 throw new IllegalStateException(
8669 " not available in mode " + AudioSystem.modeToString(getMode()));
8670 } else if (redirectMode == CALL_REDIRECT_PSTN && !isPstnCallAudioInterceptable()) {
8671 throw new UnsupportedOperationException(" PSTN Call audio not accessible ");
8672 }
8673
8674 track = new AudioTrack.Builder()
8675 .setAudioAttributes(new AudioAttributes.Builder()
8676 .setSystemUsage(AudioAttributes.USAGE_CALL_ASSISTANT)
8677 .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
8678 .build())
8679 .setAudioFormat(format)
8680 .setCallRedirectionMode(redirectMode)
8681 .build();
8682
8683 if (track != null && track.getState() != AudioTrack.STATE_UNINITIALIZED) {
8684 synchronized (mCallRedirectionLock) {
8685 if (mCallRedirectionModeListener == null) {
8686 mCallRedirectionModeListener = new CallInjectionModeChangedListener();
8687 try {
8688 addOnModeChangedListener(
8689 Executors.newSingleThreadExecutor(), mCallRedirectionModeListener);
8690 } catch (Exception e) {
8691 Log.e(TAG, "addOnModeChangedListener failed with exception: " + e);
8692 mCallRedirectionModeListener = null;
8693 throw new UnsupportedOperationException(" Cannot register mode listener ");
8694 }
8695 mCallIRedirectionClients = new ArrayList<CallIRedirectionClientInfo>();
8696 }
8697 CallIRedirectionClientInfo info = new CallIRedirectionClientInfo();
8698 info.redirectMode = redirectMode;
8699 info.trackOrRecord = new WeakReference<AudioTrack>(track);
8700 mCallIRedirectionClients.add(info);
8701 }
8702 } else {
8703 throw new UnsupportedOperationException(" Cannot create the AudioTrack");
8704 }
8705 return track;
8706 }
8707
8708 /**
8709 * @hide
8710 * Returns an AudioRecord that can be used to extract audio from an active call downlink.
8711 * This can be used for functions like call screening or call audio redirection and is reserved
8712 * to system apps with privileged permission.
8713 * @param format the desired audio format for audio capture.
8714 *<p>Formats accepted are:
8715 * <ul>
8716 * <li><em>Sampling rate</em> - 8kHz to 48kHz. </li>
8717 * <li><em>Channel mask</em> - Mono or Stereo </li>
8718 * <li><em>Sample format</em> - PCM 16 bit or FLOAT 32 bit </li>
8719 * </ul>
8720 *
8721 * @return The AudioRecord used for audio extraction
8722 * @throws UnsupportedOperationException if on unsupported AudioFormat is specified.
8723 * @throws IllegalArgumentException if an invalid AudioFormat is specified.
8724 * @throws NullPointerException if AudioFormat argument is null.
8725 * @throws SecurityException if permission CALL_AUDIO_INTERCEPTION is missing .
8726 * @throws IllegalStateException if current audio mode is not MODE_IN_CALL,
8727 * MODE_IN_COMMUNICATION, MODE_CALL_SCREENING, MODE_CALL_REDIRECT
8728 * or MODE_COMMUNICATION_REDIRECT.
8729 */
8730 @TestApi
8731 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00008732 @RequiresPermission(Manifest.permission.CALL_AUDIO_INTERCEPTION)
Eric Laurent78eef3a2021-11-09 16:10:42 +01008733 public @NonNull AudioRecord getCallDownlinkExtractionAudioRecord(@NonNull AudioFormat format) {
8734 Objects.requireNonNull(format);
8735 checkCallRedirectionFormat(format, false /* isOutput */);
8736
8737 AudioRecord record = null;
8738 int redirectMode = getCallRedirectMode();
8739 if (redirectMode == CALL_REDIRECT_NONE) {
8740 throw new IllegalStateException(
8741 " not available in mode " + AudioSystem.modeToString(getMode()));
8742 } else if (redirectMode == CALL_REDIRECT_PSTN && !isPstnCallAudioInterceptable()) {
8743 throw new UnsupportedOperationException(" PSTN Call audio not accessible ");
8744 }
8745
8746 record = new AudioRecord.Builder()
8747 .setAudioAttributes(new AudioAttributes.Builder()
8748 .setInternalCapturePreset(MediaRecorder.AudioSource.VOICE_DOWNLINK)
8749 .build())
8750 .setAudioFormat(format)
8751 .setCallRedirectionMode(redirectMode)
8752 .build();
8753
8754 if (record != null && record.getState() != AudioRecord.STATE_UNINITIALIZED) {
8755 synchronized (mCallRedirectionLock) {
8756 if (mCallRedirectionModeListener == null) {
8757 mCallRedirectionModeListener = new CallInjectionModeChangedListener();
8758 try {
8759 addOnModeChangedListener(
8760 Executors.newSingleThreadExecutor(), mCallRedirectionModeListener);
8761 } catch (Exception e) {
8762 Log.e(TAG, "addOnModeChangedListener failed with exception: " + e);
8763 mCallRedirectionModeListener = null;
8764 throw new UnsupportedOperationException(" Cannot register mode listener ");
8765 }
8766 mCallIRedirectionClients = new ArrayList<CallIRedirectionClientInfo>();
8767 }
8768 CallIRedirectionClientInfo info = new CallIRedirectionClientInfo();
8769 info.redirectMode = redirectMode;
8770 info.trackOrRecord = new WeakReference<AudioRecord>(record);
8771 mCallIRedirectionClients.add(info);
8772 }
8773 } else {
8774 throw new UnsupportedOperationException(" Cannot create the AudioRecord");
8775 }
8776 return record;
8777 }
8778
8779 class CallInjectionModeChangedListener implements OnModeChangedListener {
8780 @Override
8781 public void onModeChanged(@AudioMode int mode) {
8782 synchronized (mCallRedirectionLock) {
8783 final ArrayList<CallIRedirectionClientInfo> clientInfos =
8784 (ArrayList<CallIRedirectionClientInfo>) mCallIRedirectionClients.clone();
8785 for (CallIRedirectionClientInfo info : clientInfos) {
8786 Object trackOrRecord = info.trackOrRecord.get();
8787 if (trackOrRecord != null) {
8788 if ((info.redirectMode == CALL_REDIRECT_PSTN
8789 && mode != MODE_IN_CALL && mode != MODE_CALL_SCREENING
8790 && mode != MODE_CALL_REDIRECT)
8791 || (info.redirectMode == CALL_REDIRECT_VOIP
8792 && mode != MODE_IN_COMMUNICATION
8793 && mode != MODE_COMMUNICATION_REDIRECT)) {
8794 if (trackOrRecord instanceof AudioTrack) {
8795 AudioTrack track = (AudioTrack) trackOrRecord;
8796 track.release();
8797 } else {
8798 AudioRecord record = (AudioRecord) trackOrRecord;
8799 record.release();
8800 }
8801 mCallIRedirectionClients.remove(info);
8802 }
8803 }
8804 }
8805 if (mCallIRedirectionClients.isEmpty()) {
8806 try {
8807 if (mCallRedirectionModeListener != null) {
8808 removeOnModeChangedListener(mCallRedirectionModeListener);
8809 }
8810 } catch (Exception e) {
8811 Log.e(TAG, "removeOnModeChangedListener failed with exception: " + e);
8812 } finally {
8813 mCallRedirectionModeListener = null;
8814 mCallIRedirectionClients = null;
8815 }
8816 }
8817 }
8818 }
8819 }
8820
Paul McLeane3383cc2015-05-08 11:41:20 -07008821 //---------------------------------------------------------
Jean-Michel Trivi933bf142021-11-19 16:18:52 -08008822 // audio device connection-dependent muting
8823 /**
8824 * @hide
8825 * Mute a set of playback use cases until a given audio device is connected.
8826 * Automatically unmute upon connection of the device, or after the given timeout, whichever
8827 * happens first.
8828 * @param usagesToMute non-empty array of {@link AudioAttributes} usages (for example
8829 * {@link AudioAttributes#USAGE_MEDIA}) to mute until the
8830 * device connects
8831 * @param device the audio device expected to connect within the timeout duration
8832 * @param timeout the maximum amount of time to wait for the device connection
8833 * @param timeUnit the unit for the timeout
8834 * @throws IllegalStateException when trying to issue the command while another is already in
8835 * progress and hasn't been cancelled by
8836 * {@link #cancelMuteAwaitConnection(AudioDeviceAttributes)}. See
8837 * {@link #getMutingExpectedDevice()} to check if a muting command is active.
8838 * @see #registerMuteAwaitConnectionCallback(Executor, AudioManager.MuteAwaitConnectionCallback)
8839 */
8840 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00008841 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Jean-Michel Trivi933bf142021-11-19 16:18:52 -08008842 public void muteAwaitConnection(@NonNull int[] usagesToMute,
8843 @NonNull AudioDeviceAttributes device,
8844 long timeout, @NonNull TimeUnit timeUnit) throws IllegalStateException {
8845 if (timeout <= 0) {
8846 throw new IllegalArgumentException("Timeout must be greater than 0");
8847 }
8848 Objects.requireNonNull(usagesToMute);
8849 if (usagesToMute.length == 0) {
8850 throw new IllegalArgumentException("Array of usages to mute cannot be empty");
8851 }
8852 Objects.requireNonNull(device);
8853 Objects.requireNonNull(timeUnit);
8854 try {
8855 getService().muteAwaitConnection(usagesToMute, device, timeUnit.toMillis(timeout));
8856 } catch (RemoteException e) {
8857 throw e.rethrowFromSystemServer();
8858 }
8859 }
8860
8861 /**
8862 * @hide
8863 * Query which audio device, if any, is causing some playback use cases to be muted until it
8864 * connects.
8865 * @return the audio device used in
8866 * {@link #muteAwaitConnection(int[], AudioDeviceAttributes, long, TimeUnit)}, or null
8867 * if there is no active muting command (either because the muting command was not issued
8868 * or because it timed out)
8869 */
8870 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00008871 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Jean-Michel Trivi933bf142021-11-19 16:18:52 -08008872 public @Nullable AudioDeviceAttributes getMutingExpectedDevice() {
8873 try {
8874 return getService().getMutingExpectedDevice();
8875 } catch (RemoteException e) {
8876 throw e.rethrowFromSystemServer();
8877 }
8878 }
8879
8880 /**
8881 * @hide
8882 * Cancel a {@link #muteAwaitConnection(int[], AudioDeviceAttributes, long, TimeUnit)}
8883 * command.
8884 * @param device the device whose connection was expected when the {@code muteAwaitConnection}
8885 * command was issued.
8886 * @throws IllegalStateException when trying to issue the command for a device whose connection
8887 * is not anticipated by a previous call to
8888 * {@link #muteAwaitConnection(int[], AudioDeviceAttributes, long, TimeUnit)}
8889 */
8890 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00008891 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Jean-Michel Trivi933bf142021-11-19 16:18:52 -08008892 public void cancelMuteAwaitConnection(@NonNull AudioDeviceAttributes device)
8893 throws IllegalStateException {
8894 Objects.requireNonNull(device);
8895 try {
8896 getService().cancelMuteAwaitConnection(device);
8897 } catch (RemoteException e) {
8898 throw e.rethrowFromSystemServer();
8899 }
8900 }
8901
8902 /**
8903 * @hide
8904 * A callback class to receive events about the muting and unmuting of playback use cases
8905 * conditional on the upcoming connection of an audio device.
8906 * @see #registerMuteAwaitConnectionCallback(Executor, AudioManager.MuteAwaitConnectionCallback)
8907 */
8908 @SystemApi
8909 public abstract static class MuteAwaitConnectionCallback {
8910
8911 /**
8912 * An event where the expected audio device connected
8913 * @see MuteAwaitConnectionCallback#onUnmutedEvent(int, AudioDeviceAttributes, int[])
8914 */
8915 public static final int EVENT_CONNECTION = 1;
8916 /**
8917 * An event where the expected audio device failed connect before the timeout happened
8918 * @see MuteAwaitConnectionCallback#onUnmutedEvent(int, AudioDeviceAttributes, int[])
8919 */
8920 public static final int EVENT_TIMEOUT = 2;
8921 /**
8922 * An event where the {@code muteAwaitConnection()} command
8923 * was cancelled with {@link #cancelMuteAwaitConnection(AudioDeviceAttributes)}
8924 * @see MuteAwaitConnectionCallback#onUnmutedEvent(int, AudioDeviceAttributes, int[])
8925 */
8926 public static final int EVENT_CANCEL = 3;
8927
8928 /** @hide */
8929 @IntDef(flag = false, prefix = "EVENT_", value = {
8930 EVENT_CONNECTION,
8931 EVENT_TIMEOUT,
8932 EVENT_CANCEL }
8933 )
8934 @Retention(RetentionPolicy.SOURCE)
8935 public @interface UnmuteEvent {}
8936
8937 /**
8938 * Called when a number of playback use cases are muted in response to a call to
8939 * {@link #muteAwaitConnection(int[], AudioDeviceAttributes, long, TimeUnit)}.
8940 * @param device the audio device whose connection is expected. Playback use cases are
8941 * unmuted when that device connects
8942 * @param mutedUsages an array of {@link AudioAttributes} usages that describe the affected
8943 * playback use cases.
8944 */
8945 public void onMutedUntilConnection(
8946 @NonNull AudioDeviceAttributes device,
8947 @NonNull int[] mutedUsages) {}
8948
8949 /**
8950 * Called when an event occurred that caused playback uses cases to be unmuted
8951 * @param unmuteEvent the nature of the event
8952 * @param device the device that was expected to connect
8953 * @param mutedUsages the array of {@link AudioAttributes} usages that were muted until
8954 * the event occurred
8955 */
8956 public void onUnmutedEvent(
8957 @UnmuteEvent int unmuteEvent,
8958 @NonNull AudioDeviceAttributes device, @NonNull int[] mutedUsages) {}
8959 }
8960
8961
8962 /**
8963 * @hide
8964 * Register a callback to receive updates on the playback muting conditional on a specific
8965 * audio device connection.
8966 * @param executor the {@link Executor} handling the callback
8967 * @param callback the callback to register
8968 */
8969 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00008970 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Jean-Michel Trivi933bf142021-11-19 16:18:52 -08008971 public void registerMuteAwaitConnectionCallback(
8972 @NonNull @CallbackExecutor Executor executor,
8973 @NonNull MuteAwaitConnectionCallback callback) {
8974 synchronized (mMuteAwaitConnectionListenerLock) {
8975 final Pair<ArrayList<ListenerInfo<MuteAwaitConnectionCallback>>,
8976 MuteAwaitConnectionDispatcherStub> res =
8977 CallbackUtil.addListener("registerMuteAwaitConnectionCallback",
8978 executor, callback, mMuteAwaitConnectionListeners,
8979 mMuteAwaitConnDispatcherStub,
8980 () -> new MuteAwaitConnectionDispatcherStub(),
8981 stub -> stub.register(true));
8982 mMuteAwaitConnectionListeners = res.first;
8983 mMuteAwaitConnDispatcherStub = res.second;
8984 }
8985 }
8986
8987 /**
8988 * @hide
8989 * Unregister a previously registered callback for playback muting conditional on device
8990 * connection.
8991 * @param callback the callback to unregister
8992 */
8993 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00008994 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Jean-Michel Trivi933bf142021-11-19 16:18:52 -08008995 public void unregisterMuteAwaitConnectionCallback(
8996 @NonNull MuteAwaitConnectionCallback callback) {
8997 synchronized (mMuteAwaitConnectionListenerLock) {
8998 final Pair<ArrayList<ListenerInfo<MuteAwaitConnectionCallback>>,
8999 MuteAwaitConnectionDispatcherStub> res =
9000 CallbackUtil.removeListener("unregisterMuteAwaitConnectionCallback",
9001 callback, mMuteAwaitConnectionListeners, mMuteAwaitConnDispatcherStub,
9002 stub -> stub.register(false));
9003 mMuteAwaitConnectionListeners = res.first;
9004 mMuteAwaitConnDispatcherStub = res.second;
9005 }
9006 }
9007
Oscar Azucena3ef3f582022-01-31 19:07:14 -08009008 /**
Oscar Azucena88467fe2022-02-15 21:55:11 +00009009 * Add UIDs that can be considered as assistant.
Oscar Azucena3ef3f582022-01-31 19:07:14 -08009010 *
Oscar Azucena88467fe2022-02-15 21:55:11 +00009011 * @param assistantUids UIDs of the services that can be considered as assistant.
Oscar Azucena3ef3f582022-01-31 19:07:14 -08009012 *
9013 * @hide
9014 */
9015 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00009016 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Oscar Azucena88467fe2022-02-15 21:55:11 +00009017 public void addAssistantServicesUids(@NonNull int[] assistantUids) {
Oscar Azucena3ef3f582022-01-31 19:07:14 -08009018 try {
Oscar Azucena88467fe2022-02-15 21:55:11 +00009019 getService().addAssistantServicesUids(assistantUids);
Oscar Azucena3ef3f582022-01-31 19:07:14 -08009020 } catch (RemoteException e) {
9021 throw e.rethrowFromSystemServer();
9022 }
9023 }
9024
9025 /**
Oscar Azucena88467fe2022-02-15 21:55:11 +00009026 * Remove UIDs that can be considered as assistant.
Oscar Azucena3ef3f582022-01-31 19:07:14 -08009027 *
Oscar Azucena88467fe2022-02-15 21:55:11 +00009028 * @param assistantUids UIDs of the services that should be remove.
Oscar Azucena3ef3f582022-01-31 19:07:14 -08009029 *
9030 * @hide
9031 */
9032 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00009033 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Oscar Azucena88467fe2022-02-15 21:55:11 +00009034 public void removeAssistantServicesUids(@NonNull int[] assistantUids) {
Oscar Azucena3ef3f582022-01-31 19:07:14 -08009035 try {
Oscar Azucena88467fe2022-02-15 21:55:11 +00009036 getService().removeAssistantServicesUids(assistantUids);
Oscar Azucena3ef3f582022-01-31 19:07:14 -08009037 } catch (RemoteException e) {
9038 throw e.rethrowFromSystemServer();
9039 }
9040 }
9041
9042 /**
Oscar Azucena88467fe2022-02-15 21:55:11 +00009043 * Get the assistants UIDs that been added with the
9044 * {@link #addAssistantServicesUids(int[])} and not yet removed with
9045 * {@link #removeAssistantServicesUids(int[])}
Oscar Azucena3ef3f582022-01-31 19:07:14 -08009046 *
Oscar Azucena3ae33762022-03-04 04:44:59 +00009047 * <p> Note that during native audioserver crash and after boot up the list of assistant
9048 * UIDs will be reset to an empty list (i.e. no UID will be considered as assistant)
9049 * Just after user switch, the list of assistant will also reset to empty.
9050 * In both cases,The component's UID of the assistiant role or assistant setting will be
9051 * automitically added to the list by the audio service.
9052 *
Oscar Azucena88467fe2022-02-15 21:55:11 +00009053 * @return array of assistants UIDs
Oscar Azucena3ef3f582022-01-31 19:07:14 -08009054 *
9055 * @hide
9056 */
9057 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00009058 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Oscar Azucena88467fe2022-02-15 21:55:11 +00009059 public @NonNull int[] getAssistantServicesUids() {
Oscar Azucena3ef3f582022-01-31 19:07:14 -08009060 try {
9061 int[] uids = getService().getAssistantServicesUids();
Oscar Azucena88467fe2022-02-15 21:55:11 +00009062 return Arrays.copyOf(uids, uids.length);
Oscar Azucena3ef3f582022-01-31 19:07:14 -08009063 } catch (RemoteException e) {
9064 throw e.rethrowFromSystemServer();
9065 }
9066 }
9067
9068 /**
Oscar Azucena88467fe2022-02-15 21:55:11 +00009069 * Sets UIDs that can be considered as active assistant. Calling the API with a new array will
9070 * overwrite previous UIDs. If the array of UIDs is empty then no UID will be considered active.
9071 * In this manner calling the API with an empty array will remove all UIDs previously set.
Oscar Azucena3ef3f582022-01-31 19:07:14 -08009072 *
Oscar Azucena88467fe2022-02-15 21:55:11 +00009073 * @param assistantUids UIDs of the services that can be considered active assistant. Can be
9074 * an empty array, for this no UID will be considered active.
Oscar Azucena3ef3f582022-01-31 19:07:14 -08009075 *
9076 * <p> Note that during audio service crash reset and after boot up the list of active assistant
Oscar Azucena88467fe2022-02-15 21:55:11 +00009077 * 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 -08009078 * Just after user switch the list of active assistant will also reset to empty.
9079 *
9080 * @hide
9081 */
9082 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00009083 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Oscar Azucena88467fe2022-02-15 21:55:11 +00009084 public void setActiveAssistantServiceUids(@NonNull int[] assistantUids) {
Oscar Azucena3ef3f582022-01-31 19:07:14 -08009085 try {
Oscar Azucena88467fe2022-02-15 21:55:11 +00009086 getService().setActiveAssistantServiceUids(assistantUids);
Oscar Azucena3ef3f582022-01-31 19:07:14 -08009087 } catch (RemoteException e) {
9088 throw e.rethrowFromSystemServer();
9089 }
9090 }
9091
9092 /**
Oscar Azucena88467fe2022-02-15 21:55:11 +00009093 * Get active assistant UIDs last set with the
9094 * {@link #setActiveAssistantServiceUids(int[])}
Oscar Azucena3ef3f582022-01-31 19:07:14 -08009095 *
Oscar Azucena88467fe2022-02-15 21:55:11 +00009096 * @return array of active assistants UIDs
Oscar Azucena3ef3f582022-01-31 19:07:14 -08009097 *
9098 * @hide
9099 */
9100 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00009101 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Oscar Azucena88467fe2022-02-15 21:55:11 +00009102 public @NonNull int[] getActiveAssistantServicesUids() {
Oscar Azucena3ef3f582022-01-31 19:07:14 -08009103 try {
9104 int[] uids = getService().getActiveAssistantServiceUids();
Oscar Azucena88467fe2022-02-15 21:55:11 +00009105 return Arrays.copyOf(uids, uids.length);
Oscar Azucena3ef3f582022-01-31 19:07:14 -08009106 } catch (RemoteException e) {
9107 throw e.rethrowFromSystemServer();
9108 }
9109 }
9110
jiabinfe6b7f12022-02-04 18:45:44 +00009111 /**
Shunkai Yao601b6132022-11-22 01:47:48 +00009112 * Returns an {@link AudioHalVersionInfo} indicating the Audio Hal Version. If there is no audio
9113 * HAL found, null will be returned.
jiabinfe6b7f12022-02-04 18:45:44 +00009114 *
Shunkai Yao601b6132022-11-22 01:47:48 +00009115 * @return @see @link #AudioHalVersionInfo The version of Audio HAL.
jiabinfe6b7f12022-02-04 18:45:44 +00009116 * @hide
9117 */
9118 @TestApi
Shunkai Yao601b6132022-11-22 01:47:48 +00009119 public static @Nullable AudioHalVersionInfo getHalVersion() {
jiabinfe6b7f12022-02-04 18:45:44 +00009120 try {
9121 return getService().getHalVersion();
9122 } catch (RemoteException e) {
9123 Log.e(TAG, "Error querying getHalVersion", e);
9124 throw e.rethrowFromSystemServer();
9125 }
9126 }
9127
jiabin89f87ed2022-12-01 22:55:05 +00009128 //====================================================================
9129 // Preferred mixer attributes
9130
9131 /**
9132 * Returns the {@link AudioMixerAttributes} that can be used to set as preferred mixe
9133 * attributes via {@link #setPreferredMixerAttributes(
9134 * AudioAttributes, AudioDeviceInfo, AudioMixerAttributes)}.
9135 * <p>Note that only USB devices are guaranteed to expose configurable mixer attributes, the
9136 * returned list may be empty when devices do not allow dynamic configuration.
9137 *
9138 * @param device the device to query
9139 * @return a list of {@link AudioMixerAttributes} that can be used as preferred mixer attributes
9140 * for the given device.
9141 * @see #setPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo, AudioMixerAttributes)
9142 */
9143 @NonNull
9144 public List<AudioMixerAttributes> getSupportedMixerAttributes(@NonNull AudioDeviceInfo device) {
9145 Objects.requireNonNull(device);
9146 List<AudioMixerAttributes> mixerAttrs = new ArrayList<>();
9147 return (AudioSystem.getSupportedMixerAttributes(device.getId(), mixerAttrs)
9148 == AudioSystem.SUCCESS) ? mixerAttrs : new ArrayList<>();
9149 }
9150
9151 /**
9152 * Configures the mixer attributes for a particular {@link AudioAttributes} over a given
9153 * {@link AudioDeviceInfo}.
9154 * <p>When constructing an {@link AudioMixerAttributes} for setting preferred mixer attributes,
9155 * the mixer format must be constructed from an {@link AudioProfile} that can be used to set
9156 * preferred mixer attributes.
9157 * <p>The ownership of preferred mixer attributes is recognized by uid. When a playback from the
9158 * same uid is routed to the given audio device when calling this API, the output mixer/stream
9159 * will be configured with the values previously set via this API.
9160 * <p>Use {@link #clearPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo)}
9161 * to cancel setting mixer attributes for this {@link AudioAttributes}.
9162 *
9163 * @param attributes the {@link AudioAttributes} whose mixer attributes should be set.
9164 * Currently, only {@link AudioAttributes#USAGE_MEDIA} is supported. When
9165 * playing audio targeted at the given device, use the same attributes for
9166 * playback.
9167 * @param device the device to be routed. Currently, only USB device will be allowed.
9168 * @param mixerAttributes the preferred mixer attributes. When playing audio targeted at the
9169 * given device, use the same {@link AudioFormat} for both playback
9170 * and the mixer attributes.
9171 * @return true only if the preferred mixer attributes are set successfully.
9172 * @see #getPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo)
9173 * @see #clearPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo)
9174 */
Jean-Michel Trivic4557822023-01-23 18:19:52 +00009175 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
jiabin89f87ed2022-12-01 22:55:05 +00009176 public boolean setPreferredMixerAttributes(@NonNull AudioAttributes attributes,
9177 @NonNull AudioDeviceInfo device,
9178 @NonNull AudioMixerAttributes mixerAttributes) {
9179 Objects.requireNonNull(attributes);
9180 Objects.requireNonNull(device);
9181 Objects.requireNonNull(mixerAttributes);
9182 try {
9183 final int status = getService().setPreferredMixerAttributes(
9184 attributes, device.getId(), mixerAttributes);
9185 return status == AudioSystem.SUCCESS;
9186 } catch (RemoteException e) {
9187 throw e.rethrowFromSystemServer();
9188 }
9189 }
9190
9191 /**
9192 * Returns current preferred mixer attributes that is set via
9193 * {@link #setPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo, AudioMixerAttributes)}
9194 *
9195 * @param attributes the {@link AudioAttributes} whose mixer attributes should be set.
9196 * @param device the expected routing device
9197 * @return the preferred mixer attributes, which will be null when no preferred mixer attributes
9198 * have been set, or when they have been cleared.
9199 * @see #setPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo, AudioMixerAttributes)
9200 * @see #clearPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo)
9201 */
9202 @Nullable
9203 public AudioMixerAttributes getPreferredMixerAttributes(
9204 @NonNull AudioAttributes attributes,
9205 @NonNull AudioDeviceInfo device) {
9206 Objects.requireNonNull(attributes);
9207 Objects.requireNonNull(device);
9208 List<AudioMixerAttributes> mixerAttrList = new ArrayList<>();
9209 int ret = AudioSystem.getPreferredMixerAttributes(
9210 attributes, device.getId(), mixerAttrList);
9211 if (ret == AudioSystem.SUCCESS) {
9212 return mixerAttrList.isEmpty() ? null : mixerAttrList.get(0);
9213 } else {
9214 Log.e(TAG, "Failed calling getPreferredMixerAttributes, ret=" + ret);
9215 return null;
9216 }
9217 }
9218
9219 /**
9220 * Clears the current preferred mixer attributes that were previously set via
9221 * {@link #setPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo, AudioMixerAttributes)}
9222 *
9223 * @param attributes the {@link AudioAttributes} whose mixer attributes should be cleared.
9224 * @param device the expected routing device
9225 * @return true only if the preferred mixer attributes are removed successfully.
9226 * @see #setPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo, AudioMixerAttributes)
9227 * @see #getPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo)
9228 */
Jean-Michel Trivic4557822023-01-23 18:19:52 +00009229 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
jiabin89f87ed2022-12-01 22:55:05 +00009230 public boolean clearPreferredMixerAttributes(
9231 @NonNull AudioAttributes attributes,
9232 @NonNull AudioDeviceInfo device) {
9233 Objects.requireNonNull(attributes);
9234 Objects.requireNonNull(device);
9235 try {
9236 final int status = getService().clearPreferredMixerAttributes(
9237 attributes, device.getId());
9238 return status == AudioSystem.SUCCESS;
9239 } catch (RemoteException e) {
9240 throw e.rethrowFromSystemServer();
9241 }
9242 }
9243
9244 /**
9245 * Interface to be notified of changes in the preferred mixer attributes.
9246 * <p>Note that this listener will only be invoked whenever
9247 * {@link #setPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo, AudioMixerAttributes)}
9248 * or {@link #clearPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo)} or device
9249 * disconnection causes a change in preferred mixer attributes.
9250 * @see #setPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo, AudioMixerAttributes)
9251 * @see #clearPreferredMixerAttributes(AudioAttributes, AudioDeviceInfo)
9252 */
9253 public interface OnPreferredMixerAttributesChangedListener {
9254 /**
9255 * Called on the listener to indicate that the preferred mixer attributes for the audio
9256 * attributes over the given device has changed.
9257 *
9258 * @param attributes the audio attributes for playback
9259 * @param device the targeted device
9260 * @param mixerAttributes the {@link AudioMixerAttributes} that contains information for
9261 * preferred mixer attributes or null if preferred mixer attributes
9262 * is cleared
9263 */
9264 void onPreferredMixerAttributesChanged(
9265 @NonNull AudioAttributes attributes,
9266 @NonNull AudioDeviceInfo device,
9267 @Nullable AudioMixerAttributes mixerAttributes);
9268 }
9269
9270 /**
9271 * Manage the {@link OnPreferredMixerAttributesChangedListener} listeners and the
9272 * {@link PreferredMixerAttributesDispatcherStub}.
9273 */
9274 private final CallbackUtil.LazyListenerManager<OnPreferredMixerAttributesChangedListener>
9275 mPrefMixerAttributesListenerMgr = new CallbackUtil.LazyListenerManager();
9276
9277 /**
9278 * Adds a listener for being notified of changes to the preferred mixer attributes.
9279 * @param executor the executor to execute the callback
9280 * @param listener the listener to be notified of changes in the preferred mixer attributes.
9281 */
9282 public void addOnPreferredMixerAttributesChangedListener(
9283 @NonNull @CallbackExecutor Executor executor,
9284 @NonNull OnPreferredMixerAttributesChangedListener listener) {
9285 Objects.requireNonNull(executor);
9286 Objects.requireNonNull(listener);
9287 mPrefMixerAttributesListenerMgr.addListener(executor, listener,
9288 "addOnPreferredMixerAttributesChangedListener",
9289 () -> new PreferredMixerAttributesDispatcherStub());
9290 }
9291
9292 /**
9293 * Removes a previously added listener of changes to the preferred mixer attributes.
9294 * @param listener the listener to be notified of changes in the preferred mixer attributes,
9295 * which were added via {@link #addOnPreferredMixerAttributesChangedListener(
9296 * Executor, OnPreferredMixerAttributesChangedListener)}.
9297 */
9298 public void removeOnPreferredMixerAttributesChangedListener(
9299 @NonNull OnPreferredMixerAttributesChangedListener listener) {
9300 Objects.requireNonNull(listener);
9301 mPrefMixerAttributesListenerMgr.removeListener(listener,
9302 "removeOnPreferredMixerAttributesChangedListener");
9303 }
9304
9305 private final class PreferredMixerAttributesDispatcherStub
9306 extends IPreferredMixerAttributesDispatcher.Stub
9307 implements CallbackUtil.DispatcherStub {
9308
9309 @Override
9310 public void register(boolean register) {
9311 try {
9312 if (register) {
9313 getService().registerPreferredMixerAttributesDispatcher(this);
9314 } else {
9315 getService().unregisterPreferredMixerAttributesDispatcher(this);
9316 }
9317 } catch (RemoteException e) {
9318 e.rethrowFromSystemServer();
9319 }
9320 }
9321
9322 @Override
9323 public void dispatchPrefMixerAttributesChanged(@NonNull AudioAttributes attr,
9324 int deviceId,
9325 @Nullable AudioMixerAttributes mixerAttr) {
9326 // TODO: If the device is disconnected, we may not be able to find the device with
9327 // given device id. We need a better to carry the device information via binder.
9328 AudioDeviceInfo device = getDeviceForPortId(deviceId, GET_DEVICES_OUTPUTS);
9329 if (device == null) {
9330 Log.d(TAG, "Drop preferred mixer attributes changed as the device("
9331 + deviceId + ") is disconnected");
9332 return;
9333 }
9334 mPrefMixerAttributesListenerMgr.callListeners(
9335 (listener) -> listener.onPreferredMixerAttributesChanged(
9336 attr, device, mixerAttr));
9337 }
9338 }
9339
Eric Laurent3c474bc2022-12-16 17:24:32 +01009340 /**
9341 * Requests if the implementation supports controlling the latency modes
9342 * over the Bluetooth A2DP or LE Audio links.
9343 *
9344 * @return true if supported, false otherwise
9345 *
9346 * @hide
9347 */
9348 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00009349 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Eric Laurent3c474bc2022-12-16 17:24:32 +01009350 public boolean supportsBluetoothVariableLatency() {
9351 try {
9352 return getService().supportsBluetoothVariableLatency();
9353 } catch (RemoteException e) {
9354 throw e.rethrowFromSystemServer();
9355 }
9356 }
9357
9358 /**
9359 * Enables or disables the variable Bluetooth latency control mechanism in the
9360 * audio framework and the audio HAL. This does not apply to the latency mode control
9361 * on the spatializer output as this is a built-in feature.
9362 *
9363 * @hide
9364 */
9365 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00009366 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Eric Laurent3c474bc2022-12-16 17:24:32 +01009367 public void setBluetoothVariableLatencyEnabled(boolean enabled) {
9368 try {
9369 getService().setBluetoothVariableLatencyEnabled(enabled);
9370 } catch (RemoteException e) {
9371 throw e.rethrowFromSystemServer();
9372 }
9373 }
9374
9375 /**
9376 * Indicates if the variable Bluetooth latency control mechanism is enabled or disabled.
9377 * @hide
9378 */
9379 @SystemApi
Jean-Michel Trivic4557822023-01-23 18:19:52 +00009380 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
Eric Laurent3c474bc2022-12-16 17:24:32 +01009381 public boolean isBluetoothVariableLatencyEnabled() {
9382 try {
9383 return getService().isBluetoothVariableLatencyEnabled();
9384 } catch (RemoteException e) {
9385 throw e.rethrowFromSystemServer();
9386 }
9387 }
9388
jiabin89f87ed2022-12-01 22:55:05 +00009389 //====================================================================
9390 // Mute await connection
9391
Jean-Michel Trivi933bf142021-11-19 16:18:52 -08009392 private final Object mMuteAwaitConnectionListenerLock = new Object();
9393
9394 @GuardedBy("mMuteAwaitConnectionListenerLock")
9395 private @Nullable ArrayList<ListenerInfo<MuteAwaitConnectionCallback>>
9396 mMuteAwaitConnectionListeners;
9397
9398 @GuardedBy("mMuteAwaitConnectionListenerLock")
9399 private MuteAwaitConnectionDispatcherStub mMuteAwaitConnDispatcherStub;
9400
9401 private final class MuteAwaitConnectionDispatcherStub
9402 extends IMuteAwaitConnectionCallback.Stub {
9403 public void register(boolean register) {
9404 try {
9405 getService().registerMuteAwaitConnectionDispatcher(this, register);
9406 } catch (RemoteException e) {
9407 throw e.rethrowFromSystemServer();
9408 }
9409 }
9410
9411 @Override
9412 @SuppressLint("GuardedBy") // lock applied inside callListeners method
9413 public void dispatchOnMutedUntilConnection(AudioDeviceAttributes device,
9414 int[] mutedUsages) {
9415 CallbackUtil.callListeners(mMuteAwaitConnectionListeners,
9416 mMuteAwaitConnectionListenerLock,
9417 (listener) -> listener.onMutedUntilConnection(device, mutedUsages));
9418 }
9419
9420 @Override
9421 @SuppressLint("GuardedBy") // lock applied inside callListeners method
9422 public void dispatchOnUnmutedEvent(int event, AudioDeviceAttributes device,
9423 int[] mutedUsages) {
9424 CallbackUtil.callListeners(mMuteAwaitConnectionListeners,
9425 mMuteAwaitConnectionListenerLock,
9426 (listener) -> listener.onUnmutedEvent(event, device, mutedUsages));
9427 }
9428 }
9429
9430 //---------------------------------------------------------
Paul McLeane3383cc2015-05-08 11:41:20 -07009431 // Inner classes
9432 //--------------------
9433 /**
9434 * Helper class to handle the forwarding of native events to the appropriate listener
9435 * (potentially) handled in a different thread.
9436 */
9437 private class NativeEventHandlerDelegate {
9438 private final Handler mHandler;
9439
Paul McLean03346882015-05-12 15:36:56 -07009440 NativeEventHandlerDelegate(final AudioDeviceCallback callback,
Paul McLeane3383cc2015-05-08 11:41:20 -07009441 Handler handler) {
9442 // find the looper for our new event handler
9443 Looper looper;
9444 if (handler != null) {
9445 looper = handler.getLooper();
9446 } else {
9447 // no given handler, use the looper the addListener call was called in
9448 looper = Looper.getMainLooper();
9449 }
9450
9451 // construct the event handler with this looper
9452 if (looper != null) {
9453 // implement the event handler delegate
9454 mHandler = new Handler(looper) {
9455 @Override
9456 public void handleMessage(Message msg) {
9457 switch(msg.what) {
Paul McLeancbeb8a22015-06-10 08:21:27 -07009458 case MSG_DEVICES_CALLBACK_REGISTERED:
Paul McLean03346882015-05-12 15:36:56 -07009459 case MSG_DEVICES_DEVICES_ADDED:
Paul McLean03346882015-05-12 15:36:56 -07009460 if (callback != null) {
9461 callback.onAudioDevicesAdded((AudioDeviceInfo[])msg.obj);
Paul McLeane3383cc2015-05-08 11:41:20 -07009462 }
9463 break;
Paul McLean03346882015-05-12 15:36:56 -07009464
9465 case MSG_DEVICES_DEVICES_REMOVED:
9466 if (callback != null) {
9467 callback.onAudioDevicesRemoved((AudioDeviceInfo[])msg.obj);
9468 }
9469 break;
9470
Paul McLeane3383cc2015-05-08 11:41:20 -07009471 default:
9472 Log.e(TAG, "Unknown native event type: " + msg.what);
9473 break;
9474 }
9475 }
9476 };
9477 } else {
9478 mHandler = null;
9479 }
9480 }
9481
9482 Handler getHandler() {
9483 return mHandler;
9484 }
9485 }
Carter Hsu2065d1e2022-01-19 19:54:50 +08009486}