1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.media.audiopolicy; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.RequiresPermission; 23 import android.annotation.SystemApi; 24 import android.annotation.TestApi; 25 import android.annotation.UserIdInt; 26 import android.app.ActivityManager; 27 import android.content.Context; 28 import android.content.pm.PackageManager; 29 import android.media.AudioAttributes; 30 import android.media.AudioDeviceInfo; 31 import android.media.AudioFocusInfo; 32 import android.media.AudioFormat; 33 import android.media.AudioManager; 34 import android.media.AudioRecord; 35 import android.media.AudioTrack; 36 import android.media.IAudioService; 37 import android.media.MediaRecorder; 38 import android.media.projection.MediaProjection; 39 import android.os.Binder; 40 import android.os.Handler; 41 import android.os.IBinder; 42 import android.os.Looper; 43 import android.os.Message; 44 import android.os.RemoteException; 45 import android.os.ServiceManager; 46 import android.util.Log; 47 import android.util.Slog; 48 49 import com.android.internal.annotations.GuardedBy; 50 51 import java.lang.annotation.Retention; 52 import java.lang.annotation.RetentionPolicy; 53 import java.lang.ref.WeakReference; 54 import java.util.ArrayList; 55 import java.util.List; 56 import java.util.Objects; 57 58 /** 59 * @hide 60 * AudioPolicy provides access to the management of audio routing and audio focus. 61 */ 62 @SystemApi 63 public class AudioPolicy { 64 65 private static final String TAG = "AudioPolicy"; 66 private static final boolean DEBUG = false; 67 private final Object mLock = new Object(); 68 69 /** 70 * The status of an audio policy that is valid but cannot be used because it is not registered. 71 */ 72 public static final int POLICY_STATUS_UNREGISTERED = 1; 73 /** 74 * The status of an audio policy that is valid, successfully registered and thus active. 75 */ 76 public static final int POLICY_STATUS_REGISTERED = 2; 77 78 private int mStatus; 79 private String mRegistrationId; 80 private AudioPolicyStatusListener mStatusListener; 81 private boolean mIsFocusPolicy; 82 private boolean mIsTestFocusPolicy; 83 84 /** 85 * The list of AudioTrack instances created to inject audio into the associated mixes 86 * Lazy initialization in {@link #createAudioTrackSource(AudioMix)} 87 */ 88 @GuardedBy("mLock") 89 @Nullable private ArrayList<WeakReference<AudioTrack>> mInjectors; 90 /** 91 * The list AudioRecord instances created to capture audio from the associated mixes 92 * Lazy initialization in {@link #createAudioRecordSink(AudioMix)} 93 */ 94 @GuardedBy("mLock") 95 @Nullable private ArrayList<WeakReference<AudioRecord>> mCaptors; 96 97 /** 98 * The behavior of a policy with regards to audio focus where it relies on the application 99 * to do the ducking, the is the legacy and default behavior. 100 */ 101 public static final int FOCUS_POLICY_DUCKING_IN_APP = 0; 102 public static final int FOCUS_POLICY_DUCKING_DEFAULT = FOCUS_POLICY_DUCKING_IN_APP; 103 /** 104 * The behavior of a policy with regards to audio focus where it handles ducking instead 105 * of the application losing focus and being signaled it can duck (as communicated by 106 * {@link android.media.AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}). 107 * <br>Can only be used after having set a listener with 108 * {@link AudioPolicy#setAudioPolicyFocusListener(AudioPolicyFocusListener)}. 109 */ 110 public static final int FOCUS_POLICY_DUCKING_IN_POLICY = 1; 111 112 private AudioPolicyFocusListener mFocusListener; 113 114 private final AudioPolicyVolumeCallback mVolCb; 115 116 private Context mContext; 117 118 private AudioPolicyConfig mConfig; 119 120 private final MediaProjection mProjection; 121 122 /** @hide */ getConfig()123 public AudioPolicyConfig getConfig() { return mConfig; } 124 /** @hide */ hasFocusListener()125 public boolean hasFocusListener() { return mFocusListener != null; } 126 /** @hide */ isFocusPolicy()127 public boolean isFocusPolicy() { return mIsFocusPolicy; } 128 /** @hide */ isTestFocusPolicy()129 public boolean isTestFocusPolicy() { 130 return mIsTestFocusPolicy; 131 } 132 /** @hide */ isVolumeController()133 public boolean isVolumeController() { return mVolCb != null; } 134 /** @hide */ getMediaProjection()135 public @Nullable MediaProjection getMediaProjection() { 136 return mProjection; 137 } 138 139 /** 140 * The parameters are guaranteed non-null through the Builder 141 */ AudioPolicy(AudioPolicyConfig config, Context context, Looper looper, AudioPolicyFocusListener fl, AudioPolicyStatusListener sl, boolean isFocusPolicy, boolean isTestFocusPolicy, AudioPolicyVolumeCallback vc, @Nullable MediaProjection projection)142 private AudioPolicy(AudioPolicyConfig config, Context context, Looper looper, 143 AudioPolicyFocusListener fl, AudioPolicyStatusListener sl, 144 boolean isFocusPolicy, boolean isTestFocusPolicy, 145 AudioPolicyVolumeCallback vc, @Nullable MediaProjection projection) { 146 mConfig = config; 147 mStatus = POLICY_STATUS_UNREGISTERED; 148 mContext = context; 149 if (looper == null) { 150 looper = Looper.getMainLooper(); 151 } 152 if (looper != null) { 153 mEventHandler = new EventHandler(this, looper); 154 } else { 155 mEventHandler = null; 156 Log.e(TAG, "No event handler due to looper without a thread"); 157 } 158 mFocusListener = fl; 159 mStatusListener = sl; 160 mIsFocusPolicy = isFocusPolicy; 161 mIsTestFocusPolicy = isTestFocusPolicy; 162 mVolCb = vc; 163 mProjection = projection; 164 } 165 166 /** 167 * Builder class for {@link AudioPolicy} objects. 168 * By default the policy to be created doesn't govern audio focus decisions. 169 */ 170 public static class Builder { 171 private ArrayList<AudioMix> mMixes; 172 private Context mContext; 173 private Looper mLooper; 174 private AudioPolicyFocusListener mFocusListener; 175 private AudioPolicyStatusListener mStatusListener; 176 private boolean mIsFocusPolicy = false; 177 private boolean mIsTestFocusPolicy = false; 178 private AudioPolicyVolumeCallback mVolCb; 179 private MediaProjection mProjection; 180 181 /** 182 * Constructs a new Builder with no audio mixes. 183 * @param context the context for the policy 184 */ Builder(Context context)185 public Builder(Context context) { 186 mMixes = new ArrayList<AudioMix>(); 187 mContext = context; 188 } 189 190 /** 191 * Add an {@link AudioMix} to be part of the audio policy being built. 192 * @param mix a non-null {@link AudioMix} to be part of the audio policy. 193 * @return the same Builder instance. 194 * @throws IllegalArgumentException 195 */ 196 @NonNull addMix(@onNull AudioMix mix)197 public Builder addMix(@NonNull AudioMix mix) throws IllegalArgumentException { 198 if (mix == null) { 199 throw new IllegalArgumentException("Illegal null AudioMix argument"); 200 } 201 mMixes.add(mix); 202 return this; 203 } 204 205 /** 206 * Sets the {@link Looper} on which to run the event loop. 207 * @param looper a non-null specific Looper. 208 * @return the same Builder instance. 209 * @throws IllegalArgumentException 210 */ 211 @NonNull setLooper(@onNull Looper looper)212 public Builder setLooper(@NonNull Looper looper) throws IllegalArgumentException { 213 if (looper == null) { 214 throw new IllegalArgumentException("Illegal null Looper argument"); 215 } 216 mLooper = looper; 217 return this; 218 } 219 220 /** 221 * Sets the audio focus listener for the policy. 222 * @param l a {@link AudioPolicy.AudioPolicyFocusListener} 223 */ setAudioPolicyFocusListener(AudioPolicyFocusListener l)224 public void setAudioPolicyFocusListener(AudioPolicyFocusListener l) { 225 mFocusListener = l; 226 } 227 228 /** 229 * Declares whether this policy will grant and deny audio focus through 230 * the {@link AudioPolicy.AudioPolicyFocusListener}. 231 * If set to {@code true}, it is mandatory to set an 232 * {@link AudioPolicy.AudioPolicyFocusListener} in order to successfully build 233 * an {@code AudioPolicy} instance. 234 * @param isFocusPolicy true if the policy will govern audio focus decisions. 235 * @return the same Builder instance. 236 */ 237 @NonNull setIsAudioFocusPolicy(boolean isFocusPolicy)238 public Builder setIsAudioFocusPolicy(boolean isFocusPolicy) { 239 mIsFocusPolicy = isFocusPolicy; 240 return this; 241 } 242 243 /** 244 * @hide 245 * Test method to declare whether this audio focus policy is for test purposes only. 246 * Having a test policy registered will disable the current focus policy and replace it 247 * with this test policy. When unregistered, the previous focus policy will be restored. 248 * <p>A value of <code>true</code> will be ignored if the AudioPolicy is not also 249 * focus policy. 250 * @param isTestFocusPolicy true if the focus policy to register is for testing purposes. 251 * @return the same Builder instance 252 */ 253 @TestApi 254 @NonNull setIsTestFocusPolicy(boolean isTestFocusPolicy)255 public Builder setIsTestFocusPolicy(boolean isTestFocusPolicy) { 256 mIsTestFocusPolicy = isTestFocusPolicy; 257 return this; 258 } 259 260 /** 261 * Sets the audio policy status listener. 262 * @param l a {@link AudioPolicy.AudioPolicyStatusListener} 263 */ setAudioPolicyStatusListener(AudioPolicyStatusListener l)264 public void setAudioPolicyStatusListener(AudioPolicyStatusListener l) { 265 mStatusListener = l; 266 } 267 268 /** 269 * Sets the callback to receive all volume key-related events. 270 * The callback will only be called if the device is configured to handle volume events 271 * in the PhoneWindowManager (see config_handleVolumeKeysInWindowManager) 272 * @param vc 273 * @return the same Builder instance. 274 */ 275 @NonNull setAudioPolicyVolumeCallback(@onNull AudioPolicyVolumeCallback vc)276 public Builder setAudioPolicyVolumeCallback(@NonNull AudioPolicyVolumeCallback vc) { 277 if (vc == null) { 278 throw new IllegalArgumentException("Invalid null volume callback"); 279 } 280 mVolCb = vc; 281 return this; 282 } 283 284 /** 285 * Set a media projection obtained through createMediaProjection(). 286 * 287 * A MediaProjection that can project audio allows to register an audio 288 * policy LOOPBACK|RENDER without the MODIFY_AUDIO_ROUTING permission. 289 * 290 * @hide 291 */ 292 @NonNull setMediaProjection(@onNull MediaProjection projection)293 public Builder setMediaProjection(@NonNull MediaProjection projection) { 294 if (projection == null) { 295 throw new IllegalArgumentException("Invalid null volume callback"); 296 } 297 mProjection = projection; 298 return this; 299 300 } 301 302 /** 303 * Combines all of the attributes that have been set on this {@code Builder} and returns a 304 * new {@link AudioPolicy} object. 305 * @return a new {@code AudioPolicy} object. 306 * @throws IllegalStateException if there is no 307 * {@link AudioPolicy.AudioPolicyStatusListener} but the policy was configured 308 * as an audio focus policy with {@link #setIsAudioFocusPolicy(boolean)}. 309 */ 310 @NonNull build()311 public AudioPolicy build() { 312 if (mStatusListener != null) { 313 // the AudioPolicy status listener includes updates on each mix activity state 314 for (AudioMix mix : mMixes) { 315 mix.mCallbackFlags |= AudioMix.CALLBACK_FLAG_NOTIFY_ACTIVITY; 316 } 317 } 318 if (mIsFocusPolicy && mFocusListener == null) { 319 throw new IllegalStateException("Cannot be a focus policy without " 320 + "an AudioPolicyFocusListener"); 321 } 322 return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext, mLooper, 323 mFocusListener, mStatusListener, mIsFocusPolicy, mIsTestFocusPolicy, 324 mVolCb, mProjection); 325 } 326 } 327 328 /** 329 * Update the current configuration of the set of audio mixes by adding new ones, while 330 * keeping the policy registered. 331 * This method can only be called on a registered policy. 332 * @param mixes the list of {@link AudioMix} to add 333 * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR} 334 * otherwise. 335 */ attachMixes(@onNull List<AudioMix> mixes)336 public int attachMixes(@NonNull List<AudioMix> mixes) { 337 if (mixes == null) { 338 throw new IllegalArgumentException("Illegal null list of AudioMix"); 339 } 340 synchronized (mLock) { 341 if (mStatus != POLICY_STATUS_REGISTERED) { 342 throw new IllegalStateException("Cannot alter unregistered AudioPolicy"); 343 } 344 final ArrayList<AudioMix> zeMixes = new ArrayList<AudioMix>(mixes.size()); 345 for (AudioMix mix : mixes) { 346 if (mix == null) { 347 throw new IllegalArgumentException("Illegal null AudioMix in attachMixes"); 348 } else { 349 zeMixes.add(mix); 350 } 351 } 352 final AudioPolicyConfig cfg = new AudioPolicyConfig(zeMixes); 353 IAudioService service = getService(); 354 try { 355 final int status = service.addMixForPolicy(cfg, this.cb()); 356 if (status == AudioManager.SUCCESS) { 357 mConfig.add(zeMixes); 358 } 359 return status; 360 } catch (RemoteException e) { 361 Log.e(TAG, "Dead object in attachMixes", e); 362 return AudioManager.ERROR; 363 } 364 } 365 } 366 367 /** 368 * Update the current configuration of the set of audio mixes by removing some, while 369 * keeping the policy registered. 370 * This method can only be called on a registered policy. 371 * @param mixes the list of {@link AudioMix} to remove 372 * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR} 373 * otherwise. 374 */ detachMixes(@onNull List<AudioMix> mixes)375 public int detachMixes(@NonNull List<AudioMix> mixes) { 376 if (mixes == null) { 377 throw new IllegalArgumentException("Illegal null list of AudioMix"); 378 } 379 synchronized (mLock) { 380 if (mStatus != POLICY_STATUS_REGISTERED) { 381 throw new IllegalStateException("Cannot alter unregistered AudioPolicy"); 382 } 383 final ArrayList<AudioMix> zeMixes = new ArrayList<AudioMix>(mixes.size()); 384 for (AudioMix mix : mixes) { 385 if (mix == null) { 386 throw new IllegalArgumentException("Illegal null AudioMix in detachMixes"); 387 // TODO also check mix is currently contained in list of mixes 388 } else { 389 zeMixes.add(mix); 390 } 391 } 392 final AudioPolicyConfig cfg = new AudioPolicyConfig(zeMixes); 393 IAudioService service = getService(); 394 try { 395 final int status = service.removeMixForPolicy(cfg, this.cb()); 396 if (status == AudioManager.SUCCESS) { 397 mConfig.remove(zeMixes); 398 } 399 return status; 400 } catch (RemoteException e) { 401 Log.e(TAG, "Dead object in detachMixes", e); 402 return AudioManager.ERROR; 403 } 404 } 405 } 406 407 /** 408 * @hide 409 * Configures the audio framework so that all audio streams originating from the given UID 410 * can only come from a set of audio devices. 411 * For this routing to be operational, a number of {@link AudioMix} instances must have been 412 * previously registered on this policy, and routed to a super-set of the given audio devices 413 * with {@link AudioMix.Builder#setDevice(android.media.AudioDeviceInfo)}. Note that having 414 * multiple devices in the list doesn't imply the signals will be duplicated on the different 415 * audio devices, final routing will depend on the {@link AudioAttributes} of the sounds being 416 * played. 417 * @param uid UID of the application to affect. 418 * @param devices list of devices to which the audio stream of the application may be routed. 419 * @return true if the change was successful, false otherwise. 420 */ 421 @SystemApi setUidDeviceAffinity(int uid, @NonNull List<AudioDeviceInfo> devices)422 public boolean setUidDeviceAffinity(int uid, @NonNull List<AudioDeviceInfo> devices) { 423 if (devices == null) { 424 throw new IllegalArgumentException("Illegal null list of audio devices"); 425 } 426 synchronized (mLock) { 427 if (mStatus != POLICY_STATUS_REGISTERED) { 428 throw new IllegalStateException("Cannot use unregistered AudioPolicy"); 429 } 430 final int[] deviceTypes = new int[devices.size()]; 431 final String[] deviceAdresses = new String[devices.size()]; 432 int i = 0; 433 for (AudioDeviceInfo device : devices) { 434 if (device == null) { 435 throw new IllegalArgumentException( 436 "Illegal null AudioDeviceInfo in setUidDeviceAffinity"); 437 } 438 deviceTypes[i] = 439 AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()); 440 deviceAdresses[i] = device.getAddress(); 441 i++; 442 } 443 final IAudioService service = getService(); 444 try { 445 final int status = service.setUidDeviceAffinity(this.cb(), 446 uid, deviceTypes, deviceAdresses); 447 return (status == AudioManager.SUCCESS); 448 } catch (RemoteException e) { 449 Log.e(TAG, "Dead object in setUidDeviceAffinity", e); 450 return false; 451 } 452 } 453 } 454 455 /** 456 * @hide 457 * Removes audio device affinity previously set by 458 * {@link #setUidDeviceAffinity(int, java.util.List)}. 459 * @param uid UID of the application affected. 460 * @return true if the change was successful, false otherwise. 461 */ 462 @SystemApi removeUidDeviceAffinity(int uid)463 public boolean removeUidDeviceAffinity(int uid) { 464 synchronized (mLock) { 465 if (mStatus != POLICY_STATUS_REGISTERED) { 466 throw new IllegalStateException("Cannot use unregistered AudioPolicy"); 467 } 468 final IAudioService service = getService(); 469 try { 470 final int status = service.removeUidDeviceAffinity(this.cb(), uid); 471 return (status == AudioManager.SUCCESS); 472 } catch (RemoteException e) { 473 Log.e(TAG, "Dead object in removeUidDeviceAffinity", e); 474 return false; 475 } 476 } 477 } 478 479 /** 480 * @hide 481 * Removes audio device affinity previously set by 482 * {@link #setUserIdDeviceAffinity(int, java.util.List)}. 483 * @param userId userId of the application affected, as obtained via 484 * {@link UserHandle#getIdentifier}. Not to be confused with application uid. 485 * @return true if the change was successful, false otherwise. 486 */ 487 @SystemApi removeUserIdDeviceAffinity(@serIdInt int userId)488 public boolean removeUserIdDeviceAffinity(@UserIdInt int userId) { 489 synchronized (mLock) { 490 if (mStatus != POLICY_STATUS_REGISTERED) { 491 throw new IllegalStateException("Cannot use unregistered AudioPolicy"); 492 } 493 final IAudioService service = getService(); 494 try { 495 final int status = service.removeUserIdDeviceAffinity(this.cb(), userId); 496 return (status == AudioManager.SUCCESS); 497 } catch (RemoteException e) { 498 Log.e(TAG, "Dead object in removeUserIdDeviceAffinity", e); 499 return false; 500 } 501 } 502 } 503 504 /** 505 * @hide 506 * Configures the audio framework so that all audio streams originating from the given user 507 * can only come from a set of audio devices. 508 * For this routing to be operational, a number of {@link AudioMix} instances must have been 509 * previously registered on this policy, and routed to a super-set of the given audio devices 510 * with {@link AudioMix.Builder#setDevice(android.media.AudioDeviceInfo)}. Note that having 511 * multiple devices in the list doesn't imply the signals will be duplicated on the different 512 * audio devices, final routing will depend on the {@link AudioAttributes} of the sounds being 513 * played. 514 * @param userId userId of the application affected, as obtained via 515 * {@link UserHandle#getIdentifier}. Not to be confused with application uid. 516 * @param devices list of devices to which the audio stream of the application may be routed. 517 * @return true if the change was successful, false otherwise. 518 */ 519 @SystemApi setUserIdDeviceAffinity(@serIdInt int userId, @NonNull List<AudioDeviceInfo> devices)520 public boolean setUserIdDeviceAffinity(@UserIdInt int userId, 521 @NonNull List<AudioDeviceInfo> devices) { 522 Objects.requireNonNull(devices, "Illegal null list of audio devices"); 523 synchronized (mLock) { 524 if (mStatus != POLICY_STATUS_REGISTERED) { 525 throw new IllegalStateException("Cannot use unregistered AudioPolicy"); 526 } 527 final int[] deviceTypes = new int[devices.size()]; 528 final String[] deviceAddresses = new String[devices.size()]; 529 int i = 0; 530 for (AudioDeviceInfo device : devices) { 531 if (device == null) { 532 throw new IllegalArgumentException( 533 "Illegal null AudioDeviceInfo in setUserIdDeviceAffinity"); 534 } 535 deviceTypes[i] = 536 AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()); 537 deviceAddresses[i] = device.getAddress(); 538 i++; 539 } 540 final IAudioService service = getService(); 541 try { 542 final int status = service.setUserIdDeviceAffinity(this.cb(), 543 userId, deviceTypes, deviceAddresses); 544 return (status == AudioManager.SUCCESS); 545 } catch (RemoteException e) { 546 Log.e(TAG, "Dead object in setUserIdDeviceAffinity", e); 547 return false; 548 } 549 } 550 } 551 552 /** @hide */ reset()553 public void reset() { 554 setRegistration(null); 555 mConfig.reset(); 556 } 557 setRegistration(String regId)558 public void setRegistration(String regId) { 559 synchronized (mLock) { 560 mRegistrationId = regId; 561 mConfig.setRegistration(regId); 562 if (regId != null) { 563 mStatus = POLICY_STATUS_REGISTERED; 564 } else { 565 mStatus = POLICY_STATUS_UNREGISTERED; 566 } 567 } 568 sendMsg(MSG_POLICY_STATUS_CHANGE); 569 } 570 571 /**@hide*/ getRegistration()572 public String getRegistration() { 573 return mRegistrationId; 574 } 575 policyReadyToUse()576 private boolean policyReadyToUse() { 577 synchronized (mLock) { 578 if (mStatus != POLICY_STATUS_REGISTERED) { 579 Log.e(TAG, "Cannot use unregistered AudioPolicy"); 580 return false; 581 } 582 if (mRegistrationId == null) { 583 Log.e(TAG, "Cannot use unregistered AudioPolicy"); 584 return false; 585 } 586 } 587 588 // Loopback|capture only need an audio projection, everything else need MODIFY_AUDIO_ROUTING 589 boolean canModifyAudioRouting = PackageManager.PERMISSION_GRANTED 590 == checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING); 591 592 boolean canInterceptCallAudio = PackageManager.PERMISSION_GRANTED 593 == checkCallingOrSelfPermission( 594 android.Manifest.permission.CALL_AUDIO_INTERCEPTION); 595 596 boolean canProjectAudio; 597 try { 598 canProjectAudio = mProjection != null && mProjection.getProjection().canProjectAudio(); 599 } catch (RemoteException e) { 600 Log.e(TAG, "Failed to check if MediaProjection#canProjectAudio"); 601 throw e.rethrowFromSystemServer(); 602 } 603 604 if (!((isLoopbackRenderPolicy() && canProjectAudio) 605 || (isCallRedirectionPolicy() && canInterceptCallAudio) 606 || canModifyAudioRouting)) { 607 Slog.w(TAG, "Cannot use AudioPolicy for pid " + Binder.getCallingPid() + " / uid " 608 + Binder.getCallingUid() + ", needs MODIFY_AUDIO_ROUTING or " 609 + "MediaProjection that can project audio."); 610 return false; 611 } 612 return true; 613 } 614 isLoopbackRenderPolicy()615 private boolean isLoopbackRenderPolicy() { 616 synchronized (mLock) { 617 return mConfig.mMixes.stream().allMatch(mix -> mix.getRouteFlags() 618 == (mix.ROUTE_FLAG_RENDER | mix.ROUTE_FLAG_LOOP_BACK)); 619 } 620 } 621 isCallRedirectionPolicy()622 private boolean isCallRedirectionPolicy() { 623 synchronized (mLock) { 624 for (AudioMix mix : mConfig.mMixes) { 625 if (mix.isForCallRedirection()) { 626 return true; 627 } 628 } 629 return false; 630 } 631 } 632 633 /** 634 * Returns {@link PackageManager#PERMISSION_GRANTED} if the caller has the given permission. 635 */ checkCallingOrSelfPermission(String permission)636 private @PackageManager.PermissionResult int checkCallingOrSelfPermission(String permission) { 637 if (mContext != null) { 638 return mContext.checkCallingOrSelfPermission(permission); 639 } 640 Slog.v(TAG, "Null context, checking permission via ActivityManager"); 641 int pid = Binder.getCallingPid(); 642 int uid = Binder.getCallingUid(); 643 try { 644 return ActivityManager.getService().checkPermission(permission, pid, uid); 645 } catch (RemoteException e) { 646 throw e.rethrowFromSystemServer(); 647 } 648 } 649 checkMixReadyToUse(AudioMix mix, boolean forTrack)650 private void checkMixReadyToUse(AudioMix mix, boolean forTrack) 651 throws IllegalArgumentException{ 652 if (mix == null) { 653 String msg = forTrack ? "Invalid null AudioMix for AudioTrack creation" 654 : "Invalid null AudioMix for AudioRecord creation"; 655 throw new IllegalArgumentException(msg); 656 } 657 if (!mConfig.mMixes.contains(mix)) { 658 throw new IllegalArgumentException("Invalid mix: not part of this policy"); 659 } 660 if ((mix.getRouteFlags() & AudioMix.ROUTE_FLAG_LOOP_BACK) != AudioMix.ROUTE_FLAG_LOOP_BACK) 661 { 662 throw new IllegalArgumentException("Invalid AudioMix: not defined for loop back"); 663 } 664 if (forTrack && (mix.getMixType() != AudioMix.MIX_TYPE_RECORDERS)) { 665 throw new IllegalArgumentException( 666 "Invalid AudioMix: not defined for being a recording source"); 667 } 668 if (!forTrack && (mix.getMixType() != AudioMix.MIX_TYPE_PLAYERS)) { 669 throw new IllegalArgumentException( 670 "Invalid AudioMix: not defined for capturing playback"); 671 } 672 } 673 674 /** 675 * Returns the current behavior for audio focus-related ducking. 676 * @return {@link #FOCUS_POLICY_DUCKING_IN_APP} or {@link #FOCUS_POLICY_DUCKING_IN_POLICY} 677 */ getFocusDuckingBehavior()678 public int getFocusDuckingBehavior() { 679 return mConfig.mDuckingPolicy; 680 } 681 682 // Note on implementation: not part of the Builder as there can be only one registered policy 683 // that handles ducking but there can be multiple policies 684 /** 685 * Sets the behavior for audio focus-related ducking. 686 * There must be a focus listener if this policy is to handle ducking. 687 * @param behavior {@link #FOCUS_POLICY_DUCKING_IN_APP} or 688 * {@link #FOCUS_POLICY_DUCKING_IN_POLICY} 689 * @return {@link AudioManager#SUCCESS} or {@link AudioManager#ERROR} (for instance if there 690 * is already an audio policy that handles ducking). 691 * @throws IllegalArgumentException 692 * @throws IllegalStateException 693 */ setFocusDuckingBehavior(int behavior)694 public int setFocusDuckingBehavior(int behavior) 695 throws IllegalArgumentException, IllegalStateException { 696 if ((behavior != FOCUS_POLICY_DUCKING_IN_APP) 697 && (behavior != FOCUS_POLICY_DUCKING_IN_POLICY)) { 698 throw new IllegalArgumentException("Invalid ducking behavior " + behavior); 699 } 700 synchronized (mLock) { 701 if (mStatus != POLICY_STATUS_REGISTERED) { 702 throw new IllegalStateException( 703 "Cannot change ducking behavior for unregistered policy"); 704 } 705 if ((behavior == FOCUS_POLICY_DUCKING_IN_POLICY) 706 && (mFocusListener == null)) { 707 // there must be a focus listener if the policy handles ducking 708 throw new IllegalStateException( 709 "Cannot handle ducking without an audio focus listener"); 710 } 711 IAudioService service = getService(); 712 try { 713 final int status = service.setFocusPropertiesForPolicy(behavior /*duckingBehavior*/, 714 this.cb()); 715 if (status == AudioManager.SUCCESS) { 716 mConfig.mDuckingPolicy = behavior; 717 } 718 return status; 719 } catch (RemoteException e) { 720 Log.e(TAG, "Dead object in setFocusPropertiesForPolicy for behavior", e); 721 return AudioManager.ERROR; 722 } 723 } 724 } 725 726 /** 727 * Returns the list of entries in the focus stack. 728 * The list is ordered with increasing rank of focus ownership, where the last entry is at the 729 * top of the focus stack and is the current focus owner. 730 * @return the ordered list of focus owners 731 * @see AudioManager#registerAudioPolicy(AudioPolicy) 732 */ 733 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) getFocusStack()734 public @NonNull List<AudioFocusInfo> getFocusStack() { 735 try { 736 return getService().getFocusStack(); 737 } catch (RemoteException e) { 738 throw e.rethrowFromSystemServer(); 739 } 740 } 741 742 /** 743 * Send AUDIOFOCUS_LOSS to a specific stack entry, causing it to be notified of the focus 744 * loss, and for it to exit the focus stack (its focus listener will not be invoked after that). 745 * This operation is only valid for a registered policy (with 746 * {@link AudioManager#registerAudioPolicy(AudioPolicy)}) that is also set as the policy focus 747 * listener (with {@link Builder#setAudioPolicyFocusListener(AudioPolicyFocusListener)}. 748 * @param focusLoser the stack entry that is exiting the stack through a focus loss 749 * @return false if the focusLoser wasn't found in the stack, true otherwise 750 * @throws IllegalStateException if used on an unregistered policy, or a registered policy 751 * with no {@link AudioPolicyFocusListener} set 752 * @see AudioManager#registerAudioPolicy(AudioPolicy) 753 * @see Builder#setAudioPolicyStatusListener(AudioPolicyStatusListener) 754 */ 755 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) sendFocusLoss(@onNull AudioFocusInfo focusLoser)756 public boolean sendFocusLoss(@NonNull AudioFocusInfo focusLoser) throws IllegalStateException { 757 Objects.requireNonNull(focusLoser); 758 try { 759 return getService().sendFocusLoss(focusLoser, cb()); 760 } catch (RemoteException e) { 761 throw e.rethrowFromSystemServer(); 762 } 763 } 764 765 /** 766 * Create an {@link AudioRecord} instance that is associated with the given {@link AudioMix}. 767 * Audio buffers recorded through the created instance will contain the mix of the audio 768 * streams that fed the given mixer. 769 * @param mix a non-null {@link AudioMix} instance whose routing flags was defined with 770 * {@link AudioMix#ROUTE_FLAG_LOOP_BACK}, previously added to this policy. 771 * @return a new {@link AudioRecord} instance whose data format is the one defined in the 772 * {@link AudioMix}, or null if this policy was not successfully registered 773 * with {@link AudioManager#registerAudioPolicy(AudioPolicy)}. 774 * @throws IllegalArgumentException 775 */ createAudioRecordSink(AudioMix mix)776 public AudioRecord createAudioRecordSink(AudioMix mix) throws IllegalArgumentException { 777 if (!policyReadyToUse()) { 778 Log.e(TAG, "Cannot create AudioRecord sink for AudioMix"); 779 return null; 780 } 781 checkMixReadyToUse(mix, false/*not for an AudioTrack*/); 782 // create an AudioFormat from the mix format compatible with recording, as the mix 783 // was defined for playback 784 AudioFormat mixFormat = new AudioFormat.Builder(mix.getFormat()) 785 .setChannelMask(AudioFormat.inChannelMaskFromOutChannelMask( 786 mix.getFormat().getChannelMask())) 787 .build(); 788 789 AudioAttributes.Builder ab = new AudioAttributes.Builder() 790 .setInternalCapturePreset(MediaRecorder.AudioSource.REMOTE_SUBMIX) 791 .addTag(addressForTag(mix)) 792 .addTag(AudioRecord.SUBMIX_FIXED_VOLUME); 793 if (mix.isForCallRedirection()) { 794 ab.setForCallRedirection(); 795 } 796 // create the AudioRecord, configured for loop back, using the same format as the mix 797 AudioRecord ar = new AudioRecord(ab.build(), 798 mixFormat, 799 AudioRecord.getMinBufferSize(mix.getFormat().getSampleRate(), 800 // using stereo for buffer size to avoid the current poor support for masks 801 AudioFormat.CHANNEL_IN_STEREO, mix.getFormat().getEncoding()), 802 AudioManager.AUDIO_SESSION_ID_GENERATE 803 ); 804 synchronized (mLock) { 805 if (mCaptors == null) { 806 mCaptors = new ArrayList<>(1); 807 } 808 mCaptors.add(new WeakReference<AudioRecord>(ar)); 809 } 810 return ar; 811 } 812 813 /** 814 * Create an {@link AudioTrack} instance that is associated with the given {@link AudioMix}. 815 * Audio buffers played through the created instance will be sent to the given mix 816 * to be recorded through the recording APIs. 817 * @param mix a non-null {@link AudioMix} instance whose routing flags was defined with 818 * {@link AudioMix#ROUTE_FLAG_LOOP_BACK}, previously added to this policy. 819 * @return a new {@link AudioTrack} instance whose data format is the one defined in the 820 * {@link AudioMix}, or null if this policy was not successfully registered 821 * with {@link AudioManager#registerAudioPolicy(AudioPolicy)}. 822 * @throws IllegalArgumentException 823 */ createAudioTrackSource(AudioMix mix)824 public AudioTrack createAudioTrackSource(AudioMix mix) throws IllegalArgumentException { 825 if (!policyReadyToUse()) { 826 Log.e(TAG, "Cannot create AudioTrack source for AudioMix"); 827 return null; 828 } 829 checkMixReadyToUse(mix, true/*for an AudioTrack*/); 830 // create the AudioTrack, configured for loop back, using the same format as the mix 831 AudioAttributes.Builder ab = new AudioAttributes.Builder() 832 .setUsage(AudioAttributes.USAGE_VIRTUAL_SOURCE) 833 .addTag(addressForTag(mix)); 834 if (mix.isForCallRedirection()) { 835 ab.setForCallRedirection(); 836 } 837 AudioTrack at = new AudioTrack(ab.build(), 838 mix.getFormat(), 839 AudioTrack.getMinBufferSize(mix.getFormat().getSampleRate(), 840 mix.getFormat().getChannelMask(), mix.getFormat().getEncoding()), 841 AudioTrack.MODE_STREAM, 842 AudioManager.AUDIO_SESSION_ID_GENERATE 843 ); 844 synchronized (mLock) { 845 if (mInjectors == null) { 846 mInjectors = new ArrayList<>(1); 847 } 848 mInjectors.add(new WeakReference<AudioTrack>(at)); 849 } 850 return at; 851 } 852 853 /** 854 * @hide 855 */ invalidateCaptorsAndInjectors()856 public void invalidateCaptorsAndInjectors() { 857 if (!policyReadyToUse()) { 858 return; 859 } 860 synchronized (mLock) { 861 if (mInjectors != null) { 862 for (final WeakReference<AudioTrack> weakTrack : mInjectors) { 863 final AudioTrack track = weakTrack.get(); 864 if (track == null) { 865 break; 866 } 867 try { 868 // TODO: add synchronous versions 869 track.stop(); 870 track.flush(); 871 } catch (IllegalStateException e) { 872 // ignore exception, AudioTrack could have already been stopped or 873 // released by the user of the AudioPolicy 874 } 875 } 876 } 877 if (mCaptors != null) { 878 for (final WeakReference<AudioRecord> weakRecord : mCaptors) { 879 final AudioRecord record = weakRecord.get(); 880 if (record == null) { 881 break; 882 } 883 try { 884 // TODO: if needed: implement an invalidate method 885 record.stop(); 886 } catch (IllegalStateException e) { 887 // ignore exception, AudioRecord could have already been stopped or 888 // released by the user of the AudioPolicy 889 } 890 } 891 } 892 } 893 } 894 getStatus()895 public int getStatus() { 896 return mStatus; 897 } 898 899 public static abstract class AudioPolicyStatusListener { onStatusChange()900 public void onStatusChange() {} onMixStateUpdate(AudioMix mix)901 public void onMixStateUpdate(AudioMix mix) {} 902 } 903 904 public static abstract class AudioPolicyFocusListener { onAudioFocusGrant(AudioFocusInfo afi, int requestResult)905 public void onAudioFocusGrant(AudioFocusInfo afi, int requestResult) {} onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified)906 public void onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) {} 907 /** 908 * Called whenever an application requests audio focus. 909 * Only ever called if the {@link AudioPolicy} was built with 910 * {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to {@code true}. 911 * @param afi information about the focus request and the requester 912 * @param requestResult deprecated after the addition of 913 * {@link AudioManager#setFocusRequestResult(AudioFocusInfo, int, AudioPolicy)} 914 * in Android P, always equal to {@link #AUDIOFOCUS_REQUEST_GRANTED}. 915 */ onAudioFocusRequest(AudioFocusInfo afi, int requestResult)916 public void onAudioFocusRequest(AudioFocusInfo afi, int requestResult) {} 917 /** 918 * Called whenever an application abandons audio focus. 919 * Only ever called if the {@link AudioPolicy} was built with 920 * {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to {@code true}. 921 * @param afi information about the focus request being abandoned and the original 922 * requester. 923 */ onAudioFocusAbandon(AudioFocusInfo afi)924 public void onAudioFocusAbandon(AudioFocusInfo afi) {} 925 } 926 927 /** 928 * Callback class to receive volume change-related events. 929 * See {@link #Builder.setAudioPolicyVolumeCallback(AudioPolicyCallback)} to configure the 930 * {@link AudioPolicy} to receive those events. 931 * 932 */ 933 public static abstract class AudioPolicyVolumeCallback { AudioPolicyVolumeCallback()934 public AudioPolicyVolumeCallback() {} 935 /** 936 * Called when volume key-related changes are triggered, on the key down event. 937 * @param adjustment the type of volume adjustment for the key. 938 */ onVolumeAdjustment(@udioManager.VolumeAdjustment int adjustment)939 public void onVolumeAdjustment(@AudioManager.VolumeAdjustment int adjustment) {} 940 } 941 onPolicyStatusChange()942 private void onPolicyStatusChange() { 943 AudioPolicyStatusListener l; 944 synchronized (mLock) { 945 if (mStatusListener == null) { 946 return; 947 } 948 l = mStatusListener; 949 } 950 l.onStatusChange(); 951 } 952 953 //================================================== 954 // Callback interface 955 956 /** @hide */ cb()957 public IAudioPolicyCallback cb() { return mPolicyCb; } 958 959 private final IAudioPolicyCallback mPolicyCb = new IAudioPolicyCallback.Stub() { 960 961 public void notifyAudioFocusGrant(AudioFocusInfo afi, int requestResult) { 962 sendMsg(MSG_FOCUS_GRANT, afi, requestResult); 963 if (DEBUG) { 964 Log.v(TAG, "notifyAudioFocusGrant: pack=" + afi.getPackageName() + " client=" 965 + afi.getClientId() + "reqRes=" + requestResult); 966 } 967 } 968 969 public void notifyAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) { 970 sendMsg(MSG_FOCUS_LOSS, afi, wasNotified ? 1 : 0); 971 if (DEBUG) { 972 Log.v(TAG, "notifyAudioFocusLoss: pack=" + afi.getPackageName() + " client=" 973 + afi.getClientId() + "wasNotified=" + wasNotified); 974 } 975 } 976 977 public void notifyAudioFocusRequest(AudioFocusInfo afi, int requestResult) { 978 sendMsg(MSG_FOCUS_REQUEST, afi, requestResult); 979 if (DEBUG) { 980 Log.v(TAG, "notifyAudioFocusRequest: pack=" + afi.getPackageName() + " client=" 981 + afi.getClientId() + " gen=" + afi.getGen()); 982 } 983 } 984 985 public void notifyAudioFocusAbandon(AudioFocusInfo afi) { 986 sendMsg(MSG_FOCUS_ABANDON, afi, 0 /* ignored */); 987 if (DEBUG) { 988 Log.v(TAG, "notifyAudioFocusAbandon: pack=" + afi.getPackageName() + " client=" 989 + afi.getClientId()); 990 } 991 } 992 993 public void notifyMixStateUpdate(String regId, int state) { 994 for (AudioMix mix : mConfig.getMixes()) { 995 if (mix.getRegistration().equals(regId)) { 996 mix.mMixState = state; 997 sendMsg(MSG_MIX_STATE_UPDATE, mix, 0/*ignored*/); 998 if (DEBUG) { 999 Log.v(TAG, "notifyMixStateUpdate: regId=" + regId + " state=" + state); 1000 } 1001 } 1002 } 1003 } 1004 1005 public void notifyVolumeAdjust(int adjustment) { 1006 sendMsg(MSG_VOL_ADJUST, null /* ignored */, adjustment); 1007 if (DEBUG) { 1008 Log.v(TAG, "notifyVolumeAdjust: " + adjustment); 1009 } 1010 } 1011 1012 public void notifyUnregistration() { 1013 setRegistration(null); 1014 } 1015 }; 1016 1017 //================================================== 1018 // Event handling 1019 private final EventHandler mEventHandler; 1020 private final static int MSG_POLICY_STATUS_CHANGE = 0; 1021 private final static int MSG_FOCUS_GRANT = 1; 1022 private final static int MSG_FOCUS_LOSS = 2; 1023 private final static int MSG_MIX_STATE_UPDATE = 3; 1024 private final static int MSG_FOCUS_REQUEST = 4; 1025 private final static int MSG_FOCUS_ABANDON = 5; 1026 private final static int MSG_VOL_ADJUST = 6; 1027 1028 private class EventHandler extends Handler { EventHandler(AudioPolicy ap, Looper looper)1029 public EventHandler(AudioPolicy ap, Looper looper) { 1030 super(looper); 1031 } 1032 1033 @Override handleMessage(Message msg)1034 public void handleMessage(Message msg) { 1035 switch(msg.what) { 1036 case MSG_POLICY_STATUS_CHANGE: 1037 onPolicyStatusChange(); 1038 break; 1039 case MSG_FOCUS_GRANT: 1040 if (mFocusListener != null) { 1041 mFocusListener.onAudioFocusGrant( 1042 (AudioFocusInfo) msg.obj, msg.arg1); 1043 } 1044 break; 1045 case MSG_FOCUS_LOSS: 1046 if (mFocusListener != null) { 1047 mFocusListener.onAudioFocusLoss( 1048 (AudioFocusInfo) msg.obj, msg.arg1 != 0); 1049 } 1050 break; 1051 case MSG_MIX_STATE_UPDATE: 1052 if (mStatusListener != null) { 1053 mStatusListener.onMixStateUpdate((AudioMix) msg.obj); 1054 } 1055 break; 1056 case MSG_FOCUS_REQUEST: 1057 if (mFocusListener != null) { 1058 mFocusListener.onAudioFocusRequest((AudioFocusInfo) msg.obj, msg.arg1); 1059 } else { // should never be null, but don't crash 1060 Log.e(TAG, "Invalid null focus listener for focus request event"); 1061 } 1062 break; 1063 case MSG_FOCUS_ABANDON: 1064 if (mFocusListener != null) { // should never be null 1065 mFocusListener.onAudioFocusAbandon((AudioFocusInfo) msg.obj); 1066 } else { // should never be null, but don't crash 1067 Log.e(TAG, "Invalid null focus listener for focus abandon event"); 1068 } 1069 break; 1070 case MSG_VOL_ADJUST: 1071 if (mVolCb != null) { 1072 mVolCb.onVolumeAdjustment(msg.arg1); 1073 } else { // should never be null, but don't crash 1074 Log.e(TAG, "Invalid null volume event"); 1075 } 1076 break; 1077 default: 1078 Log.e(TAG, "Unknown event " + msg.what); 1079 } 1080 } 1081 } 1082 1083 //========================================================== 1084 // Utils addressForTag(AudioMix mix)1085 private static String addressForTag(AudioMix mix) { 1086 return "addr=" + mix.getRegistration(); 1087 } 1088 sendMsg(int msg)1089 private void sendMsg(int msg) { 1090 if (mEventHandler != null) { 1091 mEventHandler.sendEmptyMessage(msg); 1092 } 1093 } 1094 sendMsg(int msg, Object obj, int i)1095 private void sendMsg(int msg, Object obj, int i) { 1096 if (mEventHandler != null) { 1097 mEventHandler.sendMessage( 1098 mEventHandler.obtainMessage(msg, i /*arg1*/, 0 /*arg2, ignored*/, obj)); 1099 } 1100 } 1101 1102 private static IAudioService sService; 1103 getService()1104 private static IAudioService getService() 1105 { 1106 if (sService != null) { 1107 return sService; 1108 } 1109 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); 1110 sService = IAudioService.Stub.asInterface(b); 1111 return sService; 1112 } 1113 toLogFriendlyString()1114 public String toLogFriendlyString() { 1115 String textDump = new String("android.media.audiopolicy.AudioPolicy:\n"); 1116 textDump += "config=" + mConfig.toLogFriendlyString(); 1117 return (textDump); 1118 } 1119 1120 /** @hide */ 1121 @IntDef({ 1122 POLICY_STATUS_REGISTERED, 1123 POLICY_STATUS_UNREGISTERED 1124 }) 1125 @Retention(RetentionPolicy.SOURCE) 1126 public @interface PolicyStatus {} 1127 } 1128