Loading services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java +89 −22 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.credentials; import android.Manifest; import android.annotation.Nullable; import android.app.PendingIntent; import android.content.ComponentName; Loading @@ -28,17 +29,20 @@ import android.credentials.GetCredentialResponse; import android.credentials.IGetCredentialCallback; import android.credentials.IPrepareGetCredentialCallback; import android.credentials.PrepareGetCredentialResponseInternal; import android.credentials.ui.GetCredentialProviderData; import android.credentials.ui.ProviderData; import android.credentials.ui.RequestInfo; import android.os.CancellationSignal; import android.os.RemoteException; import android.service.credentials.CallingAppInfo; import android.service.credentials.PermissionUtils; import android.util.Log; import com.android.server.credentials.metrics.ApiName; import com.android.server.credentials.metrics.ProviderStatusForMetrics; import java.util.ArrayList; import java.util.Set; import java.util.stream.Collectors; /** Loading Loading @@ -222,38 +226,101 @@ public class PrepareGetRequestSession extends RequestSession<GetCredentialReques // If all provider responses have been received, we can either need the UI, // or we need to respond with error. The only other case is the entry being // selected after the UI has been invoked which has a separate code path. if (isUiInvocationNeeded()) { if (mIsInitialQuery) { // First time in this state. UI shouldn't be invoked because developer wants to // punt it for later boolean hasQueryCandidatePermission = PermissionUtils.hasPermission( mContext, mClientAppInfo.getPackageName(), Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS); if (isUiInvocationNeeded()) { ArrayList<ProviderData> providerData = getProviderDataForUi(); if (!providerData.isEmpty()) { constructPendingResponseAndInvokeCallback(hasQueryCandidatePermission, getCredentialResultTypes(hasQueryCandidatePermission), hasAuthenticationResults(providerData, hasQueryCandidatePermission), hasRemoteResults(providerData, hasQueryCandidatePermission), getUiIntent()); } else { constructEmptyPendingResponseAndInvokeCallback(hasQueryCandidatePermission); } } else { constructEmptyPendingResponseAndInvokeCallback(hasQueryCandidatePermission); } mIsInitialQuery = false; } else { // Not the first time. This could be a result of a user selection leading to a UI // invocation again. if (isUiInvocationNeeded()) { getProviderDataAndInitiateUi(); } else { respondToClientWithErrorAndFinish(GetCredentialException.TYPE_NO_CREDENTIAL, "No credentials available"); } } } } private void constructPendingResponseAndInvokeCallback(boolean hasPermission, Set<String> credentialTypes, boolean hasAuthenticationResults, boolean hasRemoteResults, PendingIntent uiIntent) { try { mPrepareGetCredentialCallback.onResponse( new PrepareGetCredentialResponseInternal( false, null, false, false, getUiIntent())); } catch (Exception e) { hasPermission, credentialTypes, hasAuthenticationResults, hasRemoteResults, uiIntent)); } catch (RemoteException e) { Log.e(TAG, "EXCEPTION while mPendingCallback.onResponse", e); } mIsInitialQuery = false; } else { getProviderDataAndInitiateUi(); } } else { if (mIsInitialQuery) { private void constructEmptyPendingResponseAndInvokeCallback( boolean hasQueryCandidatePermission) { try { mPrepareGetCredentialCallback.onResponse( new PrepareGetCredentialResponseInternal( false, null, false, false, null)); } catch (Exception e) { hasQueryCandidatePermission, /*credentialResultTypes=*/ null, /*hasAuthenticationResults=*/false, /*hasRemoteResults=*/ false, /*pendingIntent=*/ null)); } catch (RemoteException e) { Log.e(TAG, "EXCEPTION while mPendingCallback.onResponse", e); } mIsInitialQuery = false; // TODO(273308895): should also clear session here } else { respondToClientWithErrorAndFinish(GetCredentialException.TYPE_NO_CREDENTIAL, "No credentials available"); } private boolean hasRemoteResults(ArrayList<ProviderData> providerData, boolean hasQueryCandidatePermission) { if (!hasQueryCandidatePermission) { return false; } return providerData.stream() .map(data -> (GetCredentialProviderData) data) .anyMatch(getCredentialProviderData -> getCredentialProviderData.getRemoteEntry() != null); } private boolean hasAuthenticationResults(ArrayList<ProviderData> providerData, boolean hasQueryCandidatePermission) { if (!hasQueryCandidatePermission) { return false; } return providerData.stream() .map(data -> (GetCredentialProviderData) data) .anyMatch(getCredentialProviderData -> !getCredentialProviderData.getAuthenticationEntries().isEmpty()); } @Nullable private Set<String> getCredentialResultTypes(boolean hasQueryCandidatePermission) { if (!hasQueryCandidatePermission) { return null; } return mProviders.values().stream() .map(session -> (ProviderGetSession) session) .flatMap(providerGetSession -> providerGetSession .getCredentialEntryTypes().stream()) .collect(Collectors.toSet()); } private PendingIntent getUiIntent() { Loading services/credentials/java/com/android/server/credentials/ProviderGetSession.java +16 −0 Original line number Diff line number Diff line Loading @@ -47,9 +47,11 @@ import com.android.server.credentials.metrics.EntryEnum; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; /** Loading Loading @@ -338,6 +340,11 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential } } @NonNull protected Set<String> getCredentialEntryTypes() { return mProviderResponseDataHandler.getCredentialEntryTypes(); } @Override // Call from request session to data to be shown on the UI @Nullable protected GetCredentialProviderData prepareUiData() throws IllegalArgumentException { Loading Loading @@ -575,6 +582,9 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential private final Map<String, Pair<Action, AuthenticationEntry>> mUiAuthenticationEntries = new HashMap<>(); @NonNull private final Set<String> mCredentialEntryTypes = new HashSet<>(); @Nullable private Pair<String, Pair<RemoteEntry, Entry>> mUiRemoteEntry = null; Loading Loading @@ -607,6 +617,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential id, credentialEntry.getSlice(), setUpFillInIntent(credentialEntry.getBeginGetCredentialOptionId())); mUiCredentialEntries.put(id, new Pair<>(credentialEntry, entry)); mCredentialEntryTypes.add(credentialEntry.getType()); } public void addAction(Action action) { Loading Loading @@ -703,6 +714,11 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential && response.getRemoteCredentialEntry() == null; } @NonNull public Set<String> getCredentialEntryTypes() { return mCredentialEntryTypes; } @Nullable public Action getAuthenticationAction(String entryKey) { return mUiAuthenticationEntries.get(entryKey) == null ? null : Loading services/credentials/java/com/android/server/credentials/RequestSession.java +13 −7 Original line number Diff line number Diff line Loading @@ -236,16 +236,26 @@ abstract class RequestSession<T, U> implements CredentialManagerUi.CredentialMan } void getProviderDataAndInitiateUi() { ArrayList<ProviderData> providerDataList = getProviderDataForUi(); if (!providerDataList.isEmpty()) { Log.i(TAG, "provider list not empty about to initiate ui"); MetricUtilities.logApiCalled(mProviders, ++mSequenceCounter); launchUiWithProviderData(providerDataList); } } @NonNull protected ArrayList<ProviderData> getProviderDataForUi() { Log.i(TAG, "In getProviderDataAndInitiateUi"); Log.i(TAG, "In getProviderDataAndInitiateUi providers size: " + mProviders.size()); ArrayList<ProviderData> providerDataList = new ArrayList<>(); if (isSessionCancelled()) { MetricUtilities.logApiCalled(mProviders, ++mSequenceCounter); finishSession(/*propagateCancellation=*/true); return; return providerDataList; } ArrayList<ProviderData> providerDataList = new ArrayList<>(); for (ProviderSession session : mProviders.values()) { Log.i(TAG, "preparing data for : " + session.getComponentName()); ProviderData providerData = session.prepareUiData(); Loading @@ -254,11 +264,7 @@ abstract class RequestSession<T, U> implements CredentialManagerUi.CredentialMan providerDataList.add(providerData); } } if (!providerDataList.isEmpty()) { Log.i(TAG, "provider list not empty about to initiate ui"); MetricUtilities.logApiCalled(mProviders, ++mSequenceCounter); launchUiWithProviderData(providerDataList); } return providerDataList; } protected void collectFinalPhaseMetricStatus(boolean hasException, Loading Loading
services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java +89 −22 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.credentials; import android.Manifest; import android.annotation.Nullable; import android.app.PendingIntent; import android.content.ComponentName; Loading @@ -28,17 +29,20 @@ import android.credentials.GetCredentialResponse; import android.credentials.IGetCredentialCallback; import android.credentials.IPrepareGetCredentialCallback; import android.credentials.PrepareGetCredentialResponseInternal; import android.credentials.ui.GetCredentialProviderData; import android.credentials.ui.ProviderData; import android.credentials.ui.RequestInfo; import android.os.CancellationSignal; import android.os.RemoteException; import android.service.credentials.CallingAppInfo; import android.service.credentials.PermissionUtils; import android.util.Log; import com.android.server.credentials.metrics.ApiName; import com.android.server.credentials.metrics.ProviderStatusForMetrics; import java.util.ArrayList; import java.util.Set; import java.util.stream.Collectors; /** Loading Loading @@ -222,38 +226,101 @@ public class PrepareGetRequestSession extends RequestSession<GetCredentialReques // If all provider responses have been received, we can either need the UI, // or we need to respond with error. The only other case is the entry being // selected after the UI has been invoked which has a separate code path. if (isUiInvocationNeeded()) { if (mIsInitialQuery) { // First time in this state. UI shouldn't be invoked because developer wants to // punt it for later boolean hasQueryCandidatePermission = PermissionUtils.hasPermission( mContext, mClientAppInfo.getPackageName(), Manifest.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS); if (isUiInvocationNeeded()) { ArrayList<ProviderData> providerData = getProviderDataForUi(); if (!providerData.isEmpty()) { constructPendingResponseAndInvokeCallback(hasQueryCandidatePermission, getCredentialResultTypes(hasQueryCandidatePermission), hasAuthenticationResults(providerData, hasQueryCandidatePermission), hasRemoteResults(providerData, hasQueryCandidatePermission), getUiIntent()); } else { constructEmptyPendingResponseAndInvokeCallback(hasQueryCandidatePermission); } } else { constructEmptyPendingResponseAndInvokeCallback(hasQueryCandidatePermission); } mIsInitialQuery = false; } else { // Not the first time. This could be a result of a user selection leading to a UI // invocation again. if (isUiInvocationNeeded()) { getProviderDataAndInitiateUi(); } else { respondToClientWithErrorAndFinish(GetCredentialException.TYPE_NO_CREDENTIAL, "No credentials available"); } } } } private void constructPendingResponseAndInvokeCallback(boolean hasPermission, Set<String> credentialTypes, boolean hasAuthenticationResults, boolean hasRemoteResults, PendingIntent uiIntent) { try { mPrepareGetCredentialCallback.onResponse( new PrepareGetCredentialResponseInternal( false, null, false, false, getUiIntent())); } catch (Exception e) { hasPermission, credentialTypes, hasAuthenticationResults, hasRemoteResults, uiIntent)); } catch (RemoteException e) { Log.e(TAG, "EXCEPTION while mPendingCallback.onResponse", e); } mIsInitialQuery = false; } else { getProviderDataAndInitiateUi(); } } else { if (mIsInitialQuery) { private void constructEmptyPendingResponseAndInvokeCallback( boolean hasQueryCandidatePermission) { try { mPrepareGetCredentialCallback.onResponse( new PrepareGetCredentialResponseInternal( false, null, false, false, null)); } catch (Exception e) { hasQueryCandidatePermission, /*credentialResultTypes=*/ null, /*hasAuthenticationResults=*/false, /*hasRemoteResults=*/ false, /*pendingIntent=*/ null)); } catch (RemoteException e) { Log.e(TAG, "EXCEPTION while mPendingCallback.onResponse", e); } mIsInitialQuery = false; // TODO(273308895): should also clear session here } else { respondToClientWithErrorAndFinish(GetCredentialException.TYPE_NO_CREDENTIAL, "No credentials available"); } private boolean hasRemoteResults(ArrayList<ProviderData> providerData, boolean hasQueryCandidatePermission) { if (!hasQueryCandidatePermission) { return false; } return providerData.stream() .map(data -> (GetCredentialProviderData) data) .anyMatch(getCredentialProviderData -> getCredentialProviderData.getRemoteEntry() != null); } private boolean hasAuthenticationResults(ArrayList<ProviderData> providerData, boolean hasQueryCandidatePermission) { if (!hasQueryCandidatePermission) { return false; } return providerData.stream() .map(data -> (GetCredentialProviderData) data) .anyMatch(getCredentialProviderData -> !getCredentialProviderData.getAuthenticationEntries().isEmpty()); } @Nullable private Set<String> getCredentialResultTypes(boolean hasQueryCandidatePermission) { if (!hasQueryCandidatePermission) { return null; } return mProviders.values().stream() .map(session -> (ProviderGetSession) session) .flatMap(providerGetSession -> providerGetSession .getCredentialEntryTypes().stream()) .collect(Collectors.toSet()); } private PendingIntent getUiIntent() { Loading
services/credentials/java/com/android/server/credentials/ProviderGetSession.java +16 −0 Original line number Diff line number Diff line Loading @@ -47,9 +47,11 @@ import com.android.server.credentials.metrics.EntryEnum; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; /** Loading Loading @@ -338,6 +340,11 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential } } @NonNull protected Set<String> getCredentialEntryTypes() { return mProviderResponseDataHandler.getCredentialEntryTypes(); } @Override // Call from request session to data to be shown on the UI @Nullable protected GetCredentialProviderData prepareUiData() throws IllegalArgumentException { Loading Loading @@ -575,6 +582,9 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential private final Map<String, Pair<Action, AuthenticationEntry>> mUiAuthenticationEntries = new HashMap<>(); @NonNull private final Set<String> mCredentialEntryTypes = new HashSet<>(); @Nullable private Pair<String, Pair<RemoteEntry, Entry>> mUiRemoteEntry = null; Loading Loading @@ -607,6 +617,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential id, credentialEntry.getSlice(), setUpFillInIntent(credentialEntry.getBeginGetCredentialOptionId())); mUiCredentialEntries.put(id, new Pair<>(credentialEntry, entry)); mCredentialEntryTypes.add(credentialEntry.getType()); } public void addAction(Action action) { Loading Loading @@ -703,6 +714,11 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential && response.getRemoteCredentialEntry() == null; } @NonNull public Set<String> getCredentialEntryTypes() { return mCredentialEntryTypes; } @Nullable public Action getAuthenticationAction(String entryKey) { return mUiAuthenticationEntries.get(entryKey) == null ? null : Loading
services/credentials/java/com/android/server/credentials/RequestSession.java +13 −7 Original line number Diff line number Diff line Loading @@ -236,16 +236,26 @@ abstract class RequestSession<T, U> implements CredentialManagerUi.CredentialMan } void getProviderDataAndInitiateUi() { ArrayList<ProviderData> providerDataList = getProviderDataForUi(); if (!providerDataList.isEmpty()) { Log.i(TAG, "provider list not empty about to initiate ui"); MetricUtilities.logApiCalled(mProviders, ++mSequenceCounter); launchUiWithProviderData(providerDataList); } } @NonNull protected ArrayList<ProviderData> getProviderDataForUi() { Log.i(TAG, "In getProviderDataAndInitiateUi"); Log.i(TAG, "In getProviderDataAndInitiateUi providers size: " + mProviders.size()); ArrayList<ProviderData> providerDataList = new ArrayList<>(); if (isSessionCancelled()) { MetricUtilities.logApiCalled(mProviders, ++mSequenceCounter); finishSession(/*propagateCancellation=*/true); return; return providerDataList; } ArrayList<ProviderData> providerDataList = new ArrayList<>(); for (ProviderSession session : mProviders.values()) { Log.i(TAG, "preparing data for : " + session.getComponentName()); ProviderData providerData = session.prepareUiData(); Loading @@ -254,11 +264,7 @@ abstract class RequestSession<T, U> implements CredentialManagerUi.CredentialMan providerDataList.add(providerData); } } if (!providerDataList.isEmpty()) { Log.i(TAG, "provider list not empty about to initiate ui"); MetricUtilities.logApiCalled(mProviders, ++mSequenceCounter); launchUiWithProviderData(providerDataList); } return providerDataList; } protected void collectFinalPhaseMetricStatus(boolean hasException, Loading