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

Commit 013f0acb authored by Atneya Nair's avatar Atneya Nair Committed by Automerger Merge Worker
Browse files

Merge "Update AudioRecord JNI callback and cleanup" into tm-dev am: 10af76b8

parents 3f17057e 10af76b8
Loading
Loading
Loading
Loading
+103 −193
Original line number Original line Diff line number Diff line
@@ -33,12 +33,13 @@


#include <nativehelper/ScopedUtfChars.h>
#include <nativehelper/ScopedUtfChars.h>


#include "android_media_AudioAttributes.h"
#include "android_media_AudioFormat.h"
#include "android_media_AudioFormat.h"
#include "android_media_AudioErrors.h"
#include "android_media_AudioErrors.h"
#include "android_media_DeviceCallback.h"
#include "android_media_DeviceCallback.h"
#include "android_media_JNIUtils.h"
#include "android_media_MediaMetricsJNI.h"
#include "android_media_MediaMetricsJNI.h"
#include "android_media_MicrophoneInfo.h"
#include "android_media_MicrophoneInfo.h"
#include "android_media_AudioAttributes.h"




// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
@@ -57,8 +58,7 @@ struct audio_record_fields_t {
    // these fields provide access from C++ to the...
    // these fields provide access from C++ to the...
    jmethodID postNativeEventInJava; //... event post callback method
    jmethodID postNativeEventInJava; //... event post callback method
    jfieldID  nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object
    jfieldID  nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object
    jfieldID  nativeCallbackCookie;    // provides access to the AudioRecord callback data
    jfieldID  jniData;    // provides access to AudioRecord JNI Handle
    jfieldID  nativeDeviceCallback;    // provides access to the JNIDeviceCallback instance
};
};
static audio_record_fields_t     javaAudioRecordFields;
static audio_record_fields_t     javaAudioRecordFields;
static struct {
static struct {
@@ -66,122 +66,81 @@ static struct {
    jfieldID  fieldNanoTime;          // AudioTimestamp.nanoTime
    jfieldID  fieldNanoTime;          // AudioTimestamp.nanoTime
} javaAudioTimestampFields;
} javaAudioTimestampFields;


struct audiorecord_callback_cookie {
    jclass      audioRecord_class;
    jobject     audioRecord_ref;
    bool        busy;
    Condition   cond;
};


static Mutex sLock;
class AudioRecordJNIStorage : public AudioRecord::IAudioRecordCallback {
static SortedVector <audiorecord_callback_cookie *> sAudioRecordCallBackCookies;
 private:

   // Keep in sync with frameworks/base/media/java/android/media/AudioRecord.java NATIVE_EVENT_*.
// ----------------------------------------------------------------------------
   enum class EventType {

