Add AutofillService#onSavedDatasetsInfoRequest.
System settings will use this to display the number of saved passwords in each AutofillService available on the device. The API supports some flexibility to make it easier to support a different settings UX in the future. Bug: 169455298 CTS-Coverage-Bug: 183499221 Test: manual - check the result displayed in settings Change-Id: I13dd2f06dad18cfe8ef5de9b44854e44ac8d7365
This commit is contained in:
@@ -37468,6 +37468,7 @@ package android.service.autofill {
|
||||
method public void onDisconnected();
|
||||
method public abstract void onFillRequest(@NonNull android.service.autofill.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.FillCallback);
|
||||
method public abstract void onSaveRequest(@NonNull android.service.autofill.SaveRequest, @NonNull android.service.autofill.SaveCallback);
|
||||
method public void onSavedDatasetsInfoRequest(@NonNull android.service.autofill.SavedDatasetsInfoCallback);
|
||||
field public static final String SERVICE_INTERFACE = "android.service.autofill.AutofillService";
|
||||
field public static final String SERVICE_META_DATA = "android.autofill";
|
||||
}
|
||||
@@ -37743,6 +37744,22 @@ package android.service.autofill {
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
|
||||
}
|
||||
|
||||
public final class SavedDatasetsInfo {
|
||||
ctor public SavedDatasetsInfo(@NonNull String, @IntRange(from=0) int);
|
||||
method @IntRange(from=0) public int getCount();
|
||||
method @NonNull public String getType();
|
||||
field public static final String TYPE_OTHER = "other";
|
||||
field public static final String TYPE_PASSWORDS = "passwords";
|
||||
}
|
||||
|
||||
public interface SavedDatasetsInfoCallback {
|
||||
method public void onError(int);
|
||||
method public void onSuccess(@NonNull java.util.Set<android.service.autofill.SavedDatasetsInfo>);
|
||||
field public static final int ERROR_NEEDS_USER_ACTION = 2; // 0x2
|
||||
field public static final int ERROR_OTHER = 0; // 0x0
|
||||
field public static final int ERROR_UNSUPPORTED = 1; // 0x1
|
||||
}
|
||||
|
||||
public final class TextValueSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
|
||||
ctor public TextValueSanitizer(@NonNull java.util.regex.Pattern, @NonNull String);
|
||||
method public int describeContents();
|
||||
|
||||
@@ -38,6 +38,8 @@ import android.view.autofill.AutofillId;
|
||||
import android.view.autofill.AutofillManager;
|
||||
import android.view.autofill.AutofillValue;
|
||||
|
||||
import com.android.internal.os.IResultReceiver;
|
||||
|
||||
/**
|
||||
* An {@code AutofillService} is a service used to automatically fill the contents of the screen
|
||||
* on behalf of a given user - for more information about autofill, read
|
||||
@@ -575,6 +577,20 @@ public abstract class AutofillService extends Service {
|
||||
*/
|
||||
public static final String SERVICE_META_DATA = "android.autofill";
|
||||
|
||||
/**
|
||||
* Name of the {@link IResultReceiver} extra used to return the primary result of a request.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final String EXTRA_RESULT = "result";
|
||||
|
||||
/**
|
||||
* Name of the {@link IResultReceiver} extra used to return the error reason of a request.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final String EXTRA_ERROR = "error";
|
||||
|
||||
private final IAutoFillService mInterface = new IAutoFillService.Stub() {
|
||||
@Override
|
||||
public void onConnectedStateChanged(boolean connected) {
|
||||
@@ -603,6 +619,14 @@ public abstract class AutofillService extends Service {
|
||||
AutofillService::onSaveRequest,
|
||||
AutofillService.this, request, new SaveCallback(callback)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSavedPasswordCountRequest(IResultReceiver receiver) {
|
||||
mHandler.sendMessage(obtainMessage(
|
||||
AutofillService::onSavedDatasetsInfoRequest,
|
||||
AutofillService.this,
|
||||
new SavedDatasetsInfoCallbackImpl(receiver, SavedDatasetsInfo.TYPE_PASSWORDS)));
|
||||
}
|
||||
};
|
||||
|
||||
private Handler mHandler;
|
||||
@@ -672,6 +696,19 @@ public abstract class AutofillService extends Service {
|
||||
public abstract void onSaveRequest(@NonNull SaveRequest request,
|
||||
@NonNull SaveCallback callback);
|
||||
|
||||
/**
|
||||
* Called from system settings to display information about the datasets the user saved to this
|
||||
* service.
|
||||
*
|
||||
* <p>There is no timeout for the request, but it's recommended to return the result within a
|
||||
* few seconds, or the user may navigate away from the activity that would display the result.
|
||||
*
|
||||
* @param callback callback for responding to the request
|
||||
*/
|
||||
public void onSavedDatasetsInfoRequest(@NonNull SavedDatasetsInfoCallback callback) {
|
||||
callback.onError(SavedDatasetsInfoCallback.ERROR_UNSUPPORTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the Android system disconnects from the service.
|
||||
*
|
||||
|
||||
@@ -31,4 +31,5 @@ oneway interface IAutoFillService {
|
||||
void onConnectedStateChanged(boolean connected);
|
||||
void onFillRequest(in FillRequest request, in IFillCallback callback);
|
||||
void onSaveRequest(in SaveRequest request, in ISaveCallback callback);
|
||||
void onSavedPasswordCountRequest(in IResultReceiver receiver);
|
||||
}
|
||||
|
||||
186
core/java/android/service/autofill/SavedDatasetsInfo.java
Normal file
186
core/java/android/service/autofill/SavedDatasetsInfo.java
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.StringDef;
|
||||
|
||||
import com.android.internal.util.DataClass;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* A result returned from
|
||||
* {@link AutofillService#onSavedDatasetsInfoRequest(SavedDatasetsInfoCallback)}.
|
||||
*/
|
||||
@DataClass(
|
||||
genToString = true,
|
||||
genHiddenConstDefs = true,
|
||||
genEqualsHashCode = true)
|
||||
public final class SavedDatasetsInfo {
|
||||
|
||||
/**
|
||||
* Any other type of datasets.
|
||||
*/
|
||||
public static final String TYPE_OTHER = "other";
|
||||
|
||||
/**
|
||||
* Datasets such as login credentials.
|
||||
*/
|
||||
public static final String TYPE_PASSWORDS = "passwords";
|
||||
|
||||
/**
|
||||
* The type of the datasets that this info is about.
|
||||
*/
|
||||
@NonNull
|
||||
@Type
|
||||
private final String mType;
|
||||
|
||||
/**
|
||||
* The number of datasets of {@link #getType() this type} that the user has saved to the
|
||||
* service.
|
||||
*/
|
||||
@IntRange(from = 0)
|
||||
private final int mCount;
|
||||
|
||||
|
||||
// Code below generated by codegen v1.0.22.
|
||||
//
|
||||
// DO NOT MODIFY!
|
||||
// CHECKSTYLE:OFF Generated code
|
||||
//
|
||||
// To regenerate run:
|
||||
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/autofill/SavedDatasetsInfo.java
|
||||
//
|
||||
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
|
||||
// Settings > Editor > Code Style > Formatter Control
|
||||
//@formatter:off
|
||||
|
||||
|
||||
/** @hide */
|
||||
@StringDef(prefix = "TYPE_", value = {
|
||||
TYPE_OTHER,
|
||||
TYPE_PASSWORDS
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@DataClass.Generated.Member
|
||||
public @interface Type {}
|
||||
|
||||
/**
|
||||
* Creates a new SavedDatasetsInfo.
|
||||
*
|
||||
* @param type
|
||||
* The type of the datasets.
|
||||
* @param count
|
||||
* The number of datasets of this type that the user has saved to the service.
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public SavedDatasetsInfo(
|
||||
@NonNull @Type String type,
|
||||
@IntRange(from = 0) int count) {
|
||||
this.mType = type;
|
||||
|
||||
if (!(java.util.Objects.equals(mType, TYPE_OTHER))
|
||||
&& !(java.util.Objects.equals(mType, TYPE_PASSWORDS))) {
|
||||
throw new java.lang.IllegalArgumentException(
|
||||
"type was " + mType + " but must be one of: "
|
||||
+ "TYPE_OTHER(" + TYPE_OTHER + "), "
|
||||
+ "TYPE_PASSWORDS(" + TYPE_PASSWORDS + ")");
|
||||
}
|
||||
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
NonNull.class, null, mType);
|
||||
this.mCount = count;
|
||||
com.android.internal.util.AnnotationValidations.validate(
|
||||
IntRange.class, null, mCount,
|
||||
"from", 0);
|
||||
|
||||
// onConstructed(); // You can define this method to get a callback
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the datasets.
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @NonNull @Type String getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of datasets of this type that the user has saved to the service.
|
||||
*/
|
||||
@DataClass.Generated.Member
|
||||
public @IntRange(from = 0) int getCount() {
|
||||
return mCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
@DataClass.Generated.Member
|
||||
public String toString() {
|
||||
// You can override field toString logic by defining methods like:
|
||||
// String fieldNameToString() { ... }
|
||||
|
||||
return "SavedDatasetsInfo { " +
|
||||
"type = " + mType + ", " +
|
||||
"count = " + mCount +
|
||||
" }";
|
||||
}
|
||||
|
||||
@Override
|
||||
@DataClass.Generated.Member
|
||||
public boolean equals(@android.annotation.Nullable Object o) {
|
||||
// You can override field equality logic by defining either of the methods like:
|
||||
// boolean fieldNameEquals(SavedDatasetsInfo other) { ... }
|
||||
// boolean fieldNameEquals(FieldType otherValue) { ... }
|
||||
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
@SuppressWarnings("unchecked")
|
||||
SavedDatasetsInfo that = (SavedDatasetsInfo) o;
|
||||
//noinspection PointlessBooleanExpression
|
||||
return true
|
||||
&& java.util.Objects.equals(mType, that.mType)
|
||||
&& mCount == that.mCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
@DataClass.Generated.Member
|
||||
public int hashCode() {
|
||||
// You can override field hashCode logic by defining methods like:
|
||||
// int fieldNameHashCode() { ... }
|
||||
|
||||
int _hash = 1;
|
||||
_hash = 31 * _hash + java.util.Objects.hashCode(mType);
|
||||
_hash = 31 * _hash + mCount;
|
||||
return _hash;
|
||||
}
|
||||
|
||||
@DataClass.Generated(
|
||||
time = 1615325704446L,
|
||||
codegenVersion = "1.0.22",
|
||||
sourceFile = "frameworks/base/core/java/android/service/autofill/SavedDatasetsInfo.java",
|
||||
inputSignatures = "public static final java.lang.String TYPE_OTHER\npublic static final java.lang.String TYPE_PASSWORDS\nprivate final @android.annotation.NonNull @android.service.autofill.SavedDatasetsInfo.Type java.lang.String mType\nprivate final @android.annotation.IntRange int mCount\nclass SavedDatasetsInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
|
||||
@Deprecated
|
||||
private void __metadata() {}
|
||||
|
||||
|
||||
//@formatter:on
|
||||
// End of generated code
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Handles the response to
|
||||
* {@link AutofillService#onSavedDatasetsInfoRequest(SavedDatasetsInfoCallback)}.
|
||||
* <p>
|
||||
* Use {@link #onSuccess(Set)} to return the computed info about the datasets the user saved to this
|
||||
* service. If there was an error querying the info, or if the service is unable to do so at this
|
||||
* time (for example, if the user isn't logged in), call {@link #onError(int)}.
|
||||
* <p>
|
||||
* This callback can be used only once.
|
||||
*/
|
||||
public interface SavedDatasetsInfoCallback {
|
||||
|
||||
/** @hide */
|
||||
@IntDef(prefix = {"ERROR_"}, value = {
|
||||
ERROR_OTHER,
|
||||
ERROR_UNSUPPORTED,
|
||||
ERROR_NEEDS_USER_ACTION
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface Error {
|
||||
}
|
||||
|
||||
/**
|
||||
* The result could not be computed for any other reason.
|
||||
*/
|
||||
int ERROR_OTHER = 0;
|
||||
/**
|
||||
* The service does not support this request.
|
||||
*/
|
||||
int ERROR_UNSUPPORTED = 1;
|
||||
/**
|
||||
* The result cannot be computed until the user takes some action, such as setting up their
|
||||
* account.
|
||||
*/
|
||||
int ERROR_NEEDS_USER_ACTION = 2;
|
||||
|
||||
/**
|
||||
* Successfully respond to the request with the info on each type of saved datasets.
|
||||
*/
|
||||
void onSuccess(@NonNull Set<SavedDatasetsInfo> results);
|
||||
|
||||
/**
|
||||
* Respond to the request with an error. System settings may display a suitable notice to the
|
||||
* user.
|
||||
*/
|
||||
void onError(@Error int error);
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 static android.service.autofill.AutofillService.EXTRA_ERROR;
|
||||
import static android.service.autofill.AutofillService.EXTRA_RESULT;
|
||||
|
||||
import static com.android.internal.util.Preconditions.checkArgumentInRange;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.os.Bundle;
|
||||
import android.os.DeadObjectException;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.os.IResultReceiver;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
final class SavedDatasetsInfoCallbackImpl implements SavedDatasetsInfoCallback {
|
||||
private static final String TAG = "AutofillService";
|
||||
|
||||
@NonNull
|
||||
private final IResultReceiver mReceiver;
|
||||
@NonNull
|
||||
private final String mType;
|
||||
|
||||
/**
|
||||
* Creates a {@link SavedDatasetsInfoCallback} that returns the {@link
|
||||
* SavedDatasetsInfo#getCount() number} of saved datasets of {@code type} to the {@code
|
||||
* receiver}.
|
||||
*/
|
||||
SavedDatasetsInfoCallbackImpl(@NonNull IResultReceiver receiver, @NonNull String type) {
|
||||
mReceiver = requireNonNull(receiver);
|
||||
mType = requireNonNull(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(@NonNull Set<SavedDatasetsInfo> results) {
|
||||
requireNonNull(results);
|
||||
if (results.isEmpty()) {
|
||||
send(1, null);
|
||||
return;
|
||||
}
|
||||
int count = -1;
|
||||
for (SavedDatasetsInfo info : results) {
|
||||
if (mType.equals(info.getType())) {
|
||||
count = info.getCount();
|
||||
}
|
||||
}
|
||||
if (count < 0) {
|
||||
send(1, null);
|
||||
return;
|
||||
}
|
||||
Bundle bundle = new Bundle(/* capacity= */ 1);
|
||||
bundle.putInt(EXTRA_RESULT, count);
|
||||
send(0, bundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@Error int error) {
|
||||
checkArgumentInRange(error, ERROR_OTHER, ERROR_NEEDS_USER_ACTION, "error");
|
||||
Bundle bundle = new Bundle(/* capacity= */ 1);
|
||||
bundle.putInt(EXTRA_ERROR, error);
|
||||
send(1, bundle);
|
||||
}
|
||||
|
||||
private void send(int resultCode, Bundle bundle) {
|
||||
try {
|
||||
mReceiver.send(resultCode, bundle);
|
||||
} catch (DeadObjectException e) {
|
||||
Log.w(TAG, "Failed to send onSavedPasswordCountRequest result: " + e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowAsRuntimeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user