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