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