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()); } }