Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright 2015, 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 "JMediaDataSource-JNI"
     19 #include <utils/Log.h>
     20 
     21 #include "android_media_MediaDataSource.h"
     22 
     23 #include "android_runtime/AndroidRuntime.h"
     24 #include "android_runtime/Log.h"
     25 #include "jni.h"
     26 #include "JNIHelp.h"
     27 
     28 #include <binder/MemoryDealer.h>
     29 #include <drm/drm_framework_common.h>
     30 #include <media/stagefright/foundation/ADebug.h>
     31 #include <nativehelper/ScopedLocalRef.h>
     32 
     33 namespace android {
     34 
     35 JMediaDataSource::JMediaDataSource(JNIEnv* env, jobject source)
     36     : mJavaObjStatus(OK), mSizeIsCached(false), mCachedSize(0), mMemory(NULL) {
     37     mMediaDataSourceObj = env->NewGlobalRef(source);
     38     CHECK(mMediaDataSourceObj != NULL);
     39 
     40     ScopedLocalRef<jclass> mediaDataSourceClass(env, env->GetObjectClass(mMediaDataSourceObj));
     41     CHECK(mediaDataSourceClass.get() != NULL);
     42 
     43     mReadMethod = env->GetMethodID(mediaDataSourceClass.get(), "readAt", "(J[BII)I");
     44     CHECK(mReadMethod != NULL);
     45     mGetSizeMethod = env->GetMethodID(mediaDataSourceClass.get(), "getSize", "()J");
     46     CHECK(mGetSizeMethod != NULL);
     47     mCloseMethod = env->GetMethodID(mediaDataSourceClass.get(), "close", "()V");
     48     CHECK(mCloseMethod != NULL);
     49 
     50     ScopedLocalRef<jbyteArray> tmp(env, env->NewByteArray(kBufferSize));
     51     mByteArrayObj = (jbyteArray)env->NewGlobalRef(tmp.get());
     52     CHECK(mByteArrayObj != NULL);
     53 
     54     sp<MemoryDealer> memoryDealer = new MemoryDealer(kBufferSize, "JMediaDataSource");
     55     mMemory = memoryDealer->allocate(kBufferSize);
     56     if (mMemory == NULL) {
     57         ALOGE("Failed to allocate memory!");
     58     }
     59 }
     60 
     61 JMediaDataSource::~JMediaDataSource() {
     62     JNIEnv* env = AndroidRuntime::getJNIEnv();
     63     env->DeleteGlobalRef(mMediaDataSourceObj);
     64     env->DeleteGlobalRef(mByteArrayObj);
     65 }
     66 
     67 sp<IMemory> JMediaDataSource::getIMemory() {
     68     Mutex::Autolock lock(mLock);
     69     return mMemory;
     70 }
     71 
     72 ssize_t JMediaDataSource::readAt(off64_t offset, size_t size) {
     73     Mutex::Autolock lock(mLock);
     74 
     75     if (mJavaObjStatus != OK || mMemory == NULL) {
     76         return -1;
     77     }
     78     if (size > kBufferSize) {
     79         size = kBufferSize;
     80     }
     81 
     82     JNIEnv* env = AndroidRuntime::getJNIEnv();
     83     jint numread = env->CallIntMethod(mMediaDataSourceObj, mReadMethod,
     84             (jlong)offset, mByteArrayObj, (jint)0, (jint)size);
     85     if (env->ExceptionCheck()) {
     86         ALOGW("An exception occurred in readAt()");
     87         LOGW_EX(env);
     88         env->ExceptionClear();
     89         mJavaObjStatus = UNKNOWN_ERROR;
     90         return -1;
     91     }
     92     if (numread < 0) {
     93         if (numread != -1) {
     94             ALOGW("An error occurred in readAt()");
     95             mJavaObjStatus = UNKNOWN_ERROR;
     96             return -1;
     97         } else {
     98             // numread == -1 indicates EOF
     99             return 0;
    100         }
    101     }
    102     if ((size_t)numread > size) {
    103         ALOGE("readAt read too many bytes.");
    104         mJavaObjStatus = UNKNOWN_ERROR;
    105         return -1;
    106     }
    107 
    108     ALOGV("readAt %lld / %zu => %d.", (long long)offset, size, numread);
    109     env->GetByteArrayRegion(mByteArrayObj, 0, numread, (jbyte*)mMemory->pointer());
    110     return numread;
    111 }
    112 
    113 status_t JMediaDataSource::getSize(off64_t* size) {
    114     Mutex::Autolock lock(mLock);
    115 
    116     if (mJavaObjStatus != OK) {
    117         return UNKNOWN_ERROR;
    118     }
    119     if (mSizeIsCached) {
    120         *size = mCachedSize;
    121         return OK;
    122     }
    123 
    124     JNIEnv* env = AndroidRuntime::getJNIEnv();
    125     *size = env->CallLongMethod(mMediaDataSourceObj, mGetSizeMethod);
    126     if (env->ExceptionCheck()) {
    127         ALOGW("An exception occurred in getSize()");
    128         LOGW_EX(env);
    129         env->ExceptionClear();
    130         // After returning an error, size shouldn't be used by callers.
    131         *size = UNKNOWN_ERROR;
    132         mJavaObjStatus = UNKNOWN_ERROR;
    133         return UNKNOWN_ERROR;
    134     }
    135 
    136     // The minimum size should be -1, which indicates unknown size.
    137     if (*size < 0) {
    138         *size = -1;
    139     }
    140 
    141     mCachedSize = *size;
    142     mSizeIsCached = true;
    143     return OK;
    144 }
    145 
    146 void JMediaDataSource::close() {
    147     Mutex::Autolock lock(mLock);
    148 
    149     JNIEnv* env = AndroidRuntime::getJNIEnv();
    150     env->CallVoidMethod(mMediaDataSourceObj, mCloseMethod);
    151     // The closed state is effectively the same as an error state.
    152     mJavaObjStatus = UNKNOWN_ERROR;
    153 }
    154 
    155 uint32_t JMediaDataSource::getFlags() {
    156     return 0;
    157 }
    158 
    159 String8 JMediaDataSource::toString() {
    160     return String8::format("JMediaDataSource(pid %d, uid %d)", getpid(), getuid());
    161 }
    162 
    163 sp<DecryptHandle> JMediaDataSource::DrmInitialization(const char * /* mime */) {
    164     return NULL;
    165 }
    166 
    167 }  // namespace android
    168