Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d497edd9 authored by Jean-Michel Trivi's avatar Jean-Michel Trivi
Browse files

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
Merged-In: I1dbfdac1873ed9c44c20ca9d25bbe01ce9904ee5
parent 81852c84
Loading
Loading
Loading
Loading
+47 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.media;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
@@ -26,6 +27,8 @@ import android.media.audiofx.AudioEffect;
import android.media.audiopolicy.AudioMix;
import android.util.Log;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Map;

@@ -431,6 +434,50 @@ public class AudioSystem
    public static final int DEAD_OBJECT        = -6;
    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
     */
+4 −0
Original line number Diff line number Diff line
@@ -853,6 +853,10 @@ public class AudioPolicy {
                Log.v(TAG, "notifyVolumeAdjust: " + adjustment);
            }
        }

        public void notifyUnregistration() {
            setRegistration(null);
        }
    };

    //==================================================
+4 −0
Original line number Diff line number Diff line
@@ -34,4 +34,8 @@ oneway interface IAudioPolicyCallback {

    // callback for volume events
    void notifyVolumeAdjust(int adjustment);

    // callback for unregistration (e.g. if policy couldn't automatically be re-registered after
    // an audioserver crash)
    void notifyUnregistration();
}
+22 −12
Original line number Diff line number Diff line
@@ -1018,7 +1018,14 @@ public class AudioService extends IAudioService.Stub

        synchronized (mAudioPolicies) {
            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();
                }
            }
        }

@@ -7007,16 +7014,8 @@ public class AudioService extends IAudioService.Stub
        }

        public void binderDied() {
            synchronized (mAudioPolicies) {
            Log.i(TAG, "audio policy " + mPolicyCallback + " died");
            release();
                mAudioPolicies.remove(mPolicyCallback.asBinder());
            }
            if (mIsVolumeController) {
                synchronized (mExtVolumeControllerLock) {
                    mExtVolumeController = null;
                }
            }
        }

        String getRegistrationId() {
@@ -7040,9 +7039,20 @@ public class AudioService extends IAudioService.Stub
                    Log.e(TAG, "Fail to unregister Audiopolicy callback from MediaProjection");
                }
            }
            if (mIsVolumeController) {
                synchronized (mExtVolumeControllerLock) {
                    mExtVolumeController = null;
                }
            }
            final long identity = Binder.clearCallingIdentity();
            AudioSystem.registerPolicyMixes(mMixes, false);
            Binder.restoreCallingIdentity(identity);
            synchronized (mAudioPolicies) {
                mAudioPolicies.remove(mPolicyCallback.asBinder());
            }
            try {
                mPolicyCallback.notifyUnregistration();
            } catch (RemoteException e) { }
        }

        boolean hasMixAffectingUsage(int usage, int excludedFlags) {
@@ -7093,7 +7103,7 @@ public class AudioService extends IAudioService.Stub
            }
        }

        int connectMixes() {
        @AudioSystem.AudioSystemError int connectMixes() {
            final long identity = Binder.clearCallingIdentity();
            int status = AudioSystem.registerPolicyMixes(mMixes, true);
            Binder.restoreCallingIdentity(identity);