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