summaryrefslogtreecommitdiff
path: root/core/java
diff options
context:
space:
mode:
authorTreeHugger Robot <treehugger-gerrit@google.com>2017-10-27 19:38:32 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2017-10-27 19:38:32 +0000
commit599ef4698bfa71b35f62f84cb5893faf499fbcd2 (patch)
treeba88f1cd86c6d9040e7a4154710f92b91b109bd7 /core/java
parente03114f9f648d29e7419a051f84dd7d651bcb070 (diff)
parent17292d1a25a4d0c3910a687a4207e7ff5688be1d (diff)
Merge "New Autofill API: FillResponse.disableAutofill(duration)"
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/app/Activity.java3
-rw-r--r--core/java/android/service/autofill/FillResponse.java106
-rw-r--r--core/java/android/view/autofill/AutofillManager.java86
-rw-r--r--core/java/android/view/autofill/IAutoFillManager.aidl6
-rw-r--r--core/java/android/view/autofill/IAutoFillManagerClient.aidl5
5 files changed, 161 insertions, 45 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 85f73bb7c0ef..9d331a02e392 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5867,10 +5867,11 @@ public class Activity extends ContextThemeWrapper
}
/**
- * Returns complete component name of this activity.
+ * Returns the complete component name of this activity.
*
* @return Returns the complete component name for this activity
*/
+ @Override
public ComponentName getComponentName()
{
return mComponent;
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 06992479bbf4..2f6342af2b3b 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -31,6 +31,8 @@ import android.os.Parcelable;
import android.view.autofill.AutofillId;
import android.widget.RemoteViews;
+import com.android.internal.util.Preconditions;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -51,9 +53,16 @@ public final class FillResponse implements Parcelable {
*/
public static final int FLAG_TRACK_CONTEXT_COMMITED = 0x1;
+ /**
+ * Used in conjunction to {@link FillResponse.Builder#disableAutofill(long)} to disable autofill
+ * only for the activiy associated with the {@link FillResponse}, instead of the whole app.
+ */
+ public static final int FLAG_DISABLE_ACTIVITY_ONLY = 0x2;
+
/** @hide */
@IntDef(flag = true, value = {
- FLAG_TRACK_CONTEXT_COMMITED
+ FLAG_TRACK_CONTEXT_COMMITED,
+ FLAG_DISABLE_ACTIVITY_ONLY
})
@Retention(RetentionPolicy.SOURCE)
@interface FillResponseFlags {}
@@ -65,6 +74,7 @@ public final class FillResponse implements Parcelable {
private final @Nullable IntentSender mAuthentication;
private final @Nullable AutofillId[] mAuthenticationIds;
private final @Nullable AutofillId[] mIgnoredIds;
+ private final long mDisableDuration;
private final int mFlags;
private int mRequestId;
@@ -76,6 +86,7 @@ public final class FillResponse implements Parcelable {
mAuthentication = builder.mAuthentication;
mAuthenticationIds = builder.mAuthenticationIds;
mIgnoredIds = builder.mIgnoredIds;
+ mDisableDuration = builder.mDisableDuration;
mFlags = builder.mFlags;
mRequestId = INVALID_REQUEST_ID;
}
@@ -116,6 +127,11 @@ public final class FillResponse implements Parcelable {
}
/** @hide */
+ public long getDisableDuration() {
+ return mDisableDuration;
+ }
+
+ /** @hide */
public int getFlags() {
return mFlags;
}
@@ -150,6 +166,7 @@ public final class FillResponse implements Parcelable {
private IntentSender mAuthentication;
private AutofillId[] mAuthenticationIds;
private AutofillId[] mIgnoredIds;
+ private long mDisableDuration;
private int mFlags;
private boolean mDestroyed;
@@ -187,7 +204,7 @@ public final class FillResponse implements Parcelable {
* which is used to visualize visualize the response for triggering the authentication
* flow.
*
- * <p></><strong>Note:</strong> Do not make the provided pending intent
+ * <p><b>Note:</b> 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.
*
@@ -197,13 +214,15 @@ public final class FillResponse implements Parcelable {
*
* @return This builder.
* @throws IllegalArgumentException if {@code ids} is {@code null} or empty, or if
- * neither {@code authentication} nor {@code presentation} is non-{@code null}.
+ * both {@code authentication} and {@code presentation} are {@code null}, or if
+ * both {@code authentication} and {@code presentation} are non-{@code null}
*
* @see android.app.PendingIntent#getIntentSender()
*/
public @NonNull Builder setAuthentication(@NonNull AutofillId[] ids,
@Nullable IntentSender authentication, @Nullable RemoteViews presentation) {
throwIfDestroyed();
+ throwIfDisableAutofillCalled();
if (ids == null || ids.length == 0) {
throw new IllegalArgumentException("ids cannot be null or empry");
}
@@ -226,6 +245,7 @@ public final class FillResponse implements Parcelable {
* text field representing the result of a Captcha challenge.
*/
public Builder setIgnoredIds(AutofillId...ids) {
+ throwIfDestroyed();
mIgnoredIds = ids;
return this;
}
@@ -246,6 +266,7 @@ public final class FillResponse implements Parcelable {
*/
public @NonNull Builder addDataset(@Nullable Dataset dataset) {
throwIfDestroyed();
+ throwIfDisableAutofillCalled();
if (dataset == null) {
return this;
}
@@ -265,6 +286,7 @@ public final class FillResponse implements Parcelable {
*/
public @NonNull Builder setSaveInfo(@NonNull SaveInfo saveInfo) {
throwIfDestroyed();
+ throwIfDisableAutofillCalled();
mSaveInfo = saveInfo;
return this;
}
@@ -295,30 +317,82 @@ public final class FillResponse implements Parcelable {
/**
* Sets flags changing the response behavior.
*
- * @param flags {@link #FLAG_TRACK_CONTEXT_COMMITED}, or {@code 0}.
+ * @param flags a combination of {@link #FLAG_TRACK_CONTEXT_COMMITED} and
+ * {@link #FLAG_DISABLE_ACTIVITY_ONLY}, or {@code 0}.
*
* @return This builder.
*/
public Builder setFlags(@FillResponseFlags int flags) {
throwIfDestroyed();
- mFlags = flags;
+ mFlags = Preconditions.checkFlagsArgument(flags,
+ FLAG_TRACK_CONTEXT_COMMITED | FLAG_DISABLE_ACTIVITY_ONLY);
+ return this;
+ }
+
+ /**
+ * Disables autofill for the app or activity.
+ *
+ * <p>This method is useful to optimize performance in cases where the service knows it
+ * can not autofill an app&mdash;for example, when the service has a list of "blacklisted"
+ * apps such as office suites.
+ *
+ * <p>By default, it disables autofill for all activities in the app, unless the response is
+ * {@link #setFlags(int) flagged} with {@link #FLAG_DISABLE_ACTIVITY_ONLY}.
+ *
+ * <p>Autofill for the app or activity is automatically re-enabled after any of the
+ * following conditions:
+ *
+ * <ol>
+ * <li>{@code duration} milliseconds have passed.
+ * <li>The autofill service for the user has changed.
+ * <li>The device has rebooted.
+ * </ol>
+ *
+ * <p><b>Note:</b> Activities that are running when autofill is re-enabled remain
+ * disabled for autofill until they finish and restart.
+ *
+ * @param duration duration to disable autofill, in milliseconds.
+ *
+ * @return this builder
+ *
+ * @throws IllegalArgumentException if {@code duration} is not a positive number.
+ * @throws IllegalStateException if either {@link #addDataset(Dataset)},
+ * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)}, or
+ * {@link #setSaveInfo(SaveInfo)} was already called.
+ */
+ public Builder disableAutofill(long duration) {
+ throwIfDestroyed();
+ if (duration <= 0) {
+ throw new IllegalArgumentException("duration must be greater than 0");
+ }
+ if (mAuthentication != null || mDatasets != null || mSaveInfo != null) {
+ throw new IllegalStateException("disableAutofill() must be the only method called");
+ }
+
+ mDisableDuration = duration;
return this;
}
/**
* Builds a new {@link FillResponse} instance.
*
- * <p>You must provide at least one dataset or some savable ids or an authentication with a
- * presentation view.
+ * @throws IllegalStateException if any of the following conditions occur:
+ * <ol>
+ * <li>{@link #build()} was already called.
+ * <li>No call was made to {@link #addDataset(Dataset)},
+ * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)},
+ * {@link #setSaveInfo(SaveInfo)}, or {@link #disableAutofill(long)}.
+ * </ol>
*
* @return A built response.
*/
public FillResponse build() {
throwIfDestroyed();
- if (mAuthentication == null && mDatasets == null && mSaveInfo == null) {
- throw new IllegalArgumentException("need to provide at least one DataSet or a "
- + "SaveInfo or an authentication with a presentation");
+ if (mAuthentication == null && mDatasets == null && mSaveInfo == null
+ && mDisableDuration == 0) {
+ throw new IllegalStateException("need to provide at least one DataSet or a "
+ + "SaveInfo or an authentication with a presentation or disable autofill");
}
mDestroyed = true;
return new FillResponse(this);
@@ -329,6 +403,12 @@ public final class FillResponse implements Parcelable {
throw new IllegalStateException("Already called #build()");
}
}
+
+ private void throwIfDisableAutofillCalled() {
+ if (mDisableDuration > 0) {
+ throw new IllegalStateException("Already called #disableAutofill()");
+ }
+ }
}
/////////////////////////////////////
@@ -348,6 +428,7 @@ public final class FillResponse implements Parcelable {
.append(", hasAuthentication=").append(mAuthentication != null)
.append(", authenticationIds=").append(Arrays.toString(mAuthenticationIds))
.append(", ignoredIds=").append(Arrays.toString(mIgnoredIds))
+ .append(", disableDuration=").append(mDisableDuration)
.append(", flags=").append(mFlags)
.append("]")
.toString();
@@ -371,6 +452,7 @@ public final class FillResponse implements Parcelable {
parcel.writeParcelable(mAuthentication, flags);
parcel.writeParcelable(mPresentation, flags);
parcel.writeParcelableArray(mIgnoredIds, flags);
+ parcel.writeLong(mDisableDuration);
parcel.writeInt(mFlags);
parcel.writeInt(mRequestId);
}
@@ -402,6 +484,10 @@ public final class FillResponse implements Parcelable {
}
builder.setIgnoredIds(parcel.readParcelableArray(null, AutofillId.class));
+ final long disableDuration = parcel.readLong();
+ if (disableDuration > 0) {
+ builder.disableAutofill(disableDuration);
+ }
builder.setFlags(parcel.readInt());
final FillResponse response = builder.build();
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index e564fa344ce5..e79d201be2c5 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -24,6 +24,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemService;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
@@ -224,7 +225,7 @@ public final class AutofillManager {
/**
* State where the autofill context was finished by the server because the autofill
- * service could not autofill the page.
+ * service could not autofill the activity.
*
* <p>In this state, most apps callback (such as {@link #notifyViewEntered(View)}) are ignored,
* exception {@link #requestAutofill(View)} (and {@link #requestAutofill(View, int, Rect)}).
@@ -242,6 +243,16 @@ public final class AutofillManager {
public static final int STATE_SHOWING_SAVE_UI = 3;
/**
+ * State where the autofill is disabled because the service cannot autofill the activity at all.
+ *
+ * <p>In this state, every call is ignored, even {@link #requestAutofill(View)}
+ * (and {@link #requestAutofill(View, int, Rect)}).
+ *
+ * @hide
+ */
+ public static final int STATE_DISABLED_BY_SERVICE = 4;
+
+ /**
* Makes an authentication id from a request id and a dataset id.
*
* @param requestId The request id.
@@ -398,6 +409,11 @@ public final class AutofillManager {
* Runs the specified action on the UI thread.
*/
void runOnUiThread(Runnable action);
+
+ /**
+ * Gets the complete component name of this client.
+ */
+ ComponentName getComponentName();
}
/**
@@ -506,7 +522,7 @@ public final class AutofillManager {
* @return whether autofill is enabled for the current user.
*/
public boolean isEnabled() {
- if (!hasAutofillFeature()) {
+ if (!hasAutofillFeature() || isDisabledByService()) {
return false;
}
synchronized (mLock) {
@@ -580,19 +596,31 @@ public final class AutofillManager {
notifyViewEntered(view, 0);
}
+ private boolean shouldIgnoreViewEnteredLocked(@NonNull View view, int flags) {
+ if (isDisabledByService()) {
+ if (sVerbose) {
+ Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + view
+ + ") on state " + getStateAsStringLocked());
+ }
+ return true;
+ }
+ if (mState == STATE_FINISHED && (flags & FLAG_MANUAL_REQUEST) == 0) {
+ if (sVerbose) {
+ Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + view
+ + ") on state " + getStateAsStringLocked());
+ }
+ return true;
+ }
+ return false;
+ }
+
private void notifyViewEntered(@NonNull View view, int flags) {
if (!hasAutofillFeature()) {
return;
}
AutofillCallback callback = null;
synchronized (mLock) {
- if (isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
- if (sVerbose) {
- Log.v(TAG, "notifyViewEntered(flags=" + flags + ", view=" + view
- + "): ignored on state " + getStateAsStringLocked());
- }
- return;
- }
+ if (shouldIgnoreViewEnteredLocked(view, flags)) return;
ensureServiceClientAddedIfNeededLocked();
@@ -717,14 +745,8 @@ public final class AutofillManager {
}
AutofillCallback callback = null;
synchronized (mLock) {
- if (isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
- if (sVerbose) {
- Log.v(TAG, "notifyViewEntered(flags=" + flags + ", view=" + view
- + ", virtualId=" + virtualId
- + "): ignored on state " + getStateAsStringLocked());
- }
- return;
- }
+ if (shouldIgnoreViewEnteredLocked(view, flags)) return;
+
ensureServiceClientAddedIfNeededLocked();
if (!mEnabled) {
@@ -1059,13 +1081,13 @@ public final class AutofillManager {
return;
}
try {
+ final AutofillClient client = getClientLocked();
mSessionId = mService.startSession(mContext.getActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
- mCallback != null, flags, mContext.getOpPackageName());
+ mCallback != null, flags, client.getComponentName());
if (mSessionId != NO_SESSION) {
mState = STATE_ACTIVE;
}
- final AutofillClient client = getClientLocked();
if (client != null) {
client.autofillCallbackResetableStateAvailable();
}
@@ -1120,14 +1142,14 @@ public final class AutofillManager {
try {
if (restartIfNecessary) {
+ final AutofillClient client = getClientLocked();
final int newId = mService.updateOrRestartSession(mContext.getActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
- mCallback != null, flags, mContext.getOpPackageName(), mSessionId, action);
+ mCallback != null, flags, client.getComponentName(), mSessionId, action);
if (newId != mSessionId) {
if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
mSessionId = newId;
mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE;
- final AutofillClient client = getClientLocked();
if (client != null) {
client.autofillCallbackResetableStateAvailable();
}
@@ -1437,7 +1459,9 @@ public final class AutofillManager {
* Marks the state of the session as finished.
*
* @param newState {@link #STATE_FINISHED} (because the autofill service returned a {@code null}
- * FillResponse) or {@link #STATE_UNKNOWN} (because the session was removed).
+ * FillResponse), {@link #STATE_UNKNOWN} (because the session was removed), or
+ * {@link #STATE_DISABLED_BY_SERVICE} (because the autofill service disabled further autofill
+ * requests for the activity).
*/
private void setSessionFinished(int newState) {
synchronized (mLock) {
@@ -1482,10 +1506,10 @@ public final class AutofillManager {
}
}
- private void notifyNoFillUi(int sessionId, AutofillId id, boolean sessionFinished) {
+ private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
if (sVerbose) {
Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
- + ", finished=" + sessionFinished);
+ + ", sessionFinishedState=" + sessionFinishedState);
}
final View anchor = findView(id);
if (anchor == null) {
@@ -1508,9 +1532,9 @@ public final class AutofillManager {
}
}
- if (sessionFinished) {
+ if (sessionFinishedState != 0) {
// Callback call was "hijacked" to also update the session state.
- setSessionFinished(STATE_FINISHED);
+ setSessionFinished(sessionFinishedState);
}
}
@@ -1613,6 +1637,8 @@ public final class AutofillManager {
return "STATE_FINISHED";
case STATE_SHOWING_SAVE_UI:
return "STATE_SHOWING_SAVE_UI";
+ case STATE_DISABLED_BY_SERVICE:
+ return "STATE_DISABLED_BY_SERVICE";
default:
return "INVALID:" + mState;
}
@@ -1622,8 +1648,8 @@ public final class AutofillManager {
return mState == STATE_ACTIVE;
}
- private boolean isFinishedLocked() {
- return mState == STATE_FINISHED;
+ private boolean isDisabledByService() {
+ return mState == STATE_DISABLED_BY_SERVICE;
}
private void post(Runnable runnable) {
@@ -1957,10 +1983,10 @@ public final class AutofillManager {
}
@Override
- public void notifyNoFillUi(int sessionId, AutofillId id, boolean sessionFinished) {
+ public void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
- afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinished));
+ afm.post(() -> afm.notifyNoFillUi(sessionId, id, sessionFinishedState));
}
}
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 6bd9bec368c8..9329c4dcff6a 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -16,6 +16,7 @@
package android.view.autofill;
+import android.content.ComponentName;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
@@ -34,14 +35,15 @@ interface IAutoFillManager {
int addClient(in IAutoFillManagerClient client, int userId);
int startSession(IBinder activityToken, in IBinder appCallback, in AutofillId autoFillId,
in Rect bounds, in AutofillValue value, int userId, boolean hasCallback, int flags,
- String packageName);
+ in ComponentName componentName);
FillEventHistory getFillEventHistory();
boolean restoreSession(int sessionId, in IBinder activityToken, in IBinder appCallback);
void updateSession(int sessionId, in AutofillId id, in Rect bounds,
in AutofillValue value, int action, int flags, int userId);
int updateOrRestartSession(IBinder activityToken, in IBinder appCallback,
in AutofillId autoFillId, in Rect bounds, in AutofillValue value, int userId,
- boolean hasCallback, int flags, String packageName, int sessionId, int action);
+ boolean hasCallback, int flags, in ComponentName componentName, int sessionId,
+ int action);
void finishSession(int sessionId, int userId);
void cancelSession(int sessionId, int userId);
void setAuthenticationResult(in Bundle data, int sessionId, int authenticationId, int userId);
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 56a22c22f4c5..368c02907bf3 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -68,9 +68,10 @@ oneway interface IAutoFillManagerClient {
void requestHideFillUi(int sessionId, in AutofillId id);
/**
- * Notifies no fill UI will be shown, and also mark the state as finished if necessary.
+ * Notifies no fill UI will be shown, and also mark the state as finished if necessary (if
+ * sessionFinishedState != 0).
*/
- void notifyNoFillUi(int sessionId, in AutofillId id, boolean sessionFinished);
+ void notifyNoFillUi(int sessionId, in AutofillId id, int sessionFinishedState);
/**
* Starts the provided intent sender.