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