1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.credentials;
18 
19 import android.Manifest;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.pm.ApplicationInfo;
25 import android.content.pm.PackageManager;
26 import android.credentials.Credential;
27 import android.credentials.CredentialProviderInfo;
28 import android.credentials.ui.ProviderData;
29 import android.credentials.ui.ProviderPendingIntentResponse;
30 import android.os.ICancellationSignal;
31 import android.os.RemoteException;
32 import android.util.Slog;
33 
34 import com.android.server.credentials.metrics.ProviderSessionMetric;
35 
36 import java.util.UUID;
37 
38 /**
39  * Provider session storing the state of provider response and ui entries.
40  *
41  * @param <T> The request to be sent to the provider
42  * @param <R> The response to be expected from the provider
43  */
44 public abstract class ProviderSession<T, R>
45         implements RemoteCredentialService.ProviderCallbacks<R> {
46 
47     private static final String TAG = "ProviderSession";
48 
49     @NonNull
50     protected final Context mContext;
51     @NonNull
52     protected final ComponentName mComponentName;
53     @Nullable
54     protected final CredentialProviderInfo mProviderInfo;
55     @Nullable
56     protected final RemoteCredentialService mRemoteCredentialService;
57     @NonNull
58     protected final int mUserId;
59     @NonNull
60     protected Status mStatus = Status.NOT_STARTED;
61     @Nullable
62     protected final ProviderInternalCallback mCallbacks;
63     @Nullable
64     protected Credential mFinalCredentialResponse;
65     @Nullable
66     protected ICancellationSignal mProviderCancellationSignal;
67     @NonNull
68     protected final T mProviderRequest;
69     @Nullable
70     protected R mProviderResponse;
71     @NonNull
72     protected Boolean mProviderResponseSet = false;
73     @NonNull
74     protected final ProviderSessionMetric mProviderSessionMetric;
75     @NonNull
76     private int mProviderSessionUid;
77 
78     enum CredentialsSource {
79         REMOTE_PROVIDER,
80         REGISTRY,
81         AUTH_ENTRY
82     }
83 
84     /**
85      * Returns true if the given status reflects that the provider state is ready to be shown
86      * on the credMan UI.
87      */
isUiInvokingStatus(Status status)88     public static boolean isUiInvokingStatus(Status status) {
89         return status == Status.CREDENTIALS_RECEIVED || status == Status.SAVE_ENTRIES_RECEIVED
90                 || status == Status.NO_CREDENTIALS_FROM_AUTH_ENTRY;
91     }
92 
93     /**
94      * Returns true if the given status reflects that the provider is waiting for a remote
95      * response.
96      */
isStatusWaitingForRemoteResponse(Status status)97     public static boolean isStatusWaitingForRemoteResponse(Status status) {
98         return status == Status.PENDING;
99     }
100 
101     /**
102      * Returns true if the given status means that the provider session must be terminated.
103      */
isTerminatingStatus(Status status)104     public static boolean isTerminatingStatus(Status status) {
105         return status == Status.CANCELED || status == Status.SERVICE_DEAD;
106     }
107 
108     /**
109      * Returns true if the given status reflects that the provider is done getting the response,
110      * and is ready to return the final credential back to the user.
111      */
isCompletionStatus(Status status)112     public static boolean isCompletionStatus(Status status) {
113         return status == Status.COMPLETE || status == Status.EMPTY_RESPONSE;
114     }
115 
116     /**
117      * Gives access to the objects metric collectors.
118      */
getProviderSessionMetric()119     public ProviderSessionMetric getProviderSessionMetric() {
120         return this.mProviderSessionMetric;
121     }
122 
123     /**
124      * Interface to be implemented by any class that wishes to get a callback when a particular
125      * provider session's status changes. Typically, implemented by the {@link RequestSession}
126      * class.
127      *
128      * @param <V> the type of the final response expected
129      */
130     public interface ProviderInternalCallback<V> {
131         /** Called when status changes. */
onProviderStatusChanged(Status status, ComponentName componentName, CredentialsSource source)132         void onProviderStatusChanged(Status status, ComponentName componentName,
133                 CredentialsSource source);
134 
135         /** Called when the final credential is received through an entry selection. */
onFinalResponseReceived(ComponentName componentName, V response)136         void onFinalResponseReceived(ComponentName componentName, V response);
137 
138         /** Called when an error is received through an entry selection. */
onFinalErrorReceived(ComponentName componentName, String errorType, @Nullable String message)139         void onFinalErrorReceived(ComponentName componentName, String errorType,
140                 @Nullable String message);
141     }
142 
ProviderSession(@onNull Context context, @NonNull T providerRequest, @Nullable ProviderInternalCallback callbacks, @NonNull ComponentName componentName, @NonNull int userId, @Nullable RemoteCredentialService remoteCredentialService)143     protected ProviderSession(@NonNull Context context,
144             @NonNull T providerRequest,
145             @Nullable ProviderInternalCallback callbacks,
146             @NonNull ComponentName componentName,
147             @NonNull int userId,
148             @Nullable RemoteCredentialService remoteCredentialService) {
149         mContext = context;
150         mProviderInfo = null;
151         mProviderRequest = providerRequest;
152         mCallbacks = callbacks;
153         mUserId = userId;
154         mComponentName = componentName;
155         mRemoteCredentialService = remoteCredentialService;
156         mProviderSessionUid = MetricUtilities.getPackageUid(mContext, mComponentName);
157         mProviderSessionMetric = new ProviderSessionMetric(
158                 ((RequestSession) mCallbacks).mRequestSessionMetric.getSessionIdTrackTwo());
159     }
160 
161     /** Provider status at various states of the provider session. */
162     enum Status {
163         NOT_STARTED,
164         PENDING,
165         CREDENTIALS_RECEIVED,
166         SERVICE_DEAD,
167         SAVE_ENTRIES_RECEIVED,
168         CANCELED,
169         EMPTY_RESPONSE,
170         NO_CREDENTIALS_FROM_AUTH_ENTRY,
171         COMPLETE
172     }
173 
generateUniqueId()174     protected static String generateUniqueId() {
175         return UUID.randomUUID().toString();
176     }
177 
getFinalCredentialResponse()178     public Credential getFinalCredentialResponse() {
179         return mFinalCredentialResponse;
180     }
181 
182     /** Propagates cancellation signal to the remote provider service. */
cancelProviderRemoteSession()183     public void cancelProviderRemoteSession() {
184         try {
185             if (mProviderCancellationSignal != null) {
186                 mProviderCancellationSignal.cancel();
187             }
188             setStatus(Status.CANCELED);
189         } catch (RemoteException e) {
190             Slog.e(TAG, "Issue while cancelling provider session: ", e);
191         }
192     }
193 
setStatus(@onNull Status status)194     protected void setStatus(@NonNull Status status) {
195         mStatus = status;
196     }
197 
198     @NonNull
getStatus()199     protected Status getStatus() {
200         return mStatus;
201     }
202 
203     @NonNull
getComponentName()204     protected ComponentName getComponentName() {
205         return mComponentName;
206     }
207 
208     @Nullable
getRemoteCredentialService()209     protected RemoteCredentialService getRemoteCredentialService() {
210         return mRemoteCredentialService;
211     }
212 
213     /** Updates the status . */
updateStatusAndInvokeCallback(@onNull Status status, CredentialsSource source)214     protected void updateStatusAndInvokeCallback(@NonNull Status status,
215             CredentialsSource source) {
216         setStatus(status);
217         boolean isPrimary = mProviderInfo != null && mProviderInfo.isPrimary();
218         mProviderSessionMetric.collectCandidateMetricUpdate(isTerminatingStatus(status)
219                         || isStatusWaitingForRemoteResponse(status),
220                 isCompletionStatus(status) || isUiInvokingStatus(status),
221                 mProviderSessionUid,
222                 /*isAuthEntry*/source == CredentialsSource.AUTH_ENTRY,
223                 /*isPrimary*/isPrimary);
224         mCallbacks.onProviderStatusChanged(status, mComponentName, source);
225     }
226     /** Common method that transfers metrics from the init phase to candidates */
startCandidateMetrics()227     protected void startCandidateMetrics() {
228         mProviderSessionMetric.collectCandidateMetricSetupViaInitialMetric(
229                 ((RequestSession) mCallbacks).mRequestSessionMetric.getInitialPhaseMetric());
230     }
231 
232     /** Get the request to be sent to the provider. */
getProviderRequest()233     protected T getProviderRequest() {
234         return mProviderRequest;
235     }
236 
237     /** Returns whether the provider response is set. */
isProviderResponseSet()238     protected Boolean isProviderResponseSet() {
239         return mProviderResponse != null || mProviderResponseSet;
240     }
241 
invokeCallbackWithError(String errorType, @Nullable String errorMessage)242     protected void invokeCallbackWithError(String errorType, @Nullable String errorMessage) {
243         // TODO: Determine what the error message should be
244         mCallbacks.onFinalErrorReceived(mComponentName, errorType, errorMessage);
245     }
246 
247     /** Update the response state stored with the provider session. */
248     @Nullable
getProviderResponse()249     protected R getProviderResponse() {
250         return mProviderResponse;
251     }
252 
enforceRemoteEntryRestrictions( @ullable ComponentName expectedRemoteEntryProviderService)253     protected boolean enforceRemoteEntryRestrictions(
254             @Nullable ComponentName expectedRemoteEntryProviderService) {
255         // Check if the service is the one set by the OEM. If not silently reject this entry
256         if (!mComponentName.equals(expectedRemoteEntryProviderService)) {
257             Slog.w(TAG, "Remote entry being dropped as it is not from the service "
258                     + "configured by the OEM.");
259             return false;
260         }
261         // Check if the service has the hybrid permission .If not, silently reject this entry.
262         // This check is in addition to the permission check happening in the provider's process.
263         try {
264             ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo(
265                     mComponentName.getPackageName(),
266                     PackageManager.ApplicationInfoFlags.of(PackageManager.MATCH_SYSTEM_ONLY));
267             if (appInfo != null
268                     && mContext.checkPermission(
269                     Manifest.permission.PROVIDE_REMOTE_CREDENTIALS,
270                     /*pId=*/-1, appInfo.uid) == PackageManager.PERMISSION_GRANTED) {
271                 return true;
272             }
273         } catch (SecurityException | PackageManager.NameNotFoundException e) {
274             Slog.e(TAG, "Error getting info for " + mComponentName.flattenToString(), e);
275             return false;
276         }
277         return false;
278     }
279 
280     /**
281      * Should be overridden to prepare, and stores state for {@link ProviderData} to be
282      * shown on the UI.
283      */
284     @Nullable
prepareUiData()285     protected abstract ProviderData prepareUiData();
286 
287     /** Should be overridden to handle the selected entry from the UI. */
onUiEntrySelected(String entryType, String entryId, ProviderPendingIntentResponse providerPendingIntentResponse)288     protected abstract void onUiEntrySelected(String entryType, String entryId,
289             ProviderPendingIntentResponse providerPendingIntentResponse);
290 
291     /**
292      * Should be overridden to invoke the provider at a defined location. Helpful for
293      * situations such as metric generation.
294      */
invokeSession()295     protected abstract void invokeSession();
296 }
297