• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package android.app.assist;
2 
3 import android.app.Activity;
4 import android.content.ComponentName;
5 import android.graphics.Matrix;
6 import android.graphics.Rect;
7 import android.os.BadParcelableException;
8 import android.os.Binder;
9 import android.os.Bundle;
10 import android.os.IBinder;
11 import android.os.Parcel;
12 import android.os.Parcelable;
13 import android.os.PooledStringReader;
14 import android.os.PooledStringWriter;
15 import android.os.RemoteException;
16 import android.os.SystemClock;
17 import android.text.TextUtils;
18 import android.util.Log;
19 import android.view.View;
20 import android.view.ViewStructure;
21 import android.view.ViewRootImpl;
22 import android.view.WindowManager;
23 import android.view.WindowManagerGlobal;
24 
25 import java.util.ArrayList;
26 
27 /**
28  * Assist data automatically created by the platform's implementation
29  * of {@link android.app.Activity#onProvideAssistData}.
30  */
31 public class AssistStructure implements Parcelable {
32     static final String TAG = "AssistStructure";
33 
34     static final boolean DEBUG_PARCEL = false;
35     static final boolean DEBUG_PARCEL_CHILDREN = false;
36     static final boolean DEBUG_PARCEL_TREE = false;
37 
38     static final int VALIDATE_WINDOW_TOKEN = 0x11111111;
39     static final int VALIDATE_VIEW_TOKEN = 0x22222222;
40 
41     boolean mHaveData;
42 
43     ComponentName mActivityComponent;
44 
45     final ArrayList<WindowNode> mWindowNodes = new ArrayList<>();
46 
47     final ArrayList<ViewNodeBuilder> mPendingAsyncChildren = new ArrayList<>();
48 
49     SendChannel mSendChannel;
50     IBinder mReceiveChannel;
51 
52     Rect mTmpRect = new Rect();
53 
54     static final int TRANSACTION_XFER = Binder.FIRST_CALL_TRANSACTION+1;
55     static final String DESCRIPTOR = "android.app.AssistStructure";
56 
57     final static class SendChannel extends Binder {
58         volatile AssistStructure mAssistStructure;
59 
SendChannel(AssistStructure as)60         SendChannel(AssistStructure as) {
61             mAssistStructure = as;
62         }
63 
onTransact(int code, Parcel data, Parcel reply, int flags)64         @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
65                 throws RemoteException {
66             if (code == TRANSACTION_XFER) {
67                 AssistStructure as = mAssistStructure;
68                 if (as == null) {
69                     return true;
70                 }
71 
72                 data.enforceInterface(DESCRIPTOR);
73                 IBinder token = data.readStrongBinder();
74                 if (DEBUG_PARCEL) Log.d(TAG, "Request for data on " + as
75                         + " using token " + token);
76                 if (token != null) {
77                     if (DEBUG_PARCEL) Log.d(TAG, "Resuming partial write of " + token);
78                     if (token instanceof ParcelTransferWriter) {
79                         ParcelTransferWriter xfer = (ParcelTransferWriter)token;
80                         xfer.writeToParcel(as, reply);
81                         return true;
82                     }
83                     Log.w(TAG, "Caller supplied bad token type: " + token);
84                     // Don't write anything; this is the end of the data.
85                     return true;
86                 }
87                 //long start = SystemClock.uptimeMillis();
88                 ParcelTransferWriter xfer = new ParcelTransferWriter(as, reply);
89                 xfer.writeToParcel(as, reply);
90                 //Log.i(TAG, "Time to parcel: " + (SystemClock.uptimeMillis()-start) + "ms");
91                 return true;
92             } else {
93                 return super.onTransact(code, data, reply, flags);
94             }
95         }
96     }
97 
98     final static class ViewStackEntry {
99         ViewNode node;
100         int curChild;
101         int numChildren;
102     }
103 
104     final static class ParcelTransferWriter extends Binder {
105         final boolean mWriteStructure;
106         int mCurWindow;
107         int mNumWindows;
108         final ArrayList<ViewStackEntry> mViewStack = new ArrayList<>();
109         ViewStackEntry mCurViewStackEntry;
110         int mCurViewStackPos;
111         int mNumWrittenWindows;
112         int mNumWrittenViews;
113         final float[] mTmpMatrix = new float[9];
114 
ParcelTransferWriter(AssistStructure as, Parcel out)115         ParcelTransferWriter(AssistStructure as, Parcel out) {
116             mWriteStructure = as.waitForReady();
117             ComponentName.writeToParcel(as.mActivityComponent, out);
118             mNumWindows = as.mWindowNodes.size();
119             if (mWriteStructure && mNumWindows > 0) {
120                 out.writeInt(mNumWindows);
121             } else {
122                 out.writeInt(0);
123             }
124         }
125 
writeToParcel(AssistStructure as, Parcel out)126         void writeToParcel(AssistStructure as, Parcel out) {
127             int start = out.dataPosition();
128             mNumWrittenWindows = 0;
129             mNumWrittenViews = 0;
130             boolean more = writeToParcelInner(as, out);
131             Log.i(TAG, "Flattened " + (more ? "partial" : "final") + " assist data: "
132                     + (out.dataPosition() - start)
133                     + " bytes, containing " + mNumWrittenWindows + " windows, "
134                     + mNumWrittenViews + " views");
135         }
136 
writeToParcelInner(AssistStructure as, Parcel out)137         boolean writeToParcelInner(AssistStructure as, Parcel out) {
138             if (mNumWindows == 0) {
139                 return false;
140             }
141             if (DEBUG_PARCEL) Log.d(TAG, "Creating PooledStringWriter @ " + out.dataPosition());
142             PooledStringWriter pwriter = new PooledStringWriter(out);
143             while (writeNextEntryToParcel(as, out, pwriter)) {
144                 // If the parcel is above the IPC limit, then we are getting too
145                 // large for a single IPC so stop here and let the caller come back when it
146                 // is ready for more.
147                 if (out.dataSize() > IBinder.MAX_IPC_SIZE) {
148                     if (DEBUG_PARCEL) Log.d(TAG, "Assist data size is " + out.dataSize()
149                             + " @ pos " + out.dataPosition() + "; returning partial result");
150                     out.writeInt(0);
151                     out.writeStrongBinder(this);
152                     if (DEBUG_PARCEL) Log.d(TAG, "Finishing PooledStringWriter @ "
153                             + out.dataPosition() + ", size " + pwriter.getStringCount());
154                     pwriter.finish();
155                     return true;
156                 }
157             }
158             if (DEBUG_PARCEL) Log.d(TAG, "Finishing PooledStringWriter @ "
159                     + out.dataPosition() + ", size " + pwriter.getStringCount());
160             pwriter.finish();
161             mViewStack.clear();
162             return false;
163         }
164 
pushViewStackEntry(ViewNode node, int pos)165         void pushViewStackEntry(ViewNode node, int pos) {
166             ViewStackEntry entry;
167             if (pos >= mViewStack.size()) {
168                 entry = new ViewStackEntry();
169                 mViewStack.add(entry);
170                 if (DEBUG_PARCEL_TREE) Log.d(TAG, "New stack entry at " + pos + ": " + entry);
171             } else {
172                 entry = mViewStack.get(pos);
173                 if (DEBUG_PARCEL_TREE) Log.d(TAG, "Existing stack entry at " + pos + ": " + entry);
174             }
175             entry.node = node;
176             entry.numChildren = node.getChildCount();
177             entry.curChild = 0;
178             mCurViewStackEntry = entry;
179         }
180 
writeView(ViewNode child, Parcel out, PooledStringWriter pwriter, int levelAdj)181         void writeView(ViewNode child, Parcel out, PooledStringWriter pwriter, int levelAdj) {
182             if (DEBUG_PARCEL) Log.d(TAG, "write view: at " + out.dataPosition()
183                     + ", windows=" + mNumWrittenWindows
184                     + ", views=" + mNumWrittenViews
185                     + ", level=" + (mCurViewStackPos+levelAdj));
186             out.writeInt(VALIDATE_VIEW_TOKEN);
187             int flags = child.writeSelfToParcel(out, pwriter, mTmpMatrix);
188             mNumWrittenViews++;
189             // If the child has children, push it on the stack to write them next.
190             if ((flags&ViewNode.FLAGS_HAS_CHILDREN) != 0) {
191                 if (DEBUG_PARCEL_TREE || DEBUG_PARCEL_CHILDREN) Log.d(TAG,
192                         "Preparing to write " + child.mChildren.length
193                                 + " children: @ #" + mNumWrittenViews
194                                 + ", level " + (mCurViewStackPos+levelAdj));
195                 out.writeInt(child.mChildren.length);
196                 int pos = ++mCurViewStackPos;
197                 pushViewStackEntry(child, pos);
198             }
199         }
200 
writeNextEntryToParcel(AssistStructure as, Parcel out, PooledStringWriter pwriter)201         boolean writeNextEntryToParcel(AssistStructure as, Parcel out, PooledStringWriter pwriter) {
202             // Write next view node if appropriate.
203             if (mCurViewStackEntry != null) {
204                 if (mCurViewStackEntry.curChild < mCurViewStackEntry.numChildren) {
205                     // Write the next child in the current view.
206                     if (DEBUG_PARCEL_TREE) Log.d(TAG, "Writing child #"
207                             + mCurViewStackEntry.curChild + " in " + mCurViewStackEntry.node);
208                     ViewNode child = mCurViewStackEntry.node.mChildren[mCurViewStackEntry.curChild];
209                     mCurViewStackEntry.curChild++;
210                     writeView(child, out, pwriter, 1);
211                     return true;
212                 }
213 
214                 // We are done writing children of the current view; pop off the stack.
215                 do {
216                     int pos = --mCurViewStackPos;
217                     if (DEBUG_PARCEL_TREE) Log.d(TAG, "Done with " + mCurViewStackEntry.node
218                             + "; popping up to " + pos);
219                     if (pos < 0) {
220                         // Reached the last view; step to next window.
221                         if (DEBUG_PARCEL_TREE) Log.d(TAG, "Done with view hierarchy!");
222                         mCurViewStackEntry = null;
223                         break;
224                     }
225                     mCurViewStackEntry = mViewStack.get(pos);
226                 } while (mCurViewStackEntry.curChild >= mCurViewStackEntry.numChildren);
227                 return true;
228             }
229 
230             // Write the next window if appropriate.
231             int pos = mCurWindow;
232             if (pos < mNumWindows) {
233                 WindowNode win = as.mWindowNodes.get(pos);
234                 mCurWindow++;
235                 if (DEBUG_PARCEL) Log.d(TAG, "write window #" + pos + ": at " + out.dataPosition()
236                         + ", windows=" + mNumWrittenWindows
237                         + ", views=" + mNumWrittenViews);
238                 out.writeInt(VALIDATE_WINDOW_TOKEN);
239                 win.writeSelfToParcel(out, pwriter, mTmpMatrix);
240                 mNumWrittenWindows++;
241                 ViewNode root = win.mRoot;
242                 mCurViewStackPos = 0;
243                 if (DEBUG_PARCEL_TREE) Log.d(TAG, "Writing initial root view " + root);
244                 writeView(root, out, pwriter, 0);
245                 return true;
246             }
247 
248             return false;
249         }
250     }
251 
252     final class ParcelTransferReader {
253         final float[] mTmpMatrix = new float[9];
254         PooledStringReader mStringReader;
255 
256         int mNumReadWindows;
257         int mNumReadViews;
258 
259         private final IBinder mChannel;
260         private IBinder mTransferToken;
261         private Parcel mCurParcel;
262 
ParcelTransferReader(IBinder channel)263         ParcelTransferReader(IBinder channel) {
264             mChannel = channel;
265         }
266 
go()267         void go() {
268             fetchData();
269             mActivityComponent = ComponentName.readFromParcel(mCurParcel);
270             final int N = mCurParcel.readInt();
271             if (N > 0) {
272                 if (DEBUG_PARCEL) Log.d(TAG, "Creating PooledStringReader @ "
273                         + mCurParcel.dataPosition());
274                 mStringReader = new PooledStringReader(mCurParcel);
275                 if (DEBUG_PARCEL) Log.d(TAG, "PooledStringReader size = "
276                         + mStringReader.getStringCount());
277                 for (int i=0; i<N; i++) {
278                     mWindowNodes.add(new WindowNode(this));
279                 }
280             }
281             if (DEBUG_PARCEL) Log.d(TAG, "Finished reading: at " + mCurParcel.dataPosition()
282                     + ", avail=" + mCurParcel.dataAvail() + ", windows=" + mNumReadWindows
283                     + ", views=" + mNumReadViews);
284         }
285 
readParcel(int validateToken, int level)286         Parcel readParcel(int validateToken, int level) {
287             if (DEBUG_PARCEL) Log.d(TAG, "readParcel: at " + mCurParcel.dataPosition()
288                     + ", avail=" + mCurParcel.dataAvail() + ", windows=" + mNumReadWindows
289                     + ", views=" + mNumReadViews + ", level=" + level);
290             int token = mCurParcel.readInt();
291             if (token != 0) {
292                 if (token != validateToken) {
293                     throw new BadParcelableException("Got token " + Integer.toHexString(token)
294                             + ", expected token " + Integer.toHexString(validateToken));
295                 }
296                 return mCurParcel;
297             }
298             // We have run out of partial data, need to read another batch.
299             mTransferToken = mCurParcel.readStrongBinder();
300             if (mTransferToken == null) {
301                 throw new IllegalStateException(
302                         "Reached end of partial data without transfer token");
303             }
304             if (DEBUG_PARCEL) Log.d(TAG, "Ran out of partial data at "
305                     + mCurParcel.dataPosition() + ", token " + mTransferToken);
306             fetchData();
307             if (DEBUG_PARCEL) Log.d(TAG, "Creating PooledStringReader @ "
308                     + mCurParcel.dataPosition());
309             mStringReader = new PooledStringReader(mCurParcel);
310             if (DEBUG_PARCEL) Log.d(TAG, "PooledStringReader size = "
311                     + mStringReader.getStringCount());
312             if (DEBUG_PARCEL) Log.d(TAG, "readParcel: at " + mCurParcel.dataPosition()
313                     + ", avail=" + mCurParcel.dataAvail() + ", windows=" + mNumReadWindows
314                     + ", views=" + mNumReadViews);
315             mCurParcel.readInt();
316             return mCurParcel;
317         }
318 
fetchData()319         private void fetchData() {
320             Parcel data = Parcel.obtain();
321             data.writeInterfaceToken(DESCRIPTOR);
322             data.writeStrongBinder(mTransferToken);
323             if (DEBUG_PARCEL) Log.d(TAG, "Requesting data with token " + mTransferToken);
324             if (mCurParcel != null) {
325                 mCurParcel.recycle();
326             }
327             mCurParcel = Parcel.obtain();
328             try {
329                 mChannel.transact(TRANSACTION_XFER, data, mCurParcel, 0);
330             } catch (RemoteException e) {
331                 Log.w(TAG, "Failure reading AssistStructure data", e);
332                 throw new IllegalStateException("Failure reading AssistStructure data: " + e);
333             }
334             data.recycle();
335             mNumReadWindows = mNumReadViews = 0;
336         }
337     }
338 
339     final static class ViewNodeText {
340         CharSequence mText;
341         float mTextSize;
342         int mTextStyle;
343         int mTextColor = ViewNode.TEXT_COLOR_UNDEFINED;
344         int mTextBackgroundColor = ViewNode.TEXT_COLOR_UNDEFINED;
345         int mTextSelectionStart;
346         int mTextSelectionEnd;
347         int[] mLineCharOffsets;
348         int[] mLineBaselines;
349         String mHint;
350 
ViewNodeText()351         ViewNodeText() {
352         }
353 
isSimple()354         boolean isSimple() {
355             return mTextBackgroundColor == ViewNode.TEXT_COLOR_UNDEFINED
356                     && mTextSelectionStart == 0 && mTextSelectionEnd == 0
357                     && mLineCharOffsets == null && mLineBaselines == null && mHint == null;
358         }
359 
ViewNodeText(Parcel in, boolean simple)360         ViewNodeText(Parcel in, boolean simple) {
361             mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
362             mTextSize = in.readFloat();
363             mTextStyle = in.readInt();
364             mTextColor = in.readInt();
365             if (!simple) {
366                 mTextBackgroundColor = in.readInt();
367                 mTextSelectionStart = in.readInt();
368                 mTextSelectionEnd = in.readInt();
369                 mLineCharOffsets = in.createIntArray();
370                 mLineBaselines = in.createIntArray();
371                 mHint = in.readString();
372             }
373         }
374 
writeToParcel(Parcel out, boolean simple)375         void writeToParcel(Parcel out, boolean simple) {
376             TextUtils.writeToParcel(mText, out, 0);
377             out.writeFloat(mTextSize);
378             out.writeInt(mTextStyle);
379             out.writeInt(mTextColor);
380             if (!simple) {
381                 out.writeInt(mTextBackgroundColor);
382                 out.writeInt(mTextSelectionStart);
383                 out.writeInt(mTextSelectionEnd);
384                 out.writeIntArray(mLineCharOffsets);
385                 out.writeIntArray(mLineBaselines);
386                 out.writeString(mHint);
387             }
388         }
389     }
390 
391     /**
392      * Describes a window in the assist data.
393      */
394     static public class WindowNode {
395         final int mX;
396         final int mY;
397         final int mWidth;
398         final int mHeight;
399         final CharSequence mTitle;
400         final int mDisplayId;
401         final ViewNode mRoot;
402 
WindowNode(AssistStructure assist, ViewRootImpl root)403         WindowNode(AssistStructure assist, ViewRootImpl root) {
404             View view = root.getView();
405             Rect rect = new Rect();
406             view.getBoundsOnScreen(rect);
407             mX = rect.left - view.getLeft();
408             mY = rect.top - view.getTop();
409             mWidth = rect.width();
410             mHeight = rect.height();
411             mTitle = root.getTitle();
412             mDisplayId = root.getDisplayId();
413             mRoot = new ViewNode();
414             ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false);
415             if ((root.getWindowFlags()& WindowManager.LayoutParams.FLAG_SECURE) != 0) {
416                 // This is a secure window, so it doesn't want a screenshot, and that
417                 // means we should also not copy out its view hierarchy.
418                 view.onProvideStructure(builder);
419                 builder.setAssistBlocked(true);
420                 return;
421             }
422             view.dispatchProvideStructure(builder);
423         }
424 
WindowNode(ParcelTransferReader reader)425         WindowNode(ParcelTransferReader reader) {
426             Parcel in = reader.readParcel(VALIDATE_WINDOW_TOKEN, 0);
427             reader.mNumReadWindows++;
428             mX = in.readInt();
429             mY = in.readInt();
430             mWidth = in.readInt();
431             mHeight = in.readInt();
432             mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
433             mDisplayId = in.readInt();
434             mRoot = new ViewNode(reader, 0);
435         }
436 
writeSelfToParcel(Parcel out, PooledStringWriter pwriter, float[] tmpMatrix)437         void writeSelfToParcel(Parcel out, PooledStringWriter pwriter, float[] tmpMatrix) {
438             out.writeInt(mX);
439             out.writeInt(mY);
440             out.writeInt(mWidth);
441             out.writeInt(mHeight);
442             TextUtils.writeToParcel(mTitle, out, 0);
443             out.writeInt(mDisplayId);
444         }
445 
446         /**
447          * Returns the left edge of the window, in pixels, relative to the left
448          * edge of the screen.
449          */
getLeft()450         public int getLeft() {
451             return mX;
452         }
453 
454         /**
455          * Returns the top edge of the window, in pixels, relative to the top
456          * edge of the screen.
457          */
getTop()458         public int getTop() {
459             return mY;
460         }
461 
462         /**
463          * Returns the total width of the window in pixels.
464          */
getWidth()465         public int getWidth() {
466             return mWidth;
467         }
468 
469         /**
470          * Returns the total height of the window in pixels.
471          */
getHeight()472         public int getHeight() {
473             return mHeight;
474         }
475 
476         /**
477          * Returns the title associated with the window, if it has one.
478          */
getTitle()479         public CharSequence getTitle() {
480             return mTitle;
481         }
482 
483         /**
484          * Returns the ID of the display this window is on, for use with
485          * {@link android.hardware.display.DisplayManager#getDisplay DisplayManager.getDisplay()}.
486          */
getDisplayId()487         public int getDisplayId() {
488             return mDisplayId;
489         }
490 
491         /**
492          * Returns the {@link ViewNode} containing the root content of the window.
493          */
getRootViewNode()494         public ViewNode getRootViewNode() {
495             return mRoot;
496         }
497     }
498 
499     /**
500      * Describes a single view in the assist data.
501      */
502     static public class ViewNode {
503         /**
504          * Magic value for text color that has not been defined, which is very unlikely
505          * to be confused with a real text color.
506          */
507         public static final int TEXT_COLOR_UNDEFINED = 1;
508 
509         public static final int TEXT_STYLE_BOLD = 1<<0;
510         public static final int TEXT_STYLE_ITALIC = 1<<1;
511         public static final int TEXT_STYLE_UNDERLINE = 1<<2;
512         public static final int TEXT_STYLE_STRIKE_THRU = 1<<3;
513 
514         int mId = View.NO_ID;
515         String mIdPackage;
516         String mIdType;
517         String mIdEntry;
518         int mX;
519         int mY;
520         int mScrollX;
521         int mScrollY;
522         int mWidth;
523         int mHeight;
524         Matrix mMatrix;
525         float mElevation;
526         float mAlpha = 1.0f;
527 
528         static final int FLAGS_DISABLED = 0x00000001;
529         static final int FLAGS_VISIBILITY_MASK = View.VISIBLE|View.INVISIBLE|View.GONE;
530         static final int FLAGS_FOCUSABLE = 0x00000010;
531         static final int FLAGS_FOCUSED = 0x00000020;
532         static final int FLAGS_SELECTED = 0x00000040;
533         static final int FLAGS_ASSIST_BLOCKED = 0x00000080;
534         static final int FLAGS_CHECKABLE = 0x00000100;
535         static final int FLAGS_CHECKED = 0x00000200;
536         static final int FLAGS_CLICKABLE = 0x00000400;
537         static final int FLAGS_LONG_CLICKABLE = 0x00000800;
538         static final int FLAGS_ACCESSIBILITY_FOCUSED = 0x00001000;
539         static final int FLAGS_ACTIVATED = 0x00002000;
540         static final int FLAGS_CONTEXT_CLICKABLE = 0x00004000;
541 
542         static final int FLAGS_HAS_MATRIX = 0x40000000;
543         static final int FLAGS_HAS_ALPHA = 0x20000000;
544         static final int FLAGS_HAS_ELEVATION = 0x10000000;
545         static final int FLAGS_HAS_SCROLL = 0x08000000;
546         static final int FLAGS_HAS_LARGE_COORDS = 0x04000000;
547         static final int FLAGS_HAS_CONTENT_DESCRIPTION = 0x02000000;
548         static final int FLAGS_HAS_TEXT = 0x01000000;
549         static final int FLAGS_HAS_COMPLEX_TEXT = 0x00800000;
550         static final int FLAGS_HAS_EXTRAS = 0x00400000;
551         static final int FLAGS_HAS_ID = 0x00200000;
552         static final int FLAGS_HAS_CHILDREN = 0x00100000;
553         static final int FLAGS_ALL_CONTROL = 0xfff00000;
554 
555         int mFlags;
556 
557         String mClassName;
558         CharSequence mContentDescription;
559 
560         ViewNodeText mText;
561         Bundle mExtras;
562 
563         ViewNode[] mChildren;
564 
ViewNode()565         ViewNode() {
566         }
567 
ViewNode(ParcelTransferReader reader, int nestingLevel)568         ViewNode(ParcelTransferReader reader, int nestingLevel) {
569             final Parcel in = reader.readParcel(VALIDATE_VIEW_TOKEN, nestingLevel);
570             reader.mNumReadViews++;
571             final PooledStringReader preader = reader.mStringReader;
572             mClassName = preader.readString();
573             mFlags = in.readInt();
574             final int flags = mFlags;
575             if ((flags&FLAGS_HAS_ID) != 0) {
576                 mId = in.readInt();
577                 if (mId != 0) {
578                     mIdEntry = preader.readString();
579                     if (mIdEntry != null) {
580                         mIdType = preader.readString();
581                         mIdPackage = preader.readString();
582                     }
583                 }
584             }
585             if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
586                 mX = in.readInt();
587                 mY = in.readInt();
588                 mWidth = in.readInt();
589                 mHeight = in.readInt();
590             } else {
591                 int val = in.readInt();
592                 mX = val&0x7fff;
593                 mY = (val>>16)&0x7fff;
594                 val = in.readInt();
595                 mWidth = val&0x7fff;
596                 mHeight = (val>>16)&0x7fff;
597             }
598             if ((flags&FLAGS_HAS_SCROLL) != 0) {
599                 mScrollX = in.readInt();
600                 mScrollY = in.readInt();
601             }
602             if ((flags&FLAGS_HAS_MATRIX) != 0) {
603                 mMatrix = new Matrix();
604                 in.readFloatArray(reader.mTmpMatrix);
605                 mMatrix.setValues(reader.mTmpMatrix);
606             }
607             if ((flags&FLAGS_HAS_ELEVATION) != 0) {
608                 mElevation = in.readFloat();
609             }
610             if ((flags&FLAGS_HAS_ALPHA) != 0) {
611                 mAlpha = in.readFloat();
612             }
613             if ((flags&FLAGS_HAS_CONTENT_DESCRIPTION) != 0) {
614                 mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
615             }
616             if ((flags&FLAGS_HAS_TEXT) != 0) {
617                 mText = new ViewNodeText(in, (flags&FLAGS_HAS_COMPLEX_TEXT) == 0);
618             }
619             if ((flags&FLAGS_HAS_EXTRAS) != 0) {
620                 mExtras = in.readBundle();
621             }
622             if ((flags&FLAGS_HAS_CHILDREN) != 0) {
623                 final int NCHILDREN = in.readInt();
624                 if (DEBUG_PARCEL_TREE || DEBUG_PARCEL_CHILDREN) Log.d(TAG,
625                         "Preparing to read " + NCHILDREN
626                                 + " children: @ #" + reader.mNumReadViews
627                                 + ", level " + nestingLevel);
628                 mChildren = new ViewNode[NCHILDREN];
629                 for (int i=0; i<NCHILDREN; i++) {
630                     mChildren[i] = new ViewNode(reader, nestingLevel + 1);
631                 }
632             }
633         }
634 
writeSelfToParcel(Parcel out, PooledStringWriter pwriter, float[] tmpMatrix)635         int writeSelfToParcel(Parcel out, PooledStringWriter pwriter, float[] tmpMatrix) {
636             int flags = mFlags & ~FLAGS_ALL_CONTROL;
637             if (mId != View.NO_ID) {
638                 flags |= FLAGS_HAS_ID;
639             }
640             if ((mX&~0x7fff) != 0 || (mY&~0x7fff) != 0
641                     || (mWidth&~0x7fff) != 0 | (mHeight&~0x7fff) != 0) {
642                 flags |= FLAGS_HAS_LARGE_COORDS;
643             }
644             if (mScrollX != 0 || mScrollY != 0) {
645                 flags |= FLAGS_HAS_SCROLL;
646             }
647             if (mMatrix != null) {
648                 flags |= FLAGS_HAS_MATRIX;
649             }
650             if (mElevation != 0) {
651                 flags |= FLAGS_HAS_ELEVATION;
652             }
653             if (mAlpha != 1.0f) {
654                 flags |= FLAGS_HAS_ALPHA;
655             }
656             if (mContentDescription != null) {
657                 flags |= FLAGS_HAS_CONTENT_DESCRIPTION;
658             }
659             if (mText != null) {
660                 flags |= FLAGS_HAS_TEXT;
661                 if (!mText.isSimple()) {
662                     flags |= FLAGS_HAS_COMPLEX_TEXT;
663                 }
664             }
665             if (mExtras != null) {
666                 flags |= FLAGS_HAS_EXTRAS;
667             }
668             if (mChildren != null) {
669                 flags |= FLAGS_HAS_CHILDREN;
670             }
671 
672             pwriter.writeString(mClassName);
673             out.writeInt(flags);
674             if ((flags&FLAGS_HAS_ID) != 0) {
675                 out.writeInt(mId);
676                 if (mId != 0) {
677                     pwriter.writeString(mIdEntry);
678                     if (mIdEntry != null) {
679                         pwriter.writeString(mIdType);
680                         pwriter.writeString(mIdPackage);
681                     }
682                 }
683             }
684             if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
685                 out.writeInt(mX);
686                 out.writeInt(mY);
687                 out.writeInt(mWidth);
688                 out.writeInt(mHeight);
689             } else {
690                 out.writeInt((mY<<16) | mX);
691                 out.writeInt((mHeight<<16) | mWidth);
692             }
693             if ((flags&FLAGS_HAS_SCROLL) != 0) {
694                 out.writeInt(mScrollX);
695                 out.writeInt(mScrollY);
696             }
697             if ((flags&FLAGS_HAS_MATRIX) != 0) {
698                 mMatrix.getValues(tmpMatrix);
699                 out.writeFloatArray(tmpMatrix);
700             }
701             if ((flags&FLAGS_HAS_ELEVATION) != 0) {
702                 out.writeFloat(mElevation);
703             }
704             if ((flags&FLAGS_HAS_ALPHA) != 0) {
705                 out.writeFloat(mAlpha);
706             }
707             if ((flags&FLAGS_HAS_CONTENT_DESCRIPTION) != 0) {
708                 TextUtils.writeToParcel(mContentDescription, out, 0);
709             }
710             if ((flags&FLAGS_HAS_TEXT) != 0) {
711                 mText.writeToParcel(out, (flags&FLAGS_HAS_COMPLEX_TEXT) == 0);
712             }
713             if ((flags&FLAGS_HAS_EXTRAS) != 0) {
714                 out.writeBundle(mExtras);
715             }
716             return flags;
717         }
718 
719         /**
720          * Returns the ID associated with this view, as per {@link View#getId() View.getId()}.
721          */
getId()722         public int getId() {
723             return mId;
724         }
725 
726         /**
727          * If {@link #getId()} is a resource identifier, this is the package name of that
728          * identifier.  See {@link android.view.ViewStructure#setId ViewStructure.setId}
729          * for more information.
730          */
getIdPackage()731         public String getIdPackage() {
732             return mIdPackage;
733         }
734 
735         /**
736          * If {@link #getId()} is a resource identifier, this is the type name of that
737          * identifier.  See {@link android.view.ViewStructure#setId ViewStructure.setId}
738          * for more information.
739          */
getIdType()740         public String getIdType() {
741             return mIdType;
742         }
743 
744         /**
745          * If {@link #getId()} is a resource identifier, this is the entry name of that
746          * identifier.  See {@link android.view.ViewStructure#setId ViewStructure.setId}
747          * for more information.
748          */
getIdEntry()749         public String getIdEntry() {
750             return mIdEntry;
751         }
752 
753         /**
754          * Returns the left edge of this view, in pixels, relative to the left edge of its parent.
755          */
getLeft()756         public int getLeft() {
757             return mX;
758         }
759 
760         /**
761          * Returns the top edge of this view, in pixels, relative to the top edge of its parent.
762          */
getTop()763         public int getTop() {
764             return mY;
765         }
766 
767         /**
768          * Returns the current X scroll offset of this view, as per
769          * {@link android.view.View#getScrollX() View.getScrollX()}.
770          */
getScrollX()771         public int getScrollX() {
772             return mScrollX;
773         }
774 
775         /**
776          * Returns the current Y scroll offset of this view, as per
777          * {@link android.view.View#getScrollX() View.getScrollY()}.
778          */
getScrollY()779         public int getScrollY() {
780             return mScrollY;
781         }
782 
783         /**
784          * Returns the width of this view, in pixels.
785          */
getWidth()786         public int getWidth() {
787             return mWidth;
788         }
789 
790         /**
791          * Returns the height of this view, in pixels.
792          */
getHeight()793         public int getHeight() {
794             return mHeight;
795         }
796 
797         /**
798          * Returns the transformation that has been applied to this view, such as a translation
799          * or scaling.  The returned Matrix object is owned by ViewNode; do not modify it.
800          * Returns null if there is no transformation applied to the view.
801          */
getTransformation()802         public Matrix getTransformation() {
803             return mMatrix;
804         }
805 
806         /**
807          * Returns the visual elevation of the view, used for shadowing and other visual
808          * characterstics, as set by {@link ViewStructure#setElevation
809          * ViewStructure.setElevation(float)}.
810          */
getElevation()811         public float getElevation() {
812             return mElevation;
813         }
814 
815         /**
816          * Returns the alpha transformation of the view, used to reduce the overall opacity
817          * of the view's contents, as set by {@link ViewStructure#setAlpha
818          * ViewStructure.setAlpha(float)}.
819          */
getAlpha()820         public float getAlpha() {
821             return mAlpha;
822         }
823 
824         /**
825          * Returns the visibility mode of this view, as per
826          * {@link android.view.View#getVisibility() View.getVisibility()}.
827          */
getVisibility()828         public int getVisibility() {
829             return mFlags&ViewNode.FLAGS_VISIBILITY_MASK;
830         }
831 
832         /**
833          * Returns true if assist data has been blocked starting at this node in the hierarchy.
834          */
isAssistBlocked()835         public boolean isAssistBlocked() {
836             return (mFlags&ViewNode.FLAGS_ASSIST_BLOCKED) != 0;
837         }
838 
839         /**
840          * Returns true if this node is in an enabled state.
841          */
isEnabled()842         public boolean isEnabled() {
843             return (mFlags&ViewNode.FLAGS_DISABLED) == 0;
844         }
845 
846         /**
847          * Returns true if this node is clickable by the user.
848          */
isClickable()849         public boolean isClickable() {
850             return (mFlags&ViewNode.FLAGS_CLICKABLE) != 0;
851         }
852 
853         /**
854          * Returns true if this node can take input focus.
855          */
isFocusable()856         public boolean isFocusable() {
857             return (mFlags&ViewNode.FLAGS_FOCUSABLE) != 0;
858         }
859 
860         /**
861          * Returns true if this node currently had input focus at the time that the
862          * structure was collected.
863          */
isFocused()864         public boolean isFocused() {
865             return (mFlags&ViewNode.FLAGS_FOCUSED) != 0;
866         }
867 
868         /**
869          * Returns true if this node currently had accessibility focus at the time that the
870          * structure was collected.
871          */
isAccessibilityFocused()872         public boolean isAccessibilityFocused() {
873             return (mFlags&ViewNode.FLAGS_ACCESSIBILITY_FOCUSED) != 0;
874         }
875 
876         /**
877          * Returns true if this node represents something that is checkable by the user.
878          */
isCheckable()879         public boolean isCheckable() {
880             return (mFlags&ViewNode.FLAGS_CHECKABLE) != 0;
881         }
882 
883         /**
884          * Returns true if this node is currently in a checked state.
885          */
isChecked()886         public boolean isChecked() {
887             return (mFlags&ViewNode.FLAGS_CHECKED) != 0;
888         }
889 
890         /**
891          * Returns true if this node has currently been selected by the user.
892          */
isSelected()893         public boolean isSelected() {
894             return (mFlags&ViewNode.FLAGS_SELECTED) != 0;
895         }
896 
897         /**
898          * Returns true if this node has currently been activated by the user.
899          */
isActivated()900         public boolean isActivated() {
901             return (mFlags&ViewNode.FLAGS_ACTIVATED) != 0;
902         }
903 
904         /**
905          * Returns true if this node is something the user can perform a long click/press on.
906          */
isLongClickable()907         public boolean isLongClickable() {
908             return (mFlags&ViewNode.FLAGS_LONG_CLICKABLE) != 0;
909         }
910 
911         /**
912          * Returns true if this node is something the user can perform a context click on.
913          */
isContextClickable()914         public boolean isContextClickable() {
915             return (mFlags&ViewNode.FLAGS_CONTEXT_CLICKABLE) != 0;
916         }
917 
918         /**
919          * Returns the class name of the node's implementation, indicating its behavior.
920          * For example, a button will report "android.widget.Button" meaning it behaves
921          * like a {@link android.widget.Button}.
922          */
getClassName()923         public String getClassName() {
924             return mClassName;
925         }
926 
927         /**
928          * Returns any content description associated with the node, which semantically describes
929          * its purpose for accessibility and other uses.
930          */
getContentDescription()931         public CharSequence getContentDescription() {
932             return mContentDescription;
933         }
934 
935         /**
936          * Returns any text associated with the node that is displayed to the user, or null
937          * if there is none.
938          */
getText()939         public CharSequence getText() {
940             return mText != null ? mText.mText : null;
941         }
942 
943         /**
944          * If {@link #getText()} is non-null, this is where the current selection starts.
945          */
getTextSelectionStart()946         public int getTextSelectionStart() {
947             return mText != null ? mText.mTextSelectionStart : -1;
948         }
949 
950         /**
951          * If {@link #getText()} is non-null, this is where the current selection starts.
952          * If there is no selection, returns the same value as {@link #getTextSelectionStart()},
953          * indicating the cursor position.
954          */
getTextSelectionEnd()955         public int getTextSelectionEnd() {
956             return mText != null ? mText.mTextSelectionEnd : -1;
957         }
958 
959         /**
960          * If {@link #getText()} is non-null, this is the main text color associated with it.
961          * If there is no text color, {@link #TEXT_COLOR_UNDEFINED} is returned.
962          * Note that the text may also contain style spans that modify the color of specific
963          * parts of the text.
964          */
getTextColor()965         public int getTextColor() {
966             return mText != null ? mText.mTextColor : TEXT_COLOR_UNDEFINED;
967         }
968 
969         /**
970          * If {@link #getText()} is non-null, this is the main text background color associated
971          * with it.
972          * If there is no text background color, {@link #TEXT_COLOR_UNDEFINED} is returned.
973          * Note that the text may also contain style spans that modify the color of specific
974          * parts of the text.
975          */
getTextBackgroundColor()976         public int getTextBackgroundColor() {
977             return mText != null ? mText.mTextBackgroundColor : TEXT_COLOR_UNDEFINED;
978         }
979 
980         /**
981          * If {@link #getText()} is non-null, this is the main text size (in pixels) associated
982          * with it.
983          * Note that the text may also contain style spans that modify the size of specific
984          * parts of the text.
985          */
getTextSize()986         public float getTextSize() {
987             return mText != null ? mText.mTextSize : 0;
988         }
989 
990         /**
991          * If {@link #getText()} is non-null, this is the main text style associated
992          * with it, containing a bit mask of {@link #TEXT_STYLE_BOLD},
993          * {@link #TEXT_STYLE_BOLD}, {@link #TEXT_STYLE_STRIKE_THRU}, and/or
994          * {@link #TEXT_STYLE_UNDERLINE}.
995          * Note that the text may also contain style spans that modify the style of specific
996          * parts of the text.
997          */
getTextStyle()998         public int getTextStyle() {
999             return mText != null ? mText.mTextStyle : 0;
1000         }
1001 
1002         /**
1003          * Return per-line offsets into the text returned by {@link #getText()}.  Each entry
1004          * in the array is a formatted line of text, and the value it contains is the offset
1005          * into the text string where that line starts.  May return null if there is no line
1006          * information.
1007          */
getTextLineCharOffsets()1008         public int[] getTextLineCharOffsets() {
1009             return mText != null ? mText.mLineCharOffsets : null;
1010         }
1011 
1012         /**
1013          * Return per-line baselines into the text returned by {@link #getText()}.  Each entry
1014          * in the array is a formatted line of text, and the value it contains is the baseline
1015          * where that text appears in the view.  May return null if there is no line
1016          * information.
1017          */
getTextLineBaselines()1018         public int[] getTextLineBaselines() {
1019             return mText != null ? mText.mLineBaselines : null;
1020         }
1021 
1022         /**
1023          * Return additional hint text associated with the node; this is typically used with
1024          * a node that takes user input, describing to the user what the input means.
1025          */
getHint()1026         public String getHint() {
1027             return mText != null ? mText.mHint : null;
1028         }
1029 
1030         /**
1031          * Return a Bundle containing optional vendor-specific extension information.
1032          */
getExtras()1033         public Bundle getExtras() {
1034             return mExtras;
1035         }
1036 
1037         /**
1038          * Return the number of children this node has.
1039          */
getChildCount()1040         public int getChildCount() {
1041             return mChildren != null ? mChildren.length : 0;
1042         }
1043 
1044         /**
1045          * Return a child of this node, given an index value from 0 to
1046          * {@link #getChildCount()}-1.
1047          */
getChildAt(int index)1048         public ViewNode getChildAt(int index) {
1049             return mChildren[index];
1050         }
1051     }
1052 
1053     static class ViewNodeBuilder extends ViewStructure {
1054         final AssistStructure mAssist;
1055         final ViewNode mNode;
1056         final boolean mAsync;
1057 
ViewNodeBuilder(AssistStructure assist, ViewNode node, boolean async)1058         ViewNodeBuilder(AssistStructure assist, ViewNode node, boolean async) {
1059             mAssist = assist;
1060             mNode = node;
1061             mAsync = async;
1062         }
1063 
1064         @Override
setId(int id, String packageName, String typeName, String entryName)1065         public void setId(int id, String packageName, String typeName, String entryName) {
1066             mNode.mId = id;
1067             mNode.mIdPackage = packageName;
1068             mNode.mIdType = typeName;
1069             mNode.mIdEntry = entryName;
1070         }
1071 
1072         @Override
setDimens(int left, int top, int scrollX, int scrollY, int width, int height)1073         public void setDimens(int left, int top, int scrollX, int scrollY, int width, int height) {
1074             mNode.mX = left;
1075             mNode.mY = top;
1076             mNode.mScrollX = scrollX;
1077             mNode.mScrollY = scrollY;
1078             mNode.mWidth = width;
1079             mNode.mHeight = height;
1080         }
1081 
1082         @Override
setTransformation(Matrix matrix)1083         public void setTransformation(Matrix matrix) {
1084             if (matrix == null) {
1085                 mNode.mMatrix = null;
1086             } else {
1087                 mNode.mMatrix = new Matrix(matrix);
1088             }
1089         }
1090 
1091         @Override
setElevation(float elevation)1092         public void setElevation(float elevation) {
1093             mNode.mElevation = elevation;
1094         }
1095 
1096         @Override
setAlpha(float alpha)1097         public void setAlpha(float alpha) {
1098             mNode.mAlpha = alpha;
1099         }
1100 
1101         @Override
setVisibility(int visibility)1102         public void setVisibility(int visibility) {
1103             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_VISIBILITY_MASK) | visibility;
1104         }
1105 
1106         @Override
setAssistBlocked(boolean state)1107         public void setAssistBlocked(boolean state) {
1108             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ASSIST_BLOCKED)
1109                     | (state ? ViewNode.FLAGS_ASSIST_BLOCKED : 0);
1110         }
1111 
1112         @Override
setEnabled(boolean state)1113         public void setEnabled(boolean state) {
1114             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_DISABLED)
1115                     | (state ? 0 : ViewNode.FLAGS_DISABLED);
1116         }
1117 
1118         @Override
setClickable(boolean state)1119         public void setClickable(boolean state) {
1120             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CLICKABLE)
1121                     | (state ? ViewNode.FLAGS_CLICKABLE : 0);
1122         }
1123 
1124         @Override
setLongClickable(boolean state)1125         public void setLongClickable(boolean state) {
1126             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_LONG_CLICKABLE)
1127                     | (state ? ViewNode.FLAGS_LONG_CLICKABLE : 0);
1128         }
1129 
1130         @Override
setContextClickable(boolean state)1131         public void setContextClickable(boolean state) {
1132             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CONTEXT_CLICKABLE)
1133                     | (state ? ViewNode.FLAGS_CONTEXT_CLICKABLE : 0);
1134         }
1135 
1136         @Override
setFocusable(boolean state)1137         public void setFocusable(boolean state) {
1138             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_FOCUSABLE)
1139                     | (state ? ViewNode.FLAGS_FOCUSABLE : 0);
1140         }
1141 
1142         @Override
setFocused(boolean state)1143         public void setFocused(boolean state) {
1144             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_FOCUSED)
1145                     | (state ? ViewNode.FLAGS_FOCUSED : 0);
1146         }
1147 
1148         @Override
setAccessibilityFocused(boolean state)1149         public void setAccessibilityFocused(boolean state) {
1150             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ACCESSIBILITY_FOCUSED)
1151                     | (state ? ViewNode.FLAGS_ACCESSIBILITY_FOCUSED : 0);
1152         }
1153 
1154         @Override
setCheckable(boolean state)1155         public void setCheckable(boolean state) {
1156             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CHECKABLE)
1157                     | (state ? ViewNode.FLAGS_CHECKABLE : 0);
1158         }
1159 
1160         @Override
setChecked(boolean state)1161         public void setChecked(boolean state) {
1162             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CHECKED)
1163                     | (state ? ViewNode.FLAGS_CHECKED : 0);
1164         }
1165 
1166         @Override
setSelected(boolean state)1167         public void setSelected(boolean state) {
1168             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_SELECTED)
1169                     | (state ? ViewNode.FLAGS_SELECTED : 0);
1170         }
1171 
1172         @Override
setActivated(boolean state)1173         public void setActivated(boolean state) {
1174             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ACTIVATED)
1175                     | (state ? ViewNode.FLAGS_ACTIVATED : 0);
1176         }
1177 
1178         @Override
setClassName(String className)1179         public void setClassName(String className) {
1180             mNode.mClassName = className;
1181         }
1182 
1183         @Override
setContentDescription(CharSequence contentDescription)1184         public void setContentDescription(CharSequence contentDescription) {
1185             mNode.mContentDescription = contentDescription;
1186         }
1187 
getNodeText()1188         private final ViewNodeText getNodeText() {
1189             if (mNode.mText != null) {
1190                 return mNode.mText;
1191             }
1192             mNode.mText = new ViewNodeText();
1193             return mNode.mText;
1194         }
1195 
1196         @Override
setText(CharSequence text)1197         public void setText(CharSequence text) {
1198             ViewNodeText t = getNodeText();
1199             t.mText = text;
1200             t.mTextSelectionStart = t.mTextSelectionEnd = -1;
1201         }
1202 
1203         @Override
setText(CharSequence text, int selectionStart, int selectionEnd)1204         public void setText(CharSequence text, int selectionStart, int selectionEnd) {
1205             ViewNodeText t = getNodeText();
1206             t.mText = text;
1207             t.mTextSelectionStart = selectionStart;
1208             t.mTextSelectionEnd = selectionEnd;
1209         }
1210 
1211         @Override
setTextStyle(float size, int fgColor, int bgColor, int style)1212         public void setTextStyle(float size, int fgColor, int bgColor, int style) {
1213             ViewNodeText t = getNodeText();
1214             t.mTextColor = fgColor;
1215             t.mTextBackgroundColor = bgColor;
1216             t.mTextSize = size;
1217             t.mTextStyle = style;
1218         }
1219 
1220         @Override
setTextLines(int[] charOffsets, int[] baselines)1221         public void setTextLines(int[] charOffsets, int[] baselines) {
1222             ViewNodeText t = getNodeText();
1223             t.mLineCharOffsets = charOffsets;
1224             t.mLineBaselines = baselines;
1225         }
1226 
1227         @Override
setHint(CharSequence hint)1228         public void setHint(CharSequence hint) {
1229             getNodeText().mHint = hint != null ? hint.toString() : null;
1230         }
1231 
1232         @Override
getText()1233         public CharSequence getText() {
1234             return mNode.mText != null ? mNode.mText.mText : null;
1235         }
1236 
1237         @Override
getTextSelectionStart()1238         public int getTextSelectionStart() {
1239             return mNode.mText != null ? mNode.mText.mTextSelectionStart : -1;
1240         }
1241 
1242         @Override
getTextSelectionEnd()1243         public int getTextSelectionEnd() {
1244             return mNode.mText != null ? mNode.mText.mTextSelectionEnd : -1;
1245         }
1246 
1247         @Override
getHint()1248         public CharSequence getHint() {
1249             return mNode.mText != null ? mNode.mText.mHint : null;
1250         }
1251 
1252         @Override
getExtras()1253         public Bundle getExtras() {
1254             if (mNode.mExtras != null) {
1255                 return mNode.mExtras;
1256             }
1257             mNode.mExtras = new Bundle();
1258             return mNode.mExtras;
1259         }
1260 
1261         @Override
hasExtras()1262         public boolean hasExtras() {
1263             return mNode.mExtras != null;
1264         }
1265 
1266         @Override
setChildCount(int num)1267         public void setChildCount(int num) {
1268             mNode.mChildren = new ViewNode[num];
1269         }
1270 
1271         @Override
addChildCount(int num)1272         public int addChildCount(int num) {
1273             if (mNode.mChildren == null) {
1274                 setChildCount(num);
1275                 return 0;
1276             }
1277             final int start = mNode.mChildren.length;
1278             ViewNode[] newArray = new ViewNode[start + num];
1279             System.arraycopy(mNode.mChildren, 0, newArray, 0, start);
1280             mNode.mChildren = newArray;
1281             return start;
1282         }
1283 
1284         @Override
getChildCount()1285         public int getChildCount() {
1286             return mNode.mChildren != null ? mNode.mChildren.length : 0;
1287         }
1288 
1289         @Override
newChild(int index)1290         public ViewStructure newChild(int index) {
1291             ViewNode node = new ViewNode();
1292             mNode.mChildren[index] = node;
1293             return new ViewNodeBuilder(mAssist, node, false);
1294         }
1295 
1296         @Override
asyncNewChild(int index)1297         public ViewStructure asyncNewChild(int index) {
1298             synchronized (mAssist) {
1299                 ViewNode node = new ViewNode();
1300                 mNode.mChildren[index] = node;
1301                 ViewNodeBuilder builder = new ViewNodeBuilder(mAssist, node, true);
1302                 mAssist.mPendingAsyncChildren.add(builder);
1303                 return builder;
1304             }
1305         }
1306 
1307         @Override
asyncCommit()1308         public void asyncCommit() {
1309             synchronized (mAssist) {
1310                 if (!mAsync) {
1311                     throw new IllegalStateException("Child " + this
1312                             + " was not created with ViewStructure.asyncNewChild");
1313                 }
1314                 if (!mAssist.mPendingAsyncChildren.remove(this)) {
1315                     throw new IllegalStateException("Child " + this + " already committed");
1316                 }
1317                 mAssist.notifyAll();
1318             }
1319         }
1320 
1321         @Override
getTempRect()1322         public Rect getTempRect() {
1323             return mAssist.mTmpRect;
1324         }
1325     }
1326 
1327     /** @hide */
AssistStructure(Activity activity)1328     public AssistStructure(Activity activity) {
1329         mHaveData = true;
1330         mActivityComponent = activity.getComponentName();
1331         ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews(
1332                 activity.getActivityToken());
1333         for (int i=0; i<views.size(); i++) {
1334             ViewRootImpl root = views.get(i);
1335             mWindowNodes.add(new WindowNode(this, root));
1336         }
1337     }
1338 
AssistStructure()1339     public AssistStructure() {
1340         mHaveData = true;
1341         mActivityComponent = null;
1342     }
1343 
1344     /** @hide */
AssistStructure(Parcel in)1345     public AssistStructure(Parcel in) {
1346         mReceiveChannel = in.readStrongBinder();
1347     }
1348 
1349     /** @hide */
dump()1350     public void dump() {
1351         Log.i(TAG, "Activity: " + mActivityComponent.flattenToShortString());
1352         final int N = getWindowNodeCount();
1353         for (int i=0; i<N; i++) {
1354             WindowNode node = getWindowNodeAt(i);
1355             Log.i(TAG, "Window #" + i + " [" + node.getLeft() + "," + node.getTop()
1356                     + " " + node.getWidth() + "x" + node.getHeight() + "]" + " " + node.getTitle());
1357             dump("  ", node.getRootViewNode());
1358         }
1359     }
1360 
dump(String prefix, ViewNode node)1361     void dump(String prefix, ViewNode node) {
1362         Log.i(TAG, prefix + "View [" + node.getLeft() + "," + node.getTop()
1363                 + " " + node.getWidth() + "x" + node.getHeight() + "]" + " " + node.getClassName());
1364         int id = node.getId();
1365         if (id != 0) {
1366             StringBuilder sb = new StringBuilder();
1367             sb.append(prefix); sb.append("  ID: #"); sb.append(Integer.toHexString(id));
1368             String entry = node.getIdEntry();
1369             if (entry != null) {
1370                 String type = node.getIdType();
1371                 String pkg = node.getIdPackage();
1372                 sb.append(" "); sb.append(pkg); sb.append(":"); sb.append(type);
1373                 sb.append("/"); sb.append(entry);
1374             }
1375             Log.i(TAG, sb.toString());
1376         }
1377         int scrollX = node.getScrollX();
1378         int scrollY = node.getScrollY();
1379         if (scrollX != 0 || scrollY != 0) {
1380             Log.i(TAG, prefix + "  Scroll: " + scrollX + "," + scrollY);
1381         }
1382         Matrix matrix = node.getTransformation();
1383         if (matrix != null) {
1384             Log.i(TAG, prefix + "  Transformation: " + matrix);
1385         }
1386         float elevation = node.getElevation();
1387         if (elevation != 0) {
1388             Log.i(TAG, prefix + "  Elevation: " + elevation);
1389         }
1390         float alpha = node.getAlpha();
1391         if (alpha != 0) {
1392             Log.i(TAG, prefix + "  Alpha: " + elevation);
1393         }
1394         CharSequence contentDescription = node.getContentDescription();
1395         if (contentDescription != null) {
1396             Log.i(TAG, prefix + "  Content description: " + contentDescription);
1397         }
1398         CharSequence text = node.getText();
1399         if (text != null) {
1400             Log.i(TAG, prefix + "  Text (sel " + node.getTextSelectionStart() + "-"
1401                     + node.getTextSelectionEnd() + "): " + text);
1402             Log.i(TAG, prefix + "  Text size: " + node.getTextSize() + " , style: #"
1403                     + node.getTextStyle());
1404             Log.i(TAG, prefix + "  Text color fg: #" + Integer.toHexString(node.getTextColor())
1405                     + ", bg: #" + Integer.toHexString(node.getTextBackgroundColor()));
1406         }
1407         String hint = node.getHint();
1408         if (hint != null) {
1409             Log.i(TAG, prefix + "  Hint: " + hint);
1410         }
1411         Bundle extras = node.getExtras();
1412         if (extras != null) {
1413             Log.i(TAG, prefix + "  Extras: " + extras);
1414         }
1415         if (node.isAssistBlocked()) {
1416             Log.i(TAG, prefix + "  BLOCKED");
1417         }
1418         final int NCHILDREN = node.getChildCount();
1419         if (NCHILDREN > 0) {
1420             Log.i(TAG, prefix + "  Children:");
1421             String cprefix = prefix + "    ";
1422             for (int i=0; i<NCHILDREN; i++) {
1423                 ViewNode cnode = node.getChildAt(i);
1424                 dump(cprefix, cnode);
1425             }
1426         }
1427     }
1428 
1429     /**
1430      * Return the activity this AssistStructure came from.
1431      */
getActivityComponent()1432     public ComponentName getActivityComponent() {
1433         ensureData();
1434         return mActivityComponent;
1435     }
1436 
1437     /**
1438      * Return the number of window contents that have been collected in this assist data.
1439      */
getWindowNodeCount()1440     public int getWindowNodeCount() {
1441         ensureData();
1442         return mWindowNodes.size();
1443     }
1444 
1445     /**
1446      * Return one of the windows in the assist data.
1447      * @param index Which window to retrieve, may be 0 to {@link #getWindowNodeCount()}-1.
1448      */
getWindowNodeAt(int index)1449     public WindowNode getWindowNodeAt(int index) {
1450         ensureData();
1451         return mWindowNodes.get(index);
1452     }
1453 
1454     /** @hide */
ensureData()1455     public void ensureData() {
1456         if (mHaveData) {
1457             return;
1458         }
1459         mHaveData = true;
1460         ParcelTransferReader reader = new ParcelTransferReader(mReceiveChannel);
1461         reader.go();
1462     }
1463 
waitForReady()1464     boolean waitForReady() {
1465         boolean skipStructure = false;
1466         synchronized (this) {
1467             long endTime = SystemClock.uptimeMillis() + 5000;
1468             long now;
1469             while (mPendingAsyncChildren.size() > 0 && (now=SystemClock.uptimeMillis()) < endTime) {
1470                 try {
1471                     wait(endTime-now);
1472                 } catch (InterruptedException e) {
1473                 }
1474             }
1475             if (mPendingAsyncChildren.size() > 0) {
1476                 // We waited too long, assume none of the assist structure is valid.
1477                 Log.w(TAG, "Skipping assist structure, waiting too long for async children (have "
1478                         + mPendingAsyncChildren.size() + " remaining");
1479                 skipStructure = true;
1480             }
1481         }
1482         return !skipStructure;
1483     }
1484 
1485     /** @hide */
clearSendChannel()1486     public void clearSendChannel() {
1487         if (mSendChannel != null) {
1488             mSendChannel.mAssistStructure = null;
1489         }
1490     }
1491 
describeContents()1492     public int describeContents() {
1493         return 0;
1494     }
1495 
writeToParcel(Parcel out, int flags)1496     public void writeToParcel(Parcel out, int flags) {
1497         if (mHaveData) {
1498             // This object holds its data.  We want to write a send channel that the
1499             // other side can use to retrieve that data.
1500             if (mSendChannel == null) {
1501                 mSendChannel = new SendChannel(this);
1502             }
1503             out.writeStrongBinder(mSendChannel);
1504         } else {
1505             // This object doesn't hold its data, so just propagate along its receive channel.
1506             out.writeStrongBinder(mReceiveChannel);
1507         }
1508     }
1509 
1510     public static final Parcelable.Creator<AssistStructure> CREATOR
1511             = new Parcelable.Creator<AssistStructure>() {
1512         public AssistStructure createFromParcel(Parcel in) {
1513             return new AssistStructure(in);
1514         }
1515 
1516         public AssistStructure[] newArray(int size) {
1517             return new AssistStructure[size];
1518         }
1519     };
1520 }
1521