Refactor auto-fill
* Fix a layering issue where auto-fill manager which is in view
depended on activity which is in app
* Moved auto-fill classes to view or service based on their
purpose and removed dependecy on the classes in view to the
classes in service
* Push state to local auto-fill manager whether auto-fill is
enabled to avoid making IPC for every focus transition if
the user did not enable the feature
* Remove unnecessary offload to messages when handling calls
to auto-fill manager service as these are made over a oneway
interface and in general they do almost no work and typically
we do these on the binder thread
* Removed id from data set and fill response as the provider
can embed everything it needs to id them in the auth pending
intent
* Enforce the auth UI to be only an activity as this will work
with multi-window, recents, and back and also does not require
draw on top of other app special permission
* Authentication also no longer requires passing a remotable
callback to the auth activity but the activity handles the
request as if called for a result
* Handling stopping of a user to clean up in-memory state as
well as handling when a user gets unlocked as a provider may
be non-direct boot aware
* User the correct context when creating an auto-fill manager
* Move the receiver that listens for requests to hide system
windows to the manager service as the UI is a singleton and
no need every per-user state to register its own
* Removed extras from dataset as the only case a provider needs
to associate state with a dataset is for auth and the provider
can embed this data in the auth pending intent
Test: manual and CTS
Change-Id: I4bc54c13cf779d7f6fdb3ab894637f9fac73f603
diff --git a/Android.mk b/Android.mk
index 9ebc276..28adbca 100644
--- a/Android.mk
+++ b/Android.mk
@@ -264,8 +264,6 @@
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 @@
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 e0d895e..6f9cb13 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9181,10 +9181,6 @@
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";
@@ -36199,12 +36195,37 @@
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 onFailure(java.lang.CharSequence);
- method public void onSuccess(android.view.autofill.FillResponse);
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.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.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.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 {
@@ -46838,6 +46859,8 @@
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 {
@@ -46865,35 +46888,6 @@
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 a5f3081..9095322 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -9619,10 +9619,6 @@
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";
@@ -39265,12 +39261,37 @@
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 onFailure(java.lang.CharSequence);
- method public void onSuccess(android.view.autofill.FillResponse);
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.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.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.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 {
@@ -50277,6 +50298,8 @@
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 {
@@ -50304,35 +50327,6 @@
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 a983cf5..b414d95e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -9207,10 +9207,6 @@
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";
@@ -36334,12 +36330,37 @@
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 onFailure(java.lang.CharSequence);
- method public void onSuccess(android.view.autofill.FillResponse);
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.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.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.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 {
@@ -47151,6 +47172,8 @@
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 {
@@ -47178,35 +47201,6 @@
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 edf60b4..4449454 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.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 @@
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 @@
"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 @@
private boolean mHasCurrentPermissionsRequest;
- @GuardedBy("this")
- private AutoFillSession mAutoFillSession;
-
private static native String getDlWarning();
/** Return the intent that started this activity. */
@@ -1695,25 +1695,6 @@
}
/**
- * 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 @@
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 @@
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 @@
mCurrentConfig = config;
mWindow.setColorMode(info.colorMode);
+
+ AutoFillManager.addClient(token, this);
}
/** @hide */
@@ -7038,6 +7015,8 @@
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 @@
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 dffd81f..23f0b09 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -89,8 +89,6 @@
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;
@@ -2931,16 +2929,13 @@
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));
@@ -2950,16 +2945,10 @@
} 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 44db326..f330a4b 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -114,7 +114,8 @@
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.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 @@
@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 5f4c36c..028a7bcf 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1806,41 +1806,6 @@
@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 6da6a39..4099f59 100644
--- a/core/java/android/service/autofill/AutoFillService.java
+++ b/core/java/android/service/autofill/AutoFillService.java
@@ -30,7 +30,6 @@
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 @@
// 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 @@
* 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 @@
* @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
similarity index 94%
rename from core/java/android/view/autofill/Dataset.aidl
rename to core/java/android/service/autofill/Dataset.aidl
index 2a8e67c..2342c5f 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
similarity index 60%
rename from core/java/android/view/autofill/Dataset.java
rename to core/java/android/service/autofill/Dataset.java
index 2708358..bd38c7f 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 @@
}
/** @hide */
- public @Nullable Bundle getExtras() {
- return mExtras;
- }
-
- /** @hide */
public @Nullable IntentSender getAuthentication() {
return mAuthentication;
}
@@ -100,71 +84,32 @@
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 @@
* 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 @@
/**
* 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 @@
}
/**
- * 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 @@
@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 @@
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 a306809..69c9904 100644
--- a/core/java/android/service/autofill/FillCallback.java
+++ b/core/java/android/service/autofill/FillCallback.java
@@ -19,16 +19,13 @@
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 @@
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 @@
}
}
- /** @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
similarity index 94%
rename from core/java/android/view/autofill/FillResponse.aidl
rename to core/java/android/service/autofill/FillResponse.aidl
index b018f15..e9c2d21 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
similarity index 73%
rename from core/java/android/view/autofill/FillResponse.java
rename to core/java/android/service/autofill/FillResponse.java
index 596a06c..ea36e64 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 @@
* .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 @@
* #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 @@
* 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 @@
}
/** @hide */
- public @NonNull String getId() {
- return mId;
- }
-
- /** @hide */
public @Nullable Bundle getExtras() {
return mExtras;
}
@@ -202,71 +191,49 @@
* 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.
+ * any data set in this response.
*
- * <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>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>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>
+ * <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>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>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()
*/
@@ -313,8 +280,8 @@
/**
* 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 @@
/**
* 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 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 @@
@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 @@
// 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/service/autofill/IAuthenticationCallback.aidl b/core/java/android/service/autofill/IAuthenticationCallback.aidl
new file mode 100644
index 0000000..36b989d
--- /dev/null
+++ b/core/java/android/service/autofill/IAuthenticationCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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 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/IAutoFillManagerService.aidl b/core/java/android/service/autofill/IAutoFillManagerService.aidl
deleted file mode 100644
index 6cdb516..0000000
--- a/core/java/android/service/autofill/IAutoFillManagerService.aidl
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2016 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.service.autofill;
-
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.view.autofill.AutoFillId;
-import android.view.autofill.AutoFillValue;
-
-/**
- * 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);
-}
diff --git a/core/java/android/service/autofill/IFillCallback.aidl b/core/java/android/service/autofill/IFillCallback.aidl
index 537403e..2bb3e9a 100644
--- a/core/java/android/service/autofill/IFillCallback.aidl
+++ b/core/java/android/service/autofill/IFillCallback.aidl
@@ -18,7 +18,7 @@
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 46b3072..c6dd1df 100644
--- a/core/java/android/service/autofill/SaveCallback.java
+++ b/core/java/android/service/autofill/SaveCallback.java
@@ -23,8 +23,6 @@
/**
* 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 58607ba..f7a1b61 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 AutoFillSession mSession;
+ private final Rect mTempRect = new Rect();
+
+ 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 @@
* @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);
+ ensureServiceClientAddedIfNeeded();
+
+ if (!mEnabled) {
return;
}
- if (!mSession.isEnabled()) {
- // Auto-fill is disabled for this session.
- 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 @@
* @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);
+ ensureServiceClientAddedIfNeeded();
+
+ if (!mEnabled) {
return;
}
- if (!mSession.isEnabled()) {
- // Auto-fill is disabled for this session.
- 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 @@
* @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 @@
* @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 @@
* 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();
}
- /**
- * Gets the current session, if any.
- *
- * @hide
- */
- @Nullable
- public AutoFillSession getSession() {
- return mSession;
+ /** @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;
+ }
+
+ /** @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 @@
}
private void startSession(AutoFillId id, Rect bounds, AutoFillValue value) {
- if (VERBOSE) {
+ if (DEBUG) {
Log.v(TAG, "startSession(): id=" + id + ", bounds=" + bounds + ", value=" + value);
}
-
- final IBinder activityToken = mContext.getActivityToken();
- mSession = new AutoFillSession(this, activityToken);
- final IBinder appCallback = mSession.getCallback().asBinder();
try {
- mService.startSession(activityToken, appCallback, id, bounds, value);
+ mService.startSession(mContext.getActivityToken(), mServiceClient.asBinder(),
+ id, bounds, value, mContext.getUserId());
+ mHasSession = true;
+ activateClient();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private void finishSession() {
+ if (DEBUG) {
+ Log.v(TAG, "finishSession()");
+ }
+ mHasSession = false;
+ try {
+ 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 64df62f..0000000
--- 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/view/autofill/FieldId.aidl b/core/java/android/view/autofill/FieldId.aidl
deleted file mode 100644
index 35af645..0000000
--- a/core/java/android/view/autofill/FieldId.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (c) 2016, 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;
-
-parcelable FieldId;
\ No newline at end of file
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
new file mode 100644
index 0000000..0433a8f
--- /dev/null
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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 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}
+ */
+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
similarity index 64%
rename from core/java/android/service/autofill/IAutoFillAppCallback.aidl
rename to core/java/android/view/autofill/IAutoFillManagerClient.aidl
index c2e72e8..45f363d 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 {
+ /**
+ * Notifies the client when the auto-fill enabled state changed.
+ */
+ void setState(boolean enabled);
+
/**
* Auto-fills the activity with the contents of a dataset.
*/
- void autoFill(in Dataset dataset);
+ void autoFill(in List<AutoFillId> ids, in List<AutoFillValue> values);
/**
- * Start an intent sender from the context of the filled app
+ * Authenticates a fill response or a data set.
*/
- void startIntentSender(in IntentSender intent, in Intent fillInIntent);
-
- /**
- * Called by system_service to enable auto-fill in a session, after it was asynchronously
- * started by the manager.
- */
- 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 ed0d234..64c6abd 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
* 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 c16a51c..3257812 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.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.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.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 @@
/**
* 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 @@
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 @@
// 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 @@
}
}
- 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;
- }
+ @Override
+ public void onUnlockUser(int userId) {
+ synchronized (mLock) {
+ updateCachedServiceLocked(userId);
}
+ }
- if (serviceInfo == null) {
- return null;
+ @Override
+ public void onStopUser(int userId) {
+ synchronized (mLock) {
+ removeCachedServiceLocked(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);
- }
-
- 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 @@
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 @@
}
}
- 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 @@
}
}
- 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 @@
}
}
- 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 @@
@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 2891518..8d43dfb 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 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 @@
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 @@
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 @@
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 @@
};
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 @@
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 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 @@
// 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 @@
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 @@
}
private void handleSessionSave(IBinder activityToken) {
-
synchronized (mLock) {
final Session session = mSessions.get(activityToken);
if (session == null) {
@@ -301,7 +349,6 @@
void destroyLocked() {
if (VERBOSE) Slog.v(TAG, "destroyLocked()");
- mContext.unregisterReceiver(mBroadcastReceiver);
for (Session session : mSessions.values()) {
session.destroyLocked();
}
@@ -311,7 +358,8 @@
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 @@
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 @@
pw.print(prefix); pw.print("updated:" ); pw.println(mValueUpdated);
pw.print(prefix); pw.print("bounds:" ); pw.println(mBounds);
}
-
}
/**
@@ -448,7 +525,7 @@
@Nullable
private ViewState mCurrentViewState;
- private final IAutoFillAppCallback mAppCallback;
+ private final IAutoFillManagerClient mClient;
@GuardedBy("mLock")
RemoteFillService mRemoteFillService;
@@ -471,19 +548,20 @@
@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 @@
// FillServiceCallbacks
@Override
public void authenticate(IntentSender intent, Intent fillInIntent) {
- startAuthIntent(intent, fillInIntent);
+ startAuthentication(intent, fillInIntent);
}
// FillServiceCallbacks
@@ -550,6 +628,30 @@
.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 @@
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 @@
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 @@
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 @@
}
// ...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 @@
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 @@
}
}
- 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 @@
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 0763c74..9770040 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.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 @@
}
/**
- * 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 9bee61a..a54cab9 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 @@
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 0f2bb60..48ae635 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 @@
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 @@
}
/**
- * 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 c070f77..767fb46 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.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;