1 /*
2  * Copyright 2019, 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 "AndroidMediaStreams"
19 
20 #include <utils/Log.h>
21 #include "android_media_Streams.h"
22 
23 #include <media/stagefright/foundation/ADebug.h>
24 #include <media/stagefright/foundation/ABuffer.h>
25 #include <media/stagefright/foundation/AMessage.h>
26 
27 #include <nativehelper/ScopedLocalRef.h>
28 
29 namespace android {
30 
FileStream(const int fd)31 FileStream::FileStream(const int fd)
32     : mPosition(0) {
33     mFile = fdopen(fd, "r");
34     if (mFile == NULL) {
35         return;
36     }
37 }
38 
FileStream(const String8 filename)39 FileStream::FileStream(const String8 filename)
40     : mPosition(0) {
41     mFile = fopen(filename.string(), "r");
42     if (mFile == NULL) {
43         return;
44     }
45 }
46 
~FileStream()47 FileStream::~FileStream() {
48     if (mFile != NULL) {
49         fclose(mFile);
50         mFile = NULL;
51     }
52 }
53 
GetData(const size_t offset,const size_t length,std::uint8_t * data)54 piex::Error FileStream::GetData(
55         const size_t offset, const size_t length, std::uint8_t* data) {
56     if (mFile == NULL) {
57         return piex::Error::kFail;
58     }
59 
60     // Seek first.
61     if (mPosition != offset) {
62         fseek(mFile, offset, SEEK_SET);
63     }
64 
65     // Read bytes.
66     size_t size = fread((void*)data, sizeof(std::uint8_t), length, mFile);
67     mPosition += size;
68 
69     // Handle errors and verify the size.
70     if (ferror(mFile) || size != length) {
71         ALOGV("GetData read failed: (offset: %zu, length: %zu)", offset, length);
72         return piex::Error::kFail;
73     }
74     return piex::Error::kOk;
75 }
76 
exists() const77 bool FileStream::exists() const {
78     return mFile != NULL;
79 }
80 
GetExifFromRawImage(piex::StreamInterface * stream,const String8 & filename,piex::PreviewImageData & image_data)81 bool GetExifFromRawImage(
82         piex::StreamInterface* stream, const String8& filename,
83         piex::PreviewImageData& image_data) {
84     // Reset the PreviewImageData to its default.
85     image_data = piex::PreviewImageData();
86 
87     if (!piex::IsRaw(stream)) {
88         // Format not supported.
89         ALOGV("Format not supported: %s", filename.string());
90         return false;
91     }
92 
93     piex::Error err = piex::GetPreviewImageData(stream, &image_data);
94 
95     if (err != piex::Error::kOk) {
96         // The input data seems to be broken.
97         ALOGV("Raw image not detected: %s (piex error code: %d)", filename.string(), (int32_t)err);
98         return false;
99     }
100 
101     return true;
102 }
103 
ConvertKeyValueArraysToKeyedVector(JNIEnv * env,jobjectArray keys,jobjectArray values,KeyedVector<String8,String8> * keyedVector)104 bool ConvertKeyValueArraysToKeyedVector(
105         JNIEnv *env, jobjectArray keys, jobjectArray values,
106         KeyedVector<String8, String8>* keyedVector) {
107 
108     int nKeyValuePairs = 0;
109     bool failed = false;
110     if (keys != NULL && values != NULL) {
111         nKeyValuePairs = env->GetArrayLength(keys);
112         failed = (nKeyValuePairs != env->GetArrayLength(values));
113     }
114 
115     if (!failed) {
116         failed = ((keys != NULL && values == NULL) ||
117                   (keys == NULL && values != NULL));
118     }
119 
120     if (failed) {
121         ALOGE("keys and values arrays have different length");
122         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
123         return false;
124     }
125 
126     for (int i = 0; i < nKeyValuePairs; ++i) {
127         // No need to check on the ArrayIndexOutOfBoundsException, since
128         // it won't happen here.
129         jstring key = (jstring) env->GetObjectArrayElement(keys, i);
130         jstring value = (jstring) env->GetObjectArrayElement(values, i);
131 
132         const char* keyStr = env->GetStringUTFChars(key, NULL);
133         if (!keyStr) {  // OutOfMemoryError
134             return false;
135         }
136 
137         const char* valueStr = env->GetStringUTFChars(value, NULL);
138         if (!valueStr) {  // OutOfMemoryError
139             env->ReleaseStringUTFChars(key, keyStr);
140             return false;
141         }
142 
143         keyedVector->add(String8(keyStr), String8(valueStr));
144 
145         env->ReleaseStringUTFChars(key, keyStr);
146         env->ReleaseStringUTFChars(value, valueStr);
147         env->DeleteLocalRef(key);
148         env->DeleteLocalRef(value);
149     }
150     return true;
151 }
152 
makeIntegerObject(JNIEnv * env,int32_t value)153 static jobject makeIntegerObject(JNIEnv *env, int32_t value) {
154     ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Integer"));
155     CHECK(clazz.get() != NULL);
156 
157     jmethodID integerConstructID =
158         env->GetMethodID(clazz.get(), "<init>", "(I)V");
159     CHECK(integerConstructID != NULL);
160 
161     return env->NewObject(clazz.get(), integerConstructID, value);
162 }
163 
makeLongObject(JNIEnv * env,int64_t value)164 static jobject makeLongObject(JNIEnv *env, int64_t value) {
165     ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Long"));
166     CHECK(clazz.get() != NULL);
167 
168     jmethodID longConstructID = env->GetMethodID(clazz.get(), "<init>", "(J)V");
169     CHECK(longConstructID != NULL);
170 
171     return env->NewObject(clazz.get(), longConstructID, value);
172 }
173 
makeFloatObject(JNIEnv * env,float value)174 static jobject makeFloatObject(JNIEnv *env, float value) {
175     ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Float"));
176     CHECK(clazz.get() != NULL);
177 
178     jmethodID floatConstructID =
179         env->GetMethodID(clazz.get(), "<init>", "(F)V");
180     CHECK(floatConstructID != NULL);
181 
182     return env->NewObject(clazz.get(), floatConstructID, value);
183 }
184 
makeByteBufferObject(JNIEnv * env,const void * data,size_t size)185 static jobject makeByteBufferObject(
186         JNIEnv *env, const void *data, size_t size) {
187     jbyteArray byteArrayObj = env->NewByteArray(size);
188     env->SetByteArrayRegion(byteArrayObj, 0, size, (const jbyte *)data);
189 
190     ScopedLocalRef<jclass> clazz(env, env->FindClass("java/nio/ByteBuffer"));
191     CHECK(clazz.get() != NULL);
192 
193     jmethodID byteBufWrapID =
194         env->GetStaticMethodID(
195                 clazz.get(), "wrap", "([B)Ljava/nio/ByteBuffer;");
196     CHECK(byteBufWrapID != NULL);
197 
198     jobject byteBufObj = env->CallStaticObjectMethod(
199             clazz.get(), byteBufWrapID, byteArrayObj);
200 
201     env->DeleteLocalRef(byteArrayObj); byteArrayObj = NULL;
202 
203     return byteBufObj;
204 }
205 
SetMapInt32(JNIEnv * env,jobject hashMapObj,jmethodID hashMapPutID,const char * key,int32_t value)206 static void SetMapInt32(
207         JNIEnv *env, jobject hashMapObj, jmethodID hashMapPutID,
208         const char *key, int32_t value) {
209     jstring keyObj = env->NewStringUTF(key);
210     jobject valueObj = makeIntegerObject(env, value);
211 
212     env->CallObjectMethod(hashMapObj, hashMapPutID, keyObj, valueObj);
213 
214     env->DeleteLocalRef(valueObj); valueObj = NULL;
215     env->DeleteLocalRef(keyObj); keyObj = NULL;
216 }
217 
ConvertMessageToMap(JNIEnv * env,const sp<AMessage> & msg,jobject * map)218 status_t ConvertMessageToMap(
219         JNIEnv *env, const sp<AMessage> &msg, jobject *map) {
220     ScopedLocalRef<jclass> hashMapClazz(
221             env, env->FindClass("java/util/HashMap"));
222 
223     if (hashMapClazz.get() == NULL) {
224         return -EINVAL;
225     }
226 
227     jmethodID hashMapConstructID =
228         env->GetMethodID(hashMapClazz.get(), "<init>", "()V");
229 
230     if (hashMapConstructID == NULL) {
231         return -EINVAL;
232     }
233 
234     jmethodID hashMapPutID =
235         env->GetMethodID(
236                 hashMapClazz.get(),
237                 "put",
238                 "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
239 
240     if (hashMapPutID == NULL) {
241         return -EINVAL;
242     }
243 
244     jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID);
245 
246     for (size_t i = 0; i < msg->countEntries(); ++i) {
247         AMessage::Type valueType;
248         const char *key = msg->getEntryNameAt(i, &valueType);
249 
250         if (!strncmp(key, "android._", 9)) {
251             // don't expose private keys (starting with android._)
252             continue;
253         }
254 
255         jobject valueObj = NULL;
256 
257         switch (valueType) {
258             case AMessage::kTypeInt32:
259             {
260                 int32_t val;
261                 CHECK(msg->findInt32(key, &val));
262 
263                 valueObj = makeIntegerObject(env, val);
264                 break;
265             }
266 
267             case AMessage::kTypeInt64:
268             {
269                 int64_t val;
270                 CHECK(msg->findInt64(key, &val));
271 
272                 valueObj = makeLongObject(env, val);
273                 break;
274             }
275 
276             case AMessage::kTypeFloat:
277             {
278                 float val;
279                 CHECK(msg->findFloat(key, &val));
280 
281                 valueObj = makeFloatObject(env, val);
282                 break;
283             }
284 
285             case AMessage::kTypeString:
286             {
287                 AString val;
288                 CHECK(msg->findString(key, &val));
289 
290                 valueObj = env->NewStringUTF(val.c_str());
291                 break;
292             }
293 
294             case AMessage::kTypeBuffer:
295             {
296                 sp<ABuffer> buffer;
297                 CHECK(msg->findBuffer(key, &buffer));
298 
299                 valueObj = makeByteBufferObject(
300                         env, buffer->data(), buffer->size());
301                 break;
302             }
303 
304             case AMessage::kTypeRect:
305             {
306                 int32_t left, top, right, bottom;
307                 CHECK(msg->findRect(key, &left, &top, &right, &bottom));
308 
309                 SetMapInt32(
310                         env,
311                         hashMap,
312                         hashMapPutID,
313                         AStringPrintf("%s-left", key).c_str(),
314                         left);
315 
316                 SetMapInt32(
317                         env,
318                         hashMap,
319                         hashMapPutID,
320                         AStringPrintf("%s-top", key).c_str(),
321                         top);
322 
323                 SetMapInt32(
324                         env,
325                         hashMap,
326                         hashMapPutID,
327                         AStringPrintf("%s-right", key).c_str(),
328                         right);
329 
330                 SetMapInt32(
331                         env,
332                         hashMap,
333                         hashMapPutID,
334                         AStringPrintf("%s-bottom", key).c_str(),
335                         bottom);
336                 break;
337             }
338 
339             default:
340                 break;
341         }
342 
343         if (valueObj != NULL) {
344             jstring keyObj = env->NewStringUTF(key);
345 
346             env->CallObjectMethod(hashMap, hashMapPutID, keyObj, valueObj);
347 
348             env->DeleteLocalRef(keyObj); keyObj = NULL;
349             env->DeleteLocalRef(valueObj); valueObj = NULL;
350         }
351     }
352 
353     *map = hashMap;
354 
355     return OK;
356 }
357 
ConvertKeyValueArraysToMessage(JNIEnv * env,jobjectArray keys,jobjectArray values,sp<AMessage> * out)358 status_t ConvertKeyValueArraysToMessage(
359         JNIEnv *env, jobjectArray keys, jobjectArray values,
360         sp<AMessage> *out) {
361     ScopedLocalRef<jclass> stringClass(env, env->FindClass("java/lang/String"));
362     CHECK(stringClass.get() != NULL);
363     ScopedLocalRef<jclass> integerClass(env, env->FindClass("java/lang/Integer"));
364     CHECK(integerClass.get() != NULL);
365     ScopedLocalRef<jclass> longClass(env, env->FindClass("java/lang/Long"));
366     CHECK(longClass.get() != NULL);
367     ScopedLocalRef<jclass> floatClass(env, env->FindClass("java/lang/Float"));
368     CHECK(floatClass.get() != NULL);
369     ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer"));
370     CHECK(byteBufClass.get() != NULL);
371 
372     sp<AMessage> msg = new AMessage;
373 
374     jsize numEntries = 0;
375 
376     if (keys != NULL) {
377         if (values == NULL) {
378             return -EINVAL;
379         }
380 
381         numEntries = env->GetArrayLength(keys);
382 
383         if (numEntries != env->GetArrayLength(values)) {
384             return -EINVAL;
385         }
386     } else if (values != NULL) {
387         return -EINVAL;
388     }
389 
390     for (jsize i = 0; i < numEntries; ++i) {
391         jobject keyObj = env->GetObjectArrayElement(keys, i);
392 
393         if (!env->IsInstanceOf(keyObj, stringClass.get())) {
394             return -EINVAL;
395         }
396 
397         const char *tmp = env->GetStringUTFChars((jstring)keyObj, NULL);
398 
399         if (tmp == NULL) {
400             return -ENOMEM;
401         }
402 
403         AString key = tmp;
404 
405         env->ReleaseStringUTFChars((jstring)keyObj, tmp);
406         tmp = NULL;
407 
408         if (key.startsWith("android._")) {
409             // don't propagate private keys (starting with android._)
410             continue;
411         }
412 
413         jobject valueObj = env->GetObjectArrayElement(values, i);
414 
415         if (env->IsInstanceOf(valueObj, stringClass.get())) {
416             const char *value = env->GetStringUTFChars((jstring)valueObj, NULL);
417 
418             if (value == NULL) {
419                 return -ENOMEM;
420             }
421 
422             msg->setString(key.c_str(), value);
423 
424             env->ReleaseStringUTFChars((jstring)valueObj, value);
425             value = NULL;
426         } else if (env->IsInstanceOf(valueObj, integerClass.get())) {
427             jmethodID intValueID =
428                 env->GetMethodID(integerClass.get(), "intValue", "()I");
429             CHECK(intValueID != NULL);
430 
431             jint value = env->CallIntMethod(valueObj, intValueID);
432 
433             msg->setInt32(key.c_str(), value);
434         } else if (env->IsInstanceOf(valueObj, longClass.get())) {
435             jmethodID longValueID =
436                 env->GetMethodID(longClass.get(), "longValue", "()J");
437             CHECK(longValueID != NULL);
438 
439             jlong value = env->CallLongMethod(valueObj, longValueID);
440 
441             msg->setInt64(key.c_str(), value);
442         } else if (env->IsInstanceOf(valueObj, floatClass.get())) {
443             jmethodID floatValueID =
444                 env->GetMethodID(floatClass.get(), "floatValue", "()F");
445             CHECK(floatValueID != NULL);
446 
447             jfloat value = env->CallFloatMethod(valueObj, floatValueID);
448 
449             msg->setFloat(key.c_str(), value);
450         } else if (env->IsInstanceOf(valueObj, byteBufClass.get())) {
451             jmethodID positionID =
452                 env->GetMethodID(byteBufClass.get(), "position", "()I");
453             CHECK(positionID != NULL);
454 
455             jmethodID limitID =
456                 env->GetMethodID(byteBufClass.get(), "limit", "()I");
457             CHECK(limitID != NULL);
458 
459             jint position = env->CallIntMethod(valueObj, positionID);
460             jint limit = env->CallIntMethod(valueObj, limitID);
461 
462             sp<ABuffer> buffer = new ABuffer(limit - position);
463 
464             void *data = env->GetDirectBufferAddress(valueObj);
465 
466             if (data != NULL) {
467                 memcpy(buffer->data(),
468                        (const uint8_t *)data + position,
469                        buffer->size());
470             } else {
471                 jmethodID arrayID =
472                     env->GetMethodID(byteBufClass.get(), "array", "()[B");
473                 CHECK(arrayID != NULL);
474 
475                 jbyteArray byteArray =
476                     (jbyteArray)env->CallObjectMethod(valueObj, arrayID);
477                 CHECK(byteArray != NULL);
478 
479                 env->GetByteArrayRegion(
480                         byteArray,
481                         position,
482                         buffer->size(),
483                         (jbyte *)buffer->data());
484 
485                 env->DeleteLocalRef(byteArray); byteArray = NULL;
486             }
487 
488             msg->setBuffer(key.c_str(), buffer);
489         }
490     }
491 
492     *out = msg;
493 
494     return OK;
495 }
496 
497 }  // namespace android
498 
499