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