Enable JAudioTrack.

MediaPlayer2 AudioOutput using public APIs.

Bug: 112549970
Test: android.media.cts.MediaPlayer2Test, android.media.cts.RoutingTest
Change-Id: Id76d80c040a52fd2ab724999697e222495906aec
diff --git a/media/libmediaplayer2/JAudioTrack.cpp b/media/libmediaplayer2/JAudioTrack.cpp
index 778ae1b..5c0c6ea 100644
--- a/media/libmediaplayer2/JAudioTrack.cpp
+++ b/media/libmediaplayer2/JAudioTrack.cpp
@@ -28,7 +28,6 @@
 // TODO: Store Java class/methodID as a member variable in the class.
 // TODO: Add NULL && Exception checks after every JNI call.
 JAudioTrack::JAudioTrack(                             // < Usages of the arguments are below >
-        audio_stream_type_t streamType,               // AudioAudioAttributes
         uint32_t sampleRate,                          // AudioFormat && bufferSizeInBytes
         audio_format_t format,                        // AudioFormat && bufferSizeInBytes
         audio_channel_mask_t channelMask,             // AudioFormat && bufferSizeInBytes
@@ -40,8 +39,10 @@
         float maxRequiredSpeed) {                     // bufferSizeInBytes
 
     JNIEnv *env = JavaVMHelper::getJNIEnv();
+
     jclass jAudioTrackCls = env->FindClass("android/media/AudioTrack");
-    mAudioTrackCls = (jclass) env->NewGlobalRef(jAudioTrackCls);
+    mAudioTrackCls = reinterpret_cast<jclass>(env->NewGlobalRef(jAudioTrackCls));
+    env->DeleteLocalRef(jAudioTrackCls);
 
     maxRequiredSpeed = std::min(std::max(maxRequiredSpeed, 1.0f), AUDIO_TIMESTRETCH_SPEED_MAX);
 
@@ -64,10 +65,13 @@
     jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
     jobject jBuilderObj = env->NewObject(jBuilderCls, jBuilderCtor);
 
+    jobject jAudioAttributesObj = JAudioAttributes::createAudioAttributesObj(env, pAttributes);
+    mAudioAttributesObj = reinterpret_cast<jobject>(env->NewGlobalRef(jAudioAttributesObj));
+    env->DeleteLocalRef(jAudioAttributesObj);
+
     jmethodID jSetAudioAttributes = env->GetMethodID(jBuilderCls, "setAudioAttributes",
             "(Landroid/media/AudioAttributes;)Landroid/media/AudioTrack$Builder;");
-    jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetAudioAttributes,
-            JAudioAttributes::createAudioAttributesObj(env, pAttributes, streamType));
+    jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetAudioAttributes, mAudioAttributesObj);
 
     jmethodID jSetAudioFormat = env->GetMethodID(jBuilderCls, "setAudioFormat",
             "(Landroid/media/AudioFormat;)Landroid/media/AudioTrack$Builder;");
@@ -100,7 +104,9 @@
     }
 
     jmethodID jBuild = env->GetMethodID(jBuilderCls, "build", "()Landroid/media/AudioTrack;");
