1package android.app.assist;
2
3import android.app.Activity;
4import android.content.ComponentName;
5import android.graphics.Matrix;
6import android.graphics.Rect;
7import android.os.BadParcelableException;
8import android.os.Binder;
9import android.os.Bundle;
10import android.os.IBinder;
11import android.os.Parcel;
12import android.os.Parcelable;
13import android.os.PooledStringReader;
14import android.os.PooledStringWriter;
15import android.os.RemoteException;
16import android.os.SystemClock;
17import android.text.TextUtils;
18import android.util.Log;
19import android.view.View;
20import android.view.ViewStructure;
21import android.view.ViewRootImpl;
22import android.view.WindowManager;
23import android.view.WindowManagerGlobal;
24
25import java.util.ArrayList;
26
27/**
28 * Assist data automatically created by the platform's implementation
29 * of {@link android.app.Activity#onProvideAssistData}.
30 */
31public 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
60        SendChannel(AssistStructure as) {
61            mAssistStructure = as;
62        }
63
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
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
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
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
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
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
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
263        ParcelTransferReader(IBinder channel) {
264            mChannel = channel;
265        }
266
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
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
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
351        ViewNodeText() {
352        }
353
354        boolean isSimple() {
355            return mTextBackgroundColor == ViewNode.TEXT_COLOR_UNDEFINED
356                    && mTextSelectionStart == 0 && mTextSelectionEnd == 0
357                    && mLineCharOffsets == null && mLineBaselines == null && mHint == null;
358        }
359
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
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
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
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
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         */
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         */
458        public int getTop() {
459            return mY;
460        }
461
462        /**
463         * Returns the total width of the window in pixels.
464         */
465        public int getWidth() {
466            return mWidth;
467        }
468
469        /**
470         * Returns the total height of the window in pixels.
471         */
472        public int getHeight() {
473            return mHeight;
474        }
475
476        /**
477         * Returns the title associated with the window, if it has one.
478         */
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         */
487        public int getDisplayId() {
488            return mDisplayId;
489        }
490
491        /**
492         * Returns the {@link ViewNode} containing the root content of the window.
493         */
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
565        ViewNode() {
566        }
567
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
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
779        public int getScrollY() {
780            return mScrollY;
781        }
782
783        /**
784         * Returns the width of this view, in pixels.
785         */
786        public int getWidth() {
787            return mWidth;
788        }
789
790        /**
791         * Returns the height of this view, in pixels.
792         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
1033        public Bundle getExtras() {
1034            return mExtras;
1035        }
1036
1037        /**
1038         * Return the number of children this node has.
1039         */
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         */
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
1058        ViewNodeBuilder(AssistStructure assist, ViewNode node, boolean async) {
1059            mAssist = assist;
1060            mNode = node;
1061            mAsync = async;
1062        }
1063
1064        @Override
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
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
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
1092        public void setElevation(float elevation) {
1093            mNode.mElevation = elevation;
1094        }
1095
1096        @Override
1097        public void setAlpha(float alpha) {
1098            mNode.mAlpha = alpha;
1099        }
1100
1101        @Override
1102        public void setVisibility(int visibility) {
1103            mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_VISIBILITY_MASK) | visibility;
1104        }
1105
1106        @Override
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
1113        public void setEnabled(boolean state) {
1114            mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_DISABLED)
1115                    | (state ? 0 : ViewNode.FLAGS_DISABLED);
1116        }
1117
1118        @Override
1119        public void setClickable(boolean state) {
1120            mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CLICKABLE)
1121                    | (state ? ViewNode.FLAGS_CLICKABLE : 0);
1122        }
1123
1124        @Override
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
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
1137        public void setFocusable(boolean state) {
1138            mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_FOCUSABLE)
1139                    | (state ? ViewNode.FLAGS_FOCUSABLE : 0);
1140        }
1141
1142        @Override
1143        public void setFocused(boolean state) {
1144            mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_FOCUSED)
1145                    | (state ? ViewNode.FLAGS_FOCUSED : 0);
1146        }
1147
1148        @Override
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
1155        public void setCheckable(boolean state) {
1156            mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CHECKABLE)
1157                    | (state ? ViewNode.FLAGS_CHECKABLE : 0);
1158        }
1159
1160        @Override
1161        public void setChecked(boolean state) {
1162            mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CHECKED)
1163                    | (state ? ViewNode.FLAGS_CHECKED : 0);
1164        }
1165
1166        @Override
1167        public void setSelected(boolean state) {
1168            mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_SELECTED)
1169                    | (state ? ViewNode.FLAGS_SELECTED : 0);
1170        }
1171
1172        @Override
1173        public void setActivated(boolean state) {
1174            mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ACTIVATED)
1175                    | (state ? ViewNode.FLAGS_ACTIVATED : 0);
1176        }
1177
1178        @Override
1179        public void setClassName(String className) {
1180            mNode.mClassName = className;
1181        }
1182
1183        @Override
1184        public void setContentDescription(CharSequence contentDescription) {
1185            mNode.mContentDescription = contentDescription;
1186        }
1187
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
1197        public void setText(CharSequence text) {
1198            ViewNodeText t = getNodeText();
1199            t.mText = text;
1200            t.mTextSelectionStart = t.mTextSelectionEnd = -1;
1201        }
1202
1203        @Override
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
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
1221        public void setTextLines(int[] charOffsets, int[] baselines) {
1222            ViewNodeText t = getNodeText();
1223            t.mLineCharOffsets = charOffsets;
1224            t.mLineBaselines = baselines;
1225        }
1226
1227        @Override
1228        public void setHint(CharSequence hint) {
1229            getNodeText().mHint = hint != null ? hint.toString() : null;
1230        }
1231
1232        @Override
1233        public CharSequence getText() {
1234            return mNode.mText != null ? mNode.mText.mText : null;
1235        }
1236
1237        @Override
1238        public int getTextSelectionStart() {
1239            return mNode.mText != null ? mNode.mText.mTextSelectionStart : -1;
1240        }
1241
1242        @Override
1243        public int getTextSelectionEnd() {
1244            return mNode.mText != null ? mNode.mText.mTextSelectionEnd : -1;
1245        }
1246
1247        @Override
1248        public CharSequence getHint() {
1249            return mNode.mText != null ? mNode.mText.mHint : null;
1250        }
1251
1252        @Override
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
1262        public boolean hasExtras() {
1263            return mNode.mExtras != null;
1264        }
1265
1266        @Override
1267        public void setChildCount(int num) {
1268            mNode.mChildren = new ViewNode[num];
1269        }
1270
1271        @Override
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
1285        public int getChildCount() {
1286            return mNode.mChildren != null ? mNode.mChildren.length : 0;
1287        }
1288
1289        @Override
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
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
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
1322        public Rect getTempRect() {
1323            return mAssist.mTmpRect;
1324        }
1325    }
1326
1327    /** @hide */
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
1339    public AssistStructure() {
1340        mHaveData = true;
1341        mActivityComponent = null;
1342    }
1343
1344    /** @hide */
1345    public AssistStructure(Parcel in) {
1346        mReceiveChannel = in.readStrongBinder();
1347    }
1348
1349    /** @hide */
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
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     */
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     */
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     */
1449    public WindowNode getWindowNodeAt(int index) {
1450        ensureData();
1451        return mWindowNodes.get(index);
1452    }
1453
1454    /** @hide */
1455    public void ensureData() {
1456        if (mHaveData) {
1457            return;
1458        }
1459        mHaveData = true;
1460        ParcelTransferReader reader = new ParcelTransferReader(mReceiveChannel);
1461        reader.go();
1462    }
1463
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 */
1486    public void clearSendChannel() {
1487        if (mSendChannel != null) {
1488            mSendChannel.mAssistStructure = null;
1489        }
1490    }
1491
1492    public int describeContents() {
1493        return 0;
1494    }
1495
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