        EVENT_MORE_DATA = 0,        // Request to read available data from buffer.
#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT      (-16)
                                    // If this event is delivered but the callback handler
#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK  (-17)
                                    // does not want to read the available data, the handler must
#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT       (-18)
                                    // explicitly ignore the event by setting frameCount to zero.
#define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE       (-19)
        EVENT_OVERRUN = 1,          // Buffer overrun occurred.
#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED    (-20)
        EVENT_MARKER = 2,           // Record head is at the specified marker position
                                    // (See setMarkerPosition()).
        EVENT_NEW_POS = 3,          // Record head is at a new position
                                    // (See setPositionUpdatePeriod()).
        EVENT_NEW_IAUDIORECORD = 4, // IAudioRecord was re-created, either due to re-routing and
                                    // voluntary invalidation by mediaserver, or mediaserver crash.
    };


// ----------------------------------------------------------------------------
  public:
static void recorderCallback(int event, void* user, void *info) {
    AudioRecordJNIStorage(jclass audioRecordClass, jobject audioRecordWeakRef)
          : mAudioRecordClass(audioRecordClass), mAudioRecordWeakRef(audioRecordWeakRef) {}
    AudioRecordJNIStorage(const AudioRecordJNIStorage &) = delete;
    AudioRecordJNIStorage& operator=(const AudioRecordJNIStorage &) = delete;


    audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
    void onMarker(uint32_t) override {
    {
        postEvent(EventType::EVENT_MARKER);
        Mutex::Autolock l(sLock);
        if (sAudioRecordCallBackCookies.indexOf(callbackInfo) < 0) {
            return;
        }
        callbackInfo->busy = true;
    }
    }


    switch (event) {
    void onNewPos(uint32_t) override {
    case AudioRecord::EVENT_MARKER: {
        postEvent(EventType::EVENT_NEW_POS);
        JNIEnv *env = AndroidRuntime::getJNIEnv();
        if (user != NULL && env != NULL) {
            env->CallStaticVoidMethod(
                callbackInfo->audioRecord_class,
                javaAudioRecordFields.postNativeEventInJava,
                callbackInfo->audioRecord_ref, event, 0,0, NULL);
            if (env->ExceptionCheck()) {
                env->ExceptionDescribe();
                env->ExceptionClear();
    }
    }

    void setDeviceCallback(const sp<JNIDeviceCallback>& callback) {
        mDeviceCallback = callback;
    }
    }
        } break;


    case AudioRecord::EVENT_NEW_POS: {
    sp<JNIDeviceCallback> getDeviceCallback() const { return mDeviceCallback; }
        JNIEnv *env = AndroidRuntime::getJNIEnv();

        if (user != NULL && env != NULL) {
    jobject getAudioTrackWeakRef() const & { return mAudioRecordWeakRef.get(); }

    // If we attempt to get a jobject from a rvalue, it will soon go out of
    // scope, and the reference count can drop to zero, which is unsafe.
    jobject getAudioTrackWeakRef() const && = delete;

  private:
    void postEvent(EventType event, int arg = 0) const {
      JNIEnv *env = getJNIEnvOrDie();
      env->CallStaticVoidMethod(
      env->CallStaticVoidMethod(
                callbackInfo->audioRecord_class,
          static_cast<jclass>(mAudioRecordClass.get()),
          javaAudioRecordFields.postNativeEventInJava,
          javaAudioRecordFields.postNativeEventInJava,
                callbackInfo->audioRecord_ref, event, 0,0, NULL);
          mAudioRecordWeakRef.get(), static_cast<int>(event), arg, 0, nullptr);
      if (env->ExceptionCheck()) {
      if (env->ExceptionCheck()) {
          env->ExceptionDescribe();
          env->ExceptionDescribe();
          env->ExceptionClear();
          env->ExceptionClear();
      }
      }
    }
    }
        } break;
    }


    {
    // Mutation of this object is protected using Java concurrency constructs
        Mutex::Autolock l(sLock);
    sp<JNIDeviceCallback> mDeviceCallback;
        callbackInfo->busy = false;
    const GlobalRef   mAudioRecordClass;
        callbackInfo->cond.broadcast();
    const GlobalRef   mAudioRecordWeakRef;
    }
};
}


static sp<JNIDeviceCallback> getJniDeviceCallback(JNIEnv* env, jobject thiz)
// ----------------------------------------------------------------------------
{
    Mutex::Autolock l(sLock);
    JNIDeviceCallback* const cb =
            (JNIDeviceCallback*)env->GetLongField(thiz,
                                                  javaAudioRecordFields.nativeDeviceCallback);
    return sp<JNIDeviceCallback>(cb);
}


static sp<JNIDeviceCallback> setJniDeviceCallback(JNIEnv* env,
#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT      (-16)
                                                  jobject thiz,
#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK  (-17)
                                                  const sp<JNIDeviceCallback>& cb)
#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT       (-18)
{
#define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE       (-19)
    Mutex::Autolock l(sLock);
#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED    (-20)
    sp<JNIDeviceCallback> old =
            (JNIDeviceCallback*)env->GetLongField(thiz,
                                                  javaAudioRecordFields.nativeDeviceCallback);
    if (cb.get()) {
        cb->incStrong((void*)setJniDeviceCallback);
    }
    if (old != 0) {
        old->decStrong((void*)setJniDeviceCallback);
    }
    env->SetLongField(thiz, javaAudioRecordFields.nativeDeviceCallback, (jlong)cb.get());
    return old;
}


// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz)
static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz)
{
{
    Mutex::Autolock l(sLock);
    return getFieldSp<AudioRecord>(env, thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
    AudioRecord* const ar =
            (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
    return sp<AudioRecord>(ar);
}

static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar)
{
    Mutex::Autolock l(sLock);
    sp<AudioRecord> old =
            (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
    if (ar.get()) {
        ar->incStrong((void*)setAudioRecord);
    }
    if (old != 0) {
        old->decStrong((void*)setAudioRecord);
    }
    env->SetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (jlong)ar.get());
    return old;
}
}


// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
@@ -211,9 +170,8 @@ static jint android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject w
    env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
    env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
    nSession = NULL;
    nSession = NULL;


    sp<AudioRecord> lpRecorder = 0;
    sp<AudioRecord> lpRecorder;
    audiorecord_callback_cookie *lpCallbackData = NULL;
    sp<AudioRecordJNIStorage> callbackData;

    jclass clazz = env->GetObjectClass(thiz);
    jclass clazz = env->GetObjectClass(thiz);
    if (clazz == NULL) {
    if (clazz == NULL) {
        ALOGE("Can't find %s when setting up callback.", kClassPathName);
        ALOGE("Can't find %s when setting up callback.", kClassPathName);
@@ -287,18 +245,14 @@ static jint android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject w
        }
        }
        // create the callback information:
        // create the callback information:
        // this data will be passed with every AudioRecord callback
        // this data will be passed with every AudioRecord callback
        lpCallbackData = new audiorecord_callback_cookie;
        lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
        // we use a weak reference so the AudioRecord object can be garbage collected.
        // we use a weak reference so the AudioRecord object can be garbage collected.
        lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
        callbackData = sp<AudioRecordJNIStorage>::make(clazz, weak_this);
        lpCallbackData->busy = false;


        const status_t status =
        const status_t status =
                lpRecorder->set(paa->source, sampleRateInHertz,
                lpRecorder->set(paa->source, sampleRateInHertz,
                                format, // word length, PCM
                                format, // word length, PCM
                                localChanMask, frameCount,
                                localChanMask, frameCount,
                                recorderCallback, // callback_t
                                callbackData,   // callback
                                lpCallbackData,   // void* user
                                0,                // notificationFrames,
                                0,                // notificationFrames,
                                true,             // threadCanCallJava
                                true,             // threadCanCallJava
                                sessionId, AudioRecord::TRANSFER_DEFAULT, flags, -1,
                                sessionId, AudioRecord::TRANSFER_DEFAULT, flags, -1,
@@ -330,11 +284,8 @@ static jint android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject w


        // create the callback information:
        // create the callback information:
        // this data will be passed with every AudioRecord callback
        // this data will be passed with every AudioRecord callback
        lpCallbackData = new audiorecord_callback_cookie;
        // This next line makes little sense
        lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
        // callbackData = sp<AudioRecordJNIStorage>::make(clazz, weak_this);
        // we use a weak reference so the AudioRecord object can be garbage collected.
        lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
        lpCallbackData->busy = false;
    }
    }


    nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
    nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
@@ -352,26 +303,20 @@ static jint android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject w
        env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
        env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
    }
    }


    {   // scope for the lock
        Mutex::Autolock l(sLock);
        sAudioRecordCallBackCookies.add(lpCallbackData);
    }
    // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
    // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
    // of the Java object
    // of the Java object
    setAudioRecord(env, thiz, lpRecorder);
    setFieldSp(env, thiz, lpRecorder, javaAudioRecordFields.nativeRecorderInJavaObj);


    // save our newly created callback information in the "nativeCallbackCookie" field
    // save our newly created callback information in the "jniData" field
    // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
    // of the Java object (in mNativeJNIDataHandle) so we can free the memory in finalize()
    env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData);
    setFieldSp(env, thiz, callbackData, javaAudioRecordFields.jniData);


    return (jint) AUDIO_JAVA_SUCCESS;
    return (jint) AUDIO_JAVA_SUCCESS;


    // failure:
    // failure:
