AudioService: handle errors when reconnecting mixes after server crash

Log errors when reconnecting dynamic policy mixes after an audio
server crash.
  Add IntDef for AudioSystem errors, and conversion to String.
  Add support for notifying an AudioPolicy user that it was
unregistered.

Bug: 133279309
Test: connect DAP, kill audio server, verify no errors reported
Change-Id: I1dbfdac1873ed9c44c20ca9d25bbe01ce9904ee5
This commit is contained in:
Jean-Michel Trivi
2019-10-22 08:59:36 -07:00
parent 7bd4314e4e
commit 549ac37d31
4 changed files with 77 additions and 12 deletions

View File

@@ -16,6 +16,7 @@
package android.media; package android.media;
import android.annotation.IntDef;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.TestApi; import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage; import android.annotation.UnsupportedAppUsage;
@@ -26,6 +27,8 @@ import android.media.audiofx.AudioEffect;
import android.media.audiopolicy.AudioMix; import android.media.audiopolicy.AudioMix;
import android.util.Log; import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Map; import java.util.Map;
@@ -431,6 +434,50 @@ public class AudioSystem
public static final int DEAD_OBJECT = -6; public static final int DEAD_OBJECT = -6;
public static final int WOULD_BLOCK = -7; public static final int WOULD_BLOCK = -7;
/** @hide */
@IntDef({
SUCCESS,
ERROR,
BAD_VALUE,
INVALID_OPERATION,
PERMISSION_DENIED,
NO_INIT,
DEAD_OBJECT,
WOULD_BLOCK
})
@Retention(RetentionPolicy.SOURCE)
public @interface AudioSystemError {}
/**
* Convert an int error value to its String value for readability.
* Accepted error values are the java AudioSystem errors, matching android_media_AudioErrors.h,
* which map onto the native status_t type.
* @param error one of the java AudioSystem errors
* @return a human-readable string
*/
public static String audioSystemErrorToString(@AudioSystemError int error) {
switch(error) {
case SUCCESS:
return "SUCCESS";
case ERROR:
return "ERROR";
case BAD_VALUE:
return "BAD_VALUE";
case INVALID_OPERATION:
return "INVALID_OPERATION";
case PERMISSION_DENIED:
return "PERMISSION_DENIED";
case NO_INIT:
return "NO_INIT";
case DEAD_OBJECT:
return "DEAD_OBJECT";
case WOULD_BLOCK:
return "WOULD_BLOCK";
default:
return ("unknown error:" + error);
}
}
/* /*
* AudioPolicyService methods * AudioPolicyService methods
*/ */

View File

@@ -853,6 +853,10 @@ public class AudioPolicy {
Log.v(TAG, "notifyVolumeAdjust: " + adjustment); Log.v(TAG, "notifyVolumeAdjust: " + adjustment);
} }
} }
public void notifyUnregistration() {
setRegistration(null);
}
}; };
//================================================== //==================================================

View File

@@ -34,4 +34,8 @@ oneway interface IAudioPolicyCallback {
// callback for volume events // callback for volume events
void notifyVolumeAdjust(int adjustment); void notifyVolumeAdjust(int adjustment);
// callback for unregistration (e.g. if policy couldn't automatically be re-registered after
// an audioserver crash)
void notifyUnregistration();
} }

View File

@@ -1018,7 +1018,14 @@ public class AudioService extends IAudioService.Stub
synchronized (mAudioPolicies) { synchronized (mAudioPolicies) {
for (AudioPolicyProxy policy : mAudioPolicies.values()) { for (AudioPolicyProxy policy : mAudioPolicies.values()) {
policy.connectMixes(); final int status = policy.connectMixes();
if (status != AudioSystem.SUCCESS) {
// note that PERMISSION_DENIED may also indicate trouble getting to APService
Log.e(TAG, "onAudioServerDied: error "
+ AudioSystem.audioSystemErrorToString(status)
+ " when connecting mixes for policy " + policy.toLogFriendlyString());
policy.release();
}
} }
} }
@@ -7019,16 +7026,8 @@ public class AudioService extends IAudioService.Stub
} }
public void binderDied() { public void binderDied() {
synchronized (mAudioPolicies) { Log.i(TAG, "audio policy " + mPolicyCallback + " died");
Log.i(TAG, "audio policy " + mPolicyCallback + " died"); release();
release();
mAudioPolicies.remove(mPolicyCallback.asBinder());
}
if (mIsVolumeController) {
synchronized (mExtVolumeControllerLock) {
mExtVolumeController = null;
}
}
} }
String getRegistrationId() { String getRegistrationId() {
@@ -7052,9 +7051,20 @@ public class AudioService extends IAudioService.Stub
Log.e(TAG, "Fail to unregister Audiopolicy callback from MediaProjection"); Log.e(TAG, "Fail to unregister Audiopolicy callback from MediaProjection");
} }
} }
if (mIsVolumeController) {
synchronized (mExtVolumeControllerLock) {
mExtVolumeController = null;
}
}
final long identity = Binder.clearCallingIdentity(); final long identity = Binder.clearCallingIdentity();
AudioSystem.registerPolicyMixes(mMixes, false); AudioSystem.registerPolicyMixes(mMixes, false);
Binder.restoreCallingIdentity(identity); Binder.restoreCallingIdentity(identity);
synchronized (mAudioPolicies) {
mAudioPolicies.remove(mPolicyCallback.asBinder());
}
try {
mPolicyCallback.notifyUnregistration();
} catch (RemoteException e) { }
} }
boolean hasMixAffectingUsage(int usage, int excludedFlags) { boolean hasMixAffectingUsage(int usage, int excludedFlags) {
@@ -7105,7 +7115,7 @@ public class AudioService extends IAudioService.Stub
} }
} }
int connectMixes() { @AudioSystem.AudioSystemError int connectMixes() {
final long identity = Binder.clearCallingIdentity(); final long identity = Binder.clearCallingIdentity();
int status = AudioSystem.registerPolicyMixes(mMixes, true); int status = AudioSystem.registerPolicyMixes(mMixes, true);
Binder.restoreCallingIdentity(identity); Binder.restoreCallingIdentity(identity);