Audio focus: fix delayed focus request during locked focus behavior
Fix issue where if audio focus was requested with GAIN, and accepted delayed focus, while focus was locked, then previous focus owners didn't receive LOSS, and thus would be removed from the stack. The fix consists in propagating the focus loss through the stack under the focus locker, and remove any focus loser if the loss was definitive. In order to simulate the bug conditions, the test API for simulating focus requests and abandons, was modified to support locking the focus like in a phone call. Bug: 206856107 Test: atest AudioFocusTest#testAudioFocusDelayedByCall Test: atest AudioFocusTest#testAudioFocusTransientDelayedByCall Change-Id: I211cd46b66c882499bfa43718bc0cb37b2f79817
This commit is contained in:
@@ -4003,8 +4003,7 @@ public class AudioManager {
|
||||
* @hide
|
||||
* flag set on test API calls,
|
||||
* see {@link #requestAudioFocusForTest(AudioFocusRequest, String, int, int)},
|
||||
* note that it isn't used in conjunction with other flags, it is passed as the single
|
||||
* value for flags */
|
||||
*/
|
||||
public static final int AUDIOFOCUS_FLAG_TEST = 0x1 << 3;
|
||||
/** @hide */
|
||||
public static final int AUDIOFOCUS_FLAGS_APPS = AUDIOFOCUS_FLAG_DELAY_OK
|
||||
@@ -4187,7 +4186,9 @@ public class AudioManager {
|
||||
afr.getFocusGain(),
|
||||
mICallBack,
|
||||
mAudioFocusDispatcher,
|
||||
clientFakeId, "com.android.test.fakeclient", clientFakeUid, clientTargetSdk);
|
||||
clientFakeId, "com.android.test.fakeclient",
|
||||
afr.getFlags() | AudioManager.AUDIOFOCUS_FLAG_TEST,
|
||||
clientFakeUid, clientTargetSdk);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
|
||||
@@ -384,7 +384,7 @@ interface IAudioService {
|
||||
|
||||
int requestAudioFocusForTest(in AudioAttributes aa, int durationHint, IBinder cb,
|
||||
in IAudioFocusDispatcher fd, in String clientId, in String callingPackageName,
|
||||
int uid, int sdk);
|
||||
int flags, int uid, int sdk);
|
||||
|
||||
int abandonAudioFocusForTest(in IAudioFocusDispatcher fd, in String clientId,
|
||||
in AudioAttributes aa, in String callingPackageName);
|
||||
|
||||
@@ -8252,6 +8252,9 @@ public class AudioService extends IAudioService.Stub
|
||||
public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb,
|
||||
IAudioFocusDispatcher fd, String clientId, String callingPackageName,
|
||||
String attributionTag, int flags, IAudioPolicyCallback pcb, int sdk) {
|
||||
if ((flags & AudioManager.AUDIOFOCUS_FLAG_TEST) != 0) {
|
||||
throw new IllegalArgumentException("Invalid test flag");
|
||||
}
|
||||
final int uid = Binder.getCallingUid();
|
||||
MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "focus")
|
||||
.setUid(uid)
|
||||
@@ -8310,7 +8313,7 @@ public class AudioService extends IAudioService.Stub
|
||||
/** see {@link AudioManager#requestAudioFocusForTest(AudioFocusRequest, String, int, int)} */
|
||||
public int requestAudioFocusForTest(AudioAttributes aa, int durationHint, IBinder cb,
|
||||
IAudioFocusDispatcher fd, String clientId, String callingPackageName,
|
||||
int fakeUid, int sdk) {
|
||||
int flags, int fakeUid, int sdk) {
|
||||
if (!enforceQueryAudioStateForTest("focus request")) {
|
||||
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
|
||||
}
|
||||
@@ -8320,7 +8323,7 @@ public class AudioService extends IAudioService.Stub
|
||||
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
|
||||
}
|
||||
return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
|
||||
clientId, callingPackageName, null, AudioManager.AUDIOFOCUS_FLAG_TEST,
|
||||
clientId, callingPackageName, null, flags,
|
||||
sdk, false /*forceDuck*/, fakeUid);
|
||||
}
|
||||
|
||||
|
||||
@@ -461,13 +461,19 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
|
||||
* at the top of the focus stack
|
||||
* Push the focus requester onto the audio focus stack at the first position immediately
|
||||
* following the locked focus owners.
|
||||
* Propagate through the stack the changes that the new (future) focus owner causes.
|
||||
* @param nfr the future focus owner that will gain focus when the locked focus owners are
|
||||
* removed from the stack
|
||||
* @return {@link AudioManager#AUDIOFOCUS_REQUEST_GRANTED} or
|
||||
* {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED}
|
||||
*/
|
||||
@GuardedBy("mAudioFocusLock")
|
||||
private int pushBelowLockedFocusOwners(FocusRequester nfr) {
|
||||
private int pushBelowLockedFocusOwnersAndPropagate(FocusRequester nfr) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "pushBelowLockedFocusOwnersAndPropagate client=" + nfr.getClientId());
|
||||
}
|
||||
int lastLockedFocusOwnerIndex = mFocusStack.size();
|
||||
for (int index = mFocusStack.size()-1; index >= 0; index--) {
|
||||
for (int index = mFocusStack.size() - 1; index >= 0; index--) {
|
||||
if (isLockedFocusOwner(mFocusStack.elementAt(index))) {
|
||||
lastLockedFocusOwnerIndex = index;
|
||||
}
|
||||
@@ -480,10 +486,33 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
|
||||
propagateFocusLossFromGain_syncAf(nfr.getGainRequest(), nfr, false /*forceDuck*/);
|
||||
mFocusStack.push(nfr);
|
||||
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
|
||||
} else {
|
||||
mFocusStack.insertElementAt(nfr, lastLockedFocusOwnerIndex);
|
||||
return AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "> lastLockedFocusOwnerIndex=" + lastLockedFocusOwnerIndex);
|
||||
}
|
||||
mFocusStack.insertElementAt(nfr, lastLockedFocusOwnerIndex);
|
||||
|
||||
// propagate potential focus loss (and removal from stack) after the newly
|
||||
// inserted FocusRequester (at index lastLockedFocusOwnerIndex-1)
|
||||
final List<String> clientsToRemove = new LinkedList<String>();
|
||||
for (int index = lastLockedFocusOwnerIndex - 1; index >= 0; index--) {
|
||||
final boolean isDefinitiveLoss =
|
||||
mFocusStack.elementAt(index).handleFocusLossFromGain(
|
||||
nfr.getGainRequest(), nfr, false /*forceDuck*/);
|
||||
if (isDefinitiveLoss) {
|
||||
clientsToRemove.add(mFocusStack.elementAt(index).getClientId());
|
||||
}
|
||||
}
|
||||
for (String clientToRemove : clientsToRemove) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "> removing focus client " + clientToRemove);
|
||||
}
|
||||
removeFocusStackEntry(clientToRemove, false /*signal*/,
|
||||
true /*notifyFocusFollowers*/);
|
||||
}
|
||||
|
||||
return AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -868,7 +897,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
|
||||
* @param forceDuck only true if
|
||||
* {@link android.media.AudioFocusRequest.Builder#setFocusGain(int)} was set to true for
|
||||
* accessibility.
|
||||
* @param testUid ignored if flags is not AudioManager.AUDIOFOCUS_FLAG_TEST (strictly equals to)
|
||||
* @param testUid ignored if flags doesn't contain AudioManager.AUDIOFOCUS_FLAG_TEST
|
||||
* otherwise the UID being injected for testing
|
||||
* @return
|
||||
*/
|
||||
@@ -1029,7 +1058,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
|
||||
if (focusGrantDelayed) {
|
||||
// focusGrantDelayed being true implies we can't reassign focus right now
|
||||
// which implies the focus stack is not empty.
|
||||
final int requestResult = pushBelowLockedFocusOwners(nfr);
|
||||
final int requestResult = pushBelowLockedFocusOwnersAndPropagate(nfr);
|
||||
if (requestResult != AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
|
||||
notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), requestResult);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user