diff options
| author | Felipe Leme <felipeal@google.com> | 2017-02-02 02:05:29 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2017-02-02 02:05:32 +0000 |
| commit | 5523f9a2fcaf472e92c1d380a79776c4bf857a80 (patch) | |
| tree | 037bdcc7b53d6eec28e5b73e1039e2ec5345f4d4 /core/java/android | |
| parent | 82233e92036718f95c37d4ae5cb5ce9b27d39ff0 (diff) | |
| parent | 0200d9ea1509089c0c03b7071aa271e3a9b35c11 (diff) | |
Merge "Improved AutoFill Save workflow."
Diffstat (limited to 'core/java/android')
21 files changed, 375 insertions, 200 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index b367d0cb4a1b..5b05d581c4e0 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -505,9 +505,6 @@ public class ActivityManager { /** @hide requestType for assist context: generate full AssistStructure for auto-fill. */ public static final int ASSIST_CONTEXT_AUTO_FILL = 2; - /** @hide requestType for assist context: generate full AssistStructure for auto-fill save. */ - public static final int ASSIST_CONTEXT_AUTO_FILL_SAVE = 3; - /** @hide Flag for registerUidObserver: report changes in process state. */ public static final int UID_OBSERVER_PROCSTATE = 1<<0; diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index cf20b68cf200..d5371f8b7b3c 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -91,7 +91,6 @@ import android.security.NetworkSecurityPolicy; import android.security.net.config.NetworkSecurityConfigProvider; import android.service.autofill.AutoFillService; import android.service.autofill.IAutoFillAppCallback; -import android.service.voice.VoiceInteractionSession; import android.util.AndroidRuntimeException; import android.util.ArrayMap; import android.util.DisplayMetrics; @@ -641,7 +640,6 @@ public final class ActivityThread { IBinder requestToken; int requestType; int sessionId; - int flags; } static final class ActivityConfigChangeData { @@ -1249,13 +1247,12 @@ public final class ActivityThread { @Override public void requestAssistContextExtras(IBinder activityToken, IBinder requestToken, - int requestType, int sessionId, int flags) { + int requestType, int sessionId) { RequestAssistContextExtras cmd = new RequestAssistContextExtras(); cmd.activityToken = activityToken; cmd.requestToken = requestToken; cmd.requestType = requestType; cmd.sessionId = sessionId; - cmd.flags = flags; sendMessage(H.REQUEST_ASSIST_CONTEXT_EXTRAS, cmd); } @@ -2905,9 +2902,7 @@ public final class ActivityThread { // - it does not need an AssistContent // - it does not call onProvideAssistData() // - it needs an IAutoFillCallback - // - it sets the flags so views can provide autofill-specific data (such as passwords) - boolean forAutoFill = cmd.requestType == ActivityManager.ASSIST_CONTEXT_AUTO_FILL - || cmd.requestType == ActivityManager.ASSIST_CONTEXT_AUTO_FILL_SAVE; + boolean forAutoFill = cmd.requestType == ActivityManager.ASSIST_CONTEXT_AUTO_FILL; // TODO(b/33197203): decide if lastSessionId logic applies to auto-fill sessions if (mLastSessionId != cmd.sessionId) { @@ -2934,11 +2929,8 @@ public final class ActivityThread { referrer = r.activity.onProvideReferrer(); } if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL || forAutoFill) { - structure = new AssistStructure(r.activity, cmd.flags); + structure = new AssistStructure(r.activity, forAutoFill); Intent activityIntent = r.activity.getIntent(); - if (forAutoFill) { - data.putInt(VoiceInteractionSession.KEY_FLAGS, cmd.flags); - } boolean addAutoFillCallback = false; // TODO(b/33197203): re-evaluate conditions below for auto-fill. In particular, // FLAG_SECURE might be allowed on AUTO_FILL but not on AUTO_FILL_SAVE) @@ -2979,6 +2971,7 @@ public final class ActivityThread { if (structure == null) { structure = new AssistStructure(); } + // TODO(b/33197203): decide if lastSessionId logic applies to auto-fill sessions mLastAssistStructures.add(new WeakReference<>(structure)); IActivityManager mgr = ActivityManager.getService(); diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 0a2f8049dc66..3cc6282d6ab3 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -584,7 +584,7 @@ interface IActivityManager { void unregisterTaskStackListener(ITaskStackListener listener); void moveStackToDisplay(int stackId, int displayId); boolean requestAutoFillData(in IResultReceiver receiver, in Bundle receiverExtras, - int resultCode, in IBinder activityToken, int flags); + int resultCode, in IBinder activityToken); void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback); int restartUserInBackground(int userId); diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 41d1255c0a28..4fc6fb9eee2e 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -134,7 +134,7 @@ oneway interface IApplicationThread { void dumpDbInfo(in ParcelFileDescriptor fd, in String[] args); void unstableProviderDied(IBinder provider); void requestAssistContextExtras(IBinder activityToken, IBinder requestToken, - int requestType, int sessionId, int flags); + int requestType, int sessionId); void scheduleTranslucentConversionComplete(IBinder token, boolean timeout); void setProcessState(int state); void scheduleInstallProvider(in ProviderInfo provider); diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index b94264e1ac5d..08aa5f2589bf 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -22,6 +22,7 @@ import android.view.ViewRootImpl; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.autofill.AutoFillType; +import android.view.autofill.AutoFillValue; import android.view.autofill.AutoFillId; import java.util.ArrayList; @@ -53,6 +54,8 @@ public class AssistStructure implements Parcelable { Rect mTmpRect = new Rect(); + boolean mSanitizeOnWrite = false; + static final int TRANSACTION_XFER = Binder.FIRST_CALL_TRANSACTION+1; static final String DESCRIPTOR = "android.app.AssistStructure"; @@ -113,8 +116,10 @@ public class AssistStructure implements Parcelable { int mNumWrittenWindows; int mNumWrittenViews; final float[] mTmpMatrix = new float[9]; + final boolean mSanitizeOnWrite; ParcelTransferWriter(AssistStructure as, Parcel out) { + mSanitizeOnWrite = as.mSanitizeOnWrite; mWriteStructure = as.waitForReady(); ComponentName.writeToParcel(as.mActivityComponent, out); mNumWindows = as.mWindowNodes.size(); @@ -186,7 +191,7 @@ public class AssistStructure implements Parcelable { + ", views=" + mNumWrittenViews + ", level=" + (mCurViewStackPos+levelAdj)); out.writeInt(VALIDATE_VIEW_TOKEN); - int flags = child.writeSelfToParcel(out, pwriter, mTmpMatrix); + int flags = child.writeSelfToParcel(out, pwriter, mSanitizeOnWrite, mTmpMatrix); mNumWrittenViews++; // If the child has children, push it on the stack to write them next. if ((flags&ViewNode.FLAGS_HAS_CHILDREN) != 0) { @@ -374,8 +379,8 @@ public class AssistStructure implements Parcelable { } } - void writeToParcel(Parcel out, boolean simple) { - TextUtils.writeToParcel(mText, out, 0); + void writeToParcel(Parcel out, boolean simple, boolean writeSensitive) { + TextUtils.writeToParcel(writeSensitive ? mText : "", out, 0); out.writeFloat(mTextSize); out.writeInt(mTextStyle); out.writeInt(mTextColor); @@ -402,7 +407,7 @@ public class AssistStructure implements Parcelable { final int mDisplayId; final ViewNode mRoot; - WindowNode(AssistStructure assist, ViewRootImpl root, int flags) { + WindowNode(AssistStructure assist, ViewRootImpl root, boolean forAutoFill) { View view = root.getView(); Rect rect = new Rect(); view.getBoundsOnScreen(rect); @@ -414,19 +419,14 @@ public class AssistStructure implements Parcelable { mDisplayId = root.getDisplayId(); mRoot = new ViewNode(); - // Must explicitly call the proper method based on flags since we don't know which - // method (if any) was overridden by the View subclass. - boolean forAutoFill = (flags - & (View.AUTO_FILL_FLAG_TYPE_FILL - | View.AUTO_FILL_FLAG_TYPE_SAVE)) != 0; - - ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false); + ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false, 0); if ((root.getWindowFlags()& WindowManager.LayoutParams.FLAG_SECURE) != 0) { // This is a secure window, so it doesn't want a screenshot, and that // means we should also not copy out its view hierarchy. if (forAutoFill) { - view.onProvideAutoFillStructure(builder, flags); + // NOTE: flags are currently not supported, hence 0 + view.onProvideAutoFillStructure(builder, 0); } else { view.onProvideStructure(builder); } @@ -434,7 +434,8 @@ public class AssistStructure implements Parcelable { return; } if (forAutoFill) { - view.dispatchProvideAutoFillStructure(builder, flags); + // NOTE: flags are currently not supported, hence 0 + view.dispatchProvideAutoFillStructure(builder, 0); } else { view.dispatchProvideStructure(builder); } @@ -537,6 +538,8 @@ public class AssistStructure implements Parcelable { // fields (viewId and childId) of the field. AutoFillId mAutoFillId; AutoFillType mAutoFillType; + AutoFillValue mAutoFillValue; + boolean mSanitized; int mX; int mY; int mScrollX; @@ -610,8 +613,10 @@ public class AssistStructure implements Parcelable { } } if ((flags&FLAGS_HAS_AUTO_FILL_DATA) != 0) { + mSanitized = in.readInt() == 1; mAutoFillId = in.readParcelable(null); mAutoFillType = in.readParcelable(null); + mAutoFillValue = in.readParcelable(null); } if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) { mX = in.readInt(); @@ -663,7 +668,11 @@ public class AssistStructure implements Parcelable { } } - int writeSelfToParcel(Parcel out, PooledStringWriter pwriter, float[] tmpMatrix) { + int writeSelfToParcel(Parcel out, PooledStringWriter pwriter, boolean sanitizeOnWrite, + float[] tmpMatrix) { + // Guard used to skip non-sanitized data when writing for auto-fill. + boolean writeSensitive = true; + int flags = mFlags & ~FLAGS_ALL_CONTROL; if (mId != View.NO_ID) { flags |= FLAGS_HAS_ID; @@ -716,8 +725,12 @@ public class AssistStructure implements Parcelable { } } if ((flags&FLAGS_HAS_AUTO_FILL_DATA) != 0) { + writeSensitive = mSanitized || !sanitizeOnWrite; + out.writeInt(mSanitized ? 1 : 0); out.writeParcelable(mAutoFillId, 0); out.writeParcelable(mAutoFillType, 0); + final AutoFillValue sanitizedValue = writeSensitive ? mAutoFillValue : null; + out.writeParcelable(sanitizedValue, 0); } if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) { out.writeInt(mX); @@ -746,7 +759,7 @@ public class AssistStructure implements Parcelable { TextUtils.writeToParcel(mContentDescription, out, 0); } if ((flags&FLAGS_HAS_TEXT) != 0) { - mText.writeToParcel(out, (flags&FLAGS_HAS_COMPLEX_TEXT) == 0); + mText.writeToParcel(out, (flags&FLAGS_HAS_COMPLEX_TEXT) == 0, writeSensitive); } if ((flags&FLAGS_HAS_EXTRAS) != 0) { out.writeBundle(mExtras); @@ -794,6 +807,7 @@ public class AssistStructure implements Parcelable { * <p>It's only set when the {@link AssistStructure} is used for auto-filling purposes, not * for assist. */ + // TODO(b/33197203, b/33802548): add CTS/unit test public AutoFillId getAutoFillId() { return mAutoFillId; } @@ -804,11 +818,47 @@ public class AssistStructure implements Parcelable { * <p>It's only set when the {@link AssistStructure} is used for auto-filling purposes, not * for assist. */ + // TODO(b/33197203, b/33802548): add CTS/unit test public AutoFillType getAutoFillType() { return mAutoFillType; } /** + * Gets the the value of this view. + * + * <p>It's only set when the {@link AssistStructure} is used for auto-filling purposes, not + * for assist. + */ + // TODO(b/33197203, b/33802548): add CTS/unit test + public AutoFillValue getAutoFillValue() { + return mAutoFillValue; + } + + /** @hide */ + public boolean isSanitized() { + return mSanitized; + } + + /** + * Updates the {@link AutoFillValue} of this structure. + * + * <p>Should be used just before sending the structure to the + * {@link android.service.autofill.AutoFillService} for saving, since it will override the + * initial value. + * + * @hide + */ + public void updateAutoFillValue(AutoFillValue value) { + mAutoFillValue = value; + // TODO(b/33197203, b/33802548): decide whether to set text as well (so it would work + // with "legacy" views) or just the auto-fill value + final CharSequence text = value.getTextValue(); + if (text != null) { + mText.mText = text; + } + } + + /** * Returns the left edge of this view, in pixels, relative to the left edge of its parent. */ public int getLeft() { @@ -1113,10 +1163,11 @@ public class AssistStructure implements Parcelable { final ViewNode mNode; final boolean mAsync; - ViewNodeBuilder(AssistStructure assist, ViewNode node, boolean async) { + ViewNodeBuilder(AssistStructure assist, ViewNode node, boolean async, int flags) { mAssist = assist; mNode = node; mAsync = async; + mNode.mSanitized = (flags & AUTO_FILL_FLAG_SANITIZED) != 0; } @Override @@ -1350,19 +1401,20 @@ public class AssistStructure implements Parcelable { } } - private ViewStructure newChild(int index, boolean forAutoFill, int virtualId) { + private ViewStructure newChild(int index, boolean forAutoFill, int virtualId, int flags) { ViewNode node = new ViewNode(); setAutoFillId(node, forAutoFill, virtualId); mNode.mChildren[index] = node; - return new ViewNodeBuilder(mAssist, node, false); + return new ViewNodeBuilder(mAssist, node, false, flags); } - private ViewStructure asyncNewChild(int index, boolean forAutoFill, int virtualId) { + private ViewStructure asyncNewChild(int index, boolean forAutoFill, int virtualId, + int flags) { synchronized (mAssist) { ViewNode node = new ViewNode(); setAutoFillId(node, forAutoFill, virtualId); mNode.mChildren[index] = node; - ViewNodeBuilder builder = new ViewNodeBuilder(mAssist, node, true); + ViewNodeBuilder builder = new ViewNodeBuilder(mAssist, node, true, flags); mAssist.mPendingAsyncChildren.add(builder); return builder; } @@ -1370,22 +1422,23 @@ public class AssistStructure implements Parcelable { @Override public ViewStructure newChild(int index) { - return newChild(index, false, 0); + return newChild(index, false, 0, 0); } + // TODO(b/33197203, b/33802548): add CTS/unit test @Override - public ViewStructure newChild(int index, int virtualId) { - return newChild(index, true, virtualId); + public ViewStructure newChild(int index, int virtualId, int flags) { + return newChild(index, true, virtualId, flags); } @Override public ViewStructure asyncNewChild(int index) { - return asyncNewChild(index, false, 0); + return asyncNewChild(index, false, 0, 0); } @Override - public ViewStructure asyncNewChild(int index, int virtualId) { - return asyncNewChild(index, true, virtualId); + public ViewStructure asyncNewChild(int index, int virtualId, int flags) { + return asyncNewChild(index, true, virtualId, flags); } @Override @@ -1422,17 +1475,29 @@ public class AssistStructure implements Parcelable { mNode.mAutoFillType = type; } + @Override + public void setAutoFillValue(AutoFillValue value) { + mNode.mAutoFillValue = value; + } + + /** + * @hide + */ + @Override + public void setSanitized(boolean sanitized) { + mNode.mSanitized = sanitized; + } } /** @hide */ - public AssistStructure(Activity activity, int flags) { + public AssistStructure(Activity activity, boolean forAutoFill) { mHaveData = true; mActivityComponent = activity.getComponentName(); ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews( activity.getActivityToken()); for (int i=0; i<views.size(); i++) { ViewRootImpl root = views.get(i); - mWindowNodes.add(new WindowNode(this, root, flags)); + mWindowNodes.add(new WindowNode(this, root, forAutoFill)); } } @@ -1446,9 +1511,24 @@ public class AssistStructure implements Parcelable { mReceiveChannel = in.readStrongBinder(); } + /** + * Helper method used to sanitize the structure before it's written to a parcel. + * + * <p>Used just on auto-fill. + * @hide + */ + public void sanitizeForParceling(boolean sanitize) { + mSanitizeOnWrite = sanitize; + } + /** @hide */ public void dump() { + if (mActivityComponent == null) { + Log.i(TAG, "dump(): calling ensureData() first"); + ensureData(); + } Log.i(TAG, "Activity: " + mActivityComponent.flattenToShortString()); + Log.i(TAG, "Sanitize on write: " + mSanitizeOnWrite); final int N = getWindowNodeCount(); for (int i=0; i<N; i++) { WindowNode node = getWindowNodeAt(i); @@ -1515,6 +1595,16 @@ public class AssistStructure implements Parcelable { if (node.isAssistBlocked()) { Log.i(TAG, prefix + " BLOCKED"); } + AutoFillId autoFillId = node.getAutoFillId(); + if (autoFillId == null) { + Log.i(TAG, prefix + " NO AUTO-FILL ID"); + } else { + Log.i(TAG, prefix + "AutoFill info: id= " + autoFillId + + ", type=" + node.getAutoFillType() + + ", value=" + node.getAutoFillValue() + + ", sanitized=" + node.isSanitized()); + } + final int NCHILDREN = node.getChildCount(); if (NCHILDREN > 0) { Log.i(TAG, prefix + " Children:"); @@ -1589,10 +1679,12 @@ public class AssistStructure implements Parcelable { } } + @Override public int describeContents() { return 0; } + @Override public void writeToParcel(Parcel out, int flags) { if (mHaveData) { // This object holds its data. We want to write a send channel that the @@ -1609,10 +1701,12 @@ public class AssistStructure implements Parcelable { public static final Parcelable.Creator<AssistStructure> CREATOR = new Parcelable.Creator<AssistStructure>() { + @Override public AssistStructure createFromParcel(Parcel in) { return new AssistStructure(in); } + @Override public AssistStructure[] newArray(int size) { return new AssistStructure[size]; } diff --git a/core/java/android/service/autofill/AutoFillService.java b/core/java/android/service/autofill/AutoFillService.java index 1e4f90d1ed07..bfaf23cd5fae 100644 --- a/core/java/android/service/autofill/AutoFillService.java +++ b/core/java/android/service/autofill/AutoFillService.java @@ -15,9 +15,7 @@ */ package android.service.autofill; -import static android.view.View.AUTO_FILL_FLAG_TYPE_FILL; -import static android.view.View.AUTO_FILL_FLAG_TYPE_SAVE; - +import android.annotation.CallSuper; import android.annotation.SdkConstant; import android.app.Activity; import android.app.Service; @@ -28,8 +26,8 @@ import android.os.CancellationSignal; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.RemoteException; import android.util.Log; -import android.view.autofill.AutoFillId; import android.view.autofill.Dataset; import android.view.autofill.FillResponse; @@ -127,14 +125,22 @@ public abstract class AutoFillService extends Service { private static final int MSG_AUTO_FILL_ACTIVITY = 3; private static final int MSG_AUTHENTICATE_FILL_RESPONSE = 4; private static final int MSG_AUTHENTICATE_DATASET = 5; + private static final int MSG_SAVE = 6; private final IAutoFillService mInterface = new IAutoFillService.Stub() { @Override - public void autoFill(AssistStructure structure, IAutoFillServerCallback callback, - int flags) { + public void autoFill(AssistStructure structure, IAutoFillServerCallback callback) { mHandlerCaller - .obtainMessageIOO(MSG_AUTO_FILL_ACTIVITY, flags, structure, callback) + .obtainMessageOO(MSG_AUTO_FILL_ACTIVITY, structure, callback) + .sendToTarget(); + } + + @Override + public void save(AssistStructure structure, IAutoFillServerCallback callback, + Bundle extras) throws RemoteException { + mHandlerCaller + .obtainMessageOOO(MSG_SAVE, structure, callback, extras) .sendToTarget(); } @@ -175,10 +181,26 @@ public abstract class AutoFillService extends Service { break; } case MSG_AUTO_FILL_ACTIVITY: { final SomeArgs args = (SomeArgs) msg.obj; - final int flags = msg.arg1; - final AssistStructure structure = (AssistStructure) args.arg1; - final IAutoFillServerCallback callback = (IAutoFillServerCallback) args.arg2; - requestAutoFill(callback, structure, flags); + try { + final AssistStructure structure = (AssistStructure) args.arg1; + final IAutoFillServerCallback callback = + (IAutoFillServerCallback) args.arg2; + handleAutoFill(structure, callback); + } finally { + args.recycle(); + } + break; + } case MSG_SAVE: { + final SomeArgs args = (SomeArgs) msg.obj; + try { + final AssistStructure structure = (AssistStructure) args.arg1; + final IAutoFillServerCallback callback = + (IAutoFillServerCallback) args.arg2; + final Bundle extras = (Bundle) args.arg3; + handleSave(structure, callback, extras); + } finally { + args.recycle(); + } break; } case MSG_AUTHENTICATE_FILL_RESPONSE: { final int flags = msg.arg1; @@ -258,7 +280,7 @@ public abstract class AutoFillService extends Service { * Called when user requests service to save the fields of an {@link Activity}. * * <p>Service must call one of the {@link SaveCallback} methods (like - * {@link SaveCallback#onSuccess(AutoFillId[])} or {@link SaveCallback#onFailure(CharSequence)}) + * {@link SaveCallback#onSuccess()} or {@link SaveCallback#onFailure(CharSequence)}) * to notify the result of the request. * * @param structure {@link Activity}'s view structure. @@ -313,9 +335,8 @@ public abstract class AutoFillService extends Service { if (DEBUG) Log.d(TAG, "onDatasetAuthenticationRequest(): flags=" + flags); } - // TODO(b/33197203): make it final and create another method classes could extend so it's - // guaranteed to dump the pending callbacks? @Override + @CallSuper protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mPendingCallbacks != null) { pw.print("Number of pending callbacks: "); pw.println(mPendingCallbacks.size()); @@ -331,29 +352,23 @@ public abstract class AutoFillService extends Service { } } - private void requestAutoFill(IAutoFillServerCallback callback, AssistStructure structure, - int flags) { - if (DEBUG) Log.d(TAG, "requestAutoFill(): flags=" + flags); - - if ((flags & AUTO_FILL_FLAG_TYPE_FILL) != 0) { - final FillCallback fillCallback = new FillCallback(callback); - if (DEBUG_PENDING_CALLBACKS) { - addPendingCallback(fillCallback); - } - // TODO(b/33197203): hook up the cancelationSignal - onFillRequest(structure, null, new CancellationSignal(), fillCallback); - return; - } - if ((flags & AUTO_FILL_FLAG_TYPE_SAVE) != 0) { - final SaveCallback saveCallback = new SaveCallback(callback); - if (DEBUG_PENDING_CALLBACKS) { - addPendingCallback(saveCallback); - } - onSaveRequest(structure, null, saveCallback); - return; + private void handleAutoFill(AssistStructure structure, IAutoFillServerCallback callback) { + final FillCallback fillCallback = new FillCallback(callback); + if (DEBUG_PENDING_CALLBACKS) { + addPendingCallback(fillCallback); } + // TODO(b/33197203): hook up the cancelationSignal + onFillRequest(structure, null, new CancellationSignal(), fillCallback); + return; + } - Log.w(TAG, "invalid flags on requestAutoFill(): " + flags); + private void handleSave(AssistStructure structure, IAutoFillServerCallback callback, + Bundle extras) { + final SaveCallback saveCallback = new SaveCallback(callback); + if (DEBUG_PENDING_CALLBACKS) { + addPendingCallback(saveCallback); + } + onSaveRequest(structure, extras, saveCallback); } private void addPendingCallback(CallbackHelper.Dumpable callback) { diff --git a/core/java/android/service/autofill/IAutoFillManagerService.aidl b/core/java/android/service/autofill/IAutoFillManagerService.aidl index ace5411ca0d3..088e6498a0d5 100644 --- a/core/java/android/service/autofill/IAutoFillManagerService.aidl +++ b/core/java/android/service/autofill/IAutoFillManagerService.aidl @@ -19,6 +19,7 @@ package android.service.autofill; import android.graphics.Rect; import android.os.Bundle; import android.view.autofill.AutoFillId; +import android.view.autofill.AutoFillValue; /** * Mediator between apps being auto-filled and auto-fill service implementations. @@ -30,6 +31,12 @@ oneway interface IAutoFillManagerService { // Called by AutoFillManager (app). void requestAutoFill(in AutoFillId id, in Rect bounds, int flags); + // Called by AutoFillManager (app). + void onValueChanged(in AutoFillId id, in AutoFillValue value); + + // Called by ShellCommand only. + void requestAutoFillForUser(int userId); + // Called by ShellCommand only. - void requestAutoFillForUser(int userId, int flags); + void requestSaveForUser(int userId); } diff --git a/core/java/android/service/autofill/IAutoFillServerCallback.aidl b/core/java/android/service/autofill/IAutoFillServerCallback.aidl index f7d5064e9e26..480438ac44ea 100644 --- a/core/java/android/service/autofill/IAutoFillServerCallback.aidl +++ b/core/java/android/service/autofill/IAutoFillServerCallback.aidl @@ -33,7 +33,7 @@ oneway interface IAutoFillServerCallback { // TODO(b/33197203): document methods void showResponse(in FillResponse response); void showError(CharSequence message); - void highlightSavedFields(in AutoFillId[] ids); + void onSaved(); void unlockFillResponse(int flags); void unlockDataset(in Dataset dataset, int flags); } diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl index 3e8087b2a5a2..a4e6ebc996d3 100644 --- a/core/java/android/service/autofill/IAutoFillService.aidl +++ b/core/java/android/service/autofill/IAutoFillService.aidl @@ -27,7 +27,8 @@ import com.android.internal.os.IResultReceiver; // TODO(b/33197203): document class and methods oneway interface IAutoFillService { // TODO(b/33197203): rename method to make them more consistent - void autoFill(in AssistStructure structure, in IAutoFillServerCallback callback, int flags); + void autoFill(in AssistStructure structure, in IAutoFillServerCallback callback); + void save(in AssistStructure structure, in IAutoFillServerCallback callback, in Bundle extras); void authenticateFillResponse(in Bundle extras, int flags); void authenticateDataset(in Bundle extras, int flags); void onConnected(); diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java index e2fb588db212..9dd979599f5d 100644 --- a/core/java/android/service/autofill/SaveCallback.java +++ b/core/java/android/service/autofill/SaveCallback.java @@ -57,23 +57,18 @@ public final class SaveCallback implements Dumpable { /** * Notifies the Android System that an - * {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle, + * {@link AutoFillService#onSaveRequest (android.app.assist.AssistStructure, Bundle, * SaveCallback)} was successfully fulfilled by the service. * - * @param ids ids ({@link ViewNode#getAutoFillId()}) of the fields that were saved. - * * @throws RuntimeException if an error occurred while calling the Android System. */ - public void onSuccess(AutoFillId[] ids) { - if (DEBUG) Log.d(TAG, "onSuccess(): ids=" + ((ids == null) ? "null" : ids.length)); - - Preconditions.checkArgument(ids != null, "ids cannot be null"); - Preconditions.checkArgument(ids.length > 0, "ids cannot be empty"); + public void onSuccess() { + if (DEBUG) Log.d(TAG, "onSuccess()"); synchronized (mCallback) { checkNotRepliedYetLocked(); try { - mCallback.highlightSavedFields(ids); + mCallback.onSaved(); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } finally { @@ -94,8 +89,6 @@ public final class SaveCallback implements Dumpable { public void onFailure(CharSequence message) { if (DEBUG) Log.d(TAG, "onFailure(): message=" + message); - Preconditions.checkArgument(message != null, "message cannot be null"); - synchronized (mCallback) { checkNotRepliedYetLocked(); diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index 48f3ac3f06f7..e9bbc2de26cc 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -119,8 +119,6 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall public static final String KEY_CONTENT = "content"; /** @hide */ public static final String KEY_RECEIVER_EXTRAS = "receiverExtras"; - /** @hide */ - public static final String KEY_FLAGS = "flags"; final Context mContext; final HandlerCaller mHandlerCaller; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 12ba30cf3308..e2eee9516142 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -39,7 +39,6 @@ import android.annotation.Nullable; import android.annotation.Size; import android.annotation.TestApi; import android.annotation.UiThread; -import android.app.Application.OnProvideAssistDataListener; import android.content.ClipData; import android.content.Context; import android.content.ContextWrapper; @@ -4023,39 +4022,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int mLayerType = LAYER_TYPE_NONE; Paint mLayerPaint; - - /** - * Set when a request was made to decide if views in an {@link android.app.Activity} can be - * auto-filled by an {@link android.service.autofill.AutoFillService}. - * - * <p>Since this request is made without a explicit user consent, the resulting - * {@link android.app.assist.AssistStructure} should not contain any PII - * (Personally Identifiable Information). - * - * <p>Examples: - * <ul> - * <li>{@link android.widget.TextView} texts should only be included when they were set by - * static resources. - * <li>{@link android.webkit.WebView} virtual children should be restricted to a subset of - * input fields and tags (like {@code id}). - * </ul> - */ - // TODO(b/33197203): cannot conflict with flags defined on AutoFillManager until they're removed - // (when save is refactored). - public static final int AUTO_FILL_FLAG_TYPE_FILL = 0x10000000; - - /** - * Set when the user explicitly asked a {@link android.service.autofill.AutoFillService} to save - * the value of the {@link View}s in an {@link android.app.Activity}. - * - * <p>The resulting {@link android.app.assist.AssistStructure} can contain any kind of PII - * (Personally Identifiable Information). For example, the text of password fields should be - * included since that's what's typically saved. - */ - // TODO(b/33197203): cannot conflict with flags defined on AutoFillManager until they're removed - // (when save is refactored). - public static final int AUTO_FILL_FLAG_TYPE_SAVE = 0x20000000; - /** * Set to true when drawing cache is enabled and cannot be created. * @@ -6914,7 +6880,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * fills in all data that can be inferred from the view itself. */ public void onProvideStructure(ViewStructure structure) { - onProvideStructureForAssistOrAutoFill(structure, 0); + onProvideStructureForAssistOrAutoFill(structure, false); } /** @@ -6925,19 +6891,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @param structure Fill in with structured view data. The default implementation * fills in all data that can be inferred from the view itself. - * @param flags optional flags (see {@link #AUTO_FILL_FLAG_TYPE_FILL} and - * {@link #AUTO_FILL_FLAG_TYPE_SAVE} for more info). + * @param flags optional flags (currently {@code 0}). */ public void onProvideAutoFillStructure(ViewStructure structure, int flags) { - onProvideStructureForAssistOrAutoFill(structure, flags); + onProvideStructureForAssistOrAutoFill(structure, true); } - private void onProvideStructureForAssistOrAutoFill(ViewStructure structure, int flags) { - // NOTE: currently flags are only used for AutoFill; if they're used for Assist as well, - // this method should take a boolean with the type of request. - boolean forAutoFill = (flags - & (View.AUTO_FILL_FLAG_TYPE_FILL - | View.AUTO_FILL_FLAG_TYPE_SAVE)) != 0; + private void onProvideStructureForAssistOrAutoFill(ViewStructure structure, + boolean forAutoFill) { final int id = mID; if (id != NO_ID && !isViewIdGenerated(id)) { String pkg, type, entry; @@ -7011,7 +6972,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * optimal implementation providing this data. */ public void onProvideVirtualStructure(ViewStructure structure) { - onProvideVirtualStructureForAssistOrAutoFill(structure, 0); + onProvideVirtualStructureForAssistOrAutoFill(structure, false); } /** @@ -7026,14 +6987,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@code flags} parameter - see the documentation on each flag for more details. * * @param structure Fill in with structured view data. - * @param flags optional flags (see {@link #AUTO_FILL_FLAG_TYPE_FILL} and - * {@link #AUTO_FILL_FLAG_TYPE_SAVE} for more info). + * @param flags optional flags (currently {@code 0}). */ public void onProvideAutoFillVirtualStructure(ViewStructure structure, int flags) { - onProvideVirtualStructureForAssistOrAutoFill(structure, flags); + onProvideVirtualStructureForAssistOrAutoFill(structure, true); } - private void onProvideVirtualStructureForAssistOrAutoFill(ViewStructure structure, int flags) { + private void onProvideVirtualStructureForAssistOrAutoFill(ViewStructure structure, + boolean forAutoFill) { // NOTE: currently flags are only used for AutoFill; if they're used for Assist as well, // this method should take a boolean with the type of request. AccessibilityNodeProvider provider = getAccessibilityNodeProvider(); @@ -7041,7 +7002,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityNodeInfo info = createAccessibilityNodeInfo(); structure.setChildCount(1); ViewStructure root = structure.newChild(0); - populateVirtualStructure(root, provider, info, flags); + populateVirtualStructure(root, provider, info, forAutoFill); info.recycle(); } } @@ -7051,9 +7012,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * this view. * * <p>By default returns {@code null} but should be overridden when view provides a virtual - * hierachy on {@link OnProvideAssistDataListener} that takes flags used by the AutoFill - * Framework (such as {@link #AUTO_FILL_FLAG_TYPE_FILL} and - * {@link #AUTO_FILL_FLAG_TYPE_SAVE}). + * hierachy on {@link #onProvideAutoFillVirtualStructure(ViewStructure, int)}. */ @Nullable public VirtualViewDelegate getAutoFillVirtualViewDelegate( @@ -7109,12 +7068,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } private void populateVirtualStructure(ViewStructure structure, - AccessibilityNodeProvider provider, AccessibilityNodeInfo info, int flags) { - // NOTE: currently flags are only used for AutoFill; if they're used for Assist as well, - // this method should take a boolean with the type of request. - - final boolean sanitized = (flags & View.AUTO_FILL_FLAG_TYPE_FILL) != 0; - + AccessibilityNodeProvider provider, AccessibilityNodeInfo info, boolean forAutoFill) { structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()), null, null, null); Rect rect = structure.getTempRect(); @@ -7152,7 +7106,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, CharSequence cname = info.getClassName(); structure.setClassName(cname != null ? cname.toString() : null); structure.setContentDescription(info.getContentDescription()); - if (!sanitized && (info.getText() != null || info.getError() != null)) { + if (!forAutoFill && (info.getText() != null || info.getError() != null)) { // TODO(b/33197203) (b/33269702): when sanitized, try to use the Accessibility API to // just set sanitized values (like text coming from resource files), rather than not // setting it at all. @@ -7166,7 +7120,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityNodeInfo cinfo = provider.createAccessibilityNodeInfo( AccessibilityNodeInfo.getVirtualDescendantId(info.getChildId(i))); ViewStructure child = structure.newChild(i); - populateVirtualStructure(child, provider, cinfo, flags); + populateVirtualStructure(child, provider, cinfo, forAutoFill); cinfo.recycle(); } } @@ -7178,7 +7132,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link #onProvideVirtualStructure}. */ public void dispatchProvideStructure(ViewStructure structure) { - dispatchProvideStructureForAssistOrAutoFill(structure, 0); + dispatchProvideStructureForAssistOrAutoFill(structure, false); } /** @@ -7191,25 +7145,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * and {@link #onProvideAutoFillVirtualStructure(ViewStructure, int)}. * * @param structure Fill in with structured view data. - * @param flags optional flags (see {@link #AUTO_FILL_FLAG_TYPE_FILL} and - * {@link #AUTO_FILL_FLAG_TYPE_SAVE} for more info). + * @param flags optional flags (currently {@code 0}). */ public void dispatchProvideAutoFillStructure(ViewStructure structure, int flags) { - dispatchProvideStructureForAssistOrAutoFill(structure, flags); + dispatchProvideStructureForAssistOrAutoFill(structure, true); } - private void dispatchProvideStructureForAssistOrAutoFill(ViewStructure structure, int flags) { - // NOTE: currently flags are only used for AutoFill; if they're used for Assist as well, - // this method should take a boolean with the type of request. - boolean forAutoFill = (flags - & (View.AUTO_FILL_FLAG_TYPE_FILL - | View.AUTO_FILL_FLAG_TYPE_SAVE)) != 0; - + private void dispatchProvideStructureForAssistOrAutoFill(ViewStructure structure, + boolean forAutoFill) { boolean blocked = forAutoFill ? isAutoFillBlocked() : isAssistBlocked(); if (!blocked) { if (forAutoFill) { - onProvideAutoFillStructure(structure, flags); - onProvideAutoFillVirtualStructure(structure, flags); + // NOTE: flags are not currently supported, hence 0 + onProvideAutoFillStructure(structure, 0); + onProvideAutoFillVirtualStructure(structure, 0); } else { onProvideStructure(structure); onProvideVirtualStructure(structure); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index ee92744ea1d0..b11b3d7ef44a 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -3207,7 +3207,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager @Override public void dispatchProvideStructure(ViewStructure structure) { super.dispatchProvideStructure(structure); - dispatchProvideStructureForAssistOrAutoFill(structure, 0); + dispatchProvideStructureForAssistOrAutoFill(structure, false); } /** @@ -3219,16 +3219,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager @Override public void dispatchProvideAutoFillStructure(ViewStructure structure, int flags) { super.dispatchProvideAutoFillStructure(structure, flags); - dispatchProvideStructureForAssistOrAutoFill(structure, flags); + dispatchProvideStructureForAssistOrAutoFill(structure, true); } - private void dispatchProvideStructureForAssistOrAutoFill(ViewStructure structure, int flags) { - // NOTE: currently flags are only used for AutoFill; if they're used for Assist as well, - // this method should take a boolean with the type of request. - boolean forAutoFill = (flags - & (View.AUTO_FILL_FLAG_TYPE_FILL - | View.AUTO_FILL_FLAG_TYPE_SAVE)) != 0; - + private void dispatchProvideStructureForAssistOrAutoFill(ViewStructure structure, + boolean forAutoFill) { boolean blocked = forAutoFill ? isAutoFillBlocked() : isAssistBlocked(); if (!blocked) { @@ -3295,7 +3290,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // Must explicitly check which recursive method to call. if (forAutoFill) { - child.dispatchProvideAutoFillStructure(cstructure, flags); + // NOTE: flags are not currently supported, hence 0 + child.dispatchProvideAutoFillStructure(cstructure, 0); } else { child.dispatchProvideStructure(cstructure); } diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java index 839e11c01303..5bae594b5659 100644 --- a/core/java/android/view/ViewStructure.java +++ b/core/java/android/view/ViewStructure.java @@ -29,6 +29,15 @@ import android.view.autofill.VirtualViewDelegate; * View.onProvideStructure}. */ public abstract class ViewStructure { + + /** + * Flag used when adding virtual views for auto-fill, it indicates the contents of the view + * (such as * {@link android.app.assist.AssistStructure.ViewNode#getText()} and + * {@link android.app.assist.AssistStructure.ViewNode#getAutoFillValue()}) + * can be passed to the {@link android.service.autofill.AutoFillService}. + */ + public static final int AUTO_FILL_FLAG_SANITIZED = 0x1; + /** * Set the identifier for this view. * @@ -264,8 +273,14 @@ public abstract class ViewStructure { /** * Like {@link #newChild(int)}, but providing a {@code virtualId} to the child so it can be * auto-filled by {@link VirtualViewDelegate#autoFill(int, AutoFillValue)}. + * + * @param index child index + * @param virtualId child's id as defined by {@link VirtualViewDelegate#autoFill(int, + * AutoFillValue)}. + * @param flags currently either {@code 0} or {@link #AUTO_FILL_FLAG_SANITIZED}. */ - public abstract ViewStructure newChild(int index, int virtualId); + // TODO(b/33197203, b/33802548): add CTS/unit test + public abstract ViewStructure newChild(int index, int virtualId, int flags); /** * Like {@link #newChild}, but allows the caller to asynchronously populate the returned @@ -280,15 +295,36 @@ public abstract class ViewStructure { /** * Like {@link #asyncNewChild(int)}, but providing a {@code virtualId} to the child so it can be * auto-filled by {@link VirtualViewDelegate#autoFill(int, AutoFillValue)}. + * + * @param index child index + * @param virtualId child's id as defined by {@link VirtualViewDelegate#autoFill(int, + * AutoFillValue)}. + * @param flags currently either {@code 0} or {@link #AUTO_FILL_FLAG_SANITIZED}. */ - public abstract ViewStructure asyncNewChild(int index, int virtualId); + // TODO(b/33197203, b/33802548): add CTS/unit test + public abstract ViewStructure asyncNewChild(int index, int virtualId, int flags); /** * Sets the {@link AutoFillType} that can be used to auto-fill this node. */ + // TODO(b/33197203, b/33802548): add CTS/unit test public abstract void setAutoFillType(AutoFillType info); /** + * Sets the {@link AutoFillValue} representing the current value of this node. + */ + // TODO(b/33197203, b/33802548): add CTS/unit test + public abstract void setAutoFillValue(AutoFillValue value); + + /** + * @hide + * + * TODO(b/33197203, b/33269702): temporary set it as not sanitized until + * AssistStructure automaticaly sets sanitization based on text coming from resources + */ + public abstract void setSanitized(boolean sensitive); + + /** * Call when done populating a {@link ViewStructure} returned by * {@link #asyncNewChild}. */ diff --git a/core/java/android/view/autofill/AutoFillId.java b/core/java/android/view/autofill/AutoFillId.java index e9c1c3bccd72..3dbf5a8b0415 100644 --- a/core/java/android/view/autofill/AutoFillId.java +++ b/core/java/android/view/autofill/AutoFillId.java @@ -29,6 +29,7 @@ public final class AutoFillId implements Parcelable { private boolean mVirtual; private int mVirtualId; + // TODO(b/33197203): use factory and cache values, since they're immutable /** @hide */ public AutoFillId(int id) { mVirtual = false; diff --git a/core/java/android/view/autofill/AutoFillManager.java b/core/java/android/view/autofill/AutoFillManager.java index cf56e0e95aae..f2f522daa707 100644 --- a/core/java/android/view/autofill/AutoFillManager.java +++ b/core/java/android/view/autofill/AutoFillManager.java @@ -36,15 +36,11 @@ public final class AutoFillManager { /** * Flag used to show the auto-fill UI affordance for a view. */ - // TODO(b/33197203): cannot conflict with flags defined on View until they're removed (when - // save is refactored). public static final int FLAG_UPDATE_UI_SHOW = 0x1; /** * Flag used to hide the auto-fill UI affordance for a view. */ - // TODO(b/33197203): cannot conflict with flags defined on View until they're removed (when - // save is refactored). public static final int FLAG_UPDATE_UI_HIDE = 0x2; private final IAutoFillManagerService mService; @@ -71,7 +67,7 @@ public final class AutoFillManager { final Rect bounds = new Rect(); view.getBoundsOnScreen(bounds); - requestAutoFill(new AutoFillId(view.getAccessibilityViewId()), bounds, flags); + requestAutoFill(getAutoFillId(view), bounds, flags); } /** @@ -92,7 +88,30 @@ public final class AutoFillManager { requestAutoFill(new AutoFillId(parent.getAccessibilityViewId(), childId), bounds, flags); } + /** + * Notifies the framework that the value of a view changed. + * @param view view whose value was updated + * @param value new value. + */ + public void onValueChanged(View view, AutoFillValue value) { + // TODO(b/33197203): optimize it by not calling service when the view does not belong to + // the session. + final AutoFillId id = getAutoFillId(view); + if (DEBUG) Log.v(TAG, "onValueChanged(): id=" + id + ", value=" + value); + try { + mService.onValueChanged(id, value); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + private AutoFillId getAutoFillId(View view) { + return new AutoFillId(view.getAccessibilityViewId()); + } + private void requestAutoFill(AutoFillId id, Rect bounds, int flags) { + // TODO(b/33197203): optimize it by not calling service when the view does not belong to + // the session. if (DEBUG) { Log.v(TAG, "requestAutoFill(): id=" + id + ", bounds=" + bounds + ", flags=" + flags); } diff --git a/core/java/android/view/autofill/AutoFillValue.java b/core/java/android/view/autofill/AutoFillValue.java index c39f26b9b864..57b23ef4976e 100644 --- a/core/java/android/view/autofill/AutoFillValue.java +++ b/core/java/android/view/autofill/AutoFillValue.java @@ -126,6 +126,7 @@ public final class AutoFillValue implements Parcelable { * * <p>See {@link AutoFillType#isText()} for more info. */ + // TODO(b/33197203): use cache public static AutoFillValue forText(CharSequence value) { return new AutoFillValue(value, 0, false); } diff --git a/core/java/android/view/autofill/VirtualViewDelegate.java b/core/java/android/view/autofill/VirtualViewDelegate.java index 278bf4f34c36..e465c679e5f7 100644 --- a/core/java/android/view/autofill/VirtualViewDelegate.java +++ b/core/java/android/view/autofill/VirtualViewDelegate.java @@ -27,9 +27,10 @@ import android.view.ViewStructure; * * <p>The view hierarchy is typically created through the * {@link View#onProvideAutoFillVirtualStructure(android.view.ViewStructure, int)} call and client - * add virtual children by calling {@link ViewStructure#newChild(int, int)} or - * {@link ViewStructure#asyncNewChild(int, int)}, where the client provides the {@code virtualId} - * of the children - the same {@code virtualId} is used in the methods of this class. + * add virtual children by calling {@link ViewStructure#newChild(int, int, int)} or + * {@link ViewStructure#asyncNewChild(int, int, int)}, where the client provides the + * {@code virtualId} of the children - the same {@code virtualId} is used in the methods of this + * class. * * <p>Objects of this class are typically created by overriding * {@link View#getAutoFillVirtualViewDelegate(Callback)} and saving the passed callback, which must diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index 718070d0d49a..500f381e6bb8 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -32,6 +32,7 @@ import android.view.Gravity; import android.view.SoundEffectConstants; import android.view.ViewDebug; import android.view.ViewHierarchyEncoder; +import android.view.ViewStructure; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.autofill.AutoFillType; @@ -564,6 +565,13 @@ public abstract class CompoundButton extends Button implements Checkable { // TODO(b/33197203): add unit/CTS tests for auto-fill methods @Override + public void onProvideAutoFillStructure(ViewStructure structure, int flags) { + super.onProvideAutoFillStructure(structure, flags); + structure.setAutoFillValue(AutoFillValue.forToggle(isChecked())); + // TODO(b/33197203): add unit/CTS tests for auto-fill methods + } + + @Override public void autoFill(AutoFillValue value) { setChecked(value.getToggleValue()); } diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java index 45fd9e6ef5a7..72dc1cc5c63c 100644 --- a/core/java/android/widget/RadioGroup.java +++ b/core/java/android/widget/RadioGroup.java @@ -24,6 +24,7 @@ import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.ViewGroup; +import android.view.ViewStructure; import android.view.autofill.AutoFillType; import android.view.autofill.AutoFillValue; @@ -406,6 +407,12 @@ public class RadioGroup extends LinearLayout { // TODO(b/33197203): add unit/CTS tests for auto-fill methods @Override + public void onProvideAutoFillStructure(ViewStructure structure, int flags) { + super.onProvideAutoFillStructure(structure, flags); + structure.setAutoFillValue(AutoFillValue.forList(getCheckedRadioButtonId())); + } + + @Override public void autoFill(AutoFillValue value) { final int index = value.getListValue(); final View child = getChildAt(index); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index a7a8fb45d365..4a8ec94962d1 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -711,6 +711,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Contains the sorted set of desired text sizes in pixels to pick from when auto-sizing text. private int[] mAutoSizeTextSizesInPx; + // Watcher used to notify changes to auto-fill manager. + private AutoFillChangeWatcher mAutoFillChangeWatcher; + /** * Kick-start the font cache for the zygote process (to pay the cost of * initializing freetype for our default font only once). @@ -9699,24 +9702,31 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public void onProvideStructure(ViewStructure structure) { super.onProvideStructure(structure); - onProvideAutoStructureForAssistOrAutoFill(structure, 0); + onProvideAutoStructureForAssistOrAutoFill(structure, false); } @Override public void onProvideAutoFillStructure(ViewStructure structure, int flags) { super.onProvideAutoFillStructure(structure, flags); - onProvideAutoStructureForAssistOrAutoFill(structure, flags); + onProvideAutoStructureForAssistOrAutoFill(structure, true); } - private void onProvideAutoStructureForAssistOrAutoFill(ViewStructure structure, int flags) { - // NOTE: currently flags are only used for AutoFill; if they're used for Assist as well, - // this method should take a boolean with the type of request. - final boolean forAutoFillSave = - (flags & AUTO_FILL_FLAG_TYPE_SAVE) != 0; - + private void onProvideAutoStructureForAssistOrAutoFill(ViewStructure structure, + boolean forAutoFill) { final boolean isPassword = hasPasswordTransformationMethod() || isPasswordInputType(getInputType()); - if (!isPassword || forAutoFillSave) { + if (forAutoFill) { + // TODO(b/33197203, b/33269702): temporary set it as not sanitized until + // AssistStructure automaticaly sets sanitization based on text coming from resources + structure.setSanitized(!isPassword); + if (mAutoFillChangeWatcher == null && isTextEditable()) { + mAutoFillChangeWatcher = new AutoFillChangeWatcher(); + addTextChangedListener(mAutoFillChangeWatcher); + // TODO(b/33197203): remove mAutoFillValueListener auto-fill session is finished + } + } + + if (!isPassword || forAutoFill) { if (mLayout == null) { assumeLayout(); } @@ -9724,7 +9734,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int lineCount = layout.getLineCount(); if (lineCount <= 1) { // Simple case: this is a single line. - structure.setText(getText(), getSelectionStart(), getSelectionEnd()); + final CharSequence text = getText(); + structure.setText(text, getSelectionStart(), getSelectionEnd()); + if (forAutoFill && isTextEditable()) { + structure.setAutoFillValue(AutoFillValue.forText(text)); + } } else { // Complex case: multi-line, could be scrolled or within a scroll container // so some lines are not visible. @@ -9781,6 +9795,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener text = text.subSequence(expandedTopChar, expandedBottomChar); } structure.setText(text, selStart - expandedTopChar, selEnd - expandedTopChar); + if (forAutoFill && isTextEditable()) { + structure.setAutoFillValue(AutoFillValue.forText(text)); + } final int[] lineOffsets = new int[bottomLine - topLine + 1]; final int[] lineBaselines = new int[bottomLine - topLine + 1]; final int baselineOffset = getBaselineOffset(); @@ -9828,7 +9845,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final CharSequence text = value.getTextValue(); if (text != null && isTextEditable()) { - setText(text); + if (mAutoFillChangeWatcher == null || mAutoFillChangeWatcher.mOnAutoFill) { + setText(text, mBufferType, true, 0); + } else { + // Must disable listener first so it's not triggered. + mAutoFillChangeWatcher.mOnAutoFill = true; + try { + setText(text, mBufferType, true, 0); + } finally { + mAutoFillChangeWatcher.mOnAutoFill = false; + } + } } } @@ -11183,6 +11210,38 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + // TODO(b/33197203): implements SpanWatcher too? + private final class AutoFillChangeWatcher implements TextWatcher { + + private boolean mOnAutoFill; + private final AutoFillManager mAfm = mContext.getSystemService(AutoFillManager.class); + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void afterTextChanged(Editable s) { + if (mOnAutoFill) { + if (DEBUG_AUTOFILL) { + Log.v(LOG_TAG, "AutoFillChangeWatcher.afterTextChanged() skipped during " + + "autoFill(): s=" + s); + } + return; + } + if (mAfm != null) { + if (DEBUG_AUTOFILL) { + Log.v(LOG_TAG, "AutoFillChangeWatcher.afterTextChanged(): s=" + s); + } + mAfm.onValueChanged(TextView.this, AutoFillValue.forText(s)); + } + } + } + private class ChangeWatcher implements TextWatcher, SpanWatcher { private CharSequence mBeforeText; |
