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