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

Commit ca6fda2c authored by Priyanka Advani (xWF)'s avatar Priyanka Advani (xWF) Committed by Android Build Coastguard Worker
Browse files

Revert "[audio] Fix AudioRecordingMonitorImpl leak"

This reverts commit 0503b823.

Reason for revert: Droidmonitor created revert due to b/441693602. Will be verifying through ABTD before submission.

Fix: 441693602
Bug: 432245274
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:6cea280194e7f9a706c46413b51daa9254f67561)
Merged-In: I5e3d9772a13cfdb8fe69be4a09dc59b78eb6885f
Change-Id: I5e3d9772a13cfdb8fe69be4a09dc59b78eb6885f
parent 22f01492
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -1219,7 +1219,6 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
            AudioManager.unregisterAudioPolicyAsyncStatic(mAudioCapturePolicy);
            mAudioCapturePolicy = null;
        }
        mRecordingInfoImpl.endRecordingCallbackHandling();
        native_release();
        mState = STATE_UNINITIALIZED;
    }
+112 −53
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.media.AudioManager.AudioRecordingCallback;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
@@ -47,26 +46,8 @@ public class AudioRecordingMonitorImpl implements AudioRecordingMonitor {
    private static final String TAG = "android.media.AudioRecordingMonitor";

    private static IAudioService sService; //lazy initialization, use getService()
    private final AudioRecordingMonitorClient mClient;

    private final Object mRecordCallbackLock = new Object();

    @GuardedBy("mRecordCallbackLock")
    private List<AudioRecordingCallbackInfo> mRecordCallbackList =
            new ArrayList<AudioRecordingCallbackInfo>();

    private final IRecordingConfigDispatcher mRecordingCallback =
            new IRecordingConfigDispatcher.Stub() {
                @Override
                public void dispatchRecordingConfigChange(
                        List<AudioRecordingConfiguration> configs) {
                    AudioRecordingConfiguration config = getMyConfig(configs);
                    if (config != null) {
                        configs.removeIf(c -> c != config);
                        handleCallback(configs);
                    }
                }
            };
    private final AudioRecordingMonitorClient mClient;

    AudioRecordingMonitorImpl(@NonNull AudioRecordingMonitorClient client) {
        mClient = client;
@@ -81,7 +62,7 @@ public class AudioRecordingMonitorImpl implements AudioRecordingMonitor {
     * @param cb non-null callback to register
     */
    public void registerAudioRecordingCallback(@NonNull @CallbackExecutor Executor executor,
            @NonNull AudioRecordingCallback cb) {
            @NonNull AudioManager.AudioRecordingCallback cb) {
        if (cb == null) {
            throw new IllegalArgumentException("Illegal null AudioRecordingCallback");
        }
@@ -91,19 +72,13 @@ public class AudioRecordingMonitorImpl implements AudioRecordingMonitor {
        synchronized (mRecordCallbackLock) {
            // check if eventCallback already in list
            for (AudioRecordingCallbackInfo arci : mRecordCallbackList) {
                if (arci.cb == cb) {
                if (arci.mCb == cb) {
                    throw new IllegalArgumentException(
                            "AudioRecordingCallback already registered");
                }
            }
            if (mRecordCallbackList.isEmpty()) {
                try {
                    getService().registerRecordingCallback(mRecordingCallback);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            mRecordCallbackList.add(new AudioRecordingCallbackInfo(cb, executor));
            beginRecordingCallbackHandling();
            mRecordCallbackList.add(new AudioRecordingCallbackInfo(executor, cb));
        }
    }

@@ -112,18 +87,23 @@ public class AudioRecordingMonitorImpl implements AudioRecordingMonitor {
     * {@link #registerAudioRecordingCallback(Executor, AudioManager.AudioRecordingCallback)}.
     * @param cb non-null callback to unregister
     */
    public void unregisterAudioRecordingCallback(@NonNull AudioRecordingCallback cb) {
    public void unregisterAudioRecordingCallback(@NonNull AudioManager.AudioRecordingCallback cb) {
        if (cb == null) {
            throw new IllegalArgumentException("Illegal null AudioRecordingCallback argument");
        }

        synchronized (mRecordCallbackLock) {
            if (mRecordCallbackList.removeIf((arci) -> arci.cb == cb)) {
            for (AudioRecordingCallbackInfo arci : mRecordCallbackList) {
                if (arci.mCb == cb) {
                    // ok to remove while iterating over list as we exit iteration
                    mRecordCallbackList.remove(arci);
                    if (mRecordCallbackList.size() == 0) {
                        endRecordingCallbackHandling();
                    }
            } else {
                throw new IllegalArgumentException("AudioRecordingCallback was not registered");
                    return;
                }
            }
            throw new IllegalArgumentException("AudioRecordingCallback was not registered");
        }
    }

@@ -143,31 +123,110 @@ public class AudioRecordingMonitorImpl implements AudioRecordingMonitor {
        }
    }

    private static record AudioRecordingCallbackInfo(AudioRecordingCallback cb, Executor executor) {
        void dispatch(List<AudioRecordingConfiguration> configs) {
            executor.execute(() -> cb.onRecordingConfigChanged(configs));
    private static class AudioRecordingCallbackInfo {
        final AudioManager.AudioRecordingCallback mCb;
        final Executor mExecutor;
        AudioRecordingCallbackInfo(Executor e, AudioManager.AudioRecordingCallback cb) {
            mExecutor = e;
            mCb = cb;
        }
    }

    private void handleCallback(List<AudioRecordingConfiguration> configs) {
        List<AudioRecordingCallbackInfo> cbInfoList;
    private static final int MSG_RECORDING_CONFIG_CHANGE = 1;

    private final Object mRecordCallbackLock = new Object();
    @GuardedBy("mRecordCallbackLock")
    @NonNull private LinkedList<AudioRecordingCallbackInfo> mRecordCallbackList =
            new LinkedList<AudioRecordingCallbackInfo>();
    @GuardedBy("mRecordCallbackLock")
    private @Nullable HandlerThread mRecordingCallbackHandlerThread;
    @GuardedBy("mRecordCallbackLock")
    private @Nullable volatile Handler mRecordingCallbackHandler;

    @GuardedBy("mRecordCallbackLock")
    private final IRecordingConfigDispatcher mRecordingCallback =
            new IRecordingConfigDispatcher.Stub() {
        @Override
        public void dispatchRecordingConfigChange(List<AudioRecordingConfiguration> configs) {
            AudioRecordingConfiguration config = getMyConfig(configs);
            if (config != null) {
                synchronized (mRecordCallbackLock) {
            if (mRecordCallbackList.isEmpty()) {
                    if (mRecordingCallbackHandler != null) {
                        final Message m = mRecordingCallbackHandler.obtainMessage(
                                              MSG_RECORDING_CONFIG_CHANGE/*what*/, config /*obj*/);
                        mRecordingCallbackHandler.sendMessage(m);
                    }
                }
            }
        }
    };

    @GuardedBy("mRecordCallbackLock")
    private void beginRecordingCallbackHandling() {
        if (mRecordingCallbackHandlerThread == null) {
            mRecordingCallbackHandlerThread = new HandlerThread(TAG + ".RecordingCallback");
            mRecordingCallbackHandlerThread.start();
            final Looper looper = mRecordingCallbackHandlerThread.getLooper();
            if (looper != null) {
                mRecordingCallbackHandler = new Handler(looper) {
                    @Override
                    public void handleMessage(Message msg) {
                        switch (msg.what) {
                            case MSG_RECORDING_CONFIG_CHANGE: {
                                if (msg.obj == null) {
                                    return;
                                }
            cbInfoList = new ArrayList<AudioRecordingCallbackInfo>(mRecordCallbackList);
                                ArrayList<AudioRecordingConfiguration> configs =
                                        new ArrayList<AudioRecordingConfiguration>();
                                configs.add((AudioRecordingConfiguration) msg.obj);

                                final LinkedList<AudioRecordingCallbackInfo> cbInfoList;
                                synchronized (mRecordCallbackLock) {
                                    if (mRecordCallbackList.size() == 0) {
                                        return;
                                    }
        Binder.withCleanCallingIdentity(() -> cbInfoList.forEach(cbi -> cbi.dispatch(configs)));
                                    cbInfoList = new LinkedList<AudioRecordingCallbackInfo>(
                                        mRecordCallbackList);
                                }

    // Package private for cleanup from AudioRecord#release
    // AudioService tolerates double unregister calls
    void endRecordingCallbackHandling() {
                                final long identity = Binder.clearCallingIdentity();
                                try {
            getService().unregisterRecordingCallback(mRecordingCallback);
                                    for (AudioRecordingCallbackInfo cbi : cbInfoList) {
                                        cbi.mExecutor.execute(() ->
                                                cbi.mCb.onRecordingConfigChanged(configs));
                                    }
                                } finally {
                                    Binder.restoreCallingIdentity(identity);
                                }
                            } break;
                            default:
                                Log.e(TAG, "Unknown event " + msg.what);
                                break;
                        }
                    }
                };
                final IAudioService service = getService();
                try {
                    service.registerRecordingCallback(mRecordingCallback);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
        }
    }

    @GuardedBy("mRecordCallbackLock")
    private void endRecordingCallbackHandling() {
        if (mRecordingCallbackHandlerThread != null) {
            final IAudioService service = getService();
            try {
                service.unregisterRecordingCallback(mRecordingCallback);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
            mRecordingCallbackHandlerThread.quit();
            mRecordingCallbackHandlerThread = null;
        }
    }

    AudioRecordingConfiguration getMyConfig(List<AudioRecordingConfiguration> configs) {
+1 −6
Original line number Diff line number Diff line
@@ -1975,12 +1975,7 @@ public class MediaRecorder implements AudioRouting,
     * may be expected when unnecessary multiple instances are used
     * at the same time.
     */
    public void release() {
        native_release();
        mRecordingInfoImpl.endRecordingCallbackHandling();
    }

    private native void native_release();
    public native void release();

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    private static native final void native_init();
+3 −3
Original line number Diff line number Diff line
@@ -574,7 +574,7 @@ android_media_MediaRecorder_native_reset(JNIEnv *env, jobject thiz)
}

static void
android_media_MediaRecorder_native_release(JNIEnv *env, jobject thiz)
android_media_MediaRecorder_release(JNIEnv *env, jobject thiz)
{
    ALOGV("release");
    sp<MediaRecorder> mr = setMediaRecorder(env, thiz, 0);
@@ -669,7 +669,7 @@ static void
android_media_MediaRecorder_native_finalize(JNIEnv *env, jobject thiz)
{
    ALOGV("finalize");
    android_media_MediaRecorder_native_release(env, thiz);
    android_media_MediaRecorder_release(env, thiz);
}

void android_media_MediaRecorder_setInputSurface(
@@ -891,7 +891,7 @@ static const JNINativeMethod gMethods[] = {
    {"pause",                "()V",                             (void *)android_media_MediaRecorder_pause},
    {"resume",               "()V",                             (void *)android_media_MediaRecorder_resume},
    {"native_reset",         "()V",                             (void *)android_media_MediaRecorder_native_reset},
    {"native_release",       "()V",                             (void *)android_media_MediaRecorder_native_release},
    {"release",              "()V",                             (void *)android_media_MediaRecorder_release},
    {"native_init",          "()V",                             (void *)android_media_MediaRecorder_native_init},
    {"native_setup",         "(Ljava/lang/Object;Ljava/lang/String;Landroid/os/Parcel;)V",
                                                                (void *)android_media_MediaRecorder_native_setup},