• 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;
18 
19 import static org.junit.Assert.assertArrayEquals;
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertNull;
22 import static org.junit.Assert.fail;
23 
24 import android.content.Context;
25 import android.content.res.AssetFileDescriptor;
26 import android.media.ExifInterface;
27 import android.os.FileUtils;
28 import android.util.LongArray;
29 import android.util.ArraySet;
30 import android.util.Xml;
31 
32 import com.android.providers.media.tests.R;
33 import com.android.providers.media.util.IsoInterface;
34 import com.android.providers.media.util.XmpInterface;
35 
36 import org.junit.Test;
37 import org.junit.runner.RunWith;
38 import org.xmlpull.v1.XmlPullParser;
39 import org.xmlpull.v1.XmlPullParserException;
40 import org.xmlpull.v1.XmlPullParserFactory;
41 
42 import java.io.ByteArrayInputStream;
43 import java.io.File;
44 import java.io.FileOutputStream;
45 import java.io.IOException;
46 import java.io.InputStream;
47 import java.io.OutputStream;
48 import java.io.StringReader;
49 import java.nio.charset.StandardCharsets;
50 import java.util.Set;
51 
52 import androidx.test.InstrumentationRegistry;
53 import androidx.test.runner.AndroidJUnit4;
54 
55 @RunWith(AndroidJUnit4.class)
56 public class XmpInterfaceTest {
57     @Test
testContainer_Empty()58     public void testContainer_Empty() throws Exception {
59         final Context context = InstrumentationRegistry.getContext();
60         try (InputStream in = context.getResources().openRawResource(R.raw.test_image)) {
61             final XmpInterface xmp = XmpInterface.fromContainer(in);
62             assertNull(xmp.getFormat());
63             assertNull(xmp.getDocumentId());
64             assertNull(xmp.getInstanceId());
65             assertNull(xmp.getOriginalDocumentId());
66         }
67     }
68 
69     @Test
testContainer_ValidAttrs()70     public void testContainer_ValidAttrs() throws Exception {
71         final Context context = InstrumentationRegistry.getContext();
72         try (InputStream in = context.getResources().openRawResource(R.raw.lg_g4_iso_800_jpg)) {
73             final XmpInterface xmp = XmpInterface.fromContainer(in);
74             assertEquals("image/dng", xmp.getFormat());
75             assertEquals("xmp.did:041dfd42-0b46-4302-918a-836fba5016ed", xmp.getDocumentId());
76             assertEquals("xmp.iid:041dfd42-0b46-4302-918a-836fba5016ed", xmp.getInstanceId());
77             assertEquals("3F9DD7A46B26513A7C35272F0D623A06", xmp.getOriginalDocumentId());
78         }
79     }
80 
81     @Test
testContainer_ValidTags()82     public void testContainer_ValidTags() throws Exception {
83         final Context context = InstrumentationRegistry.getContext();
84         try (InputStream in = context.getResources().openRawResource(R.raw.lg_g4_iso_800_dng)) {
85             final XmpInterface xmp = XmpInterface.fromContainer(in);
86             assertEquals("image/dng", xmp.getFormat());
87             assertEquals("xmp.did:041dfd42-0b46-4302-918a-836fba5016ed", xmp.getDocumentId());
88             assertEquals("xmp.iid:041dfd42-0b46-4302-918a-836fba5016ed", xmp.getInstanceId());
89             assertEquals("3F9DD7A46B26513A7C35272F0D623A06", xmp.getOriginalDocumentId());
90         }
91     }
92 
93     @Test
testContainer_ExifRedactionRanges()94     public void testContainer_ExifRedactionRanges() throws Exception {
95         final Set<String> redactionTags = new ArraySet<>();
96         redactionTags.add(ExifInterface.TAG_GPS_LATITUDE);
97         redactionTags.add(ExifInterface.TAG_GPS_LONGITUDE);
98         redactionTags.add(ExifInterface.TAG_GPS_TIMESTAMP);
99         redactionTags.add(ExifInterface.TAG_GPS_VERSION_ID);
100 
101         // The XMP contents start at byte 1809. These are the file offsets.
102         final long[] expectedRanges = new long[]{2625,2675,2678,2730,2733,2792,2795,2841};
103         final Context context = InstrumentationRegistry.getContext();
104         try (InputStream in = context.getResources().openRawResource(R.raw.lg_g4_iso_800_jpg)) {
105             ExifInterface exif = new ExifInterface(in);
106             assertEquals(1809, exif.getAttributeRange(ExifInterface.TAG_XMP)[0]);
107             final XmpInterface xmp = XmpInterface.fromContainer(exif, redactionTags);
108             assertArrayEquals(expectedRanges, xmp.getRedactionRanges().toArray());
109         }
110     }
111 
112     @Test
testContainer_IsoRedactionRanges()113     public void testContainer_IsoRedactionRanges() throws Exception {
114         final Set<String> redactionTags = new ArraySet<>();
115         redactionTags.add(ExifInterface.TAG_GPS_LATITUDE);
116         redactionTags.add(ExifInterface.TAG_GPS_LONGITUDE);
117         redactionTags.add(ExifInterface.TAG_GPS_TIMESTAMP);
118         redactionTags.add(ExifInterface.TAG_GPS_VERSION_ID);
119 
120         final File file = stageFile(R.raw.test_video_xmp);
121         final IsoInterface mp4 = IsoInterface.fromFile(file);
122         final XmpInterface xmp = XmpInterface.fromContainer(mp4, redactionTags);
123 
124         // The XMP contents start at byte 30286. These are the file offsets.
125         final long[] expectedRanges = new long[]{37299,37349,37352,37404,37407,37466,37469,37515};
126         assertArrayEquals(expectedRanges, xmp.getRedactionRanges().toArray());
127     }
128 
129     @Test
testContainer_IsoRedactionRanges_BadTagValue()130     public void testContainer_IsoRedactionRanges_BadTagValue() throws Exception {
131         final Set<String> redactionTags = new ArraySet<>();
132         redactionTags.add(ExifInterface.TAG_GPS_LATITUDE);
133         redactionTags.add(ExifInterface.TAG_GPS_LONGITUDE);
134         redactionTags.add(ExifInterface.TAG_GPS_TIMESTAMP);
135         redactionTags.add(ExifInterface.TAG_GPS_VERSION_ID);
136 
137         // This file has some inner xml in the latitude tag. We should redact anyway.
138         final File file = stageFile(R.raw.test_video_xmp_bad_tag);
139         final IsoInterface mp4 = IsoInterface.fromFile(file);
140         final XmpInterface xmp = XmpInterface.fromContainer(mp4, redactionTags);
141 
142         // The XMP contents start at byte 30286. These are the file offsets.
143         final long[] expectedRanges = new long[]{37299,37349,37352,37404,37407,37466,37469,37515};
144         assertArrayEquals(expectedRanges, xmp.getRedactionRanges().toArray());
145     }
146 
147     @Test
testContainer_IsoRedactionRanges_MalformedXml()148     public void testContainer_IsoRedactionRanges_MalformedXml() throws Exception {
149         final Set<String> redactionTags = new ArraySet<>();
150         redactionTags.add(ExifInterface.TAG_GPS_LATITUDE);
151         redactionTags.add(ExifInterface.TAG_GPS_LONGITUDE);
152         redactionTags.add(ExifInterface.TAG_GPS_TIMESTAMP);
153         redactionTags.add(ExifInterface.TAG_GPS_VERSION_ID);
154 
155         // This file has malformed XML in the latitude tag. XML parsing will fail
156         final File file = stageFile(R.raw.test_video_xmp_malformed);
157         final IsoInterface mp4 = IsoInterface.fromFile(file);
158         try {
159             final XmpInterface xmp = XmpInterface.fromContainer(mp4, redactionTags);
160             fail("Should throw IOException");
161         } catch (IOException e) {}
162     }
163 
164     @Test
testStream_LineOffsets()165     public void testStream_LineOffsets() throws Exception {
166         final String xml =
167                 "<a:b xmlns:a='a' xmlns:c='c' c:d=''\n  c:f='g'>\n  <c:i>j</c:i>\n  </a:b>";
168         final InputStream xmlStream = new ByteArrayInputStream(xml.getBytes("UTF-8"));
169         final XmpInterface.ByteCountingInputStream stream =
170                 new XmpInterface.ByteCountingInputStream(xmlStream);
171 
172         final long[] expectedElementOffsets = new long[]{46,54,61,70};
173         XmlPullParser parser = Xml.newPullParser();
174         parser.setInput(stream, StandardCharsets.UTF_8.name());
175         int type;
176         int i = 0;
177         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
178             if (type == XmlPullParser.START_TAG || type == XmlPullParser.END_TAG) {
179                 assertEquals(expectedElementOffsets[i++], stream.getOffset(parser));
180             }
181         }
182     }
183 
stageFile(int resId)184     private static File stageFile(int resId) throws Exception {
185         final Context context = InstrumentationRegistry.getContext();
186         final File file = File.createTempFile("test", ".mp4");
187         try (InputStream in = context.getResources().openRawResource(resId);
188              OutputStream out = new FileOutputStream(file)) {
189             FileUtils.copy(in, out);
190         }
191         return file;
192     }
193 }
194