Status Update
Comments
mh...@google.com <mh...@google.com> #2
Branch: androidx-main
commit 2db6d01af84b5752190c6dd28cdea12500b701cb
Author: Charcoal Chen <charcoalchen@google.com>
Date: Fri Dec 23 10:15:10 2022
Fix JPEG image corruption issue if writing Exif location data on some Samsung Android 12 devices
This issue can be avoided in CameraX side by skipping the unnecessary Exif data copy. But there should still be some unknown reason in ExifInterface or these problematic devices to cause the issue.
Relnote: "Fixed JPEG image corruption issue if writing Exif location data on some Samsung Android 12 devices."
Bug: 263289024
Test: ImageCaptureTest
Change-Id: Ib70862aa6e654f06b9358e3f92bbb98c86cb9caf
M camera/camera-core/src/main/java/androidx/camera/core/impl/utils/Exif.java
M camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
ra...@google.com <ra...@google.com> #3
Thanks for reporting, i can use the example app to reproduce the problem. I saved the corrupted file and pulled it off the device and ran it through exiftool
, which complained (it doesn't complain as loudly about the source file, just some technically out-of-order tags):
$ exiftool -v2 editedFile.jpg
ExifToolVersion = 12.52
FileName = editedFile.jpg
Directory = .
FileSize = 433785
FileModifyDate = 1678109782
FileAccessDate = 1678109782
FileInodeChangeDate = 1678109782
FilePermissions = 33184
FileType = JPEG
FileTypeExtension = JPG
MIMEType = image/jpeg
Warning = [minor] Skipped unknown 617 bytes after JPEG APP1 segment
JPEG APP1 (46 bytes):
ExifByteOrder = II
Warning = Short directory size for IFD0 (missing 150 bytes)
| Warning = Bad IFD0 directory
Warning = [minor] Skipped unknown 917 bytes after JPEG NULL segment
JPEG SOI
JPEG APP0 (14 bytes):
+ [BinaryData directory, 9 bytes]
| JFIFVersion = 1 1
| - Tag 0x0000 (2 bytes, int8u[2])
| ResolutionUnit = 0
| - Tag 0x0002 (1 bytes, int8u[1])
| XResolution = 1
| - Tag 0x0003 (2 bytes, int16u[1])
| YResolution = 1
| - Tag 0x0005 (2 bytes, int16u[1])
| ThumbnailWidth = 0
| - Tag 0x0007 (1 bytes, int8u[1])
| ThumbnailHeight = 0
| - Tag 0x0008 (1 bytes, int8u[1])
JPEG DQT (65 bytes):
JPEG DQT (65 bytes):
JPEG SOF0 (15 bytes):
ImageWidth = 512
ImageHeight = 384
EncodingProcess = 0
BitsPerSample = 8
ColorComponents = 3
YCbCrSubSampling = 2 1
JPEG DHT (29 bytes):
JPEG DHT (179 bytes):
JPEG DHT (29 bytes):
JPEG DHT (179 bytes):
JPEG SOS
cc...@google.com <cc...@google.com> #4
The size of the JPEG APP1
segment looks wrong in the edited file (from exiftool
):
JPEG APP1 (46 bytes):
Compared to the original file:
JPEG APP1 (65279 bytes):
In binary this difference is visible in the 5th/6th byte of each file (from hexdump -C
). The first 4 bytes are a JPEG marker (0xFF
), a JPEG SOI (start of image, 0xd8
), another marker and an APP1 (0xe1
), followed by 2 bytes indicating the size of the APP1 segment.
Edited image:
ff d8 ff e1 00 30
Original image:
ff d8 ff e1 ff 01
Looking into why ExifInterface
is writing out the wrong APP1 size, I turned on
$ adb shell setprop log.tag.ExifInterface VERBOSE
And now it becomes a bit more obvious (this logging is from
saveJpegAttributes starting with (inputStream: java.io.BufferedInputStream@4fba9e8, outputStream: java.io.BufferedOutputStream@e5c3801)
index: 0, offsets: 8, tag count: 15, data sizes: 81, total size: 65584
65584
(0x10030
) is greater than the max 16-bit unsigned value (2^16 = 65536
), so when we ByteOrderedDataOutputStream.writeUnsignedShort
writeShort((short) val)
) truncates to the lower two bytes (0x0030
) - and this gets written out to the file and then breaks everything.
I think this likely only affects files that are very close to the 2^16
threshold, and the act of 'copying' all the EXIF attributes over probably reformats some of them just enough to take up slightly more bytes.
I'm not sure what is supposed to happen when a JPEG APP1 segment gets larger than 2^16
bytes, I need to dig a bit further into that part.
ap...@google.com <ap...@google.com> #5
Actually thinking about it more, it's not obvious to me why the APP1
segment of the original image is so large either - looking in exiftool
it doesn't seem to contain any particularly enormous fields, so I can't really see where 65kB comes from.
na...@google.com <na...@google.com> #6
re APP1
segment is so large, I missed the thumbnail the first time, which takes up a significant chunk (64kB):
+ [IFD1 directory with 15 entries]
| 0) ImageHeight = 384
| - Tag 0x0101 (4 bytes, int32u[1]):
| 0323: 80 01 00 00 [....]
| 1) Orientation = 6
| - Tag 0x0112 (2 bytes, int16u[1]):
| 032f: 06 00 [..]
| Warning = Tag ID 0x0103 Compression out of sequence in IFD1
| 2) Compression = 6
| - Tag 0x0103 (2 bytes, int16u[1]):
| 033b: 06 00 [..]
| 3) ThumbnailOffset = 1273
| - Tag 0x0201 (4 bytes, int32u[1]):
| 0347: f9 04 00 00 [....]
| 4) ThumbnailLength = 64000
| - Tag 0x0202 (4 bytes, int32u[1]):
| 0353: 00 fa 00 00 [....]
| Warning = Tag ID 0x010f Make out of sequence in IFD1
Description
Component used: not sure what this means but BaselineProfileRule from macrobenchmark library
Version used: latest nightly snapshot from androidx.dev (1.2.0-SNAPSHOT)
Devices/Android versions reproduced on: Android P on go/acid
The macrobenchmark library fails to get a profile when the apk under test is actually a bundle with multiple splits. We at chrome use bundles with multiple splits (actually most of our code lives in splits rather than in the base module). When I try to generate a profile I get this in the output of the instrumentation:
The cause of this is this line here expects only a single path to return but for bundles, you get multiple ones (one for each split). eg:
The rest of the code in BaselineProfiles.kt also expects only the one profile. But there are usually multiple (one for each split).
The documentation for baseline profiles mentions both APKs and Bundles so I assume bundles should be supported?