1 /*
2  * Copyright (C) 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 "ItemTable"
19 
20 #include <unordered_set>
21 
22 #include <ItemTable.h>
23 #include <media/MediaExtractorPluginApi.h>
24 #include <media/MediaExtractorPluginHelper.h>
25 #include <media/stagefright/MetaData.h>
26 #include <media/stagefright/MediaErrors.h>
27 #include <media/stagefright/foundation/ABuffer.h>
28 #include <media/stagefright/foundation/ByteUtils.h>
29 #include <media/stagefright/foundation/hexdump.h>
30 #include <media/stagefright/foundation/MediaDefs.h>
31 #include <utils/Log.h>
32 
33 namespace android {
34 
35 namespace heif {
36 
37 /////////////////////////////////////////////////////////////////////
38 //
39 //  struct to keep track of one image item
40 //
41 
42 struct ImageItem {
43     friend struct ItemReference;
44     friend struct ItemProperty;
45 
ImageItemandroid::heif::ImageItem46     ImageItem() : ImageItem(0, 0, false) {}
ImageItemandroid::heif::ImageItem47     ImageItem(uint32_t _type, uint32_t _id, bool _hidden) :
48             type(_type), itemId(_id), hidden(_hidden),
49             rows(0), columns(0), width(0), height(0), rotation(0),
50             offset(0), size(0), nextTileIndex(0) {}
51 
isGridandroid::heif::ImageItem52     bool isGrid() const {
53         return type == FOURCC("grid");
54     }
55 
getNextTileItemIdandroid::heif::ImageItem56     status_t getNextTileItemId(uint32_t *nextTileItemId, bool reset) {
57         if (reset) {
58             nextTileIndex = 0;
59         }
60         if (nextTileIndex >= dimgRefs.size()) {
61             return ERROR_END_OF_STREAM;
62         }
63         *nextTileItemId = dimgRefs[nextTileIndex++];
64         return OK;
65     }
66 
67     uint32_t type;
68     uint32_t itemId;
69     bool hidden;
70     int32_t rows;
71     int32_t columns;
72     int32_t width;
73     int32_t height;
74     int32_t rotation;
75     off64_t offset;
76     size_t size;
77     sp<ABuffer> hvcc;
78     sp<ABuffer> icc;
79 
80     Vector<uint32_t> thumbnails;
81     Vector<uint32_t> dimgRefs;
82     Vector<uint32_t> cdscRefs;
83     size_t nextTileIndex;
84 };
85 
86 struct ExifItem {
87     off64_t offset;
88     size_t size;
89 };
90 
91 /////////////////////////////////////////////////////////////////////
92 //
93 //  ISO boxes
94 //
95 
96 struct Box {
97 protected:
Boxandroid::heif::Box98     Box(DataSourceHelper *source, uint32_t type) :
99         mDataSource(source), mType(type) {}
100 
~Boxandroid::heif::Box101     virtual ~Box() {}
102 
onChunkDataandroid::heif::Box103     virtual status_t onChunkData(
104             uint32_t /*type*/, off64_t /*offset*/, size_t /*size*/) {
105         return OK;
106     }
107 
typeandroid::heif::Box108     inline uint32_t type() const { return mType; }
109 
sourceandroid::heif::Box110     inline DataSourceHelper *source() const { return mDataSource; }
111 
112     status_t parseChunk(off64_t *offset);
113 
114     status_t parseChunks(off64_t offset, size_t size);
115 
116 private:
117     DataSourceHelper *mDataSource;
118     uint32_t mType;
119 };
120 
parseChunk(off64_t * offset)121 status_t Box::parseChunk(off64_t *offset) {
122     if (*offset < 0) {
123         ALOGE("b/23540914");
124         return ERROR_MALFORMED;
125     }
126     uint32_t hdr[2];
127     if (mDataSource->readAt(*offset, hdr, 8) < 8) {
128         return ERROR_IO;
129     }
130     uint64_t chunk_size = ntohl(hdr[0]);
131     int32_t chunk_type = ntohl(hdr[1]);
132     off64_t data_offset = *offset + 8;
133 
134     if (chunk_size == 1) {
135         if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {
136             return ERROR_IO;
137         }
138         chunk_size = ntoh64(chunk_size);
139         data_offset += 8;
140 
141         if (chunk_size < 16) {
142             // The smallest valid chunk is 16 bytes long in this case.
143             return ERROR_MALFORMED;
144         }
145     } else if (chunk_size == 0) {
146         // This shouldn't happen since we should never be top level
147         ALOGE("invalid chunk size 0 for non-top level box");
148         return ERROR_MALFORMED;
149     } else if (chunk_size < 8) {
150         // The smallest valid chunk is 8 bytes long.
151         ALOGE("invalid chunk size: %lld", (long long)chunk_size);
152         return ERROR_MALFORMED;
153     }
154 
155     char chunk[5];
156     MakeFourCCString(chunk_type, chunk);
157     ALOGV("chunk: %s @ %lld", chunk, (long long)*offset);
158 
159     off64_t chunk_data_size = chunk_size - (data_offset - *offset);
160     if (chunk_data_size < 0) {
161         ALOGE("b/23540914");
162         return ERROR_MALFORMED;
163     }
164 
165     status_t err = onChunkData(chunk_type, data_offset, chunk_data_size);
166 
167     if (err != OK) {
168         return err;
169     }
170     *offset += chunk_size;
171     return OK;
172 }
173 
parseChunks(off64_t offset,size_t size)174 status_t Box::parseChunks(off64_t offset, size_t size) {
175     off64_t stopOffset = offset + size;
176     while (offset < stopOffset) {
177         status_t err = parseChunk(&offset);
178         if (err != OK) {
179             return err;
180         }
181     }
182     if (offset != stopOffset) {
183         return ERROR_MALFORMED;
184     }
185     return OK;
186 }
187 
188 ///////////////////////////////////////////////////////////////////////
189 
190 struct FullBox : public Box {
191 protected:
FullBoxandroid::heif::FullBox192     FullBox(DataSourceHelper *source, uint32_t type) :
193         Box(source, type), mVersion(0), mFlags(0) {}
194 
versionandroid::heif::FullBox195     inline uint8_t version() const { return mVersion; }
196 
flagsandroid::heif::FullBox197     inline uint32_t flags() const { return mFlags; }
198 
199     status_t parseFullBoxHeader(off64_t *offset, size_t *size);
200 
201 private:
202     uint8_t mVersion;
203     uint32_t mFlags;
204 };
205 
parseFullBoxHeader(off64_t * offset,size_t * size)206 status_t FullBox::parseFullBoxHeader(off64_t *offset, size_t *size) {
207     if (*size < 4) {
208         return ERROR_MALFORMED;
209     }
210     if (!source()->readAt(*offset, &mVersion, 1)) {
211         return ERROR_IO;
212     }
213     if (!source()->getUInt24(*offset + 1, &mFlags)) {
214         return ERROR_IO;
215     }
216     *offset += 4;
217     *size -= 4;
218     return OK;
219 }
220 
221 /////////////////////////////////////////////////////////////////////
222 //
223 //  PrimaryImage box
224 //
225 
226 struct PitmBox : public FullBox {
PitmBoxandroid::heif::PitmBox227     PitmBox(DataSourceHelper *source) :
228         FullBox(source, FOURCC("pitm")) {}
229 
230     status_t parse(off64_t offset, size_t size, uint32_t *primaryItemId);
231 };
232 
parse(off64_t offset,size_t size,uint32_t * primaryItemId)233 status_t PitmBox::parse(off64_t offset, size_t size, uint32_t *primaryItemId) {
234     status_t err = parseFullBoxHeader(&offset, &size);
235     if (err != OK) {
236         return err;
237     }
238 
239     size_t itemIdSize = (version() == 0) ? 2 : 4;
240     if (size < itemIdSize) {
241         return ERROR_MALFORMED;
242     }
243     uint32_t itemId;
244     if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) {
245         return ERROR_IO;
246     }
247 
248     ALOGV("primary id %d", itemId);
249     *primaryItemId = itemId;
250 
251     return OK;
252 }
253 
254 /////////////////////////////////////////////////////////////////////
255 //
256 //  ItemLocation related boxes
257 //
258 
259 struct ExtentEntry {
260     uint64_t extentIndex;
261     uint64_t extentOffset;
262     uint64_t extentLength;
263 };
264 
265 struct ItemLoc {
ItemLocandroid::heif::ItemLoc266     ItemLoc() : ItemLoc(0, 0, 0, 0) {}
ItemLocandroid::heif::ItemLoc267     ItemLoc(uint32_t item_id, uint16_t construction_method,
268             uint16_t data_reference_index, uint64_t base_offset) :
269         itemId(item_id),
270         constructionMethod(construction_method),
271         dataReferenceIndex(data_reference_index),
272         baseOffset(base_offset) {}
273 
addExtentandroid::heif::ItemLoc274     void addExtent(const ExtentEntry& extent) {
275         extents.push_back(extent);
276     }
277 
getLocandroid::heif::ItemLoc278     status_t getLoc(off64_t *offset, size_t *size,
279             off64_t idatOffset, size_t idatSize) const {
280         // TODO: fix extent handling, fix constructionMethod = 2
281         CHECK(extents.size() == 1);
282         if (constructionMethod == 0) {
283             *offset = baseOffset + extents[0].extentOffset;
284             *size = extents[0].extentLength;
285             return OK;
286         } else if (constructionMethod == 1) {
287             if (baseOffset + extents[0].extentOffset + extents[0].extentLength
288                     > idatSize) {
289                 return ERROR_MALFORMED;
290             }
291             *offset = baseOffset + extents[0].extentOffset + idatOffset;
292             *size = extents[0].extentLength;
293             return OK;
294         }
295         return ERROR_UNSUPPORTED;
296     }
297 
298     // parsed info
299     uint32_t itemId;
300     uint16_t constructionMethod;
301     uint16_t dataReferenceIndex;
302     off64_t baseOffset;
303     Vector<ExtentEntry> extents;
304 };
305 
306 struct IlocBox : public FullBox {
IlocBoxandroid::heif::IlocBox307     IlocBox(DataSourceHelper *source, KeyedVector<uint32_t, ItemLoc> *itemLocs) :
308         FullBox(source, FOURCC("iloc")),
309         mItemLocs(itemLocs), mHasConstructMethod1(false) {}
310 
311     status_t parse(off64_t offset, size_t size);
312 
hasConstructMethod1android::heif::IlocBox313     bool hasConstructMethod1() { return mHasConstructMethod1; }
314 
315 private:
isSizeFieldValidandroid::heif::IlocBox316     static bool isSizeFieldValid(uint32_t offset_size) {
317         return offset_size == 0 || offset_size == 4 || offset_size == 8;
318     }
319     KeyedVector<uint32_t, ItemLoc> *mItemLocs;
320     bool mHasConstructMethod1;
321 };
322 
parse(off64_t offset,size_t size)323 status_t IlocBox::parse(off64_t offset, size_t size) {
324     status_t err = parseFullBoxHeader(&offset, &size);
325     if (err != OK) {
326         return err;
327     }
328     if (version() > 2) {
329         ALOGE("%s: invalid version %d", __FUNCTION__, version());
330         return ERROR_MALFORMED;
331     }
332 
333     if (size < 2) {
334         return ERROR_MALFORMED;
335     }
336     uint8_t offset_size;
337     if (!source()->readAt(offset++, &offset_size, 1)) {
338         return ERROR_IO;
339     }
340     uint8_t length_size = (offset_size & 0xF);
341     offset_size >>= 4;
342 
343     uint8_t base_offset_size;
344     if (!source()->readAt(offset++, &base_offset_size, 1)) {
345         return ERROR_IO;
346     }
347     uint8_t index_size = 0;
348     if (version() == 1 || version() == 2) {
349         index_size = (base_offset_size & 0xF);
350     }
351     base_offset_size >>= 4;
352     size -= 2;
353 
354     if (!isSizeFieldValid(offset_size)
355             || !isSizeFieldValid(length_size)
356             || !isSizeFieldValid(base_offset_size)
357             || !isSizeFieldValid((index_size))) {
358         ALOGE("%s: offset size not valid: %d, %d, %d, %d", __FUNCTION__,
359                 offset_size, length_size, base_offset_size, index_size);
360         return ERROR_MALFORMED;
361     }
362 
363     uint32_t item_count;
364     size_t itemFieldSize = version() < 2 ? 2 : 4;
365     if (size < itemFieldSize) {
366         return ERROR_MALFORMED;
367     }
368     if (!source()->getUInt32Var(offset, &item_count, itemFieldSize)) {
369         return ERROR_IO;
370     }
371 
372     ALOGV("item_count %lld", (long long) item_count);
373     offset += itemFieldSize;
374     size -= itemFieldSize;
375 
376     for (size_t i = 0; i < item_count; i++) {
377         uint32_t item_id;
378         if (!source()->getUInt32Var(offset, &item_id, itemFieldSize)) {
379             return ERROR_IO;
380         }
381         ALOGV("item[%zu]: id %lld", i, (long long)item_id);
382         offset += itemFieldSize;
383 
384         uint8_t construction_method = 0;
385         if (version() == 1 || version() == 2) {
386             uint8_t buf[2];
387             if (!source()->readAt(offset, buf, 2)) {
388                 return ERROR_IO;
389             }
390             construction_method = (buf[1] & 0xF);
391             ALOGV("construction_method %d", construction_method);
392             if (construction_method == 1) {
393                 mHasConstructMethod1 = true;
394             }
395 
396             offset += 2;
397         }
398 
399         uint16_t data_reference_index;
400         if (!source()->getUInt16(offset, &data_reference_index)) {
401             return ERROR_IO;
402         }
403         ALOGV("data_reference_index %d", data_reference_index);
404         if (data_reference_index != 0) {
405             // we don't support reference to other files
406             return ERROR_UNSUPPORTED;
407         }
408         offset += 2;
409 
410         uint64_t base_offset = 0;
411         if (base_offset_size != 0) {
412             if (!source()->getUInt64Var(offset, &base_offset, base_offset_size)) {
413                 return ERROR_IO;
414             }
415             offset += base_offset_size;
416         }
417         ALOGV("base_offset %lld", (long long) base_offset);
418 
419         ssize_t index = mItemLocs->add(item_id, ItemLoc(
420                 item_id, construction_method, data_reference_index, base_offset));
421         ItemLoc &item = mItemLocs->editValueAt(index);
422 
423         uint16_t extent_count;
424         if (!source()->getUInt16(offset, &extent_count)) {
425             return ERROR_IO;
426         }
427         ALOGV("extent_count %d", extent_count);
428 
429         if (extent_count > 1) {
430             return ERROR_UNSUPPORTED;
431         }
432         offset += 2;
433 
434         for (size_t j = 0; j < extent_count; j++) {
435             uint64_t extent_index = 1; // default=1
436             if ((version() == 1 || version() == 2) && (index_size > 0)) {
437                 if (!source()->getUInt64Var(offset, &extent_index, index_size)) {
438                     return ERROR_IO;
439                 }
440                 // TODO: add support for this mode
441                 offset += index_size;
442                 ALOGV("extent_index %lld", (long long)extent_index);
443             }
444 
445             uint64_t extent_offset = 0; // default=0
446             if (offset_size > 0) {
447                 if (!source()->getUInt64Var(offset, &extent_offset, offset_size)) {
448                     return ERROR_IO;
449                 }
450                 offset += offset_size;
451             }
452             ALOGV("extent_offset %lld", (long long)extent_offset);
453 
454             uint64_t extent_length = 0; // this indicates full length of file
455             if (length_size > 0) {
456                 if (!source()->getUInt64Var(offset, &extent_length, length_size)) {
457                     return ERROR_IO;
458                 }
459                 offset += length_size;
460             }
461             ALOGV("extent_length %lld", (long long)extent_length);
462 
463             item.addExtent({ extent_index, extent_offset, extent_length });
464         }
465     }
466     return OK;
467 }
468 
469 /////////////////////////////////////////////////////////////////////
470 //
471 //  ItemReference related boxes
472 //
473 
474 struct ItemReference : public Box, public RefBase {
ItemReferenceandroid::heif::ItemReference475     ItemReference(DataSourceHelper *source, uint32_t type, uint32_t itemIdSize) :
476         Box(source, type), mItemId(0), mRefIdSize(itemIdSize) {}
477 
478     status_t parse(off64_t offset, size_t size);
479 
itemIdandroid::heif::ItemReference480     uint32_t itemId() { return mItemId; }
481 
482     void apply(
483             KeyedVector<uint32_t, ImageItem> &itemIdToItemMap,
484             KeyedVector<uint32_t, ExifItem> &itemIdToExifMap) const;
485 
486 private:
487     uint32_t mItemId;
488     uint32_t mRefIdSize;
489     Vector<uint32_t> mRefs;
490 
491     DISALLOW_EVIL_CONSTRUCTORS(ItemReference);
492 };
493 
apply(KeyedVector<uint32_t,ImageItem> & itemIdToItemMap,KeyedVector<uint32_t,ExifItem> & itemIdToExifMap) const494 void ItemReference::apply(
495         KeyedVector<uint32_t, ImageItem> &itemIdToItemMap,
496         KeyedVector<uint32_t, ExifItem> &itemIdToExifMap) const {
497     ALOGV("attach reference type 0x%x to item id %d)", type(), mItemId);
498 
499     switch(type()) {
500     case FOURCC("dimg"): {
501         ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
502 
503         // ignore non-image items
504         if (itemIndex < 0) {
505             return;
506         }
507 
508         ImageItem &derivedImage = itemIdToItemMap.editValueAt(itemIndex);
509         if (!derivedImage.dimgRefs.empty()) {
510             ALOGW("dimgRefs not clean!");
511         }
512         derivedImage.dimgRefs.appendVector(mRefs);
513 
514         for (size_t i = 0; i < mRefs.size(); i++) {
515             itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
516 
517             // ignore non-image items
518             if (itemIndex < 0) {
519                 continue;
520             }
521             ImageItem &sourceImage = itemIdToItemMap.editValueAt(itemIndex);
522 
523             // mark the source image of the derivation as hidden
524             sourceImage.hidden = true;
525         }
526         break;
527     }
528     case FOURCC("thmb"): {
529         ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
530 
531         // ignore non-image items
532         if (itemIndex < 0) {
533             return;
534         }
535 
536         // mark thumbnail image as hidden, these can be retrieved if the client
537         // request thumbnail explicitly, but won't be exposed as displayables.
538         ImageItem &thumbImage = itemIdToItemMap.editValueAt(itemIndex);
539         thumbImage.hidden = true;
540 
541         for (size_t i = 0; i < mRefs.size(); i++) {
542             itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
543 
544             // ignore non-image items
545             if (itemIndex < 0) {
546                 continue;
547             }
548             ALOGV("Image item id %d uses thumbnail item id %d", mRefs[i], mItemId);
549             ImageItem &imageItem = itemIdToItemMap.editValueAt(itemIndex);
550             if (!imageItem.thumbnails.empty()) {
551                 ALOGW("already has thumbnails!");
552             }
553             imageItem.thumbnails.push_back(mItemId);
554         }
555         break;
556     }
557     case FOURCC("cdsc"): {
558         ssize_t itemIndex = itemIdToExifMap.indexOfKey(mItemId);
559 
560         // ignore non-exif block items
561         if (itemIndex < 0) {
562             return;
563         }
564 
565         for (size_t i = 0; i < mRefs.size(); i++) {
566             itemIndex = itemIdToItemMap.indexOfKey(mRefs[i]);
567 
568             // ignore non-image items
569             if (itemIndex < 0) {
570                 continue;
571             }
572             ALOGV("Image item id %d uses metadata item id %d", mRefs[i], mItemId);
573             ImageItem &image = itemIdToItemMap.editValueAt(itemIndex);
574             image.cdscRefs.push_back(mItemId);
575         }
576         break;
577     }
578     case FOURCC("auxl"): {
579         ssize_t itemIndex = itemIdToItemMap.indexOfKey(mItemId);
580 
581         // ignore non-image items
582         if (itemIndex < 0) {
583             return;
584         }
585 
586         // mark auxiliary image as hidden
587         ImageItem &auxImage = itemIdToItemMap.editValueAt(itemIndex);
588         auxImage.hidden = true;
589         break;
590     }
591     default:
592         ALOGW("ignoring unsupported ref type 0x%x", type());
593     }
594 }
595 
parse(off64_t offset,size_t size)596 status_t ItemReference::parse(off64_t offset, size_t size) {
597     if (size < mRefIdSize + 2) {
598         return ERROR_MALFORMED;
599     }
600     if (!source()->getUInt32Var(offset, &mItemId, mRefIdSize)) {
601         return ERROR_IO;
602     }
603     offset += mRefIdSize;
604 
605     uint16_t count;
606     if (!source()->getUInt16(offset, &count)) {
607         return ERROR_IO;
608     }
609     offset += 2;
610     size -= (mRefIdSize + 2);
611 
612     if (size < count * mRefIdSize) {
613         return ERROR_MALFORMED;
614     }
615 
616     for (size_t i = 0; i < count; i++) {
617         uint32_t refItemId;
618         if (!source()->getUInt32Var(offset, &refItemId, mRefIdSize)) {
619             return ERROR_IO;
620         }
621         offset += mRefIdSize;
622         mRefs.push_back(refItemId);
623         ALOGV("item id %d: referencing item id %d", mItemId, refItemId);
624     }
625 
626     return OK;
627 }
628 
629 struct IrefBox : public FullBox {
IrefBoxandroid::heif::IrefBox630     IrefBox(DataSourceHelper *source, Vector<sp<ItemReference> > *itemRefs) :
631         FullBox(source, FOURCC("iref")), mRefIdSize(0), mItemRefs(itemRefs) {}
632 
633     status_t parse(off64_t offset, size_t size);
634 
635 protected:
636     status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
637 
638 private:
639     uint32_t mRefIdSize;
640     Vector<sp<ItemReference> > *mItemRefs;
641 };
642 
parse(off64_t offset,size_t size)643 status_t IrefBox::parse(off64_t offset, size_t size) {
644     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
645     status_t err = parseFullBoxHeader(&offset, &size);
646     if (err != OK) {
647         return err;
648     }
649 
650     mRefIdSize = (version() == 0) ? 2 : 4;
651     return parseChunks(offset, size);
652 }
653 
onChunkData(uint32_t type,off64_t offset,size_t size)654 status_t IrefBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
655     sp<ItemReference> itemRef = new ItemReference(source(), type, mRefIdSize);
656 
657     status_t err = itemRef->parse(offset, size);
658     if (err != OK) {
659         return err;
660     }
661     mItemRefs->push_back(itemRef);
662     return OK;
663 }
664 
665 /////////////////////////////////////////////////////////////////////
666 //
667 //  ItemProperty related boxes
668 //
669 
670 struct AssociationEntry {
671     uint32_t itemId;
672     bool essential;
673     uint16_t index;
674 };
675 
676 struct ItemProperty : public RefBase {
ItemPropertyandroid::heif::ItemProperty677     ItemProperty() {}
678 
attachToandroid::heif::ItemProperty679     virtual void attachTo(ImageItem &/*image*/) const {
680         ALOGW("Unrecognized property");
681     }
parseandroid::heif::ItemProperty682     virtual status_t parse(off64_t /*offset*/, size_t /*size*/) {
683         ALOGW("Unrecognized property");
684         return OK;
685     }
686 
687 private:
688     DISALLOW_EVIL_CONSTRUCTORS(ItemProperty);
689 };
690 
691 struct IspeBox : public FullBox, public ItemProperty {
IspeBoxandroid::heif::IspeBox692     IspeBox(DataSourceHelper *source) :
693         FullBox(source, FOURCC("ispe")), mWidth(0), mHeight(0) {}
694 
695     status_t parse(off64_t offset, size_t size) override;
696 
attachToandroid::heif::IspeBox697     void attachTo(ImageItem &image) const override {
698         image.width = mWidth;
699         image.height = mHeight;
700     }
701 
702 private:
703     int32_t mWidth;
704     int32_t mHeight;
705 };
706 
parse(off64_t offset,size_t size)707 status_t IspeBox::parse(off64_t offset, size_t size) {
708     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
709 
710     status_t err = parseFullBoxHeader(&offset, &size);
711     if (err != OK) {
712         return err;
713     }
714 
715     if (size < 8) {
716         return ERROR_MALFORMED;
717     }
718     if (!source()->getUInt32(offset, (uint32_t *)&mWidth)
719             || !source()->getUInt32(offset + 4, (uint32_t *)&mHeight)) {
720         return ERROR_IO;
721     }
722 
723     // Validate that the dimension doesn't cause overflow on calculated max input size.
724     // Max input size is width*height*1.5, restrict width*height to 1<<29 so that
725     // we don't need to cast to int64_t when doing mults.
726     if (mWidth <= 0 || mHeight <= 0 || mWidth > (1 << 29) / mHeight) {
727         return ERROR_MALFORMED;
728     }
729 
730     ALOGV("property ispe: %dx%d", mWidth, mHeight);
731     return OK;
732 }
733 
734 struct HvccBox : public Box, public ItemProperty {
HvccBoxandroid::heif::HvccBox735     HvccBox(DataSourceHelper *source) :
736         Box(source, FOURCC("hvcC")) {}
737 
738     status_t parse(off64_t offset, size_t size) override;
739 
attachToandroid::heif::HvccBox740     void attachTo(ImageItem &image) const override {
741         image.hvcc = mHVCC;
742     }
743 
744 private:
745     sp<ABuffer> mHVCC;
746 };
747 
parse(off64_t offset,size_t size)748 status_t HvccBox::parse(off64_t offset, size_t size) {
749     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
750 
751     mHVCC = new ABuffer(size);
752 
753     if (mHVCC->data() == NULL) {
754         ALOGE("b/28471206");
755         return NO_MEMORY;
756     }
757 
758     if (source()->readAt(offset, mHVCC->data(), size) < (ssize_t)size) {
759         return ERROR_IO;
760     }
761 
762     ALOGV("property hvcC");
763 
764     return OK;
765 }
766 
767 struct IrotBox : public Box, public ItemProperty {
IrotBoxandroid::heif::IrotBox768     IrotBox(DataSourceHelper *source) :
769         Box(source, FOURCC("irot")), mAngle(0) {}
770 
771     status_t parse(off64_t offset, size_t size) override;
772 
attachToandroid::heif::IrotBox773     void attachTo(ImageItem &image) const override {
774         image.rotation = mAngle * 90;
775     }
776 
777 private:
778     uint8_t mAngle;
779 };
780 
parse(off64_t offset,size_t size)781 status_t IrotBox::parse(off64_t offset, size_t size) {
782     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
783 
784     if (size < 1) {
785         return ERROR_MALFORMED;
786     }
787     if (source()->readAt(offset, &mAngle, 1) != 1) {
788         return ERROR_IO;
789     }
790     mAngle &= 0x3;
791     ALOGV("property irot: %d", mAngle);
792 
793     return OK;
794 }
795 
796 struct ColrBox : public Box, public ItemProperty {
ColrBoxandroid::heif::ColrBox797     ColrBox(DataSourceHelper *source) :
798         Box(source, FOURCC("colr")) {}
799 
800     status_t parse(off64_t offset, size_t size) override;
801 
attachToandroid::heif::ColrBox802     void attachTo(ImageItem &image) const override {
803         image.icc = mICCData;
804     }
805 
806 private:
807     sp<ABuffer> mICCData;
808 };
809 
parse(off64_t offset,size_t size)810 status_t ColrBox::parse(off64_t offset, size_t size) {
811     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
812 
813     if (size < 4) {
814         return ERROR_MALFORMED;
815     }
816     uint32_t colour_type;
817     if (!source()->getUInt32(offset, &colour_type)) {
818         return ERROR_IO;
819     }
820     offset += 4;
821     size -= 4;
822     if (colour_type == FOURCC("nclx")) {
823         return OK;
824     }
825     if ((colour_type != FOURCC("rICC")) &&
826         (colour_type != FOURCC("prof"))) {
827         return ERROR_MALFORMED;
828     }
829 
830     mICCData = new ABuffer(size);
831     if (mICCData->data() == NULL) {
832         ALOGE("b/28471206");
833         return NO_MEMORY;
834     }
835 
836     if (source()->readAt(offset, mICCData->data(), size) != (ssize_t)size) {
837         return ERROR_IO;
838     }
839 
840     ALOGV("property Colr: size %zd", size);
841     return OK;
842 }
843 
844 struct IpmaBox : public FullBox {
IpmaBoxandroid::heif::IpmaBox845     IpmaBox(DataSourceHelper *source, Vector<AssociationEntry> *associations) :
846         FullBox(source, FOURCC("ipma")), mAssociations(associations) {}
847 
848     status_t parse(off64_t offset, size_t size);
849 private:
850     Vector<AssociationEntry> *mAssociations;
851 };
852 
parse(off64_t offset,size_t size)853 status_t IpmaBox::parse(off64_t offset, size_t size) {
854     status_t err = parseFullBoxHeader(&offset, &size);
855     if (err != OK) {
856         return err;
857     }
858 
859     if (size < 4) {
860         return ERROR_MALFORMED;
861     }
862     uint32_t entryCount;
863     if (!source()->getUInt32(offset, &entryCount)) {
864         return ERROR_IO;
865     }
866     offset += 4;
867     size -= 4;
868 
869     for (size_t k = 0; k < entryCount; ++k) {
870         uint32_t itemId = 0;
871         size_t itemIdSize = (version() < 1) ? 2 : 4;
872 
873         if (size < itemIdSize + 1) {
874             return ERROR_MALFORMED;
875         }
876 
877         if (!source()->getUInt32Var(offset, &itemId, itemIdSize)) {
878             return ERROR_IO;
879         }
880         offset += itemIdSize;
881         size -= itemIdSize;
882 
883         uint8_t associationCount;
884         if (!source()->readAt(offset, &associationCount, 1)) {
885             return ERROR_IO;
886         }
887         offset++;
888         size--;
889 
890         for (size_t i = 0; i < associationCount; ++i) {
891             size_t propIndexSize = (flags() & 1) ? 2 : 1;
892             if (size < propIndexSize) {
893                 return ERROR_MALFORMED;
894             }
895             uint16_t propIndex;
896             if (!source()->getUInt16Var(offset, &propIndex, propIndexSize)) {
897                 return ERROR_IO;
898             }
899             offset += propIndexSize;
900             size -= propIndexSize;
901             uint16_t bitmask = (1 << (8 * propIndexSize - 1));
902             AssociationEntry entry = {
903                     .itemId = itemId,
904                     .essential = !!(propIndex & bitmask),
905                     .index = (uint16_t) (propIndex & ~bitmask)
906             };
907 
908             ALOGV("item id %d associated to property %d (essential %d)",
909                     itemId, entry.index, entry.essential);
910 
911             mAssociations->push_back(entry);
912         }
913     }
914 
915     return OK;
916 }
917 
918 struct IpcoBox : public Box {
IpcoBoxandroid::heif::IpcoBox919     IpcoBox(DataSourceHelper *source, Vector<sp<ItemProperty> > *properties) :
920         Box(source, FOURCC("ipco")), mItemProperties(properties) {}
921 
922     status_t parse(off64_t offset, size_t size);
923 protected:
924     status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
925 
926 private:
927     Vector<sp<ItemProperty> > *mItemProperties;
928 };
929 
parse(off64_t offset,size_t size)930 status_t IpcoBox::parse(off64_t offset, size_t size) {
931     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
932     // push a placeholder as the index is 1-based
933     mItemProperties->push_back(new ItemProperty());
934     return parseChunks(offset, size);
935 }
936 
onChunkData(uint32_t type,off64_t offset,size_t size)937 status_t IpcoBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
938     sp<ItemProperty> itemProperty;
939     switch(type) {
940         case FOURCC("hvcC"):
941         {
942             itemProperty = new HvccBox(source());
943             break;
944         }
945         case FOURCC("ispe"):
946         {
947             itemProperty = new IspeBox(source());
948             break;
949         }
950         case FOURCC("irot"):
951         {
952             itemProperty = new IrotBox(source());
953             break;
954         }
955         case FOURCC("colr"):
956         {
957             itemProperty = new ColrBox(source());
958             break;
959         }
960         default:
961         {
962             // push dummy to maintain correct item property index
963             itemProperty = new ItemProperty();
964             break;
965         }
966     }
967     status_t err = itemProperty->parse(offset, size);
968     if (err != OK) {
969         return err;
970     }
971     mItemProperties->push_back(itemProperty);
972     return OK;
973 }
974 
975 struct IprpBox : public Box {
IprpBoxandroid::heif::IprpBox976     IprpBox(DataSourceHelper *source,
977             Vector<sp<ItemProperty> > *properties,
978             Vector<AssociationEntry> *associations) :
979         Box(source, FOURCC("iprp")),
980         mProperties(properties), mAssociations(associations) {}
981 
982     status_t parse(off64_t offset, size_t size);
983 protected:
984     status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
985 
986 private:
987     Vector<sp<ItemProperty> > *mProperties;
988     Vector<AssociationEntry> *mAssociations;
989 };
990 
parse(off64_t offset,size_t size)991 status_t IprpBox::parse(off64_t offset, size_t size) {
992     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
993 
994     status_t err = parseChunks(offset, size);
995     if (err != OK) {
996         return err;
997     }
998     return OK;
999 }
1000 
onChunkData(uint32_t type,off64_t offset,size_t size)1001 status_t IprpBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
1002     switch(type) {
1003         case FOURCC("ipco"):
1004         {
1005             IpcoBox ipcoBox(source(), mProperties);
1006             return ipcoBox.parse(offset, size);
1007         }
1008         case FOURCC("ipma"):
1009         {
1010             IpmaBox ipmaBox(source(), mAssociations);
1011             return ipmaBox.parse(offset, size);
1012         }
1013         default:
1014         {
1015             ALOGW("Unrecognized box.");
1016             break;
1017         }
1018     }
1019     return OK;
1020 }
1021 
1022 /////////////////////////////////////////////////////////////////////
1023 //
1024 //  ItemInfo related boxes
1025 //
1026 struct ItemInfo {
1027     uint32_t itemId;
1028     uint32_t itemType;
1029     bool hidden;
1030 };
1031 
1032 struct InfeBox : public FullBox {
InfeBoxandroid::heif::InfeBox1033     InfeBox(DataSourceHelper *source) :
1034         FullBox(source, FOURCC("infe")) {}
1035 
1036     status_t parse(off64_t offset, size_t size, ItemInfo *itemInfo);
1037 
1038 private:
1039     bool parseNullTerminatedString(off64_t *offset, size_t *size, String8 *out);
1040 };
1041 
parseNullTerminatedString(off64_t * offset,size_t * size,String8 * out)1042 bool InfeBox::parseNullTerminatedString(
1043         off64_t *offset, size_t *size, String8 *out) {
1044     char tmp;
1045     Vector<char> buf;
1046     buf.setCapacity(256);
1047     off64_t newOffset = *offset;
1048     off64_t stopOffset = *offset + *size;
1049     while (newOffset < stopOffset) {
1050         if (!source()->readAt(newOffset++, &tmp, 1)) {
1051             return false;
1052         }
1053         buf.push_back(tmp);
1054         if (tmp == 0) {
1055             out->setTo(buf.array());
1056 
1057             *offset = newOffset;
1058             *size = stopOffset - newOffset;
1059 
1060             return true;
1061         }
1062     }
1063     return false;
1064 }
1065 
parse(off64_t offset,size_t size,ItemInfo * itemInfo)1066 status_t InfeBox::parse(off64_t offset, size_t size, ItemInfo *itemInfo) {
1067     status_t err = parseFullBoxHeader(&offset, &size);
1068     if (err != OK) {
1069         return err;
1070     }
1071 
1072     if (version() == 0 || version() == 1) {
1073         return ERROR_UNSUPPORTED;
1074     } else { // version >= 2
1075         uint32_t item_id;
1076         size_t itemIdSize = (version() == 2) ? 2 : 4;
1077         if (size < itemIdSize + 6) {
1078             return ERROR_MALFORMED;
1079         }
1080         if (!source()->getUInt32Var(offset, &item_id, itemIdSize)) {
1081             return ERROR_IO;
1082         }
1083         ALOGV("item_id %d", item_id);
1084         offset += itemIdSize;
1085         uint16_t item_protection_index;
1086         if (!source()->getUInt16(offset, &item_protection_index)) {
1087             return ERROR_IO;
1088         }
1089         ALOGV("item_protection_index %d", item_protection_index);
1090         offset += 2;
1091         uint32_t item_type;
1092         if (!source()->getUInt32(offset, &item_type)) {
1093             return ERROR_IO;
1094         }
1095 
1096         itemInfo->itemId = item_id;
1097         itemInfo->itemType = item_type;
1098         // According to HEIF spec, (flags & 1) indicates the image is hidden
1099         // and not supposed to be displayed.
1100         itemInfo->hidden = (flags() & 1);
1101 
1102         char itemTypeString[5];
1103         MakeFourCCString(item_type, itemTypeString);
1104         ALOGV("item_type %s", itemTypeString);
1105         offset += 4;
1106         size -= itemIdSize + 6;
1107 
1108         String8 item_name;
1109         if (!parseNullTerminatedString(&offset, &size, &item_name)) {
1110             return ERROR_MALFORMED;
1111         }
1112         ALOGV("item_name %s", item_name.c_str());
1113 
1114         if (item_type == FOURCC("mime")) {
1115             String8 content_type;
1116             if (!parseNullTerminatedString(&offset, &size, &content_type)) {
1117                 return ERROR_MALFORMED;
1118             }
1119 
1120             // content_encoding is optional; can be omitted if would be empty
1121             if (size > 0) {
1122                 String8 content_encoding;
1123                 if (!parseNullTerminatedString(&offset, &size, &content_encoding)) {
1124                     return ERROR_MALFORMED;
1125                 }
1126             }
1127         } else if (item_type == FOURCC("uri ")) {
1128             String8 item_uri_type;
1129             if (!parseNullTerminatedString(&offset, &size, &item_uri_type)) {
1130                 return ERROR_MALFORMED;
1131             }
1132         }
1133     }
1134     return OK;
1135 }
1136 
1137 struct IinfBox : public FullBox {
IinfBoxandroid::heif::IinfBox1138     IinfBox(DataSourceHelper *source, Vector<ItemInfo> *itemInfos) :
1139         FullBox(source, FOURCC("iinf")), mItemInfos(itemInfos) {}
1140 
1141     status_t parse(off64_t offset, size_t size);
1142 
hasFourCCandroid::heif::IinfBox1143     bool hasFourCC(uint32_t type) { return mFourCCSeen.count(type) > 0; }
1144 
1145 protected:
1146     status_t onChunkData(uint32_t type, off64_t offset, size_t size) override;
1147 
1148 private:
1149     Vector<ItemInfo> *mItemInfos;
1150     std::unordered_set<uint32_t> mFourCCSeen;
1151 };
1152 
parse(off64_t offset,size_t size)1153 status_t IinfBox::parse(off64_t offset, size_t size) {
1154     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1155 
1156     status_t err = parseFullBoxHeader(&offset, &size);
1157     if (err != OK) {
1158         return err;
1159     }
1160 
1161     size_t entryCountSize = version() == 0 ? 2 : 4;
1162     if (size < entryCountSize) {
1163         return ERROR_MALFORMED;
1164     }
1165     uint32_t entry_count;
1166     if (!source()->getUInt32Var(offset, &entry_count, entryCountSize)) {
1167         return ERROR_IO;
1168     }
1169     ALOGV("entry_count %d", entry_count);
1170 
1171     off64_t stopOffset = offset + size;
1172     offset += entryCountSize;
1173     for (size_t i = 0; i < entry_count && offset < stopOffset; i++) {
1174         ALOGV("entry %zu", i);
1175         status_t err = parseChunk(&offset);
1176         if (err != OK) {
1177             return err;
1178         }
1179     }
1180     if (offset != stopOffset) {
1181         return ERROR_MALFORMED;
1182     }
1183 
1184     return OK;
1185 }
1186 
onChunkData(uint32_t type,off64_t offset,size_t size)1187 status_t IinfBox::onChunkData(uint32_t type, off64_t offset, size_t size) {
1188     if (type != FOURCC("infe")) {
1189         return OK;
1190     }
1191 
1192     InfeBox infeBox(source());
1193     ItemInfo itemInfo;
1194     status_t err = infeBox.parse(offset, size, &itemInfo);
1195     if (err == OK) {
1196         mItemInfos->push_back(itemInfo);
1197         mFourCCSeen.insert(itemInfo.itemType);
1198     }
1199     // InfeBox parse returns ERROR_UNSUPPORTED if the box if an unsupported
1200     // version. Ignore this error as it's not fatal.
1201     return (err == ERROR_UNSUPPORTED) ? OK : err;
1202 }
1203 
1204 //////////////////////////////////////////////////////////////////
1205 
ItemTable(DataSourceHelper * source)1206 ItemTable::ItemTable(DataSourceHelper *source)
1207     : mDataSource(source),
1208       mPrimaryItemId(0),
1209       mIdatOffset(0),
1210       mIdatSize(0),
1211       mImageItemsValid(false),
1212       mCurrentItemIndex(0) {
1213     mRequiredBoxes.insert('iprp');
1214     mRequiredBoxes.insert('iloc');
1215     mRequiredBoxes.insert('pitm');
1216     mRequiredBoxes.insert('iinf');
1217 }
1218 
~ItemTable()1219 ItemTable::~ItemTable() {}
1220 
parse(uint32_t type,off64_t data_offset,size_t chunk_data_size)1221 status_t ItemTable::parse(uint32_t type, off64_t data_offset, size_t chunk_data_size) {
1222     switch(type) {
1223         case FOURCC("iloc"):
1224         {
1225             return parseIlocBox(data_offset, chunk_data_size);
1226         }
1227         case FOURCC("iinf"):
1228         {
1229             return parseIinfBox(data_offset, chunk_data_size);
1230         }
1231         case FOURCC("iprp"):
1232         {
1233             return parseIprpBox(data_offset, chunk_data_size);
1234         }
1235         case FOURCC("pitm"):
1236         {
1237             return parsePitmBox(data_offset, chunk_data_size);
1238         }
1239         case FOURCC("idat"):
1240         {
1241             return parseIdatBox(data_offset, chunk_data_size);
1242         }
1243         case FOURCC("iref"):
1244         {
1245             return parseIrefBox(data_offset, chunk_data_size);
1246         }
1247         case FOURCC("ipro"):
1248         {
1249             ALOGW("ipro box not supported!");
1250             break;
1251         }
1252         default:
1253         {
1254             ALOGW("unrecognized box type: 0x%x", type);
1255             break;
1256         }
1257     }
1258     return ERROR_UNSUPPORTED;
1259 }
1260 
parseIlocBox(off64_t offset,size_t size)1261 status_t ItemTable::parseIlocBox(off64_t offset, size_t size) {
1262     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1263 
1264     IlocBox ilocBox(mDataSource, &mItemLocs);
1265     status_t err = ilocBox.parse(offset, size);
1266     if (err != OK) {
1267         return err;
1268     }
1269 
1270     if (ilocBox.hasConstructMethod1()) {
1271         mRequiredBoxes.insert('idat');
1272     }
1273 
1274     return buildImageItemsIfPossible('iloc');
1275 }
1276 
parseIinfBox(off64_t offset,size_t size)1277 status_t ItemTable::parseIinfBox(off64_t offset, size_t size) {
1278     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1279 
1280     IinfBox iinfBox(mDataSource, &mItemInfos);
1281     status_t err = iinfBox.parse(offset, size);
1282     if (err != OK) {
1283         return err;
1284     }
1285 
1286     if (iinfBox.hasFourCC(FOURCC("grid")) || iinfBox.hasFourCC(FOURCC("Exif"))) {
1287         mRequiredBoxes.insert('iref');
1288     }
1289 
1290     return buildImageItemsIfPossible('iinf');
1291 }
1292 
parsePitmBox(off64_t offset,size_t size)1293 status_t ItemTable::parsePitmBox(off64_t offset, size_t size) {
1294     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1295 
1296     PitmBox pitmBox(mDataSource);
1297     status_t err = pitmBox.parse(offset, size, &mPrimaryItemId);
1298     if (err != OK) {
1299         return err;
1300     }
1301 
1302     return buildImageItemsIfPossible('pitm');
1303 }
1304 
parseIprpBox(off64_t offset,size_t size)1305 status_t ItemTable::parseIprpBox(off64_t offset, size_t size) {
1306     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1307 
1308     IprpBox iprpBox(mDataSource, &mItemProperties, &mAssociations);
1309     status_t err = iprpBox.parse(offset, size);
1310     if (err != OK) {
1311         return err;
1312     }
1313 
1314     return buildImageItemsIfPossible('iprp');
1315 }
1316 
parseIdatBox(off64_t offset,size_t size)1317 status_t ItemTable::parseIdatBox(off64_t offset, size_t size) {
1318     ALOGV("%s: idat offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1319 
1320     // only remember the offset and size of idat box for later use
1321     mIdatOffset = offset;
1322     mIdatSize = size;
1323 
1324     return buildImageItemsIfPossible('idat');
1325 }
1326 
parseIrefBox(off64_t offset,size_t size)1327 status_t ItemTable::parseIrefBox(off64_t offset, size_t size) {
1328     ALOGV("%s: offset %lld, size %zu", __FUNCTION__, (long long)offset, size);
1329 
1330     IrefBox irefBox(mDataSource, &mItemReferences);
1331     status_t err = irefBox.parse(offset, size);
1332     if (err != OK) {
1333         return err;
1334     }
1335 
1336     return buildImageItemsIfPossible('iref');
1337 }
1338 
buildImageItemsIfPossible(uint32_t type)1339 status_t ItemTable::buildImageItemsIfPossible(uint32_t type) {
1340     if (mImageItemsValid) {
1341         return OK;
1342     }
1343 
1344     mBoxesSeen.insert(type);
1345 
1346     // need at least 'iprp', 'iloc', 'pitm', 'iinf';
1347     // need 'idat' if any items used construction_method of 2;
1348     // need 'iref' if there are grids.
1349     if (!std::includes(
1350             mBoxesSeen.begin(), mBoxesSeen.end(),
1351             mRequiredBoxes.begin(), mRequiredBoxes.end())) {
1352         return OK;
1353     }
1354 
1355     ALOGV("building image table...");
1356 
1357     for (size_t i = 0; i < mItemInfos.size(); i++) {
1358         const ItemInfo &info = mItemInfos[i];
1359 
1360         // Only handle 3 types of items, all others are ignored:
1361         //   'grid': derived image from tiles
1362         //   'hvc1': coded image (or tile)
1363         //   'Exif': EXIF metadata
1364         if (info.itemType != FOURCC("grid") &&
1365             info.itemType != FOURCC("hvc1") &&
1366             info.itemType != FOURCC("Exif")) {
1367             continue;
1368         }
1369 
1370         ssize_t itemIndex = mItemIdToItemMap.indexOfKey(info.itemId);
1371         if (itemIndex >= 0) {
1372             ALOGW("ignoring duplicate image item id %d", info.itemId);
1373             continue;
1374         }
1375 
1376         ssize_t ilocIndex = mItemLocs.indexOfKey(info.itemId);
1377         if (ilocIndex < 0) {
1378             ALOGE("iloc missing for image item id %d", info.itemId);
1379             continue;
1380         }
1381         const ItemLoc &iloc = mItemLocs[ilocIndex];
1382 
1383         off64_t offset;
1384         size_t size;
1385         if (iloc.getLoc(&offset, &size, mIdatOffset, mIdatSize) != OK) {
1386             return ERROR_MALFORMED;
1387         }
1388 
1389         if (info.itemType == FOURCC("Exif")) {
1390             // Only add if the Exif data is non-empty. The first 4 bytes contain
1391             // the offset to TIFF header, which the Exif parser doesn't use.
1392             if (size > 4) {
1393                 ExifItem exifItem = {
1394                         .offset = offset,
1395                         .size = size,
1396                 };
1397                 mItemIdToExifMap.add(info.itemId, exifItem);
1398             }
1399             continue;
1400         }
1401 
1402         ImageItem image(info.itemType, info.itemId, info.hidden);
1403 
1404         ALOGV("adding %s: itemId %d", image.isGrid() ? "grid" : "image", info.itemId);
1405 
1406         if (image.isGrid()) {
1407             // ImageGrid struct is at least 8-byte, at most 12-byte (if flags&1)
1408             if (size < 8 || size > 12) {
1409                 return ERROR_MALFORMED;
1410             }
1411             uint8_t buf[12];
1412             if (!mDataSource->readAt(offset, buf, size)) {
1413                 return ERROR_IO;
1414             }
1415 
1416             image.rows = buf[2] + 1;
1417             image.columns = buf[3] + 1;
1418 
1419             ALOGV("rows %d, columans %d", image.rows, image.columns);
1420         } else {
1421             image.offset = offset;
1422             image.size = size;
1423         }
1424         mItemIdToItemMap.add(info.itemId, image);
1425     }
1426 
1427     for (size_t i = 0; i < mAssociations.size(); i++) {
1428         attachProperty(mAssociations[i]);
1429     }
1430 
1431     for (size_t i = 0; i < mItemReferences.size(); i++) {
1432         mItemReferences[i]->apply(mItemIdToItemMap, mItemIdToExifMap);
1433     }
1434 
1435     bool foundPrimary = false;
1436     for (size_t i = 0; i < mItemIdToItemMap.size(); i++) {
1437         // add all non-hidden images, also add the primary even if it's marked
1438         // hidden, in case the primary is set to a thumbnail
1439         bool isPrimary = (mItemIdToItemMap[i].itemId == mPrimaryItemId);
1440         if (!mItemIdToItemMap[i].hidden || isPrimary) {
1441             mDisplayables.push_back(i);
1442         }
1443         foundPrimary |= isPrimary;
1444     }
1445 
1446     ALOGV("found %zu displayables", mDisplayables.size());
1447 
1448     // fail if no displayables are found
1449     if (mDisplayables.empty()) {
1450         return ERROR_MALFORMED;
1451     }
1452 
1453     // if the primary item id is invalid, set primary to the first displayable
1454     if (!foundPrimary) {
1455         mPrimaryItemId = mItemIdToItemMap[mDisplayables[0]].itemId;
1456     }
1457 
1458     mImageItemsValid = true;
1459     return OK;
1460 }
1461 
attachProperty(const AssociationEntry & association)1462 void ItemTable::attachProperty(const AssociationEntry &association) {
1463     ssize_t itemIndex = mItemIdToItemMap.indexOfKey(association.itemId);
1464 
1465     // ignore non-image items
1466     if (itemIndex < 0) {
1467         return;
1468     }
1469 
1470     uint16_t propertyIndex = association.index;
1471     if (propertyIndex >= mItemProperties.size()) {
1472         ALOGW("Ignoring invalid property index %d", propertyIndex);
1473         return;
1474     }
1475 
1476     ALOGV("attach property %d to item id %d)",
1477             propertyIndex, association.itemId);
1478 
1479     mItemProperties[propertyIndex]->attachTo(mItemIdToItemMap.editValueAt(itemIndex));
1480 }
1481 
countImages() const1482 uint32_t ItemTable::countImages() const {
1483     return mImageItemsValid ? mDisplayables.size() : 0;
1484 }
1485 
getImageMeta(const uint32_t imageIndex)1486 AMediaFormat *ItemTable::getImageMeta(const uint32_t imageIndex) {
1487     if (!mImageItemsValid) {
1488         return NULL;
1489     }
1490 
1491     if (imageIndex >= mDisplayables.size()) {
1492         ALOGE("%s: invalid image index %u", __FUNCTION__, imageIndex);
1493         return NULL;
1494     }
1495     const uint32_t itemIndex = mDisplayables[imageIndex];
1496     ALOGV("image[%u]: item index %u", imageIndex, itemIndex);
1497 
1498     const ImageItem *image = &mItemIdToItemMap[itemIndex];
1499 
1500     ssize_t tileItemIndex = -1;
1501     if (image->isGrid()) {
1502         if (image->dimgRefs.empty()) {
1503             return NULL;
1504         }
1505         tileItemIndex = mItemIdToItemMap.indexOfKey(image->dimgRefs[0]);
1506         if (tileItemIndex < 0) {
1507             return NULL;
1508         }
1509     }
1510 
1511     AMediaFormat *meta = AMediaFormat_new();
1512     AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
1513 
1514     if (image->itemId == mPrimaryItemId) {
1515         AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_DEFAULT, 1);
1516     }
1517 
1518     ALOGV("image[%u]: size %dx%d", imageIndex, image->width, image->height);
1519 
1520     AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_WIDTH, image->width);
1521     AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_HEIGHT, image->height);
1522     if (image->rotation != 0) {
1523         // Rotation angle in HEIF is CCW, convert to CW here to be
1524         // consistent with the other media formats.
1525         switch(image->rotation) {
1526             case 90:
1527             case 180:
1528             case 270:
1529                 AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_ROTATION, 360 - image->rotation);
1530                 break;
1531             default: break; // don't set if invalid
1532         }
1533     }
1534     // we validated no overflow in IspeBox::parse()
1535     AMediaFormat_setInt32(meta,
1536             AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 3 / 2);
1537 
1538     if (!image->thumbnails.empty()) {
1539         ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(image->thumbnails[0]);
1540         if (thumbItemIndex >= 0) {
1541             const ImageItem &thumbnail = mItemIdToItemMap[thumbItemIndex];
1542 
1543             if (thumbnail.hvcc != NULL) {
1544                 AMediaFormat_setInt32(meta,
1545                         AMEDIAFORMAT_KEY_THUMBNAIL_WIDTH, thumbnail.width);
1546                 AMediaFormat_setInt32(meta,
1547                         AMEDIAFORMAT_KEY_THUMBNAIL_HEIGHT, thumbnail.height);
1548                 AMediaFormat_setBuffer(meta,
1549                         AMEDIAFORMAT_KEY_THUMBNAIL_CSD_HEVC,
1550                         thumbnail.hvcc->data(), thumbnail.hvcc->size());
1551                 ALOGV("image[%u]: thumbnail: size %dx%d, item index %zd",
1552                         imageIndex, thumbnail.width, thumbnail.height, thumbItemIndex);
1553             } else {
1554                 ALOGW("%s: thumbnail data is missing for image[%u]!", __FUNCTION__, imageIndex);
1555             }
1556         } else {
1557             ALOGW("%s: Referenced thumbnail does not exist!", __FUNCTION__);
1558         }
1559     }
1560 
1561     if (image->isGrid()) {
1562         AMediaFormat_setInt32(meta,
1563                 AMEDIAFORMAT_KEY_GRID_ROWS, image->rows);
1564         AMediaFormat_setInt32(meta,
1565                 AMEDIAFORMAT_KEY_GRID_COLUMNS, image->columns);
1566         // point image to the first tile for grid size and HVCC
1567         image = &mItemIdToItemMap.editValueAt(tileItemIndex);
1568         AMediaFormat_setInt32(meta,
1569                 AMEDIAFORMAT_KEY_TILE_WIDTH, image->width);
1570         AMediaFormat_setInt32(meta,
1571                 AMEDIAFORMAT_KEY_TILE_HEIGHT, image->height);
1572         // we validated no overflow in IspeBox::parse()
1573         AMediaFormat_setInt32(meta,
1574                 AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, image->width * image->height * 3 / 2);
1575     }
1576 
1577     if (image->hvcc == NULL) {
1578         ALOGE("%s: hvcc is missing for image[%u]!", __FUNCTION__, imageIndex);
1579         return NULL;
1580     }
1581     AMediaFormat_setBuffer(meta,
1582             AMEDIAFORMAT_KEY_CSD_HEVC, image->hvcc->data(), image->hvcc->size());
1583 
1584     if (image->icc != NULL) {
1585         AMediaFormat_setBuffer(meta,
1586                 AMEDIAFORMAT_KEY_ICC_PROFILE, image->icc->data(), image->icc->size());
1587     }
1588     return meta;
1589 }
1590 
findImageItem(const uint32_t imageIndex,uint32_t * itemIndex)1591 status_t ItemTable::findImageItem(const uint32_t imageIndex, uint32_t *itemIndex) {
1592     if (!mImageItemsValid) {
1593         return INVALID_OPERATION;
1594     }
1595 
1596     if (imageIndex >= mDisplayables.size()) {
1597         ALOGE("%s: invalid image index %d", __FUNCTION__, imageIndex);
1598         return BAD_VALUE;
1599     }
1600 
1601     *itemIndex = mDisplayables[imageIndex];
1602 
1603     ALOGV("image[%u]: item index %u", imageIndex, *itemIndex);
1604     return OK;
1605 }
1606 
findThumbnailItem(const uint32_t imageIndex,uint32_t * itemIndex)1607 status_t ItemTable::findThumbnailItem(const uint32_t imageIndex, uint32_t *itemIndex) {
1608     if (!mImageItemsValid) {
1609         return INVALID_OPERATION;
1610     }
1611 
1612     if (imageIndex >= mDisplayables.size()) {
1613         ALOGE("%s: invalid image index %d", __FUNCTION__, imageIndex);
1614         return BAD_VALUE;
1615     }
1616 
1617     uint32_t imageItemIndex = mDisplayables[imageIndex];
1618 
1619     const ImageItem &imageItem = mItemIdToItemMap[imageItemIndex];
1620     if (imageItem.thumbnails.empty()) {
1621         *itemIndex = imageItemIndex;
1622         return OK;
1623     }
1624 
1625     ssize_t thumbItemIndex = mItemIdToItemMap.indexOfKey(imageItem.thumbnails[0]);
1626     if (thumbItemIndex < 0) {
1627         // Do not return the image item in this case, fail it so that the
1628         // thumbnail extraction code knows we really don't have it.
1629         return INVALID_OPERATION;
1630     }
1631 
1632     *itemIndex = thumbItemIndex;
1633     return OK;
1634 }
1635 
getImageOffsetAndSize(uint32_t * itemIndex,off64_t * offset,size_t * size)1636 status_t ItemTable::getImageOffsetAndSize(
1637         uint32_t *itemIndex, off64_t *offset, size_t *size) {
1638     if (!mImageItemsValid) {
1639         return INVALID_OPERATION;
1640     }
1641 
1642     if (itemIndex != NULL) {
1643         if (*itemIndex >= mItemIdToItemMap.size()) {
1644             ALOGE("%s: Bad item index!", __FUNCTION__);
1645             return BAD_VALUE;
1646         }
1647         mCurrentItemIndex = *itemIndex;
1648     }
1649 
1650     ImageItem &image = mItemIdToItemMap.editValueAt(mCurrentItemIndex);
1651     if (image.isGrid()) {
1652         uint32_t tileItemId;
1653         status_t err = image.getNextTileItemId(&tileItemId, itemIndex != NULL);
1654         if (err != OK) {
1655             return err;
1656         }
1657         ssize_t tileItemIndex = mItemIdToItemMap.indexOfKey(tileItemId);
1658         if (tileItemIndex < 0) {
1659             return ERROR_END_OF_STREAM;
1660         }
1661         *offset = mItemIdToItemMap[tileItemIndex].offset;
1662         *size = mItemIdToItemMap[tileItemIndex].size;
1663     } else {
1664         if (itemIndex == NULL) {
1665             // For single images, we only allow it to be read once, after that
1666             // it's EOS.  New item index must be requested each time.
1667             return ERROR_END_OF_STREAM;
1668         }
1669         *offset = mItemIdToItemMap[mCurrentItemIndex].offset;
1670         *size = mItemIdToItemMap[mCurrentItemIndex].size;
1671     }
1672 
1673     return OK;
1674 }
1675 
getExifOffsetAndSize(off64_t * offset,size_t * size)1676 status_t ItemTable::getExifOffsetAndSize(off64_t *offset, size_t *size) {
1677     if (!mImageItemsValid) {
1678         return INVALID_OPERATION;
1679     }
1680 
1681     ssize_t itemIndex = mItemIdToItemMap.indexOfKey(mPrimaryItemId);
1682 
1683     // this should not happen, something's seriously wrong.
1684     if (itemIndex < 0) {
1685         return INVALID_OPERATION;
1686     }
1687 
1688     const ImageItem &image = mItemIdToItemMap[itemIndex];
1689     if (image.cdscRefs.size() == 0) {
1690         return NAME_NOT_FOUND;
1691     }
1692 
1693     ssize_t exifIndex = mItemIdToExifMap.indexOfKey(image.cdscRefs[0]);
1694     if (exifIndex < 0) {
1695         return NAME_NOT_FOUND;
1696     }
1697 
1698     // skip the first 4-byte of the offset to TIFF header
1699     uint32_t tiffOffset;
1700     if (!mDataSource->readAt(
1701             mItemIdToExifMap[exifIndex].offset, &tiffOffset, 4)) {
1702         return ERROR_IO;
1703     }
1704 
1705     // We need 'Exif\0\0' before the tiff header
1706     tiffOffset = ntohl(tiffOffset);
1707     if (tiffOffset < 6) {
1708         return ERROR_MALFORMED;
1709     }
1710     // The first 4-byte of the item is the offset of the tiff header within the
1711     // exif data. The size of the item should be > 4 for a non-empty exif (this
1712     // was already checked when the item was added). Also check that the tiff
1713     // header offset is valid.
1714     if (mItemIdToExifMap[exifIndex].size <= 4 ||
1715             tiffOffset > mItemIdToExifMap[exifIndex].size - 4) {
1716         return ERROR_MALFORMED;
1717     }
1718 
1719     // Offset of 'Exif\0\0' relative to the beginning of 'Exif' item
1720     // (first 4-byte is the tiff header offset)
1721     uint32_t exifOffset = 4 + tiffOffset - 6;
1722     *offset = mItemIdToExifMap[exifIndex].offset + exifOffset;
1723     *size = mItemIdToExifMap[exifIndex].size - exifOffset;
1724     return OK;
1725 }
1726 
1727 } // namespace heif
1728 
1729 }  // namespace android
1730