1 /* 2 * Copyright 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 androidx.versionedparcelable; 18 19 import android.os.Bundle; 20 import android.os.IBinder; 21 import android.os.IInterface; 22 import android.os.Parcelable; 23 24 import androidx.annotation.RestrictTo; 25 import androidx.collection.SimpleArrayMap; 26 27 import org.jspecify.annotations.NonNull; 28 import org.jspecify.annotations.Nullable; 29 30 import java.io.ByteArrayOutputStream; 31 import java.io.DataInputStream; 32 import java.io.DataOutputStream; 33 import java.io.FilterInputStream; 34 import java.io.IOException; 35 import java.io.InputStream; 36 import java.io.OutputStream; 37 import java.lang.reflect.Method; 38 import java.nio.charset.Charset; 39 import java.util.Set; 40 41 /** 42 */ 43 @RestrictTo(RestrictTo.Scope.LIBRARY) 44 class VersionedParcelStream extends VersionedParcel { 45 46 private static final @NonNull Charset UTF_16 = Charset.forName("UTF-16"); 47 48 // Supported types held inside a bundle. These cannot be added to or changed once shipped. 49 private static final int TYPE_NULL = 0; 50 private static final int TYPE_SUB_BUNDLE = 1; 51 private static final int TYPE_SUB_PERSISTABLE_BUNDLE = 2; 52 private static final int TYPE_STRING = 3; 53 private static final int TYPE_STRING_ARRAY = 4; 54 private static final int TYPE_BOOLEAN = 5; 55 private static final int TYPE_BOOLEAN_ARRAY = 6; 56 private static final int TYPE_DOUBLE = 7; 57 private static final int TYPE_DOUBLE_ARRAY = 8; 58 private static final int TYPE_INT = 9; 59 private static final int TYPE_INT_ARRAY = 10; 60 private static final int TYPE_LONG = 11; 61 private static final int TYPE_LONG_ARRAY = 12; 62 private static final int TYPE_FLOAT = 13; 63 private static final int TYPE_FLOAT_ARRAY = 14; 64 65 private final @Nullable DataInputStream mMasterInput; 66 private final @Nullable DataOutputStream mMasterOutput; 67 68 private @Nullable DataInputStream mCurrentInput; 69 private @Nullable DataOutputStream mCurrentOutput; 70 private @Nullable FieldBuffer mFieldBuffer; 71 private boolean mIgnoreParcelables; 72 73 int mCount = 0; 74 private int mFieldId = -1; 75 int mFieldSize = -1; 76 VersionedParcelStream(@ullable InputStream input, @Nullable OutputStream output)77 VersionedParcelStream(@Nullable InputStream input, @Nullable OutputStream output) { 78 this(input, output, new SimpleArrayMap<String, Method>(), 79 new SimpleArrayMap<String, Method>(), new SimpleArrayMap<String, Class<?>>()); 80 } 81 VersionedParcelStream( @ullable InputStream input, @Nullable OutputStream output, @NonNull SimpleArrayMap<String, Method> readCache, @NonNull SimpleArrayMap<String, Method> writeCache, @NonNull SimpleArrayMap<String, Class<?>> parcelizerCache )82 private VersionedParcelStream( 83 @Nullable InputStream input, 84 @Nullable OutputStream output, 85 @NonNull SimpleArrayMap<String, Method> readCache, 86 @NonNull SimpleArrayMap<String, Method> writeCache, 87 @NonNull SimpleArrayMap<String, Class<?>> parcelizerCache 88 ) { 89 super(readCache, writeCache, parcelizerCache); 90 mMasterInput = input != null ? new DataInputStream(new FilterInputStream(input) { 91 @Override 92 public int read() throws IOException { 93 if (mFieldSize != -1 && mCount >= mFieldSize) { 94 throw new IOException(); 95 } 96 int read = super.read(); 97 mCount += 1; 98 return read; 99 } 100 101 @Override 102 public int read(byte[] b, int off, int len) throws IOException { 103 if (mFieldSize != -1 && mCount >= mFieldSize) { 104 throw new IOException(); 105 } 106 int read = super.read(b, off, len); 107 if (read > 0) { 108 mCount += read; 109 } 110 return read; 111 } 112 113 @Override 114 public long skip(long n) throws IOException { 115 if (mFieldSize != -1 && mCount >= mFieldSize) { 116 throw new IOException(); 117 } 118 long skip = super.skip(n); 119 if (skip > 0) { 120 mCount += (int) skip; 121 } 122 return skip; 123 } 124 }) : null; 125 mMasterOutput = output != null ? new DataOutputStream(output) : null; 126 mCurrentInput = mMasterInput; 127 mCurrentOutput = mMasterOutput; 128 } 129 130 @Override isStream()131 public boolean isStream() { 132 return true; 133 } 134 135 /** 136 */ 137 @Override setSerializationFlags(boolean allowSerialization, boolean ignoreParcelables)138 public void setSerializationFlags(boolean allowSerialization, boolean ignoreParcelables) { 139 if (!allowSerialization) { 140 throw new RuntimeException("Serialization of this object is not allowed"); 141 } 142 mIgnoreParcelables = ignoreParcelables; 143 } 144 145 @Override closeField()146 public void closeField() { 147 if (mFieldBuffer != null) { 148 try { 149 if (mFieldBuffer.mOutput.size() != 0) { 150 mFieldBuffer.flushField(); 151 } 152 } catch (IOException e) { 153 throw new ParcelException(e); 154 } 155 mFieldBuffer = null; 156 } 157 } 158 159 @Override createSubParcel()160 protected @NonNull VersionedParcel createSubParcel() { 161 return new VersionedParcelStream(mCurrentInput, mCurrentOutput, mReadCache, mWriteCache, 162 mParcelizerCache); 163 } 164 165 @Override readField(int fieldId)166 public boolean readField(int fieldId) { 167 try { 168 while (true) { 169 if (mFieldId == fieldId) { 170 return true; 171 } 172 if (String.valueOf(mFieldId).compareTo(String.valueOf(fieldId)) > 0) { 173 return false; 174 } 175 if (mCount < mFieldSize) { 176 mMasterInput.skip(mFieldSize - mCount); 177 } 178 mFieldSize = -1; 179 int fieldInfo = mMasterInput.readInt(); 180 mCount = 0; 181 int size = fieldInfo & 0xffff; 182 if (size == 0xffff) { 183 size = mMasterInput.readInt(); 184 } 185 int id = (fieldInfo >> 16) & 0xffff; 186 mFieldId = id; 187 mFieldSize = size; 188 } 189 } catch (IOException e) { 190 } 191 return false; 192 } 193 194 @Override setOutputField(int fieldId)195 public void setOutputField(int fieldId) { 196 closeField(); 197 mFieldBuffer = new FieldBuffer(fieldId, mMasterOutput); 198 mCurrentOutput = mFieldBuffer.mDataStream; 199 } 200 201 @Override writeByteArray(byte @Nullable [] b)202 public void writeByteArray(byte @Nullable [] b) { 203 try { 204 if (b != null) { 205 mCurrentOutput.writeInt(b.length); 206 mCurrentOutput.write(b); 207 } else { 208 mCurrentOutput.writeInt(-1); 209 } 210 } catch (IOException e) { 211 throw new ParcelException(e); 212 } 213 } 214 215 @Override writeByteArray(byte @Nullable [] b, int offset, int len)216 public void writeByteArray(byte @Nullable [] b, int offset, int len) { 217 try { 218 if (b != null) { 219 mCurrentOutput.writeInt(len); 220 mCurrentOutput.write(b, offset, len); 221 } else { 222 mCurrentOutput.writeInt(-1); 223 } 224 } catch (IOException e) { 225 throw new ParcelException(e); 226 } 227 } 228 229 @Override writeCharSequence(@ullable CharSequence charSequence)230 protected void writeCharSequence(@Nullable CharSequence charSequence) { 231 if (!mIgnoreParcelables) { 232 throw new RuntimeException("CharSequence cannot be written to an OutputStream"); 233 } 234 } 235 236 @Override writeInt(int val)237 public void writeInt(int val) { 238 try { 239 mCurrentOutput.writeInt(val); 240 } catch (IOException e) { 241 throw new ParcelException(e); 242 } 243 } 244 245 @Override writeLong(long val)246 public void writeLong(long val) { 247 try { 248 mCurrentOutput.writeLong(val); 249 } catch (IOException e) { 250 throw new ParcelException(e); 251 } 252 253 } 254 255 @Override writeFloat(float val)256 public void writeFloat(float val) { 257 try { 258 mCurrentOutput.writeFloat(val); 259 } catch (IOException e) { 260 throw new ParcelException(e); 261 } 262 263 } 264 265 @Override writeDouble(double val)266 public void writeDouble(double val) { 267 try { 268 mCurrentOutput.writeDouble(val); 269 } catch (IOException e) { 270 throw new ParcelException(e); 271 } 272 273 } 274 275 @Override writeString(@ullable String val)276 public void writeString(@Nullable String val) { 277 try { 278 if (val != null) { 279 byte[] bytes = val.getBytes(UTF_16); 280 mCurrentOutput.writeInt(bytes.length); 281 mCurrentOutput.write(bytes); 282 } else { 283 mCurrentOutput.writeInt(-1); 284 } 285 } catch (IOException e) { 286 throw new ParcelException(e); 287 } 288 } 289 290 @Override writeBoolean(boolean val)291 public void writeBoolean(boolean val) { 292 try { 293 mCurrentOutput.writeBoolean(val); 294 } catch (IOException e) { 295 throw new ParcelException(e); 296 } 297 } 298 299 @Override writeStrongBinder(@ullable IBinder val)300 public void writeStrongBinder(@Nullable IBinder val) { 301 if (!mIgnoreParcelables) { 302 throw new RuntimeException("Binders cannot be written to an OutputStream"); 303 } 304 } 305 306 @Override writeParcelable(@ullable Parcelable p)307 public void writeParcelable(@Nullable Parcelable p) { 308 if (!mIgnoreParcelables) { 309 throw new RuntimeException("Parcelables cannot be written to an OutputStream"); 310 } 311 } 312 313 @Override writeStrongInterface(@ullable IInterface val)314 public void writeStrongInterface(@Nullable IInterface val) { 315 if (!mIgnoreParcelables) { 316 throw new RuntimeException("Binders cannot be written to an OutputStream"); 317 } 318 } 319 320 @Override readStrongBinder()321 public @Nullable IBinder readStrongBinder() { 322 return null; 323 } 324 325 @Override 326 @SuppressWarnings("TypeParameterUnusedInFormals") readParcelable()327 public <T extends Parcelable> @Nullable T readParcelable() { 328 return null; 329 } 330 331 @Override readInt()332 public int readInt() { 333 try { 334 return mCurrentInput.readInt(); 335 } catch (IOException e) { 336 throw new ParcelException(e); 337 } 338 } 339 340 @Override readLong()341 public long readLong() { 342 try { 343 return mCurrentInput.readLong(); 344 } catch (IOException e) { 345 throw new ParcelException(e); 346 } 347 } 348 349 @Override readFloat()350 public float readFloat() { 351 try { 352 return mCurrentInput.readFloat(); 353 } catch (IOException e) { 354 throw new ParcelException(e); 355 } 356 } 357 358 @Override readDouble()359 public double readDouble() { 360 try { 361 return mCurrentInput.readDouble(); 362 } catch (IOException e) { 363 throw new ParcelException(e); 364 } 365 } 366 367 @Override readString()368 public @Nullable String readString() { 369 try { 370 int len = mCurrentInput.readInt(); 371 if (len > 0) { 372 byte[] bytes = new byte[len]; 373 mCurrentInput.readFully(bytes); 374 return new String(bytes, UTF_16); 375 } else { 376 return null; 377 } 378 } catch (IOException e) { 379 throw new ParcelException(e); 380 } 381 } 382 383 @Override readByteArray()384 public byte @Nullable [] readByteArray() { 385 try { 386 int len = mCurrentInput.readInt(); 387 if (len > 0) { 388 byte[] bytes = new byte[len]; 389 mCurrentInput.readFully(bytes); 390 return bytes; 391 } else { 392 return null; 393 } 394 } catch (IOException e) { 395 throw new ParcelException(e); 396 } 397 } 398 399 @Override readCharSequence()400 protected @Nullable CharSequence readCharSequence() { 401 return null; 402 } 403 404 @Override readBoolean()405 public boolean readBoolean() { 406 try { 407 return mCurrentInput.readBoolean(); 408 } catch (IOException e) { 409 throw new ParcelException(e); 410 } 411 } 412 413 @Override 414 @SuppressWarnings("deprecation") writeBundle(Bundle val)415 public void writeBundle(Bundle val) { 416 try { 417 if (val != null) { 418 Set<String> keys = val.keySet(); 419 mCurrentOutput.writeInt(keys.size()); 420 for (String key : keys) { 421 writeString(key); 422 Object o = val.get(key); 423 writeObject(o); 424 } 425 } else { 426 mCurrentOutput.writeInt(-1); 427 } 428 } catch (IOException e) { 429 throw new ParcelException(e); 430 } 431 } 432 433 @Override readBundle()434 public @Nullable Bundle readBundle() { 435 int size = readInt(); 436 if (size < 0) { 437 return null; 438 } 439 Bundle b = new Bundle(); 440 for (int i = 0; i < size; i++) { 441 String key = readString(); 442 readObject(readInt(), key, b); 443 } 444 return b; 445 } 446 writeObject(@ullable Object o)447 private void writeObject(@Nullable Object o) { 448 if (o == null) { 449 writeInt(TYPE_NULL); 450 } else if (o instanceof Bundle) { 451 writeInt(TYPE_SUB_BUNDLE); 452 writeBundle((Bundle) o); 453 } else if (o instanceof String) { 454 writeInt(TYPE_STRING); 455 writeString((String) o); 456 } else if (o instanceof String[]) { 457 writeInt(TYPE_STRING_ARRAY); 458 writeArray((String[]) o); 459 } else if (o instanceof Boolean) { 460 writeInt(TYPE_BOOLEAN); 461 writeBoolean((Boolean) o); 462 } else if (o instanceof boolean[]) { 463 writeInt(TYPE_BOOLEAN_ARRAY); 464 writeBooleanArray((boolean[]) o); 465 } else if (o instanceof Double) { 466 writeInt(TYPE_DOUBLE); 467 writeDouble((Double) o); 468 } else if (o instanceof double[]) { 469 writeInt(TYPE_DOUBLE_ARRAY); 470 writeDoubleArray((double[]) o); 471 } else if (o instanceof Integer) { 472 writeInt(TYPE_INT); 473 writeInt((Integer) o); 474 } else if (o instanceof int[]) { 475 writeInt(TYPE_INT_ARRAY); 476 writeIntArray((int[]) o); 477 } else if (o instanceof Long) { 478 writeInt(TYPE_LONG); 479 writeLong((Long) o); 480 } else if (o instanceof long[]) { 481 writeInt(TYPE_LONG_ARRAY); 482 writeLongArray((long[]) o); 483 } else if (o instanceof Float) { 484 writeInt(TYPE_FLOAT); 485 writeFloat((Float) o); 486 } else if (o instanceof float[]) { 487 writeInt(TYPE_FLOAT_ARRAY); 488 writeFloatArray((float[]) o); 489 } else { 490 throw new IllegalArgumentException("Unsupported type " + o.getClass()); 491 } 492 } 493 readObject(int type, @Nullable String key, @NonNull Bundle b)494 private void readObject(int type, @Nullable String key, @NonNull Bundle b) { 495 switch (type) { 496 case TYPE_NULL: 497 b.putParcelable(key, null); 498 break; 499 case TYPE_SUB_BUNDLE: 500 b.putBundle(key, readBundle()); 501 break; 502 case TYPE_SUB_PERSISTABLE_BUNDLE: 503 b.putBundle(key, readBundle()); 504 break; 505 case TYPE_STRING: 506 b.putString(key, readString()); 507 break; 508 case TYPE_STRING_ARRAY: 509 b.putStringArray(key, readArray(new String[0])); 510 break; 511 case TYPE_BOOLEAN: 512 b.putBoolean(key, readBoolean()); 513 break; 514 case TYPE_BOOLEAN_ARRAY: 515 b.putBooleanArray(key, readBooleanArray()); 516 break; 517 case TYPE_DOUBLE: 518 b.putDouble(key, readDouble()); 519 break; 520 case TYPE_DOUBLE_ARRAY: 521 b.putDoubleArray(key, readDoubleArray()); 522 break; 523 case TYPE_INT: 524 b.putInt(key, readInt()); 525 break; 526 case TYPE_INT_ARRAY: 527 b.putIntArray(key, readIntArray()); 528 break; 529 case TYPE_LONG: 530 b.putLong(key, readLong()); 531 break; 532 case TYPE_LONG_ARRAY: 533 b.putLongArray(key, readLongArray()); 534 break; 535 case TYPE_FLOAT: 536 b.putFloat(key, readFloat()); 537 break; 538 case TYPE_FLOAT_ARRAY: 539 b.putFloatArray(key, readFloatArray()); 540 break; 541 default: 542 throw new RuntimeException("Unknown type " + type); 543 } 544 } 545 546 // This uses extra buffers at the moment, but makes the code really clean. 547 // TODO: Use less buffers 548 private static class FieldBuffer { 549 550 final @NonNull ByteArrayOutputStream mOutput = new ByteArrayOutputStream(); 551 final @NonNull DataOutputStream mDataStream = new DataOutputStream(mOutput); 552 private final int mFieldId; 553 private final @NonNull DataOutputStream mTarget; 554 FieldBuffer(int fieldId, @NonNull DataOutputStream target)555 FieldBuffer(int fieldId, @NonNull DataOutputStream target) { 556 mFieldId = fieldId; 557 mTarget = target; 558 } 559 flushField()560 void flushField() throws IOException { 561 mDataStream.flush(); 562 int size = mOutput.size(); 563 int fieldInfo = (mFieldId << 16) | (size >= 0xffff ? 0xffff : size); 564 mTarget.writeInt(fieldInfo); 565 if (size >= 0xffff) { 566 mTarget.writeInt(size); 567 } 568 mOutput.writeTo(mTarget); 569 } 570 } 571 572 } 573