1 /*
2  * Copyright (C) 2018 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.overlaytest;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertTrue;
23 import static org.junit.Assert.fail;
24 
25 import android.content.Context;
26 import android.content.om.OverlayIdentifier;
27 import android.content.res.AssetManager;
28 import android.content.res.Configuration;
29 import android.content.res.Resources;
30 import android.content.res.XmlResourceParser;
31 import android.os.LocaleList;
32 import android.util.AttributeSet;
33 import android.util.TypedValue;
34 import android.util.Xml;
35 import android.view.LayoutInflater;
36 import android.view.View;
37 
38 import androidx.test.InstrumentationRegistry;
39 
40 import com.android.compatibility.common.util.AmUtils;
41 import com.android.internal.util.ArrayUtils;
42 import com.android.overlaytest.view.TestTextView;
43 
44 import org.junit.Before;
45 import org.junit.BeforeClass;
46 import org.junit.Ignore;
47 import org.junit.Test;
48 
49 import java.io.BufferedReader;
50 import java.io.InputStream;
51 import java.io.InputStreamReader;
52 import java.nio.charset.StandardCharsets;
53 import java.util.Locale;
54 
55 @Ignore
56 public abstract class OverlayBaseTest {
57     private Context mContext;
58     private Resources mResources;
59     private final int mMode;
60     static final int MODE_NO_OVERLAY = 0;
61     static final int MODE_SINGLE_OVERLAY = 1;
62     static final int MODE_MULTIPLE_OVERLAYS = 2;
63 
64     static final OverlayIdentifier APP_OVERLAY_ONE_PKG =
65             new OverlayIdentifier("com.android.overlaytest.app_overlay_one");
66     static final OverlayIdentifier APP_OVERLAY_TWO_PKG =
67             new OverlayIdentifier("com.android.overlaytest.app_overlay_two");
68     static final OverlayIdentifier FRAMEWORK_OVERLAY_PKG =
69             new OverlayIdentifier("com.android.overlaytest.framework");
70 
OverlayBaseTest(int mode)71     protected OverlayBaseTest(int mode) {
72         mMode = mode;
73     }
74 
75     @BeforeClass
setUpClass()76     public static void setUpClass() {
77         // Wait for package_added broadcasts to be handled so that OverlayManagerService
78         // can update it's internal state with the new packages.
79         AmUtils.waitForBroadcastBarrier();
80     }
81 
82     @Before
setUp()83     public void setUp() {
84         mContext = InstrumentationRegistry.getContext();
85         mResources = mContext.getResources();
86     }
87 
calculateRawResourceChecksum(int resId)88     private int calculateRawResourceChecksum(int resId) throws Throwable {
89         try (InputStream input = mResources.openRawResource(resId)) {
90             int ch, checksum = 0;
91             while ((ch = input.read()) != -1) {
92                 checksum = (checksum + ch) % 0xffddbb00;
93             }
94             return checksum;
95         }
96     }
97 
setLocale(Locale locale)98     private void setLocale(Locale locale) {
99         final LocaleList locales = new LocaleList(locale);
100         LocaleList.setDefault(locales);
101         Configuration config = new Configuration();
102         config.setLocales(locales);
103         mResources.updateConfiguration(config, mResources.getDisplayMetrics());
104     }
105 
getExpected(boolean no, boolean so, boolean mo)106     private boolean getExpected(boolean no, boolean so, boolean mo) {
107         switch (mMode) {
108             case MODE_NO_OVERLAY:
109                 return no;
110             case MODE_SINGLE_OVERLAY:
111                 return so;
112             case MODE_MULTIPLE_OVERLAYS:
113                 return mo;
114             default:
115                 fail("Unknown mode!");
116                 return no;
117         }
118     }
119 
getExpected(String no, String so, String mo)120     private String getExpected(String no, String so, String mo) {
121         switch (mMode) {
122             case MODE_NO_OVERLAY:
123                 return no;
124             case MODE_SINGLE_OVERLAY:
125                 return so;
126             case MODE_MULTIPLE_OVERLAYS:
127                 return mo;
128             default:
129                 fail("Unknown mode!");
130                 return no;
131         }
132     }
133 
getExpected(int no, int so, int mo)134     private int getExpected(int no, int so, int mo) {
135         switch (mMode) {
136             case MODE_NO_OVERLAY:
137                 return no;
138             case MODE_SINGLE_OVERLAY:
139                 return so;
140             case MODE_MULTIPLE_OVERLAYS:
141                 return mo;
142             default:
143                 fail("Unknown mode!");
144                 return no;
145         }
146     }
147 
getExpected(int[] no, int[] so, int[] mo)148     private int[] getExpected(int[] no, int[] so, int[] mo) {
149         switch (mMode) {
150             case MODE_NO_OVERLAY:
151                 return no;
152             case MODE_SINGLE_OVERLAY:
153                 return so;
154             case MODE_MULTIPLE_OVERLAYS:
155                 return mo;
156             default:
157                 fail("Unknown mode!");
158                 return no;
159         }
160     }
161 
assertResource(int resId, boolean no, boolean so, boolean mo)162     private void assertResource(int resId, boolean no, boolean so, boolean mo) throws Throwable {
163         boolean expected = getExpected(no, so, mo);
164         boolean actual = mResources.getBoolean(resId);
165         assertEquals(expected, actual);
166     }
167 
assertResource(int resId, int no, int so, int mo)168     private void assertResource(int resId, int no, int so, int mo) throws Throwable {
169         int expected = getExpected(no, so, mo);
170         int actual = mResources.getInteger(resId);
171         assertEquals(expected, actual);
172     }
173 
assertResource(int resId, String no, String so, String mo)174     private void assertResource(int resId, String no, String so, String mo) throws Throwable {
175         String expected = getExpected(no, so, mo);
176         String actual = mResources.getString(resId);
177         assertEquals(expected, actual);
178     }
179 
assertResource(int resId, int[] no, int[] so, int[] mo)180     private void assertResource(int resId, int[] no, int[] so, int[] mo) throws Throwable {
181         int[] expected = getExpected(no, so, mo);
182         int[] actual = mResources.getIntArray(resId);
183         assertEquals("length:", expected.length, actual.length);
184         for (int i = 0; i < actual.length; ++i) {
185             assertEquals("index " + i + ":", actual[i], expected[i]);
186         }
187     }
188 
189     @Test
testFrameworkBooleanOverlay()190     public void testFrameworkBooleanOverlay() throws Throwable {
191         // config_annoy_dianne has the value:
192         // - true when no overlay exists (MODE_NO_OVERLAY)
193         // - false when a single overlay exists (MODE_SINGLE_OVERLAY)
194         // - false when multiple overlays exists (MODE_MULTIPLE_OVERLAYS)
195         final int resId = com.android.internal.R.bool.config_annoy_dianne;
196         assertResource(resId, true, false, false);
197     }
198 
199     @Test
testBooleanOverlay()200     public void testBooleanOverlay() throws Throwable {
201         // usually_false has the value:
202         // - false when no overlay exists (MODE_NO_OVERLAY)
203         // - true when a single overlay exists (MODE_SINGLE_OVERLAY)
204         // - false when multiple overlays exists (MODE_MULTIPLE_OVERLAYS)
205         final int resId = R.bool.usually_false;
206         assertResource(resId, false, true, false);
207     }
208 
209     @Test
testBoolean()210     public void testBoolean() throws Throwable {
211         // always_true has no overlay
212         final int resId = R.bool.always_true;
213         assertResource(resId, true, true, true);
214     }
215 
216     @Test
testIntegerArrayOverlay()217     public void testIntegerArrayOverlay() throws Throwable {
218         // fibonacci has values:
219         // - eight first values of Fibonacci sequence, when no overlay exists (MODE_NO_OVERLAY)
220         // - eight first values of Fibonacci sequence (reversed), for single and multiple overlays
221         //   (MODE_SINGLE_OVERLAY, MODE_MULTIPLE_OVERLAYS)
222         final int resId = R.array.fibonacci;
223         assertResource(resId,
224                 new int[]{1, 1, 2, 3, 5, 8, 13, 21},
225                 new int[]{21, 13, 8, 5, 3, 2, 1, 1},
226                 new int[]{21, 13, 8, 5, 3, 2, 1, 1});
227     }
228 
229     @Test
testIntegerArray()230     public void testIntegerArray() throws Throwable {
231         // prime_numbers has no overlay
232         final int resId = R.array.prime_numbers;
233         final int[] expected = {2, 3, 5, 7, 11, 13, 17, 19};
234         assertResource(resId, expected, expected, expected);
235     }
236 
237     @Test
testDrawable()238     public void testDrawable() throws Throwable {
239         // drawable-nodpi/drawable has overlay (default config)
240         final int resId = R.drawable.drawable;
241         int actual = calculateRawResourceChecksum(resId);
242         int expected = 0;
243         switch (mMode) {
244             case MODE_NO_OVERLAY:
245                 expected = 0x00005665;
246                 break;
247             case MODE_SINGLE_OVERLAY:
248             case MODE_MULTIPLE_OVERLAYS:
249                 expected = 0x000051da;
250                 break;
251             default:
252                 fail("Unknown mode " + mMode);
253         }
254         assertEquals(expected, actual);
255     }
256 
257     @Test
testAppString()258     public void testAppString() throws Throwable {
259         final int resId = R.string.str;
260         assertResource(resId, "none", "single", "multiple");
261     }
262 
263     @Test
testApp2()264     public void testApp2() throws Throwable {
265         final int resId = R.string.str2; // only in base package and first app overlay
266         assertResource(resId, "none", "single", "single");
267     }
268 
269     @Test
testAppXml()270     public void testAppXml() throws Throwable {
271         int expected = getExpected(0, 1, 2);
272         int actual = -1;
273         XmlResourceParser parser = mResources.getXml(R.xml.integer);
274         int type = parser.getEventType();
275         while (type != XmlResourceParser.END_DOCUMENT && actual == -1) {
276             if (type == XmlResourceParser.START_TAG && "integer".equals(parser.getName())) {
277                 AttributeSet as = Xml.asAttributeSet(parser);
278                 actual = as.getAttributeIntValue(null, "value", -1);
279             }
280             type = parser.next();
281         }
282         parser.close();
283         assertEquals(expected, actual);
284     }
285 
286     @Test
testAppRaw()287     public void testAppRaw() throws Throwable {
288         final int resId = R.raw.lorem_ipsum;
289 
290         InputStream input = null;
291         BufferedReader reader = null;
292         String actual = "";
293         try {
294             input = mResources.openRawResource(resId);
295             reader = new BufferedReader(new InputStreamReader(input));
296             actual = reader.readLine();
297         } finally {
298             if (reader != null) {
299                 reader.close();
300             }
301             if (input != null) {
302                 input.close();
303             }
304         }
305 
306         final String no = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
307                 + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
308                 + "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo "
309                 + "consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse "
310                 + "cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non "
311                 + "proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
312         final String so = "Lorem ipsum: single overlay.";
313         final String mo = "Lorem ipsum: multiple overlays.";
314 
315         assertEquals(getExpected(no, so, mo), actual);
316     }
317 
318     @Test
testAssetsNotPossibleToOverlay()319     public void testAssetsNotPossibleToOverlay() throws Throwable {
320         final AssetManager am = mResources.getAssets();
321 
322         // AssetManager#list will include assets from all loaded non-overlay
323         // APKs, including the framework; framework-res.apk contains at least
324         // assets/{images,webkit}. Rather than checking the list, verify that
325         // assets only present in overlays are never part of the list.
326         String[] files = am.list("");
327         assertTrue(ArrayUtils.contains(files, "package-name.txt"));
328         assertFalse(ArrayUtils.contains(files, "foo.txt"));
329         assertFalse(ArrayUtils.contains(files, "bar.txt"));
330 
331         String contents = null;
332         try (InputStream is = am.open("package-name.txt")) {
333             final BufferedReader reader = new BufferedReader(
334                     new InputStreamReader(is, StandardCharsets.UTF_8));
335             StringBuilder str = new StringBuilder();
336             String line;
337             while ((line = reader.readLine()) != null) {
338                 str.append(line);
339             }
340             contents = str.toString();
341         }
342         assertEquals("com.android.overlaytest", contents);
343     }
344 
345     @Test
testRewrite()346     public void testRewrite() throws Throwable {
347         final TypedValue result = new TypedValue();
348         mResources.getValue(R.string.str, result, true);
349         assertEquals(result.resourceId & 0xff000000, 0x7f000000);
350     }
351 
352     @Test
testOverlayLayout()353     public void testOverlayLayout() throws Throwable {
354         final LayoutInflater inflater = LayoutInflater.from(mContext);
355         final View layout = inflater.inflate(R.layout.layout, null);
356         assertNotNull(layout.findViewById(R.id.view_1));
357 
358         final TestTextView view2 = layout.findViewById(R.id.view_2);
359         assertNotNull(view2);
360         switch (mMode) {
361             case MODE_NO_OVERLAY:
362                 assertEquals("none", view2.getCustomAttributeValue());
363                 break;
364             case MODE_SINGLE_OVERLAY:
365                 assertEquals("single", view2.getCustomAttributeValue());
366                 break;
367             case MODE_MULTIPLE_OVERLAYS:
368                 assertEquals("multiple", view2.getCustomAttributeValue());
369                 break;
370             default:
371                 fail("Unknown mode " + mMode);
372         }
373 
374         final TestTextView view3 = layout.findViewById(R.id.view_3);
375         assertNotNull(view3);
376         switch (mMode) {
377             case MODE_NO_OVERLAY:
378                 assertEquals("none", view3.getCustomAttributeValue());
379                 break;
380             case MODE_SINGLE_OVERLAY:
381             case MODE_MULTIPLE_OVERLAYS:
382                 assertEquals("overlaid", view3.getCustomAttributeValue());
383                 break;
384             default:
385                 fail("Unknown mode " + mMode);
386         }
387     }
388 
389     /*
390      * testMatrix* tests
391      *
392      * The naming convention textMatrixABCDEF refers to in which packages and
393      * which configurations a resource is defined (1 if the resource is
394      * defined). If defined, a slot is always given the same value.
395      *
396      * SLOT  PACKAGE           CONFIGURATION  VALUE
397      * A     target package    (default)      100
398      * B     target package    -sv            200
399      * C     AppOverlayOne     (default)      300
400      * D     AppOverlayOne     -sv            400
401      * E     AppOverlayTwo     (default)      500
402      * F     AppOverlayTwo     -sv            600
403      *
404      * Example: in testMatrix101110, the base package defines the
405      * R.integer.matrix101110 resource for the default configuration (value
406      * 100), OverlayAppFirst defines it for both default and Swedish
407      * configurations (values 300 and 400, respectively), and OverlayAppSecond
408      * defines it for the default configuration (value 500). If both overlays
409      * are loaded, the expected value after setting the language to Swedish is
410      * 400.
411      */
412     @Test
testMatrix100000()413     public void testMatrix100000() throws Throwable {
414         final int resId = R.integer.matrix_100000;
415         setLocale(new Locale("sv", "SE"));
416         assertResource(resId, 100, 100, 100);
417     }
418 
419     @Test
testMatrix100001()420     public void testMatrix100001() throws Throwable {
421         final int resId = R.integer.matrix_100001;
422         setLocale(new Locale("sv", "SE"));
423         assertResource(resId, 100, 100, 600);
424     }
425 
426     @Test
testMatrix100010()427     public void testMatrix100010() throws Throwable {
428         final int resId = R.integer.matrix_100010;
429         setLocale(new Locale("sv", "SE"));
430         assertResource(resId, 100, 100, 500);
431     }
432 
433     @Test
testMatrix100011()434     public void testMatrix100011() throws Throwable {
435         final int resId = R.integer.matrix_100011;
436         setLocale(new Locale("sv", "SE"));
437         assertResource(resId, 100, 100, 600);
438     }
439 
440     @Test
testMatrix100100()441     public void testMatrix100100() throws Throwable {
442         final int resId = R.integer.matrix_100100;
443         setLocale(new Locale("sv", "SE"));
444         assertResource(resId, 100, 400, 400);
445     }
446 
447     @Test
testMatrix100101()448     public void testMatrix100101() throws Throwable {
449         final int resId = R.integer.matrix_100101;
450         setLocale(new Locale("sv", "SE"));
451         assertResource(resId, 100, 400, 600);
452     }
453 
454     @Test
testMatrix100110()455     public void testMatrix100110() throws Throwable {
456         final int resId = R.integer.matrix_100110;
457         setLocale(new Locale("sv", "SE"));
458         assertResource(resId, 100, 400, 400);
459     }
460 
461     @Test
testMatrix100111()462     public void testMatrix100111() throws Throwable {
463         final int resId = R.integer.matrix_100111;
464         setLocale(new Locale("sv", "SE"));
465         assertResource(resId, 100, 400, 600);
466     }
467 
468     @Test
testMatrix101000()469     public void testMatrix101000() throws Throwable {
470         final int resId = R.integer.matrix_101000;
471         setLocale(new Locale("sv", "SE"));
472         assertResource(resId, 100, 300, 300);
473     }
474 
475     @Test
testMatrix101001()476     public void testMatrix101001() throws Throwable {
477         final int resId = R.integer.matrix_101001;
478         setLocale(new Locale("sv", "SE"));
479         assertResource(resId, 100, 300, 600);
480     }
481 
482     @Test
testMatrix101010()483     public void testMatrix101010() throws Throwable {
484         final int resId = R.integer.matrix_101010;
485         setLocale(new Locale("sv", "SE"));
486         assertResource(resId, 100, 300, 500);
487     }
488 
489     @Test
testMatrix101011()490     public void testMatrix101011() throws Throwable {
491         final int resId = R.integer.matrix_101011;
492         setLocale(new Locale("sv", "SE"));
493         assertResource(resId, 100, 300, 600);
494     }
495 
496     @Test
testMatrix101100()497     public void testMatrix101100() throws Throwable {
498         final int resId = R.integer.matrix_101100;
499         setLocale(new Locale("sv", "SE"));
500         assertResource(resId, 100, 400, 400);
501     }
502 
503     @Test
testMatrix101101()504     public void testMatrix101101() throws Throwable {
505         final int resId = R.integer.matrix_101101;
506         setLocale(new Locale("sv", "SE"));
507         assertResource(resId, 100, 400, 600);
508     }
509 
510     @Test
testMatrix101110()511     public void testMatrix101110() throws Throwable {
512         final int resId = R.integer.matrix_101110;
513         setLocale(new Locale("sv", "SE"));
514         assertResource(resId, 100, 400, 400);
515     }
516 
517     @Test
testMatrix101111()518     public void testMatrix101111() throws Throwable {
519         final int resId = R.integer.matrix_101111;
520         setLocale(new Locale("sv", "SE"));
521         assertResource(resId, 100, 400, 600);
522     }
523 
524     @Test
testMatrix110000()525     public void testMatrix110000() throws Throwable {
526         final int resId = R.integer.matrix_110000;
527         setLocale(new Locale("sv", "SE"));
528         assertResource(resId, 200, 200, 200);
529     }
530 
531     @Test
testMatrix110001()532     public void testMatrix110001() throws Throwable {
533         final int resId = R.integer.matrix_110001;
534         setLocale(new Locale("sv", "SE"));
535         assertResource(resId, 200, 200, 600);
536     }
537 
538     @Test
testMatrix110010()539     public void testMatrix110010() throws Throwable {
540         final int resId = R.integer.matrix_110010;
541         setLocale(new Locale("sv", "SE"));
542         assertResource(resId, 200, 200, 200);
543     }
544 
545     @Test
testMatrix110011()546     public void testMatrix110011() throws Throwable {
547         final int resId = R.integer.matrix_110011;
548         setLocale(new Locale("sv", "SE"));
549         assertResource(resId, 200, 200, 600);
550     }
551 
552     @Test
testMatrix110100()553     public void testMatrix110100() throws Throwable {
554         final int resId = R.integer.matrix_110100;
555         setLocale(new Locale("sv", "SE"));
556         assertResource(resId, 200, 400, 400);
557     }
558 
559     @Test
testMatrix110101()560     public void testMatrix110101() throws Throwable {
561         final int resId = R.integer.matrix_110101;
562         setLocale(new Locale("sv", "SE"));
563         assertResource(resId, 200, 400, 600);
564     }
565 
566     @Test
testMatrix110110()567     public void testMatrix110110() throws Throwable {
568         final int resId = R.integer.matrix_110110;
569         setLocale(new Locale("sv", "SE"));
570         assertResource(resId, 200, 400, 400);
571     }
572 
573     @Test
testMatrix110111()574     public void testMatrix110111() throws Throwable {
575         final int resId = R.integer.matrix_110111;
576         setLocale(new Locale("sv", "SE"));
577         assertResource(resId, 200, 400, 600);
578     }
579 
580     @Test
testMatrix111000()581     public void testMatrix111000() throws Throwable {
582         final int resId = R.integer.matrix_111000;
583         setLocale(new Locale("sv", "SE"));
584         assertResource(resId, 200, 200, 200);
585     }
586 
587     @Test
testMatrix111001()588     public void testMatrix111001() throws Throwable {
589         final int resId = R.integer.matrix_111001;
590         setLocale(new Locale("sv", "SE"));
591         assertResource(resId, 200, 200, 600);
592     }
593 
594     @Test
testMatrix111010()595     public void testMatrix111010() throws Throwable {
596         final int resId = R.integer.matrix_111010;
597         setLocale(new Locale("sv", "SE"));
598         assertResource(resId, 200, 200, 200);
599     }
600 
601     @Test
testMatrix111011()602     public void testMatrix111011() throws Throwable {
603         final int resId = R.integer.matrix_111011;
604         setLocale(new Locale("sv", "SE"));
605         assertResource(resId, 200, 200, 600);
606     }
607 
608     @Test
testMatrix111100()609     public void testMatrix111100() throws Throwable {
610         final int resId = R.integer.matrix_111100;
611         setLocale(new Locale("sv", "SE"));
612         assertResource(resId, 200, 400, 400);
613     }
614 
615     @Test
testMatrix111101()616     public void testMatrix111101() throws Throwable {
617         final int resId = R.integer.matrix_111101;
618         setLocale(new Locale("sv", "SE"));
619         assertResource(resId, 200, 400, 600);
620     }
621 
622     @Test
testMatrix111110()623     public void testMatrix111110() throws Throwable {
624         final int resId = R.integer.matrix_111110;
625         setLocale(new Locale("sv", "SE"));
626         assertResource(resId, 200, 400, 400);
627     }
628 
629     @Test
testMatrix111111()630     public void testMatrix111111() throws Throwable {
631         final int resId = R.integer.matrix_111111;
632         setLocale(new Locale("sv", "SE"));
633         assertResource(resId, 200, 400, 600);
634     }
635 }
636