Home | History | Annotate | Download | only in assist
      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 
     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