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 
JMediaDataSource(JNIEnv * env,jobject source)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 
~JMediaDataSource()61 JMediaDataSource::~JMediaDataSource() {
62     JNIEnv* env = AndroidRuntime::getJNIEnv();
63     env->DeleteGlobalRef(mMediaDataSourceObj);
64     env->DeleteGlobalRef(mByteArrayObj);
65 }
66 
getIMemory()67 sp<IMemory> JMediaDataSource::getIMemory() {
68     Mutex::Autolock lock(mLock);
69     return mMemory;
70 }
71 
readAt(off64_t offset,size_t size)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 
getSize(off64_t * size)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 
close()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 
getFlags()155 uint32_t JMediaDataSource::getFlags() {
156     return 0;
157 }
158 
toString()159 String8 JMediaDataSource::toString() {
160     return String8::format("JMediaDataSource(pid %d, uid %d)", getpid(), getuid());
161 }
162 
DrmInitialization(const char *)163 sp<DecryptHandle> JMediaDataSource::DrmInitialization(const char * /* mime */) {
164     return NULL;
165 }
166 
167 }  // namespace android
168