Merge "Add ListEnabledProviders API method for Credential Manager"
This commit is contained in:
committed by
Android (Google) Code Review
commit
a6ec92a1b9
@@ -196,6 +196,48 @@ public final class CredentialManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of all user configurable credential providers registered on the system. This API
|
||||
* is intended for browsers and settings apps.
|
||||
*
|
||||
* @param cancellationSignal an optional signal that allows for cancelling this call
|
||||
* @param executor the callback will take place on this {@link Executor}
|
||||
* @param callback the callback invoked when the request succeeds or fails
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(
|
||||
allOf = {
|
||||
android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS,
|
||||
android.Manifest.permission.QUERY_ALL_PACKAGES
|
||||
})
|
||||
public void listEnabledProviders(
|
||||
@Nullable CancellationSignal cancellationSignal,
|
||||
@CallbackExecutor @NonNull Executor executor,
|
||||
@NonNull
|
||||
OutcomeReceiver<ListEnabledProvidersResponse, ListEnabledProvidersException>
|
||||
callback) {
|
||||
requireNonNull(executor, "executor must not be null");
|
||||
requireNonNull(callback, "callback must not be null");
|
||||
|
||||
if (cancellationSignal != null && cancellationSignal.isCanceled()) {
|
||||
Log.w(TAG, "listEnabledProviders already canceled");
|
||||
return;
|
||||
}
|
||||
|
||||
ICancellationSignal cancelRemote = null;
|
||||
try {
|
||||
cancelRemote =
|
||||
mService.listEnabledProviders(
|
||||
new ListEnabledProvidersTransport(executor, callback));
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowFromSystemServer();
|
||||
}
|
||||
|
||||
if (cancellationSignal != null && cancelRemote != null) {
|
||||
cancellationSignal.setRemote(cancelRemote);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a list of all user configurable credential providers registered on the system. This API
|
||||
* is intended for settings apps.
|
||||
@@ -331,6 +373,33 @@ public final class CredentialManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static class ListEnabledProvidersTransport extends IListEnabledProvidersCallback.Stub {
|
||||
// TODO: listen for cancellation to release callback.
|
||||
|
||||
private final Executor mExecutor;
|
||||
private final OutcomeReceiver<ListEnabledProvidersResponse, ListEnabledProvidersException>
|
||||
mCallback;
|
||||
|
||||
private ListEnabledProvidersTransport(
|
||||
Executor executor,
|
||||
OutcomeReceiver<ListEnabledProvidersResponse, ListEnabledProvidersException>
|
||||
callback) {
|
||||
mExecutor = executor;
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(ListEnabledProvidersResponse response) {
|
||||
mExecutor.execute(() -> mCallback.onResult(response));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String errorType, String message) {
|
||||
mExecutor.execute(
|
||||
() -> mCallback.onError(new ListEnabledProvidersException(errorType, message)));
|
||||
}
|
||||
}
|
||||
|
||||
private static class SetEnabledProvidersTransport extends ISetEnabledProvidersCallback.Stub {
|
||||
// TODO: listen for cancellation to release callback.
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.credentials.GetCredentialRequest;
|
||||
import android.credentials.IClearCredentialStateCallback;
|
||||
import android.credentials.ICreateCredentialCallback;
|
||||
import android.credentials.IGetCredentialCallback;
|
||||
import android.credentials.IListEnabledProvidersCallback;
|
||||
import android.credentials.ISetEnabledProvidersCallback;
|
||||
import android.os.ICancellationSignal;
|
||||
|
||||
@@ -40,5 +41,7 @@ interface ICredentialManager {
|
||||
|
||||
@nullable ICancellationSignal clearCredentialState(in ClearCredentialStateRequest request, in IClearCredentialStateCallback callback, String callingPackage);
|
||||
|
||||
@nullable ICancellationSignal listEnabledProviders(in IListEnabledProvidersCallback callback);
|
||||
|
||||
void setEnabledProviders(in List<String> providers, in int userId, in ISetEnabledProvidersCallback callback);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2022 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.credentials;
|
||||
|
||||
import android.credentials.ListEnabledProvidersResponse;
|
||||
|
||||
/**
|
||||
* Listener for an listEnabledProviders request.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
interface IListEnabledProvidersCallback {
|
||||
oneway void onResponse(in ListEnabledProvidersResponse response);
|
||||
oneway void onError(String errorType, String message);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2022 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.credentials;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.OutcomeReceiver;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
/**
|
||||
* Represents an error encountered during the {@link
|
||||
* CredentialManager#listEnabledProviders(CancellationSignal Executor, OutcomeReceiver)} operation.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class ListEnabledProvidersException extends Exception {
|
||||
|
||||
@NonNull public final String errorType;
|
||||
|
||||
/**
|
||||
* Constructs a {@link ListEnabledProvidersException}.
|
||||
*
|
||||
* @throws IllegalArgumentException If errorType is empty.
|
||||
*/
|
||||
public ListEnabledProvidersException(@NonNull String errorType, @Nullable String message) {
|
||||
this(errorType, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@link ListEnabledProvidersException}.
|
||||
*
|
||||
* @throws IllegalArgumentException If errorType is empty.
|
||||
*/
|
||||
public ListEnabledProvidersException(
|
||||
@NonNull String errorType, @Nullable String message, @Nullable Throwable cause) {
|
||||
super(message, cause);
|
||||
this.errorType =
|
||||
Preconditions.checkStringNotEmpty(errorType, "errorType must not be empty");
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@link ListEnabledProvidersException}.
|
||||
*
|
||||
* @throws IllegalArgumentException If errorType is empty.
|
||||
*/
|
||||
public ListEnabledProvidersException(@NonNull String errorType, @Nullable Throwable cause) {
|
||||
this(errorType, null, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@link ListEnabledProvidersException}.
|
||||
*
|
||||
* @throws IllegalArgumentException If errorType is empty.
|
||||
*/
|
||||
public ListEnabledProvidersException(@NonNull String errorType) {
|
||||
this(errorType, null, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2022 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.credentials;
|
||||
|
||||
parcelable ListEnabledProvidersResponse;
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2022 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.credentials;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Response from Credential Manager listing the providers that are enabled and available to the
|
||||
* user.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class ListEnabledProvidersResponse implements Parcelable {
|
||||
|
||||
/** List of providers. */
|
||||
@NonNull private final List<String> mProviders;
|
||||
|
||||
/**
|
||||
* Creates a {@link ListEnabledProvidersResponse} with a list of providers.
|
||||
*
|
||||
* @throws NullPointerException If args are null.
|
||||
*/
|
||||
public static @NonNull ListEnabledProvidersResponse create(@NonNull List<String> providers) {
|
||||
Objects.requireNonNull(providers, "providers must not be null");
|
||||
Preconditions.checkCollectionElementsNotNull(providers, /* valueName= */ "providers");
|
||||
return new ListEnabledProvidersResponse(providers);
|
||||
}
|
||||
|
||||
private ListEnabledProvidersResponse(@NonNull List<String> providers) {
|
||||
mProviders = providers;
|
||||
}
|
||||
|
||||
private ListEnabledProvidersResponse(@NonNull Parcel in) {
|
||||
mProviders = in.createStringArrayList();
|
||||
}
|
||||
|
||||
public static final @NonNull Creator<ListEnabledProvidersResponse> CREATOR =
|
||||
new Creator<ListEnabledProvidersResponse>() {
|
||||
@Override
|
||||
public ListEnabledProvidersResponse createFromParcel(Parcel in) {
|
||||
return new ListEnabledProvidersResponse(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListEnabledProvidersResponse[] newArray(int size) {
|
||||
return new ListEnabledProvidersResponse[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeStringList(mProviders);
|
||||
}
|
||||
|
||||
/** Returns the list of flattened Credential Manager provider component names as strings. */
|
||||
public @NonNull List<String> getProviderComponentNames() {
|
||||
return mProviders;
|
||||
}
|
||||
}
|
||||
@@ -3863,6 +3863,11 @@
|
||||
<permission android:name="android.permission.REQUEST_UNIQUE_ID_ATTESTATION"
|
||||
android:protectionLevel="signature" />
|
||||
|
||||
<!-- Allows an application to get enabled credential manager providers.
|
||||
@hide -->
|
||||
<permission android:name="android.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS"
|
||||
android:protectionLevel="signature|privileged" />
|
||||
|
||||
<!-- ========================================= -->
|
||||
<!-- Permissions for special development tools -->
|
||||
<!-- ========================================= -->
|
||||
|
||||
@@ -31,6 +31,8 @@ import android.credentials.IClearCredentialStateCallback;
|
||||
import android.credentials.ICreateCredentialCallback;
|
||||
import android.credentials.ICredentialManager;
|
||||
import android.credentials.IGetCredentialCallback;
|
||||
import android.credentials.IListEnabledProvidersCallback;
|
||||
import android.credentials.ListEnabledProvidersResponse;
|
||||
import android.credentials.ISetEnabledProvidersCallback;
|
||||
import android.os.Binder;
|
||||
import android.os.CancellationSignal;
|
||||
@@ -55,20 +57,23 @@ import java.util.stream.Collectors;
|
||||
/**
|
||||
* Entry point service for credential management.
|
||||
*
|
||||
* <p>This service provides the {@link ICredentialManager} implementation and keeps a list of
|
||||
* {@link CredentialManagerServiceImpl} per user; the real work is done by
|
||||
* {@link CredentialManagerServiceImpl} itself.
|
||||
* <p>This service provides the {@link ICredentialManager} implementation and keeps a list of {@link
|
||||
* CredentialManagerServiceImpl} per user; the real work is done by {@link
|
||||
* CredentialManagerServiceImpl} itself.
|
||||
*/
|
||||
public final class CredentialManagerService extends
|
||||
AbstractMasterSystemService<CredentialManagerService, CredentialManagerServiceImpl> {
|
||||
public final class CredentialManagerService
|
||||
extends AbstractMasterSystemService<
|
||||
CredentialManagerService, CredentialManagerServiceImpl> {
|
||||
|
||||
private static final String TAG = "CredManSysService";
|
||||
|
||||
public CredentialManagerService(@NonNull Context context) {
|
||||
super(context,
|
||||
new SecureSettingsServiceNameResolver(context, Settings.Secure.CREDENTIAL_SERVICE,
|
||||
/*isMultipleMode=*/true),
|
||||
null, PACKAGE_UPDATE_POLICY_REFRESH_EAGER);
|
||||
super(
|
||||
context,
|
||||
new SecureSettingsServiceNameResolver(
|
||||
context, Settings.Secure.CREDENTIAL_SERVICE, /* isMultipleMode= */ true),
|
||||
null,
|
||||
PACKAGE_UPDATE_POLICY_REFRESH_EAGER);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -77,12 +82,14 @@ public final class CredentialManagerService extends
|
||||
}
|
||||
|
||||
@Override // from AbstractMasterSystemService
|
||||
protected CredentialManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId,
|
||||
boolean disabled) {
|
||||
protected CredentialManagerServiceImpl newServiceLocked(
|
||||
@UserIdInt int resolvedUserId, boolean disabled) {
|
||||
// This method should not be called for CredentialManagerService as it is configured to use
|
||||
// multiple services.
|
||||
Slog.w(TAG, "Should not be here - CredentialManagerService is configured to use "
|
||||
+ "multiple services");
|
||||
Slog.w(
|
||||
TAG,
|
||||
"Should not be here - CredentialManagerService is configured to use "
|
||||
+ "multiple services");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -92,8 +99,8 @@ public final class CredentialManagerService extends
|
||||
}
|
||||
|
||||
@Override // from AbstractMasterSystemService
|
||||
protected List<CredentialManagerServiceImpl> newServiceListLocked(int resolvedUserId,
|
||||
boolean disabled, String[] serviceNames) {
|
||||
protected List<CredentialManagerServiceImpl> newServiceListLocked(
|
||||
int resolvedUserId, boolean disabled, String[] serviceNames) {
|
||||
if (serviceNames == null || serviceNames.length == 0) {
|
||||
Slog.i(TAG, "serviceNames sent in newServiceListLocked is null, or empty");
|
||||
return new ArrayList<>();
|
||||
@@ -105,8 +112,8 @@ public final class CredentialManagerService extends
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
serviceList.add(new CredentialManagerServiceImpl(this, mLock, resolvedUserId,
|
||||
serviceName));
|
||||
serviceList.add(
|
||||
new CredentialManagerServiceImpl(this, mLock, resolvedUserId, serviceName));
|
||||
} catch (PackageManager.NameNotFoundException | SecurityException e) {
|
||||
Log.i(TAG, "Unable to add serviceInfo : " + e.getMessage());
|
||||
}
|
||||
@@ -130,19 +137,20 @@ public final class CredentialManagerService extends
|
||||
}
|
||||
}
|
||||
|
||||
private List<ProviderSession> initiateProviderSessions(RequestSession session,
|
||||
List<String> requestOptions) {
|
||||
private List<ProviderSession> initiateProviderSessions(
|
||||
RequestSession session, List<String> requestOptions) {
|
||||
List<ProviderSession> providerSessions = new ArrayList<>();
|
||||
// Invoke all services of a user to initiate a provider session
|
||||
runForUser((service) -> {
|
||||
if (service.isServiceCapable(requestOptions)) {
|
||||
ProviderSession providerSession = service
|
||||
.initiateProviderSessionForRequest(session);
|
||||
if (providerSession != null) {
|
||||
providerSessions.add(providerSession);
|
||||
}
|
||||
}
|
||||
});
|
||||
runForUser(
|
||||
(service) -> {
|
||||
if (service.isServiceCapable(requestOptions)) {
|
||||
ProviderSession providerSession =
|
||||
service.initiateProviderSessionForRequest(session);
|
||||
if (providerSession != null) {
|
||||
providerSessions.add(providerSession);
|
||||
}
|
||||
}
|
||||
});
|
||||
return providerSessions;
|
||||
}
|
||||
|
||||
@@ -157,25 +165,33 @@ public final class CredentialManagerService extends
|
||||
ICancellationSignal cancelTransport = CancellationSignal.createTransport();
|
||||
|
||||
// New request session, scoped for this request only.
|
||||
final GetRequestSession session = new GetRequestSession(getContext(),
|
||||
UserHandle.getCallingUserId(),
|
||||
callback,
|
||||
request,
|
||||
callingPackage);
|
||||
final GetRequestSession session =
|
||||
new GetRequestSession(
|
||||
getContext(),
|
||||
UserHandle.getCallingUserId(),
|
||||
callback,
|
||||
request,
|
||||
callingPackage);
|
||||
|
||||
// Initiate all provider sessions
|
||||
List<ProviderSession> providerSessions =
|
||||
initiateProviderSessions(session, request.getGetCredentialOptions()
|
||||
.stream().map(GetCredentialOption::getType)
|
||||
.collect(Collectors.toList()));
|
||||
initiateProviderSessions(
|
||||
session,
|
||||
request.getGetCredentialOptions().stream()
|
||||
.map(GetCredentialOption::getType)
|
||||
.collect(Collectors.toList()));
|
||||
// TODO : Return error when no providers available
|
||||
|
||||
// Iterate over all provider sessions and invoke the request
|
||||
providerSessions.forEach(providerGetSession -> {
|
||||
providerGetSession.getRemoteCredentialService().onBeginGetCredentials(
|
||||
(BeginGetCredentialsRequest) providerGetSession.getProviderRequest(),
|
||||
/*callback=*/providerGetSession);
|
||||
});
|
||||
providerSessions.forEach(
|
||||
providerGetSession -> {
|
||||
providerGetSession
|
||||
.getRemoteCredentialService()
|
||||
.onBeginGetCredentials(
|
||||
(BeginGetCredentialsRequest)
|
||||
providerGetSession.getProviderRequest(),
|
||||
/* callback= */ providerGetSession);
|
||||
});
|
||||
return cancelTransport;
|
||||
}
|
||||
|
||||
@@ -189,11 +205,13 @@ public final class CredentialManagerService extends
|
||||
ICancellationSignal cancelTransport = CancellationSignal.createTransport();
|
||||
|
||||
// New request session, scoped for this request only.
|
||||
final CreateRequestSession session = new CreateRequestSession(getContext(),
|
||||
UserHandle.getCallingUserId(),
|
||||
request,
|
||||
callback,
|
||||
callingPackage);
|
||||
final CreateRequestSession session =
|
||||
new CreateRequestSession(
|
||||
getContext(),
|
||||
UserHandle.getCallingUserId(),
|
||||
request,
|
||||
callback,
|
||||
callingPackage);
|
||||
|
||||
// Initiate all provider sessions
|
||||
List<ProviderSession> providerSessions =
|
||||
@@ -201,12 +219,38 @@ public final class CredentialManagerService extends
|
||||
// TODO : Return error when no providers available
|
||||
|
||||
// Iterate over all provider sessions and invoke the request
|
||||
providerSessions.forEach(providerCreateSession -> {
|
||||
providerCreateSession.getRemoteCredentialService().onCreateCredential(
|
||||
(BeginCreateCredentialRequest)
|
||||
providerCreateSession.getProviderRequest(),
|
||||
/*callback=*/providerCreateSession);
|
||||
});
|
||||
providerSessions.forEach(
|
||||
providerCreateSession -> {
|
||||
providerCreateSession
|
||||
.getRemoteCredentialService()
|
||||
.onCreateCredential(
|
||||
(BeginCreateCredentialRequest)
|
||||
providerCreateSession.getProviderRequest(),
|
||||
/* callback= */ providerCreateSession);
|
||||
});
|
||||
return cancelTransport;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICancellationSignal listEnabledProviders(IListEnabledProvidersCallback callback) {
|
||||
Log.i(TAG, "listEnabledProviders");
|
||||
ICancellationSignal cancelTransport = CancellationSignal.createTransport();
|
||||
|
||||
List<String> enabledProviders = new ArrayList<>();
|
||||
runForUser(
|
||||
(service) -> {
|
||||
enabledProviders.add(
|
||||
service.getServiceInfo().getComponentName().flattenToString());
|
||||
});
|
||||
|
||||
// Call the callback.
|
||||
try {
|
||||
callback.onResponse(ListEnabledProvidersResponse.create(enabledProviders));
|
||||
} catch (RemoteException e) {
|
||||
Log.i(TAG, "Issue with invoking response: " + e.getMessage());
|
||||
// TODO: Propagate failure
|
||||
}
|
||||
|
||||
return cancelTransport;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user