native_init_failure:
native_init_failure:
    env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
    setFieldSp(env, thiz, sp<AudioRecord>{}, javaAudioRecordFields.nativeRecorderInJavaObj);
    env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
    setFieldSp(env, thiz, sp<AudioRecordJNIStorage>{}, javaAudioRecordFields.jniData);
    delete lpCallbackData;
    env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);


    // lpRecorder goes out of scope, so reference count drops to zero
    // lpRecorder goes out of scope, so reference count drops to zero
    return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
    return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
@@ -411,36 +356,9 @@ android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)


#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
static void android_media_AudioRecord_release(JNIEnv *env,  jobject thiz) {
static void android_media_AudioRecord_release(JNIEnv *env,  jobject thiz) {
    sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0);
    if (lpRecorder == NULL) {
        return;
    }
    ALOGV("About to delete lpRecorder: %p", lpRecorder.get());
    lpRecorder->stop();

    audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetLongField(
        thiz, javaAudioRecordFields.nativeCallbackCookie);

    // reset the native resources in the Java object so any attempt to access
    // them after a call to release fails.
    env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);


    // delete the callback information
    setFieldSp(env, thiz, sp<AudioRecord>{}, javaAudioRecordFields.nativeRecorderInJavaObj);
    if (lpCookie) {
    setFieldSp(env, thiz, sp<AudioRecordJNIStorage>{}, javaAudioRecordFields.jniData);
        Mutex::Autolock l(sLock);
        ALOGV("deleting lpCookie: %p", lpCookie);
        while (lpCookie->busy) {
            if (lpCookie->cond.waitRelative(sLock,
                                            milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
                                                    NO_ERROR) {
                break;
            }
        }
        sAudioRecordCallBackCookies.remove(lpCookie);
        env->DeleteGlobalRef(lpCookie->audioRecord_class);
        env->DeleteGlobalRef(lpCookie->audioRecord_ref);
        delete lpCookie;
    }
}
}




