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