• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static com.android.server.credentials.CredentialManagerService.getPrimaryProvidersForUserId;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.pm.PackageManager;
27 import android.content.pm.ResolveInfo;
28 import android.content.pm.ServiceInfo;
29 import android.credentials.CredentialManager;
30 import android.credentials.CredentialProviderInfo;
31 import android.credentials.flags.Flags;
32 import android.service.credentials.CredentialProviderInfoFactory;
33 import android.service.credentials.CredentialProviderService;
34 import android.util.Slog;
35 
36 import com.android.internal.annotations.GuardedBy;
37 import com.android.server.infra.AbstractPerUserSystemService;
38 
39 import java.util.List;
40 import java.util.Set;
41 
42 
43 /**
44  * Per-user, per remote service implementation of {@link CredentialManagerService}
45  */
46 public final class CredentialManagerServiceImpl extends
47         AbstractPerUserSystemService<CredentialManagerServiceImpl, CredentialManagerService> {
48     private static final String TAG = CredentialManager.TAG;
49 
50     @GuardedBy("mLock")
51     @NonNull
52     private CredentialProviderInfo mInfo;
53 
CredentialManagerServiceImpl( @onNull CredentialManagerService master, @NonNull Object lock, int userId, String serviceName)54     CredentialManagerServiceImpl(
55             @NonNull CredentialManagerService master,
56             @NonNull Object lock, int userId, String serviceName)
57             throws PackageManager.NameNotFoundException {
58         super(master, lock, userId);
59         Slog.i(TAG, "CredentialManagerServiceImpl constructed for: " + serviceName);
60         synchronized (mLock) {
61             newServiceInfoLocked(ComponentName.unflattenFromString(serviceName));
62         }
63     }
64 
65     @Nullable
66     @Override
67     @GuardedBy("mLock")
getServiceComponentName()68     public ComponentName getServiceComponentName() {
69         return getComponentName();
70     }
71 
72     @GuardedBy("mLock")
getComponentName()73     public ComponentName getComponentName() {
74         return mInfo.getServiceInfo().getComponentName();
75     }
76 
CredentialManagerServiceImpl( @onNull CredentialManagerService master, @NonNull Object lock, int userId, CredentialProviderInfo providerInfo)77     CredentialManagerServiceImpl(
78             @NonNull CredentialManagerService master,
79             @NonNull Object lock, int userId, CredentialProviderInfo providerInfo) {
80         super(master, lock, userId);
81         Slog.i(TAG, "CredentialManagerServiceImpl constructed for: "
82                 + providerInfo.getServiceInfo().getComponentName().flattenToString());
83         mInfo = providerInfo;
84     }
85 
86     @Override // from PerUserSystemService when a new service is to be created
87     @GuardedBy("mLock")
newServiceInfoLocked(@onNull ComponentName serviceComponent)88     protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
89             throws PackageManager.NameNotFoundException, SecurityException, NullPointerException {
90         boolean isSystemProvider = false;
91         if (mInfo != null) {
92             Slog.i(TAG, "newServiceInfoLocked, mInfo not null : "
93                     + mInfo.getServiceInfo().getComponentName().flattenToString() + " , "
94                     + serviceComponent.flattenToString());
95             isSystemProvider = mInfo.isSystemProvider();
96         } else {
97             Slog.i(TAG, "newServiceInfoLocked, mInfo null, "
98                     + serviceComponent.flattenToString());
99         }
100         Set<ComponentName> primaryProviders =
101                 getPrimaryProvidersForUserId(mMaster.getContext(), mUserId);
102         mInfo = CredentialProviderInfoFactory.create(
103                 getContext(), serviceComponent,
104                 mUserId, isSystemProvider,
105                 primaryProviders.contains(serviceComponent));
106         return mInfo.getServiceInfo();
107     }
108 
109     /**
110      * Starts a provider session and associates it with the given request session.
111      */
112     @Nullable
113     @GuardedBy("mLock")
initiateProviderSessionForRequestLocked( RequestSession requestSession, List<String> requestOptions)114     public ProviderSession initiateProviderSessionForRequestLocked(
115             RequestSession requestSession, List<String> requestOptions) {
116         if (!requestOptions.isEmpty() && !isServiceCapableLocked(requestOptions)) {
117             if (mInfo != null) {
118                 Slog.i(TAG, "Service does not have the required capabilities: "
119                         + mInfo.getComponentName());
120             }
121             return null;
122         }
123         if (mInfo == null) {
124             Slog.w(TAG, "Initiating provider session for request "
125                     + "but mInfo is null. This shouldn't happen");
126             return null;
127         }
128         final RemoteCredentialService remoteService = new RemoteCredentialService(
129                 getContext(), mInfo.getServiceInfo().getComponentName(), mUserId);
130         return requestSession.initiateProviderSession(mInfo, remoteService);
131     }
132 
133     /** Return true if at least one capability found. */
134     @GuardedBy("mLock")
isServiceCapableLocked(List<String> requestedOptions)135     boolean isServiceCapableLocked(List<String> requestedOptions) {
136         if (mInfo == null) {
137             return false;
138         }
139         for (String capability : requestedOptions) {
140             if (mInfo.hasCapability(capability)) {
141                 return true;
142             }
143         }
144         return false;
145     }
146 
147     @GuardedBy("mLock")
getCredentialProviderInfo()148     public CredentialProviderInfo getCredentialProviderInfo() {
149         return mInfo;
150     }
151 
152     /**
153      * Callback called when an app has been updated.
154      *
155      * @param packageName package of the app being updated.
156      */
157     @GuardedBy("mLock")
158     @SuppressWarnings("GuardedBy") // ErrorProne requires this.mMaster.mLock which is the case
159     // because this method is called by this.mMaster anyway
handlePackageUpdateLocked(@onNull String packageName)160     protected void handlePackageUpdateLocked(@NonNull String packageName) {
161         if (mInfo != null && mInfo.getServiceInfo() != null
162                 && mInfo.getServiceInfo().getComponentName()
163                 .getPackageName().equals(packageName)) {
164             if (Flags.packageUpdateFixEnabled()) {
165                 try {
166                     updateCredentialProviderInfo(mInfo.getServiceInfo().getComponentName(),
167                             mInfo.isSystemProvider());
168                 } catch (SecurityException | PackageManager.NameNotFoundException
169                          | NullPointerException e) {
170                     Slog.w(TAG, "Unable to update provider, must be removed: " + e.getMessage());
171                     mMaster.handleServiceRemovedMultiModeLocked(mInfo.getComponentName(), mUserId);
172                 }
173             } else {
174                 try {
175                     newServiceInfoLocked(mInfo.getServiceInfo().getComponentName());
176                 } catch (PackageManager.NameNotFoundException e) {
177                     Slog.e(TAG, "Issue while updating serviceInfo: " + e.getMessage());
178                 }
179             }
180         }
181     }
182 
183     @GuardedBy("mLock")
updateCredentialProviderInfo(ComponentName componentName, boolean isSystemProvider)184     private void updateCredentialProviderInfo(ComponentName componentName, boolean isSystemProvider)
185             throws SecurityException, PackageManager.NameNotFoundException {
186         Slog.d(TAG, "Updating credential provider: " + componentName.flattenToString());
187         if (!isValidCredentialProviderInfo(componentName, mUserId, isSystemProvider)) {
188             throw new SecurityException("Service has not been set up correctly");
189         }
190         newServiceInfoLocked(componentName);
191     }
192 
isValidCredentialProviderInfo(ComponentName componentName, int userId, boolean isSystemProvider)193     private boolean isValidCredentialProviderInfo(ComponentName componentName, int userId,
194             boolean isSystemProvider) {
195         Context context = getContext();
196         if (context == null) {
197             return false;
198         }
199         String serviceInterface = CredentialProviderService.SERVICE_INTERFACE;
200         if (isSystemProvider) {
201             serviceInterface = CredentialProviderService.SYSTEM_SERVICE_INTERFACE;
202         }
203         final List<ResolveInfo> resolveInfos =
204                 context.getPackageManager()
205                         .queryIntentServicesAsUser(
206                                 new Intent(serviceInterface),
207                                 PackageManager.ResolveInfoFlags.of(PackageManager.GET_META_DATA),
208                                 userId);
209         for (ResolveInfo resolveInfo : resolveInfos) {
210             final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
211             if (serviceInfo.getComponentName().equals(componentName)) {
212                 return true;
213             }
214         }
215         return false;
216     }
217 }
218