@@ -685,43 +603,40 @@ static jint android_media_AudioRecord_getRoutedDeviceId(
    return (jint)lpRecorder->getRoutedDeviceId();
    return (jint)lpRecorder->getRoutedDeviceId();
}
}


// Enable and Disable Callback methods are synchronized on the Java side
static void android_media_AudioRecord_enableDeviceCallback(
static void android_media_AudioRecord_enableDeviceCallback(
                JNIEnv *env,  jobject thiz) {
                JNIEnv *env,  jobject thiz) {


    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    if (lpRecorder == 0) {
    if (lpRecorder == nullptr) {
        return;
        return;
    }
    }
    sp<JNIDeviceCallback> cb = getJniDeviceCallback(env, thiz);
    const auto pJniStorage =
    if (cb != 0) {
            getFieldSp<AudioRecordJNIStorage>(env, thiz, javaAudioRecordFields.jniData);
        return;
   if (pJniStorage == nullptr || pJniStorage->getDeviceCallback() != nullptr) {
    }
    audiorecord_callback_cookie *cookie =
            (audiorecord_callback_cookie *)env->GetLongField(thiz,
                                                     javaAudioRecordFields.nativeCallbackCookie);
    if (cookie == NULL) {
        return;
        return;
    }
    }


    cb = new JNIDeviceCallback(env, thiz, cookie->audioRecord_ref,
    pJniStorage->setDeviceCallback(
                               javaAudioRecordFields.postNativeEventInJava);
            sp<JNIDeviceCallback>::make(env, thiz, pJniStorage->getAudioTrackWeakRef(),
    status_t status = lpRecorder->addAudioDeviceCallback(cb);
                                        javaAudioRecordFields.postNativeEventInJava));
    if (status == NO_ERROR) {
    lpRecorder->addAudioDeviceCallback(pJniStorage->getDeviceCallback());
        setJniDeviceCallback(env, thiz, cb);
    }
}
}


