diff options
28 files changed, 805 insertions, 1096 deletions
diff --git a/Android.mk b/Android.mk index 9ebc276d4a84..28adbcaf88fc 100644 --- a/Android.mk +++ b/Android.mk @@ -264,8 +264,6 @@ LOCAL_SRC_FILES += \ core/java/android/os/storage/IObbActionListener.aidl \ core/java/android/security/IKeystoreService.aidl \ core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl \ - core/java/android/service/autofill/IAutoFillAppCallback.aidl \ - core/java/android/service/autofill/IAutoFillManagerService.aidl \ core/java/android/service/autofill/IAutoFillService.aidl \ core/java/android/service/autofill/IFillCallback.aidl \ core/java/android/service/autofill/ISaveCallback.aidl \ @@ -318,6 +316,8 @@ LOCAL_SRC_FILES += \ core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl\ core/java/android/view/accessibility/IAccessibilityManager.aidl \ core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \ + core/java/android/view/autofill/IAutoFillManager.aidl \ + core/java/android/view/autofill/IAutoFillManagerClient.aidl \ core/java/android/view/IApplicationToken.aidl \ core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl \ core/java/android/view/IDockedStackListener.aidl \ diff --git a/api/current.txt b/api/current.txt index 88e99e7adbc7..10b87f2bb7ea 100644 --- a/api/current.txt +++ b/api/current.txt @@ -9182,10 +9182,6 @@ package android.content { field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD"; field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE"; field public static final java.lang.String EXTRA_ASSIST_UID = "android.intent.extra.ASSIST_UID"; - field public static final java.lang.String EXTRA_AUTO_FILL_ASSIST_STRUCTURE = "android.intent.extra.AUTO_FILL_ASSIST_STRUCTURE"; - field public static final java.lang.String EXTRA_AUTO_FILL_CALLBACK = "android.intent.extra.AUTO_FILL_CALLBACK"; - field public static final java.lang.String EXTRA_AUTO_FILL_EXTRAS = "android.intent.extra.AUTO_FILL_EXTRAS"; - field public static final java.lang.String EXTRA_AUTO_FILL_ITEM_ID = "android.intent.extra.AUTO_FILL_ITEM_ID"; field public static final java.lang.String EXTRA_BCC = "android.intent.extra.BCC"; field public static final java.lang.String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT"; field public static final java.lang.String EXTRA_CC = "android.intent.extra.CC"; @@ -36204,12 +36200,37 @@ package android.service.autofill { field public static final java.lang.String SERVICE_META_DATA = "android.autofill"; } - public final class FillCallback implements android.os.Parcelable { + public final class Dataset implements android.os.Parcelable { method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.service.autofill.Dataset> CREATOR; + } + + public static final class Dataset.Builder { + ctor public Dataset.Builder(java.lang.CharSequence); + method public android.service.autofill.Dataset build(); + method public android.service.autofill.Dataset.Builder setAuthentication(android.content.IntentSender); + method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue); + } + + public final class FillCallback { method public void onFailure(java.lang.CharSequence); - method public void onSuccess(android.view.autofill.FillResponse); + method public void onSuccess(android.service.autofill.FillResponse); + } + + public final class FillResponse implements android.os.Parcelable { + method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.service.autofill.FillCallback> CREATOR; + field public static final android.os.Parcelable.Creator<android.service.autofill.FillResponse> CREATOR; + } + + public static final class FillResponse.Builder { + ctor public FillResponse.Builder(); + method public android.service.autofill.FillResponse.Builder addDataset(android.service.autofill.Dataset); + method public android.service.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...); + method public android.service.autofill.FillResponse build(); + method public android.service.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender); + method public android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle); } public final class SaveCallback { @@ -46854,6 +46875,8 @@ package android.view.autofill { method public void valueChanged(android.view.View); method public void virtualFocusChanged(android.view.View, int, android.graphics.Rect, boolean); method public void virtualValueChanged(android.view.View, int, android.view.autofill.AutoFillValue); + field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE"; + field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT"; } public final class AutoFillType implements android.os.Parcelable { @@ -46881,35 +46904,6 @@ package android.view.autofill { field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillValue> CREATOR; } - public final class Dataset implements android.os.Parcelable { - method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.view.autofill.Dataset> CREATOR; - } - - public static final class Dataset.Builder { - ctor public Dataset.Builder(java.lang.String, java.lang.CharSequence); - method public android.view.autofill.Dataset build(); - method public android.view.autofill.Dataset.Builder setAuthentication(android.content.IntentSender); - method public android.view.autofill.Dataset.Builder setExtras(android.os.Bundle); - method public android.view.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue); - } - - public final class FillResponse implements android.os.Parcelable { - method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.view.autofill.FillResponse> CREATOR; - } - - public static final class FillResponse.Builder { - ctor public FillResponse.Builder(java.lang.String); - method public android.view.autofill.FillResponse.Builder addDataset(android.view.autofill.Dataset); - method public android.view.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...); - method public android.view.autofill.FillResponse build(); - method public android.view.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender); - method public android.view.autofill.FillResponse.Builder setExtras(android.os.Bundle); - } - } package android.view.inputmethod { diff --git a/api/system-current.txt b/api/system-current.txt index ee6814b3b52e..5cd64f61dfc9 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -9620,10 +9620,6 @@ package android.content { field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD"; field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE"; field public static final java.lang.String EXTRA_ASSIST_UID = "android.intent.extra.ASSIST_UID"; - field public static final java.lang.String EXTRA_AUTO_FILL_ASSIST_STRUCTURE = "android.intent.extra.AUTO_FILL_ASSIST_STRUCTURE"; - field public static final java.lang.String EXTRA_AUTO_FILL_CALLBACK = "android.intent.extra.AUTO_FILL_CALLBACK"; - field public static final java.lang.String EXTRA_AUTO_FILL_EXTRAS = "android.intent.extra.AUTO_FILL_EXTRAS"; - field public static final java.lang.String EXTRA_AUTO_FILL_ITEM_ID = "android.intent.extra.AUTO_FILL_ITEM_ID"; field public static final java.lang.String EXTRA_BCC = "android.intent.extra.BCC"; field public static final java.lang.String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT"; field public static final java.lang.String EXTRA_CC = "android.intent.extra.CC"; @@ -39270,12 +39266,37 @@ package android.service.autofill { field public static final java.lang.String SERVICE_META_DATA = "android.autofill"; } - public final class FillCallback implements android.os.Parcelable { + public final class Dataset implements android.os.Parcelable { method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.service.autofill.Dataset> CREATOR; + } + + public static final class Dataset.Builder { + ctor public Dataset.Builder(java.lang.CharSequence); + method public android.service.autofill.Dataset build(); + method public android.service.autofill.Dataset.Builder setAuthentication(android.content.IntentSender); + method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue); + } + + public final class FillCallback { method public void onFailure(java.lang.CharSequence); - method public void onSuccess(android.view.autofill.FillResponse); + method public void onSuccess(android.service.autofill.FillResponse); + } + + public final class FillResponse implements android.os.Parcelable { + method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.service.autofill.FillCallback> CREATOR; + field public static final android.os.Parcelable.Creator<android.service.autofill.FillResponse> CREATOR; + } + + public static final class FillResponse.Builder { + ctor public FillResponse.Builder(); + method public android.service.autofill.FillResponse.Builder addDataset(android.service.autofill.Dataset); + method public android.service.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...); + method public android.service.autofill.FillResponse build(); + method public android.service.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender); + method public android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle); } public final class SaveCallback { @@ -50293,6 +50314,8 @@ package android.view.autofill { method public void valueChanged(android.view.View); method public void virtualFocusChanged(android.view.View, int, android.graphics.Rect, boolean); method public void virtualValueChanged(android.view.View, int, android.view.autofill.AutoFillValue); + field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE"; + field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT"; } public final class AutoFillType implements android.os.Parcelable { @@ -50320,35 +50343,6 @@ package android.view.autofill { field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillValue> CREATOR; } - public final class Dataset implements android.os.Parcelable { - method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.view.autofill.Dataset> CREATOR; - } - - public static final class Dataset.Builder { - ctor public Dataset.Builder(java.lang.String, java.lang.CharSequence); - method public android.view.autofill.Dataset build(); - method public android.view.autofill.Dataset.Builder setAuthentication(android.content.IntentSender); - method public android.view.autofill.Dataset.Builder setExtras(android.os.Bundle); - method public android.view.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue); - } - - public final class FillResponse implements android.os.Parcelable { - method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.view.autofill.FillResponse> CREATOR; - } - - public static final class FillResponse.Builder { - ctor public FillResponse.Builder(java.lang.String); - method public android.view.autofill.FillResponse.Builder addDataset(android.view.autofill.Dataset); - method public android.view.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...); - method public android.view.autofill.FillResponse build(); - method public android.view.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender); - method public android.view.autofill.FillResponse.Builder setExtras(android.os.Bundle); - } - } package android.view.inputmethod { diff --git a/api/test-current.txt b/api/test-current.txt index 49b6f61e51f7..0f55684fa21a 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -9208,10 +9208,6 @@ package android.content { field public static final java.lang.String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD"; field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE"; field public static final java.lang.String EXTRA_ASSIST_UID = "android.intent.extra.ASSIST_UID"; - field public static final java.lang.String EXTRA_AUTO_FILL_ASSIST_STRUCTURE = "android.intent.extra.AUTO_FILL_ASSIST_STRUCTURE"; - field public static final java.lang.String EXTRA_AUTO_FILL_CALLBACK = "android.intent.extra.AUTO_FILL_CALLBACK"; - field public static final java.lang.String EXTRA_AUTO_FILL_EXTRAS = "android.intent.extra.AUTO_FILL_EXTRAS"; - field public static final java.lang.String EXTRA_AUTO_FILL_ITEM_ID = "android.intent.extra.AUTO_FILL_ITEM_ID"; field public static final java.lang.String EXTRA_BCC = "android.intent.extra.BCC"; field public static final java.lang.String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT"; field public static final java.lang.String EXTRA_CC = "android.intent.extra.CC"; @@ -36340,12 +36336,37 @@ package android.service.autofill { field public static final java.lang.String SERVICE_META_DATA = "android.autofill"; } - public final class FillCallback implements android.os.Parcelable { + public final class Dataset implements android.os.Parcelable { method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.service.autofill.Dataset> CREATOR; + } + + public static final class Dataset.Builder { + ctor public Dataset.Builder(java.lang.CharSequence); + method public android.service.autofill.Dataset build(); + method public android.service.autofill.Dataset.Builder setAuthentication(android.content.IntentSender); + method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue); + } + + public final class FillCallback { method public void onFailure(java.lang.CharSequence); - method public void onSuccess(android.view.autofill.FillResponse); + method public void onSuccess(android.service.autofill.FillResponse); + } + + public final class FillResponse implements android.os.Parcelable { + method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.service.autofill.FillCallback> CREATOR; + field public static final android.os.Parcelable.Creator<android.service.autofill.FillResponse> CREATOR; + } + + public static final class FillResponse.Builder { + ctor public FillResponse.Builder(); + method public android.service.autofill.FillResponse.Builder addDataset(android.service.autofill.Dataset); + method public android.service.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...); + method public android.service.autofill.FillResponse build(); + method public android.service.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender); + method public android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle); } public final class SaveCallback { @@ -47168,6 +47189,8 @@ package android.view.autofill { method public void valueChanged(android.view.View); method public void virtualFocusChanged(android.view.View, int, android.graphics.Rect, boolean); method public void virtualValueChanged(android.view.View, int, android.view.autofill.AutoFillValue); + field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE"; + field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT"; } public final class AutoFillType implements android.os.Parcelable { @@ -47195,35 +47218,6 @@ package android.view.autofill { field public static final android.os.Parcelable.Creator<android.view.autofill.AutoFillValue> CREATOR; } - public final class Dataset implements android.os.Parcelable { - method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.view.autofill.Dataset> CREATOR; - } - - public static final class Dataset.Builder { - ctor public Dataset.Builder(java.lang.String, java.lang.CharSequence); - method public android.view.autofill.Dataset build(); - method public android.view.autofill.Dataset.Builder setAuthentication(android.content.IntentSender); - method public android.view.autofill.Dataset.Builder setExtras(android.os.Bundle); - method public android.view.autofill.Dataset.Builder setValue(android.view.autofill.AutoFillId, android.view.autofill.AutoFillValue); - } - - public final class FillResponse implements android.os.Parcelable { - method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.view.autofill.FillResponse> CREATOR; - } - - public static final class FillResponse.Builder { - ctor public FillResponse.Builder(java.lang.String); - method public android.view.autofill.FillResponse.Builder addDataset(android.view.autofill.Dataset); - method public android.view.autofill.FillResponse.Builder addSavableFields(android.view.autofill.AutoFillId...); - method public android.view.autofill.FillResponse build(); - method public android.view.autofill.FillResponse.Builder setAuthentication(android.content.IntentSender); - method public android.view.autofill.FillResponse.Builder setExtras(android.os.Bundle); - } - } package android.view.inputmethod { diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index edf60b4b1926..44494541c1a7 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -16,6 +16,9 @@ package android.app; +import android.view.autofill.AutoFillId; +import android.view.autofill.AutoFillManager; +import android.view.autofill.AutoFillValue; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IVoiceInteractor; import com.android.internal.app.ToolbarActionBar; @@ -113,8 +116,6 @@ import android.view.Window.WindowControllerCallback; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityEvent; -import android.view.autofill.AutoFillManager; -import android.view.autofill.AutoFillSession; import android.widget.AdapterView; import android.widget.Toast; import android.widget.Toolbar; @@ -688,7 +689,8 @@ public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, - Window.OnWindowDismissedCallback, WindowControllerCallback { + Window.OnWindowDismissedCallback, WindowControllerCallback, + AutoFillManager.AutoFillClient { private static final String TAG = "Activity"; private static final boolean DEBUG_LIFECYCLE = false; @@ -726,6 +728,7 @@ public class Activity extends ContextThemeWrapper "android:hasCurrentPermissionsRequest"; private static final String REQUEST_PERMISSIONS_WHO_PREFIX = "@android:requestPermissions:"; + private static final String AUTO_FILL_AUTH_WHO_PREFIX = "@android:autoFillAuth:"; private static final String KEYBOARD_SHORTCUTS_RECEIVER_PKG_NAME = "com.android.systemui"; @@ -841,9 +844,6 @@ public class Activity extends ContextThemeWrapper private boolean mHasCurrentPermissionsRequest; - @GuardedBy("this") - private AutoFillSession mAutoFillSession; - private static native String getDlWarning(); /** Return the intent that started this activity. */ @@ -1695,25 +1695,6 @@ public class Activity extends ContextThemeWrapper } /** - * Lazily attachs the activity to the current {@link AutoFillSession} (if any). - */ - void attachToAutoFillSession() { - synchronized (this) { - if (mAutoFillSession == null) { - final AutoFillManager afm = getSystemService(AutoFillManager.class); - if (afm != null) { - mAutoFillSession = afm.getSession(); - if (mAutoFillSession != null) { - mAutoFillSession.attachActivity(this); - } else { - Log.w(TAG, "attachToAutoFillSession(): not in a session"); - } - } - } - } - } - - /** * Request the Keyboard Shortcuts screen to show up. This will trigger * {@link #onProvideKeyboardShortcuts} to retrieve the shortcuts for the foreground activity. */ @@ -1799,9 +1780,8 @@ public class Activity extends ContextThemeWrapper getApplication().dispatchActivityStopped(this); mTranslucentCallback = null; mCalled = true; - if (mAutoFillSession != null && isFinishing()) { - mAutoFillSession.finishSession(); - mAutoFillSession = null; + if (isFinishing() && AutoFillManager.isClientActive(getActivityToken())) { + getSystemService(AutoFillManager.class).reset(); } } @@ -6021,11 +6001,6 @@ public class Activity extends ContextThemeWrapper getWindow().peekDecorView().getViewRootImpl().dump(prefix, fd, writer, args); } - if (mAutoFillSession!= null) { - writer.print(prefix); writer.print("mAutoFillSession: " ); - writer.println(mAutoFillSession); - } - mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix); } @@ -6748,6 +6723,8 @@ public class Activity extends ContextThemeWrapper mCurrentConfig = config; mWindow.setColorMode(info.colorMode); + + AutoFillManager.addClient(token, this); } /** @hide */ @@ -7038,6 +7015,8 @@ public class Activity extends ContextThemeWrapper return; } } + } else if (who.startsWith(AUTO_FILL_AUTH_WHO_PREFIX)) { + getSystemService(AutoFillManager.class).onAuthenticationResult(data); } else { Fragment frag = mFragments.findFragmentByWho(who); if (frag != null) { @@ -7178,6 +7157,39 @@ public class Activity extends ContextThemeWrapper fragment.onRequestPermissionsResult(requestCode, permissions, grantResults); } + /** @hide */ + @Override + public void autoFill(List<AutoFillId> ids, List<AutoFillValue> values) { + final View root = getWindow().getDecorView(); + final int itemCount = ids.size(); + for (int i = 0; i < itemCount; i++) { + final AutoFillId id = ids.get(i); + final AutoFillValue value = values.get(i); + final int viewId = id.getViewId(); + final View view = root.findViewByAccessibilityIdTraversal(viewId); + if (view == null) { + Log.w(TAG, "autoFill(): no View with id " + viewId); + continue; + } + if (id.isVirtual()) { + view.autoFillVirtual(id.getVirtualChildId(), value); + } else { + view.autoFill(value); + } + } + } + + /** @hide */ + @Override + public void authenticate(IntentSender intent, Intent fillInIntent) { + try { + startIntentSenderForResultInner(intent, AUTO_FILL_AUTH_WHO_PREFIX, + 0, fillInIntent, 0, 0, null); + } catch (IntentSender.SendIntentException e) { + Log.e(TAG, "authenticate() failed for intent:" + intent, e); + } + } + class HostCallbacks extends FragmentHostCallback<Activity> { public HostCallbacks() { super(Activity.this /*activity*/); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 77ed5f47f8a3..50fee835168e 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -89,8 +89,6 @@ import android.provider.Downloads; import android.provider.Settings; import android.security.NetworkSecurityPolicy; import android.security.net.config.NetworkSecurityConfigProvider; -import android.service.autofill.AutoFillService; -import android.service.autofill.IAutoFillAppCallback; import android.util.AndroidRuntimeException; import android.util.ArrayMap; import android.util.DisplayMetrics; @@ -3074,16 +3072,13 @@ public final class ActivityThread { if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL || forAutoFill) { structure = new AssistStructure(r.activity, forAutoFill); Intent activityIntent = r.activity.getIntent(); - boolean attachToSession = 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) boolean notSecure = r.window == null || (r.window.getAttributes().flags & WindowManager.LayoutParams.FLAG_SECURE) == 0; if (activityIntent != null && notSecure) { - if (forAutoFill) { - attachToSession = true; - } else { + if (!forAutoFill) { Intent intent = new Intent(activityIntent); intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)); @@ -3093,16 +3088,10 @@ public final class ActivityThread { } else { if (!forAutoFill) { content.setDefaultIntent(new Intent()); - } else { - // activityIntent is unlikely to be null, but if it is, we should still - // set the auto-fill callback. - attachToSession = notSecure; } } if (!forAutoFill) { r.activity.onProvideAssistContent(content); - } else if (attachToSession) { - r.activity.attachToAutoFillSession(); } } } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 44db326ba33a..f330a4b7cf09 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -114,7 +114,8 @@ import android.os.health.SystemHealthManager; import android.os.storage.StorageManager; import android.print.IPrintManager; import android.print.PrintManager; -import android.service.autofill.IAutoFillManagerService; +import android.view.autofill.AutoFillManager; +import android.view.autofill.IAutoFillManager; import android.service.persistentdata.IPersistentDataBlockService; import android.service.persistentdata.PersistentDataBlockManager; import android.service.vr.IVrManager; @@ -130,7 +131,6 @@ import android.view.WindowManager; import android.view.WindowManagerImpl; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.CaptioningManager; -import android.view.autofill.AutoFillManager; import android.view.inputmethod.InputMethodManager; import android.view.textclassifier.TextClassificationManager; import android.view.textservice.TextServicesManager; @@ -826,8 +826,8 @@ final class SystemServiceRegistry { @Override public AutoFillManager createService(ContextImpl ctx) throws ServiceNotFoundException { IBinder b = ServiceManager.getServiceOrThrow(Context.AUTO_FILL_MANAGER_SERVICE); - IAutoFillManagerService service = IAutoFillManagerService.Stub.asInterface(b); - return new AutoFillManager(ctx, service); + IAutoFillManager service = IAutoFillManager.Stub.asInterface(b); + return new AutoFillManager(ctx.getOuterContext(), service); }}); registerService(Context.VR_SERVICE, VrManager.class, new CachedServiceFetcher<VrManager>() { diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 5f4c36c3fb49..028a7bcf9539 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1806,41 +1806,6 @@ public class Intent implements Parcelable, Cloneable { @SystemApi public static final String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME"; - /** - * Intent extra: An id if an autofill item ({@link - * android.view.autofill.Dataset} or {@link android.view.autofill.FillResponse}). - * <p> - * Type: String - * </p> - */ - public static final String EXTRA_AUTO_FILL_ITEM_ID = "android.intent.extra.AUTO_FILL_ITEM_ID"; - - /** - * Intent extra: The assist structure which captures the filled screen. - * <p> - * Type: {@link android.app.assist.AssistStructure} - * </p> - */ - public static final String EXTRA_AUTO_FILL_ASSIST_STRUCTURE = - "android.intent.extra.AUTO_FILL_ASSIST_STRUCTURE"; - - /** - * Intent extra: The metadata associated with the authenticated entity ({@link - * android.view.autofill.Dataset} or {@link android.view.autofill.FillResponse}). - * <p> - * Type: {@link android.os.Bundle} - * </p> - */ - public static final String EXTRA_AUTO_FILL_EXTRAS = "android.intent.extra.AUTO_FILL_EXTRAS"; - - /** - * Intent extra: A callback to report an authentication result. - * <p> - * Type: {@link android.view.autofill.FillResponse} - * </p> - */ - public static final String EXTRA_AUTO_FILL_CALLBACK = "android.intent.extra.AUTO_FILL_CALLBACK"; - // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard intent broadcast actions (see action variable). diff --git a/core/java/android/service/autofill/AutoFillService.java b/core/java/android/service/autofill/AutoFillService.java index 6da6a398203b..4099f59ec9ef 100644 --- a/core/java/android/service/autofill/AutoFillService.java +++ b/core/java/android/service/autofill/AutoFillService.java @@ -30,7 +30,6 @@ import android.os.IBinder; import android.os.ICancellationSignal; import android.os.Looper; import android.util.Log; -import android.view.autofill.FillResponse; import com.android.internal.os.SomeArgs; @@ -70,7 +69,7 @@ public abstract class AutoFillService extends Service { // Internal extras /** @hide */ public static final String EXTRA_ACTIVITY_TOKEN = - "android.service.autofill.EXTRA_ACTIVITY_TOKEN"; + "android.service.autofill.extra.ACTIVITY_TOKEN"; // Handler messages. private static final int MSG_CONNECT = 1; @@ -180,15 +179,16 @@ public abstract class AutoFillService extends Service { * service. * * <p>Service must call one of the {@link FillCallback} methods (like - * {@link FillCallback#onSuccess(FillResponse)} or {@link FillCallback#onFailure(CharSequence)}) + * {@link FillCallback#onSuccess(FillResponse)} + * or {@link FillCallback#onFailure(CharSequence)}) * to notify the result of the request. * * @param structure {@link Activity}'s view structure. * @param data bundle containing data passed by the service on previous calls to fill. * This bundle allows your service to keep state between fill and save requests * as well as when filling different sections of the UI as the system will try to - * aggressively unbind from the service to conserve resources. See {@link FillResponse} - * Javadoc for examples of multiple-sections requests. + * aggressively unbind from the service to conserve resources. See {@link + * FillResponse} Javadoc for examples of multiple-sections requests. * @param cancellationSignal signal for observing cancellation requests. The system will use * this to notify you that the fill result is no longer needed and you should stop * handling this fill request in order to save resources. @@ -208,8 +208,8 @@ public abstract class AutoFillService extends Service { * @param data bundle containing data passed by the service on previous calls to fill. * This bundle allows your service to keep state between fill and save requests * as well as when filling different sections of the UI as the system will try to - * aggressively unbind from the service to conserve resources. See {@link FillResponse} - * Javadoc for examples of multiple-sections requests. + * aggressively unbind from the service to conserve resources. See {@link + * FillResponse} Javadoc for examples of multiple-sections requests. * @param callback object used to notify the result of the request. */ public abstract void onSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle data, diff --git a/core/java/android/view/autofill/Dataset.aidl b/core/java/android/service/autofill/Dataset.aidl index 2a8e67caace1..2342c5f5a45a 100644 --- a/core/java/android/view/autofill/Dataset.aidl +++ b/core/java/android/service/autofill/Dataset.aidl @@ -14,6 +14,6 @@ * limitations under the License. */ -package android.view.autofill; +package android.service.autofill; -parcelable Dataset;
\ No newline at end of file +parcelable Dataset; diff --git a/core/java/android/view/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java index 2708358c3143..bd38c7f1fdd9 100644 --- a/core/java/android/view/autofill/Dataset.java +++ b/core/java/android/service/autofill/Dataset.java @@ -14,59 +14,48 @@ * limitations under the License. */ -package android.view.autofill; - -import static android.view.autofill.Helper.DEBUG; +package android.service.autofill; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.Activity; -import android.app.assist.AssistStructure.ViewNode; import android.content.IntentSender; -import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; - +import android.view.autofill.AutoFillId; +import android.view.autofill.AutoFillValue; import com.android.internal.util.Preconditions; import java.util.ArrayList; /** - * A set of data that can be used to auto-fill an {@link Activity}. + * A set of data that can be used to auto-fill an {@link android.app.Activity}. * * <p>It contains: * * <ol> * <li>A name used to identify the dataset in the UI. * <li>A list of id/value pairs for the fields that can be auto-filled. - * <li>An optional {@link Bundle} with extras (used only by the service creating it). + * <li>A list of savable ids in addition to the ones with a provided value. * </ol> * - * @see FillResponse for examples. + * @see android.service.autofill.FillResponse for examples. */ public final class Dataset implements Parcelable { - private final String mId; + private static final boolean DEBUG = false; + private final CharSequence mName; private final ArrayList<AutoFillId> mFieldIds; private final ArrayList<AutoFillValue> mFieldValues; - private final Bundle mExtras; private final IntentSender mAuthentication; private Dataset(Builder builder) { - mId = builder.mId; mName = builder.mName; mFieldIds = builder.mFieldIds; mFieldValues = builder.mFieldValues; - mExtras = builder.mExtras; mAuthentication = builder.mAuthentication; } /** @hide */ - public @NonNull String getId() { - return mId; - } - - /** @hide */ public @NonNull CharSequence getName() { return mName; } @@ -82,11 +71,6 @@ public final class Dataset implements Parcelable { } /** @hide */ - public @Nullable Bundle getExtras() { - return mExtras; - } - - /** @hide */ public @Nullable IntentSender getAuthentication() { return mAuthentication; } @@ -100,71 +84,32 @@ public final class Dataset implements Parcelable { public String toString() { if (!DEBUG) return super.toString(); - final StringBuilder builder = new StringBuilder("Dataset [id=").append(mId) - .append(", name=").append(mName) + final StringBuilder builder = new StringBuilder("Dataset [name=").append(mName) .append(", fieldIds=").append(mFieldIds) .append(", fieldValues=").append(mFieldValues) - .append(", hasAuthentication=").append(mAuthentication != null) - .append(", hasExtras=").append(mExtras != null); + .append(", hasAuthentication=").append(mAuthentication != null); return builder.append(']').toString(); } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final Dataset other = (Dataset) obj; - if (mId == null) { - if (other.mId != null) { - return false; - } - } else if (!mId.equals(other.mId)) { - return false; - } - return true; - } - - @Override - public int hashCode() { - return mId != null ? mId.hashCode() : 0; - } - /** * A builder for {@link Dataset} objects. You must to provide at least * one value for a field or set an authentication intent. */ public static final class Builder { - private String mId; private CharSequence mName; private ArrayList<AutoFillId> mFieldIds; private ArrayList<AutoFillValue> mFieldValues; - private Bundle mExtras; private IntentSender mAuthentication; private boolean mDestroyed; - /** @hide */ - // TODO(b/33197203): Remove once GCore migrates - public Builder(@NonNull CharSequence name) { - this(String.valueOf(System.currentTimeMillis()), name); - } - /** * Creates a new builder. * - * @param id A required id to identify this dataset for future interactions related to it. * @param name Name used to identify the dataset in the UI. Typically it's the same value as * the first field in the dataset (like username or email address) or a user-provided name * (like "My Work Address"). */ - public Builder(@NonNull String id, @NonNull CharSequence name) { - mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty or null"); + public Builder(@NonNull CharSequence name) { mName = Preconditions.checkStringNotEmpty(name, "name cannot be empty or null"); } @@ -172,50 +117,36 @@ public final class Dataset implements Parcelable { * Requires a dataset authentication before auto-filling the activity with this dataset. * * <p>This method is called when you need to provide an authentication - * UI for the dataset. For example, when a dataset contains credit card information + * UI for the data set. For example, when a data set contains credit card information * (such as number, expiration date, and verification code), you can display UI * asking for the verification code to before filing in the data). Even if the - * dataset is completely populated the system will launch the specified authentication - * intent and will need your approval to fill it in. Since the dataset is "locked" - * until the user authenticates it, typically this dataset name is masked - * (for example, "VISA....1234"). Typically you would want to store the dataset - * labels non-encypted and the actual sensitive data encrypted and not in memory. + * data set is completely populated the system will launch the specified authentication + * intent and will need your approval to fill it in. Since the data set is "locked" + * until the user authenticates it, typically this data set name is masked + * (for example, "VISA....1234"). Typically you would want to store the data set + * labels non-encrypted and the actual sensitive data encrypted and not in memory. * This allows showing the labels in the UI while involving the user if one of * the items with these labels is chosen. Note that if you use sensitive data as - * a label, for example an email address, then it should also be encrypted. - *</p> + * a label, for example an email address, then it should also be encrypted.</p> * - * <p>When a user selects this dataset, the system triggers the provided intent - * whose extras will have the {@link android.content.Intent#EXTRA_AUTO_FILL_ITEM_ID id} - * of the {@link android.view.autofill.Dataset dataset} to authenticate, the {@link - * android.content.Intent#EXTRA_AUTO_FILL_EXTRAS extras} associated with this - * dataset, and a {@link android.content.Intent#EXTRA_AUTO_FILL_CALLBACK callback} - * to dispatch the authentication result.</p> + * <p>When a user triggers auto-fill, the system launches the provided intent + * whose extras will have the {@link + * android.view.autofill.AutoFillManager#EXTRA_ASSIST_STRUCTURE screen content}. Once + * you complete your authentication flow you should set the activity result to {@link + * android.app.Activity#RESULT_OK} and provide the fully populated {@link Dataset + * dataset} by setting it to the {@link + * android.view.autofill.AutoFillManager#EXTRA_AUTHENTICATION_RESULT} extra. For example, + * if you provided an credit card information without the CVV for the data set in the + * {@link FillResponse response} then the returned data set should contain the + * CVV entry.</p> * - * <p>Once you complete your authentication flow you should use the provided callback - * to notify for a failure or a success. In case of a success you need to provide - * only the fully populated dataset that is being authenticated. For example, if you - * provided a {@link FillResponse} with two {@link Dataset}s and marked that - * only the first dataset needs an authentication then in the provided response - * you need to provide only the fully populated dataset being authenticated instead - * of both of them. - * </p> - * - * <p>The indent sender mechanism allows you to have your authentication UI - * implemented as an activity or a service or a receiver. However, the recommended - * way is to do this is with an activity which the system will start in the - * filled activity's task meaning it will properly work with back, recent apps, and - * free-form multi-window, while avoiding the need for the "draw on top of other" - * apps special permission. You can still theme your authentication activity's - * UI to look like a dialog if desired.</p> - * - * <p></><strong>Note:</strong> Do not make the provided intent sender + * <p></><strong>Note:</strong> Do not make the provided pending intent * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the * platform needs to fill in the authentication arguments.</p> * - * @param authentication Intent to trigger your authentication flow. + * @param authentication Intent to an activity with your authentication flow. * - * @see android.app.PendingIntent#getIntentSender() + * @see android.app.PendingIntent */ public @NonNull Builder setAuthentication(@Nullable IntentSender authentication) { throwIfDestroyed(); @@ -226,7 +157,8 @@ public final class Dataset implements Parcelable { /** * Sets the value of a field. * - * @param id id returned by {@link ViewNode#getAutoFillId()}. + * @param id id returned by {@link + * android.app.assist.AssistStructure.ViewNode#getAutoFillId()}. * @param value value to be auto filled. */ public @NonNull Builder setValue(@NonNull AutoFillId id, @NonNull AutoFillValue value) { @@ -249,18 +181,6 @@ public final class Dataset implements Parcelable { } /** - * Sets a {@link Bundle} that will be passed to subsequent APIs that - * manipulate this dataset. For example, they are passed in as {@link - * android.content.Intent#EXTRA_AUTO_FILL_EXTRAS extras} to your - * authentication flow. - */ - public @NonNull Builder setExtras(@Nullable Bundle extras) { - throwIfDestroyed(); - mExtras = extras; - return this; - } - - /** * Creates a new {@link Dataset} instance. You should not interact * with this builder once this method is called. */ @@ -292,21 +212,19 @@ public final class Dataset implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { - parcel.writeString(mId); parcel.writeCharSequence(mName); parcel.writeTypedArrayList(mFieldIds, 0); parcel.writeTypedArrayList(mFieldValues, 0); - parcel.writeBundle(mExtras); parcel.writeParcelable(mAuthentication, flags); } - public static final Parcelable.Creator<Dataset> CREATOR = new Parcelable.Creator<Dataset>() { + public static final Creator<Dataset> CREATOR = new Creator<Dataset>() { @Override public Dataset createFromParcel(Parcel parcel) { // Always go through the builder to ensure the data ingested by // the system obeys the contract of the builder to avoid attacks // using specially crafted parcels. - final Builder builder = new Builder(parcel.readString(), parcel.readCharSequence()); + final Builder builder = new Builder(parcel.readCharSequence()); final ArrayList<AutoFillId> ids = parcel.readTypedArrayList(null); final ArrayList<AutoFillValue> values = parcel.readTypedArrayList(null); final int idCount = (ids != null) ? ids.size() : 0; @@ -316,7 +234,6 @@ public final class Dataset implements Parcelable { AutoFillValue value = (valueCount > i) ? values.get(i) : null; builder.setValue(id, value); } - builder.setExtras(parcel.readBundle()); builder.setAuthentication(parcel.readParcelable(null)); return builder.build(); } diff --git a/core/java/android/service/autofill/FillCallback.java b/core/java/android/service/autofill/FillCallback.java index a306809886a7..69c990463ab7 100644 --- a/core/java/android/service/autofill/FillCallback.java +++ b/core/java/android/service/autofill/FillCallback.java @@ -19,16 +19,13 @@ package android.service.autofill; import android.annotation.Nullable; import android.app.Activity; import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; import android.os.RemoteException; -import android.view.autofill.FillResponse; /** * Handles auto-fill requests from the {@link AutoFillService} into the {@link Activity} being * auto-filled. */ -public final class FillCallback implements Parcelable { +public final class FillCallback { private final IFillCallback mCallback; private boolean mCalled; @@ -37,11 +34,6 @@ public final class FillCallback implements Parcelable { mCallback = callback; } - /** @hide */ - private FillCallback(Parcel parcel) { - mCallback = IFillCallback.Stub.asInterface(parcel.readStrongBinder()); - } - /** * Notifies the Android System that an * {@link AutoFillService#onFillRequest(android.app.assist.AssistStructure, Bundle, @@ -79,33 +71,9 @@ public final class FillCallback implements Parcelable { } } - /** @hide */ - @Override - public int describeContents() { - return 0; - } - - /** @hide */ - @Override - public void writeToParcel(Parcel parcel, int flags) { - parcel.writeStrongBinder(mCallback.asBinder()); - } - private void assertNotCalled() { if (mCalled) { throw new IllegalStateException("Already called"); } } - - public static final Creator<FillCallback> CREATOR = new Creator<FillCallback>() { - @Override - public FillCallback createFromParcel(Parcel parcel) { - return new FillCallback(parcel); - } - - @Override - public FillCallback[] newArray(int size) { - return new FillCallback[size]; - } - }; } diff --git a/core/java/android/view/autofill/FillResponse.aidl b/core/java/android/service/autofill/FillResponse.aidl index b018f15923ec..e9c2d21dda65 100644 --- a/core/java/android/view/autofill/FillResponse.aidl +++ b/core/java/android/service/autofill/FillResponse.aidl @@ -14,6 +14,6 @@ * limitations under the License. */ -package android.view.autofill; +package android.service.autofill; -parcelable FillResponse;
\ No newline at end of file +parcelable FillResponse; diff --git a/core/java/android/view/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java index 596a06c28d57..ea36e64c076b 100644 --- a/core/java/android/view/autofill/FillResponse.java +++ b/core/java/android/service/autofill/FillResponse.java @@ -13,30 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.view.autofill; - -import static android.view.autofill.Helper.DEBUG; +package android.service.autofill; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.Activity; import android.content.IntentSender; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.util.ArraySet; - -import com.android.internal.util.Preconditions; +import android.view.autofill.AutoFillId; +import android.view.autofill.AutoFillManager; /** * Response for a {@link - * android.service.autofill.AutoFillService#onFillRequest(android.app.assist.AssistStructure, - * Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback)} and + * AutoFillService#onFillRequest(android.app.assist.AssistStructure, + * Bundle, android.os.CancellationSignal, FillCallback)} and * authentication requests. * * <p>The response typically contains one or more {@link Dataset}s, each representing a set of * fields that can be auto-filled together, and the Android system displays a dataset picker UI - * affordance that the user must use before the {@link Activity} is filled with the dataset. + * affordance that the user must use before the {@link android.app.Activity} is filled with + * the dataset. * * <p>For example, for a login page with username/password where the user only has one account in * the response could be: @@ -65,9 +63,9 @@ import com.android.internal.util.Preconditions; * .build(); * </pre> * - * <p>If the user does not have any data associated with this {@link Activity} but the service wants - * to offer the user the option to save the data that was entered, then the service could populate - * the response with {@code savableIds} instead of {@link Dataset}s: + * <p>If the user does not have any data associated with this {@link android.app.Activity} but + * the service wants to offer the user the option to save the data that was entered, then the + * service could populate the response with {@code savableIds} instead of {@link Dataset}s: * * <pre class="prettyprint"> * new FillResponse.Builder() @@ -142,7 +140,7 @@ import com.android.internal.util.Preconditions; * #setAuthentication(IntentSender)} and {@link Dataset.Builder#setAuthentication(IntentSender)}. * It is recommended that you encrypt only the sensitive data but leave the labels unencrypted * which would allow you to provide the dataset names to the user and if they choose one - * them challenge the user to authenticate. For example, if the user has a home and a work + * them challenge the user to onAuthenticate. For example, if the user has a home and a work * address the Home and Work labels could be stored unencrypted as they don't have any sensitive * data while the address data is in an encrypted storage. If the user chooses Home, then the * platform will start your authentication flow. If you encrypt all data and require auth @@ -152,20 +150,16 @@ import com.android.internal.util.Preconditions; * Work options where they can pick one. Hence, you have flexibility how to implement your * auth while storing labels non-encrypted and data encrypted provides a better user * experience.</p> - * - * <p>Finally, the service can use {@link Dataset.Builder#setExtras(Bundle)} methods - * to pass {@link Bundle extras} provided to all future calls related to a dataset, - * for example during authentication and saving.</p> */ public final class FillResponse implements Parcelable { - private final String mId; + private static final boolean DEBUG = false; + private final ArraySet<Dataset> mDatasets; private final ArraySet<AutoFillId> mSavableIds; private final Bundle mExtras; private final IntentSender mAuthentication; private FillResponse(@NonNull Builder builder) { - mId = builder.mId; mDatasets = builder.mDatasets; mSavableIds = builder.mSavableIds; mExtras = builder.mExtras; @@ -173,11 +167,6 @@ public final class FillResponse implements Parcelable { } /** @hide */ - public @NonNull String getId() { - return mId; - } - - /** @hide */ public @Nullable Bundle getExtras() { return mExtras; } @@ -202,71 +191,49 @@ public final class FillResponse implements Parcelable { * one dataset or set an authentication intent. */ public static final class Builder { - private final String mId; private ArraySet<Dataset> mDatasets; private ArraySet<AutoFillId> mSavableIds; private Bundle mExtras; private IntentSender mAuthentication; private boolean mDestroyed; - /** @hide */ - // TODO(b/33197203): Remove once GCore migrates - public Builder() { - this(String.valueOf(System.currentTimeMillis())); - } - /** * Creates a new {@link FillResponse} builder. - * - * @param id A required id to identify this dataset for future interactions related to it. */ - public Builder(@NonNull String id) { - mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty or null"); + public Builder() { + } /** * Requires a fill response authentication before auto-filling the activity with - * any dataset in this response. This is typically useful when a user interaction - * is required to unlock their data vault if you encrypt the dataset labels and - * dataset data. It is recommended to encrypt only the sensitive data and not the - * dataset labels which would allow auth on the dataset level leading to a better - * user experience. Note that if you use sensitive data as a label, for example an - * email address, then it should also be encrypted. - * - * <p>This method is called when you need to provide an authentication - * UI for the fill response. For example, when the user's data is stored - * encrypted and needs a user interaction to decrypt before offering fill - * suggestions.</p> - * - * <p>When a user initiates an auto fill, the system triggers the provided - * intent whose extras will have the {@link android.content.Intent - * #EXTRA_AUTO_FILL_ITEM_ID id} of the {@link android.view.autofill.FillResponse}) - * to authenticate, the {@link android.content.Intent#EXTRA_AUTO_FILL_EXTRAS extras} - * associated with this response, and a {@link android.content.Intent - * #EXTRA_AUTO_FILL_CALLBACK callback} to dispatch the authentication result.</p> + * any data set in this response. * - * <p>Once you complete your authentication flow you should use the provided callback - * to notify for a failure or a success. In case of a success you need to provide - * the fully populated response that is being authenticated. For example, if you - * provided an empty {@link FillResponse} because the user's data was locked and - * marked that the response needs an authentication then in the response returned - * if authentication succeeds you need to provide all available datasets some of - * which may need to be further authenticated, for example a credit card whose - * CVV needs to be entered.</p> + * <p>This is typically useful when a user interaction is required to unlock their + * data vault if you encrypt the data set labels and data set data. It is recommended + * to encrypt only the sensitive data and not the data set labels which would allow + * auth on the data set level leading to a better user experience. Note that if you + * use sensitive data as a label, for example an email address, then it should also + * be encrypted. The provided {@link android.app.PendingIntent intent} must be an + * activity which implements your authentication flow.</p> * - * <p>The indent sender mechanism allows you to have your authentication UI - * implemented as an activity or a service or a receiver. However, the recommended - * way is to do this is with an activity which the system will start in the - * filled activity's task meaning it will properly work with back, recent apps, and - * free-form multi-window, while avoiding the need for the "draw on top of other" - * apps special permission. You can still theme your authentication activity's - * UI to look like a dialog if desired.</p> + * <p>When a user triggers auto-fill, the system launches the provided intent + * whose extras will have the {@link + * AutoFillManager#EXTRA_ASSIST_STRUCTURE screen + * content}. Once you complete your authentication flow you should set the activity + * result to {@link android.app.Activity#RESULT_OK} and provide the fully populated {@link + * FillResponse response} by setting it to the {@link + * AutoFillManager#EXTRA_AUTHENTICATION_RESULT} extra. + * For example, if you provided an empty {@link FillResponse resppnse} because the + * user's data was locked and marked that the response needs an authentication then + * in the response returned if authentication succeeds you need to provide all + * available data sets some of which may need to be further authenticated, for + * example a credit card whose CVV needs to be entered.</p> * - * <p></><strong>Note:</strong> Do not make the provided intent sender + * <p></><strong>Note:</strong> Do not make the provided pending intent * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the * platform needs to fill in the authentication arguments.</p> * - * @param authentication Intent to trigger your authentication flow. + * @param authentication Intent to an activity with your authentication flow. * * @see android.app.PendingIntent#getIntentSender() */ @@ -313,8 +280,8 @@ public final class FillResponse implements Parcelable { /** * Adds ids of additional fields that the service would be interested to save (through - * {@link android.service.autofill.AutoFillService#onSaveRequest( - * android.app.assist.AssistStructure, Bundle, android.service.autofill.SaveCallback)}) + * {@link AutoFillService#onSaveRequest( + * android.app.assist.AssistStructure, Bundle, SaveCallback)}) * but were not indirectly set through {@link #addDataset(Dataset)}. * * <p>See {@link FillResponse} for examples. @@ -335,15 +302,13 @@ public final class FillResponse implements Parcelable { /** * Sets a {@link Bundle} that will be passed to subsequent APIs that - * manipulate this response. For example, they are passed in as {@link - * android.content.Intent#EXTRA_AUTO_FILL_EXTRAS extras} to your - * authentication flow and to subsequent calls to {@link - * android.service.autofill.AutoFillService#onFillRequest( + * manipulate this response. For example, they are passed to subsequent + * calls to {@link AutoFillService#onFillRequest( * android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal, - * android.service.autofill.FillCallback)} and {@link - * android.service.autofill.AutoFillService#onSaveRequest( + * FillCallback)} and {@link + * AutoFillService#onSaveRequest( * android.app.assist.AssistStructure, Bundle, - * android.service.autofill.SaveCallback)}. + * SaveCallback)}. */ public Builder setExtras(Bundle extras) { throwIfDestroyed(); @@ -374,8 +339,7 @@ public final class FillResponse implements Parcelable { public String toString() { if (!DEBUG) return super.toString(); final StringBuilder builder = new StringBuilder( - "FillResponse: [id=").append(mId) - .append(", datasets=").append(mDatasets) + "FillResponse: [datasets=").append(mDatasets) .append(", savableIds=").append(mSavableIds) .append(", hasExtras=").append(mExtras != null) .append(", hasAuthentication=").append(mAuthentication != null); @@ -393,7 +357,6 @@ public final class FillResponse implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { - parcel.writeString(mId); parcel.writeTypedArraySet(mDatasets, 0); parcel.writeTypedArraySet(mSavableIds, 0); parcel.writeParcelable(mExtras, 0); @@ -407,7 +370,7 @@ public final class FillResponse implements Parcelable { // Always go through the builder to ensure the data ingested by // the system obeys the contract of the builder to avoid attacks // using specially crafted parcels. - final Builder builder = new Builder(parcel.readString()); + final Builder builder = new Builder(); final ArraySet<Dataset> datasets = parcel.readTypedArraySet(null); final int datasetCount = (datasets != null) ? datasets.size() : 0; for (int i = 0; i < datasetCount; i++) { diff --git a/core/java/android/view/autofill/FieldId.aidl b/core/java/android/service/autofill/IAuthenticationCallback.aidl index 35af6453dbd1..36b989dec886 100644 --- a/core/java/android/view/autofill/FieldId.aidl +++ b/core/java/android/service/autofill/IAuthenticationCallback.aidl @@ -1,11 +1,11 @@ -/** - * Copyright (c) 2016, The Android Open Source Project +/* + * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -16,4 +16,16 @@ package android.view.autofill; -parcelable FieldId;
\ No newline at end of file +import android.view.autofill.Dataset; +import android.service.autofill.FillResponse; + +/** + * Callback for delivering authentication result. + * + * {@hide} + */ +interface IAutoFillAuthCallback { + void onSuccessForDataset(in Dataset dataset); + void onSuccessForFillResponse(in FillResponse response); + void onFailure(CharSequence message); +} diff --git a/core/java/android/service/autofill/IFillCallback.aidl b/core/java/android/service/autofill/IFillCallback.aidl index 537403e72fc5..2bb3e9acd3e1 100644 --- a/core/java/android/service/autofill/IFillCallback.aidl +++ b/core/java/android/service/autofill/IFillCallback.aidl @@ -18,7 +18,7 @@ package android.service.autofill; import android.os.ICancellationSignal; -import android.view.autofill.FillResponse; +import android.service.autofill.FillResponse; /** * Interface to receive the result of a save request. diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java index 46b307233e88..c6dd1dfa40a8 100644 --- a/core/java/android/service/autofill/SaveCallback.java +++ b/core/java/android/service/autofill/SaveCallback.java @@ -23,8 +23,6 @@ import android.os.RemoteException; /** * Handles save requests from the {@link AutoFillService} into the {@link Activity} being * auto-filled. - * - * <p>This class is thread safe. */ public final class SaveCallback { private final ISaveCallback mCallback; diff --git a/core/java/android/view/autofill/AutoFillManager.java b/core/java/android/view/autofill/AutoFillManager.java index 58607ba51579..f7a1b61446c3 100644 --- a/core/java/android/view/autofill/AutoFillManager.java +++ b/core/java/android/view/autofill/AutoFillManager.java @@ -16,40 +16,101 @@ package android.view.autofill; -import static android.view.autofill.Helper.VERBOSE; - -import android.annotation.Nullable; import android.content.Context; +import android.content.Intent; +import android.content.IntentSender; import android.graphics.Rect; +import android.os.Bundle; import android.os.IBinder; +import android.os.Parcelable; import android.os.RemoteException; -import android.service.autofill.IAutoFillManagerService; +import android.util.ArrayMap; import android.util.Log; import android.view.View; +import java.lang.ref.WeakReference; +import java.util.List; + /** * App entry point to the AutoFill Framework. */ // TODO(b/33197203): improve this javadoc //TODO(b/33197203): restrict manager calls to activity public final class AutoFillManager { + private static final boolean DEBUG = false; private static final String TAG = "AutoFillManager"; + /** + * Intent extra: The assist structure which captures the filled screen. + * <p> + * Type: {@link android.app.assist.AssistStructure} + * </p> + */ + public static final String EXTRA_ASSIST_STRUCTURE = + "android.view.autofill.extra.ASSIST_STRUCTURE"; + + /** + * Intent extra: The result of an authentication operation. It is + * either a fully populated {@link android.service.autofill.FillResponse} + * or a fully populated {@link android.service.autofill.Dataset} if + * a response or a dataset is being authenticated respectively. + * + * <p> + * Type: {@link android.service.autofill.FillResponse} or a + * {@link android.service.autofill.Dataset} + * </p> + */ + public static final String EXTRA_AUTHENTICATION_RESULT = + "android.view.autofill.extra.AUTHENTICATION_RESULT"; + /** @hide */ public static final int FLAG_START_SESSION = 0x1; /** @hide */ public static final int FLAG_FOCUS_GAINED = 0x2; /** @hide */ public static final int FLAG_FOCUS_LOST = 0x4; /** @hide */ public static final int FLAG_VALUE_CHANGED = 0x8; - private final IAutoFillManagerService mService; - private final Context mContext; + // These are activities that may have auto-fill UI which are keyed off their tokens. + // This is done instead of the activity setting the client in the auto-fill manager + // to avoid unnecessary instantiation of the manager and do this only if there is an + // auto-fillable focused. This has only the cost of loading the class vs creating an + // auto-fill manager for every activity even one that cannot be filled. + private static final ArrayMap<IBinder, AutoFillClient> sPendingClients = new ArrayMap<>(); + + private final Rect mTempRect = new Rect(); - private AutoFillSession mSession; + private final IAutoFillManager mService; + private IAutoFillManagerClient mServiceClient; + + private Context mContext; + + private AutoFillClient mClient; + + private boolean mHasSession; + private boolean mEnabled; + + /** @hide */ + public interface AutoFillClient { + /** + * Asks the client to perform an auto-fill. + * + * @param ids The values to auto-fill + * @param values The values to auto-fill + */ + void autoFill(List<AutoFillId> ids, List<AutoFillValue> values); + + /** + * Asks the client to start an authentication flow. + * + * @param intent The authentication intent. + * @param fillInIntent The authentication fill-in intent. + */ + void authenticate(IntentSender intent, Intent fillInIntent); + } /** * @hide */ - public AutoFillManager(Context context, IAutoFillManagerService service) { + public AutoFillManager(Context context, IAutoFillManager service) { mContext = context; mService = service; } @@ -61,27 +122,26 @@ public final class AutoFillManager { * @param gainFocus whether focus was gained or lost. */ public void focusChanged(View view, boolean gainFocus) { - if (mSession == null) { - // Starts new session. - final Rect bounds = new Rect(); - view.getBoundsOnScreen(bounds); - final AutoFillId id = getAutoFillId(view); - final AutoFillValue value = view.getAutoFillValue(); - startSession(id, bounds, value); - return; - } + ensureServiceClientAddedIfNeeded(); - if (!mSession.isEnabled()) { - // Auto-fill is disabled for this session. + if (!mEnabled) { return; } - // Update focus on existing session. - final Rect bounds = new Rect(); + final Rect bounds = mTempRect; view.getBoundsOnScreen(bounds); final AutoFillId id = getAutoFillId(view); final AutoFillValue value = view.getAutoFillValue(); - updateSession(id, bounds, value, gainFocus ? FLAG_FOCUS_GAINED : FLAG_FOCUS_LOST); + + if (!mHasSession) { + if (gainFocus) { + // Starts new session. + startSession(id, bounds, value); + } + } else { + // Update focus on existing session. + updateSession(id, bounds, value, gainFocus ? FLAG_FOCUS_GAINED : FLAG_FOCUS_LOST); + } } /** @@ -93,21 +153,23 @@ public final class AutoFillManager { * @param gainFocus whether focus was gained or lost. */ public void virtualFocusChanged(View parent, int childId, Rect bounds, boolean gainFocus) { - if (mSession == null) { - // Starts new session. - final AutoFillId id = getAutoFillId(parent, childId); - startSession(id, bounds, null); - return; - } + ensureServiceClientAddedIfNeeded(); - if (!mSession.isEnabled()) { - // Auto-fill is disabled for this session. + if (!mEnabled) { return; } - // Update focus on existing session. final AutoFillId id = getAutoFillId(parent, childId); - updateSession(id, bounds, null, gainFocus ? FLAG_FOCUS_GAINED : FLAG_FOCUS_LOST); + + if (!mHasSession) { + if (gainFocus) { + // Starts new session. + startSession(id, bounds, null); + } + } else { + // Update focus on existing session. + updateSession(id, bounds, null, gainFocus ? FLAG_FOCUS_GAINED : FLAG_FOCUS_LOST); + } } /** @@ -116,7 +178,11 @@ public final class AutoFillManager { * @param view view whose focus changed. */ public void valueChanged(View view) { - if (mSession == null) return; + ensureServiceClientAddedIfNeeded(); + + if (!mEnabled || !mHasSession) { + return; + } final AutoFillId id = getAutoFillId(view); final AutoFillValue value = view.getAutoFillValue(); @@ -132,7 +198,11 @@ public final class AutoFillManager { * @param value new value of the child. */ public void virtualValueChanged(View parent, int childId, AutoFillValue value) { - if (mSession == null) return; + ensureServiceClientAddedIfNeeded(); + + if (!mEnabled || !mHasSession) { + return; + } final AutoFillId id = getAutoFillId(parent, childId); updateSession(id, null, value, FLAG_VALUE_CHANGED); @@ -145,30 +215,51 @@ public final class AutoFillManager { * call this method after the form is submitted and another page is rendered. */ public void reset() { - if (mSession == null) return; + ensureServiceClientAddedIfNeeded(); - final IBinder activityToken = mSession.mToken.get(); - if (activityToken == null) { - Log.wtf(TAG, "finishSession(): token already GC'ed"); + if (!mEnabled && !mHasSession) { return; } - try { - mService.finishSession(activityToken); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } finally { - mSession = null; + + finishSession(); + } + + /** @hide */ + public static void addClient(IBinder token, AutoFillClient client) { + sPendingClients.put(token, client); + } + + /** @hide */ + public static boolean isClientActive(IBinder token) { + return !sPendingClients.containsKey(token); + } + + private void activateClient() { + mClient = sPendingClients.remove(mContext.getActivityToken()); + } + + private AutoFillClient getClient() { + if (mClient == null) { + return sPendingClients.get(mContext.getActivityToken()); } + return mClient; } - /** - * Gets the current session, if any. - * - * @hide - */ - @Nullable - public AutoFillSession getSession() { - return mSession; + /** @hide */ + public void onAuthenticationResult(Intent data) { + if (data == null) { + return; + } + Parcelable result = data.getParcelableExtra( + EXTRA_AUTHENTICATION_RESULT); + Bundle responseData = new Bundle(); + responseData.putParcelable(EXTRA_AUTHENTICATION_RESULT, result); + try { + mService.setAuthenticationResult(responseData, + mContext.getActivityToken(), mContext.getUserId()); + } catch (RemoteException e) { + Log.e(TAG, "Error delivering authentication result", e); + } } private AutoFillId getAutoFillId(View view) { @@ -180,34 +271,98 @@ public final class AutoFillManager { } private void startSession(AutoFillId id, Rect bounds, AutoFillValue value) { - if (VERBOSE) { + if (DEBUG) { Log.v(TAG, "startSession(): id=" + id + ", bounds=" + bounds + ", value=" + value); } + try { + mService.startSession(mContext.getActivityToken(), mServiceClient.asBinder(), + id, bounds, value, mContext.getUserId()); + mHasSession = true; + activateClient(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } - final IBinder activityToken = mContext.getActivityToken(); - mSession = new AutoFillSession(this, activityToken); - final IBinder appCallback = mSession.getCallback().asBinder(); + private void finishSession() { + if (DEBUG) { + Log.v(TAG, "finishSession()"); + } + mHasSession = false; try { - mService.startSession(activityToken, appCallback, id, bounds, value); + mService.finishSession(mContext.getActivityToken(), mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } private void updateSession(AutoFillId id, Rect bounds, AutoFillValue value, int flags) { - if (VERBOSE) { + if (DEBUG) { Log.v(TAG, "updateSession(): id=" + id + ", bounds=" + bounds + ", value=" + value + ", flags=" + flags); } - - final IBinder activityToken = mSession.mToken.get(); - if (activityToken == null) { - return; - } try { - mService.updateSession(activityToken, id, bounds, value, flags); + mService.updateSession(mContext.getActivityToken(), id, bounds, value, flags, + mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } + + private void ensureServiceClientAddedIfNeeded() { + if (getClient() == null) { + return; + } + if (mServiceClient == null) { + mServiceClient = new AutoFillManagerClient(this); + try { + mEnabled = mService.addClient(mServiceClient, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + private static final class AutoFillManagerClient extends IAutoFillManagerClient.Stub { + private final WeakReference<AutoFillManager> mAutoFillManager; + + AutoFillManagerClient(AutoFillManager autoFillManager) { + mAutoFillManager = new WeakReference<>(autoFillManager); + } + + @Override + public void setState(boolean enabled) { + final AutoFillManager autoFillManager = mAutoFillManager.get(); + if (autoFillManager != null) { + autoFillManager.mContext.getMainThreadHandler().post(() -> + autoFillManager.mEnabled = enabled); + } + } + + @Override + public void autoFill(List<AutoFillId> ids, List<AutoFillValue> values) { + // TODO(b/33197203): must keep the dataset so subsequent calls pass the same + // dataset.extras to service + final AutoFillManager autoFillManager = mAutoFillManager.get(); + if (autoFillManager != null) { + autoFillManager.mContext.getMainThreadHandler().post(() -> { + if (autoFillManager.getClient() != null) { + autoFillManager.getClient().autoFill(ids, values); + } + }); + } + } + + @Override + public void authenticate(IntentSender intent, Intent fillInIntent) { + final AutoFillManager autoFillManager = mAutoFillManager.get(); + if (autoFillManager != null) { + autoFillManager.mContext.getMainThreadHandler().post(() -> { + if (autoFillManager.getClient() != null) { + autoFillManager.getClient().authenticate(intent, fillInIntent); + } + }); + } + } + } } diff --git a/core/java/android/view/autofill/AutoFillSession.java b/core/java/android/view/autofill/AutoFillSession.java deleted file mode 100644 index 64df62f84000..000000000000 --- a/core/java/android/view/autofill/AutoFillSession.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.view.autofill; - -import static android.view.autofill.Helper.DEBUG; - -import android.app.Activity; -import android.content.Intent; -import android.content.IntentSender; -import android.os.IBinder; -import android.service.autofill.IAutoFillAppCallback; -import android.util.Log; -import android.view.View; - -import java.lang.ref.WeakReference; - -/** - * An auto-fill session associated with an activity. - * - * @hide - */ -public final class AutoFillSession { - - private static final String TAG = "AutoFillSession"; - - private final IAutoFillAppCallback mCallback = new IAutoFillAppCallback.Stub() { - - @Override - public void enableSession() { - if (DEBUG) Log.d(TAG, "enableSession()"); - - mEnabled = true; - } - - @Override - public void autoFill(Dataset dataset) { - final Activity activity = mActivity.get(); - if (activity == null) { - if (DEBUG) Log.d(TAG, "autoFill(): activity already GCed"); - return; - } - // TODO(b/33197203): must keep the dataset so subsequent calls pass the same - // dataset.extras to service - activity.runOnUiThread(() -> { - final View root = activity.getWindow().getDecorView().getRootView(); - final int itemCount = dataset.getFieldIds().size(); - for (int i = 0; i < itemCount; i++) { - final AutoFillId id = dataset.getFieldIds().get(i); - final AutoFillValue value = dataset.getFieldValues().get(i); - final int viewId = id.getViewId(); - final View view = root.findViewByAccessibilityIdTraversal(viewId); - if (view == null) { - Log.w(TAG, "autoFill(): no View with id " + viewId); - continue; - } - - if (id.isVirtual()) { - view.autoFillVirtual(id.getVirtualChildId(), value); - } else { - view.autoFill(value); - } - } - }); - } - - @Override - public void startIntentSender(IntentSender intent, Intent fillInIntent) { - final Activity activity = mActivity.get(); - if (activity != null) { - activity.runOnUiThread(() -> { - try { - activity.startIntentSender(intent, fillInIntent, 0, 0, 0); - } catch (IntentSender.SendIntentException e) { - Log.e(TAG, "startIntentSender() failed for intent:" + intent, e); - } - }); - } - } - }; - - private final AutoFillManager mAfm; - private WeakReference<Activity> mActivity; - - // Reference to the token, which is used by the server. - final WeakReference<IBinder> mToken; - - private boolean mEnabled; - - public AutoFillSession(AutoFillManager afm, IBinder token) { - mToken = new WeakReference<>(token); - mAfm = afm; - } - - /** - * Called by the {@link Activity} when it was asked to provider auto-fill data. - */ - public void attachActivity(Activity activity) { - if (mActivity != null) { - Log.w(TAG, "attachActivity(): already attached"); - return; - } - mActivity = new WeakReference<>(activity); - } - - /** - * Checks whether auto-fill is enabled for this session, as decided by the - * {@code AutoFillManagerService}. - */ - public boolean isEnabled() { - return mEnabled; - } - - /** - * Notifies the manager that a session finished. - */ - // TODO(b/33197203): hook it to other lifecycle events like fragments transition - public void finishSession() { - if (mAfm != null) { - try { - mAfm.reset(); - } catch (RuntimeException e) { - Log.w(TAG, "Failed to finish session for " + mToken.get() + ": " + e); - } - } - } - - public IAutoFillAppCallback getCallback() { - return mCallback; - } - - @Override - public String toString() { - if (!DEBUG) return super.toString(); - - return "AutoFillSession[activityoken=" + mToken.get() + "]"; - } -} diff --git a/core/java/android/service/autofill/IAutoFillManagerService.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl index 6cdb516860ef..0433a8fd36b7 100644 --- a/core/java/android/service/autofill/IAutoFillManagerService.aidl +++ b/core/java/android/view/autofill/IAutoFillManager.aidl @@ -14,24 +14,27 @@ * limitations under the License. */ -package android.service.autofill; +package android.view.autofill; import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; import android.view.autofill.AutoFillId; import android.view.autofill.AutoFillValue; +import android.view.autofill.IAutoFillManagerClient; /** * Mediator between apps being auto-filled and auto-fill service implementations. * * {@hide} */ -oneway interface IAutoFillManagerService { - // Methods called by AutoFillManager - void startSession(in IBinder activityToken, in IBinder appCallback, in AutoFillId autoFillId, - in Rect bounds, in AutoFillValue value); - void updateSession(in IBinder activityToken, in AutoFillId id, in Rect bounds, - in AutoFillValue value, int flags); - void finishSession(in IBinder activityToken); +interface IAutoFillManager { + boolean addClient(in IAutoFillManagerClient client, int userId); + oneway void startSession(in IBinder activityToken, in IBinder appCallback, + in AutoFillId autoFillId, in Rect bounds, in AutoFillValue value, int userId); + oneway void updateSession(in IBinder activityToken, in AutoFillId id, in Rect bounds, + in AutoFillValue value, int flags, int userId); + oneway void finishSession(in IBinder activityToken, int userId); + oneway void setAuthenticationResult(in Bundle data, + in IBinder activityToken, int userId); } diff --git a/core/java/android/service/autofill/IAutoFillAppCallback.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl index c2e72e8235a4..45f363d32301 100644 --- a/core/java/android/service/autofill/IAutoFillAppCallback.aidl +++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl @@ -14,34 +14,33 @@ * limitations under the License. */ -package android.service.autofill; +package android.view.autofill; import java.util.List; import android.content.Intent; import android.content.IntentSender; -import android.view.autofill.Dataset; +import android.view.autofill.AutoFillId; +import android.view.autofill.AutoFillValue; /** * Object running in the application process and responsible for auto-filling it. * * @hide */ -// TODO(b/33197203): rename IAutoFillAppSession -oneway interface IAutoFillAppCallback { +oneway interface IAutoFillManagerClient { /** - * Auto-fills the activity with the contents of a dataset. - */ - void autoFill(in Dataset dataset); + * Notifies the client when the auto-fill enabled state changed. + */ + void setState(boolean enabled); /** - * Start an intent sender from the context of the filled app + * Auto-fills the activity with the contents of a dataset. */ - void startIntentSender(in IntentSender intent, in Intent fillInIntent); + void autoFill(in List<AutoFillId> ids, in List<AutoFillValue> values); /** - * Called by system_service to enable auto-fill in a session, after it was asynchronously - * started by the manager. + * Authenticates a fill response or a data set. */ - void enableSession(); + void authenticate(in IntentSender intent, in Intent fillInIntent); } diff --git a/services/autofill/java/com/android/server/autofill/AnchoredWindow.java b/services/autofill/java/com/android/server/autofill/AnchoredWindow.java index ed0d23435984..64c6abd2f2bc 100644 --- a/services/autofill/java/com/android/server/autofill/AnchoredWindow.java +++ b/services/autofill/java/com/android/server/autofill/AnchoredWindow.java @@ -126,13 +126,13 @@ final class AnchoredWindow implements View.OnLayoutChangeListener, View.OnTouchL int oldLeft, int oldTop, int oldRight, int oldBottom) { if (view == mWindowSizeListenerView) { if (DEBUG) Slog.d(TAG, "onLayoutChange() for mWindowSizeListenerView"); - // mWindowSizeListenerView layout changed, get the size of the display bounds and update + // mWindowSizeListenerView layout changed, get the size of the display bounds and updateLocked // the window. final Rect displayBounds = new Rect(); view.getBoundsOnScreen(displayBounds); updateDisplayBounds(displayBounds); } else if (view == mContentView) { - // mContentView layout changed, update the window in case its height changed. + // mContentView layout changed, updateLocked the window in case its height changed. if (DEBUG) Slog.d(TAG, "onLayoutChange() for mContentView"); updateHeight(); } @@ -159,7 +159,7 @@ final class AnchoredWindow implements View.OnLayoutChangeListener, View.OnTouchL MeasureSpec.makeMeasureSpec(displayBounds.height(), MeasureSpec.AT_MOST)); int height = mContentView.getMeasuredHeight(); if (height != mLastHeight) { - if (DEBUG) Slog.d(TAG, "update height=" + height); + if (DEBUG) Slog.d(TAG, "updateLocked height=" + height); mLastHeight = height; update(height, mLastBounds, displayBounds); return true; @@ -170,7 +170,7 @@ final class AnchoredWindow implements View.OnLayoutChangeListener, View.OnTouchL private void updateBounds(Rect bounds) { if (!bounds.equals(mLastBounds)) { - if (DEBUG) Slog.d(TAG, "update bounds=" + bounds); + if (DEBUG) Slog.d(TAG, "updateLocked bounds=" + bounds); mLastBounds = bounds; update(mLastHeight, bounds, mLastDisplayBounds); @@ -179,7 +179,7 @@ final class AnchoredWindow implements View.OnLayoutChangeListener, View.OnTouchL private void updateDisplayBounds(Rect displayBounds) { if (!displayBounds.equals(mLastDisplayBounds)) { - if (DEBUG) Slog.d(TAG, "update displayBounds=" + displayBounds); + if (DEBUG) Slog.d(TAG, "updateLocked displayBounds=" + displayBounds); mLastDisplayBounds = displayBounds; if (!updateHeight()) { @@ -195,7 +195,7 @@ final class AnchoredWindow implements View.OnLayoutChangeListener, View.OnTouchL return; } - if (DEBUG) Slog.d(TAG, "update height=" + height + ", bounds=" + bounds + if (DEBUG) Slog.d(TAG, "updateLocked height=" + height + ", bounds=" + bounds + ", displayBounds=" + displayBounds); final LayoutParams params = createWindowLayoutParams(mAppToken, @@ -220,7 +220,7 @@ final class AnchoredWindow implements View.OnLayoutChangeListener, View.OnTouchL * the bounds is preferred, if it fits. Otherwise, anchor the window on the side with more * space. * - * @param params the params to update + * @param params the params to updateLocked * @param height the requested height of the window * @param minMargin the minimum margin between the window and the display bounds * @param bounds the region the window should be anchored to diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java index c16a51c8dfbd..3257812d6054 100644 --- a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java @@ -22,14 +22,14 @@ import static com.android.server.autofill.Helper.DEBUG; import static com.android.server.autofill.Helper.VERBOSE; import android.Manifest; -import android.annotation.Nullable; +import android.annotation.NonNull; import android.app.ActivityManagerInternal; -import android.app.AppGlobals; -import android.content.ComponentName; +import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.PackageManager; -import android.content.pm.ServiceInfo; import android.database.ContentObserver; import android.graphics.Rect; import android.net.Uri; @@ -37,14 +37,11 @@ import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.UserHandle; import android.provider.Settings; -import android.service.autofill.IAutoFillManagerService; -import android.text.TextUtils; import android.util.LocalLog; import android.util.Log; import android.util.Slog; @@ -52,11 +49,12 @@ import android.util.SparseArray; import android.view.autofill.AutoFillId; import android.view.autofill.AutoFillValue; +import android.view.autofill.IAutoFillManager; +import android.view.autofill.IAutoFillManagerClient; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BackgroundThread; -import com.android.internal.os.HandlerCaller; import com.android.internal.os.IResultReceiver; -import com.android.internal.os.SomeArgs; +import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -68,21 +66,15 @@ import java.util.List; /** * Entry point service for auto-fill management. * - * <p>This service provides the {@link IAutoFillManagerService} implementation and keeps a list of + * <p>This service provides the {@link IAutoFillManager} implementation and keeps a list of * {@link AutoFillManagerServiceImpl} per user; the real work is done by * {@link AutoFillManagerServiceImpl} itself. */ +// TODO(b/33197203): Handle removing of packages public final class AutoFillManagerService extends SystemService { private static final String TAG = "AutoFillManagerService"; - private static final int MSG_START_SESSION = 1; - private static final int MSG_UPDATE_SESSION = 2; - private static final int MSG_FINISH_SESSION = 3; - private static final int MSG_REQUEST_SAVE_FOR_USER = 4; - private static final int MSG_LIST_SESSIONS = 5; - private static final int MSG_RESET = 6; - static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions"; private final Context mContext; @@ -90,48 +82,6 @@ public final class AutoFillManagerService extends SystemService { private final Object mLock = new Object(); - private final HandlerCaller.Callback mHandlerCallback = (msg) -> { - switch (msg.what) { - case MSG_START_SESSION: { - final SomeArgs args = (SomeArgs) msg.obj; - final int userId = msg.arg1; - final IBinder activityToken = (IBinder) args.arg1; - final IBinder appCallback = (IBinder) args.arg2; - final AutoFillId autoFillId = (AutoFillId) args.arg3; - final Rect bounds = (Rect) args.arg4; - final AutoFillValue value = (AutoFillValue) args.arg5; - handleStartSession(userId, activityToken, appCallback, autoFillId, bounds, value); - return; - } case MSG_FINISH_SESSION: { - handleFinishSession(msg.arg1, (IBinder) msg.obj); - return; - } case MSG_REQUEST_SAVE_FOR_USER: { - handleSaveForUser(msg.arg1); - return; - } case MSG_UPDATE_SESSION: { - final SomeArgs args = (SomeArgs) msg.obj; - final IBinder activityToken = (IBinder) args.arg1; - final AutoFillId autoFillId = (AutoFillId) args.arg2; - final Rect bounds = (Rect) args.arg3; - final AutoFillValue value = (AutoFillValue) args.arg4; - final int userId = args.argi5; - final int flags = args.argi6; - handleUpdateSession(userId, activityToken, autoFillId, bounds, value, flags); - return; - } case MSG_LIST_SESSIONS: { - handleListForUser(msg.arg1, (IResultReceiver) msg.obj); - return; - } case MSG_RESET: { - handleReset(); - return; - } default: { - Slog.w(TAG, "Invalid message: " + msg); - } - } - }; - - private HandlerCaller mHandlerCaller; - /** * Cache of {@link AutoFillManagerServiceImpl} per user id. * <p> @@ -152,11 +102,26 @@ public final class AutoFillManagerService extends SystemService { // TODO(b/33197203): set a different max (or disable it) on low-memory devices. private final LocalLog mRequestsHistory = new LocalLog(100); + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) { + final String reason = intent.getStringExtra("reason"); + if (DEBUG) Slog.d(TAG, "close system dialogs: " + reason); + mUi.hideAll(); + } + } + }; + public AutoFillManagerService(Context context) { super(context); - mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(), mHandlerCallback, true); mContext = context; mUi = new AutoFillUI(mContext); + + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + mContext.registerReceiver(mBroadcastReceiver, filter, null, + FgThread.getHandler()); } @Override @@ -171,46 +136,30 @@ public final class AutoFillManagerService extends SystemService { } } - private AutoFillManagerServiceImpl newServiceForUser(int userId) { - ComponentName serviceComponent = null; - ServiceInfo serviceInfo = null; - final String componentName = Settings.Secure.getStringForUser( - mContext.getContentResolver(), Settings.Secure.AUTO_FILL_SERVICE, userId); - if (!TextUtils.isEmpty(componentName)) { - try { - serviceComponent = ComponentName.unflattenFromString(componentName); - serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, 0, - userId); - } catch (RuntimeException | RemoteException e) { - Slog.e(TAG, "Bad auto-fill service name " + componentName, e); - return null; - } - } - - if (serviceInfo == null) { - return null; + @Override + public void onUnlockUser(int userId) { + synchronized (mLock) { + updateCachedServiceLocked(userId); } + } - try { - return new AutoFillManagerServiceImpl(mContext, mLock, mRequestsHistory, - userId, serviceComponent, mUi); - } catch (PackageManager.NameNotFoundException e) { - Slog.w(TAG, "Auto-fill service not found: " + serviceComponent, e); + @Override + public void onStopUser(int userId) { + synchronized (mLock) { + removeCachedServiceLocked(userId); } - - return null; } /** * Gets the service instance for an user. * - * @return service instance or {@code null} if user does not have a service set. + * @return service instance. */ - @Nullable - AutoFillManagerServiceImpl getServiceForUserLocked(int userId) { + @NonNull AutoFillManagerServiceImpl getServiceForUserLocked(int userId) { AutoFillManagerServiceImpl service = mServicesCache.get(userId); if (service == null) { - service = newServiceForUser(userId); + service = new AutoFillManagerServiceImpl(mContext, mLock, + mRequestsHistory, userId, mUi); mServicesCache.put(userId, service); } return service; @@ -220,81 +169,6 @@ public final class AutoFillManagerService extends SystemService { void requestSaveForUser(int userId) { Slog.i(TAG, "requestSaveForUser(): " + userId); mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG); - mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageI( - MSG_REQUEST_SAVE_FOR_USER, userId)); - } - - // Called by Shell command. - void listSessions(int userId, IResultReceiver receiver) { - Slog.i(TAG, "listSessions() for userId " + userId); - mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG); - mHandlerCaller.sendMessage( - mHandlerCaller.obtainMessageIO(MSG_LIST_SESSIONS, userId, receiver)); - } - - // Called by Shell command. - void reset() { - Slog.i(TAG, "reset()"); - mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG); - mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_RESET)); - } - - /** - * Removes a cached service for a given user. - */ - void removeCachedServiceLocked(int userId) { - final AutoFillManagerServiceImpl service = mServicesCache.get(userId); - if (service != null) { - mServicesCache.delete(userId); - service.destroyLocked(); - } - } - - private void handleStartSession(int userId, IBinder activityToken, IBinder appCallback, - AutoFillId autoFillId, Rect bounds, AutoFillValue value) { - synchronized (mLock) { - final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId); - if (service == null) { - return; - } - service.startSessionLocked(activityToken, appCallback, autoFillId, bounds, value); - } - } - - private void handleFinishSession(int userId, IBinder activityToken) { - synchronized (mLock) { - final AutoFillManagerServiceImpl service = mServicesCache.get(userId); - if (service == null) { - return; - } - service.finishSessionLocked(activityToken); - } - } - - private void handleUpdateSession(int userId, IBinder activityToken, AutoFillId autoFillId, - Rect bounds, AutoFillValue value, int flags) { - synchronized (mLock) { - final AutoFillManagerServiceImpl service = mServicesCache.get(userId); - if (service == null) { - return; - } - - service.updateSessionLocked(activityToken, autoFillId, bounds, value, flags); - } - } - - private IBinder getTopActivityForUser() { - final List<IBinder> topActivities = LocalServices - .getService(ActivityManagerInternal.class).getTopVisibleActivities(); - if (DEBUG) Slog.d(TAG, "Top activities (" + topActivities.size() + "): " + topActivities); - if (topActivities.isEmpty()) { - Slog.w(TAG, "Could not get top activity"); - return null; - } - return topActivities.get(0); - } - - private void handleSaveForUser(int userId) { final IBinder activityToken = getTopActivityForUser(); if (activityToken != null) { synchronized (mLock) { @@ -309,7 +183,10 @@ public final class AutoFillManagerService extends SystemService { } } - private void handleListForUser(int userId, IResultReceiver receiver) { + // Called by Shell command. + void listSessions(int userId, IResultReceiver receiver) { + Slog.i(TAG, "listSessions() for userId " + userId); + mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG); final Bundle resultData = new Bundle(); final ArrayList<String> sessions = new ArrayList<>(); @@ -332,7 +209,10 @@ public final class AutoFillManagerService extends SystemService { } } - private void handleReset() { + // Called by Shell command. + void reset() { + Slog.i(TAG, "reset()"); + mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG); synchronized (mLock) { final int size = mServicesCache.size(); for (int i = 0; i < size; i++) { @@ -342,48 +222,98 @@ public final class AutoFillManagerService extends SystemService { } } - final class AutoFillManagerServiceStub extends IAutoFillManagerService.Stub { + /** + * Removes a cached service for a given user. + */ + private void removeCachedServiceLocked(int userId) { + final AutoFillManagerServiceImpl service = mServicesCache.get(userId); + if (service != null) { + mServicesCache.delete(userId); + service.destroyLocked(); + } + } + + /** + * Updates a cached service for a given user. + */ + private void updateCachedServiceLocked(int userId) { + AutoFillManagerServiceImpl service = mServicesCache.get(userId); + if (service != null) { + service.updateLocked(); + } + } + + private IBinder getTopActivityForUser() { + final List<IBinder> topActivities = LocalServices + .getService(ActivityManagerInternal.class).getTopVisibleActivities(); + if (DEBUG) Slog.d(TAG, "Top activities (" + topActivities.size() + "): " + topActivities); + if (topActivities.isEmpty()) { + Slog.w(TAG, "Could not get top activity"); + return null; + } + return topActivities.get(0); + } + + final class AutoFillManagerServiceStub extends IAutoFillManager.Stub { + @Override + public boolean addClient(IAutoFillManagerClient client, int userId) { + synchronized (mLock) { + return getServiceForUserLocked(userId).addClientLocked(client); + } + } + + @Override + public void setAuthenticationResult(Bundle data, IBinder activityToken, int userId) { + synchronized (mLock) { + final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId); + service.setAuthenticationResultLocked(data, activityToken); + } + } @Override public void startSession(IBinder activityToken, IBinder appCallback, AutoFillId autoFillId, - Rect bounds, AutoFillValue value) throws RemoteException { + Rect bounds, AutoFillValue value, int userId) { // TODO(b/33197203): make sure it's called by resumed / focused activity - final int userId = UserHandle.getCallingUserId(); if (VERBOSE) { Slog.v(TAG, "startSession: autoFillId=" + autoFillId + ", bounds=" + bounds + ", value=" + value); } - final SomeArgs args = SomeArgs.obtain(); - args.arg1 = activityToken; - args.arg2 = appCallback; - args.arg3 = autoFillId; - args.arg4 = bounds; - args.arg5 = value; - - mHandlerCaller.sendMessage(mHandlerCaller.getHandler().obtainMessage(MSG_START_SESSION, - userId, 0, args)); + synchronized (mLock) { + final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId); + service.startSessionLocked(activityToken, appCallback, autoFillId, bounds, value); + } } @Override public void updateSession(IBinder activityToken, AutoFillId id, Rect bounds, - AutoFillValue value, int flags) throws RemoteException { + AutoFillValue value, int flags, int userId) { if (DEBUG) { Slog.d(TAG, "updateSession: flags=" + flags + ", autoFillId=" + id + ", bounds=" + bounds + ", value=" + value); } - mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOOII(MSG_UPDATE_SESSION, - activityToken, id, bounds, value, UserHandle.getCallingUserId(), flags)); + synchronized (mLock) { + final AutoFillManagerServiceImpl service = mServicesCache.get( + UserHandle.getCallingUserId()); + if (service != null) { + service.updateSessionLocked(activityToken, id, bounds, value, flags); + } + } } @Override - public void finishSession(IBinder activityToken) throws RemoteException { + public void finishSession(IBinder activityToken, int userId) { if (VERBOSE) Slog.v(TAG, "finishSession(): " + activityToken); - mHandlerCaller.sendMessage(mHandlerCaller.getHandler().obtainMessage(MSG_FINISH_SESSION, - UserHandle.getCallingUserId(), 0, activityToken)); + synchronized (mLock) { + final AutoFillManagerServiceImpl service = mServicesCache.get( + UserHandle.getCallingUserId()); + if (service != null) { + service.finishSessionLocked(activityToken); + } + } } @Override @@ -433,7 +363,7 @@ public final class AutoFillManagerService extends SystemService { @Override public void onChange(boolean selfChange, Uri uri, int userId) { synchronized (mLock) { - removeCachedServiceLocked(userId); + updateCachedServiceLocked(userId); } } } diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java index 2891518c3eac..8d43dfbd180d 100644 --- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java @@ -31,43 +31,44 @@ import static com.android.server.autofill.Helper.findValue; import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityManager; -import android.app.IActivityManager; +import android.app.AppGlobals; import android.app.assist.AssistStructure; import android.app.assist.AssistStructure.ViewNode; import android.app.assist.AssistStructure.WindowNode; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; import android.graphics.Rect; +import android.os.Binder; import android.os.Bundle; import android.os.IBinder; -import android.os.ICancellationSignal; import android.os.Looper; +import android.os.Parcelable; +import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.provider.Settings; import android.service.autofill.AutoFillService; import android.service.autofill.AutoFillServiceInfo; -import android.service.autofill.FillCallback; -import android.service.autofill.IAutoFillAppCallback; +import android.service.autofill.Dataset; +import android.service.autofill.FillResponse; import android.service.autofill.IAutoFillService; -import android.service.autofill.IFillCallback; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.LocalLog; import android.util.PrintWriterPrinter; import android.util.Slog; import android.view.autofill.AutoFillId; +import android.view.autofill.AutoFillManager; import android.view.autofill.AutoFillValue; -import android.view.autofill.Dataset; -import android.view.autofill.FillResponse; +import android.view.autofill.IAutoFillManagerClient; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.HandlerCaller; import com.android.internal.os.IResultReceiver; -import com.android.server.FgThread; import java.io.PrintWriter; import java.util.ArrayList; @@ -86,26 +87,14 @@ final class AutoFillManagerServiceImpl { private static final int MSG_SERVICE_SAVE = 1; private final int mUserId; - private final ComponentName mComponent; - private final String mComponentName; private final Context mContext; - private final IActivityManager mAm; private final Object mLock; - private final AutoFillServiceInfo mInfo; private final AutoFillUI mUi; - private final LocalLog mRequestsHistory; + private RemoteCallbackList<IAutoFillManagerClient> mClients; + private AutoFillServiceInfo mInfo; - private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) { - final String reason = intent.getStringExtra("reason"); - if (DEBUG) Slog.d(TAG, "close system dialogs: " + reason); - mUi.hideAll(); - } - } - }; + private final LocalLog mRequestsHistory; private final HandlerCaller.Callback mHandlerCallback = (msg) -> { switch (msg.what) { @@ -119,6 +108,7 @@ final class AutoFillManagerServiceImpl { private final HandlerCaller mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(), mHandlerCallback, true); + /** * Cache of pending {@link Session}s, keyed by {@code activityToken}. * @@ -139,7 +129,6 @@ final class AutoFillManagerServiceImpl { if (DEBUG) Slog.d(TAG, "resultCode on mAssistReceiver: " + resultCode); final AssistStructure structure = resultData.getParcelable(KEY_STRUCTURE); - if (structure == null) { Slog.w(TAG, "no assist structure for id " + resultCode); return; @@ -183,28 +172,61 @@ final class AutoFillManagerServiceImpl { }; AutoFillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory, - int userId, ComponentName component, AutoFillUI ui) - throws PackageManager.NameNotFoundException { + int userId, AutoFillUI ui) { mContext = context; mLock = lock; mRequestsHistory = requestsHistory; mUserId = userId; - mComponent = component; - mComponentName = mComponent.flattenToShortString(); - mAm = ActivityManager.getService(); mUi = ui; - mInfo = new AutoFillServiceInfo(context.getPackageManager(), component, mUserId); - - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); - mContext.registerReceiver(mBroadcastReceiver, filter, null, FgThread.getHandler()); + updateLocked(); } + void updateLocked() { + ComponentName serviceComponent = null; + ServiceInfo serviceInfo = null; + final String componentName = Settings.Secure.getStringForUser( + mContext.getContentResolver(), Settings.Secure.AUTO_FILL_SERVICE, mUserId); + if (!TextUtils.isEmpty(componentName)) { + try { + serviceComponent = ComponentName.unflattenFromString(componentName); + serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, + 0, mUserId); + } catch (RuntimeException | RemoteException e) { + Slog.e(TAG, "Bad auto-fill service name " + componentName, e); + return; + } + } + try { + final boolean hadService = hasService(); + if (serviceInfo != null) { + mInfo = new AutoFillServiceInfo(mContext.getPackageManager(), + serviceComponent, mUserId); + } else { + mInfo = null; + } + if (hadService != hasService()) { + if (!hasService()) { + final int sessionCount = mSessions.size(); + for (int i = sessionCount - 1; i >= 0; i--) { + Session session = mSessions.valueAt(i); + session.destroyLocked(); + mSessions.removeAt(i); + } + } + sendStateToClients(); + } + } catch (PackageManager.NameNotFoundException e) { + Slog.e(TAG, "Bad auto-fill service name " + componentName, e); + } + } /** * Used by {@link AutoFillManagerServiceShellCommand} to request save for the current top app. */ void requestSaveForUserLocked(IBinder activityToken) { + if (!hasService()) { + return; + } final Session session = mSessions.get(activityToken); if (session == null) { Slog.w(TAG, "requestSaveForUserLocked(): no session for " + activityToken); @@ -214,9 +236,32 @@ final class AutoFillManagerServiceImpl { session.callSaveLocked(); } + boolean addClientLocked(IAutoFillManagerClient client) { + if (mClients == null) { + mClients = new RemoteCallbackList<>(); + } + mClients.register(client); + return hasService(); + } + + void setAuthenticationResultLocked(Bundle data, IBinder activityToken) { + if (!hasService()) { + return; + } + final Session session = mSessions.get(activityToken); + if (session != null) { + session.setAuthenticationResultLocked(data); + } + } + void startSessionLocked(IBinder activityToken, IBinder appCallbackToken, AutoFillId autoFillId, Rect bounds, AutoFillValue value) { - final String historyItem = "s=" + mComponentName + " u=" + mUserId + " a=" + activityToken + if (!hasService()) { + return; + } + + final String historyItem = "s=" + new ComponentName(mInfo.getServiceInfo().packageName, + mInfo.getServiceInfo().name) + " u=" + mUserId + " a=" + activityToken + " i=" + autoFillId + " b=" + bounds + " v=" + value; mRequestsHistory.log(historyItem); @@ -229,24 +274,23 @@ final class AutoFillManagerServiceImpl { final Session newSession = createSessionByTokenLocked(activityToken, appCallbackToken); newSession.updateLocked(autoFillId, bounds, value, FLAG_START_SESSION); - newSession.enableSessionLocked(); } void finishSessionLocked(IBinder activityToken) { - if (DEBUG) Slog.d(TAG, "finishSessionLocked(): " + activityToken); - final Session session = mSessions.get(activityToken); + if (!hasService()) { + return; + } + final Session session = mSessions.get(activityToken); if (session == null) { Slog.w(TAG, "finishSessionLocked(): no session for " + activityToken); return; } - mUi.hideFillUi(); session.showSaveLocked(); } private Session createSessionByTokenLocked(IBinder activityToken, IBinder appCallbackToken) { - final Session newSession = new Session(mContext, activityToken, appCallbackToken); mSessions.put(activityToken, newSession); @@ -261,10 +305,16 @@ final class AutoFillManagerServiceImpl { // TODO(b/33197203): add MetricsLogger call final Bundle receiverExtras = new Bundle(); receiverExtras.putBinder(EXTRA_ACTIVITY_TOKEN, activityToken); - if (!mAm.requestAutoFillData(mAssistReceiver, receiverExtras, activityToken)) { - // TODO(b/33197203): might need a way to warn user (perhaps a new method on - // AutoFillService). - Slog.w(TAG, "failed to request auto-fill data for " + activityToken); + final long identity = Binder.clearCallingIdentity(); + try { + if (!ActivityManager.getService().requestAutoFillData(mAssistReceiver, + receiverExtras, activityToken)) { + // TODO(b/33197203): might need a way to warn user (perhaps a new method on + // AutoFillService). + Slog.w(TAG, "failed to request auto-fill data for " + activityToken); + } + } finally { + Binder.restoreCallingIdentity(identity); } } catch (RemoteException e) { // Should not happen, it's a local call. @@ -274,7 +324,6 @@ final class AutoFillManagerServiceImpl { void updateSessionLocked(IBinder activityToken, AutoFillId autoFillId, Rect bounds, AutoFillValue value, int flags) { - // TODO(b/33197203): add MetricsLogger call final Session session = mSessions.get(activityToken); if (session == null) { @@ -286,7 +335,6 @@ final class AutoFillManagerServiceImpl { } private void handleSessionSave(IBinder activityToken) { - synchronized (mLock) { final Session session = mSessions.get(activityToken); if (session == null) { @@ -301,7 +349,6 @@ final class AutoFillManagerServiceImpl { void destroyLocked() { if (VERBOSE) Slog.v(TAG, "destroyLocked()"); - mContext.unregisterReceiver(mBroadcastReceiver); for (Session session : mSessions.values()) { session.destroyLocked(); } @@ -311,7 +358,8 @@ final class AutoFillManagerServiceImpl { void dumpLocked(String prefix, PrintWriter pw) { final String prefix2 = prefix + " "; - pw.print(prefix); pw.println("Component:"); pw.println(mComponentName); + pw.print(prefix); pw.println("Component:"); pw.println(mInfo != null + ? mInfo.getServiceInfo().getComponentName() : null); if (VERBOSE) { // ServiceInfo dump is too noisy and redundant (it can be obtained through other dumps) @@ -333,14 +381,44 @@ final class AutoFillManagerServiceImpl { void listSessionsLocked(ArrayList<String> output) { for (IBinder activityToken : mSessions.keySet()) { - output.add(mComponentName + ":" + activityToken); + output.add((mInfo != null ? mInfo.getServiceInfo().getComponentName() + : null) + ":" + activityToken); + } + } + + private void sendStateToClients() { + final RemoteCallbackList<IAutoFillManagerClient> clients; + final int userClientCount; + synchronized (mLock) { + if (mClients == null) { + return; + } + clients = mClients; + userClientCount = clients.beginBroadcast(); + } + try { + for (int i = 0; i < userClientCount; i++) { + IAutoFillManagerClient client = clients.getBroadcastItem(i); + try { + client.setState(hasService()); + } catch (RemoteException re) { + /* ignore */ + } + } + } finally { + clients.finishBroadcast(); } } + private boolean hasService() { + return mInfo != null; + } + @Override public String toString() { return "AutoFillManagerServiceImpl: [userId=" + mUserId - + ", component=" + mComponentName + "]"; + + ", component=" + (mInfo != null + ? mInfo.getServiceInfo().getComponentName() : null) + "]"; } /** @@ -418,7 +496,6 @@ final class AutoFillManagerServiceImpl { pw.print(prefix); pw.print("updated:" ); pw.println(mValueUpdated); pw.print(prefix); pw.print("bounds:" ); pw.println(mBounds); } - } /** @@ -448,7 +525,7 @@ final class AutoFillManagerServiceImpl { @Nullable private ViewState mCurrentViewState; - private final IAutoFillAppCallback mAppCallback; + private final IAutoFillManagerClient mClient; @GuardedBy("mLock") RemoteFillService mRemoteFillService; @@ -471,19 +548,20 @@ final class AutoFillManagerServiceImpl { @GuardedBy("mLock") private AssistStructure mStructure; - private Session(Context context, IBinder activityToken, IBinder appCallback) { - mRemoteFillService = new RemoteFillService(context, mComponent, mUserId, this); + private Session(Context context, IBinder activityToken, IBinder client) { + mRemoteFillService = new RemoteFillService(context, + mInfo.getServiceInfo().getComponentName(), mUserId, this); mActivityToken = activityToken; - mAppCallback = IAutoFillAppCallback.Stub.asInterface(appCallback); + mClient = IAutoFillManagerClient.Stub.asInterface(client); try { - appCallback.linkToDeath(() -> { + client.linkToDeath(() -> { if (DEBUG) Slog.d(TAG, "app binder died"); removeSelf(); }, 0); } catch (RemoteException e) { - Slog.w(TAG, "linkToDeath() on mAppCallback failed: " + e); + Slog.w(TAG, "linkToDeath() on mClient failed: " + e); } } @@ -528,7 +606,7 @@ final class AutoFillManagerServiceImpl { // FillServiceCallbacks @Override public void authenticate(IntentSender intent, Intent fillInIntent) { - startAuthIntent(intent, fillInIntent); + startAuthentication(intent, fillInIntent); } // FillServiceCallbacks @@ -550,6 +628,30 @@ final class AutoFillManagerServiceImpl { .sendToTarget(); } + public void setAuthenticationResultLocked(Bundle data) { + if (mCurrentResponse == null || data == null) { + removeSelf(); + } else { + Parcelable result = data.getParcelable( + AutoFillManager.EXTRA_AUTHENTICATION_RESULT); + if (result instanceof FillResponse) { + mCurrentResponse = (FillResponse) result; + processResponseLocked(mCurrentResponse); + } else if (result instanceof Dataset) { + Dataset dataset = (Dataset) result; + final int datasetIndex = Helper.indexOfDataset( + dataset.getName(), mCurrentResponse); + if (datasetIndex <= 0) { + Slog.e(TAG, "Response for a dataset auth has" + + " an invalid dataset result: " + dataset.getName()); + } + mCurrentResponse.getDatasets().removeAt(datasetIndex); + mCurrentResponse.getDatasets().add(dataset); + autoFill(dataset); + } + } + } + /** * Show the save UI, when session can be saved. */ @@ -665,7 +767,7 @@ final class AutoFillManagerServiceImpl { mViewStates.put(id, viewState); } - if ((flags & FLAG_START_SESSION) != 0 ) { + if ((flags & FLAG_START_SESSION) != 0) { // View is triggering auto-fill. mCurrentViewState = viewState; viewState.update(value, bounds); @@ -738,7 +840,7 @@ final class AutoFillManagerServiceImpl { private void processResponseLocked(FillResponse response) { if (DEBUG) Slog.d(TAG, "processResponseLocked(authRequired=" - + response.getAuthentication() +"):" + response); + + response.getAuthentication() + "):" + response); // TODO(b/33197203): add MetricsLogger calls @@ -746,30 +848,10 @@ final class AutoFillManagerServiceImpl { if (mCurrentResponse.getAuthentication() != null) { // Handle authentication. - final Intent fillInIntent = createAuthFillInIntent(response.getId(), mStructure, - new Bundle(), new FillCallback(new IFillCallback.Stub() { - @Override - public void onCancellable(ICancellationSignal cancellation) { - // TODO(b/33197203): Handle cancellation - } - - @Override - public void onSuccess(FillResponse response) { - mCurrentResponse = createAuthenticatedResponse( - mCurrentResponse, response); - processResponseLocked(mCurrentResponse); - } - - @Override - public void onFailure(CharSequence message) { - getUiForShowing().showError(message); - removeSelf(); - } - })); - - getUiForShowing().showFillResponseAuthRequest( - mCurrentResponse.getAuthentication(), fillInIntent); - return; + final Intent fillInIntent = createAuthFillInIntent(mStructure); + getUiForShowing().showFillResponseAuthRequest( + mCurrentResponse.getAuthentication(), fillInIntent); + return; } final ArraySet<AutoFillId> savableIds = mCurrentResponse.getSavableIds(); @@ -797,48 +879,20 @@ final class AutoFillManagerServiceImpl { } // ...or handle authentication. - Intent fillInIntent = createAuthFillInIntent(dataset.getId(), mStructure, - new Bundle(), new FillCallback(new IFillCallback.Stub() { - @Override - public void onCancellable(ICancellationSignal cancellation) { - // TODO(b/33197203): Handle cancellation - } - - @Override - public void onSuccess(FillResponse response) { - mCurrentResponse = createAuthenticatedResponse( - mCurrentResponse, response); - final Dataset augmentedDataset = Helper.findDatasetById(dataset.getId(), - mCurrentResponse); - if (augmentedDataset != null) { - autoFill(augmentedDataset); - } - } - - @Override - public void onFailure(CharSequence message) { - getUiForShowing().showError(message); - removeSelf(); - } - })); - - startAuthIntent(dataset.getAuthentication(), fillInIntent); + Intent fillInIntent = createAuthFillInIntent(mStructure); + startAuthentication(dataset.getAuthentication(), fillInIntent); } } - private Intent createAuthFillInIntent(String itemId, AssistStructure structure, - Bundle extras, FillCallback fillCallback) { + private Intent createAuthFillInIntent(AssistStructure structure) { Intent fillInIntent = new Intent(); - fillInIntent.putExtra(Intent.EXTRA_AUTO_FILL_ITEM_ID, itemId); - fillInIntent.putExtra(Intent.EXTRA_AUTO_FILL_ASSIST_STRUCTURE, structure); - fillInIntent.putExtra(Intent.EXTRA_AUTO_FILL_EXTRAS, extras); - fillInIntent.putExtra(Intent.EXTRA_AUTO_FILL_CALLBACK, fillCallback); + fillInIntent.putExtra(AutoFillManager.EXTRA_ASSIST_STRUCTURE, structure); return fillInIntent; } - private void startAuthIntent(IntentSender intent, Intent fillInIntent) { + private void startAuthentication(IntentSender intent, Intent fillInIntent) { try { - mAppCallback.startIntentSender(intent, fillInIntent); + mClient.authenticate(intent, fillInIntent); } catch (RemoteException e) { Slog.e(TAG, "Error launching auth intent", e); } @@ -874,7 +928,7 @@ final class AutoFillManagerServiceImpl { try { if (DEBUG) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset); - mAppCallback.autoFill(dataset); + mClient.autoFill(dataset.getFieldIds(), dataset.getFieldValues()); mAutoFilledDataset = dataset; } catch (RemoteException e) { Slog.w(TAG, "Error auto-filling activity: " + e); @@ -882,16 +936,6 @@ final class AutoFillManagerServiceImpl { } } - void enableSessionLocked() { - if (DEBUG) Slog.d(TAG, "enableSessionLocked()"); - - try { - mAppCallback.enableSession(); - } catch (RemoteException e) { - Slog.w(TAG, "Error enabling session: " + e); - } - } - private AutoFillUI getUiForShowing() { synchronized (mLock) { mUi.setCallbackLocked(this, mActivityToken); @@ -946,81 +990,5 @@ final class AutoFillManagerServiceImpl { mSessions.remove(mActivityToken); } } - - /** - * Creates a response from the {@code original} and an {@code update} by - * replacing all items that needed authentication (response or datasets) - * with their updated version if the latter does not need authentication. - * New datasets that don't require auth are appended. - * - * @param original The original response requiring auth at some level. - * @param update An updated response with auth not needed anymore at some level. - * @return A new response with updated items where auth is not needed anymore. - */ - // TODO(b/33197203) Unit test - FillResponse createAuthenticatedResponse(FillResponse original, FillResponse update) { - // Can update only if ids match - if (!original.getId().equals(update.getId())) { - return original; - } - - // If the original required auth and the update doesn't, the update wins - // but only if none of the update's datasets requires authentication. - if (original.getAuthentication() != null && update.getAuthentication() == null) { - ArraySet<Dataset> updateDatasets = update.getDatasets(); - final int udpateDatasetCount = updateDatasets.size(); - for (int i = 0; i < udpateDatasetCount; i++) { - Dataset updateDataset = updateDatasets.valueAt(i); - if (updateDataset.getAuthentication() != null) { - return original; - } - } - return update; - } - - // If no auth on response level we create a response that has all - // datasets from the original with the ones that required auth but - // not anymore updated and new ones not requiring auth appended. - - // The update shouldn't require auth - if (update.getAuthentication() != null) { - return original; - } - - final FillResponse.Builder builder = new FillResponse.Builder(original.getId()); - - // Update existing datasets - final ArraySet<Dataset> origDatasets = original.getDatasets(); - final int origDatasetCount = origDatasets.size(); - for (int i = 0; i < origDatasetCount; i++) { - Dataset origDataset = origDatasets.valueAt(i); - ArraySet<Dataset> updateDatasets = update.getDatasets(); - final int updateDatasetCount = updateDatasets.size(); - for (int j = 0; j < updateDatasetCount; j++) { - Dataset updateDataset = updateDatasets.valueAt(j); - if (origDataset.getId().equals(updateDataset.getId())) { - // The update shouldn't require auth - if (updateDataset.getAuthentication() == null) { - origDataset = updateDataset; - updateDatasets.removeAt(j); - } - break; - } - } - builder.addDataset(origDataset); - } - - // Add new datasets - final ArraySet<Dataset> updateDatasets = update.getDatasets(); - final int updateDatasetCount = updateDatasets.size(); - for (int i = 0; i < updateDatasetCount; i++) { - final Dataset updateDataset = updateDatasets.valueAt(i); - builder.addDataset(updateDataset); - } - - // For now no extras and savable id updates. - - return builder.build(); - } } } diff --git a/services/autofill/java/com/android/server/autofill/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/AutoFillUI.java index 0763c745f121..97700405279e 100644 --- a/services/autofill/java/com/android/server/autofill/AutoFillUI.java +++ b/services/autofill/java/com/android/server/autofill/AutoFillUI.java @@ -30,15 +30,13 @@ import android.content.IntentSender; import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; +import android.service.autofill.Dataset; import android.util.ArraySet; import android.os.Looper; import android.text.format.DateUtils; import android.util.Slog; -import android.view.autofill.Dataset; -import android.view.autofill.FillResponse; import android.view.Gravity; import android.view.View; -import android.view.ViewGroup; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.widget.Toast; @@ -200,7 +198,8 @@ final class AutoFillUI { } /** - * Shows an UI affordance indicating that user action is required before a {@link FillResponse} + * Shows an UI affordance indicating that user action is required before a {@link + * android.service.autofill.FillResponse} * can be used. * * <p>It typically replaces the auto-fill bar with a message saying "Press fingerprint or tap to diff --git a/services/autofill/java/com/android/server/autofill/DatasetPicker.java b/services/autofill/java/com/android/server/autofill/DatasetPicker.java index 9bee61a2c54f..a54cab928f4b 100644 --- a/services/autofill/java/com/android/server/autofill/DatasetPicker.java +++ b/services/autofill/java/com/android/server/autofill/DatasetPicker.java @@ -17,9 +17,9 @@ package com.android.server.autofill; import android.content.Context; import android.graphics.Color; +import android.service.autofill.Dataset; import android.text.TextUtils; import android.util.ArraySet; -import android.view.autofill.Dataset; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java index 0f2bb608ead3..48ae635c281f 100644 --- a/services/autofill/java/com/android/server/autofill/Helper.java +++ b/services/autofill/java/com/android/server/autofill/Helper.java @@ -18,11 +18,11 @@ package com.android.server.autofill; import android.annotation.Nullable; import android.os.Bundle; +import android.service.autofill.Dataset; +import android.service.autofill.FillResponse; import android.util.ArraySet; import android.view.autofill.AutoFillId; import android.view.autofill.AutoFillValue; -import android.view.autofill.Dataset; -import android.view.autofill.FillResponse; import java.util.ArrayList; import java.util.Arrays; @@ -78,25 +78,25 @@ final class Helper { } /** - * Finds a data set by id in a response. + * Finds the index of a data set given its name. * - * @param id The dataset id. + * @param name The dataset name. * @param response The response to search. - * @return The dataset if found or null. + * @return The index of dataset if found or -1. */ - static Dataset findDatasetById(String id, FillResponse response) { + static int indexOfDataset(CharSequence name, FillResponse response) { ArraySet<Dataset> datasets = response.getDatasets(); if (datasets == null || datasets.isEmpty()) { - return null; + return -1; } final int datasetCount = datasets.size(); for (int i = 0; i < datasetCount; i++) { Dataset dataset = datasets.valueAt(i); - if (dataset.getId().equals(id)) { - return dataset; + if (dataset.getName().toString().equals(name.toString())) { + return i; } } - return null; + return -1; } private Helper() { diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java index c070f779c6f9..767fb4619a43 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java @@ -31,12 +31,12 @@ import android.os.Message; import android.os.RemoteException; import android.os.UserHandle; import android.service.autofill.AutoFillService; +import android.service.autofill.FillResponse; import android.service.autofill.IAutoFillService; import android.service.autofill.IFillCallback; import android.service.autofill.ISaveCallback; import android.text.format.DateUtils; import android.util.Slog; -import android.view.autofill.FillResponse; import com.android.internal.os.HandlerCaller; import com.android.server.FgThread; |