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 android.os;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertThrows;
24 import static org.junit.Assert.assertTrue;
25 
26 import android.platform.test.annotations.IgnoreUnderRavenwood;
27 import android.platform.test.annotations.Presubmit;
28 import android.platform.test.ravenwood.RavenwoodRule;
29 import android.util.Log;
30 
31 import androidx.test.runner.AndroidJUnit4;
32 
33 import org.junit.Rule;
34 import org.junit.Test;
35 import org.junit.runner.RunWith;
36 
37 import java.util.ArrayList;
38 
39 @Presubmit
40 @RunWith(AndroidJUnit4.class)
41 public class ParcelTest {
42     @Rule
43     public final RavenwoodRule mRavenwood = new RavenwoodRule();
44 
45     private static final int WORK_SOURCE_1 = 1000;
46     private static final int WORK_SOURCE_2 = 1002;
47     private static final String INTERFACE_TOKEN_1 = "IBinder interface token";
48     private static final String INTERFACE_TOKEN_2 = "Another IBinder interface token";
49 
50     @Test
51     @IgnoreUnderRavenwood(blockedBy = Parcel.class)
testIsForRpc()52     public void testIsForRpc() {
53         Parcel p = Parcel.obtain();
54         assertEquals(false, p.isForRpc());
55         p.recycle();
56     }
57 
58     @Test
59     @IgnoreUnderRavenwood(blockedBy = Parcel.class)
testCallingWorkSourceUidAfterWrite()60     public void testCallingWorkSourceUidAfterWrite() {
61         Parcel p = Parcel.obtain();
62         // Method does not throw if replaceCallingWorkSourceUid is called before requests headers
63         // are added.
64         assertEquals(false, p.replaceCallingWorkSourceUid(WORK_SOURCE_1));
65         assertEquals(Binder.UNSET_WORKSOURCE, p.readCallingWorkSourceUid());
66 
67         // WorkSource can be updated.
68         p.writeInterfaceToken(INTERFACE_TOKEN_1);
69         assertEquals(true, p.replaceCallingWorkSourceUid(WORK_SOURCE_2));
70         assertEquals(WORK_SOURCE_2, p.readCallingWorkSourceUid());
71 
72         // WorkSource can be updated to unset value.
73         assertEquals(true, p.replaceCallingWorkSourceUid(Binder.UNSET_WORKSOURCE));
74         assertEquals(Binder.UNSET_WORKSOURCE, p.readCallingWorkSourceUid());
75 
76         p.recycle();
77     }
78 
79     @Test
80     @IgnoreUnderRavenwood(blockedBy = Parcel.class)
testCallingWorkSourceUidAfterEnforce()81     public void testCallingWorkSourceUidAfterEnforce() {
82         Parcel p = Parcel.obtain();
83         p.writeInterfaceToken(INTERFACE_TOKEN_1);
84         assertEquals(true, p.replaceCallingWorkSourceUid(WORK_SOURCE_1));
85         p.setDataPosition(0);
86 
87         p.enforceInterface(INTERFACE_TOKEN_1);
88         assertEquals(WORK_SOURCE_1, p.readCallingWorkSourceUid());
89 
90         // WorkSource can be updated.
91         assertEquals(true, p.replaceCallingWorkSourceUid(WORK_SOURCE_2));
92         assertEquals(WORK_SOURCE_2, p.readCallingWorkSourceUid());
93 
94         p.recycle();
95     }
96 
97     @Test
98     @IgnoreUnderRavenwood(blockedBy = Parcel.class)
testParcelWithMultipleHeaders()99     public void testParcelWithMultipleHeaders() {
100         Parcel p = Parcel.obtain();
101         Binder.setCallingWorkSourceUid(WORK_SOURCE_1);
102         p.writeInterfaceToken(INTERFACE_TOKEN_1);
103         Binder.setCallingWorkSourceUid(WORK_SOURCE_2);
104         p.writeInterfaceToken(INTERFACE_TOKEN_2);
105         p.setDataPosition(0);
106 
107         // WorkSource is from the first header.
108         p.enforceInterface(INTERFACE_TOKEN_1);
109         assertEquals(WORK_SOURCE_1, p.readCallingWorkSourceUid());
110         p.enforceInterface(INTERFACE_TOKEN_2);
111         assertEquals(WORK_SOURCE_1, p.readCallingWorkSourceUid());
112 
113         p.recycle();
114     }
115 
116     /**
117      * Verify that writing/reading UTF-8 and UTF-16 strings works well.
118      */
119     @Test
testStrings()120     public void testStrings() {
121         final String[] strings = {
122                 null, "", "abc\0def", "com.example.typical_package_name",
123                 "從不喜歡孤單一個 - 蘇永康/吳雨霏", "example"
124         };
125 
126         final Parcel p = Parcel.obtain();
127         for (String string : strings) {
128             p.writeString8(string);
129             p.writeString16(string);
130         }
131 
132         p.setDataPosition(0);
133         for (String string : strings) {
134             assertEquals(string, p.readString8());
135             assertEquals(string, p.readString16());
136         }
137     }
138 
139     @Test
testCompareDataInRange_whenSameData()140     public void testCompareDataInRange_whenSameData() {
141         Parcel pA = Parcel.obtain();
142         int iA = pA.dataPosition();
143         pA.writeInt(13);
144         pA.writeString("Tiramisu");
145         int length = pA.dataPosition() - iA;
146         Parcel pB = Parcel.obtain();
147         pB.writeString("Prefix");
148         int iB = pB.dataPosition();
149         pB.writeInt(13);
150         pB.writeString("Tiramisu");
151 
152         assertTrue(Parcel.compareData(pA, iA, pB, iB, length));
153     }
154 
155     @Test
156     @IgnoreUnderRavenwood(blockedBy = Parcel.class)
testCompareDataInRange_whenSameDataWithBinder()157     public void testCompareDataInRange_whenSameDataWithBinder() {
158         Binder binder = new Binder();
159         Parcel pA = Parcel.obtain();
160         int iA = pA.dataPosition();
161         pA.writeInt(13);
162         pA.writeStrongBinder(binder);
163         pA.writeString("Tiramisu");
164         int length = pA.dataPosition() - iA;
165         Parcel pB = Parcel.obtain();
166         pB.writeString("Prefix");
167         int iB = pB.dataPosition();
168         pB.writeInt(13);
169         pB.writeStrongBinder(binder);
170         pB.writeString("Tiramisu");
171 
172         assertTrue(Parcel.compareData(pA, iA, pB, iB, length));
173     }
174 
175     @Test
testCompareDataInRange_whenDifferentData()176     public void testCompareDataInRange_whenDifferentData() {
177         Parcel pA = Parcel.obtain();
178         int iA = pA.dataPosition();
179         pA.writeInt(13);
180         pA.writeString("Tiramisu");
181         int length = pA.dataPosition() - iA;
182         Parcel pB = Parcel.obtain();
183         int iB = pB.dataPosition();
184         pB.writeString("Prefix");
185         pB.writeInt(13);
186         pB.writeString("Tiramisu");
187 
188         assertFalse(Parcel.compareData(pA, iA, pB, iB, length));
189     }
190 
191     @Test
testCompareDataInRange_whenLimitOutOfBounds_throws()192     public void testCompareDataInRange_whenLimitOutOfBounds_throws() {
193         Parcel pA = Parcel.obtain();
194         int iA = pA.dataPosition();
195         pA.writeInt(12);
196         pA.writeString("Tiramisu");
197         int length = pA.dataPosition() - iA;
198         Parcel pB = Parcel.obtain();
199         pB.writeString("Prefix");
200         int iB = pB.dataPosition();
201         pB.writeInt(13);
202         pB.writeString("Tiramisu");
203         pB.writeInt(-1);
204 
205         assertThrows(IllegalArgumentException.class,
206                 () -> Parcel.compareData(pA, iA + length, pB, iB, 1));
207         assertThrows(IllegalArgumentException.class,
208                 () -> Parcel.compareData(pA, iA, pB, pB.dataSize(), 1));
209         assertThrows(IllegalArgumentException.class,
210                 () -> Parcel.compareData(pA, iA, pB, iB, length + 1));
211         assertThrows(IllegalArgumentException.class,
212                 () -> Parcel.compareData(pA, iA + length + 1, pB, iB, 0));
213         assertThrows(IllegalArgumentException.class,
214                 () -> Parcel.compareData(pA, iA, pB, iB + pB.dataSize() + 1, 0));
215     }
216 
217     @Test
testCompareDataInRange_whenLengthZero()218     public void testCompareDataInRange_whenLengthZero() {
219         Parcel pA = Parcel.obtain();
220         int iA = pA.dataPosition();
221         pA.writeInt(12);
222         pA.writeString("Tiramisu");
223         int length = pA.dataPosition() - iA;
224         Parcel pB = Parcel.obtain();
225         pB.writeString("Prefix");
226         int iB = pB.dataPosition();
227         pB.writeInt(13);
228         pB.writeString("Tiramisu");
229 
230         assertTrue(Parcel.compareData(pA, 0, pB, iB, 0));
231         assertTrue(Parcel.compareData(pA, iA + length, pB, iB, 0));
232         assertTrue(Parcel.compareData(pA, iA, pB, pB.dataSize(), 0));
233     }
234 
235     @Test
testCompareDataInRange_whenNegativeLength_throws()236     public void testCompareDataInRange_whenNegativeLength_throws() {
237         Parcel pA = Parcel.obtain();
238         int iA = pA.dataPosition();
239         pA.writeInt(12);
240         pA.writeString("Tiramisu");
241         Parcel pB = Parcel.obtain();
242         pB.writeString("Prefix");
243         int iB = pB.dataPosition();
244         pB.writeInt(13);
245         pB.writeString("Tiramisu");
246 
247         assertThrows(IllegalArgumentException.class, () -> Parcel.compareData(pA, iA, pB, iB, -1));
248     }
249 
250     @Test
testCompareDataInRange_whenNegativeOffset_throws()251     public void testCompareDataInRange_whenNegativeOffset_throws() {
252         Parcel pA = Parcel.obtain();
253         int iA = pA.dataPosition();
254         pA.writeInt(12);
255         pA.writeString("Tiramisu");
256         Parcel pB = Parcel.obtain();
257         pB.writeString("Prefix");
258         int iB = pB.dataPosition();
259         pB.writeInt(13);
260         pB.writeString("Tiramisu");
261 
262         assertThrows(IllegalArgumentException.class, () -> Parcel.compareData(pA, -1, pB, iB, 0));
263         assertThrows(IllegalArgumentException.class, () -> Parcel.compareData(pA, 0, pB, -1, 0));
264     }
265 
266     /***
267      * Tests for b/205282403
268      * This test checks if allocations made over limit of 1MB for primitive types
269      * and 1M length for complex objects are not allowed.
270      */
271     @Test
testAllocationsOverLimit_whenOverLimit_throws()272     public void testAllocationsOverLimit_whenOverLimit_throws() {
273         Binder.setIsDirectlyHandlingTransactionOverride(true);
274         Parcel p = Parcel.obtain();
275         p.setDataPosition(0);
276         p.writeInt(Integer.MAX_VALUE);
277 
278         p.setDataPosition(0);
279         assertThrows(BadParcelableException.class, () ->p.createBooleanArray());
280 
281         p.setDataPosition(0);
282         assertThrows(BadParcelableException.class, () ->p.createCharArray());
283 
284         p.setDataPosition(0);
285         assertThrows(BadParcelableException.class, () ->p.createIntArray());
286 
287         p.setDataPosition(0);
288         assertThrows(BadParcelableException.class, () ->p.createLongArray());
289 
290         p.setDataPosition(0);
291         assertThrows(BadParcelableException.class, () ->p.createBinderArray());
292 
293         int[] dimensions = new int[]{Integer.MAX_VALUE, 100, 100};
294         p.setDataPosition(0);
295         assertThrows(BadParcelableException.class,
296                 () -> p.createFixedArray(int[][][].class, dimensions));
297 
298         p.setDataPosition(0);
299         assertThrows(BadParcelableException.class,
300                 () -> p.createFixedArray(String[][][].class, dimensions));
301 
302         p.setDataPosition(0);
303         assertThrows(BadParcelableException.class,
304                 () -> p.createFixedArray(IBinder[][][].class, dimensions));
305 
306         p.recycle();
307         Binder.setIsDirectlyHandlingTransactionOverride(false);
308     }
309 
310     /***
311      * Tests for b/205282403
312      * This test checks if allocations made under limit of 1MB for primitive types
313      * and 1M length for complex objects are allowed.
314      */
315     @Test
316     @IgnoreUnderRavenwood(blockedBy = Parcel.class)
testAllocations_whenWithinLimit()317     public void testAllocations_whenWithinLimit() {
318         Binder.setIsDirectlyHandlingTransactionOverride(true);
319         Parcel p = Parcel.obtain();
320         p.setDataPosition(0);
321         p.writeInt(100000);
322 
323         p.setDataPosition(0);
324         p.createByteArray();
325 
326         p.setDataPosition(0);
327         p.createCharArray();
328 
329         p.setDataPosition(0);
330         p.createIntArray();
331 
332         p.setDataPosition(0);
333         p.createLongArray();
334 
335         p.setDataPosition(0);
336         p.createBinderArray();
337 
338         int[] dimensions = new int[]{ 100, 100, 100 };
339 
340         p.setDataPosition(0);
341         int[][][] data  =  new int[100][100][100];
342         p.writeFixedArray(data, 0, dimensions);
343         p.setDataPosition(0);
344         p.createFixedArray(int[][][].class, dimensions);
345 
346         p.setDataPosition(0);
347         IBinder[][][] parcelables  =  new IBinder[100][100][100];
348         p.writeFixedArray(parcelables, 0, dimensions);
349         p.setDataPosition(0);
350         p.createFixedArray(IBinder[][][].class, dimensions);
351 
352         p.recycle();
353         Binder.setIsDirectlyHandlingTransactionOverride(false);
354     }
355 
356     @Test
testClassCookies()357     public void testClassCookies() {
358         Parcel p = Parcel.obtain();
359         assertThat(p.hasClassCookie(ParcelTest.class)).isFalse();
360 
361         p.setClassCookie(ParcelTest.class, "string_cookie");
362         assertThat(p.hasClassCookie(ParcelTest.class)).isTrue();
363         assertThat(p.getClassCookie(ParcelTest.class)).isEqualTo("string_cookie");
364 
365         p.removeClassCookie(ParcelTest.class, "string_cookie");
366         assertThat(p.hasClassCookie(ParcelTest.class)).isFalse();
367         assertThat(p.getClassCookie(ParcelTest.class)).isEqualTo(null);
368 
369         p.setClassCookie(ParcelTest.class, "to_be_discarded_cookie");
370         p.recycle();
371         assertThat(p.getClassCookie(ParcelTest.class)).isNull();
372     }
373 
374     @Test
testClassCookies_removeUnexpected()375     public void testClassCookies_removeUnexpected() {
376         Parcel p = Parcel.obtain();
377 
378         assertLogsWtf(() -> p.removeClassCookie(ParcelTest.class, "not_present"));
379 
380         p.setClassCookie(ParcelTest.class, "value");
381 
382         assertLogsWtf(() -> p.removeClassCookie(ParcelTest.class, "different"));
383         assertThat(p.getClassCookie(ParcelTest.class)).isNull(); // still removed
384 
385         p.recycle();
386     }
387 
assertLogsWtf(Runnable test)388     private static void assertLogsWtf(Runnable test) {
389         ArrayList<Log.TerribleFailure> wtfs = new ArrayList<>();
390         Log.TerribleFailureHandler oldHandler = Log.setWtfHandler(
391                 (tag, what, system) -> wtfs.add(what));
392         try {
393             test.run();
394         } finally {
395             Log.setWtfHandler(oldHandler);
396         }
397         assertThat(wtfs).hasSize(1);
398     }
399 
400     @Test
401     @IgnoreUnderRavenwood(blockedBy = Parcel.class)
testHasBinders_AfterWritingBinderToParcel()402     public void testHasBinders_AfterWritingBinderToParcel() {
403         Binder binder = new Binder();
404         Parcel pA = Parcel.obtain();
405         int iA = pA.dataPosition();
406         pA.writeInt(13);
407         assertFalse(pA.hasBinders());
408         pA.writeStrongBinder(binder);
409         assertTrue(pA.hasBinders());
410     }
411 
412     @Test
413     @IgnoreUnderRavenwood(blockedBy = Parcel.class)
testHasBindersInRange_AfterWritingBinderToParcel()414     public void testHasBindersInRange_AfterWritingBinderToParcel() {
415         Binder binder = new Binder();
416         Parcel pA = Parcel.obtain();
417         pA.writeInt(13);
418 
419         int binderStartPos = pA.dataPosition();
420         pA.writeStrongBinder(binder);
421         int binderEndPos = pA.dataPosition();
422         assertTrue(pA.hasBinders(binderStartPos, binderEndPos - binderStartPos));
423     }
424 }
425