static void android_media_AudioRecord_disableDeviceCallback(
static void android_media_AudioRecord_disableDeviceCallback(
                JNIEnv *env,  jobject thiz) {
                JNIEnv *env,  jobject thiz) {

    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    if (lpRecorder == 0) {
    if (lpRecorder == nullptr) {
        return;
        return;
    }
    }
    sp<JNIDeviceCallback> cb = setJniDeviceCallback(env, thiz, 0);
    const auto pJniStorage =
    if (cb != 0) {
            getFieldSp<AudioRecordJNIStorage>(env, thiz, javaAudioRecordFields.jniData);
        lpRecorder->removeAudioDeviceCallback(cb);

    if (pJniStorage == nullptr || pJniStorage->getDeviceCallback() == nullptr) {
        return;
    }
    }
    lpRecorder->removeAudioDeviceCallback(pJniStorage->getDeviceCallback());
    pJniStorage->setDeviceCallback(nullptr);
}
}


// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
@@ -962,17 +877,15 @@ static const JNINativeMethod gMethods[] = {


// field names found in android/media/AudioRecord.java
// field names found in android/media/AudioRecord.java
#define JAVA_POSTEVENT_CALLBACK_NAME  "postEventFromNative"
#define JAVA_POSTEVENT_CALLBACK_NAME  "postEventFromNative"
#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME  "mNativeRecorderInJavaObj"
#define JAVA_NATIVEAUDIORECORDERHANDLE_FIELD_NAME  "mNativeAudioRecordHandle"
#define JAVA_NATIVECALLBACKINFO_FIELD_NAME       "mNativeCallbackCookie"
#define JAVA_NATIVEJNIDATAHANDLE_FIELD_NAME        "mNativeJNIDataHandle"
#define JAVA_NATIVEDEVICECALLBACK_FIELD_NAME       "mNativeDeviceCallback"


// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
int register_android_media_AudioRecord(JNIEnv *env)
int register_android_media_AudioRecord(JNIEnv *env)
{
{
    javaAudioRecordFields.postNativeEventInJava = NULL;
    javaAudioRecordFields.postNativeEventInJava = NULL;
    javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
    javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
    javaAudioRecordFields.nativeCallbackCookie = NULL;
    javaAudioRecordFields.jniData = NULL;
    javaAudioRecordFields.nativeDeviceCallback = NULL;




    // Get the AudioRecord class
    // Get the AudioRecord class
@@ -983,15 +896,12 @@ int register_android_media_AudioRecord(JNIEnv *env)
            "(Ljava/lang/Object;IIILjava/lang/Object;)V");
            "(Ljava/lang/Object;IIILjava/lang/Object;)V");


    // Get the variables
    // Get the variables
    //    mNativeRecorderInJavaObj
    //    mNativeAudioRecordHandle
    javaAudioRecordFields.nativeRecorderInJavaObj = GetFieldIDOrDie(env,
    javaAudioRecordFields.nativeRecorderInJavaObj = GetFieldIDOrDie(env,
            audioRecordClass, JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "J");
            audioRecordClass, JAVA_NATIVEAUDIORECORDERHANDLE_FIELD_NAME, "J");
    //     mNativeCallbackCookie
    //   mNativeJNIDataHandle
    javaAudioRecordFields.nativeCallbackCookie = GetFieldIDOrDie(env,
    javaAudioRecordFields.jniData = GetFieldIDOrDie(env,
            audioRecordClass, JAVA_NATIVECALLBACKINFO_FIELD_NAME, "J");
            audioRecordClass, JAVA_NATIVEJNIDATAHANDLE_FIELD_NAME, "J");

    javaAudioRecordFields.nativeDeviceCallback = GetFieldIDOrDie(env,
            audioRecordClass, JAVA_NATIVEDEVICECALLBACK_FIELD_NAME, "J");


    // Get the RecordTimestamp class and fields
    // Get the RecordTimestamp class and fields
    jclass audioTimestampClass = FindClassOrDie(env, "android/media/AudioTimestamp");
    jclass audioTimestampClass = FindClassOrDie(env, "android/media/AudioTimestamp");
+34 −0
Original line number Original line Diff line number Diff line
@@ -64,5 +64,39 @@ inline JNIEnv* getJNIEnvOrDie() {
    return env;
    return env;
}
}