-    mAudioTrackObj = env->CallObjectMethod(jBuilderObj, jBuild);
+    jobject jAudioTrackObj = env->CallObjectMethod(jBuilderObj, jBuild);
+    mAudioTrackObj = reinterpret_cast<jobject>(env->NewGlobalRef(jAudioTrackObj));
+    env->DeleteLocalRef(jBuilderObj);
 
     if (cbf != NULL) {
         // Set offload mode callback
@@ -118,6 +124,8 @@
 JAudioTrack::~JAudioTrack() {
     JNIEnv *env = JavaVMHelper::getJNIEnv();
     env->DeleteGlobalRef(mAudioTrackCls);
+    env->DeleteGlobalRef(mAudioTrackObj);
+    env->DeleteGlobalRef(mAudioAttributesObj);
 }
 
 size_t JAudioTrack::frameCount() {
@@ -151,21 +159,21 @@
     return NO_ERROR;
 }
 
-bool JAudioTrack::getTimestamp(AudioTimestamp& timestamp) {
+status_t JAudioTrack::getTimestamp(AudioTimestamp& timestamp) {
     JNIEnv *env = JavaVMHelper::getJNIEnv();
 
     jclass jAudioTimeStampCls = env->FindClass("android/media/AudioTimestamp");
     jobject jAudioTimeStampObj = env->AllocObject(jAudioTimeStampCls);
 
-    jfieldID jFramePosition = env->GetFieldID(jAudioTimeStampCls, "framePosition", "L");
-    jfieldID jNanoTime = env->GetFieldID(jAudioTimeStampCls, "nanoTime", "L");
+    jfieldID jFramePosition = env->GetFieldID(jAudioTimeStampCls, "framePosition", "J");
+    jfieldID jNanoTime = env->GetFieldID(jAudioTimeStampCls, "nanoTime", "J");
 
     jmethodID jGetTimestamp = env->GetMethodID(mAudioTrackCls,
-            "getTimestamp", "(Landroid/media/AudioTimestamp)B");
+            "getTimestamp", "(Landroid/media/AudioTimestamp;)Z");
     bool success = env->CallBooleanMethod(mAudioTrackObj, jGetTimestamp, jAudioTimeStampObj);
 
     if (!success) {
-        return false;
+        return NO_INIT;
     }
 
     long long framePosition = env->GetLongField(jAudioTimeStampObj, jFramePosition);
@@ -178,7 +186,7 @@
     timestamp.mTime = ts;
     timestamp.mPosition = (uint32_t) framePosition;
 
-    return true;
+    return NO_ERROR;
 }
 
 status_t JAudioTrack::getTimestamp(ExtendedTimestamp *timestamp __unused) {
@@ -423,6 +431,35 @@
     return audioFormatToNative(javaFormat);
 }
 
+size_t JAudioTrack::frameSize() {
+    JNIEnv *env = JavaVMHelper::getJNIEnv();
+
+    // TODO: Calculated here implementing the logic in AudioTrack.java
+    // wait for AudioTrack.java exposing this parameter (i.e. getFrameSizeInBtytes())
+    jmethodID jGetAudioFormat = env->GetMethodID(mAudioTrackCls, "getAudioFormat", "()I");
+    int javaFormat = env->CallIntMethod(mAudioTrackObj, jGetAudioFormat);
+
+    jclass jAudioFormatCls = env->FindClass("android/media/AudioFormat");
+    jmethodID jIsEncodingLinearFrames = env->GetStaticMethodID(
+            jAudioFormatCls, "isEncodingLinearFrames", "(I)Z");
+    jboolean javaIsEncodingLinearFrames = env->CallStaticBooleanMethod(
+            jAudioFormatCls, jIsEncodingLinearFrames, javaFormat);
+
+    if (javaIsEncodingLinearFrames == false) {
+        return 1;
+    }
+
+    jmethodID jGetBytesPerSample = env->GetStaticMethodID(jAudioFormatCls,
+            "getBytesPerSample", "(I)I");
+    int javaBytesPerSample = env->CallStaticIntMethod(jAudioFormatCls,
+            jGetBytesPerSample, javaFormat);
+
+    jmethodID jGetChannelCount = env->GetMethodID(mAudioTrackCls, "getChannelCount", "()I");
+    int javaChannelCount = env->CallIntMethod(mAudioTrackObj, jGetChannelCount);
+
+    return javaChannelCount * javaBytesPerSample;
+}
+
 status_t JAudioTrack::dump(int fd, const Vector<String16>& args __unused) const
 {
     String8 result;
@@ -432,10 +469,6 @@
     // TODO: Remove logs that includes unavailable information from below.
 //    result.appendFormat("  status(%d), state(%d), session Id(%d), flags(%#x)\n",
 //                        mStatus, mState, mSessionId, mFlags);
-//    result.appendFormat("  stream type(%d), left - right volume(%f, %f)\n",
-//                        (mStreamType == AUDIO_STREAM_DEFAULT) ?
-//                                audio_attributes_to_stream_type(&mAttributes) : mStreamType,
-//                        mVolume[AUDIO_INTERLEAVE_LEFT], mVolume[AUDIO_INTERLEAVE_RIGHT]);
 //    result.appendFormat("  format(%#x), channel mask(%#x), channel count(%u)\n",
 //                  format(), mChannelMask, channelCount());
 //    result.appendFormat("  sample rate(%u), original sample rate(%u), speed(%f)\n",
@@ -462,7 +495,7 @@
         return AUDIO_PORT_HANDLE_NONE;
     }
 
-    jclass jAudioDeviceInfoCls = env->FindClass("Landroid/media/AudioDeviceInfo");
+    jclass jAudioDeviceInfoCls = env->FindClass("android/media/AudioDeviceInfo");
     jmethodID jGetId = env->GetMethodID(jAudioDeviceInfoCls, "getId", "()I");
     jint routedDeviceId = env->CallIntMethod(jAudioDeviceInfoObj, jGetId);
     return routedDeviceId;
@@ -478,13 +511,22 @@
 status_t JAudioTrack::setOutputDevice(audio_port_handle_t deviceId) {
     JNIEnv *env = JavaVMHelper::getJNIEnv();
     jclass jMP2ImplCls = env->FindClass("android/media/MediaPlayer2Impl");
-    jmethodID jSetAudioOutputDeviceById = env->GetMethodID(
+    jmethodID jSetAudioOutputDeviceById = env->GetStaticMethodID(
             jMP2ImplCls, "setAudioOutputDeviceById", "(Landroid/media/AudioTrack;I)Z");
     jboolean result = env->CallStaticBooleanMethod(
             jMP2ImplCls, jSetAudioOutputDeviceById, mAudioTrackObj, deviceId);
     return result == true ? NO_ERROR : BAD_VALUE;
 }
 
+audio_stream_type_t JAudioTrack::getAudioStreamType() {
+    JNIEnv *env = JavaVMHelper::getJNIEnv();
+    jclass jAudioAttributesCls = env->FindClass("android/media/AudioAttributes");
+    jmethodID jGetVolumeControlStream = env->GetMethodID(jAudioAttributesCls,
+                                        "getVolumeControlStream", "()I");
+    int javaAudioStreamType = env->CallIntMethod(mAudioAttributesObj, jGetVolumeControlStream);
+    return (audio_stream_type_t)javaAudioStreamType;
+}
+
 status_t JAudioTrack::pendingDuration(int32_t *msec) {
     if (msec == nullptr) {
         return BAD_VALUE;
@@ -526,18 +568,85 @@
     return NO_ERROR;
 }
 
-status_t JAudioTrack::addAudioDeviceCallback(
-        const sp<AudioSystem::AudioDeviceCallback>& callback __unused) {
-    // TODO: Implement this after appropriate Java AudioTrack method is available.
+status_t JAudioTrack::addAudioDeviceCallback(jobject listener, jobject handler) {
+    JNIEnv *env = JavaVMHelper::getJNIEnv();
+    jmethodID jAddOnRoutingChangedListener = env->GetMethodID(mAudioTrackCls,
+            "addOnRoutingChangedListener",
+            "(Landroid/media/AudioRouting$OnRoutingChangedListener;Landroid/os/Handler;)V");
+    env->CallVoidMethod(mAudioTrackObj, jAddOnRoutingChangedListener, listener, handler);
     return NO_ERROR;
 }
 
-status_t JAudioTrack::removeAudioDeviceCallback(
-        const sp<AudioSystem::AudioDeviceCallback>& callback __unused) {
-    // TODO: Implement this after appropriate Java AudioTrack method is available.
+status_t JAudioTrack::removeAudioDeviceCallback(jobject listener) {
+    JNIEnv *env = JavaVMHelper::getJNIEnv();
+    jmethodID jRemoveOnRoutingChangedListener = env->GetMethodID(mAudioTrackCls,
+            "removeOnRoutingChangedListener",
+            "(Landroid/media/AudioRouting$OnRoutingChangedListener;)V");
+    env->CallVoidMethod(mAudioTrackObj, jRemoveOnRoutingChangedListener, listener);
     return NO_ERROR;
 }
 
+void JAudioTrack::registerRoutingDelegates(
+        std::vector<std::pair<jobject, jobject>>& routingDelegates) {
+    for (std::vector<std::pair<jobject, jobject>>::iterator it = routingDelegates.begin();
+            it != routingDelegates.end(); it++) {
+        addAudioDeviceCallback(it->second, getHandler(it->second));
+    }
+}
+
+/////////////////////////////////////////////////////////////
+///                Static methods begin                   ///
+/////////////////////////////////////////////////////////////
+jobject JAudioTrack::getListener(const jobject routingDelegateObj) {
+    JNIEnv *env = JavaVMHelper::getJNIEnv();
+    jclass jRoutingDelegateCls = env->FindClass("android/media/RoutingDelegate");
+    jmethodID jGetListener = env->GetMethodID(jRoutingDelegateCls,
+            "getListener", "()Landroid/media/AudioRouting$OnRoutingChangedListener;");
+    return env->CallObjectMethod(routingDelegateObj, jGetListener);
+}
+
+jobject JAudioTrack::getHandler(const jobject routingDelegateObj) {
+    JNIEnv *env = JavaVMHelper::getJNIEnv();
+    jclass jRoutingDelegateCls = env->FindClass("android/media/RoutingDelegate");
+    jmethodID jGetHandler = env->GetMethodID(jRoutingDelegateCls,
+        "getHandler", "()Landroid/os/Handler;");
+    return env->CallObjectMethod(routingDelegateObj, jGetHandler);
+}
+
+jobject JAudioTrack::addGlobalRef(const jobject obj) {
+    JNIEnv *env = JavaVMHelper::getJNIEnv();
+    return reinterpret_cast<jobject>(env->NewGlobalRef(obj));
+}
+
+status_t JAudioTrack::removeGlobalRef(const jobject obj) {
+    if (obj == NULL) {
+        return BAD_VALUE;
+    }
+    JNIEnv *env = JavaVMHelper::getJNIEnv();
+    env->DeleteGlobalRef(obj);
+    return NO_ERROR;
+}
+
+jobject JAudioTrack::findByKey(std::vector<std::pair<jobject, jobject>>& mp, const jobject key) {
+    JNIEnv *env = JavaVMHelper::getJNIEnv();
+    for (std::vector<std::pair<jobject, jobject>>::iterator it = mp.begin(); it != mp.end(); it++) {
+        if (env->IsSameObject(it->first, key)) {
+            return it->second;
+        }
+    }
+    return nullptr;
+}
+
+void JAudioTrack::eraseByKey(std::vector<std::pair<jobject, jobject>>& mp, const jobject key) {
+    JNIEnv *env = JavaVMHelper::getJNIEnv();
+    for (std::vector<std::pair<jobject, jobject>>::iterator it = mp.begin(); it != mp.end(); it++) {
+        if (env->IsSameObject(it->first, key)) {
+            mp.erase(it);
+            return;
+        }
+    }
+}
+
 /////////////////////////////////////////////////////////////
 ///                Private method begins                  ///
 /////////////////////////////////////////////////////////////