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.pm.PackageManager;
25 import android.content.pm.ServiceInfo;
26 import android.credentials.CredentialManager;
27 import android.credentials.CredentialProviderInfo;
28 import android.service.credentials.CredentialProviderInfoFactory;
29 import android.util.Slog;
30 
31 import com.android.internal.annotations.GuardedBy;
32 import com.android.server.infra.AbstractPerUserSystemService;
33 
34 import java.util.List;
35 import java.util.Set;
36 
37 
38 /**
39  * Per-user, per remote service implementation of {@link CredentialManagerService}
40  */
41 public final class CredentialManagerServiceImpl extends
42         AbstractPerUserSystemService<CredentialManagerServiceImpl, CredentialManagerService> {
43     private static final String TAG = CredentialManager.TAG;
44 
45     @GuardedBy("mLock")
46     @NonNull
47     private CredentialProviderInfo mInfo;
48 
CredentialManagerServiceImpl( @onNull CredentialManagerService master, @NonNull Object lock, int userId, String serviceName)49     CredentialManagerServiceImpl(
50             @NonNull CredentialManagerService master,
51             @NonNull Object lock, int userId, String serviceName)
52             throws PackageManager.NameNotFoundException {
53         super(master, lock, userId);
54         Slog.i(TAG, "CredentialManagerServiceImpl constructed for: " + serviceName);
55         synchronized (mLock) {
56             newServiceInfoLocked(ComponentName.unflattenFromString(serviceName));
57         }
58     }
59 
60     @GuardedBy("mLock")
getComponentName()61     public ComponentName getComponentName() {
62         return mInfo.getServiceInfo().getComponentName();
63     }
64 
CredentialManagerServiceImpl( @onNull CredentialManagerService master, @NonNull Object lock, int userId, CredentialProviderInfo providerInfo)65     CredentialManagerServiceImpl(
66             @NonNull CredentialManagerService master,
67             @NonNull Object lock, int userId, CredentialProviderInfo providerInfo) {
68         super(master, lock, userId);
69         Slog.i(TAG, "CredentialManagerServiceImpl constructed for: "
70                 + providerInfo.getServiceInfo().getComponentName().flattenToString());
71         mInfo = providerInfo;
72     }
73 
74     @Override // from PerUserSystemService when a new setting based service is to be created
75     @GuardedBy("mLock")
newServiceInfoLocked(@onNull ComponentName serviceComponent)76     protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
77             throws PackageManager.NameNotFoundException {
78         if (mInfo != null) {
79             Slog.i(TAG, "newServiceInfoLocked, mInfo not null : "
80                     + mInfo.getServiceInfo().getComponentName().flattenToString() + " , "
81                     + serviceComponent.flattenToString());
82         } else {
83             Slog.i(TAG, "newServiceInfoLocked, mInfo null, "
84                     + serviceComponent.flattenToString());
85         }
86         Set<ComponentName> primaryProviders =
87                 getPrimaryProvidersForUserId(mMaster.getContext(), mUserId);
88         mInfo = CredentialProviderInfoFactory.create(
89                 getContext(), serviceComponent,
90                 mUserId, /*isSystemProvider=*/false,
91                 primaryProviders.contains(serviceComponent));
92         return mInfo.getServiceInfo();
93     }
94 
95     /**
96      * Starts a provider session and associates it with the given request session.
97      */
98     @Nullable
99     @GuardedBy("mLock")
initiateProviderSessionForRequestLocked( RequestSession requestSession, List<String> requestOptions)100     public ProviderSession initiateProviderSessionForRequestLocked(
101             RequestSession requestSession, List<String> requestOptions) {
102         if (!requestOptions.isEmpty() && !isServiceCapableLocked(requestOptions)) {
103             if (mInfo != null) {
104                 Slog.i(TAG, "Service does not have the required capabilities: "
105                         + mInfo.getComponentName());
106             }
107             return null;
108         }
109         if (mInfo == null) {
110             Slog.w(TAG, "Initiating provider session for request "
111                     + "but mInfo is null. This shouldn't happen");
112             return null;
113         }
114         final RemoteCredentialService remoteService = new RemoteCredentialService(
115                 getContext(), mInfo.getServiceInfo().getComponentName(), mUserId);
116         return requestSession.initiateProviderSession(mInfo, remoteService);
117     }
118 
119     /** Return true if at least one capability found. */
120     @GuardedBy("mLock")
isServiceCapableLocked(List<String> requestedOptions)121     boolean isServiceCapableLocked(List<String> requestedOptions) {
122         if (mInfo == null) {
123             return false;
124         }
125         for (String capability : requestedOptions) {
126             if (mInfo.hasCapability(capability)) {
127                 return true;
128             }
129         }
130         return false;
131     }
132 
133     @GuardedBy("mLock")
getCredentialProviderInfo()134     public CredentialProviderInfo getCredentialProviderInfo() {
135         return mInfo;
136     }
137 
138     /**
139      * Callback called when an app has been updated.
140      *
141      * @param packageName package of the app being updated.
142      */
143     @GuardedBy("mLock")
handlePackageUpdateLocked(@onNull String packageName)144     protected void handlePackageUpdateLocked(@NonNull String packageName) {
145         if (mInfo != null && mInfo.getServiceInfo() != null
146                 && mInfo.getServiceInfo().getComponentName()
147                 .getPackageName().equals(packageName)) {
148             try {
149                 newServiceInfoLocked(mInfo.getServiceInfo().getComponentName());
150             } catch (PackageManager.NameNotFoundException e) {
151                 Slog.e(TAG, "Issue while updating serviceInfo: " + e.getMessage());
152             }
153         }
154     }
155 }
156