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