• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.providers.media.util;
18 
19 import static com.android.providers.media.util.FileCreationUtils.stageMp4File;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import static org.junit.Assert.assertThrows;
24 
25 import android.content.Context;
26 import android.media.ExifInterface;
27 import android.util.Xml;
28 
29 import androidx.test.platform.app.InstrumentationRegistry;
30 
31 import com.android.providers.media.R;
32 
33 import org.junit.Test;
34 import org.xmlpull.v1.XmlPullParser;
35 
36 import java.io.ByteArrayInputStream;
37 import java.io.File;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.nio.charset.StandardCharsets;
41 
42 public class XmpDataParserTest {
43     @Test
testContainer_Empty()44     public void testContainer_Empty() throws Exception {
45         final Context context = InstrumentationRegistry.getInstrumentation().getContext();
46         try (InputStream in = context.getResources().openRawResource(R.raw.test_image)) {
47             final ExifInterface exifInterface = new ExifInterface(in);
48             final XmpInterface xmp = XmpDataParser.createXmpInterface(exifInterface);
49             assertThat(xmp.getFormat()).isNull();
50             assertThat(xmp.getDocumentId()).isNull();
51             assertThat(xmp.getInstanceId()).isNull();
52             assertThat(xmp.getOriginalDocumentId()).isNull();
53         }
54     }
55 
56     @Test
testContainer_ValidAttrs()57     public void testContainer_ValidAttrs() throws Exception {
58         final Context context = InstrumentationRegistry.getInstrumentation().getContext();
59         try (InputStream in = context.getResources().openRawResource(R.raw.lg_g4_iso_800_jpg)) {
60             final ExifInterface exifInterface = new ExifInterface(in);
61             final XmpInterface xmp = XmpDataParser.createXmpInterface(exifInterface);
62             assertThat(xmp.getFormat()).isEqualTo("image/dng");
63             assertThat(xmp.getDocumentId()).isEqualTo(
64                     "xmp.did:041dfd42-0b46-4302-918a-836fba5016ed");
65             assertThat(xmp.getInstanceId()).isEqualTo(
66                     "xmp.iid:041dfd42-0b46-4302-918a-836fba5016ed");
67             assertThat(xmp.getOriginalDocumentId()).isEqualTo("3F9DD7A46B26513A7C35272F0D623A06");
68         }
69     }
70 
71     @Test
testContainer_ValidTags()72     public void testContainer_ValidTags() throws Exception {
73         final Context context = InstrumentationRegistry.getInstrumentation().getContext();
74         try (InputStream in = context.getResources().openRawResource(R.raw.lg_g4_iso_800_dng)) {
75             final ExifInterface exifInterface = new ExifInterface(in);
76             final XmpInterface xmp = XmpDataParser.createXmpInterface(exifInterface);
77             assertThat(xmp.getFormat()).isEqualTo("image/dng");
78             assertThat(xmp.getDocumentId()).isEqualTo(
79                     "xmp.did:041dfd42-0b46-4302-918a-836fba5016ed");
80             assertThat(xmp.getInstanceId()).isEqualTo(
81                     "xmp.iid:041dfd42-0b46-4302-918a-836fba5016ed");
82             assertThat(xmp.getOriginalDocumentId()).isEqualTo("3F9DD7A46B26513A7C35272F0D623A06");
83         }
84     }
85 
86     @Test
testContainer_ExifRedactionRanges()87     public void testContainer_ExifRedactionRanges() throws Exception {
88         final Context context = InstrumentationRegistry.getInstrumentation().getContext();
89         try (InputStream in = context.getResources().openRawResource(R.raw.lg_g4_iso_800_jpg)) {
90             ExifInterface exif = new ExifInterface(in);
91             assertThat(exif.getAttributeRange(ExifInterface.TAG_XMP)[0]).isEqualTo(1809);
92 
93             // Confirm redact range within entire file
94             // The XMP contents start at byte 1809. These are the file offsets.
95             final long[] expectedRanges =
96                     new long[]{2625, 2675, 2678, 2730, 2733, 2792, 2795, 2841};
97             assertThat(XmpDataParser.getRedactionRanges(exif).toArray()).isEqualTo(expectedRanges);
98 
99             // Confirm redact range within local copy
100             final XmpInterface xmp = XmpDataParser.createXmpInterface(exif);
101             final String redactedXmp = new String(xmp.getRedactedXmp());
102             assertThat(redactedXmp).doesNotContain("exif:GPSLatitude");
103             assertThat(redactedXmp).doesNotContain("exif:GPSLongitude");
104             assertThat(redactedXmp).contains("exif:ShutterSpeedValue");
105         }
106     }
107 
108     @Test
testContainer_IsoRedactionRanges()109     public void testContainer_IsoRedactionRanges() throws Exception {
110         final File file = stageMp4File(R.raw.test_video_xmp);
111         final IsoInterface mp4 = IsoInterface.fromFile(file);
112 
113         // Confirm redact range within entire file
114         // The XMP contents start at byte 30286. These are the file offsets.
115         final long[] expectedRanges =
116                 new long[]{37299, 37349, 37352, 37404, 37407, 37466, 37469, 37515};
117         assertThat(XmpDataParser.getRedactionRanges(mp4).toArray()).isEqualTo(expectedRanges);
118 
119         XmpInterface xmpInterface = XmpDataParser.createXmpInterface(mp4);
120         // Confirm redact range within local copy
121         final String redactedXmp = new String(xmpInterface.getRedactedXmp());
122         assertThat(redactedXmp).doesNotContain("exif:GPSLatitude");
123         assertThat(redactedXmp).doesNotContain("exif:GPSLongitude");
124         assertThat(redactedXmp).contains("exif:ShutterSpeedValue");
125     }
126 
127     @Test
testContainer_IsoRedactionRanges_BadTagValue()128     public void testContainer_IsoRedactionRanges_BadTagValue() throws Exception {
129         // This file has some inner xml in the latitude tag. We should redact anyway.
130         final File file = stageMp4File(R.raw.test_video_xmp_bad_tag);
131         final IsoInterface mp4 = IsoInterface.fromFile(file);
132 
133         // The XMP contents start at byte 30286. These are the file offsets.
134         final long[] expectedRanges =
135                 new long[]{37299, 37349, 37352, 37404, 37407, 37466, 37469, 37515};
136         assertThat(XmpDataParser.getRedactionRanges(mp4).toArray()).isEqualTo(expectedRanges);
137     }
138 
139     @Test
testContainer_IsoRedactionRanges_MalformedXml()140     public void testContainer_IsoRedactionRanges_MalformedXml() throws Exception {
141         // This file has malformed XML in the latitude tag. XML parsing will fail
142         final File file = stageMp4File(R.raw.test_video_xmp_malformed);
143         final IsoInterface mp4 = IsoInterface.fromFile(file);
144         assertThrows(IOException.class, () -> XmpDataParser.createXmpInterface(mp4));
145     }
146 
147     @Test
testContainer_ExifRedactionRanges_MalformedXml()148     public void testContainer_ExifRedactionRanges_MalformedXml() throws Exception {
149         // This file has malformed XML in the latitude tag. XML parsing will fail
150         final File file = stageMp4File(R.raw.test_image_xmp_malformed);
151         final ExifInterface image = new ExifInterface(file);
152         assertThrows(IOException.class, () -> XmpDataParser.createXmpInterface(image));
153     }
154 
155     @Test
testRedaction_IsoRedactionRanges_MalformedXml()156     public void testRedaction_IsoRedactionRanges_MalformedXml() throws Exception {
157         final File file = stageMp4File(R.raw.test_video_xmp_malformed);
158         final IsoInterface mp4 = IsoInterface.fromFile(file);
159         final long[] expectedRanges = new long[]{30286, 43483};
160         final long[] actualRanges = XmpDataParser.getRedactionRanges(mp4).toArray();
161         assertThat(actualRanges).isEqualTo(expectedRanges);
162     }
163 
164     @Test
testRedaction_ExifRedactionRanges_MalformedXml()165     public void testRedaction_ExifRedactionRanges_MalformedXml() throws Exception {
166         final File file = stageMp4File(R.raw.test_image_xmp_malformed);
167         ExifInterface exif = new ExifInterface(file);
168         final long[] expectedRanges = new long[]{289, 626};
169         final long[] actualRanges = XmpDataParser.getRedactionRanges(exif).toArray();
170         assertThat(actualRanges).isEqualTo(expectedRanges);
171     }
172 
173     @Test
testStream_LineOffsets()174     public void testStream_LineOffsets() throws Exception {
175         final String xml =
176                 "<a:b xmlns:a='a' xmlns:c='c' c:d=''\n  c:f='g'>\n  <c:i>j</c:i>\n  </a:b>";
177         final InputStream xmlStream = new ByteArrayInputStream(
178                 xml.getBytes(StandardCharsets.UTF_8));
179         final XmpDataParser.ByteCountingInputStream stream =
180                 new XmpDataParser.ByteCountingInputStream(xmlStream);
181 
182         final long[] expectedElementOffsets = new long[]{46, 54, 61, 70};
183         XmlPullParser parser = Xml.newPullParser();
184         parser.setInput(stream, StandardCharsets.UTF_8.name());
185         int type;
186         int i = 0;
187         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
188             if (type == XmlPullParser.START_TAG || type == XmlPullParser.END_TAG) {
189                 assertThat(stream.getOffset(parser)).isEqualTo(expectedElementOffsets[i++]);
190             }
191         }
192     }
193 
194     /**
195      * Exercise some random methods for code coverage purposes.
196      */
197     @Test
testStream_Misc()198     public void testStream_Misc() throws Exception {
199         final InputStream xmlStream = new ByteArrayInputStream(
200                 "abcdefghijklmnoprstuvwxyz".getBytes(StandardCharsets.UTF_8));
201         final XmpDataParser.ByteCountingInputStream stream =
202                 new XmpDataParser.ByteCountingInputStream(xmlStream);
203 
204         {
205             final byte[] buf = new byte[4];
206             stream.read(buf);
207             assertThat(buf).isEqualTo("abcd".getBytes(StandardCharsets.UTF_8));
208         }
209         {
210             final byte[] buf = new byte[4];
211             stream.read(buf, 0, buf.length);
212             assertThat(buf).isEqualTo("efgh".getBytes(StandardCharsets.UTF_8));
213         }
214         {
215             assertThat(stream.skip(4)).isEqualTo(4);
216             assertThat(stream.read()).isEqualTo('m');
217         }
218 
219         assertThat(stream.toString()).isNotNull();
220         stream.close();
221     }
222 }
223