• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package android.app.assist;
2 
3 import android.annotation.NonNull;
4 import android.annotation.Nullable;
5 import android.app.Activity;
6 import android.content.ComponentName;
7 import android.graphics.Matrix;
8 import android.graphics.Rect;
9 import android.net.Uri;
10 import android.os.BadParcelableException;
11 import android.os.Binder;
12 import android.os.Bundle;
13 import android.os.IBinder;
14 import android.os.LocaleList;
15 import android.os.Parcel;
16 import android.os.Parcelable;
17 import android.os.PooledStringReader;
18 import android.os.PooledStringWriter;
19 import android.os.RemoteException;
20 import android.os.SystemClock;
21 import android.service.autofill.FillRequest;
22 import android.text.TextUtils;
23 import android.util.Log;
24 import android.util.Pair;
25 import android.view.View;
26 import android.view.ViewRootImpl;
27 import android.view.ViewStructure;
28 import android.view.ViewStructure.HtmlInfo;
29 import android.view.ViewStructure.HtmlInfo.Builder;
30 import android.view.WindowManager;
31 import android.view.WindowManagerGlobal;
32 import android.view.autofill.AutofillId;
33 import android.view.autofill.AutofillValue;
34 
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.List;
38 
39 /**
40  * Assist data automatically created by the platform's implementation of assist and autofill.
41  *
42  * <p>The structure is used for assist purposes when created by
43  * {@link android.app.Activity#onProvideAssistData}, {@link View#onProvideStructure(ViewStructure)},
44  * or {@link View#onProvideVirtualStructure(ViewStructure)}.
45  *
46  * <p>The structure is used for autofill purposes when created by
47  * {@link View#onProvideAutofillStructure(ViewStructure, int)},
48  * or {@link View#onProvideAutofillVirtualStructure(ViewStructure, int)}.
49  *
50  * <p>For performance reasons, some properties of the assist data might be available just for assist
51  * or autofill purposes; in those case, the property availability will be document in its javadoc.
52  */
53 public class AssistStructure implements Parcelable {
54     static final String TAG = "AssistStructure";
55 
56     static final boolean DEBUG_PARCEL = false;
57     static final boolean DEBUG_PARCEL_CHILDREN = false;
58     static final boolean DEBUG_PARCEL_TREE = false;
59 
60     static final int VALIDATE_WINDOW_TOKEN = 0x11111111;
61     static final int VALIDATE_VIEW_TOKEN = 0x22222222;
62 
63     boolean mHaveData;
64 
65     ComponentName mActivityComponent;
66     private boolean mIsHomeActivity;
67     private int mFlags;
68 
69     final ArrayList<WindowNode> mWindowNodes = new ArrayList<>();
70 
71     final ArrayList<ViewNodeBuilder> mPendingAsyncChildren = new ArrayList<>();
72 
73     SendChannel mSendChannel;
74     IBinder mReceiveChannel;
75 
76     Rect mTmpRect = new Rect();
77 
78     boolean mSanitizeOnWrite = false;
79     private long mAcquisitionStartTime;
80     private long mAcquisitionEndTime;
81 
82     static final int TRANSACTION_XFER = Binder.FIRST_CALL_TRANSACTION+1;
83     static final String DESCRIPTOR = "android.app.AssistStructure";
84 
85     /** @hide */
setAcquisitionStartTime(long acquisitionStartTime)86     public void setAcquisitionStartTime(long acquisitionStartTime) {
87         mAcquisitionStartTime = acquisitionStartTime;
88     }
89 
90     /** @hide */
setAcquisitionEndTime(long acquisitionEndTime)91     public void setAcquisitionEndTime(long acquisitionEndTime) {
92         mAcquisitionEndTime = acquisitionEndTime;
93     }
94 
95     /**
96      * @hide
97      * Set the home activity flag.
98      */
setHomeActivity(boolean isHomeActivity)99     public void setHomeActivity(boolean isHomeActivity) {
100         mIsHomeActivity = isHomeActivity;
101     }
102 
103     /**
104      * Returns the time when the activity started generating assist data to build the
105      * AssistStructure. The time is as specified by {@link SystemClock#uptimeMillis()}.
106      *
107      * @see #getAcquisitionEndTime()
108      * @return Returns the acquisition start time of the assist data, in milliseconds.
109      */
getAcquisitionStartTime()110     public long getAcquisitionStartTime() {
111         ensureData();
112         return mAcquisitionStartTime;
113     }
114 
115     /**
116      * Returns the time when the activity finished generating assist data to build the
117      * AssistStructure. The time is as specified by {@link SystemClock#uptimeMillis()}.
118      *
119      * @see #getAcquisitionStartTime()
120      * @return Returns the acquisition end time of the assist data, in milliseconds.
121      */
getAcquisitionEndTime()122     public long getAcquisitionEndTime() {
123         ensureData();
124         return mAcquisitionEndTime;
125     }
126 
127     final static class SendChannel extends Binder {
128         volatile AssistStructure mAssistStructure;
129 
SendChannel(AssistStructure as)130         SendChannel(AssistStructure as) {
131             mAssistStructure = as;
132         }
133 
onTransact(int code, Parcel data, Parcel reply, int flags)134         @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
135                 throws RemoteException {
136             if (code == TRANSACTION_XFER) {
137                 AssistStructure as = mAssistStructure;
138                 if (as == null) {
139                     return true;
140                 }
141 
142                 data.enforceInterface(DESCRIPTOR);
143                 IBinder token = data.readStrongBinder();
144                 if (DEBUG_PARCEL) Log.d(TAG, "Request for data on " + as
145                         + " using token " + token);
146                 if (token != null) {
147                     if (DEBUG_PARCEL) Log.d(TAG, "Resuming partial write of " + token);
148                     if (token instanceof ParcelTransferWriter) {
149                         ParcelTransferWriter xfer = (ParcelTransferWriter)token;
150                         xfer.writeToParcel(as, reply);
151                         return true;
152                     }
153                     Log.w(TAG, "Caller supplied bad token type: " + token);
154                     // Don't write anything; this is the end of the data.
155                     return true;
156                 }
157                 //long start = SystemClock.uptimeMillis();
158                 ParcelTransferWriter xfer = new ParcelTransferWriter(as, reply);
159                 xfer.writeToParcel(as, reply);
160                 //Log.i(TAG, "Time to parcel: " + (SystemClock.uptimeMillis()-start) + "ms");
161                 return true;
162             } else {
163                 return super.onTransact(code, data, reply, flags);
164             }
165         }
166     }
167 
168     final static class ViewStackEntry {
169         ViewNode node;
170         int curChild;
171         int numChildren;
172     }
173 
174     final static class ParcelTransferWriter extends Binder {
175         final boolean mWriteStructure;
176         int mCurWindow;
177         int mNumWindows;
178         final ArrayList<ViewStackEntry> mViewStack = new ArrayList<>();
179         ViewStackEntry mCurViewStackEntry;
180         int mCurViewStackPos;
181         int mNumWrittenWindows;
182         int mNumWrittenViews;
183         final float[] mTmpMatrix = new float[9];
184         final boolean mSanitizeOnWrite;
185 
ParcelTransferWriter(AssistStructure as, Parcel out)186         ParcelTransferWriter(AssistStructure as, Parcel out) {
187             mSanitizeOnWrite = as.mSanitizeOnWrite;
188             mWriteStructure = as.waitForReady();
189             ComponentName.writeToParcel(as.mActivityComponent, out);
190             out.writeInt(as.mFlags);
191             out.writeLong(as.mAcquisitionStartTime);
192             out.writeLong(as.mAcquisitionEndTime);
193             mNumWindows = as.mWindowNodes.size();
194             if (mWriteStructure && mNumWindows > 0) {
195                 out.writeInt(mNumWindows);
196             } else {
197                 out.writeInt(0);
198             }
199         }
200 
writeToParcel(AssistStructure as, Parcel out)201         void writeToParcel(AssistStructure as, Parcel out) {
202             int start = out.dataPosition();
203             mNumWrittenWindows = 0;
204             mNumWrittenViews = 0;
205             boolean more = writeToParcelInner(as, out);
206             Log.i(TAG, "Flattened " + (more ? "partial" : "final") + " assist data: "
207                     + (out.dataPosition() - start)
208                     + " bytes, containing " + mNumWrittenWindows + " windows, "
209                     + mNumWrittenViews + " views");
210         }
211 
writeToParcelInner(AssistStructure as, Parcel out)212         boolean writeToParcelInner(AssistStructure as, Parcel out) {
213             if (mNumWindows == 0) {
214                 return false;
215             }
216             if (DEBUG_PARCEL) Log.d(TAG, "Creating PooledStringWriter @ " + out.dataPosition());
217             PooledStringWriter pwriter = new PooledStringWriter(out);
218             while (writeNextEntryToParcel(as, out, pwriter)) {
219                 // If the parcel is above the IPC limit, then we are getting too
220                 // large for a single IPC so stop here and let the caller come back when it
221                 // is ready for more.
222                 if (out.dataSize() > IBinder.MAX_IPC_SIZE) {
223                     if (DEBUG_PARCEL) Log.d(TAG, "Assist data size is " + out.dataSize()
224                             + " @ pos " + out.dataPosition() + "; returning partial result");
225                     out.writeInt(0);
226                     out.writeStrongBinder(this);
227                     if (DEBUG_PARCEL) Log.d(TAG, "Finishing PooledStringWriter @ "
228                             + out.dataPosition() + ", size " + pwriter.getStringCount());
229                     pwriter.finish();
230                     return true;
231                 }
232             }
233             if (DEBUG_PARCEL) Log.d(TAG, "Finishing PooledStringWriter @ "
234                     + out.dataPosition() + ", size " + pwriter.getStringCount());
235             pwriter.finish();
236             mViewStack.clear();
237             return false;
238         }
239 
pushViewStackEntry(ViewNode node, int pos)240         void pushViewStackEntry(ViewNode node, int pos) {
241             ViewStackEntry entry;
242             if (pos >= mViewStack.size()) {
243                 entry = new ViewStackEntry();
244                 mViewStack.add(entry);
245                 if (DEBUG_PARCEL_TREE) Log.d(TAG, "New stack entry at " + pos + ": " + entry);
246             } else {
247                 entry = mViewStack.get(pos);
248                 if (DEBUG_PARCEL_TREE) Log.d(TAG, "Existing stack entry at " + pos + ": " + entry);
249             }
250             entry.node = node;
251             entry.numChildren = node.getChildCount();
252             entry.curChild = 0;
253             mCurViewStackEntry = entry;
254         }
255 
writeView(ViewNode child, Parcel out, PooledStringWriter pwriter, int levelAdj)256         void writeView(ViewNode child, Parcel out, PooledStringWriter pwriter, int levelAdj) {
257             if (DEBUG_PARCEL) Log.d(TAG, "write view: at " + out.dataPosition()
258                     + ", windows=" + mNumWrittenWindows
259                     + ", views=" + mNumWrittenViews
260                     + ", level=" + (mCurViewStackPos+levelAdj));
261             out.writeInt(VALIDATE_VIEW_TOKEN);
262             int flags = child.writeSelfToParcel(out, pwriter, mSanitizeOnWrite, mTmpMatrix);
263             mNumWrittenViews++;
264             // If the child has children, push it on the stack to write them next.
265             if ((flags&ViewNode.FLAGS_HAS_CHILDREN) != 0) {
266                 if (DEBUG_PARCEL_TREE || DEBUG_PARCEL_CHILDREN) Log.d(TAG,
267                         "Preparing to write " + child.mChildren.length
268                                 + " children: @ #" + mNumWrittenViews
269                                 + ", level " + (mCurViewStackPos+levelAdj));
270                 out.writeInt(child.mChildren.length);
271                 int pos = ++mCurViewStackPos;
272                 pushViewStackEntry(child, pos);
273             }
274         }
275 
writeNextEntryToParcel(AssistStructure as, Parcel out, PooledStringWriter pwriter)276         boolean writeNextEntryToParcel(AssistStructure as, Parcel out, PooledStringWriter pwriter) {
277             // Write next view node if appropriate.
278             if (mCurViewStackEntry != null) {
279                 if (mCurViewStackEntry.curChild < mCurViewStackEntry.numChildren) {
280                     // Write the next child in the current view.
281                     if (DEBUG_PARCEL_TREE) Log.d(TAG, "Writing child #"
282                             + mCurViewStackEntry.curChild + " in " + mCurViewStackEntry.node);
283                     ViewNode child = mCurViewStackEntry.node.mChildren[mCurViewStackEntry.curChild];
284                     mCurViewStackEntry.curChild++;
285                     writeView(child, out, pwriter, 1);
286                     return true;
287                 }
288 
289                 // We are done writing children of the current view; pop off the stack.
290                 do {
291                     int pos = --mCurViewStackPos;
292                     if (DEBUG_PARCEL_TREE) Log.d(TAG, "Done with " + mCurViewStackEntry.node
293                             + "; popping up to " + pos);
294                     if (pos < 0) {
295                         // Reached the last view; step to next window.
296                         if (DEBUG_PARCEL_TREE) Log.d(TAG, "Done with view hierarchy!");
297                         mCurViewStackEntry = null;
298                         break;
299                     }
300                     mCurViewStackEntry = mViewStack.get(pos);
301                 } while (mCurViewStackEntry.curChild >= mCurViewStackEntry.numChildren);
302                 return true;
303             }
304 
305             // Write the next window if appropriate.
306             int pos = mCurWindow;
307             if (pos < mNumWindows) {
308                 WindowNode win = as.mWindowNodes.get(pos);
309                 mCurWindow++;
310                 if (DEBUG_PARCEL) Log.d(TAG, "write window #" + pos + ": at " + out.dataPosition()
311                         + ", windows=" + mNumWrittenWindows
312                         + ", views=" + mNumWrittenViews);
313                 out.writeInt(VALIDATE_WINDOW_TOKEN);
314                 win.writeSelfToParcel(out, pwriter, mTmpMatrix);
315                 mNumWrittenWindows++;
316                 ViewNode root = win.mRoot;
317                 mCurViewStackPos = 0;
318                 if (DEBUG_PARCEL_TREE) Log.d(TAG, "Writing initial root view " + root);
319                 writeView(root, out, pwriter, 0);
320                 return true;
321             }
322 
323             return false;
324         }
325     }
326 
327     final class ParcelTransferReader {
328         final float[] mTmpMatrix = new float[9];
329         PooledStringReader mStringReader;
330 
331         int mNumReadWindows;
332         int mNumReadViews;
333 
334         private final IBinder mChannel;
335         private IBinder mTransferToken;
336         private Parcel mCurParcel;
337 
ParcelTransferReader(IBinder channel)338         ParcelTransferReader(IBinder channel) {
339             mChannel = channel;
340         }
341 
go()342         void go() {
343             fetchData();
344             mActivityComponent = ComponentName.readFromParcel(mCurParcel);
345             mFlags = mCurParcel.readInt();
346             mAcquisitionStartTime = mCurParcel.readLong();
347             mAcquisitionEndTime = mCurParcel.readLong();
348             final int N = mCurParcel.readInt();
349             if (N > 0) {
350                 if (DEBUG_PARCEL) Log.d(TAG, "Creating PooledStringReader @ "
351                         + mCurParcel.dataPosition());
352                 mStringReader = new PooledStringReader(mCurParcel);
353                 if (DEBUG_PARCEL) Log.d(TAG, "PooledStringReader size = "
354                         + mStringReader.getStringCount());
355                 for (int i=0; i<N; i++) {
356                     mWindowNodes.add(new WindowNode(this));
357                 }
358             }
359             if (DEBUG_PARCEL) Log.d(TAG, "Finished reading: at " + mCurParcel.dataPosition()
360                     + ", avail=" + mCurParcel.dataAvail() + ", windows=" + mNumReadWindows
361                     + ", views=" + mNumReadViews);
362         }
363 
readParcel(int validateToken, int level)364         Parcel readParcel(int validateToken, int level) {
365             if (DEBUG_PARCEL) Log.d(TAG, "readParcel: at " + mCurParcel.dataPosition()
366                     + ", avail=" + mCurParcel.dataAvail() + ", windows=" + mNumReadWindows
367                     + ", views=" + mNumReadViews + ", level=" + level);
368             int token = mCurParcel.readInt();
369             if (token != 0) {
370                 if (token != validateToken) {
371                     throw new BadParcelableException("Got token " + Integer.toHexString(token)
372                             + ", expected token " + Integer.toHexString(validateToken));
373                 }
374                 return mCurParcel;
375             }
376             // We have run out of partial data, need to read another batch.
377             mTransferToken = mCurParcel.readStrongBinder();
378             if (mTransferToken == null) {
379                 throw new IllegalStateException(
380                         "Reached end of partial data without transfer token");
381             }
382             if (DEBUG_PARCEL) Log.d(TAG, "Ran out of partial data at "
383                     + mCurParcel.dataPosition() + ", token " + mTransferToken);
384             fetchData();
385             if (DEBUG_PARCEL) Log.d(TAG, "Creating PooledStringReader @ "
386                     + mCurParcel.dataPosition());
387             mStringReader = new PooledStringReader(mCurParcel);
388             if (DEBUG_PARCEL) Log.d(TAG, "PooledStringReader size = "
389                     + mStringReader.getStringCount());
390             if (DEBUG_PARCEL) Log.d(TAG, "readParcel: at " + mCurParcel.dataPosition()
391                     + ", avail=" + mCurParcel.dataAvail() + ", windows=" + mNumReadWindows
392                     + ", views=" + mNumReadViews);
393             mCurParcel.readInt();
394             return mCurParcel;
395         }
396 
fetchData()397         private void fetchData() {
398             Parcel data = Parcel.obtain();
399             data.writeInterfaceToken(DESCRIPTOR);
400             data.writeStrongBinder(mTransferToken);
401             if (DEBUG_PARCEL) Log.d(TAG, "Requesting data with token " + mTransferToken);
402             if (mCurParcel != null) {
403                 mCurParcel.recycle();
404             }
405             mCurParcel = Parcel.obtain();
406             try {
407                 mChannel.transact(TRANSACTION_XFER, data, mCurParcel, 0);
408             } catch (RemoteException e) {
409                 Log.w(TAG, "Failure reading AssistStructure data", e);
410                 throw new IllegalStateException("Failure reading AssistStructure data: " + e);
411             }
412             data.recycle();
413             mNumReadWindows = mNumReadViews = 0;
414         }
415     }
416 
417     final static class ViewNodeText {
418         CharSequence mText;
419         float mTextSize;
420         int mTextStyle;
421         int mTextColor = ViewNode.TEXT_COLOR_UNDEFINED;
422         int mTextBackgroundColor = ViewNode.TEXT_COLOR_UNDEFINED;
423         int mTextSelectionStart;
424         int mTextSelectionEnd;
425         int[] mLineCharOffsets;
426         int[] mLineBaselines;
427         String mHint;
428 
ViewNodeText()429         ViewNodeText() {
430         }
431 
isSimple()432         boolean isSimple() {
433             return mTextBackgroundColor == ViewNode.TEXT_COLOR_UNDEFINED
434                     && mTextSelectionStart == 0 && mTextSelectionEnd == 0
435                     && mLineCharOffsets == null && mLineBaselines == null && mHint == null;
436         }
437 
ViewNodeText(Parcel in, boolean simple)438         ViewNodeText(Parcel in, boolean simple) {
439             mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
440             mTextSize = in.readFloat();
441             mTextStyle = in.readInt();
442             mTextColor = in.readInt();
443             if (!simple) {
444                 mTextBackgroundColor = in.readInt();
445                 mTextSelectionStart = in.readInt();
446                 mTextSelectionEnd = in.readInt();
447                 mLineCharOffsets = in.createIntArray();
448                 mLineBaselines = in.createIntArray();
449                 mHint = in.readString();
450             }
451         }
452 
writeToParcel(Parcel out, boolean simple, boolean writeSensitive)453         void writeToParcel(Parcel out, boolean simple, boolean writeSensitive) {
454             TextUtils.writeToParcel(writeSensitive ? mText : "", out, 0);
455             out.writeFloat(mTextSize);
456             out.writeInt(mTextStyle);
457             out.writeInt(mTextColor);
458             if (!simple) {
459                 out.writeInt(mTextBackgroundColor);
460                 out.writeInt(mTextSelectionStart);
461                 out.writeInt(mTextSelectionEnd);
462                 out.writeIntArray(mLineCharOffsets);
463                 out.writeIntArray(mLineBaselines);
464                 out.writeString(mHint);
465             }
466         }
467     }
468 
469     /**
470      * Describes a window in the assist data.
471      */
472     static public class WindowNode {
473         final int mX;
474         final int mY;
475         final int mWidth;
476         final int mHeight;
477         final CharSequence mTitle;
478         final int mDisplayId;
479         final ViewNode mRoot;
480 
WindowNode(AssistStructure assist, ViewRootImpl root, boolean forAutoFill, int flags)481         WindowNode(AssistStructure assist, ViewRootImpl root, boolean forAutoFill, int flags) {
482             View view = root.getView();
483             Rect rect = new Rect();
484             view.getBoundsOnScreen(rect);
485             mX = rect.left - view.getLeft();
486             mY = rect.top - view.getTop();
487             mWidth = rect.width();
488             mHeight = rect.height();
489             mTitle = root.getTitle();
490             mDisplayId = root.getDisplayId();
491             mRoot = new ViewNode();
492 
493             ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false);
494             if ((root.getWindowFlags() & WindowManager.LayoutParams.FLAG_SECURE) != 0) {
495                 if (forAutoFill) {
496                     final int autofillFlags = (flags & FillRequest.FLAG_MANUAL_REQUEST) != 0
497                             ? View.AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS : 0;
498                     view.onProvideAutofillStructure(builder, autofillFlags);
499                 } else {
500                     // This is a secure window, so it doesn't want a screenshot, and that
501                     // means we should also not copy out its view hierarchy for Assist
502                     view.onProvideStructure(builder);
503                     builder.setAssistBlocked(true);
504                     return;
505                 }
506             }
507             if (forAutoFill) {
508                 final int autofillFlags = (flags & FillRequest.FLAG_MANUAL_REQUEST) != 0
509                         ? View.AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS : 0;
510                 view.dispatchProvideAutofillStructure(builder, autofillFlags);
511             } else {
512                 view.dispatchProvideStructure(builder);
513             }
514         }
515 
WindowNode(ParcelTransferReader reader)516         WindowNode(ParcelTransferReader reader) {
517             Parcel in = reader.readParcel(VALIDATE_WINDOW_TOKEN, 0);
518             reader.mNumReadWindows++;
519             mX = in.readInt();
520             mY = in.readInt();
521             mWidth = in.readInt();
522             mHeight = in.readInt();
523             mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
524             mDisplayId = in.readInt();
525             mRoot = new ViewNode(reader, 0);
526         }
527 
writeSelfToParcel(Parcel out, PooledStringWriter pwriter, float[] tmpMatrix)528         void writeSelfToParcel(Parcel out, PooledStringWriter pwriter, float[] tmpMatrix) {
529             out.writeInt(mX);
530             out.writeInt(mY);
531             out.writeInt(mWidth);
532             out.writeInt(mHeight);
533             TextUtils.writeToParcel(mTitle, out, 0);
534             out.writeInt(mDisplayId);
535         }
536 
537         /**
538          * Returns the left edge of the window, in pixels, relative to the left
539          * edge of the screen.
540          */
getLeft()541         public int getLeft() {
542             return mX;
543         }
544 
545         /**
546          * Returns the top edge of the window, in pixels, relative to the top
547          * edge of the screen.
548          */
getTop()549         public int getTop() {
550             return mY;
551         }
552 
553         /**
554          * Returns the total width of the window in pixels.
555          */
getWidth()556         public int getWidth() {
557             return mWidth;
558         }
559 
560         /**
561          * Returns the total height of the window in pixels.
562          */
getHeight()563         public int getHeight() {
564             return mHeight;
565         }
566 
567         /**
568          * Returns the title associated with the window, if it has one.
569          */
getTitle()570         public CharSequence getTitle() {
571             return mTitle;
572         }
573 
574         /**
575          * Returns the ID of the display this window is on, for use with
576          * {@link android.hardware.display.DisplayManager#getDisplay DisplayManager.getDisplay()}.
577          */
getDisplayId()578         public int getDisplayId() {
579             return mDisplayId;
580         }
581 
582         /**
583          * Returns the {@link ViewNode} containing the root content of the window.
584          */
getRootViewNode()585         public ViewNode getRootViewNode() {
586             return mRoot;
587         }
588     }
589 
590     /**
591      * Describes a single view in the assist data.
592      */
593     static public class ViewNode {
594         /**
595          * Magic value for text color that has not been defined, which is very unlikely
596          * to be confused with a real text color.
597          */
598         public static final int TEXT_COLOR_UNDEFINED = 1;
599 
600         public static final int TEXT_STYLE_BOLD = 1<<0;
601         public static final int TEXT_STYLE_ITALIC = 1<<1;
602         public static final int TEXT_STYLE_UNDERLINE = 1<<2;
603         public static final int TEXT_STYLE_STRIKE_THRU = 1<<3;
604 
605         int mId = View.NO_ID;
606         String mIdPackage;
607         String mIdType;
608         String mIdEntry;
609 
610         // TODO: once we have more flags, it might be better to store the individual
611         // fields (viewId and childId) of the field.
612         AutofillId mAutofillId;
613         @View.AutofillType int mAutofillType = View.AUTOFILL_TYPE_NONE;
614         @Nullable String[] mAutofillHints;
615         AutofillValue mAutofillValue;
616         CharSequence[] mAutofillOptions;
617         boolean mSanitized;
618         HtmlInfo mHtmlInfo;
619 
620         // POJO used to override some autofill-related values when the node is parcelized.
621         // Not written to parcel.
622         AutofillOverlay mAutofillOverlay;
623 
624         int mX;
625         int mY;
626         int mScrollX;
627         int mScrollY;
628         int mWidth;
629         int mHeight;
630         Matrix mMatrix;
631         float mElevation;
632         float mAlpha = 1.0f;
633 
634         static final int FLAGS_DISABLED = 0x00000001;
635         static final int FLAGS_VISIBILITY_MASK = View.VISIBLE|View.INVISIBLE|View.GONE;
636         static final int FLAGS_FOCUSABLE = 0x00000010;
637         static final int FLAGS_FOCUSED = 0x00000020;
638         static final int FLAGS_SELECTED = 0x00000040;
639         static final int FLAGS_ASSIST_BLOCKED = 0x00000080;
640         static final int FLAGS_CHECKABLE = 0x00000100;
641         static final int FLAGS_CHECKED = 0x00000200;
642         static final int FLAGS_CLICKABLE = 0x00000400;
643         static final int FLAGS_LONG_CLICKABLE = 0x00000800;
644         static final int FLAGS_ACCESSIBILITY_FOCUSED = 0x00001000;
645         static final int FLAGS_ACTIVATED = 0x00002000;
646         static final int FLAGS_CONTEXT_CLICKABLE = 0x00004000;
647         static final int FLAGS_OPAQUE = 0x00008000;
648 
649         // TODO: autofill data is made of many fields and ideally we should verify
650         // one-by-one to optimize what's sent over, but there isn't enough flag bits for that, we'd
651         // need to create a 'flags2' or 'autoFillFlags' field and add these flags there.
652         // So, to keep thinkg simpler for now, let's just use on flag for all of them...
653         static final int FLAGS_HAS_AUTOFILL_DATA = 0x80000000;
654         static final int FLAGS_HAS_MATRIX = 0x40000000;
655         static final int FLAGS_HAS_ALPHA = 0x20000000;
656         static final int FLAGS_HAS_ELEVATION = 0x10000000;
657         static final int FLAGS_HAS_SCROLL = 0x08000000;
658         static final int FLAGS_HAS_LARGE_COORDS = 0x04000000;
659         static final int FLAGS_HAS_CONTENT_DESCRIPTION = 0x02000000;
660         static final int FLAGS_HAS_TEXT = 0x01000000;
661         static final int FLAGS_HAS_COMPLEX_TEXT = 0x00800000;
662         static final int FLAGS_HAS_EXTRAS = 0x00400000;
663         static final int FLAGS_HAS_ID = 0x00200000;
664         static final int FLAGS_HAS_CHILDREN = 0x00100000;
665         static final int FLAGS_HAS_URL = 0x00080000;
666         static final int FLAGS_HAS_INPUT_TYPE = 0x00040000;
667         static final int FLAGS_HAS_LOCALE_LIST = 0x00010000;
668         static final int FLAGS_ALL_CONTROL = 0xfff00000;
669 
670         int mFlags;
671 
672         String mClassName;
673         CharSequence mContentDescription;
674 
675         ViewNodeText mText;
676         int mInputType;
677         String mWebDomain;
678         Bundle mExtras;
679         LocaleList mLocaleList;
680 
681         ViewNode[] mChildren;
682 
ViewNode()683         ViewNode() {
684         }
685 
ViewNode(ParcelTransferReader reader, int nestingLevel)686         ViewNode(ParcelTransferReader reader, int nestingLevel) {
687             final Parcel in = reader.readParcel(VALIDATE_VIEW_TOKEN, nestingLevel);
688             reader.mNumReadViews++;
689             final PooledStringReader preader = reader.mStringReader;
690             mClassName = preader.readString();
691             mFlags = in.readInt();
692             final int flags = mFlags;
693             if ((flags&FLAGS_HAS_ID) != 0) {
694                 mId = in.readInt();
695                 if (mId != 0) {
696                     mIdEntry = preader.readString();
697                     if (mIdEntry != null) {
698                         mIdType = preader.readString();
699                         mIdPackage = preader.readString();
700                     }
701                 }
702             }
703 
704             if ((flags&FLAGS_HAS_AUTOFILL_DATA) != 0) {
705                 mSanitized = in.readInt() == 1;
706                 mAutofillId = in.readParcelable(null);
707                 mAutofillType = in.readInt();
708                 mAutofillHints = in.readStringArray();
709                 mAutofillValue = in.readParcelable(null);
710                 mAutofillOptions = in.readCharSequenceArray();
711                 final Parcelable p = in.readParcelable(null);
712                 if (p instanceof HtmlInfo) {
713                     mHtmlInfo = (HtmlInfo) p;
714                 }
715             }
716             if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
717                 mX = in.readInt();
718                 mY = in.readInt();
719                 mWidth = in.readInt();
720                 mHeight = in.readInt();
721             } else {
722                 int val = in.readInt();
723                 mX = val&0x7fff;
724                 mY = (val>>16)&0x7fff;
725                 val = in.readInt();
726                 mWidth = val&0x7fff;
727                 mHeight = (val>>16)&0x7fff;
728             }
729             if ((flags&FLAGS_HAS_SCROLL) != 0) {
730                 mScrollX = in.readInt();
731                 mScrollY = in.readInt();
732             }
733             if ((flags&FLAGS_HAS_MATRIX) != 0) {
734                 mMatrix = new Matrix();
735                 in.readFloatArray(reader.mTmpMatrix);
736                 mMatrix.setValues(reader.mTmpMatrix);
737             }
738             if ((flags&FLAGS_HAS_ELEVATION) != 0) {
739                 mElevation = in.readFloat();
740             }
741             if ((flags&FLAGS_HAS_ALPHA) != 0) {
742                 mAlpha = in.readFloat();
743             }
744             if ((flags&FLAGS_HAS_CONTENT_DESCRIPTION) != 0) {
745                 mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
746             }
747             if ((flags&FLAGS_HAS_TEXT) != 0) {
748                 mText = new ViewNodeText(in, (flags&FLAGS_HAS_COMPLEX_TEXT) == 0);
749             }
750             if ((flags&FLAGS_HAS_INPUT_TYPE) != 0) {
751                 mInputType = in.readInt();
752             }
753             if ((flags&FLAGS_HAS_URL) != 0) {
754                 mWebDomain = in.readString();
755             }
756             if ((flags&FLAGS_HAS_LOCALE_LIST) != 0) {
757                 mLocaleList = in.readParcelable(null);
758             }
759             if ((flags&FLAGS_HAS_EXTRAS) != 0) {
760                 mExtras = in.readBundle();
761             }
762             if ((flags&FLAGS_HAS_CHILDREN) != 0) {
763                 final int NCHILDREN = in.readInt();
764                 if (DEBUG_PARCEL_TREE || DEBUG_PARCEL_CHILDREN) Log.d(TAG,
765                         "Preparing to read " + NCHILDREN
766                                 + " children: @ #" + reader.mNumReadViews
767                                 + ", level " + nestingLevel);
768                 mChildren = new ViewNode[NCHILDREN];
769                 for (int i=0; i<NCHILDREN; i++) {
770                     mChildren[i] = new ViewNode(reader, nestingLevel + 1);
771                 }
772             }
773         }
774 
writeSelfToParcel(Parcel out, PooledStringWriter pwriter, boolean sanitizeOnWrite, float[] tmpMatrix)775         int writeSelfToParcel(Parcel out, PooledStringWriter pwriter, boolean sanitizeOnWrite,
776                 float[] tmpMatrix) {
777             // Guard used to skip non-sanitized data when writing for autofill.
778             boolean writeSensitive = true;
779 
780             int flags = mFlags & ~FLAGS_ALL_CONTROL;
781 
782             if (mId != View.NO_ID) {
783                 flags |= FLAGS_HAS_ID;
784             }
785             if (mAutofillId != null) {
786                 flags |= FLAGS_HAS_AUTOFILL_DATA;
787             }
788             if ((mX&~0x7fff) != 0 || (mY&~0x7fff) != 0
789                     || (mWidth&~0x7fff) != 0 | (mHeight&~0x7fff) != 0) {
790                 flags |= FLAGS_HAS_LARGE_COORDS;
791             }
792             if (mScrollX != 0 || mScrollY != 0) {
793                 flags |= FLAGS_HAS_SCROLL;
794             }
795             if (mMatrix != null) {
796                 flags |= FLAGS_HAS_MATRIX;
797             }
798             if (mElevation != 0) {
799                 flags |= FLAGS_HAS_ELEVATION;
800             }
801             if (mAlpha != 1.0f) {
802                 flags |= FLAGS_HAS_ALPHA;
803             }
804             if (mContentDescription != null) {
805                 flags |= FLAGS_HAS_CONTENT_DESCRIPTION;
806             }
807             if (mText != null) {
808                 flags |= FLAGS_HAS_TEXT;
809                 if (!mText.isSimple()) {
810                     flags |= FLAGS_HAS_COMPLEX_TEXT;
811                 }
812             }
813             if (mInputType != 0) {
814                 flags |= FLAGS_HAS_INPUT_TYPE;
815             }
816             if (mWebDomain != null) {
817                 flags |= FLAGS_HAS_URL;
818             }
819             if (mLocaleList != null) {
820                 flags |= FLAGS_HAS_LOCALE_LIST;
821             }
822             if (mExtras != null) {
823                 flags |= FLAGS_HAS_EXTRAS;
824             }
825             if (mChildren != null) {
826                 flags |= FLAGS_HAS_CHILDREN;
827             }
828 
829             pwriter.writeString(mClassName);
830 
831             int writtenFlags = flags;
832             if ((flags&FLAGS_HAS_AUTOFILL_DATA) != 0 && (mSanitized || !sanitizeOnWrite)) {
833                 // Remove 'checked' from sanitized autofill request.
834                 writtenFlags = flags & ~FLAGS_CHECKED;
835             }
836             if (mAutofillOverlay != null) {
837                 if (mAutofillOverlay.focused) {
838                     writtenFlags |= ViewNode.FLAGS_FOCUSED;
839                 } else {
840                     writtenFlags &= ~ViewNode.FLAGS_FOCUSED;
841                 }
842             }
843 
844             out.writeInt(writtenFlags);
845             if ((flags&FLAGS_HAS_ID) != 0) {
846                 out.writeInt(mId);
847                 if (mId != 0) {
848                     pwriter.writeString(mIdEntry);
849                     if (mIdEntry != null) {
850                         pwriter.writeString(mIdType);
851                         pwriter.writeString(mIdPackage);
852                     }
853                 }
854             }
855 
856             if ((flags&FLAGS_HAS_AUTOFILL_DATA) != 0) {
857                 writeSensitive = mSanitized || !sanitizeOnWrite;
858                 out.writeInt(mSanitized ? 1 : 0);
859                 out.writeParcelable(mAutofillId, 0);
860                 out.writeInt(mAutofillType);
861                 out.writeStringArray(mAutofillHints);
862                 final AutofillValue sanitizedValue;
863                 if (writeSensitive) {
864                     sanitizedValue = mAutofillValue;
865                 } else if (mAutofillOverlay != null && mAutofillOverlay.value != null) {
866                     sanitizedValue = mAutofillOverlay.value;
867                 } else {
868                     sanitizedValue = null;
869                 }
870                 out.writeParcelable(sanitizedValue,  0);
871                 out.writeCharSequenceArray(mAutofillOptions);
872                 if (mHtmlInfo instanceof Parcelable) {
873                     out.writeParcelable((Parcelable) mHtmlInfo, 0);
874                 } else {
875                     out.writeParcelable(null, 0);
876                 }
877             }
878             if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
879                 out.writeInt(mX);
880                 out.writeInt(mY);
881                 out.writeInt(mWidth);
882                 out.writeInt(mHeight);
883             } else {
884                 out.writeInt((mY<<16) | mX);
885                 out.writeInt((mHeight<<16) | mWidth);
886             }
887             if ((flags&FLAGS_HAS_SCROLL) != 0) {
888                 out.writeInt(mScrollX);
889                 out.writeInt(mScrollY);
890             }
891             if ((flags&FLAGS_HAS_MATRIX) != 0) {
892                 mMatrix.getValues(tmpMatrix);
893                 out.writeFloatArray(tmpMatrix);
894             }
895             if ((flags&FLAGS_HAS_ELEVATION) != 0) {
896                 out.writeFloat(mElevation);
897             }
898             if ((flags&FLAGS_HAS_ALPHA) != 0) {
899                 out.writeFloat(mAlpha);
900             }
901             if ((flags&FLAGS_HAS_CONTENT_DESCRIPTION) != 0) {
902                 TextUtils.writeToParcel(mContentDescription, out, 0);
903             }
904             if ((flags&FLAGS_HAS_TEXT) != 0) {
905                 mText.writeToParcel(out, (flags&FLAGS_HAS_COMPLEX_TEXT) == 0, writeSensitive);
906             }
907             if ((flags&FLAGS_HAS_INPUT_TYPE) != 0) {
908                 out.writeInt(mInputType);
909             }
910             if ((flags&FLAGS_HAS_URL) != 0) {
911                 out.writeString(mWebDomain);
912             }
913             if ((flags&FLAGS_HAS_LOCALE_LIST) != 0) {
914                 out.writeParcelable(mLocaleList, 0);
915             }
916             if ((flags&FLAGS_HAS_EXTRAS) != 0) {
917                 out.writeBundle(mExtras);
918             }
919             return flags;
920         }
921 
922         /**
923          * Returns the ID associated with this view, as per {@link View#getId() View.getId()}.
924          */
getId()925         public int getId() {
926             return mId;
927         }
928 
929         /**
930          * If {@link #getId()} is a resource identifier, this is the package name of that
931          * identifier.  See {@link android.view.ViewStructure#setId ViewStructure.setId}
932          * for more information.
933          */
getIdPackage()934         public String getIdPackage() {
935             return mIdPackage;
936         }
937 
938         /**
939          * If {@link #getId()} is a resource identifier, this is the type name of that
940          * identifier.  See {@link android.view.ViewStructure#setId ViewStructure.setId}
941          * for more information.
942          */
getIdType()943         public String getIdType() {
944             return mIdType;
945         }
946 
947         /**
948          * If {@link #getId()} is a resource identifier, this is the entry name of that
949          * identifier.  See {@link android.view.ViewStructure#setId ViewStructure.setId}
950          * for more information.
951          */
getIdEntry()952         public String getIdEntry() {
953             return mIdEntry;
954         }
955 
956         /**
957          * Gets the id that can be used to autofill the view contents.
958          *
959          * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes.
960          *
961          * @return id that can be used to autofill the view contents, or {@code null} if the
962          * structure was created for assist purposes.
963          */
getAutofillId()964         @Nullable public AutofillId getAutofillId() {
965             return mAutofillId;
966         }
967 
968         /**
969          * Gets the the type of value that can be used to autofill the view contents.
970          *
971          * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes.
972          *
973          * @return autofill type as defined by {@link View#getAutofillType()},
974          * or {@link View#AUTOFILL_TYPE_NONE} if the structure was created for assist purposes.
975          */
getAutofillType()976         public @View.AutofillType int getAutofillType() {
977             return mAutofillType;
978         }
979 
980         /**
981          * Describes the content of a view so that a autofill service can fill in the appropriate
982          * data.
983          *
984          * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes,
985          * not for Assist - see {@link View#getAutofillHints()} for more info.
986          *
987          * @return The autofill hints for this view, or {@code null} if the structure was created
988          * for assist purposes.
989          */
getAutofillHints()990         @Nullable public String[] getAutofillHints() {
991             return mAutofillHints;
992         }
993 
994         /**
995          * Gets the the value of this view.
996          *
997          * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes,
998          * not for assist purposes.
999          *
1000          * @return the autofill value of this view, or {@code null} if the structure was created
1001          * for assist purposes.
1002          */
getAutofillValue()1003         @Nullable public AutofillValue getAutofillValue() {
1004             return mAutofillValue;
1005         }
1006 
1007         /** @hide **/
setAutofillOverlay(AutofillOverlay overlay)1008         public void setAutofillOverlay(AutofillOverlay overlay) {
1009             mAutofillOverlay = overlay;
1010         }
1011 
1012         /**
1013          * Gets the options that can be used to autofill this view.
1014          *
1015          * <p>Typically used by nodes whose {@link View#getAutofillType()} is a list to indicate
1016          * the meaning of each possible value in the list.
1017          *
1018          * <p>It's relevant when the {@link AssistStructure} is used for autofill purposes, not
1019          * for assist purposes.
1020          *
1021          * @return the options that can be used to autofill this view, or {@code null} if the
1022          * structure was created for assist purposes.
1023          */
getAutofillOptions()1024         @Nullable public CharSequence[] getAutofillOptions() {
1025             return mAutofillOptions;
1026         }
1027 
1028         /**
1029          * Gets the {@link android.text.InputType} bits of this structure.
1030          *
1031          * @return bits as defined by {@link android.text.InputType}.
1032          */
getInputType()1033         public int getInputType() {
1034             return mInputType;
1035         }
1036 
1037         /** @hide */
isSanitized()1038         public boolean isSanitized() {
1039             return mSanitized;
1040         }
1041 
1042         /**
1043          * Updates the {@link AutofillValue} of this structure.
1044          *
1045          * <p>Should be used just before sending the structure to the
1046          * {@link android.service.autofill.AutofillService} for saving, since it will override the
1047          * initial value.
1048          *
1049          * @hide
1050          */
updateAutofillValue(AutofillValue value)1051         public void updateAutofillValue(AutofillValue value) {
1052             mAutofillValue = value;
1053             if (value.isText()) {
1054                 if (mText == null) {
1055                     mText = new ViewNodeText();
1056                 }
1057                 mText.mText = value.getTextValue();
1058             }
1059         }
1060 
1061         /**
1062          * Returns the left edge of this view, in pixels, relative to the left edge of its parent.
1063          */
getLeft()1064         public int getLeft() {
1065             return mX;
1066         }
1067 
1068         /**
1069          * Returns the top edge of this view, in pixels, relative to the top edge of its parent.
1070          */
getTop()1071         public int getTop() {
1072             return mY;
1073         }
1074 
1075         /**
1076          * Returns the current X scroll offset of this view, as per
1077          * {@link android.view.View#getScrollX() View.getScrollX()}.
1078          */
getScrollX()1079         public int getScrollX() {
1080             return mScrollX;
1081         }
1082 
1083         /**
1084          * Returns the current Y scroll offset of this view, as per
1085          * {@link android.view.View#getScrollX() View.getScrollY()}.
1086          */
getScrollY()1087         public int getScrollY() {
1088             return mScrollY;
1089         }
1090 
1091         /**
1092          * Returns the width of this view, in pixels.
1093          */
getWidth()1094         public int getWidth() {
1095             return mWidth;
1096         }
1097 
1098         /**
1099          * Returns the height of this view, in pixels.
1100          */
getHeight()1101         public int getHeight() {
1102             return mHeight;
1103         }
1104 
1105         /**
1106          * Returns the transformation that has been applied to this view, such as a translation
1107          * or scaling.  The returned Matrix object is owned by ViewNode; do not modify it.
1108          * Returns null if there is no transformation applied to the view.
1109          *
1110          * <p>It's only relevant when the {@link AssistStructure} is used for assist purposes,
1111          * not for autofill purposes.
1112          */
getTransformation()1113         public Matrix getTransformation() {
1114             return mMatrix;
1115         }
1116 
1117         /**
1118          * Returns the visual elevation of the view, used for shadowing and other visual
1119          * characterstics, as set by {@link ViewStructure#setElevation
1120          * ViewStructure.setElevation(float)}.
1121          *
1122          * <p>It's only relevant when the {@link AssistStructure} is used for assist purposes,
1123          * not for autofill purposes.
1124          */
getElevation()1125         public float getElevation() {
1126             return mElevation;
1127         }
1128 
1129         /**
1130          * Returns the alpha transformation of the view, used to reduce the overall opacity
1131          * of the view's contents, as set by {@link ViewStructure#setAlpha
1132          * ViewStructure.setAlpha(float)}.
1133          *
1134          * <p>It's only relevant when the {@link AssistStructure} is used for assist purposes,
1135          * not for autofill purposes.
1136          */
getAlpha()1137         public float getAlpha() {
1138             return mAlpha;
1139         }
1140 
1141         /**
1142          * Returns the visibility mode of this view, as per
1143          * {@link android.view.View#getVisibility() View.getVisibility()}.
1144          */
getVisibility()1145         public int getVisibility() {
1146             return mFlags&ViewNode.FLAGS_VISIBILITY_MASK;
1147         }
1148 
1149         /**
1150          * Returns true if assist data has been blocked starting at this node in the hierarchy.
1151          */
isAssistBlocked()1152         public boolean isAssistBlocked() {
1153             return (mFlags&ViewNode.FLAGS_ASSIST_BLOCKED) != 0;
1154         }
1155 
1156         /**
1157          * Returns true if this node is in an enabled state.
1158          */
isEnabled()1159         public boolean isEnabled() {
1160             return (mFlags&ViewNode.FLAGS_DISABLED) == 0;
1161         }
1162 
1163         /**
1164          * Returns true if this node is clickable by the user.
1165          */
isClickable()1166         public boolean isClickable() {
1167             return (mFlags&ViewNode.FLAGS_CLICKABLE) != 0;
1168         }
1169 
1170         /**
1171          * Returns true if this node can take input focus.
1172          */
isFocusable()1173         public boolean isFocusable() {
1174             return (mFlags&ViewNode.FLAGS_FOCUSABLE) != 0;
1175         }
1176 
1177         /**
1178          * Returns true if this node currently had input focus at the time that the
1179          * structure was collected.
1180          */
isFocused()1181         public boolean isFocused() {
1182             return (mFlags&ViewNode.FLAGS_FOCUSED) != 0;
1183         }
1184 
1185         /**
1186          * Returns true if this node currently had accessibility focus at the time that the
1187          * structure was collected.
1188          */
isAccessibilityFocused()1189         public boolean isAccessibilityFocused() {
1190             return (mFlags&ViewNode.FLAGS_ACCESSIBILITY_FOCUSED) != 0;
1191         }
1192 
1193         /**
1194          * Returns true if this node represents something that is checkable by the user.
1195          */
isCheckable()1196         public boolean isCheckable() {
1197             return (mFlags&ViewNode.FLAGS_CHECKABLE) != 0;
1198         }
1199 
1200         /**
1201          * Returns true if this node is currently in a checked state.
1202          */
isChecked()1203         public boolean isChecked() {
1204             return (mFlags&ViewNode.FLAGS_CHECKED) != 0;
1205         }
1206 
1207         /**
1208          * Returns true if this node has currently been selected by the user.
1209          */
isSelected()1210         public boolean isSelected() {
1211             return (mFlags&ViewNode.FLAGS_SELECTED) != 0;
1212         }
1213 
1214         /**
1215          * Returns true if this node has currently been activated by the user.
1216          */
isActivated()1217         public boolean isActivated() {
1218             return (mFlags&ViewNode.FLAGS_ACTIVATED) != 0;
1219         }
1220 
1221         /**
1222          * Returns true if this node is opaque.
1223          */
isOpaque()1224         public boolean isOpaque() { return (mFlags&ViewNode.FLAGS_OPAQUE) != 0; }
1225 
1226         /**
1227          * Returns true if this node is something the user can perform a long click/press on.
1228          */
isLongClickable()1229         public boolean isLongClickable() {
1230             return (mFlags&ViewNode.FLAGS_LONG_CLICKABLE) != 0;
1231         }
1232 
1233         /**
1234          * Returns true if this node is something the user can perform a context click on.
1235          */
isContextClickable()1236         public boolean isContextClickable() {
1237             return (mFlags&ViewNode.FLAGS_CONTEXT_CLICKABLE) != 0;
1238         }
1239 
1240         /**
1241          * Returns the class name of the node's implementation, indicating its behavior.
1242          * For example, a button will report "android.widget.Button" meaning it behaves
1243          * like a {@link android.widget.Button}.
1244          */
getClassName()1245         public String getClassName() {
1246             return mClassName;
1247         }
1248 
1249         /**
1250          * Returns any content description associated with the node, which semantically describes
1251          * its purpose for accessibility and other uses.
1252          */
getContentDescription()1253         public CharSequence getContentDescription() {
1254             return mContentDescription;
1255         }
1256 
1257         /**
1258          * Returns the domain of the HTML document represented by this view.
1259          *
1260          * <p>Typically used when the view associated with the view is a container for an HTML
1261          * document.
1262          *
1263          * <p><b>Warning:</b> an autofill service cannot trust the value reported by this method
1264          * without verifing its authenticity&mdash;see the "Web security" section of
1265          * {@link android.service.autofill.AutofillService} for more details.
1266          *
1267          * @return domain-only part of the document. For example, if the full URL is
1268          * {@code https://example.com/login?user=my_user}, it returns {@code example.com}.
1269          */
getWebDomain()1270         @Nullable public String getWebDomain() {
1271             return mWebDomain;
1272         }
1273 
1274         /**
1275          * Returns the HTML properties associated with this view.
1276          *
1277          * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes,
1278          * not for assist purposes.
1279          *
1280          * @return the HTML properties associated with this view, or {@code null} if the
1281          * structure was created for assist purposes.
1282          */
getHtmlInfo()1283         @Nullable public HtmlInfo getHtmlInfo() {
1284             return mHtmlInfo;
1285         }
1286 
1287         /**
1288          * Returns the the list of locales associated with this view.
1289          */
getLocaleList()1290         @Nullable public LocaleList getLocaleList() {
1291             return mLocaleList;
1292         }
1293 
1294         /**
1295          * Returns any text associated with the node that is displayed to the user, or null
1296          * if there is none.
1297          */
getText()1298         public CharSequence getText() {
1299             return mText != null ? mText.mText : null;
1300         }
1301 
1302         /**
1303          * If {@link #getText()} is non-null, this is where the current selection starts.
1304          *
1305          * <p>It's only relevant when the {@link AssistStructure} is used for assist purposes,
1306          * not for autofill purposes.
1307          */
getTextSelectionStart()1308         public int getTextSelectionStart() {
1309             return mText != null ? mText.mTextSelectionStart : -1;
1310         }
1311 
1312         /**
1313          * If {@link #getText()} is non-null, this is where the current selection starts.
1314          * If there is no selection, returns the same value as {@link #getTextSelectionStart()},
1315          * indicating the cursor position.
1316          *
1317          * <p>It's only relevant when the {@link AssistStructure} is used for assist purposes,
1318          * not for autofill purposes.
1319          */
getTextSelectionEnd()1320         public int getTextSelectionEnd() {
1321             return mText != null ? mText.mTextSelectionEnd : -1;
1322         }
1323 
1324         /**
1325          * If {@link #getText()} is non-null, this is the main text color associated with it.
1326          * If there is no text color, {@link #TEXT_COLOR_UNDEFINED} is returned.
1327          * Note that the text may also contain style spans that modify the color of specific
1328          * parts of the text.
1329          */
getTextColor()1330         public int getTextColor() {
1331             return mText != null ? mText.mTextColor : TEXT_COLOR_UNDEFINED;
1332         }
1333 
1334         /**
1335          * If {@link #getText()} is non-null, this is the main text background color associated
1336          * with it.
1337          * If there is no text background color, {@link #TEXT_COLOR_UNDEFINED} is returned.
1338          * Note that the text may also contain style spans that modify the color of specific
1339          * parts of the text.
1340          *
1341          * <p>It's only relevant when the {@link AssistStructure} is used for assist purposes,
1342          * not for autofill purposes.
1343          */
getTextBackgroundColor()1344         public int getTextBackgroundColor() {
1345             return mText != null ? mText.mTextBackgroundColor : TEXT_COLOR_UNDEFINED;
1346         }
1347 
1348         /**
1349          * If {@link #getText()} is non-null, this is the main text size (in pixels) associated
1350          * with it.
1351          * Note that the text may also contain style spans that modify the size of specific
1352          * parts of the text.
1353          *
1354          * <p>It's only relevant when the {@link AssistStructure} is used for assist purposes,
1355          * not for autofill purposes.
1356          */
getTextSize()1357         public float getTextSize() {
1358             return mText != null ? mText.mTextSize : 0;
1359         }
1360 
1361         /**
1362          * If {@link #getText()} is non-null, this is the main text style associated
1363          * with it, containing a bit mask of {@link #TEXT_STYLE_BOLD},
1364          * {@link #TEXT_STYLE_BOLD}, {@link #TEXT_STYLE_STRIKE_THRU}, and/or
1365          * {@link #TEXT_STYLE_UNDERLINE}.
1366          * Note that the text may also contain style spans that modify the style of specific
1367          * parts of the text.
1368          *
1369          * <p>It's only relevant when the {@link AssistStructure} is used for assist purposes,
1370          * not for autofill purposes.
1371          */
getTextStyle()1372         public int getTextStyle() {
1373             return mText != null ? mText.mTextStyle : 0;
1374         }
1375 
1376         /**
1377          * Return per-line offsets into the text returned by {@link #getText()}.  Each entry
1378          * in the array is a formatted line of text, and the value it contains is the offset
1379          * into the text string where that line starts.  May return null if there is no line
1380          * information.
1381          *
1382          * <p>It's only relevant when the {@link AssistStructure} is used for assist purposes,
1383          * not for autofill purposes.
1384          */
getTextLineCharOffsets()1385         public int[] getTextLineCharOffsets() {
1386             return mText != null ? mText.mLineCharOffsets : null;
1387         }
1388 
1389         /**
1390          * Return per-line baselines into the text returned by {@link #getText()}.  Each entry
1391          * in the array is a formatted line of text, and the value it contains is the baseline
1392          * where that text appears in the view.  May return null if there is no line
1393          * information.
1394          *
1395          * <p>It's only relevant when the {@link AssistStructure} is used for assist purposes,
1396          * not for autofill purposes.
1397          */
getTextLineBaselines()1398         public int[] getTextLineBaselines() {
1399             return mText != null ? mText.mLineBaselines : null;
1400         }
1401 
1402         /**
1403          * Return additional hint text associated with the node; this is typically used with
1404          * a node that takes user input, describing to the user what the input means.
1405          */
getHint()1406         public String getHint() {
1407             return mText != null ? mText.mHint : null;
1408         }
1409 
1410         /**
1411          * Return a Bundle containing optional vendor-specific extension information.
1412          */
getExtras()1413         public Bundle getExtras() {
1414             return mExtras;
1415         }
1416 
1417         /**
1418          * Return the number of children this node has.
1419          */
getChildCount()1420         public int getChildCount() {
1421             return mChildren != null ? mChildren.length : 0;
1422         }
1423 
1424         /**
1425          * Return a child of this node, given an index value from 0 to
1426          * {@link #getChildCount()}-1.
1427          */
getChildAt(int index)1428         public ViewNode getChildAt(int index) {
1429             return mChildren[index];
1430         }
1431     }
1432 
1433     /**
1434      * POJO used to override some autofill-related values when the node is parcelized.
1435      *
1436      * @hide
1437      */
1438     static public class AutofillOverlay {
1439         public boolean focused;
1440         public AutofillValue value;
1441     }
1442 
1443     static class ViewNodeBuilder extends ViewStructure {
1444         final AssistStructure mAssist;
1445         final ViewNode mNode;
1446         final boolean mAsync;
1447 
ViewNodeBuilder(AssistStructure assist, ViewNode node, boolean async)1448         ViewNodeBuilder(AssistStructure assist, ViewNode node, boolean async) {
1449             mAssist = assist;
1450             mNode = node;
1451             mAsync = async;
1452         }
1453 
1454         @Override
setId(int id, String packageName, String typeName, String entryName)1455         public void setId(int id, String packageName, String typeName, String entryName) {
1456             mNode.mId = id;
1457             mNode.mIdPackage = packageName;
1458             mNode.mIdType = typeName;
1459             mNode.mIdEntry = entryName;
1460         }
1461 
1462         @Override
setDimens(int left, int top, int scrollX, int scrollY, int width, int height)1463         public void setDimens(int left, int top, int scrollX, int scrollY, int width, int height) {
1464             mNode.mX = left;
1465             mNode.mY = top;
1466             mNode.mScrollX = scrollX;
1467             mNode.mScrollY = scrollY;
1468             mNode.mWidth = width;
1469             mNode.mHeight = height;
1470         }
1471 
1472         @Override
setTransformation(Matrix matrix)1473         public void setTransformation(Matrix matrix) {
1474             if (matrix == null) {
1475                 mNode.mMatrix = null;
1476             } else {
1477                 mNode.mMatrix = new Matrix(matrix);
1478             }
1479         }
1480 
1481         @Override
setElevation(float elevation)1482         public void setElevation(float elevation) {
1483             mNode.mElevation = elevation;
1484         }
1485 
1486         @Override
setAlpha(float alpha)1487         public void setAlpha(float alpha) {
1488             mNode.mAlpha = alpha;
1489         }
1490 
1491         @Override
setVisibility(int visibility)1492         public void setVisibility(int visibility) {
1493             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_VISIBILITY_MASK) | visibility;
1494         }
1495 
1496         @Override
setAssistBlocked(boolean state)1497         public void setAssistBlocked(boolean state) {
1498             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ASSIST_BLOCKED)
1499                     | (state ? ViewNode.FLAGS_ASSIST_BLOCKED : 0);
1500         }
1501 
1502         @Override
setEnabled(boolean state)1503         public void setEnabled(boolean state) {
1504             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_DISABLED)
1505                     | (state ? 0 : ViewNode.FLAGS_DISABLED);
1506         }
1507 
1508         @Override
setClickable(boolean state)1509         public void setClickable(boolean state) {
1510             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CLICKABLE)
1511                     | (state ? ViewNode.FLAGS_CLICKABLE : 0);
1512         }
1513 
1514         @Override
setLongClickable(boolean state)1515         public void setLongClickable(boolean state) {
1516             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_LONG_CLICKABLE)
1517                     | (state ? ViewNode.FLAGS_LONG_CLICKABLE : 0);
1518         }
1519 
1520         @Override
setContextClickable(boolean state)1521         public void setContextClickable(boolean state) {
1522             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CONTEXT_CLICKABLE)
1523                     | (state ? ViewNode.FLAGS_CONTEXT_CLICKABLE : 0);
1524         }
1525 
1526         @Override
setFocusable(boolean state)1527         public void setFocusable(boolean state) {
1528             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_FOCUSABLE)
1529                     | (state ? ViewNode.FLAGS_FOCUSABLE : 0);
1530         }
1531 
1532         @Override
setFocused(boolean state)1533         public void setFocused(boolean state) {
1534             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_FOCUSED)
1535                     | (state ? ViewNode.FLAGS_FOCUSED : 0);
1536         }
1537 
1538         @Override
setAccessibilityFocused(boolean state)1539         public void setAccessibilityFocused(boolean state) {
1540             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ACCESSIBILITY_FOCUSED)
1541                     | (state ? ViewNode.FLAGS_ACCESSIBILITY_FOCUSED : 0);
1542         }
1543 
1544         @Override
setCheckable(boolean state)1545         public void setCheckable(boolean state) {
1546             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CHECKABLE)
1547                     | (state ? ViewNode.FLAGS_CHECKABLE : 0);
1548         }
1549 
1550         @Override
setChecked(boolean state)1551         public void setChecked(boolean state) {
1552             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CHECKED)
1553                     | (state ? ViewNode.FLAGS_CHECKED : 0);
1554         }
1555 
1556         @Override
setSelected(boolean state)1557         public void setSelected(boolean state) {
1558             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_SELECTED)
1559                     | (state ? ViewNode.FLAGS_SELECTED : 0);
1560         }
1561 
1562         @Override
setActivated(boolean state)1563         public void setActivated(boolean state) {
1564             mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ACTIVATED)
1565                     | (state ? ViewNode.FLAGS_ACTIVATED : 0);
1566         }
1567 
1568         @Override
setOpaque(boolean opaque)1569         public void setOpaque(boolean opaque) {
1570             mNode.mFlags = (mNode.mFlags & ~ViewNode.FLAGS_OPAQUE)
1571                     | (opaque ? ViewNode.FLAGS_OPAQUE : 0);
1572         }
1573 
1574         @Override
setClassName(String className)1575         public void setClassName(String className) {
1576             mNode.mClassName = className;
1577         }
1578 
1579         @Override
setContentDescription(CharSequence contentDescription)1580         public void setContentDescription(CharSequence contentDescription) {
1581             mNode.mContentDescription = contentDescription;
1582         }
1583 
getNodeText()1584         private final ViewNodeText getNodeText() {
1585             if (mNode.mText != null) {
1586                 return mNode.mText;
1587             }
1588             mNode.mText = new ViewNodeText();
1589             return mNode.mText;
1590         }
1591 
1592         @Override
setText(CharSequence text)1593         public void setText(CharSequence text) {
1594             ViewNodeText t = getNodeText();
1595             t.mText = TextUtils.trimNoCopySpans(text);
1596             t.mTextSelectionStart = t.mTextSelectionEnd = -1;
1597         }
1598 
1599         @Override
setText(CharSequence text, int selectionStart, int selectionEnd)1600         public void setText(CharSequence text, int selectionStart, int selectionEnd) {
1601             ViewNodeText t = getNodeText();
1602             t.mText = TextUtils.trimNoCopySpans(text);
1603             t.mTextSelectionStart = selectionStart;
1604             t.mTextSelectionEnd = selectionEnd;
1605         }
1606 
1607         @Override
setTextStyle(float size, int fgColor, int bgColor, int style)1608         public void setTextStyle(float size, int fgColor, int bgColor, int style) {
1609             ViewNodeText t = getNodeText();
1610             t.mTextColor = fgColor;
1611             t.mTextBackgroundColor = bgColor;
1612             t.mTextSize = size;
1613             t.mTextStyle = style;
1614         }
1615 
1616         @Override
setTextLines(int[] charOffsets, int[] baselines)1617         public void setTextLines(int[] charOffsets, int[] baselines) {
1618             ViewNodeText t = getNodeText();
1619             t.mLineCharOffsets = charOffsets;
1620             t.mLineBaselines = baselines;
1621         }
1622 
1623         @Override
setHint(CharSequence hint)1624         public void setHint(CharSequence hint) {
1625             getNodeText().mHint = hint != null ? hint.toString() : null;
1626         }
1627 
1628         @Override
getText()1629         public CharSequence getText() {
1630             return mNode.mText != null ? mNode.mText.mText : null;
1631         }
1632 
1633         @Override
getTextSelectionStart()1634         public int getTextSelectionStart() {
1635             return mNode.mText != null ? mNode.mText.mTextSelectionStart : -1;
1636         }
1637 
1638         @Override
getTextSelectionEnd()1639         public int getTextSelectionEnd() {
1640             return mNode.mText != null ? mNode.mText.mTextSelectionEnd : -1;
1641         }
1642 
1643         @Override
getHint()1644         public CharSequence getHint() {
1645             return mNode.mText != null ? mNode.mText.mHint : null;
1646         }
1647 
1648         @Override
getExtras()1649         public Bundle getExtras() {
1650             if (mNode.mExtras != null) {
1651                 return mNode.mExtras;
1652             }
1653             mNode.mExtras = new Bundle();
1654             return mNode.mExtras;
1655         }
1656 
1657         @Override
hasExtras()1658         public boolean hasExtras() {
1659             return mNode.mExtras != null;
1660         }
1661 
1662         @Override
setChildCount(int num)1663         public void setChildCount(int num) {
1664             mNode.mChildren = new ViewNode[num];
1665         }
1666 
1667         @Override
addChildCount(int num)1668         public int addChildCount(int num) {
1669             if (mNode.mChildren == null) {
1670                 setChildCount(num);
1671                 return 0;
1672             }
1673             final int start = mNode.mChildren.length;
1674             ViewNode[] newArray = new ViewNode[start + num];
1675             System.arraycopy(mNode.mChildren, 0, newArray, 0, start);
1676             mNode.mChildren = newArray;
1677             return start;
1678         }
1679 
1680         @Override
getChildCount()1681         public int getChildCount() {
1682             return mNode.mChildren != null ? mNode.mChildren.length : 0;
1683         }
1684 
1685         @Override
newChild(int index)1686         public ViewStructure newChild(int index) {
1687             ViewNode node = new ViewNode();
1688             mNode.mChildren[index] = node;
1689             return new ViewNodeBuilder(mAssist, node, false);
1690         }
1691 
1692         @Override
asyncNewChild(int index)1693         public ViewStructure asyncNewChild(int index) {
1694             synchronized (mAssist) {
1695                 ViewNode node = new ViewNode();
1696                 mNode.mChildren[index] = node;
1697                 ViewNodeBuilder builder = new ViewNodeBuilder(mAssist, node, true);
1698                 mAssist.mPendingAsyncChildren.add(builder);
1699                 return builder;
1700             }
1701         }
1702 
1703         @Override
asyncCommit()1704         public void asyncCommit() {
1705             synchronized (mAssist) {
1706                 if (!mAsync) {
1707                     throw new IllegalStateException("Child " + this
1708                             + " was not created with ViewStructure.asyncNewChild");
1709                 }
1710                 if (!mAssist.mPendingAsyncChildren.remove(this)) {
1711                     throw new IllegalStateException("Child " + this + " already committed");
1712                 }
1713                 mAssist.notifyAll();
1714             }
1715         }
1716 
1717         @Override
getTempRect()1718         public Rect getTempRect() {
1719             return mAssist.mTmpRect;
1720         }
1721 
1722         @Override
setAutofillId(@onNull AutofillId id)1723         public void setAutofillId(@NonNull AutofillId id) {
1724             mNode.mAutofillId = id;
1725         }
1726 
1727         @Override
setAutofillId(@onNull AutofillId parentId, int virtualId)1728         public void setAutofillId(@NonNull AutofillId parentId, int virtualId) {
1729             mNode.mAutofillId = new AutofillId(parentId, virtualId);
1730         }
1731 
1732         @Override
getAutofillId()1733         public AutofillId getAutofillId() {
1734             return mNode.mAutofillId;
1735         }
1736 
1737         @Override
setAutofillType(@iew.AutofillType int type)1738         public void setAutofillType(@View.AutofillType int type) {
1739             mNode.mAutofillType = type;
1740         }
1741 
1742         @Override
setAutofillHints(@ullable String[] hints)1743         public void setAutofillHints(@Nullable String[] hints) {
1744             mNode.mAutofillHints = hints;
1745         }
1746 
1747         @Override
setAutofillValue(AutofillValue value)1748         public void setAutofillValue(AutofillValue value) {
1749             mNode.mAutofillValue = value;
1750         }
1751 
1752         @Override
setAutofillOptions(CharSequence[] options)1753         public void setAutofillOptions(CharSequence[] options) {
1754             mNode.mAutofillOptions = options;
1755         }
1756 
1757         @Override
setInputType(int inputType)1758         public void setInputType(int inputType) {
1759             mNode.mInputType = inputType;
1760         }
1761 
1762         @Override
setDataIsSensitive(boolean sensitive)1763         public void setDataIsSensitive(boolean sensitive) {
1764             mNode.mSanitized = !sensitive;
1765         }
1766 
1767         @Override
setWebDomain(@ullable String domain)1768         public void setWebDomain(@Nullable String domain) {
1769             if (domain == null) {
1770                 mNode.mWebDomain = null;
1771                 return;
1772             }
1773             mNode.mWebDomain = Uri.parse(domain).getHost();
1774         }
1775 
1776         @Override
setLocaleList(LocaleList localeList)1777         public void setLocaleList(LocaleList localeList) {
1778             mNode.mLocaleList = localeList;
1779         }
1780 
1781         @Override
newHtmlInfoBuilder(@onNull String tagName)1782         public HtmlInfo.Builder newHtmlInfoBuilder(@NonNull String tagName) {
1783             return new HtmlInfoNodeBuilder(tagName);
1784         }
1785 
1786         @Override
setHtmlInfo(@onNull HtmlInfo htmlInfo)1787         public void setHtmlInfo(@NonNull HtmlInfo htmlInfo) {
1788             mNode.mHtmlInfo = htmlInfo;
1789         }
1790     }
1791 
1792     private static final class HtmlInfoNode extends HtmlInfo implements Parcelable {
1793         private final String mTag;
1794         private final String[] mNames;
1795         private final String[] mValues;
1796 
1797         // Not parcelable
1798         private ArrayList<Pair<String, String>> mAttributes;
1799 
HtmlInfoNode(HtmlInfoNodeBuilder builder)1800         private HtmlInfoNode(HtmlInfoNodeBuilder builder) {
1801             mTag = builder.mTag;
1802             if (builder.mNames == null) {
1803                 mNames = null;
1804                 mValues = null;
1805             } else {
1806                 mNames = new String[builder.mNames.size()];
1807                 mValues = new String[builder.mValues.size()];
1808                 builder.mNames.toArray(mNames);
1809                 builder.mValues.toArray(mValues);
1810             }
1811         }
1812 
1813         @Override
getTag()1814         public String getTag() {
1815             return mTag;
1816         }
1817 
1818         @Override
getAttributes()1819         public List<Pair<String, String>> getAttributes() {
1820             if (mAttributes == null && mNames != null) {
1821                 mAttributes = new ArrayList<>(mNames.length);
1822                 for (int i = 0; i < mNames.length; i++) {
1823                     final Pair<String, String> pair = new Pair<>(mNames[i], mValues[i]);
1824                     mAttributes.add(i, pair);
1825                 }
1826             }
1827             return mAttributes;
1828         }
1829 
1830         @Override
describeContents()1831         public int describeContents() {
1832             return 0;
1833         }
1834 
1835         @Override
writeToParcel(Parcel parcel, int flags)1836         public void writeToParcel(Parcel parcel, int flags) {
1837             parcel.writeString(mTag);
1838             parcel.writeStringArray(mNames);
1839             parcel.writeStringArray(mValues);
1840         }
1841 
1842         @SuppressWarnings("hiding")
1843         public static final Creator<HtmlInfoNode> CREATOR = new Creator<HtmlInfoNode>() {
1844             @Override
1845             public HtmlInfoNode createFromParcel(Parcel parcel) {
1846                 // Always go through the builder to ensure the data ingested by
1847                 // the system obeys the contract of the builder to avoid attacks
1848                 // using specially crafted parcels.
1849                 final String tag = parcel.readString();
1850                 final HtmlInfoNodeBuilder builder = new HtmlInfoNodeBuilder(tag);
1851                 final String[] names = parcel.readStringArray();
1852                 final String[] values = parcel.readStringArray();
1853                 if (names != null && values != null) {
1854                     if (names.length != values.length) {
1855                         Log.w(TAG, "HtmlInfo attributes mismatch: names=" + names.length
1856                                 + ", values=" + values.length);
1857                     } else {
1858                         for (int i = 0; i < names.length; i++) {
1859                             builder.addAttribute(names[i], values[i]);
1860                         }
1861                     }
1862                 }
1863                 return builder.build();
1864             }
1865 
1866             @Override
1867             public HtmlInfoNode[] newArray(int size) {
1868                 return new HtmlInfoNode[size];
1869             }
1870         };
1871     }
1872 
1873     private static final class HtmlInfoNodeBuilder extends HtmlInfo.Builder {
1874         private final String mTag;
1875         private ArrayList<String> mNames;
1876         private ArrayList<String> mValues;
1877 
HtmlInfoNodeBuilder(String tag)1878         HtmlInfoNodeBuilder(String tag) {
1879             mTag = tag;
1880         }
1881 
1882         @Override
addAttribute(String name, String value)1883         public Builder addAttribute(String name, String value) {
1884             if (mNames == null) {
1885                 mNames = new ArrayList<>();
1886                 mValues = new ArrayList<>();
1887             }
1888             mNames.add(name);
1889             mValues.add(value);
1890             return this;
1891         }
1892 
1893         @Override
build()1894         public HtmlInfoNode build() {
1895             return new HtmlInfoNode(this);
1896         }
1897     }
1898 
1899     /** @hide */
AssistStructure(Activity activity, boolean forAutoFill, int flags)1900     public AssistStructure(Activity activity, boolean forAutoFill, int flags) {
1901         mHaveData = true;
1902         mActivityComponent = activity.getComponentName();
1903         mFlags = flags;
1904         ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews(
1905                 activity.getActivityToken());
1906         for (int i=0; i<views.size(); i++) {
1907             ViewRootImpl root = views.get(i);
1908             if (root.getView() == null) {
1909                 Log.w(TAG, "Skipping window with dettached view: " + root.getTitle());
1910                 continue;
1911             }
1912             mWindowNodes.add(new WindowNode(this, root, forAutoFill, flags));
1913         }
1914     }
1915 
AssistStructure()1916     public AssistStructure() {
1917         mHaveData = true;
1918         mActivityComponent = null;
1919         mFlags = 0;
1920     }
1921 
1922     /** @hide */
AssistStructure(Parcel in)1923     public AssistStructure(Parcel in) {
1924         mIsHomeActivity = in.readInt() == 1;
1925         mReceiveChannel = in.readStrongBinder();
1926     }
1927 
1928     /**
1929      * Helper method used to sanitize the structure before it's written to a parcel.
1930      *
1931      * <p>Used just on autofill.
1932      * @hide
1933      */
sanitizeForParceling(boolean sanitize)1934     public void sanitizeForParceling(boolean sanitize) {
1935         mSanitizeOnWrite = sanitize;
1936     }
1937 
1938     /** @hide */
dump(boolean showSensitive)1939     public void dump(boolean showSensitive) {
1940         if (mActivityComponent == null) {
1941             Log.i(TAG, "dump(): calling ensureData() first");
1942             ensureData();
1943         }
1944         Log.i(TAG, "Activity: " + mActivityComponent.flattenToShortString());
1945         Log.i(TAG, "Sanitize on write: " + mSanitizeOnWrite);
1946         Log.i(TAG, "Flags: " + mFlags);
1947         final int N = getWindowNodeCount();
1948         for (int i=0; i<N; i++) {
1949             WindowNode node = getWindowNodeAt(i);
1950             Log.i(TAG, "Window #" + i + " [" + node.getLeft() + "," + node.getTop()
1951                     + " " + node.getWidth() + "x" + node.getHeight() + "]" + " " + node.getTitle());
1952             dump("  ", node.getRootViewNode(), showSensitive);
1953         }
1954     }
1955 
dump(String prefix, ViewNode node, boolean showSensitive)1956     void dump(String prefix, ViewNode node, boolean showSensitive) {
1957         Log.i(TAG, prefix + "View [" + node.getLeft() + "," + node.getTop()
1958                 + " " + node.getWidth() + "x" + node.getHeight() + "]" + " " + node.getClassName());
1959         int id = node.getId();
1960         if (id != 0) {
1961             StringBuilder sb = new StringBuilder();
1962             sb.append(prefix); sb.append("  ID: #"); sb.append(Integer.toHexString(id));
1963             String entry = node.getIdEntry();
1964             if (entry != null) {
1965                 String type = node.getIdType();
1966                 String pkg = node.getIdPackage();
1967                 sb.append(" "); sb.append(pkg); sb.append(":"); sb.append(type);
1968                 sb.append("/"); sb.append(entry);
1969             }
1970             Log.i(TAG, sb.toString());
1971         }
1972         int scrollX = node.getScrollX();
1973         int scrollY = node.getScrollY();
1974         if (scrollX != 0 || scrollY != 0) {
1975             Log.i(TAG, prefix + "  Scroll: " + scrollX + "," + scrollY);
1976         }
1977         Matrix matrix = node.getTransformation();
1978         if (matrix != null) {
1979             Log.i(TAG, prefix + "  Transformation: " + matrix);
1980         }
1981         float elevation = node.getElevation();
1982         if (elevation != 0) {
1983             Log.i(TAG, prefix + "  Elevation: " + elevation);
1984         }
1985         float alpha = node.getAlpha();
1986         if (alpha != 0) {
1987             Log.i(TAG, prefix + "  Alpha: " + elevation);
1988         }
1989         CharSequence contentDescription = node.getContentDescription();
1990         if (contentDescription != null) {
1991             Log.i(TAG, prefix + "  Content description: " + contentDescription);
1992         }
1993         CharSequence text = node.getText();
1994         if (text != null) {
1995             final String safeText = node.isSanitized() || showSensitive ? text.toString()
1996                     : "REDACTED[" + text.length() + " chars]";
1997             Log.i(TAG, prefix + "  Text (sel " + node.getTextSelectionStart() + "-"
1998                     + node.getTextSelectionEnd() + "): " + safeText);
1999             Log.i(TAG, prefix + "  Text size: " + node.getTextSize() + " , style: #"
2000                     + node.getTextStyle());
2001             Log.i(TAG, prefix + "  Text color fg: #" + Integer.toHexString(node.getTextColor())
2002                     + ", bg: #" + Integer.toHexString(node.getTextBackgroundColor()));
2003             Log.i(TAG, prefix + "  Input type: " + node.getInputType());
2004         }
2005         String webDomain = node.getWebDomain();
2006         if (webDomain != null) {
2007             Log.i(TAG, prefix + "  Web domain: " + webDomain);
2008         }
2009         HtmlInfo htmlInfo = node.getHtmlInfo();
2010         if (htmlInfo != null) {
2011             Log.i(TAG, prefix + "  HtmlInfo: tag=" + htmlInfo.getTag()
2012                     + ", attr="+ htmlInfo.getAttributes());
2013         }
2014 
2015         LocaleList localeList = node.getLocaleList();
2016         if (localeList != null) {
2017             Log.i(TAG, prefix + "  LocaleList: " + localeList);
2018         }
2019         String hint = node.getHint();
2020         if (hint != null) {
2021             Log.i(TAG, prefix + "  Hint: " + hint);
2022         }
2023         Bundle extras = node.getExtras();
2024         if (extras != null) {
2025             Log.i(TAG, prefix + "  Extras: " + extras);
2026         }
2027         if (node.isAssistBlocked()) {
2028             Log.i(TAG, prefix + "  BLOCKED");
2029         }
2030         AutofillId autofillId = node.getAutofillId();
2031         if (autofillId == null) {
2032             Log.i(TAG, prefix + " NO autofill ID");
2033         } else {
2034             Log.i(TAG, prefix + "Autofill info: id= " + autofillId
2035                     + ", type=" + node.getAutofillType()
2036                     + ", options=" + Arrays.toString(node.getAutofillOptions())
2037                     + ", hints=" + Arrays.toString(node.getAutofillHints())
2038                     + ", value=" + node.getAutofillValue()
2039                     + ", sanitized=" + node.isSanitized());
2040         }
2041 
2042         final int NCHILDREN = node.getChildCount();
2043         if (NCHILDREN > 0) {
2044             Log.i(TAG, prefix + "  Children:");
2045             String cprefix = prefix + "    ";
2046             for (int i=0; i<NCHILDREN; i++) {
2047                 ViewNode cnode = node.getChildAt(i);
2048                 dump(cprefix, cnode, showSensitive);
2049             }
2050         }
2051     }
2052 
2053     /**
2054      * Return the activity this AssistStructure came from.
2055      */
getActivityComponent()2056     public ComponentName getActivityComponent() {
2057         ensureData();
2058         return mActivityComponent;
2059     }
2060 
2061     /**
2062      * Called by Autofill server when app forged a different value.
2063      *
2064      * @hide
2065      */
setActivityComponent(ComponentName componentName)2066     public void setActivityComponent(ComponentName componentName) {
2067         ensureData();
2068         mActivityComponent = componentName;
2069     }
2070 
2071     /** @hide */
getFlags()2072     public int getFlags() {
2073         return mFlags;
2074     }
2075 
2076     /**
2077      * Returns whether the activity associated with this AssistStructure was the home activity
2078      * (Launcher) at the time the assist data was acquired.
2079      * @return Whether the activity was the home activity.
2080      * @see android.content.Intent#CATEGORY_HOME
2081      */
isHomeActivity()2082     public boolean isHomeActivity() {
2083         return mIsHomeActivity;
2084     }
2085 
2086     /**
2087      * Return the number of window contents that have been collected in this assist data.
2088      */
getWindowNodeCount()2089     public int getWindowNodeCount() {
2090         ensureData();
2091         return mWindowNodes.size();
2092     }
2093 
2094     /**
2095      * Return one of the windows in the assist data.
2096      * @param index Which window to retrieve, may be 0 to {@link #getWindowNodeCount()}-1.
2097      */
getWindowNodeAt(int index)2098     public WindowNode getWindowNodeAt(int index) {
2099         ensureData();
2100         return mWindowNodes.get(index);
2101     }
2102 
2103     /** @hide */
ensureData()2104     public void ensureData() {
2105         if (mHaveData) {
2106             return;
2107         }
2108         mHaveData = true;
2109         ParcelTransferReader reader = new ParcelTransferReader(mReceiveChannel);
2110         reader.go();
2111     }
2112 
waitForReady()2113     boolean waitForReady() {
2114         boolean skipStructure = false;
2115         synchronized (this) {
2116             long endTime = SystemClock.uptimeMillis() + 5000;
2117             long now;
2118             while (mPendingAsyncChildren.size() > 0 && (now=SystemClock.uptimeMillis()) < endTime) {
2119                 try {
2120                     wait(endTime-now);
2121                 } catch (InterruptedException e) {
2122                 }
2123             }
2124             if (mPendingAsyncChildren.size() > 0) {
2125                 // We waited too long, assume none of the assist structure is valid.
2126                 Log.w(TAG, "Skipping assist structure, waiting too long for async children (have "
2127                         + mPendingAsyncChildren.size() + " remaining");
2128                 skipStructure = true;
2129             }
2130         }
2131         return !skipStructure;
2132     }
2133 
2134     /** @hide */
clearSendChannel()2135     public void clearSendChannel() {
2136         if (mSendChannel != null) {
2137             mSendChannel.mAssistStructure = null;
2138         }
2139     }
2140 
2141     @Override
describeContents()2142     public int describeContents() {
2143         return 0;
2144     }
2145 
2146     @Override
writeToParcel(Parcel out, int flags)2147     public void writeToParcel(Parcel out, int flags) {
2148         out.writeInt(mIsHomeActivity ? 1 : 0);
2149         if (mHaveData) {
2150             // This object holds its data.  We want to write a send channel that the
2151             // other side can use to retrieve that data.
2152             if (mSendChannel == null) {
2153                 mSendChannel = new SendChannel(this);
2154             }
2155             out.writeStrongBinder(mSendChannel);
2156         } else {
2157             // This object doesn't hold its data, so just propagate along its receive channel.
2158             out.writeStrongBinder(mReceiveChannel);
2159         }
2160     }
2161 
2162     public static final Parcelable.Creator<AssistStructure> CREATOR
2163             = new Parcelable.Creator<AssistStructure>() {
2164         @Override
2165         public AssistStructure createFromParcel(Parcel in) {
2166             return new AssistStructure(in);
2167         }
2168 
2169         @Override
2170         public AssistStructure[] newArray(int size) {
2171             return new AssistStructure[size];
2172         }
2173     };
2174 }
2175