summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Becca Hughes <beccahughes@google.com> 2022-12-01 17:14:45 +0000
committer Becca Hughes <beccahughes@google.com> 2022-12-12 23:23:31 +0000
commit40b6bceb91134bd914d95d0fc42d805dfe151a89 (patch)
tree1c1cde02b9c4d4628927f5f204388000a02a76f8
parentc5deccb2c8571e619e94c7e9a9e497902fca2659 (diff)
Add ListEnabledProviders API method for Credential Manager
Adds ListEnabledProviders API which is a SystemApi used by Settings and Chrome to list the enabled and user visible installed providers. Test: make Bug: 253157179 CTS-Coverage-Bug: 247549381 Change-Id: I25ef3a294f051855c0a4ae104d1111f1e8b8529d
-rw-r--r--core/java/android/credentials/CredentialManager.java69
-rw-r--r--core/java/android/credentials/ICredentialManager.aidl3
-rw-r--r--core/java/android/credentials/IListEnabledProvidersCallback.aidl29
-rw-r--r--core/java/android/credentials/ListEnabledProvidersException.java74
-rw-r--r--core/java/android/credentials/ListEnabledProvidersResponse.aidl19
-rw-r--r--core/java/android/credentials/ListEnabledProvidersResponse.java85
-rw-r--r--core/res/AndroidManifest.xml5
-rw-r--r--services/credentials/java/com/android/server/credentials/CredentialManagerService.java148
8 files changed, 380 insertions, 52 deletions
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index a578956fedd5..49e849523de2 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -197,6 +197,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.
diff --git a/core/java/android/credentials/ICredentialManager.aidl b/core/java/android/credentials/ICredentialManager.aidl
index d8c4d894a439..c3ca03dcdfd2 100644
--- a/core/java/android/credentials/ICredentialManager.aidl
+++ b/core/java/android/credentials/ICredentialManager.aidl
@@ -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);
}
diff --git a/core/java/android/credentials/IListEnabledProvidersCallback.aidl b/core/java/android/credentials/IListEnabledProvidersCallback.aidl
new file mode 100644
index 000000000000..3a8e25ed954a
--- /dev/null
+++ b/core/java/android/credentials/IListEnabledProvidersCallback.aidl
@@ -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);
+} \ No newline at end of file
diff --git a/core/java/android/credentials/ListEnabledProvidersException.java b/core/java/android/credentials/ListEnabledProvidersException.java
new file mode 100644
index 000000000000..c12c65672664
--- /dev/null
+++ b/core/java/android/credentials/ListEnabledProvidersException.java
@@ -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);
+ }
+}
diff --git a/core/java/android/credentials/ListEnabledProvidersResponse.aidl b/core/java/android/credentials/ListEnabledProvidersResponse.aidl
new file mode 100644
index 000000000000..759bf48bf57a
--- /dev/null
+++ b/core/java/android/credentials/ListEnabledProvidersResponse.aidl
@@ -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; \ No newline at end of file
diff --git a/core/java/android/credentials/ListEnabledProvidersResponse.java b/core/java/android/credentials/ListEnabledProvidersResponse.java
new file mode 100644
index 000000000000..532adf7f0c50
--- /dev/null
+++ b/core/java/android/credentials/ListEnabledProvidersResponse.java
@@ -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;
+ }
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 09181cebd3f2..585368678357 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -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 -->
<!-- ========================================= -->
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index af4e7d72e275..a92e4a18d609 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -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;
}