class GlobalRef {
  public:
    GlobalRef(jobject object) : GlobalRef(object, AndroidRuntime::getJNIEnv()) {}

    GlobalRef(jobject object, JNIEnv* env) {
        LOG_ALWAYS_FATAL_IF(env == nullptr, "Invalid JNIEnv when attempting to create a GlobalRef");
        mGlobalRef = env->NewGlobalRef(object);
        LOG_ALWAYS_FATAL_IF(env->IsSameObject(object, nullptr) == JNI_TRUE,
                            "Creating GlobalRef from null object");
    }

    GlobalRef(const GlobalRef& other) : GlobalRef(other.mGlobalRef) {}

    GlobalRef(GlobalRef&& other) : mGlobalRef(other.mGlobalRef) { other.mGlobalRef = nullptr; }

    // Logically const
    GlobalRef& operator=(const GlobalRef& other) = delete;

    GlobalRef& operator=(GlobalRef&& other) = delete;

    ~GlobalRef() {
        if (mGlobalRef == nullptr) return; // No reference to decrement
        getJNIEnvOrDie()->DeleteGlobalRef(mGlobalRef);
    }

    // Valid as long as this wrapper is in scope.
    jobject get() const {
      return mGlobalRef;
    }

  private:
    // Logically const. Not actually const so we can move from GlobalRef
    jobject mGlobalRef;
};


} // namespace android
} // namespace android
+5 −14
Original line number Original line Diff line number Diff line
@@ -187,22 +187,14 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
     */
     */
    @SuppressWarnings("unused")
    @SuppressWarnings("unused")
    @UnsupportedAppUsage
    @UnsupportedAppUsage
    private long mNativeRecorderInJavaObj;
    private long mNativeAudioRecordHandle;


    /**
    /**
     * Accessed by native methods: provides access to the callback data.
     * Accessed by native methods: provides access to the callback data.
     */
     */
    @SuppressWarnings("unused")
    @SuppressWarnings("unused")
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    private long mNativeCallbackCookie;
    private long mNativeJNIDataHandle;

    /**
     * Accessed by native methods: provides access to the JNIDeviceCallback instance.
     */
    @SuppressWarnings("unused")
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    private long mNativeDeviceCallback;



    //---------------------------------------------------------
    //---------------------------------------------------------
    // Member variables
    // Member variables
@@ -492,9 +484,8 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
     * value here as no error checking is or can be done.
     * value here as no error checking is or can be done.
     */
     */
    /*package*/ AudioRecord(long nativeRecordInJavaObj) {
    /*package*/ AudioRecord(long nativeRecordInJavaObj) {
        mNativeRecorderInJavaObj = 0;
        mNativeAudioRecordHandle = 0;
        mNativeCallbackCookie = 0;
        mNativeJNIDataHandle = 0;
        mNativeDeviceCallback = 0;


        // other initialization...
        // other initialization...
        if (nativeRecordInJavaObj != 0) {
        if (nativeRecordInJavaObj != 0) {
@@ -2131,7 +2122,7 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection,
     * @hide
     * @hide
     */
     */
    public int getPortId() {
    public int getPortId() {
        if (mNativeRecorderInJavaObj == 0) {
        if (mNativeAudioRecordHandle == 0) {
            return 0;
            return 0;
        }
        }
        try {
        try {