Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright 2017, The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 //#define LOG_NDEBUG 0
     18 #define LOG_TAG "JDataSourceCallback-JNI"
     19 #include <utils/Log.h>
     20 
     21 #include "android_media_DataSourceCallback.h"
     22 
     23 #include "log/log.h"
     24 #include "jni.h"
     25 #include <nativehelper/JNIHelp.h>
     26 
     27 #include <drm/drm_framework_common.h>
     28 #include <mediaplayer2/JavaVMHelper.h>
     29 #include <media/stagefright/foundation/ADebug.h>
     30 #include <nativehelper/ScopedLocalRef.h>
     31 
     32 namespace android {
     33 
     34 static const size_t kBufferSize = 64 * 1024;
     35 
     36 JDataSourceCallback::JDataSourceCallback(JNIEnv* env, jobject source)
     37     : mJavaObjStatus(OK),
     38       mSizeIsCached(false),
     39       mCachedSize(0) {
     40     mDataSourceCallbackObj = env->NewGlobalRef(source);
     41     CHECK(mDataSourceCallbackObj != NULL);
     42 
     43     ScopedLocalRef<jclass> media2DataSourceClass(env, env->GetObjectClass(mDataSourceCallbackObj));
     44     CHECK(media2DataSourceClass.get() != NULL);
     45 
     46     mReadAtMethod = env->GetMethodID(media2DataSourceClass.get(), "readAt", "(J[BII)I");
     47     CHECK(mReadAtMethod != NULL);
     48     mGetSizeMethod = env->GetMethodID(media2DataSourceClass.get(), "getSize", "()J");
     49     CHECK(mGetSizeMethod != NULL);
     50     mCloseMethod = env->GetMethodID(media2DataSourceClass.get(), "close", "()V");
     51     CHECK(mCloseMethod != NULL);
     52 
     53     ScopedLocalRef<jbyteArray> tmp(env, env->NewByteArray(kBufferSize));
     54     mByteArrayObj = (jbyteArray)env->NewGlobalRef(tmp.get());
     55     CHECK(mByteArrayObj != NULL);
     56 }
     57 
     58 JDataSourceCallback::~JDataSourceCallback() {
     59     JNIEnv* env = JavaVMHelper::getJNIEnv();
     60     env->DeleteGlobalRef(mDataSourceCallbackObj);
     61     env->DeleteGlobalRef(mByteArrayObj);
     62 }
     63 
     64 status_t JDataSourceCallback::initCheck() const {
     65     return OK;
     66 }
     67 
     68 ssize_t JDataSourceCallback::readAt(off64_t offset, void *data, size_t size) {
     69     Mutex::Autolock lock(mLock);
     70 
     71     if (mJavaObjStatus != OK) {
     72         return -1;
     73     }
     74     if (size > kBufferSize) {
     75         size = kBufferSize;
     76     }
     77 
     78     JNIEnv* env = JavaVMHelper::getJNIEnv();
     79     jint numread = env->CallIntMethod(mDataSourceCallbackObj, mReadAtMethod,
     80             (jlong)offset, mByteArrayObj, (jint)0, (jint)size);
     81     if (env->ExceptionCheck()) {
     82         ALOGW("An exception occurred in readAt()");
     83         jniLogException(env, ANDROID_LOG_WARN, LOG_TAG);
     84         env->ExceptionClear();
     85         mJavaObjStatus = UNKNOWN_ERROR;
     86         return -1;
     87     }
     88     if (numread < 0) {
     89         if (numread != -1) {
     90             ALOGW("An error occurred in readAt()");
     91             mJavaObjStatus = UNKNOWN_ERROR;
     92             return -1;
     93         } else {
     94             // numread == -1 indicates EOF
     95             return 0;
     96         }
     97     }
     98     if ((size_t)numread > size) {
     99         ALOGE("readAt read too many bytes.");
    100         mJavaObjStatus = UNKNOWN_ERROR;
    101         return -1;
    102     }
    103 
    104     ALOGV("readAt %lld / %zu => %d.", (long long)offset, size, numread);
    105     env->GetByteArrayRegion(mByteArrayObj, 0, numread, (jbyte*)data);
    106     return numread;
    107 }
    108 
    109 status_t JDataSourceCallback::getSize(off64_t* size) {
    110     Mutex::Autolock lock(mLock);
    111 
    112     if (mJavaObjStatus != OK) {
    113         return UNKNOWN_ERROR;
    114     }
    115     if (mSizeIsCached) {
    116         *size = mCachedSize;
    117         return OK;
    118     }
    119 
    120     JNIEnv* env = JavaVMHelper::getJNIEnv();
    121     *size = env->CallLongMethod(mDataSourceCallbackObj, mGetSizeMethod);
    122     if (env->ExceptionCheck()) {
    123         ALOGW("An exception occurred in getSize()");
    124         jniLogException(env, ANDROID_LOG_WARN, LOG_TAG);
    125         env->ExceptionClear();
    126         // After returning an error, size shouldn't be used by callers.
    127         *size = UNKNOWN_ERROR;
    128         mJavaObjStatus = UNKNOWN_ERROR;
    129         return UNKNOWN_ERROR;
    130     }
    131 
    132     // The minimum size should be -1, which indicates unknown size.
    133     if (*size < 0) {
    134         *size = -1;
    135     }
    136 
    137     mCachedSize = *size;
    138     mSizeIsCached = true;
    139     return OK;
    140 }
    141 
    142 void JDataSourceCallback::close() {
    143     Mutex::Autolock lock(mLock);
    144 
    145     JNIEnv* env = JavaVMHelper::getJNIEnv();
    146     env->CallVoidMethod(mDataSourceCallbackObj, mCloseMethod);
    147     // The closed state is effectively the same as an error state.
    148     mJavaObjStatus = UNKNOWN_ERROR;
    149 }
    150 
    151 String8 JDataSourceCallback::toString() {
    152     return String8::format("JDataSourceCallback(pid %d, uid %d)", getpid(), getuid());
    153 }
    154 
    155 String8 JDataSourceCallback::getMIMEType() const {
    156     return String8("application/octet-stream");
    157 }
    158 
    159 }  // namespace android
    160