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