First implementation of the audio focus management as an extension
of AudioManager and AudioService.
This commit is contained in:
@@ -167,6 +167,7 @@ LOCAL_SRC_FILES += \
|
||||
location/java/android/location/ILocationProvider.aidl \
|
||||
location/java/android/location/INetInitiatedListener.aidl \
|
||||
media/java/android/media/IAudioService.aidl \
|
||||
media/java/android/media/IAudioFocusDispatcher.aidl \
|
||||
media/java/android/media/IMediaScannerListener.aidl \
|
||||
media/java/android/media/IMediaScannerService.aidl \
|
||||
telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \
|
||||
|
||||
@@ -23,11 +23,16 @@ import android.database.ContentObserver;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* AudioManager provides access to volume and ringer mode control.
|
||||
* <p>
|
||||
@@ -1126,6 +1131,241 @@ public class AudioManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO unhide for SDK
|
||||
* Used to indicate a loss of audio focus of unknown duration.
|
||||
* @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
|
||||
* {@hide}
|
||||
*/
|
||||
public static final int AUDIOFOCUS_LOSS = -1;
|
||||
/**
|
||||
* TODO unhide for SDK
|
||||
* Used to indicate a transient loss of audio focus.
|
||||
* @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
|
||||
* {@hide}
|
||||
*/
|
||||
public static final int AUDIOFOCUS_LOSS_TRANSIENT = -2;
|
||||
/**
|
||||
* TODO unhide for SDK
|
||||
* Used to indicate a gain of audio focus, or a request of audio focus, of unknown duration.
|
||||
* @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
|
||||
* @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
|
||||
* {@hide}
|
||||
*/
|
||||
public static final int AUDIOFOCUS_GAIN = 1;
|
||||
/**
|
||||
* TODO unhide for SDK
|
||||
* Used to indicate a temporary gain or request of audio focus, anticipated to last a short
|
||||
* amount of time. Examples of temporary changes are the playback of driving directions, or an
|
||||
* event notification.
|
||||
* @see OnAudioFocusChangeListener#onAudioFocusChanged(int)
|
||||
* @see #requestAudioFocus(OnAudioFocusChangeListener, int, int)
|
||||
* {@hide}
|
||||
*/
|
||||
public static final int AUDIOFOCUS_GAIN_TRANSIENT = 2;
|
||||
|
||||
/**
|
||||
* TODO unhide for SDK
|
||||
* {@hide}
|
||||
* Interface definition for a callback to be invoked when the audio focus of the system is
|
||||
* updated.
|
||||
*/
|
||||
public interface OnAudioFocusChangeListener {
|
||||
/**
|
||||
* Called on the listener to notify it the audio focus for this listener has been changed.
|
||||
* The focusChange value indicates whether the focus was gained,
|
||||
* whether the focus was lost, and whether that loss is transient, or whether the new focus
|
||||
* holder will hold it for an unknown amount of time.
|
||||
* When losing focus, listeners can use the duration hint to decide what
|
||||
* behavior to adopt when losing focus. A music player could for instance elect to duck its
|
||||
* music stream for transient focus losses, and pause otherwise.
|
||||
* @param focusChange one of {@link AudioManager#AUDIOFOCUS_GAIN},
|
||||
* {@link AudioManager#AUDIOFOCUS_LOSS}, {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT}.
|
||||
*/
|
||||
public void onAudioFocusChanged(int focusChange);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map to convert focus event listener IDs, as used in the AudioService audio focus stack,
|
||||
* to actual listener objects.
|
||||
*/
|
||||
private HashMap<String, OnAudioFocusChangeListener> mFocusIdListenerMap =
|
||||
new HashMap<String, OnAudioFocusChangeListener>();
|
||||
/**
|
||||
* Lock to prevent concurrent changes to the list of focus listeners for this AudioManager
|
||||
* instance.
|
||||
*/
|
||||
private final Object mFocusListenerLock = new Object();
|
||||
|
||||
private OnAudioFocusChangeListener findFocusListener(String id) {
|
||||
return mFocusIdListenerMap.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for audio focus events coming from the audio service.
|
||||
*/
|
||||
private FocusEventHandlerDelegate mFocusEventHandlerDelegate = new FocusEventHandlerDelegate();
|
||||
/**
|
||||
* Event id denotes a loss of focus
|
||||
*/
|
||||
private static final int AUDIOFOCUS_EVENT_LOSS = 0;
|
||||
/**
|
||||
* Event id denotes a gain of focus
|
||||
*/
|
||||
private static final int AUDIOFOCUS_EVENT_GAIN = 1;
|
||||
/**
|
||||
* Helper class to handle the forwarding of audio focus events to the appropriate listener
|
||||
*/
|
||||
private class FocusEventHandlerDelegate {
|
||||
private final Handler mHandler;
|
||||
|
||||
FocusEventHandlerDelegate() {
|
||||
Looper looper;
|
||||
if ((looper = Looper.myLooper()) == null) {
|
||||
looper = Looper.getMainLooper();
|
||||
}
|
||||
|
||||
if (looper != null) {
|
||||
// implement the event handler delegate to receive audio focus events
|
||||
mHandler = new Handler(looper) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
OnAudioFocusChangeListener listener = null;
|
||||
synchronized(mFocusListenerLock) {
|
||||
listener = findFocusListener((String)msg.obj);
|
||||
}
|
||||
if (listener != null) {
|
||||
listener.onAudioFocusChanged(msg.what);
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
mHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
Handler getHandler() {
|
||||
return mHandler;
|
||||
}
|
||||
}
|
||||
|
||||
private IAudioFocusDispatcher mFocusDispatcher = new IAudioFocusDispatcher.Stub() {
|
||||
|
||||
public void dispatchAudioFocusChange(int focusChange, String id) {
|
||||
Message m = mFocusEventHandlerDelegate.getHandler().obtainMessage(focusChange, id);
|
||||
mFocusEventHandlerDelegate.getHandler().sendMessage(m);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private String getIdForFocusListener(OnAudioFocusChangeListener l) {
|
||||
if (l == null) {
|
||||
return new String();
|
||||
} else {
|
||||
return new String(this.toString() + l.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO unhide for SDK
|
||||
* {@hide}
|
||||
* Register a listener for audio focus updates.
|
||||
*/
|
||||
public void registerAudioFocusListener(OnAudioFocusChangeListener l) {
|
||||
if (l == null) {
|
||||
return;
|
||||
}
|
||||
synchronized(mFocusListenerLock) {
|
||||
if (mFocusIdListenerMap.containsKey(getIdForFocusListener(l))) {
|
||||
return;
|
||||
}
|
||||
mFocusIdListenerMap.put(getIdForFocusListener(l), l);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO unhide for SDK
|
||||
* TODO document for SDK
|
||||
* {@hide}
|
||||
*/
|
||||
public void unregisterAudioFocusListener(OnAudioFocusChangeListener l) {
|
||||
// notify service to remove it from audio focus stack
|
||||
IAudioService service = getService();
|
||||
try {
|
||||
service.unregisterFocusClient(getIdForFocusListener(l));
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Can't call unregisterFocusClient() from AudioService due to "+e);
|
||||
}
|
||||
// remove locally
|
||||
synchronized(mFocusListenerLock) {
|
||||
mFocusIdListenerMap.remove(getIdForFocusListener(l));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TODO unhide for SDK
|
||||
* TODO document for SDK
|
||||
* {@hide}
|
||||
*/
|
||||
public static final int AUDIOFOCUS_REQUEST_FAILED = 0;
|
||||
/**
|
||||
* TODO unhide for SDK
|
||||
* TODO document for SDK
|
||||
* {@hide}
|
||||
*/
|
||||
public static final int AUDIOFOCUS_REQUEST_GRANTED = 1;
|
||||
|
||||
|
||||
/**
|
||||
* TODO unhide for SDK
|
||||
* {@hide}
|
||||
* Request audio focus.
|
||||
* Send a request to obtain the audio focus for a specific stream type
|
||||
* @param l the listener to be notified of audio focus changes
|
||||
* @param streamType the main audio stream type affected by the focus request
|
||||
* @param durationHint use {@link #AUDIOFOCUS_GAIN_TRANSIENT} to indicate this focus request
|
||||
* is temporary, and focus will be abandonned shortly. Examples of transient requests are
|
||||
* for the playback of driving directions, or notifications sounds. Use
|
||||
* {@link #AUDIOFOCUS_GAIN} for a focus request of unknown duration such
|
||||
* as the playback of a song or a video.
|
||||
* @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
|
||||
*/
|
||||
public int requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint) {
|
||||
int status = AUDIOFOCUS_REQUEST_FAILED;
|
||||
registerAudioFocusListener(l);
|
||||
//TODO protect request by permission check?
|
||||
IAudioService service = getService();
|
||||
try {
|
||||
status = service.requestAudioFocus(streamType, durationHint, mICallBack,
|
||||
mFocusDispatcher, getIdForFocusListener(l));
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Can't call requestAudioFocus() from AudioService due to "+e);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TODO unhide for SDK
|
||||
* TODO document for SDK
|
||||
* {@hide}
|
||||
* Abandon audio focus.
|
||||
* @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
|
||||
*/
|
||||
public int abandonAudioFocus(OnAudioFocusChangeListener l) {
|
||||
int status = AUDIOFOCUS_REQUEST_FAILED;
|
||||
registerAudioFocusListener(l);
|
||||
IAudioService service = getService();
|
||||
try {
|
||||
status = service.abandonAudioFocus(mFocusDispatcher, getIdForFocusListener(l));
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Can't call abandonAudioFocus() from AudioService due to "+e);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Reload audio settings. This method is called by Settings backup
|
||||
|
||||
@@ -26,7 +26,6 @@ import android.bluetooth.BluetoothA2dp;
|
||||
import android.bluetooth.BluetoothClass;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothHeadset;
|
||||
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.ContentObserver;
|
||||
import android.media.MediaPlayer.OnCompletionListener;
|
||||
@@ -47,12 +46,15 @@ import android.os.SystemProperties;
|
||||
|
||||
import com.android.internal.telephony.ITelephony;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* The implementation of the volume manager service.
|
||||
@@ -76,6 +78,7 @@ public class AudioService extends IAudioService.Stub {
|
||||
private Context mContext;
|
||||
private ContentResolver mContentResolver;
|
||||
|
||||
|
||||
/** The UI */
|
||||
private VolumePanel mVolumePanel;
|
||||
|
||||
@@ -888,6 +891,7 @@ public class AudioService extends IAudioService.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Internal methods
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@@ -1600,4 +1604,223 @@ public class AudioService extends IAudioService.Stub {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================================
|
||||
// AudioFocus
|
||||
//==========================================================================================
|
||||
private static class FocusStackEntry {
|
||||
public int mStreamType = -1;// no stream type
|
||||
public boolean mIsTransportControlReceiver = false;
|
||||
public IAudioFocusDispatcher mFocusDispatcher = null;
|
||||
public IBinder mSourceRef = null;
|
||||
public String mClientId;
|
||||
public int mDurationHint;
|
||||
|
||||
public FocusStackEntry() {
|
||||
}
|
||||
|
||||
public FocusStackEntry(int streamType, int duration, boolean isTransportControlReceiver,
|
||||
IAudioFocusDispatcher afl, IBinder source, String id) {
|
||||
mStreamType = streamType;
|
||||
mIsTransportControlReceiver = isTransportControlReceiver;
|
||||
mFocusDispatcher = afl;
|
||||
mSourceRef = source;
|
||||
mClientId = id;
|
||||
mDurationHint = duration;
|
||||
}
|
||||
}
|
||||
|
||||
private Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
|
||||
|
||||
/**
|
||||
* Helper function:
|
||||
* Display in the log the current entries in the audio focus stack
|
||||
*/
|
||||
private void dumpFocusStack(PrintWriter pw) {
|
||||
pw.println("Audio Focus stack entries:");
|
||||
synchronized(mFocusStack) {
|
||||
Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
|
||||
while(stackIterator.hasNext()) {
|
||||
FocusStackEntry fse = stackIterator.next();
|
||||
pw.println(" source:" + fse.mSourceRef + " -- client: " + fse.mClientId
|
||||
+ " -- duration: " +fse.mDurationHint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function:
|
||||
* Remove a focus listener from the focus stack.
|
||||
* @param focusListenerToRemove the focus listener
|
||||
* @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
|
||||
* focus, notify the next item in the stack it gained focus.
|
||||
*/
|
||||
private void removeFocusStackEntry(String clientToRemove, boolean signal) {
|
||||
// is the current top of the focus stack abandoning focus? (because of death or request)
|
||||
if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
|
||||
{
|
||||
//Log.i(TAG, " removeFocusStackEntry() removing top of stack");
|
||||
mFocusStack.pop();
|
||||
if (signal) {
|
||||
// notify the new top of the stack it gained focus
|
||||
if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)
|
||||
&& canReassignFocus()) {
|
||||
try {
|
||||
mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
|
||||
AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, " Failure to signal gain of focus due to "+ e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// focus is abandoned by a client that's not at the top of the stack,
|
||||
// no need to update focus.
|
||||
Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
|
||||
while(stackIterator.hasNext()) {
|
||||
FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
|
||||
if(fse.mClientId.equals(clientToRemove)) {
|
||||
Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for "
|
||||
+ fse.mClientId);
|
||||
mFocusStack.remove(fse);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function:
|
||||
* Remove focus listeners from the focus stack for a particular client.
|
||||
*/
|
||||
private void removeFocusStackEntryForClient(IBinder cb) {
|
||||
// focus is abandoned by a client that's not at the top of the stack,
|
||||
// no need to update focus.
|
||||
Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
|
||||
while(stackIterator.hasNext()) {
|
||||
FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
|
||||
if(fse.mSourceRef.equals(cb)) {
|
||||
Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for "
|
||||
+ fse.mClientId);
|
||||
mFocusStack.remove(fse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function:
|
||||
* Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
|
||||
*/
|
||||
private boolean canReassignFocus() {
|
||||
// focus requests are rejected during a phone call
|
||||
if (getMode() == AudioSystem.MODE_IN_CALL) {
|
||||
Log.i(TAG, " AudioFocus can't be reassigned during a call, exiting");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class to monitor audio focus client deaths, and remove them from the audio focus
|
||||
* stack if necessary.
|
||||
*/
|
||||
private class AudioFocusDeathHandler implements IBinder.DeathRecipient {
|
||||
private IBinder mCb; // To be notified of client's death
|
||||
|
||||
AudioFocusDeathHandler(IBinder cb) {
|
||||
mCb = cb;
|
||||
}
|
||||
|
||||
public void binderDied() {
|
||||
synchronized(mFocusStack) {
|
||||
Log.w(TAG, " AudioFocus audio focus client died");
|
||||
removeFocusStackEntryForClient(mCb);
|
||||
}
|
||||
}
|
||||
|
||||
public IBinder getBinder() {
|
||||
return mCb;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** @see AudioManager#requestAudioFocus(int, int, IBinder, IAudioFocusDispatcher, String) */
|
||||
public int requestAudioFocus(int mainStreamType, int durationHint, IBinder cb,
|
||||
IAudioFocusDispatcher fd, String clientId) {
|
||||
Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId);
|
||||
// the main stream type for the audio focus request is currently not used. It may
|
||||
// potentially be used to handle multiple stream type-dependent audio focuses.
|
||||
|
||||
if ((cb == null) || !cb.pingBinder()) {
|
||||
Log.i(TAG, " AudioFocus DOA client for requestAudioFocus(), exiting");
|
||||
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
|
||||
}
|
||||
|
||||
if (!canReassignFocus()) {
|
||||
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
|
||||
}
|
||||
|
||||
synchronized(mFocusStack) {
|
||||
if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
|
||||
mFocusStack.peek().mDurationHint = durationHint;
|
||||
// if focus is already owned by this client, don't do anything
|
||||
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
|
||||
}
|
||||
|
||||
// notify current top of stack it is losing focus
|
||||
if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
|
||||
try {
|
||||
mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
|
||||
(durationHint == AudioManager.AUDIOFOCUS_GAIN) ?
|
||||
AudioManager.AUDIOFOCUS_LOSS :
|
||||
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT,
|
||||
mFocusStack.peek().mClientId);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, " Failure to signal loss of focus due to "+ e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// push focus requester at the top of the audio focus stack
|
||||
mFocusStack.push(new FocusStackEntry(mainStreamType, durationHint, false, fd, cb,
|
||||
clientId));
|
||||
}//synchronized(mFocusStack)
|
||||
|
||||
// handle the potential premature death of the new holder of the focus
|
||||
// (premature death == death before abandoning focus)
|
||||
// Register for client death notification
|
||||
AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
|
||||
try {
|
||||
cb.linkToDeath(afdh, 0);
|
||||
} catch (RemoteException e) {
|
||||
// client has already died!
|
||||
Log.w(TAG, " AudioFocus requestAudioFocus() could not link to "+cb+" binder death");
|
||||
}
|
||||
|
||||
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
|
||||
}
|
||||
|
||||
/** @see AudioManager#abandonAudioFocus(IBinder, IAudioFocusDispatcher, String) */
|
||||
public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
|
||||
Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId);
|
||||
|
||||
// this will take care of notifying the new focus owner if needed
|
||||
removeFocusStackEntry(clientId, true);
|
||||
|
||||
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
|
||||
}
|
||||
|
||||
|
||||
public void unregisterFocusClient(String clientId) {
|
||||
removeFocusStackEntry(clientId, false);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
// TODO probably a lot more to do here than just the audio focus stack
|
||||
dumpFocusStack(pw);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
28
media/java/android/media/IAudioFocusDispatcher.aidl
Executable file
28
media/java/android/media/IAudioFocusDispatcher.aidl
Executable file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.media;
|
||||
|
||||
/**
|
||||
* AIDL for the AudioService to signal audio focus listeners of focus updates.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
oneway interface IAudioFocusDispatcher {
|
||||
|
||||
void dispatchAudioFocusChange(int focusChange, String clientId);
|
||||
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package android.media;
|
||||
|
||||
import android.media.IAudioFocusDispatcher;
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
*/
|
||||
@@ -68,4 +70,11 @@ interface IAudioService {
|
||||
void setBluetoothScoOn(boolean on);
|
||||
|
||||
boolean isBluetoothScoOn();
|
||||
|
||||
int requestAudioFocus(int mainStreamType, int durationHint, IBinder cb, IAudioFocusDispatcher l,
|
||||
String clientId);
|
||||
|
||||
int abandonAudioFocus(IAudioFocusDispatcher l, String clientId);
|
||||
|
||||
void unregisterFocusClient(String clientId);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user