• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.res.android;
2 
3 import static org.robolectric.res.android.Errors.BAD_TYPE;
4 import static org.robolectric.res.android.Errors.NO_ERROR;
5 import static org.robolectric.res.android.Errors.NO_INIT;
6 import static org.robolectric.res.android.ResTable.kDebugResXMLTree;
7 import static org.robolectric.res.android.ResTable.kDebugXMLNoisy;
8 import static org.robolectric.res.android.ResXMLParser.SIZEOF_RESXMLTREE_ATTR_EXT;
9 import static org.robolectric.res.android.ResXMLParser.SIZEOF_RESXMLTREE_NODE;
10 import static org.robolectric.res.android.ResXMLParser.event_code_t.BAD_DOCUMENT;
11 import static org.robolectric.res.android.ResXMLParser.event_code_t.START_DOCUMENT;
12 import static org.robolectric.res.android.ResourceTypes.RES_STRING_POOL_TYPE;
13 import static org.robolectric.res.android.ResourceTypes.RES_XML_FIRST_CHUNK_TYPE;
14 import static org.robolectric.res.android.ResourceTypes.RES_XML_LAST_CHUNK_TYPE;
15 import static org.robolectric.res.android.ResourceTypes.RES_XML_RESOURCE_MAP_TYPE;
16 import static org.robolectric.res.android.ResourceTypes.RES_XML_START_ELEMENT_TYPE;
17 import static org.robolectric.res.android.ResourceTypes.validate_chunk;
18 import static org.robolectric.res.android.Util.ALOGI;
19 import static org.robolectric.res.android.Util.ALOGW;
20 import static org.robolectric.res.android.Util.SIZEOF_INT;
21 import static org.robolectric.res.android.Util.dtohl;
22 import static org.robolectric.res.android.Util.dtohs;
23 import static org.robolectric.res.android.Util.isTruthy;
24 
25 import java.nio.ByteBuffer;
26 import java.nio.ByteOrder;
27 import java.util.concurrent.atomic.AtomicInteger;
28 import org.robolectric.res.android.ResourceTypes.ResChunk_header;
29 import org.robolectric.res.android.ResourceTypes.ResXMLTree_attrExt;
30 import org.robolectric.res.android.ResourceTypes.ResXMLTree_header;
31 import org.robolectric.res.android.ResourceTypes.ResXMLTree_node;
32 
33 public class ResXMLTree {
34 
35   final DynamicRefTable mDynamicRefTable;
36   public final ResXMLParser mParser;
37 
38   int mError;
39   byte[] mOwnedData;
40   XmlBuffer mBuffer;
41   ResXMLTree_header mHeader;
42   int mSize;
43   //    final uint8_t*              mDataEnd;
44   int mDataLen;
45   ResStringPool mStrings = new ResStringPool();
46   int[] mResIds;
47   int mNumResIds;
48   ResXMLTree_node mRootNode;
49   int mRootExt;
50   int mRootCode;
51 
52   static volatile AtomicInteger gCount = new AtomicInteger(0);
53 
ResXMLTree(DynamicRefTable dynamicRefTable)54   public ResXMLTree(DynamicRefTable dynamicRefTable) {
55     mParser = new ResXMLParser(this);
56 
57     mDynamicRefTable = dynamicRefTable;
58     mError = NO_INIT;
59     mOwnedData = null;
60 
61     if (kDebugResXMLTree) {
62       ALOGI("Creating ResXMLTree %s #%d\n", this, gCount.getAndIncrement() + 1);
63     }
64     mParser.restart();
65   }
66 
67   //  ResXMLTree() {
68   //    this(null);
69   //  }
70 
71   //  ~ResXMLTree()
72   //  {
73   @Override
finalize()74   protected void finalize() {
75     if (kDebugResXMLTree) {
76       ALOGI("Destroying ResXMLTree in %s #%d\n", this, gCount.getAndDecrement() - 1);
77     }
78     uninit();
79   }
80 
setTo(byte[] data, int size, boolean copyData)81   public int setTo(byte[] data, int size, boolean copyData) {
82     uninit();
83     mParser.mEventCode = START_DOCUMENT;
84 
85     if (!isTruthy(data) || !isTruthy(size)) {
86       return (mError = BAD_TYPE);
87     }
88 
89     if (copyData) {
90       mOwnedData = new byte[size];
91       //      if (mOwnedData == null) {
92       //        return (mError=NO_MEMORY);
93       //      }
94       //      memcpy(mOwnedData, data, size);
95       System.arraycopy(data, 0, mOwnedData, 0, size);
96       data = mOwnedData;
97     }
98 
99     mBuffer = new XmlBuffer(data);
100     mHeader = new ResXMLTree_header(mBuffer.buf, 0);
101     mSize = dtohl(mHeader.header.size);
102     if (dtohs(mHeader.header.headerSize) > mSize || mSize > size) {
103       ALOGW(
104           "Bad XML block: header size %d or total size %d is larger than data size %d\n",
105           (int) dtohs(mHeader.header.headerSize), (int) dtohl(mHeader.header.size), (int) size);
106       mError = BAD_TYPE;
107       mParser.restart();
108       return mError;
109     }
110     //    mDataEnd = ((final uint8_t*)mHeader) + mSize;
111     mDataLen = mSize;
112 
113     mStrings.uninit();
114     mRootNode = null;
115     mResIds = null;
116     mNumResIds = 0;
117 
118     // First look for a couple interesting chunks: the string block
119     // and first XML node.
120     ResChunk_header chunk =
121         //      (final ResChunk_header*)(((final uint8_t*)mHeader) +
122         // dtohs(mHeader.header.headerSize));
123         new ResChunk_header(mBuffer.buf, mHeader.header.headerSize);
124 
125     ResChunk_header lastChunk = chunk;
126     while (chunk.myOffset() /*((final uint8_t*)chunk)*/
127             < (mDataLen - ResChunk_header.SIZEOF /*sizeof(ResChunk_header)*/)
128         && chunk.myOffset() /*((final uint8_t*)chunk)*/ < (mDataLen - dtohl(chunk.size))) {
129       int err =
130           validate_chunk(
131               chunk, ResChunk_header.SIZEOF /*sizeof(ResChunk_header)*/, mDataLen, "XML");
132       if (err != NO_ERROR) {
133         mError = err;
134         //          goto done;
135         mParser.restart();
136         return mError;
137       }
138       final short type = dtohs(chunk.type);
139       final int size1 = dtohl(chunk.size);
140       if (kDebugXMLNoisy) {
141         //        System.out.println(String.format("Scanning @ %s: type=0x%x, size=0x%zx\n",
142         //            (void*)(((uintptr_t)chunk)-((uintptr_t)mHeader)), type, size1);
143       }
144       if (type == RES_STRING_POOL_TYPE) {
145         mStrings.setTo(mBuffer.buf, chunk.myOffset(), size, false);
146       } else if (type == RES_XML_RESOURCE_MAP_TYPE) {
147         //        mResIds = (final int*)
148         //        (((final uint8_t*)chunk)+dtohs(chunk.headerSize()));
149         mNumResIds = (dtohl(chunk.size) - dtohs(chunk.headerSize)) / SIZEOF_INT /*sizeof(int)*/;
150         mResIds = new int[mNumResIds];
151         for (int i = 0; i < mNumResIds; i++) {
152           mResIds[i] = mBuffer.buf.getInt(chunk.myOffset() + chunk.headerSize + i * SIZEOF_INT);
153         }
154       } else if (type >= RES_XML_FIRST_CHUNK_TYPE && type <= RES_XML_LAST_CHUNK_TYPE) {
155         if (validateNode(new ResXMLTree_node(mBuffer.buf, chunk)) != NO_ERROR) {
156           mError = BAD_TYPE;
157           //          goto done;
158           mParser.restart();
159           return mError;
160         }
161         mParser.mCurNode = new ResXMLTree_node(mBuffer.buf, lastChunk.myOffset());
162         if (mParser.nextNode() == BAD_DOCUMENT) {
163           mError = BAD_TYPE;
164           //          goto done;
165           mParser.restart();
166           return mError;
167         }
168         mRootNode = mParser.mCurNode;
169         mRootExt = mParser.mCurExt;
170         mRootCode = mParser.mEventCode;
171         break;
172       } else {
173         if (kDebugXMLNoisy) {
174           System.out.println("Skipping unknown chunk!\n");
175         }
176       }
177       lastChunk = chunk;
178       //      chunk = (final ResChunk_header*)
179       //      (((final uint8_t*)chunk) + size1);
180       chunk = new ResChunk_header(mBuffer.buf, chunk.myOffset() + size1);
181     }
182 
183     if (mRootNode == null) {
184       ALOGW("Bad XML block: no root element node found\n");
185       mError = BAD_TYPE;
186       //          goto done;
187       mParser.restart();
188       return mError;
189     }
190 
191     mError = mStrings.getError();
192 
193     done:
194     mParser.restart();
195     return mError;
196   }
197 
getError()198   public int getError() {
199     return mError;
200   }
201 
uninit()202   void uninit() {
203     mError = NO_INIT;
204     mStrings.uninit();
205     if (isTruthy(mOwnedData)) {
206       //      free(mOwnedData);
207       mOwnedData = null;
208     }
209     mParser.restart();
210   }
211 
validateNode(final ResXMLTree_node node)212   int validateNode(final ResXMLTree_node node) {
213     final short eventCode = dtohs(node.header.type);
214 
215     int err =
216         validate_chunk(
217             node.header,
218             SIZEOF_RESXMLTREE_NODE /*sizeof(ResXMLTree_node)*/,
219             mDataLen,
220             "ResXMLTree_node");
221 
222     if (err >= NO_ERROR) {
223       // Only perform additional validation on START nodes
224       if (eventCode != RES_XML_START_ELEMENT_TYPE) {
225         return NO_ERROR;
226       }
227 
228       final short headerSize = dtohs(node.header.headerSize);
229       final int size = dtohl(node.header.size);
230       //        final ResXMLTree_attrExt attrExt = (final ResXMLTree_attrExt*)
231       //      (((final uint8_t*)node) + headerSize);
232       ResXMLTree_attrExt attrExt =
233           new ResXMLTree_attrExt(mBuffer.buf, node.myOffset() + headerSize);
234       // check for sensical values pulled out of the stream so far...
235       if ((size >= headerSize + SIZEOF_RESXMLTREE_ATTR_EXT /*sizeof(ResXMLTree_attrExt)*/)
236           && (attrExt.myOffset() > node.myOffset())) {
237         final int attrSize = ((int) dtohs(attrExt.attributeSize)) * dtohs(attrExt.attributeCount);
238         if ((dtohs(attrExt.attributeStart) + attrSize) <= (size - headerSize)) {
239           return NO_ERROR;
240         }
241         ALOGW(
242             "Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n",
243             (int) (dtohs(attrExt.attributeStart) + attrSize), (int) (size - headerSize));
244       } else {
245         ALOGW(
246             "Bad XML start block: node header size 0x%x, size 0x%x\n",
247             (int) headerSize, (int) size);
248       }
249       return BAD_TYPE;
250     }
251 
252     return err;
253 
254     //    if (false) {
255     //      final boolean isStart = dtohs(node.header().type()) == RES_XML_START_ELEMENT_TYPE;
256     //
257     //      final short headerSize = dtohs(node.header().headerSize());
258     //      final int size = dtohl(node.header().size());
259     //
260     //      if (headerSize >= (isStart ? sizeof(ResXMLTree_attrNode) : sizeof(ResXMLTree_node))) {
261     //        if (size >= headerSize) {
262     //          if ((( final uint8_t*)node) <=(mDataEnd - size)){
263     //            if (!isStart) {
264     //              return NO_ERROR;
265     //            }
266     //            if ((((int) dtohs(node.attributeSize)) * dtohs(node.attributeCount))
267     //                <= (size - headerSize)) {
268     //              return NO_ERROR;
269     //            }
270     //            ALOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n",
271     //                ((int) dtohs(node.attributeSize)) * dtohs(node.attributeCount),
272     //                (int) (size - headerSize));
273     //            return BAD_TYPE;
274     //          }
275     //          ALOGW("Bad XML block: node at 0x%x extends beyond data end 0x%x\n",
276     //              (int) ((( final uint8_t*)node)-(( final uint8_t*)mHeader)),(int) mSize);
277     //          return BAD_TYPE;
278     //        }
279     //        ALOGW("Bad XML block: node at 0x%x header size 0x%x smaller than total size 0x%x\n",
280     //            (int) ((( final uint8_t*)node)-(( final uint8_t*)mHeader)),
281     //        (int) headerSize, (int) size);
282     //        return BAD_TYPE;
283     //      }
284     //      ALOGW("Bad XML block: node at 0x%x header size 0x%x too small\n",
285     //          (int) ((( final uint8_t*)node)-(( final uint8_t*)mHeader)),
286     //      (int) headerSize);
287     //      return BAD_TYPE;
288     //    }
289   }
290 
getStrings()291   public ResStringPool getStrings() {
292     return mParser.getStrings();
293   }
294 
295   static class XmlBuffer {
296     final ByteBuffer buf;
297 
XmlBuffer(byte[] data)298     public XmlBuffer(byte[] data) {
299       this.buf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
300     }
301   }
302 }
303