1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package java.io; 19 20 import java.lang.reflect.InvocationTargetException; 21 import java.lang.reflect.Method; 22 import java.lang.reflect.Proxy; 23 import java.util.IdentityHashMap; 24 25 26 // BEGIN android-note 27 // Harmony uses ObjectAccessors to access fields through JNI. Android has not 28 // yet migrated that API. As a consequence, there's a lot of changes here... 29 // END android-note 30 31 /** 32 * A specialized {@link OutputStream} that is able to write (serialize) Java 33 * objects as well as primitive data types (int, byte, char etc.). The data can 34 * later be loaded using an ObjectInputStream. 35 * 36 * @see ObjectInputStream 37 * @see ObjectOutput 38 * @see Serializable 39 * @see Externalizable 40 */ 41 public class ObjectOutputStream extends OutputStream implements ObjectOutput, 42 ObjectStreamConstants { 43 44 /* 45 * Mask to zero SC_BLOC_DATA bit. 46 */ 47 private static final byte NOT_SC_BLOCK_DATA = (byte) (SC_BLOCK_DATA ^ 0xFF); 48 49 /* 50 * How many nested levels to writeObject. We may not need this. 51 */ 52 private int nestedLevels; 53 54 /* 55 * Where we write to 56 */ 57 private DataOutputStream output; 58 59 /* 60 * If object replacement is enabled or not 61 */ 62 private boolean enableReplace; 63 64 /* 65 * Where we write primitive types to 66 */ 67 private DataOutputStream primitiveTypes; 68 69 /* 70 * Where the write primitive types are actually written to 71 */ 72 private ByteArrayOutputStream primitiveTypesBuffer; 73 74 /* 75 * Table mapping Object -> Integer (handle) 76 */ 77 private IdentityHashMap<Object, Integer> objectsWritten; 78 79 /* 80 * All objects are assigned an ID (integer handle) 81 */ 82 private int currentHandle; 83 84 /* 85 * Used by defaultWriteObject 86 */ 87 private Object currentObject; 88 89 /* 90 * Used by defaultWriteObject 91 */ 92 private ObjectStreamClass currentClass; 93 94 /* 95 * Either ObjectStreamConstants.PROTOCOL_VERSION_1 or 96 * ObjectStreamConstants.PROTOCOL_VERSION_2 97 */ 98 private int protocolVersion; 99 100 /* 101 * Used to detect nested exception when saving an exception due to an error 102 */ 103 private StreamCorruptedException nestedException; 104 105 /* 106 * Used to keep track of the PutField object for the class/object being 107 * written 108 */ 109 private EmulatedFieldsForDumping currentPutField; 110 111 /* 112 * Allows the receiver to decide if it needs to call writeObjectOverride 113 */ 114 private boolean subclassOverridingImplementation; 115 116 117 // BEGIN android-removed 118 // private ObjectAccessor accessor = AccessorFactory.getObjectAccessor(); 119 // END android-removed 120 121 /* 122 * Descriptor for java.lang.reflect.Proxy 123 */ 124 private final ObjectStreamClass proxyClassDesc = ObjectStreamClass.lookup(Proxy.class); 125 126 /** 127 * PutField is an inner class to provide access to the persistent fields 128 * that are written to the target stream. 129 */ 130 public static abstract class PutField { 131 /** 132 * Puts the value of the boolean field identified by {@code name} to the 133 * persistent field. 134 * 135 * @param name 136 * the name of the field to serialize. 137 * @param value 138 * the value that is put to the persistent field. 139 */ put(String name, boolean value)140 public abstract void put(String name, boolean value); 141 142 /** 143 * Puts the value of the character field identified by {@code name} to 144 * the persistent field. 145 * 146 * @param name 147 * the name of the field to serialize. 148 * @param value 149 * the value that is put to the persistent field. 150 */ put(String name, char value)151 public abstract void put(String name, char value); 152 153 /** 154 * Puts the value of the byte field identified by {@code name} to the 155 * persistent field. 156 * 157 * @param name 158 * the name of the field to serialize. 159 * @param value 160 * the value that is put to the persistent field. 161 */ put(String name, byte value)162 public abstract void put(String name, byte value); 163 164 /** 165 * Puts the value of the short field identified by {@code name} to the 166 * persistent field. 167 * 168 * @param name 169 * the name of the field to serialize. 170 * @param value 171 * the value that is put to the persistent field. 172 */ put(String name, short value)173 public abstract void put(String name, short value); 174 175 /** 176 * Puts the value of the integer field identified by {@code name} to the 177 * persistent field. 178 * 179 * @param name 180 * the name of the field to serialize. 181 * @param value 182 * the value that is put to the persistent field. 183 */ put(String name, int value)184 public abstract void put(String name, int value); 185 186 /** 187 * Puts the value of the long field identified by {@code name} to the 188 * persistent field. 189 * 190 * @param name 191 * the name of the field to serialize. 192 * @param value 193 * the value that is put to the persistent field. 194 */ put(String name, long value)195 public abstract void put(String name, long value); 196 197 /** 198 * Puts the value of the float field identified by {@code name} to the 199 * persistent field. 200 * 201 * @param name 202 * the name of the field to serialize. 203 * @param value 204 * the value that is put to the persistent field. 205 */ put(String name, float value)206 public abstract void put(String name, float value); 207 208 /** 209 * Puts the value of the double field identified by {@code name} to the 210 * persistent field. 211 * 212 * @param name 213 * the name of the field to serialize. 214 * @param value 215 * the value that is put to the persistent field. 216 */ put(String name, double value)217 public abstract void put(String name, double value); 218 219 /** 220 * Puts the value of the Object field identified by {@code name} to the 221 * persistent field. 222 * 223 * @param name 224 * the name of the field to serialize. 225 * @param value 226 * the value that is put to the persistent field. 227 */ put(String name, Object value)228 public abstract void put(String name, Object value); 229 230 /** 231 * Writes the fields to the target stream {@code out}. 232 * 233 * @param out 234 * the target stream 235 * @throws IOException 236 * if an error occurs while writing to the target stream. 237 * @deprecated This method is unsafe and may corrupt the target stream. 238 * Use ObjectOutputStream#writeFields() instead. 239 */ 240 @Deprecated write(ObjectOutput out)241 public abstract void write(ObjectOutput out) throws IOException; 242 } 243 244 /** 245 * Constructs a new {@code ObjectOutputStream}. This default constructor can 246 * be used by subclasses that do not want to use the public constructor if 247 * it allocates unneeded data. 248 * 249 * @throws IOException 250 * if an error occurs when creating this stream. 251 * @throws SecurityException 252 * if a security manager is installed and it denies subclassing 253 * this class. 254 * @see SecurityManager#checkPermission(java.security.Permission) 255 */ ObjectOutputStream()256 protected ObjectOutputStream() throws IOException, SecurityException { 257 super(); 258 SecurityManager currentManager = System.getSecurityManager(); 259 if (currentManager != null) { 260 currentManager.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); 261 } 262 /* 263 * WARNING - we should throw IOException if not called from a subclass 264 * according to the JavaDoc. Add the test. 265 */ 266 this.subclassOverridingImplementation = true; 267 } 268 269 /** 270 * Constructs a new ObjectOutputStream that writes to the OutputStream 271 * {@code output}. 272 * 273 * @param output 274 * the non-null OutputStream to filter writes on. 275 * 276 * @throws IOException 277 * if an error occurs while writing the object stream 278 * header 279 * @throws SecurityException 280 * if a security manager is installed and it denies subclassing 281 * this class. 282 */ ObjectOutputStream(OutputStream output)283 public ObjectOutputStream(OutputStream output) throws IOException { 284 Class<?> implementationClass = getClass(); 285 Class<?> thisClass = ObjectOutputStream.class; 286 if (implementationClass != thisClass) { 287 boolean mustCheck = false; 288 try { 289 Method method = implementationClass.getMethod("putFields", 290 ObjectStreamClass.EMPTY_CONSTRUCTOR_PARAM_TYPES); 291 mustCheck = method.getDeclaringClass() != thisClass; 292 } catch (NoSuchMethodException e) { 293 } 294 if (!mustCheck) { 295 try { 296 Method method = implementationClass.getMethod( 297 "writeUnshared", 298 ObjectStreamClass.UNSHARED_PARAM_TYPES); 299 mustCheck = method.getDeclaringClass() != thisClass; 300 } catch (NoSuchMethodException e) { 301 } 302 } 303 if (mustCheck) { 304 SecurityManager sm = System.getSecurityManager(); 305 if (sm != null) { 306 sm 307 .checkPermission(ObjectStreamConstants.SUBCLASS_IMPLEMENTATION_PERMISSION); 308 } 309 } 310 } 311 this.output = (output instanceof DataOutputStream) ? (DataOutputStream) output 312 : new DataOutputStream(output); 313 this.enableReplace = false; 314 this.protocolVersion = PROTOCOL_VERSION_2; 315 this.subclassOverridingImplementation = false; 316 317 resetState(); 318 this.nestedException = new StreamCorruptedException(); 319 // So write...() methods can be used by 320 // subclasses during writeStreamHeader() 321 primitiveTypes = this.output; 322 // Has to be done here according to the specification 323 writeStreamHeader(); 324 primitiveTypes = null; 325 } 326 327 /** 328 * Writes optional information for class {@code aClass} to the output 329 * stream. This optional data can be read when deserializing the class 330 * descriptor (ObjectStreamClass) for this class from an input stream. By 331 * default, no extra data is saved. 332 * 333 * @param aClass 334 * the class to annotate. 335 * @throws IOException 336 * if an error occurs while writing to the target stream. 337 * @see ObjectInputStream#resolveClass(ObjectStreamClass) 338 */ annotateClass(Class<?> aClass)339 protected void annotateClass(Class<?> aClass) throws IOException { 340 // By default no extra info is saved. Subclasses can override 341 } 342 343 /** 344 * Writes optional information for a proxy class to the target stream. This 345 * optional data can be read when deserializing the proxy class from an 346 * input stream. By default, no extra data is saved. 347 * 348 * @param aClass 349 * the proxy class to annotate. 350 * @throws IOException 351 * if an error occurs while writing to the target stream. 352 * @see ObjectInputStream#resolveProxyClass(String[]) 353 */ annotateProxyClass(Class<?> aClass)354 protected void annotateProxyClass(Class<?> aClass) throws IOException { 355 // By default no extra info is saved. Subclasses can override 356 } 357 358 /** 359 * Do the necessary work to see if the receiver can be used to write 360 * primitive types like int, char, etc. 361 */ checkWritePrimitiveTypes()362 private void checkWritePrimitiveTypes() { 363 if (primitiveTypes == null) { 364 // If we got here we have no Stream previously created 365 // WARNING - if the stream does not grow, this code is wrong 366 primitiveTypesBuffer = new ByteArrayOutputStream(128); 367 primitiveTypes = new DataOutputStream(primitiveTypesBuffer); 368 } 369 } 370 371 /** 372 * Closes this stream. Any buffered data is flushed. This implementation 373 * closes the target stream. 374 * 375 * @throws IOException 376 * if an error occurs while closing this stream. 377 */ 378 @Override close()379 public void close() throws IOException { 380 // First flush what is needed (primitive data, etc) 381 flush(); 382 output.close(); 383 } 384 385 /** 386 * Computes the collection of emulated fields that users can manipulate to 387 * store a representation different than the one declared by the class of 388 * the object being dumped. 389 * 390 * @see #writeFields 391 * @see #writeFieldValues(EmulatedFieldsForDumping) 392 */ computePutField()393 private void computePutField() { 394 currentPutField = new EmulatedFieldsForDumping(currentClass); 395 } 396 397 /** 398 * Default method to write objects to this stream. Serializable fields 399 * defined in the object's class and superclasses are written to the output 400 * stream. 401 * 402 * @throws IOException 403 * if an error occurs while writing to the target stream. 404 * @throws NotActiveException 405 * if this method is not called from {@code writeObject()}. 406 * @see ObjectInputStream#defaultReadObject 407 */ defaultWriteObject()408 public void defaultWriteObject() throws IOException { 409 // We can't be called from just anywhere. There are rules. 410 if (currentObject == null) { 411 throw new NotActiveException(); 412 } 413 writeFieldValues(currentObject, currentClass); 414 } 415 416 /** 417 * Writes buffered data to the target stream. This is similar to {@code 418 * flush} but the flush is not propagated to the target stream. 419 * 420 * @throws IOException 421 * if an error occurs while writing to the target stream. 422 */ drain()423 protected void drain() throws IOException { 424 if (primitiveTypes == null || primitiveTypesBuffer == null) { 425 return; 426 } 427 428 // If we got here we have a Stream previously created 429 int offset = 0; 430 byte[] written = primitiveTypesBuffer.toByteArray(); 431 // Normalize the primitive data 432 while (offset < written.length) { 433 int toWrite = written.length - offset > 1024 ? 1024 434 : written.length - offset; 435 if (toWrite < 256) { 436 output.writeByte(TC_BLOCKDATA); 437 output.writeByte((byte) toWrite); 438 } else { 439 output.writeByte(TC_BLOCKDATALONG); 440 output.writeInt(toWrite); 441 } 442 443 // write primitive types we had and the marker of end-of-buffer 444 output.write(written, offset, toWrite); 445 offset += toWrite; 446 } 447 448 // and now we're clean to a state where we can write an object 449 primitiveTypes = null; 450 primitiveTypesBuffer = null; 451 } 452 453 /** 454 * Dumps the parameter {@code obj} only if it is {@code null} 455 * or an object that has already been dumped previously. 456 * 457 * @param obj 458 * Object to check if an instance previously dumped by this 459 * stream. 460 * @return null if it is an instance which has not been dumped yet (and this 461 * method does nothing). Integer, if {@code obj} is an 462 * instance which has been dumped already. In this case this method 463 * saves the cyclic reference. 464 * 465 * @throws IOException 466 * If an error occurs attempting to save {@code null} or 467 * a cyclic reference. 468 */ dumpCycle(Object obj)469 private Integer dumpCycle(Object obj) throws IOException { 470 // If the object has been saved already, save its handle only 471 Integer handle = objectsWritten.get(obj); 472 if (handle != null) { 473 writeCyclicReference(handle); 474 return handle; 475 } 476 return null; 477 } 478 479 /** 480 * Enables object replacement for this stream. By default this is not 481 * enabled. Only trusted subclasses (loaded with system class loader) are 482 * allowed to change this status. 483 * 484 * @param enable 485 * {@code true} to enable object replacement; {@code false} to 486 * disable it. 487 * @return the previous setting. 488 * @throws SecurityException 489 * if a security manager is installed and it denies enabling 490 * object replacement for this stream. 491 * @see #replaceObject 492 * @see ObjectInputStream#enableResolveObject 493 */ enableReplaceObject(boolean enable)494 protected boolean enableReplaceObject(boolean enable) 495 throws SecurityException { 496 if (enable) { 497 // The Stream has to be trusted for this feature to be enabled. 498 // trusted means the stream's classloader has to be null 499 SecurityManager currentManager = System.getSecurityManager(); 500 if (currentManager != null) { 501 currentManager.checkPermission(SUBSTITUTION_PERMISSION); 502 } 503 } 504 boolean originalValue = enableReplace; 505 enableReplace = enable; 506 return originalValue; 507 } 508 509 /** 510 * Writes buffered data to the target stream and calls the {@code flush} 511 * method of the target stream. 512 * 513 * @throws IOException 514 * if an error occurs while writing to or flushing the output 515 * stream. 516 */ 517 @Override flush()518 public void flush() throws IOException { 519 drain(); 520 output.flush(); 521 } 522 523 // BEGIN android-added 524 /* 525 * These methods get the value of a field named fieldName of object 526 * instance. The field is declared by declaringClass. The field is the same 527 * type as the method return value. 528 * 529 * these methods could be implemented non-natively on top of 530 * java.lang.reflect at the expense of extra object creation 531 * (java.lang.reflect.Field). Otherwise Serialization could not fetch 532 * private fields, except by the use of a native method like this one. 533 * 534 * @throws NoSuchFieldError If the field does not exist. 535 */ 536 getFieldBool(Object instance, Class<?> declaringClass, String fieldName)537 private static native boolean getFieldBool(Object instance, 538 Class<?> declaringClass, String fieldName); 539 getFieldByte(Object instance, Class<?> declaringClass, String fieldName)540 private static native byte getFieldByte(Object instance, 541 Class<?> declaringClass, String fieldName); 542 getFieldChar(Object instance, Class<?> declaringClass, String fieldName)543 private static native char getFieldChar(Object instance, 544 Class<?> declaringClass, String fieldName); 545 getFieldDouble(Object instance, Class<?> declaringClass, String fieldName)546 private static native double getFieldDouble(Object instance, 547 Class<?> declaringClass, String fieldName); 548 getFieldFloat(Object instance, Class<?> declaringClass, String fieldName)549 private static native float getFieldFloat(Object instance, 550 Class<?> declaringClass, String fieldName); 551 getFieldInt(Object instance, Class<?> declaringClass, String fieldName)552 private static native int getFieldInt(Object instance, 553 Class<?> declaringClass, String fieldName); 554 getFieldLong(Object instance, Class<?> declaringClass, String fieldName)555 private static native long getFieldLong(Object instance, 556 Class<?> declaringClass, String fieldName); 557 getFieldObj(Object instance, Class<?> declaringClass, String fieldName, String fieldTypeName)558 private static native Object getFieldObj(Object instance, 559 Class<?> declaringClass, String fieldName, String fieldTypeName); 560 getFieldShort(Object instance, Class<?> declaringClass, String fieldName)561 private static native short getFieldShort(Object instance, 562 Class<?> declaringClass, String fieldName); 563 // END android-added 564 565 /** 566 * Return the next <code>Integer</code> handle to be used to indicate cyclic 567 * references being saved to the stream. 568 * 569 * @return the next handle to represent the next cyclic reference 570 */ nextHandle()571 private Integer nextHandle() { 572 return Integer.valueOf(this.currentHandle++); 573 } 574 575 /** 576 * Gets this stream's {@code PutField} object. This object provides access 577 * to the persistent fields that are eventually written to the output 578 * stream. It is used to transfer the values from the fields of the object 579 * that is currently being written to the persistent fields. 580 * 581 * @return the PutField object from which persistent fields can be accessed 582 * by name. 583 * @throws IOException 584 * if an I/O error occurs. 585 * @throws NotActiveException 586 * if this method is not called from {@code writeObject()}. 587 * @see ObjectInputStream#defaultReadObject 588 */ putFields()589 public PutField putFields() throws IOException { 590 // We can't be called from just anywhere. There are rules. 591 if (currentObject == null) { 592 throw new NotActiveException(); 593 } 594 if (currentPutField == null) { 595 computePutField(); 596 } 597 return currentPutField; 598 } 599 600 /** 601 * Assume object {@code obj} has not been dumped yet, and assign a 602 * handle to it 603 * 604 * @param obj 605 * Non-null object being dumped. 606 * @return the handle that this object is being assigned. 607 * 608 * @see #nextHandle 609 */ registerObjectWritten(Object obj)610 private Integer registerObjectWritten(Object obj) { 611 Integer handle = nextHandle(); 612 objectsWritten.put(obj, handle); 613 return handle; 614 } 615 616 /** 617 * Remove the unshared object from the table, and restore any previous 618 * handle. 619 * 620 * @param obj 621 * Non-null object being dumped. 622 * @param previousHandle 623 * The handle of the previous identical object dumped 624 */ removeUnsharedReference(Object obj, Integer previousHandle)625 private void removeUnsharedReference(Object obj, Integer previousHandle) { 626 if (previousHandle != null) { 627 objectsWritten.put(obj, previousHandle); 628 } else { 629 objectsWritten.remove(obj); 630 } 631 } 632 633 /** 634 * Allows trusted subclasses to substitute the specified original {@code 635 * object} with a new object. Object substitution has to be activated first 636 * with calling {@code enableReplaceObject(true)}. This implementation just 637 * returns {@code object}. 638 * 639 * @param object 640 * the original object for which a replacement may be defined. 641 * @return the replacement object for {@code object}. 642 * @throws IOException 643 * if any I/O error occurs while creating the replacement 644 * object. 645 * @see #enableReplaceObject 646 * @see ObjectInputStream#enableResolveObject 647 * @see ObjectInputStream#resolveObject 648 */ replaceObject(Object object)649 protected Object replaceObject(Object object) throws IOException { 650 // By default no object replacement. Subclasses can override 651 return object; 652 } 653 654 /** 655 * Resets the state of this stream. A marker is written to the stream, so 656 * that the corresponding input stream will also perform a reset at the same 657 * point. Objects previously written are no longer remembered, so they will 658 * be written again (instead of a cyclical reference) if found in the object 659 * graph. 660 * 661 * @throws IOException 662 * if {@code reset()} is called during the serialization of an 663 * object. 664 */ reset()665 public void reset() throws IOException { 666 // First we flush what we have 667 drain(); 668 /* 669 * And dump a reset marker, so that the ObjectInputStream can reset 670 * itself at the same point 671 */ 672 output.writeByte(TC_RESET); 673 // Now we reset ourselves 674 resetState(); 675 } 676 677 /** 678 * Reset the collection of objects already dumped by the receiver. If the 679 * objects are found again in the object graph, the receiver will dump them 680 * again, instead of a handle (cyclic reference). 681 * 682 */ resetSeenObjects()683 private void resetSeenObjects() { 684 objectsWritten = new IdentityHashMap<Object, Integer>(); 685 currentHandle = baseWireHandle; 686 } 687 688 /** 689 * Reset the receiver. The collection of objects already dumped by the 690 * receiver is reset, and internal structures are also reset so that the 691 * receiver knows it is in a fresh clean state. 692 * 693 */ resetState()694 private void resetState() { 695 resetSeenObjects(); 696 nestedLevels = 0; 697 } 698 699 /** 700 * Sets the specified protocol version to be used by this stream. 701 * 702 * @param version 703 * the protocol version to be used. Use a {@code 704 * PROTOCOL_VERSION_x} constant from {@code 705 * java.io.ObjectStreamConstants}. 706 * @throws IllegalArgumentException 707 * if an invalid {@code version} is specified. 708 * @throws IOException 709 * if an I/O error occurs. 710 * @see ObjectStreamConstants#PROTOCOL_VERSION_1 711 * @see ObjectStreamConstants#PROTOCOL_VERSION_2 712 */ useProtocolVersion(int version)713 public void useProtocolVersion(int version) throws IOException { 714 if (!objectsWritten.isEmpty()) { 715 throw new IllegalStateException("Cannot set protocol version when stream in use"); 716 } 717 if (version != ObjectStreamConstants.PROTOCOL_VERSION_1 718 && version != ObjectStreamConstants.PROTOCOL_VERSION_2) { 719 throw new IllegalArgumentException("Unknown protocol: " + version); 720 } 721 protocolVersion = version; 722 } 723 724 /** 725 * Writes the entire contents of the byte array {@code buffer} to the output 726 * stream. Blocks until all bytes are written. 727 * 728 * @param buffer 729 * the buffer to write. 730 * @throws IOException 731 * if an error occurs while writing to the target stream. 732 */ 733 @Override write(byte[] buffer)734 public void write(byte[] buffer) throws IOException { 735 checkWritePrimitiveTypes(); 736 primitiveTypes.write(buffer); 737 } 738 739 /** 740 * Writes {@code count} bytes from the byte array {@code buffer} starting at 741 * offset {@code index} to the target stream. Blocks until all bytes are 742 * written. 743 * 744 * @param buffer 745 * the buffer to write. 746 * @param offset 747 * the index of the first byte in {@code buffer} to write. 748 * @param length 749 * the number of bytes from {@code buffer} to write to the output 750 * stream. 751 * @throws IOException 752 * if an error occurs while writing to the target stream. 753 */ 754 @Override write(byte[] buffer, int offset, int length)755 public void write(byte[] buffer, int offset, int length) throws IOException { 756 checkWritePrimitiveTypes(); 757 primitiveTypes.write(buffer, offset, length); 758 } 759 760 /** 761 * Writes a single byte to the target stream. Only the least significant 762 * byte of the integer {@code value} is written to the stream. Blocks until 763 * the byte is actually written. 764 * 765 * @param value 766 * the byte to write. 767 * @throws IOException 768 * if an error occurs while writing to the target stream. 769 */ 770 @Override write(int value)771 public void write(int value) throws IOException { 772 checkWritePrimitiveTypes(); 773 primitiveTypes.write(value); 774 } 775 776 /** 777 * Writes a boolean to the target stream. 778 * 779 * @param value 780 * the boolean value to write to the target stream. 781 * @throws IOException 782 * if an error occurs while writing to the target stream. 783 */ writeBoolean(boolean value)784 public void writeBoolean(boolean value) throws IOException { 785 checkWritePrimitiveTypes(); 786 primitiveTypes.writeBoolean(value); 787 } 788 789 /** 790 * Writes a byte (8 bit) to the target stream. 791 * 792 * @param value 793 * the byte to write to the target stream. 794 * @throws IOException 795 * if an error occurs while writing to the target stream. 796 */ writeByte(int value)797 public void writeByte(int value) throws IOException { 798 checkWritePrimitiveTypes(); 799 primitiveTypes.writeByte(value); 800 } 801 802 /** 803 * Writes the string {@code value} as a sequence of bytes to the target 804 * stream. Only the least significant byte of each character in the string 805 * is written. 806 * 807 * @param value 808 * the string to write to the target stream. 809 * @throws IOException 810 * if an error occurs while writing to the target stream. 811 */ writeBytes(String value)812 public void writeBytes(String value) throws IOException { 813 checkWritePrimitiveTypes(); 814 primitiveTypes.writeBytes(value); 815 } 816 817 /** 818 * Writes a character (16 bit) to the target stream. 819 * 820 * @param value 821 * the character to write to the target stream. 822 * @throws IOException 823 * if an error occurs while writing to the target stream. 824 */ writeChar(int value)825 public void writeChar(int value) throws IOException { 826 checkWritePrimitiveTypes(); 827 primitiveTypes.writeChar(value); 828 } 829 830 /** 831 * Writes the string {@code value} as a sequence of characters to the target 832 * stream. 833 * 834 * @param value 835 * the string to write to the target stream. 836 * @throws IOException 837 * if an error occurs while writing to the target stream. 838 */ writeChars(String value)839 public void writeChars(String value) throws IOException { 840 checkWritePrimitiveTypes(); 841 primitiveTypes.writeChars(value); 842 } 843 844 /** 845 * Write a class descriptor {@code classDesc} (an 846 * {@code ObjectStreamClass}) to the stream. 847 * 848 * @param classDesc 849 * The class descriptor (an {@code ObjectStreamClass}) to 850 * be dumped 851 * @param unshared 852 * Write the object unshared 853 * @return the handle assigned to the class descriptor 854 * 855 * @throws IOException 856 * If an IO exception happened when writing the class 857 * descriptor. 858 */ writeClassDesc(ObjectStreamClass classDesc, boolean unshared)859 private Integer writeClassDesc(ObjectStreamClass classDesc, boolean unshared) 860 throws IOException { 861 if (classDesc == null) { 862 writeNull(); 863 return null; 864 } 865 Integer handle = null; 866 if (!unshared) { 867 handle = dumpCycle(classDesc); 868 } 869 if (handle == null) { 870 Class<?> classToWrite = classDesc.forClass(); 871 Integer previousHandle = null; 872 if (unshared) { 873 previousHandle = objectsWritten.get(classDesc); 874 } 875 // If we got here, it is a new (non-null) classDesc that will have 876 // to be registered as well 877 handle = nextHandle(); 878 objectsWritten.put(classDesc, handle); 879 880 if (classDesc.isProxy()) { 881 output.writeByte(TC_PROXYCLASSDESC); 882 Class<?>[] interfaces = classToWrite.getInterfaces(); 883 output.writeInt(interfaces.length); 884 for (int i = 0; i < interfaces.length; i++) { 885 output.writeUTF(interfaces[i].getName()); 886 } 887 annotateProxyClass(classToWrite); 888 output.writeByte(TC_ENDBLOCKDATA); 889 writeClassDesc(proxyClassDesc, false); 890 if (unshared) { 891 // remove reference to unshared object 892 removeUnsharedReference(classDesc, previousHandle); 893 } 894 return handle; 895 } 896 897 output.writeByte(TC_CLASSDESC); 898 if (protocolVersion == PROTOCOL_VERSION_1) { 899 writeNewClassDesc(classDesc); 900 } else { 901 // So write...() methods can be used by 902 // subclasses during writeClassDescriptor() 903 primitiveTypes = output; 904 writeClassDescriptor(classDesc); 905 primitiveTypes = null; 906 } 907 // Extra class info (optional) 908 annotateClass(classToWrite); 909 drain(); // flush primitive types in the annotation 910 output.writeByte(TC_ENDBLOCKDATA); 911 writeClassDesc(classDesc.getSuperclass(), unshared); 912 if (unshared) { 913 // remove reference to unshared object 914 removeUnsharedReference(classDesc, previousHandle); 915 } 916 } 917 return handle; 918 } 919 920 /** 921 * Writes a handle representing a cyclic reference (object previously 922 * dumped). 923 * 924 * @param handle 925 * The Integer handle that represents an object previously seen 926 * 927 * @throws IOException 928 * If an IO exception happened when writing the cyclic 929 * reference. 930 */ writeCyclicReference(Integer handle)931 private void writeCyclicReference(Integer handle) throws IOException { 932 output.writeByte(TC_REFERENCE); 933 output.writeInt(handle.intValue()); 934 } 935 936 /** 937 * Writes a double (64 bit) to the target stream. 938 * 939 * @param value 940 * the double to write to the target stream. 941 * @throws IOException 942 * if an error occurs while writing to the target stream. 943 */ writeDouble(double value)944 public void writeDouble(double value) throws IOException { 945 checkWritePrimitiveTypes(); 946 primitiveTypes.writeDouble(value); 947 } 948 949 /** 950 * Writes a collection of field descriptors (name, type name, etc) for the 951 * class descriptor {@code classDesc} (an 952 * {@code ObjectStreamClass}) 953 * 954 * @param classDesc 955 * The class descriptor (an {@code ObjectStreamClass}) 956 * for which to write field information 957 * @param externalizable 958 * true if the descriptors are externalizable 959 * 960 * @throws IOException 961 * If an IO exception happened when writing the field 962 * descriptors. 963 * 964 * @see #writeObject(Object) 965 */ writeFieldDescriptors(ObjectStreamClass classDesc, boolean externalizable)966 private void writeFieldDescriptors(ObjectStreamClass classDesc, 967 boolean externalizable) throws IOException { 968 Class<?> loadedClass = classDesc.forClass(); 969 ObjectStreamField[] fields = null; 970 int fieldCount = 0; 971 972 // The fields of String are not needed since Strings are treated as 973 // primitive types 974 if (!externalizable && loadedClass != ObjectStreamClass.STRINGCLASS) { 975 fields = classDesc.fields(); 976 fieldCount = fields.length; 977 } 978 979 // Field count 980 output.writeShort(fieldCount); 981 // Field names 982 for (int i = 0; i < fieldCount; i++) { 983 ObjectStreamField f = fields[i]; 984 output.writeByte(f.getTypeCode()); 985 output.writeUTF(f.getName()); 986 if (!f.isPrimitive()) { 987 writeObject(f.getTypeString()); 988 } 989 } 990 } 991 992 /** 993 * Writes the fields of the object currently being written to the target 994 * stream. The field values are buffered in the currently active {@code 995 * PutField} object, which can be accessed by calling {@code putFields()}. 996 * 997 * @throws IOException 998 * if an error occurs while writing to the target stream. 999 * @throws NotActiveException 1000 * if there are no fields to write to the target stream. 1001 * @see #putFields 1002 */ writeFields()1003 public void writeFields() throws IOException { 1004 // Has to have fields to write 1005 if (currentPutField == null) { 1006 throw new NotActiveException(); 1007 } 1008 writeFieldValues(currentPutField); 1009 } 1010 1011 /** 1012 * Writes a collection of field values for the emulated fields 1013 * {@code emulatedFields} 1014 * 1015 * @param emulatedFields 1016 * an {@code EmulatedFieldsForDumping}, concrete subclass 1017 * of {@code PutField} 1018 * 1019 * @throws IOException 1020 * If an IO exception happened when writing the field values. 1021 * 1022 * @see #writeFields 1023 * @see #writeObject(Object) 1024 */ writeFieldValues(EmulatedFieldsForDumping emulatedFields)1025 private void writeFieldValues(EmulatedFieldsForDumping emulatedFields) 1026 throws IOException { 1027 EmulatedFields accessibleSimulatedFields = emulatedFields 1028 .emulatedFields(); // Access internal fields which we can 1029 // set/get. Users can't do this. 1030 EmulatedFields.ObjectSlot[] slots = accessibleSimulatedFields.slots(); 1031 for (int i = 0; i < slots.length; i++) { 1032 EmulatedFields.ObjectSlot slot = slots[i]; 1033 Object fieldValue = slot.getFieldValue(); 1034 Class<?> type = slot.getField().getType(); 1035 // WARNING - default values exist for each primitive type 1036 if (type == Integer.TYPE) { 1037 output.writeInt(fieldValue != null ? ((Integer) fieldValue) 1038 .intValue() : 0); 1039 } else if (type == Byte.TYPE) { 1040 output.writeByte(fieldValue != null ? ((Byte) fieldValue) 1041 .byteValue() : (byte) 0); 1042 } else if (type == Character.TYPE) { 1043 output.writeChar(fieldValue != null ? ((Character) fieldValue) 1044 .charValue() : (char) 0); 1045 } else if (type == Short.TYPE) { 1046 output.writeShort(fieldValue != null ? ((Short) fieldValue) 1047 .shortValue() : (short) 0); 1048 } else if (type == Boolean.TYPE) { 1049 output.writeBoolean(fieldValue != null ? ((Boolean) fieldValue) 1050 .booleanValue() : false); 1051 } else if (type == Long.TYPE) { 1052 output.writeLong(fieldValue != null ? ((Long) fieldValue) 1053 .longValue() : (long) 0); 1054 } else if (type == Float.TYPE) { 1055 output.writeFloat(fieldValue != null ? ((Float) fieldValue) 1056 .floatValue() : (float) 0); 1057 } else if (type == Double.TYPE) { 1058 output.writeDouble(fieldValue != null ? ((Double) fieldValue) 1059 .doubleValue() : (double) 0); 1060 } else { 1061 // Either array or Object 1062 writeObject(fieldValue); 1063 } 1064 } 1065 } 1066 1067 1068 /** 1069 * Writes a collection of field values for the fields described by class 1070 * descriptor {@code classDesc} (an {@code ObjectStreamClass}). 1071 * This is the default mechanism, when emulated fields (an 1072 * {@code PutField}) are not used. Actual values to dump are fetched 1073 * directly from object {@code obj}. 1074 * 1075 * @param obj 1076 * Instance from which to fetch field values to dump. 1077 * @param classDesc 1078 * A class descriptor (an {@code ObjectStreamClass}) 1079 * defining which fields should be dumped. 1080 * 1081 * @throws IOException 1082 * If an IO exception happened when writing the field values. 1083 * 1084 * @see #writeObject(Object) 1085 */ writeFieldValues(Object obj, ObjectStreamClass classDesc)1086 private void writeFieldValues(Object obj, ObjectStreamClass classDesc) 1087 throws IOException { 1088 ObjectStreamField[] fields = classDesc.fields(); 1089 Class<?> declaringClass = classDesc.forClass(); 1090 for(ObjectStreamField fieldDesc : fields) { 1091 try { 1092 1093 // BEGIN android-changed 1094 // // get associated Field 1095 // long fieldID = fieldDesc.getFieldID(accessor, declaringClass); 1096 1097 // Code duplication starts, just because Java is typed 1098 if (fieldDesc.isPrimitive()) { 1099 switch (fieldDesc.getTypeCode()) { 1100 case 'B': 1101 output.writeByte(getFieldByte(obj, declaringClass, 1102 fieldDesc.getName())); 1103 break; 1104 case 'C': 1105 output.writeChar(getFieldChar(obj, declaringClass, 1106 fieldDesc.getName())); 1107 break; 1108 case 'D': 1109 output.writeDouble(getFieldDouble(obj, 1110 declaringClass, fieldDesc.getName())); 1111 break; 1112 case 'F': 1113 output.writeFloat(getFieldFloat(obj, 1114 declaringClass, fieldDesc.getName())); 1115 break; 1116 case 'I': 1117 output.writeInt(getFieldInt(obj, declaringClass, 1118 fieldDesc.getName())); 1119 break; 1120 case 'J': 1121 output.writeLong(getFieldLong(obj, declaringClass, 1122 fieldDesc.getName())); 1123 break; 1124 case 'S': 1125 output.writeShort(getFieldShort(obj, 1126 declaringClass, fieldDesc.getName())); 1127 break; 1128 case 'Z': 1129 output.writeBoolean(getFieldBool(obj, 1130 declaringClass, fieldDesc.getName())); 1131 break; 1132 default: 1133 throw new IOException("Invalid typecode: " + 1134 fieldDesc.getTypeCode()); 1135 } 1136 } else { 1137 // Object type (array included). 1138 Object objField = getFieldObj(obj, declaringClass, fieldDesc 1139 .getName(), fieldDesc.getTypeString()); 1140 if (fieldDesc.isUnshared()) { 1141 writeUnshared(objField); 1142 } else { 1143 writeObject(objField); 1144 } 1145 } 1146 // END android-changed 1147 } catch (NoSuchFieldError nsf) { 1148 // The user defined serialPersistentFields but did not provide 1149 // the glue to transfer values, 1150 // (in writeObject) so we end up using the default mechanism and 1151 // fail to set the emulated field 1152 throw new InvalidClassException(classDesc.getName()); 1153 } 1154 } 1155 } 1156 1157 /** 1158 * Writes a float (32 bit) to the target stream. 1159 * 1160 * @param value 1161 * the float to write to the target stream. 1162 * @throws IOException 1163 * if an error occurs while writing to the target stream. 1164 */ writeFloat(float value)1165 public void writeFloat(float value) throws IOException { 1166 checkWritePrimitiveTypes(); 1167 primitiveTypes.writeFloat(value); 1168 } 1169 1170 /** 1171 * Walks the hierarchy of classes described by class descriptor 1172 * {@code classDesc} and writes the field values corresponding to 1173 * fields declared by the corresponding class descriptor. The instance to 1174 * fetch field values from is {@code object}. If the class 1175 * (corresponding to class descriptor {@code classDesc}) defines 1176 * private instance method {@code writeObject} it will be used to 1177 * dump field values. 1178 * 1179 * @param object 1180 * Instance from which to fetch field values to dump. 1181 * @param classDesc 1182 * A class descriptor (an {@code ObjectStreamClass}) 1183 * defining which fields should be dumped. 1184 * 1185 * @throws IOException 1186 * If an IO exception happened when writing the field values in 1187 * the hierarchy. 1188 * @throws NotActiveException 1189 * If the given object is not active 1190 * 1191 * @see #defaultWriteObject 1192 * @see #writeObject(Object) 1193 */ writeHierarchy(Object object, ObjectStreamClass classDesc)1194 private void writeHierarchy(Object object, ObjectStreamClass classDesc) 1195 throws IOException, NotActiveException { 1196 // We can't be called from just anywhere. There are rules. 1197 if (object == null) { 1198 throw new NotActiveException(); 1199 } 1200 1201 // Fields are written from class closest to Object to leaf class 1202 // (down the chain) 1203 if (classDesc.getSuperclass() != null) { 1204 // first 1205 writeHierarchy(object, classDesc.getSuperclass()); 1206 } 1207 1208 // Have to do this before calling defaultWriteObject or anything 1209 // that calls defaultWriteObject 1210 currentObject = object; 1211 currentClass = classDesc; 1212 1213 // See if the object has a writeObject method. If so, run it 1214 boolean executed = false; 1215 try { 1216 if (classDesc.hasMethodWriteObject()){ 1217 final Method method = classDesc.getMethodWriteObject(); 1218 try { 1219 method.invoke(object, new Object[] { this }); 1220 executed = true; 1221 } catch (InvocationTargetException e) { 1222 Throwable ex = e.getTargetException(); 1223 if (ex instanceof RuntimeException) { 1224 throw (RuntimeException) ex; 1225 } else if (ex instanceof Error) { 1226 throw (Error) ex; 1227 } 1228 throw (IOException) ex; 1229 } catch (IllegalAccessException e) { 1230 throw new RuntimeException(e.toString()); 1231 } 1232 } 1233 1234 1235 if (executed) { 1236 drain(); 1237 output.writeByte(TC_ENDBLOCKDATA); 1238 } else { 1239 // If the object did not have a writeMethod, call 1240 // defaultWriteObject 1241 defaultWriteObject(); 1242 } 1243 } finally { 1244 // Cleanup, needs to run always so that we can later detect 1245 // invalid calls to defaultWriteObject 1246 currentObject = null; 1247 currentClass = null; 1248 currentPutField = null; 1249 } 1250 } 1251 1252 /** 1253 * Writes an integer (32 bit) to the target stream. 1254 * 1255 * @param value 1256 * the integer to write to the target stream. 1257 * @throws IOException 1258 * if an error occurs while writing to the target stream. 1259 */ writeInt(int value)1260 public void writeInt(int value) throws IOException { 1261 checkWritePrimitiveTypes(); 1262 primitiveTypes.writeInt(value); 1263 } 1264 1265 /** 1266 * Writes a long (64 bit) to the target stream. 1267 * 1268 * @param value 1269 * the long to write to the target stream. 1270 * @throws IOException 1271 * if an error occurs while writing to the target stream. 1272 */ writeLong(long value)1273 public void writeLong(long value) throws IOException { 1274 checkWritePrimitiveTypes(); 1275 primitiveTypes.writeLong(value); 1276 } 1277 1278 /** 1279 * Write array {@code array} of class {@code arrayClass} with 1280 * component type {@code componentType} into the receiver. It is 1281 * assumed the array has not been dumped yet. Return an {@code Integer} 1282 * that represents the handle for this object (array) which is dumped here. 1283 * 1284 * @param array 1285 * The array object to dump 1286 * @param arrayClass 1287 * A {@code java.lang.Class} representing the class of the 1288 * array 1289 * @param componentType 1290 * A {@code java.lang.Class} representing the array 1291 * component type 1292 * @return the handle assigned to the array 1293 * 1294 * @throws IOException 1295 * If an IO exception happened when writing the array. 1296 */ writeNewArray(Object array, Class<?> arrayClass, ObjectStreamClass arrayClDesc, Class<?> componentType, boolean unshared)1297 private Integer writeNewArray(Object array, Class<?> arrayClass, ObjectStreamClass arrayClDesc, 1298 Class<?> componentType, boolean unshared) throws IOException { 1299 output.writeByte(TC_ARRAY); 1300 writeClassDesc(arrayClDesc, false); 1301 1302 Integer handle = nextHandle(); 1303 1304 if (!unshared) { 1305 objectsWritten.put(array, handle); 1306 } 1307 1308 // Now we have code duplication just because Java is typed. We have to 1309 // write N elements and assign to array positions, but we must typecast 1310 // the array first, and also call different methods depending on the 1311 // elements. 1312 1313 if (componentType.isPrimitive()) { 1314 if (componentType == Integer.TYPE) { 1315 int[] intArray = (int[]) array; 1316 output.writeInt(intArray.length); 1317 for (int i = 0; i < intArray.length; i++) { 1318 output.writeInt(intArray[i]); 1319 } 1320 } else if (componentType == Byte.TYPE) { 1321 byte[] byteArray = (byte[]) array; 1322 output.writeInt(byteArray.length); 1323 output.write(byteArray, 0, byteArray.length); 1324 } else if (componentType == Character.TYPE) { 1325 char[] charArray = (char[]) array; 1326 output.writeInt(charArray.length); 1327 for (int i = 0; i < charArray.length; i++) { 1328 output.writeChar(charArray[i]); 1329 } 1330 } else if (componentType == Short.TYPE) { 1331 short[] shortArray = (short[]) array; 1332 output.writeInt(shortArray.length); 1333 for (int i = 0; i < shortArray.length; i++) { 1334 output.writeShort(shortArray[i]); 1335 } 1336 } else if (componentType == Boolean.TYPE) { 1337 boolean[] booleanArray = (boolean[]) array; 1338 output.writeInt(booleanArray.length); 1339 for (int i = 0; i < booleanArray.length; i++) { 1340 output.writeBoolean(booleanArray[i]); 1341 } 1342 } else if (componentType == Long.TYPE) { 1343 long[] longArray = (long[]) array; 1344 output.writeInt(longArray.length); 1345 for (int i = 0; i < longArray.length; i++) { 1346 output.writeLong(longArray[i]); 1347 } 1348 } else if (componentType == Float.TYPE) { 1349 float[] floatArray = (float[]) array; 1350 output.writeInt(floatArray.length); 1351 for (int i = 0; i < floatArray.length; i++) { 1352 output.writeFloat(floatArray[i]); 1353 } 1354 } else if (componentType == Double.TYPE) { 1355 double[] doubleArray = (double[]) array; 1356 output.writeInt(doubleArray.length); 1357 for (int i = 0; i < doubleArray.length; i++) { 1358 output.writeDouble(doubleArray[i]); 1359 } 1360 } else { 1361 throw new InvalidClassException("Wrong base type in " + arrayClass.getName()); 1362 } 1363 } else { 1364 // Array of Objects 1365 Object[] objectArray = (Object[]) array; 1366 output.writeInt(objectArray.length); 1367 for (int i = 0; i < objectArray.length; i++) { 1368 // TODO: This place is the opportunity for enhancement 1369 // We can implement writing elements through fast-path, 1370 // without setting up the context (see writeObject()) for 1371 // each element with public API 1372 writeObject(objectArray[i]); 1373 } 1374 } 1375 return handle; 1376 } 1377 1378 /** 1379 * Write class {@code object} into the receiver. It is assumed the 1380 * class has not been dumped yet. Classes are not really dumped, but a class 1381 * descriptor ({@code ObjectStreamClass}) that corresponds to them. 1382 * Return an {@code Integer} that represents the handle for this 1383 * object (class) which is dumped here. 1384 * 1385 * @param object 1386 * The {@code java.lang.Class} object to dump 1387 * @return the handle assigned to the class being dumped 1388 * 1389 * @throws IOException 1390 * If an IO exception happened when writing the class. 1391 */ writeNewClass(Class<?> object, boolean unshared)1392 private Integer writeNewClass(Class<?> object, boolean unshared) 1393 throws IOException { 1394 output.writeByte(TC_CLASS); 1395 1396 // Instances of java.lang.Class are always Serializable, even if their 1397 // instances aren't (e.g. java.lang.Object.class). 1398 // We cannot call lookup because it returns null if the parameter 1399 // represents instances that cannot be serialized, and that is not what 1400 // we want. 1401 ObjectStreamClass clDesc = ObjectStreamClass.lookupStreamClass(object); 1402 1403 // The handle for the classDesc is NOT the handle for the class object 1404 // being dumped. We must allocate a new handle and return it. 1405 if (clDesc.isEnum()) { 1406 writeEnumDesc(object, clDesc, unshared); 1407 } else { 1408 writeClassDesc(clDesc, unshared); 1409 } 1410 1411 Integer handle = nextHandle(); 1412 1413 if (!unshared) { 1414 objectsWritten.put(object, handle); 1415 } 1416 1417 return handle; 1418 } 1419 1420 /** 1421 * Write class descriptor {@code classDesc} into the receiver. It is 1422 * assumed the class descriptor has not been dumped yet. The class 1423 * descriptors for the superclass chain will be dumped as well. Return an 1424 * {@code Integer} that represents the handle for this object (class 1425 * descriptor) which is dumped here. 1426 * 1427 * @param classDesc 1428 * The {@code ObjectStreamClass} object to dump 1429 * 1430 * @throws IOException 1431 * If an IO exception happened when writing the class 1432 * descriptor. 1433 */ writeNewClassDesc(ObjectStreamClass classDesc)1434 private void writeNewClassDesc(ObjectStreamClass classDesc) 1435 throws IOException { 1436 output.writeUTF(classDesc.getName()); 1437 output.writeLong(classDesc.getSerialVersionUID()); 1438 byte flags = classDesc.getFlags(); 1439 1440 boolean externalizable = classDesc.isExternalizable(); 1441 1442 if (externalizable) { 1443 if (protocolVersion == PROTOCOL_VERSION_1) { 1444 flags &= NOT_SC_BLOCK_DATA; 1445 } else { 1446 // Change for 1.2. Objects can be saved in old format 1447 // (PROTOCOL_VERSION_1) or in the 1.2 format (PROTOCOL_VERSION_2). 1448 flags |= SC_BLOCK_DATA; 1449 } 1450 } 1451 output.writeByte(flags); 1452 if ((SC_ENUM | SC_SERIALIZABLE) != classDesc.getFlags()) { 1453 writeFieldDescriptors(classDesc, externalizable); 1454 } else { 1455 // enum write no fields 1456 output.writeShort(0); 1457 } 1458 } 1459 1460 /** 1461 * Writes a class descriptor to the target stream. 1462 * 1463 * @param classDesc 1464 * the class descriptor to write to the target stream. 1465 * @throws IOException 1466 * if an error occurs while writing to the target stream. 1467 */ writeClassDescriptor(ObjectStreamClass classDesc)1468 protected void writeClassDescriptor(ObjectStreamClass classDesc) 1469 throws IOException { 1470 writeNewClassDesc(classDesc); 1471 } 1472 1473 /** 1474 * Write exception {@code ex} into the receiver. It is assumed the 1475 * exception has not been dumped yet. Return an {@code Integer} that 1476 * represents the handle for this object (exception) which is dumped here. 1477 * This is used to dump the exception instance that happened (if any) when 1478 * dumping the original object graph. The set of seen objects will be reset 1479 * just before and just after dumping this exception object. 1480 * 1481 * When exceptions are found normally in the object graph, they are dumped 1482 * as a regular object, and not by this method. In that case, the set of 1483 * "known objects" is not reset. 1484 * 1485 * @param ex 1486 * Exception object to dump 1487 * 1488 * @throws IOException 1489 * If an IO exception happened when writing the exception 1490 * object. 1491 */ writeNewException(Exception ex)1492 private void writeNewException(Exception ex) throws IOException { 1493 output.writeByte(TC_EXCEPTION); 1494 resetSeenObjects(); 1495 writeObjectInternal(ex, false, false, false); // No replacements 1496 resetSeenObjects(); 1497 } 1498 1499 /** 1500 * Write object {@code object} of class {@code theClass} into 1501 * the receiver. It is assumed the object has not been dumped yet. Return an 1502 * {@code Integer} that represents the handle for this object which 1503 * is dumped here. 1504 * 1505 * If the object implements {@code Externalizable} its 1506 * {@code writeExternal} is called. Otherwise, all fields described 1507 * by the class hierarchy is dumped. Each class can define how its declared 1508 * instance fields are dumped by defining a private method 1509 * {@code writeObject} 1510 * 1511 * @param object 1512 * The object to dump 1513 * @param theClass 1514 * A {@code java.lang.Class} representing the class of the 1515 * object 1516 * @param unshared 1517 * Write the object unshared 1518 * @return the handle assigned to the object 1519 * 1520 * @throws IOException 1521 * If an IO exception happened when writing the object. 1522 */ writeNewObject(Object object, Class<?> theClass, ObjectStreamClass clDesc, boolean unshared)1523 private Integer writeNewObject(Object object, Class<?> theClass, ObjectStreamClass clDesc, 1524 boolean unshared) throws IOException { 1525 // Not String, not null, not array, not cyclic reference 1526 1527 EmulatedFieldsForDumping originalCurrentPutField = currentPutField; // save 1528 currentPutField = null; // null it, to make sure one will be computed if 1529 // needed 1530 1531 boolean externalizable = clDesc.isExternalizable(); 1532 boolean serializable = clDesc.isSerializable(); 1533 if (!externalizable && !serializable) { 1534 // Object is neither externalizable nor serializable. Error 1535 throw new NotSerializableException(theClass.getName()); 1536 } 1537 1538 // Either serializable or externalizable, now we can save info 1539 output.writeByte(TC_OBJECT); 1540 writeClassDesc(clDesc, false); 1541 Integer previousHandle = null; 1542 if (unshared) { 1543 previousHandle = objectsWritten.get(object); 1544 } 1545 Integer handle = nextHandle(); 1546 objectsWritten.put(object, handle); 1547 1548 // This is how we know what to do in defaultWriteObject. And it is also 1549 // used by defaultWriteObject to check if it was called from an invalid 1550 // place. 1551 // It allows writeExternal to call defaultWriteObject and have it work. 1552 currentObject = object; 1553 currentClass = clDesc; 1554 try { 1555 if (externalizable) { 1556 boolean noBlockData = protocolVersion == PROTOCOL_VERSION_1; 1557 if (noBlockData) { 1558 primitiveTypes = output; 1559 } 1560 // Object is externalizable, just call its own method 1561 ((Externalizable) object).writeExternal(this); 1562 if (noBlockData) { 1563 primitiveTypes = null; 1564 } else { 1565 // Similar to the code in writeHierarchy when object 1566 // implements writeObject. 1567 // Any primitive data has to be flushed and a tag must be 1568 // written 1569 drain(); 1570 output.writeByte(TC_ENDBLOCKDATA); 1571 } 1572 } else { // If it got here, it has to be Serializable 1573 // Object is serializable. Walk the class chain writing the 1574 // fields 1575 writeHierarchy(object, currentClass); 1576 } 1577 } finally { 1578 // Cleanup, needs to run always so that we can later detect invalid 1579 // calls to defaultWriteObject 1580 if (unshared) { 1581 // remove reference to unshared object 1582 removeUnsharedReference(object, previousHandle); 1583 } 1584 currentObject = null; 1585 currentClass = null; 1586 currentPutField = originalCurrentPutField; 1587 } 1588 1589 return handle; 1590 } 1591 1592 /** 1593 * Write String {@code object} into the receiver. It is assumed the 1594 * String has not been dumped yet. Return an {@code Integer} that 1595 * represents the handle for this object (String) which is dumped here. 1596 * Strings are saved encoded with {@link DataInput modified UTF-8}. 1597 * 1598 * @param object 1599 * the string to dump. 1600 * @return the handle assigned to the String being dumped 1601 * 1602 * @throws IOException 1603 * If an IO exception happened when writing the String. 1604 */ writeNewString(String object, boolean unshared)1605 private Integer writeNewString(String object, boolean unshared) 1606 throws IOException { 1607 long count = output.countUTFBytes(object); 1608 byte[] buffer; 1609 int offset = 0; 1610 if (count <= 0xffff) { 1611 buffer = new byte[(int)count+3]; 1612 buffer[offset++] = TC_STRING; 1613 offset = output.writeShortToBuffer((short) count, buffer, offset); 1614 } else { 1615 buffer = new byte[(int)count+9]; 1616 buffer[offset++] = TC_LONGSTRING; 1617 offset = output.writeLongToBuffer(count, buffer, offset); 1618 } 1619 offset = output.writeUTFBytesToBuffer(object, buffer, offset); 1620 output.write(buffer, 0, offset); 1621 1622 Integer handle = nextHandle(); 1623 1624 if (!unshared) { 1625 objectsWritten.put(object, handle); 1626 } 1627 1628 return handle; 1629 } 1630 1631 /** 1632 * Write a special tag that indicates the value {@code null} into the 1633 * receiver. 1634 * 1635 * @throws IOException 1636 * If an IO exception happened when writing the tag for 1637 * {@code null}. 1638 */ writeNull()1639 private void writeNull() throws IOException { 1640 output.writeByte(TC_NULL); 1641 } 1642 1643 /** 1644 * Writes an object to the target stream. 1645 * 1646 * @param object 1647 * the object to write to the target stream. 1648 * @throws IOException 1649 * if an error occurs while writing to the target stream. 1650 * @see ObjectInputStream#readObject() 1651 */ writeObject(Object object)1652 public final void writeObject(Object object) throws IOException { 1653 writeObject(object, false); 1654 } 1655 1656 /** 1657 * Writes an unshared object to the target stream. This method is identical 1658 * to {@code writeObject}, except that it always writes a new object to the 1659 * stream versus the use of back-referencing for identical objects by 1660 * {@code writeObject}. 1661 * 1662 * @param object 1663 * the object to write to the target stream. 1664 * @throws IOException 1665 * if an error occurs while writing to the target stream. 1666 * @see ObjectInputStream#readUnshared() 1667 */ writeUnshared(Object object)1668 public void writeUnshared(Object object) throws IOException { 1669 writeObject(object, true); 1670 } 1671 writeObject(Object object, boolean unshared)1672 private void writeObject(Object object, boolean unshared) 1673 throws IOException { 1674 boolean setOutput = (primitiveTypes == output); 1675 if (setOutput) { 1676 primitiveTypes = null; 1677 } 1678 // This is the spec'ed behavior in JDK 1.2. Very bizarre way to allow 1679 // behavior overriding. 1680 if (subclassOverridingImplementation && !unshared) { 1681 writeObjectOverride(object); 1682 } else { 1683 1684 try { 1685 // First we need to flush primitive types if they were written 1686 drain(); 1687 // Actual work, and class-based replacement should be computed 1688 // if needed. 1689 writeObjectInternal(object, unshared, true, true); 1690 if (setOutput) { 1691 primitiveTypes = output; 1692 } 1693 } catch (IOException ioEx1) { 1694 // This will make it pass through until the top caller. It also 1695 // lets it pass through the nested exception. 1696 if (nestedLevels == 0 && ioEx1 != nestedException) { 1697 try { 1698 writeNewException(ioEx1); 1699 } catch (IOException ioEx2) { 1700 nestedException.fillInStackTrace(); 1701 throw nestedException; 1702 } 1703 } 1704 throw ioEx1; // and then we propagate the original exception 1705 } 1706 } 1707 } 1708 1709 /** 1710 * Write object {@code object} into the receiver's underlying stream. 1711 * 1712 * @param object 1713 * The object to write 1714 * @param unshared 1715 * Write the object unshared 1716 * @param computeClassBasedReplacement 1717 * A boolean indicating if class-based replacement should be 1718 * computed (if supported) for the object. 1719 * @param computeStreamReplacement 1720 * A boolean indicating if stream-based replacement should be 1721 * computed (if supported) for the object. 1722 * @return the handle assigned to the final object being dumped 1723 * 1724 * @throws IOException 1725 * If an IO exception happened when writing the object 1726 * 1727 * @see ObjectInputStream#readObject() 1728 */ writeObjectInternal(Object object, boolean unshared, boolean computeClassBasedReplacement, boolean computeStreamReplacement)1729 private Integer writeObjectInternal(Object object, boolean unshared, 1730 boolean computeClassBasedReplacement, 1731 boolean computeStreamReplacement) throws IOException { 1732 1733 if (object == null) { 1734 writeNull(); 1735 return null; 1736 } 1737 Integer handle = null; 1738 if (!unshared) { 1739 handle = dumpCycle(object); 1740 if (handle != null) { 1741 return handle; // cyclic reference 1742 } 1743 } 1744 1745 // Non-null object, first time seen... 1746 Class<?> objClass = object.getClass(); 1747 ObjectStreamClass clDesc = ObjectStreamClass.lookupStreamClass(objClass); 1748 1749 nestedLevels++; 1750 try { 1751 1752 if (!(enableReplace && computeStreamReplacement)) { 1753 // Is it a Class ? 1754 if (objClass == ObjectStreamClass.CLASSCLASS) { 1755 return writeNewClass((Class<?>) object, unshared); 1756 } 1757 // Is it an ObjectStreamClass ? 1758 if (objClass == ObjectStreamClass.OBJECTSTREAMCLASSCLASS) { 1759 return writeClassDesc((ObjectStreamClass) object, unshared); 1760 } 1761 } 1762 1763 if (clDesc.isSerializable() 1764 && computeClassBasedReplacement) { 1765 if(clDesc.hasMethodWriteReplace()){ 1766 Method methodWriteReplace = clDesc.getMethodWriteReplace(); 1767 Object replObj = null; 1768 try { 1769 replObj = methodWriteReplace.invoke(object, (Object[]) null); 1770 } catch (IllegalAccessException iae) { 1771 replObj = object; 1772 } catch (InvocationTargetException ite) { 1773 // WARNING - Not sure this is the right thing to do 1774 // if we can't run the method 1775 Throwable target = ite.getTargetException(); 1776 if (target instanceof ObjectStreamException) { 1777 throw (ObjectStreamException) target; 1778 } else if (target instanceof Error) { 1779 throw (Error) target; 1780 } else { 1781 throw (RuntimeException) target; 1782 } 1783 } 1784 if (replObj != object) { 1785 // All over, class-based replacement off this time. 1786 Integer replacementHandle = writeObjectInternal( 1787 replObj, false, false, 1788 computeStreamReplacement); 1789 // Make the original object also map to the same 1790 // handle. 1791 if (replacementHandle != null) { 1792 objectsWritten.put(object, replacementHandle); 1793 } 1794 return replacementHandle; 1795 } 1796 } 1797 1798 } 1799 1800 // We get here either if class-based replacement was not needed or 1801 // if it was needed but produced the same object or if it could not 1802 // be computed. 1803 if (enableReplace && computeStreamReplacement) { 1804 // Now we compute the stream-defined replacement. 1805 Object streamReplacement = replaceObject(object); 1806 if (streamReplacement != object) { 1807 // All over, class-based replacement off this time. 1808 Integer replacementHandle = writeObjectInternal( 1809 streamReplacement, false, 1810 computeClassBasedReplacement, false); 1811 // Make the original object also map to the same handle. 1812 if (replacementHandle != null) { 1813 objectsWritten.put(object, replacementHandle); 1814 } 1815 return replacementHandle; 1816 } 1817 } 1818 1819 // We get here if stream-based replacement produced the same object 1820 1821 // Is it a Class ? 1822 if (objClass == ObjectStreamClass.CLASSCLASS) { 1823 return writeNewClass((Class<?>) object, unshared); 1824 } 1825 1826 // Is it an ObjectStreamClass ? 1827 if (objClass == ObjectStreamClass.OBJECTSTREAMCLASSCLASS) { 1828 return writeClassDesc((ObjectStreamClass) object, unshared); 1829 } 1830 1831 // Is it a String ? (instanceof, but == is faster) 1832 if (objClass == ObjectStreamClass.STRINGCLASS) { 1833 return writeNewString((String) object, unshared); 1834 } 1835 1836 // Is it an Array ? 1837 if (objClass.isArray()) { 1838 return writeNewArray(object, objClass, clDesc, objClass 1839 .getComponentType(), unshared); 1840 } 1841 1842 if (object instanceof Enum) { 1843 return writeNewEnum(object, objClass, unshared); 1844 } 1845 1846 // Not a String or Class or Array. Default procedure. 1847 return writeNewObject(object, objClass, clDesc, unshared); 1848 } finally { 1849 nestedLevels--; 1850 } 1851 } 1852 1853 // write for Enum Class Desc only, which is different from other classes writeEnumDesc(Class<?> theClass, ObjectStreamClass classDesc, boolean unshared)1854 private ObjectStreamClass writeEnumDesc(Class<?> theClass, ObjectStreamClass classDesc, boolean unshared) 1855 throws IOException { 1856 // write classDesc, classDesc for enum is different 1857 1858 // set flag for enum, the flag is (SC_SERIALIZABLE | SC_ENUM) 1859 classDesc.setFlags((byte) (SC_SERIALIZABLE | SC_ENUM)); 1860 Integer previousHandle = null; 1861 if (unshared) { 1862 previousHandle = objectsWritten.get(classDesc); 1863 } 1864 Integer handle = null; 1865 if (!unshared) { 1866 handle = dumpCycle(classDesc); 1867 } 1868 if (handle == null) { 1869 Class<?> classToWrite = classDesc.forClass(); 1870 // If we got here, it is a new (non-null) classDesc that will have 1871 // to be registered as well 1872 objectsWritten.put(classDesc, nextHandle()); 1873 1874 output.writeByte(TC_CLASSDESC); 1875 if (protocolVersion == PROTOCOL_VERSION_1) { 1876 writeNewClassDesc(classDesc); 1877 } else { 1878 // So write...() methods can be used by 1879 // subclasses during writeClassDescriptor() 1880 primitiveTypes = output; 1881 writeClassDescriptor(classDesc); 1882 primitiveTypes = null; 1883 } 1884 // Extra class info (optional) 1885 annotateClass(classToWrite); 1886 drain(); // flush primitive types in the annotation 1887 output.writeByte(TC_ENDBLOCKDATA); 1888 // write super class 1889 ObjectStreamClass superClassDesc = classDesc.getSuperclass(); 1890 if (null != superClassDesc) { 1891 // super class is also enum 1892 superClassDesc.setFlags((byte) (SC_SERIALIZABLE | SC_ENUM)); 1893 writeEnumDesc(superClassDesc.forClass(), superClassDesc, unshared); 1894 } else { 1895 output.writeByte(TC_NULL); 1896 } 1897 if (unshared) { 1898 // remove reference to unshared object 1899 removeUnsharedReference(classDesc, previousHandle); 1900 } 1901 } 1902 return classDesc; 1903 } 1904 writeNewEnum(Object object, Class<?> theClass, boolean unshared)1905 private Integer writeNewEnum(Object object, Class<?> theClass, 1906 boolean unshared) throws IOException { 1907 // write new Enum 1908 EmulatedFieldsForDumping originalCurrentPutField = currentPutField; // save 1909 // null it, to make sure one will be computed if needed 1910 currentPutField = null; 1911 1912 output.writeByte(TC_ENUM); 1913 while (theClass != null && !theClass.isEnum()) { 1914 // write enum only 1915 theClass = theClass.getSuperclass(); 1916 } 1917 ObjectStreamClass classDesc = ObjectStreamClass.lookup(theClass); 1918 writeEnumDesc(theClass, classDesc, unshared); 1919 1920 Integer previousHandle = null; 1921 if (unshared) { 1922 previousHandle = objectsWritten.get(object); 1923 } 1924 Integer handle = nextHandle(); 1925 objectsWritten.put(object, handle); 1926 1927 ObjectStreamField[] fields = classDesc.getSuperclass().fields(); 1928 Class<?> declaringClass = classDesc.getSuperclass().forClass(); 1929 // Only write field "name" for enum class, which is the second field of 1930 // enum, that is fields[1]. Ignore all non-fields and fields.length < 2 1931 if (null != fields && fields.length > 1) { 1932 // BEGIN android-changed 1933 String str = (String) getFieldObj(object, declaringClass, fields[1] 1934 .getName(), fields[1].getTypeString()); 1935 // END android-changed 1936 1937 Integer strhandle = null; 1938 if (!unshared) { 1939 strhandle = dumpCycle(str); 1940 } 1941 if (null == strhandle) { 1942 writeNewString(str, unshared); 1943 } 1944 } 1945 1946 if (unshared) { 1947 // remove reference to unshared object 1948 removeUnsharedReference(object, previousHandle); 1949 } 1950 currentPutField = originalCurrentPutField; 1951 return handle; 1952 } 1953 1954 /** 1955 * Method to be overridden by subclasses to write {@code object} to the 1956 * target stream. 1957 * 1958 * @param object 1959 * the object to write to the target stream. 1960 * @throws IOException 1961 * if an error occurs while writing to the target stream. 1962 */ writeObjectOverride(Object object)1963 protected void writeObjectOverride(Object object) throws IOException { 1964 if (!subclassOverridingImplementation) { 1965 // Subclasses must override. 1966 throw new IOException(); 1967 } 1968 } 1969 1970 /** 1971 * Writes a short (16 bit) to the target stream. 1972 * 1973 * @param value 1974 * the short to write to the target stream. 1975 * @throws IOException 1976 * if an error occurs while writing to the target stream. 1977 */ writeShort(int value)1978 public void writeShort(int value) throws IOException { 1979 checkWritePrimitiveTypes(); 1980 primitiveTypes.writeShort(value); 1981 } 1982 1983 /** 1984 * Writes the {@link ObjectOutputStream} header to the target stream. 1985 * 1986 * @throws IOException 1987 * if an error occurs while writing to the target stream. 1988 */ writeStreamHeader()1989 protected void writeStreamHeader() throws IOException { 1990 output.writeShort(STREAM_MAGIC); 1991 output.writeShort(STREAM_VERSION); 1992 } 1993 1994 /** 1995 * Writes a string encoded with {@link DataInput modified UTF-8} to the 1996 * target stream. 1997 * 1998 * @param value 1999 * the string to write to the target stream. 2000 * @throws IOException 2001 * if an error occurs while writing to the target stream. 2002 */ writeUTF(String value)2003 public void writeUTF(String value) throws IOException { 2004 checkWritePrimitiveTypes(); 2005 primitiveTypes.writeUTF(value); 2006 } 2007 } 2008