• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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