From 463a6e0431bf1556fd3e087b11fd40d3a158c136 Mon Sep 17 00:00:00 2001 From: Becca Hughes Date: Thu, 5 Jan 2023 22:45:34 +0000 Subject: [PATCH] Send intent for UI when providers are updated Adds an intent that can be consumed by the UI app with the list of current providers as an extra. The intent is limited to the UI app component so this won't be picked up by other apps. Test: make & run on device Bug: 253157366 Change-Id: I01fe9286eeb37f076af0fd0bf0d59659d92f5bab --- .../android/credentials/ui/Constants.java | 9 +- .../android/credentials/ui/IntentFactory.java | 45 ++++++--- .../credentials/CredentialManagerService.java | 97 +++++++++++-------- 3 files changed, 97 insertions(+), 54 deletions(-) diff --git a/core/java/android/credentials/ui/Constants.java b/core/java/android/credentials/ui/Constants.java index 53ad40df2252d..7092f291eea5e 100644 --- a/core/java/android/credentials/ui/Constants.java +++ b/core/java/android/credentials/ui/Constants.java @@ -24,9 +24,12 @@ package android.credentials.ui; public class Constants { /** - * The intent extra key for the {@code ResultReceiver} object when launching the UX - * activities. - */ + * The intent extra key for the {@code ResultReceiver} object when launching the UX activities. + */ public static final String EXTRA_RESULT_RECEIVER = "android.credentials.ui.extra.RESULT_RECEIVER"; + + /** The intent action for when the enabled Credential Manager providers has been updated. */ + public static final String CREDMAN_ENABLED_PROVIDERS_UPDATED = + "android.credentials.ui.action.CREDMAN_ENABLED_PROVIDERS_UPDATED"; } diff --git a/core/java/android/credentials/ui/IntentFactory.java b/core/java/android/credentials/ui/IntentFactory.java index 83ebe1c74ee52..67634dc0ad8c8 100644 --- a/core/java/android/credentials/ui/IntentFactory.java +++ b/core/java/android/credentials/ui/IntentFactory.java @@ -39,14 +39,19 @@ public class IntentFactory { public static Intent createCredentialSelectorIntent( @NonNull RequestInfo requestInfo, @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling. - @NonNull ArrayList enabledProviderDataList, + @NonNull + ArrayList enabledProviderDataList, @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling. - @NonNull ArrayList disabledProviderDataList, + @NonNull + ArrayList disabledProviderDataList, @NonNull ResultReceiver resultReceiver) { Intent intent = new Intent(); - ComponentName componentName = ComponentName.unflattenFromString( - Resources.getSystem().getString( - com.android.internal.R.string.config_credentialManagerDialogComponent)); + ComponentName componentName = + ComponentName.unflattenFromString( + Resources.getSystem() + .getString( + com.android.internal.R.string + .config_credentialManagerDialogComponent)); intent.setComponent(componentName); intent.putParcelableArrayListExtra( @@ -54,17 +59,35 @@ public class IntentFactory { intent.putParcelableArrayListExtra( ProviderData.EXTRA_DISABLED_PROVIDER_DATA_LIST, disabledProviderDataList); intent.putExtra(RequestInfo.EXTRA_REQUEST_INFO, requestInfo); - intent.putExtra(Constants.EXTRA_RESULT_RECEIVER, - toIpcFriendlyResultReceiver(resultReceiver)); + intent.putExtra( + Constants.EXTRA_RESULT_RECEIVER, toIpcFriendlyResultReceiver(resultReceiver)); return intent; } /** - * Convert an instance of a "locally-defined" ResultReceiver to an instance of - * {@link android.os.ResultReceiver} itself, which the receiving process will be able to - * unmarshall. - */ + * Notify the UI that providers have been enabled/disabled. + * + * @hide + */ + @NonNull + public static Intent createProviderUpdateIntent() { + Intent intent = new Intent(); + ComponentName componentName = + ComponentName.unflattenFromString( + Resources.getSystem() + .getString( + com.android.internal.R.string + .config_credentialManagerDialogComponent)); + intent.setComponent(componentName); + intent.setAction(Constants.CREDMAN_ENABLED_PROVIDERS_UPDATED); + return intent; + } + + /** + * Convert an instance of a "locally-defined" ResultReceiver to an instance of {@link + * android.os.ResultReceiver} itself, which the receiving process will be able to unmarshall. + */ private static ResultReceiver toIpcFriendlyResultReceiver( T resultReceiver) { final Parcel parcel = Parcel.obtain(); diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java index 9b2d876441238..a30e133439d7e 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java @@ -34,6 +34,7 @@ import android.credentials.IGetCredentialCallback; import android.credentials.IListEnabledProvidersCallback; import android.credentials.ISetEnabledProvidersCallback; import android.credentials.ListEnabledProvidersResponse; +import android.credentials.ui.IntentFactory; import android.os.Binder; import android.os.CancellationSignal; import android.os.ICancellationSignal; @@ -112,9 +113,8 @@ public final class CredentialManagerService 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()); } @@ -144,15 +144,17 @@ public final class CredentialManagerService RequestSession session, List requestOptions) { List providerSessions = new ArrayList<>(); // Invoke all services of a user to initiate a provider session - runForUser((service) -> { - synchronized (mLock) { - ProviderSession providerSession = service - .initiateProviderSessionForRequestLocked(session, requestOptions); - if (providerSession != null) { - providerSessions.add(providerSession); - } - } - }); + runForUser( + (service) -> { + synchronized (mLock) { + ProviderSession providerSession = + service.initiateProviderSessionForRequestLocked( + session, requestOptions); + if (providerSession != null) { + providerSessions.add(providerSession); + } + } + }); return providerSessions; } @@ -177,27 +179,35 @@ public final class CredentialManagerService // Initiate all provider sessions List providerSessions = - initiateProviderSessions(session, request.getGetCredentialOptions() - .stream().map(GetCredentialOption::getType) - .collect(Collectors.toList())); + initiateProviderSessions( + session, + request.getGetCredentialOptions().stream() + .map(GetCredentialOption::getType) + .collect(Collectors.toList())); if (providerSessions.isEmpty()) { try { // TODO("Replace with properly defined error type") - callback.onError("unknown_type", - "No providers available to fulfill request."); + callback.onError("unknown_type", "No providers available to fulfill request."); } catch (RemoteException e) { - Log.i(TAG, "Issue invoking onError on IGetCredentialCallback " - + "callback: " + e.getMessage()); + Log.i( + TAG, + "Issue invoking onError on IGetCredentialCallback " + + "callback: " + + e.getMessage()); } } // Iterate over all provider sessions and invoke the request - providerSessions.forEach(providerGetSession -> { - providerGetSession.getRemoteCredentialService().onBeginGetCredential( - (BeginGetCredentialRequest) providerGetSession.getProviderRequest(), - /*callback=*/providerGetSession); - }); + providerSessions.forEach( + providerGetSession -> { + providerGetSession + .getRemoteCredentialService() + .onBeginGetCredential( + (BeginGetCredentialRequest) + providerGetSession.getProviderRequest(), + /* callback= */ providerGetSession); + }); return cancelTransport; } @@ -226,11 +236,13 @@ public final class CredentialManagerService if (providerSessions.isEmpty()) { try { // TODO("Replace with properly defined error type") - callback.onError("unknown_type", - "No providers available to fulfill request."); + callback.onError("unknown_type", "No providers available to fulfill request."); } catch (RemoteException e) { - Log.i(TAG, "Issue invoking onError on ICreateCredentialCallback " - + "callback: " + e.getMessage()); + Log.i( + TAG, + "Issue invoking onError on ICreateCredentialCallback " + + "callback: " + + e.getMessage()); } } @@ -247,7 +259,7 @@ public final class CredentialManagerService return cancelTransport; } - @SuppressWarnings("GuardedBy") // ErrorProne requires listEnabledProviders + @SuppressWarnings("GuardedBy") // ErrorProne requires listEnabledProviders // to be guarded by 'service.mLock', which is the same as mLock. @Override public ICancellationSignal listEnabledProviders(IListEnabledProvidersCallback callback) { @@ -257,8 +269,7 @@ public final class CredentialManagerService List enabledProviders = new ArrayList<>(); runForUser( (service) -> { - enabledProviders.add( - service.getComponentName().flattenToString()); + enabledProviders.add(service.getComponentName().flattenToString()); }); // Call the callback. @@ -300,7 +311,7 @@ public final class CredentialManagerService "Failed to store setting containing enabled providers"); } catch (RemoteException e) { Log.i(TAG, "Issue with invoking error response: " + e.getMessage()); - // TODO: Propagate failure + return; } } @@ -311,11 +322,16 @@ public final class CredentialManagerService Log.i(TAG, "Issue with invoking response: " + e.getMessage()); // TODO: Propagate failure } + + // Send an intent to the UI that we have new enabled providers. + getContext().sendBroadcast(IntentFactory.createProviderUpdateIntent()); } @Override - public ICancellationSignal clearCredentialState(ClearCredentialStateRequest request, - IClearCredentialStateCallback callback, String callingPackage) { + public ICancellationSignal clearCredentialState( + ClearCredentialStateRequest request, + IClearCredentialStateCallback callback, + String callingPackage) { Log.i(TAG, "starting clearCredentialState with callingPackage: " + callingPackage); // TODO : Implement cancellation ICancellationSignal cancelTransport = CancellationSignal.createTransport(); @@ -331,17 +347,18 @@ public final class CredentialManagerService // Initiate all provider sessions // TODO: Determine if provider needs to have clear capability in their manifest - List providerSessions = - initiateProviderSessions(session, List.of()); + List providerSessions = initiateProviderSessions(session, List.of()); if (providerSessions.isEmpty()) { try { // TODO("Replace with properly defined error type") - callback.onError("unknown_type", - "No providers available to fulfill request."); + callback.onError("unknown_type", "No providers available to fulfill request."); } catch (RemoteException e) { - Log.i(TAG, "Issue invoking onError on IClearCredentialStateCallback " - + "callback: " + e.getMessage()); + Log.i( + TAG, + "Issue invoking onError on IClearCredentialStateCallback " + + "callback: " + + e.getMessage()); } }