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.SystemApi; 22 import android.content.Context; 23 import android.content.pm.PackageManager; 24 import android.media.AudioAttributes; 25 import android.media.AudioFocusInfo; 26 import android.media.AudioFormat; 27 import android.media.AudioManager; 28 import android.media.AudioRecord; 29 import android.media.AudioTrack; 30 import android.media.IAudioService; 31 import android.media.MediaRecorder; 32 import android.os.Binder; 33 import android.os.Handler; 34 import android.os.IBinder; 35 import android.os.Looper; 36 import android.os.Message; 37 import android.os.RemoteException; 38 import android.os.ServiceManager; 39 import android.util.Log; 40 import android.util.Slog; 41 42 import java.lang.annotation.Retention; 43 import java.lang.annotation.RetentionPolicy; 44 import java.util.ArrayList; 45 46 /** 47 * @hide 48 * AudioPolicy provides access to the management of audio routing and audio focus. 49 */ 50 @SystemApi 51 public class AudioPolicy { 52 53 private static final String TAG = "AudioPolicy"; 54 private static final boolean DEBUG = false; 55 private final Object mLock = new Object(); 56 57 /** 58 * The status of an audio policy that is valid but cannot be used because it is not registered. 59 */ 60 @SystemApi 61 public static final int POLICY_STATUS_UNREGISTERED = 1; 62 /** 63 * The status of an audio policy that is valid, successfully registered and thus active. 64 */ 65 @SystemApi 66 public static final int POLICY_STATUS_REGISTERED = 2; 67 68 private int mStatus; 69 private String mRegistrationId; 70 private AudioPolicyStatusListener mStatusListener; 71 private boolean mIsFocusPolicy; 72 73 /** 74 * The behavior of a policy with regards to audio focus where it relies on the application 75 * to do the ducking, the is the legacy and default behavior. 76 */ 77 @SystemApi 78 public static final int FOCUS_POLICY_DUCKING_IN_APP = 0; 79 public static final int FOCUS_POLICY_DUCKING_DEFAULT = FOCUS_POLICY_DUCKING_IN_APP; 80 /** 81 * The behavior of a policy with regards to audio focus where it handles ducking instead 82 * of the application losing focus and being signaled it can duck (as communicated by 83 * {@link android.media.AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}). 84 * <br>Can only be used after having set a listener with 85 * {@link AudioPolicy#setAudioPolicyFocusListener(AudioPolicyFocusListener)}. 86 */ 87 @SystemApi 88 public static final int FOCUS_POLICY_DUCKING_IN_POLICY = 1; 89 90 private AudioPolicyFocusListener mFocusListener; 91 92 private Context mContext; 93 94 private AudioPolicyConfig mConfig; 95 96 /** @hide */ getConfig()97 public AudioPolicyConfig getConfig() { return mConfig; } 98 /** @hide */ hasFocusListener()99 public boolean hasFocusListener() { return mFocusListener != null; } 100 /** @hide */ isFocusPolicy()101 public boolean isFocusPolicy() { return mIsFocusPolicy; } 102 103 /** 104 * The parameter is guaranteed non-null through the Builder 105 */ AudioPolicy(AudioPolicyConfig config, Context context, Looper looper, AudioPolicyFocusListener fl, AudioPolicyStatusListener sl, boolean isFocusPolicy)106 private AudioPolicy(AudioPolicyConfig config, Context context, Looper looper, 107 AudioPolicyFocusListener fl, AudioPolicyStatusListener sl, boolean isFocusPolicy) { 108 mConfig = config; 109 mStatus = POLICY_STATUS_UNREGISTERED; 110 mContext = context; 111 if (looper == null) { 112 looper = Looper.getMainLooper(); 113 } 114 if (looper != null) { 115 mEventHandler = new EventHandler(this, looper); 116 } else { 117 mEventHandler = null; 118 Log.e(TAG, "No event handler due to looper without a thread"); 119 } 120 mFocusListener = fl; 121 mStatusListener = sl; 122 mIsFocusPolicy = isFocusPolicy; 123 } 124 125 /** 126 * Builder class for {@link AudioPolicy} objects. 127 * By default the policy to be created doesn't govern audio focus decisions. 128 */ 129 @SystemApi 130 public static class Builder { 131 private ArrayList<AudioMix> mMixes; 132 private Context mContext; 133 private Looper mLooper; 134 private AudioPolicyFocusListener mFocusListener; 135 private AudioPolicyStatusListener mStatusListener; 136 private boolean mIsFocusPolicy = false; 137 138 /** 139 * Constructs a new Builder with no audio mixes. 140 * @param context the context for the policy 141 */ 142 @SystemApi Builder(Context context)143 public Builder(Context context) { 144 mMixes = new ArrayList<AudioMix>(); 145 mContext = context; 146 } 147 148 /** 149 * Add an {@link AudioMix} to be part of the audio policy being built. 150 * @param mix a non-null {@link AudioMix} to be part of the audio policy. 151 * @return the same Builder instance. 152 * @throws IllegalArgumentException 153 */ 154 @SystemApi addMix(@onNull AudioMix mix)155 public Builder addMix(@NonNull AudioMix mix) throws IllegalArgumentException { 156 if (mix == null) { 157 throw new IllegalArgumentException("Illegal null AudioMix argument"); 158 } 159 mMixes.add(mix); 160 return this; 161 } 162 163 /** 164 * Sets the {@link Looper} on which to run the event loop. 165 * @param looper a non-null specific Looper. 166 * @return the same Builder instance. 167 * @throws IllegalArgumentException 168 */ 169 @SystemApi setLooper(@onNull Looper looper)170 public Builder setLooper(@NonNull Looper looper) throws IllegalArgumentException { 171 if (looper == null) { 172 throw new IllegalArgumentException("Illegal null Looper argument"); 173 } 174 mLooper = looper; 175 return this; 176 } 177 178 /** 179 * Sets the audio focus listener for the policy. 180 * @param l a {@link AudioPolicy.AudioPolicyFocusListener} 181 */ 182 @SystemApi setAudioPolicyFocusListener(AudioPolicyFocusListener l)183 public void setAudioPolicyFocusListener(AudioPolicyFocusListener l) { 184 mFocusListener = l; 185 } 186 187 /** 188 * Declares whether this policy will grant and deny audio focus through 189 * the {@link AudioPolicy.AudioPolicyStatusListener}. 190 * If set to {@code true}, it is mandatory to set an 191 * {@link AudioPolicy.AudioPolicyStatusListener} in order to successfully build 192 * an {@code AudioPolicy} instance. 193 * @param enforce true if the policy will govern audio focus decisions. 194 * @return the same Builder instance. 195 */ 196 @SystemApi setIsAudioFocusPolicy(boolean isFocusPolicy)197 public Builder setIsAudioFocusPolicy(boolean isFocusPolicy) { 198 mIsFocusPolicy = isFocusPolicy; 199 return this; 200 } 201 202 /** 203 * Sets the audio policy status listener. 204 * @param l a {@link AudioPolicy.AudioPolicyStatusListener} 205 */ 206 @SystemApi setAudioPolicyStatusListener(AudioPolicyStatusListener l)207 public void setAudioPolicyStatusListener(AudioPolicyStatusListener l) { 208 mStatusListener = l; 209 } 210 211 /** 212 * Combines all of the attributes that have been set on this {@code Builder} and returns a 213 * new {@link AudioPolicy} object. 214 * @return a new {@code AudioPolicy} object. 215 * @throws IllegalStateException if there is no 216 * {@link AudioPolicy.AudioPolicyStatusListener} but the policy was configured 217 * as an audio focus policy with {@link #setIsAudioFocusPolicy(boolean)}. 218 */ 219 @SystemApi build()220 public AudioPolicy build() { 221 if (mStatusListener != null) { 222 // the AudioPolicy status listener includes updates on each mix activity state 223 for (AudioMix mix : mMixes) { 224 mix.mCallbackFlags |= AudioMix.CALLBACK_FLAG_NOTIFY_ACTIVITY; 225 } 226 } 227 if (mIsFocusPolicy && mFocusListener == null) { 228 throw new IllegalStateException("Cannot be a focus policy without " 229 + "an AudioPolicyFocusListener"); 230 } 231 return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext, mLooper, 232 mFocusListener, mStatusListener, mIsFocusPolicy); 233 } 234 } 235 setRegistration(String regId)236 public void setRegistration(String regId) { 237 synchronized (mLock) { 238 mRegistrationId = regId; 239 mConfig.setRegistration(regId); 240 if (regId != null) { 241 mStatus = POLICY_STATUS_REGISTERED; 242 } else { 243 mStatus = POLICY_STATUS_UNREGISTERED; 244 } 245 } 246 sendMsg(MSG_POLICY_STATUS_CHANGE); 247 } 248 policyReadyToUse()249 private boolean policyReadyToUse() { 250 synchronized (mLock) { 251 if (mStatus != POLICY_STATUS_REGISTERED) { 252 Log.e(TAG, "Cannot use unregistered AudioPolicy"); 253 return false; 254 } 255 if (mContext == null) { 256 Log.e(TAG, "Cannot use AudioPolicy without context"); 257 return false; 258 } 259 if (mRegistrationId == null) { 260 Log.e(TAG, "Cannot use unregistered AudioPolicy"); 261 return false; 262 } 263 } 264 if (!(PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission( 265 android.Manifest.permission.MODIFY_AUDIO_ROUTING))) { 266 Slog.w(TAG, "Cannot use AudioPolicy for pid " + Binder.getCallingPid() + " / uid " 267 + Binder.getCallingUid() + ", needs MODIFY_AUDIO_ROUTING"); 268 return false; 269 } 270 return true; 271 } 272 checkMixReadyToUse(AudioMix mix, boolean forTrack)273 private void checkMixReadyToUse(AudioMix mix, boolean forTrack) 274 throws IllegalArgumentException{ 275 if (mix == null) { 276 String msg = forTrack ? "Invalid null AudioMix for AudioTrack creation" 277 : "Invalid null AudioMix for AudioRecord creation"; 278 throw new IllegalArgumentException(msg); 279 } 280 if (!mConfig.mMixes.contains(mix)) { 281 throw new IllegalArgumentException("Invalid mix: not part of this policy"); 282 } 283 if ((mix.getRouteFlags() & AudioMix.ROUTE_FLAG_LOOP_BACK) != AudioMix.ROUTE_FLAG_LOOP_BACK) 284 { 285 throw new IllegalArgumentException("Invalid AudioMix: not defined for loop back"); 286 } 287 if (forTrack && (mix.getMixType() != AudioMix.MIX_TYPE_RECORDERS)) { 288 throw new IllegalArgumentException( 289 "Invalid AudioMix: not defined for being a recording source"); 290 } 291 if (!forTrack && (mix.getMixType() != AudioMix.MIX_TYPE_PLAYERS)) { 292 throw new IllegalArgumentException( 293 "Invalid AudioMix: not defined for capturing playback"); 294 } 295 } 296 297 /** 298 * Returns the current behavior for audio focus-related ducking. 299 * @return {@link #FOCUS_POLICY_DUCKING_IN_APP} or {@link #FOCUS_POLICY_DUCKING_IN_POLICY} 300 */ 301 @SystemApi getFocusDuckingBehavior()302 public int getFocusDuckingBehavior() { 303 return mConfig.mDuckingPolicy; 304 } 305 306 // Note on implementation: not part of the Builder as there can be only one registered policy 307 // that handles ducking but there can be multiple policies 308 /** 309 * Sets the behavior for audio focus-related ducking. 310 * There must be a focus listener if this policy is to handle ducking. 311 * @param behavior {@link #FOCUS_POLICY_DUCKING_IN_APP} or 312 * {@link #FOCUS_POLICY_DUCKING_IN_POLICY} 313 * @return {@link AudioManager#SUCCESS} or {@link AudioManager#ERROR} (for instance if there 314 * is already an audio policy that handles ducking). 315 * @throws IllegalArgumentException 316 * @throws IllegalStateException 317 */ 318 @SystemApi setFocusDuckingBehavior(int behavior)319 public int setFocusDuckingBehavior(int behavior) 320 throws IllegalArgumentException, IllegalStateException { 321 if ((behavior != FOCUS_POLICY_DUCKING_IN_APP) 322 && (behavior != FOCUS_POLICY_DUCKING_IN_POLICY)) { 323 throw new IllegalArgumentException("Invalid ducking behavior " + behavior); 324 } 325 synchronized (mLock) { 326 if (mStatus != POLICY_STATUS_REGISTERED) { 327 throw new IllegalStateException( 328 "Cannot change ducking behavior for unregistered policy"); 329 } 330 if ((behavior == FOCUS_POLICY_DUCKING_IN_POLICY) 331 && (mFocusListener == null)) { 332 // there must be a focus listener if the policy handles ducking 333 throw new IllegalStateException( 334 "Cannot handle ducking without an audio focus listener"); 335 } 336 IAudioService service = getService(); 337 try { 338 final int status = service.setFocusPropertiesForPolicy(behavior /*duckingBehavior*/, 339 this.cb()); 340 if (status == AudioManager.SUCCESS) { 341 mConfig.mDuckingPolicy = behavior; 342 } 343 return status; 344 } catch (RemoteException e) { 345 Log.e(TAG, "Dead object in setFocusPropertiesForPolicy for behavior", e); 346 return AudioManager.ERROR; 347 } 348 } 349 } 350 351 /** 352 * Create an {@link AudioRecord} instance that is associated with the given {@link AudioMix}. 353 * Audio buffers recorded through the created instance will contain the mix of the audio 354 * streams that fed the given mixer. 355 * @param mix a non-null {@link AudioMix} instance whose routing flags was defined with 356 * {@link AudioMix#ROUTE_FLAG_LOOP_BACK}, previously added to this policy. 357 * @return a new {@link AudioRecord} instance whose data format is the one defined in the 358 * {@link AudioMix}, or null if this policy was not successfully registered 359 * with {@link AudioManager#registerAudioPolicy(AudioPolicy)}. 360 * @throws IllegalArgumentException 361 */ 362 @SystemApi createAudioRecordSink(AudioMix mix)363 public AudioRecord createAudioRecordSink(AudioMix mix) throws IllegalArgumentException { 364 if (!policyReadyToUse()) { 365 Log.e(TAG, "Cannot create AudioRecord sink for AudioMix"); 366 return null; 367 } 368 checkMixReadyToUse(mix, false/*not for an AudioTrack*/); 369 // create an AudioFormat from the mix format compatible with recording, as the mix 370 // was defined for playback 371 AudioFormat mixFormat = new AudioFormat.Builder(mix.getFormat()) 372 .setChannelMask(AudioFormat.inChannelMaskFromOutChannelMask( 373 mix.getFormat().getChannelMask())) 374 .build(); 375 // create the AudioRecord, configured for loop back, using the same format as the mix 376 AudioRecord ar = new AudioRecord( 377 new AudioAttributes.Builder() 378 .setInternalCapturePreset(MediaRecorder.AudioSource.REMOTE_SUBMIX) 379 .addTag(addressForTag(mix)) 380 .build(), 381 mixFormat, 382 AudioRecord.getMinBufferSize(mix.getFormat().getSampleRate(), 383 // using stereo for buffer size to avoid the current poor support for masks 384 AudioFormat.CHANNEL_IN_STEREO, mix.getFormat().getEncoding()), 385 AudioManager.AUDIO_SESSION_ID_GENERATE 386 ); 387 return ar; 388 } 389 390 /** 391 * Create an {@link AudioTrack} instance that is associated with the given {@link AudioMix}. 392 * Audio buffers played through the created instance will be sent to the given mix 393 * to be recorded through the recording APIs. 394 * @param mix a non-null {@link AudioMix} instance whose routing flags was defined with 395 * {@link AudioMix#ROUTE_FLAG_LOOP_BACK}, previously added to this policy. 396 * @return a new {@link AudioTrack} instance whose data format is the one defined in the 397 * {@link AudioMix}, or null if this policy was not successfully registered 398 * with {@link AudioManager#registerAudioPolicy(AudioPolicy)}. 399 * @throws IllegalArgumentException 400 */ 401 @SystemApi createAudioTrackSource(AudioMix mix)402 public AudioTrack createAudioTrackSource(AudioMix mix) throws IllegalArgumentException { 403 if (!policyReadyToUse()) { 404 Log.e(TAG, "Cannot create AudioTrack source for AudioMix"); 405 return null; 406 } 407 checkMixReadyToUse(mix, true/*for an AudioTrack*/); 408 // create the AudioTrack, configured for loop back, using the same format as the mix 409 AudioTrack at = new AudioTrack( 410 new AudioAttributes.Builder() 411 .setUsage(AudioAttributes.USAGE_VIRTUAL_SOURCE) 412 .addTag(addressForTag(mix)) 413 .build(), 414 mix.getFormat(), 415 AudioTrack.getMinBufferSize(mix.getFormat().getSampleRate(), 416 mix.getFormat().getChannelMask(), mix.getFormat().getEncoding()), 417 AudioTrack.MODE_STREAM, 418 AudioManager.AUDIO_SESSION_ID_GENERATE 419 ); 420 return at; 421 } 422 423 @SystemApi getStatus()424 public int getStatus() { 425 return mStatus; 426 } 427 428 @SystemApi 429 public static abstract class AudioPolicyStatusListener { onStatusChange()430 public void onStatusChange() {} onMixStateUpdate(AudioMix mix)431 public void onMixStateUpdate(AudioMix mix) {} 432 } 433 434 @SystemApi 435 public static abstract class AudioPolicyFocusListener { onAudioFocusGrant(AudioFocusInfo afi, int requestResult)436 public void onAudioFocusGrant(AudioFocusInfo afi, int requestResult) {} onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified)437 public void onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) {} 438 /** 439 * Called whenever an application requests audio focus. 440 * Only ever called if the {@link AudioPolicy} was built with 441 * {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to {@code true}. 442 * @param afi information about the focus request and the requester 443 * @param requestResult the result that was returned synchronously by the framework to the 444 * application, {@link #AUDIOFOCUS_REQUEST_FAILED},or 445 * {@link #AUDIOFOCUS_REQUEST_DELAYED}. 446 */ onAudioFocusRequest(AudioFocusInfo afi, int requestResult)447 public void onAudioFocusRequest(AudioFocusInfo afi, int requestResult) {} 448 /** 449 * Called whenever an application abandons audio focus. 450 * Only ever called if the {@link AudioPolicy} was built with 451 * {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to {@code true}. 452 * @param afi information about the focus request being abandoned and the original 453 * requester. 454 */ onAudioFocusAbandon(AudioFocusInfo afi)455 public void onAudioFocusAbandon(AudioFocusInfo afi) {} 456 } 457 onPolicyStatusChange()458 private void onPolicyStatusChange() { 459 AudioPolicyStatusListener l; 460 synchronized (mLock) { 461 if (mStatusListener == null) { 462 return; 463 } 464 l = mStatusListener; 465 } 466 l.onStatusChange(); 467 } 468 469 //================================================== 470 // Callback interface 471 472 /** @hide */ cb()473 public IAudioPolicyCallback cb() { return mPolicyCb; } 474 475 private final IAudioPolicyCallback mPolicyCb = new IAudioPolicyCallback.Stub() { 476 477 public void notifyAudioFocusGrant(AudioFocusInfo afi, int requestResult) { 478 sendMsg(MSG_FOCUS_GRANT, afi, requestResult); 479 if (DEBUG) { 480 Log.v(TAG, "notifyAudioFocusGrant: pack=" + afi.getPackageName() + " client=" 481 + afi.getClientId() + "reqRes=" + requestResult); 482 } 483 } 484 485 public void notifyAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) { 486 sendMsg(MSG_FOCUS_LOSS, afi, wasNotified ? 1 : 0); 487 if (DEBUG) { 488 Log.v(TAG, "notifyAudioFocusLoss: pack=" + afi.getPackageName() + " client=" 489 + afi.getClientId() + "wasNotified=" + wasNotified); 490 } 491 } 492 493 public void notifyAudioFocusRequest(AudioFocusInfo afi, int requestResult) { 494 sendMsg(MSG_FOCUS_REQUEST, afi, requestResult); 495 if (DEBUG) { 496 Log.v(TAG, "notifyAudioFocusRequest: pack=" + afi.getPackageName() + " client=" 497 + afi.getClientId() + "reqRes=" + requestResult); 498 } 499 } 500 501 public void notifyAudioFocusAbandon(AudioFocusInfo afi) { 502 sendMsg(MSG_FOCUS_ABANDON, afi, 0 /* ignored */); 503 if (DEBUG) { 504 Log.v(TAG, "notifyAudioFocusAbandon: pack=" + afi.getPackageName() + " client=" 505 + afi.getClientId()); 506 } 507 } 508 509 public void notifyMixStateUpdate(String regId, int state) { 510 for (AudioMix mix : mConfig.getMixes()) { 511 if (mix.getRegistration().equals(regId)) { 512 mix.mMixState = state; 513 sendMsg(MSG_MIX_STATE_UPDATE, mix, 0/*ignored*/); 514 if (DEBUG) { 515 Log.v(TAG, "notifyMixStateUpdate: regId=" + regId + " state=" + state); 516 } 517 } 518 } 519 } 520 }; 521 522 //================================================== 523 // Event handling 524 private final EventHandler mEventHandler; 525 private final static int MSG_POLICY_STATUS_CHANGE = 0; 526 private final static int MSG_FOCUS_GRANT = 1; 527 private final static int MSG_FOCUS_LOSS = 2; 528 private final static int MSG_MIX_STATE_UPDATE = 3; 529 private final static int MSG_FOCUS_REQUEST = 4; 530 private final static int MSG_FOCUS_ABANDON = 5; 531 532 private class EventHandler extends Handler { EventHandler(AudioPolicy ap, Looper looper)533 public EventHandler(AudioPolicy ap, Looper looper) { 534 super(looper); 535 } 536 537 @Override handleMessage(Message msg)538 public void handleMessage(Message msg) { 539 switch(msg.what) { 540 case MSG_POLICY_STATUS_CHANGE: 541 onPolicyStatusChange(); 542 break; 543 case MSG_FOCUS_GRANT: 544 if (mFocusListener != null) { 545 mFocusListener.onAudioFocusGrant( 546 (AudioFocusInfo) msg.obj, msg.arg1); 547 } 548 break; 549 case MSG_FOCUS_LOSS: 550 if (mFocusListener != null) { 551 mFocusListener.onAudioFocusLoss( 552 (AudioFocusInfo) msg.obj, msg.arg1 != 0); 553 } 554 break; 555 case MSG_MIX_STATE_UPDATE: 556 if (mStatusListener != null) { 557 mStatusListener.onMixStateUpdate((AudioMix) msg.obj); 558 } 559 break; 560 case MSG_FOCUS_REQUEST: 561 if (mFocusListener != null) { 562 mFocusListener.onAudioFocusRequest((AudioFocusInfo) msg.obj, msg.arg1); 563 } else { // should never be null, but don't crash 564 Log.e(TAG, "Invalid null focus listener for focus request event"); 565 } 566 break; 567 case MSG_FOCUS_ABANDON: 568 if (mFocusListener != null) { // should never be null 569 mFocusListener.onAudioFocusAbandon((AudioFocusInfo) msg.obj); 570 } else { // should never be null, but don't crash 571 Log.e(TAG, "Invalid null focus listener for focus abandon event"); 572 } 573 break; 574 default: 575 Log.e(TAG, "Unknown event " + msg.what); 576 } 577 } 578 } 579 580 //========================================================== 581 // Utils addressForTag(AudioMix mix)582 private static String addressForTag(AudioMix mix) { 583 return "addr=" + mix.getRegistration(); 584 } 585 sendMsg(int msg)586 private void sendMsg(int msg) { 587 if (mEventHandler != null) { 588 mEventHandler.sendEmptyMessage(msg); 589 } 590 } 591 sendMsg(int msg, Object obj, int i)592 private void sendMsg(int msg, Object obj, int i) { 593 if (mEventHandler != null) { 594 mEventHandler.sendMessage( 595 mEventHandler.obtainMessage(msg, i /*arg1*/, 0 /*arg2, ignored*/, obj)); 596 } 597 } 598 599 private static IAudioService sService; 600 getService()601 private static IAudioService getService() 602 { 603 if (sService != null) { 604 return sService; 605 } 606 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); 607 sService = IAudioService.Stub.asInterface(b); 608 return sService; 609 } 610 toLogFriendlyString()611 public String toLogFriendlyString() { 612 String textDump = new String("android.media.audiopolicy.AudioPolicy:\n"); 613 textDump += "config=" + mConfig.toLogFriendlyString(); 614 return (textDump); 615 } 616 617 /** @hide */ 618 @IntDef({ 619 POLICY_STATUS_REGISTERED, 620 POLICY_STATUS_UNREGISTERED 621 }) 622 @Retention(RetentionPolicy.SOURCE) 623 public @interface PolicyStatus {} 624 } 625