1 /*
2  * Copyright (C) 2016 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.settings.security.trustagent;
18 
19 import static android.service.trust.TrustAgentService.TRUST_AGENT_META_DATA;
20 
21 import android.app.admin.DevicePolicyManager;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.PackageManager;
26 import android.content.pm.ResolveInfo;
27 import android.content.res.Resources;
28 import android.content.res.TypedArray;
29 import android.content.res.XmlResourceParser;
30 import android.os.UserHandle;
31 import android.service.trust.TrustAgentService;
32 import android.text.TextUtils;
33 import android.util.AttributeSet;
34 import android.util.Log;
35 import android.util.Slog;
36 import android.util.Xml;
37 
38 import androidx.annotation.VisibleForTesting;
39 
40 import com.android.internal.widget.LockPatternUtils;
41 import com.android.settingslib.RestrictedLockUtils;
42 import com.android.settingslib.RestrictedLockUtilsInternal;
43 
44 import org.xmlpull.v1.XmlPullParser;
45 import org.xmlpull.v1.XmlPullParserException;
46 
47 import java.io.IOException;
48 import java.util.ArrayList;
49 import java.util.List;
50 
51 
52 /** A manager for trust agent state. */
53 public class TrustAgentManager {
54 
55     // Only allow one trust agent on the platform.
56     private static final boolean ONLY_ONE_TRUST_AGENT = false;
57 
58     public static class TrustAgentComponentInfo {
59         public ComponentName componentName;
60         public String title;
61         public String summary;
62         public RestrictedLockUtils.EnforcedAdmin admin = null;
63     }
64 
65     private static final String TAG = "TrustAgentManager";
66     private static final Intent TRUST_AGENT_INTENT =
67             new Intent(TrustAgentService.SERVICE_INTERFACE);
68 
69     @VisibleForTesting
70     static final String PERMISSION_PROVIDE_AGENT =
71             android.Manifest.permission.PROVIDE_TRUST_AGENT;
72 
73     /**
74      * Determines if the service associated with a resolved trust agent intent is allowed to provide
75      * trust on this device.
76      *
77      * @param resolveInfo The entry corresponding to the matched trust agent intent.
78      * @param pm          The package manager to be used to check for permissions.
79      * @return {@code true} if the associated service is allowed to provide a trust agent, and
80      * {@code false} if otherwise.
81      */
shouldProvideTrust(ResolveInfo resolveInfo, PackageManager pm)82     public boolean shouldProvideTrust(ResolveInfo resolveInfo, PackageManager pm) {
83         final String packageName = resolveInfo.serviceInfo.packageName;
84         if (pm.checkPermission(PERMISSION_PROVIDE_AGENT, packageName)
85                 != PackageManager.PERMISSION_GRANTED) {
86             Log.w(TAG, "Skipping agent because package " + packageName
87                     + " does not have permission " + PERMISSION_PROVIDE_AGENT + ".");
88             return false;
89         }
90         return true;
91     }
92 
93     /**
94      * Return the display label for active trust agent.
95      */
getActiveTrustAgentLabel(Context context, LockPatternUtils utils)96     public CharSequence getActiveTrustAgentLabel(Context context, LockPatternUtils utils) {
97         final List<TrustAgentComponentInfo> agents = getActiveTrustAgents(context, utils);
98         return agents.isEmpty() ? null : agents.get(0).title;
99     }
100 
101     /**
102      * Returns a list of trust agents that have a android:settingsActivity set in their declaration.
103      *
104      * If {@link #ONLY_ONE_TRUST_AGENT} is set, the list will contain up to 1 agent instead of all
105      * available agents on device.
106      */
getActiveTrustAgents(Context context, LockPatternUtils utils)107     public List<TrustAgentComponentInfo> getActiveTrustAgents(Context context,
108             LockPatternUtils utils) {
109         return getActiveTrustAgents(context, utils, true);
110     }
111 
112     /**
113      * Returns a list of trust agents.
114      *
115      * If {@link #ONLY_ONE_TRUST_AGENT} is set, the list will contain up to 1 agent instead of all
116      * available agents on device.
117      *
118      * @param skipTrustAgentsWithNoActivity {@code false} to only include trustagents with
119      * android:settingsActivity set in their declaration, {@code true} otherwise.
120      */
getActiveTrustAgents(Context context, LockPatternUtils utils, boolean skipTrustAgentsWithNoActivity)121     public List<TrustAgentComponentInfo> getActiveTrustAgents(Context context,
122             LockPatternUtils utils, boolean skipTrustAgentsWithNoActivity) {
123         final int myUserId = UserHandle.myUserId();
124         final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
125         final PackageManager pm = context.getPackageManager();
126         final List<TrustAgentComponentInfo> result = new ArrayList<>();
127 
128         final List<ResolveInfo> resolveInfos = pm.queryIntentServices(TRUST_AGENT_INTENT,
129                 PackageManager.GET_META_DATA);
130         final List<ComponentName> enabledTrustAgents = utils.getEnabledTrustAgents(myUserId);
131         final RestrictedLockUtils.EnforcedAdmin admin = RestrictedLockUtilsInternal
132                 .checkIfKeyguardFeaturesDisabled(
133                         context, DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS, myUserId);
134 
135         if (enabledTrustAgents != null && !enabledTrustAgents.isEmpty()) {
136             for (ResolveInfo resolveInfo : resolveInfos) {
137                 if (resolveInfo.serviceInfo == null || !shouldProvideTrust(resolveInfo, pm)) {
138                     continue;
139                 }
140                 final TrustAgentComponentInfo trustAgentComponentInfo =
141                         getSettingsComponent(pm, resolveInfo);
142                 if (skipTrustAgentsWithNoActivity
143                         && trustAgentComponentInfo.componentName == null) {
144                     continue;
145                 }
146                 if (!enabledTrustAgents.contains(getComponentName(resolveInfo))
147                         || TextUtils.isEmpty(trustAgentComponentInfo.title)) {
148                     continue;
149                 }
150                 if (admin != null && dpm.getTrustAgentConfiguration(
151                         null, getComponentName(resolveInfo)) == null) {
152                     trustAgentComponentInfo.admin = admin;
153                 }
154                 result.add(trustAgentComponentInfo);
155                 if (ONLY_ONE_TRUST_AGENT) {
156                     break;
157                 }
158             }
159         }
160         return result;
161     }
162 
getComponentName(ResolveInfo resolveInfo)163     public ComponentName getComponentName(ResolveInfo resolveInfo) {
164         if (resolveInfo == null || resolveInfo.serviceInfo == null) return null;
165         return new ComponentName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
166     }
167 
getSettingsComponent(PackageManager pm, ResolveInfo resolveInfo)168     private TrustAgentComponentInfo getSettingsComponent(PackageManager pm,
169             ResolveInfo resolveInfo) {
170         if (resolveInfo == null || resolveInfo.serviceInfo == null
171                 || resolveInfo.serviceInfo.metaData == null) {
172             return null;
173         }
174         String cn = null;
175         TrustAgentComponentInfo trustAgentComponentInfo = new TrustAgentComponentInfo();
176         XmlResourceParser parser = null;
177         Exception caughtException = null;
178         try {
179             parser = resolveInfo.serviceInfo.loadXmlMetaData(pm, TRUST_AGENT_META_DATA);
180             if (parser == null) {
181                 Slog.w(TAG, "Can't find " + TRUST_AGENT_META_DATA + " meta-data");
182                 return null;
183             }
184             Resources res = pm.getResourcesForApplication(resolveInfo.serviceInfo.applicationInfo);
185             AttributeSet attrs = Xml.asAttributeSet(parser);
186             int type;
187             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
188                     && type != XmlPullParser.START_TAG) {
189             }
190             String nodeName = parser.getName();
191             if (!"trust-agent".equals(nodeName)) {
192                 Slog.w(TAG, "Meta-data does not start with trust-agent tag");
193                 return null;
194             }
195             TypedArray sa =
196                     res.obtainAttributes(attrs, com.android.internal.R.styleable.TrustAgent);
197             trustAgentComponentInfo.summary =
198                     sa.getString(com.android.internal.R.styleable.TrustAgent_summary);
199             trustAgentComponentInfo.title =
200                     sa.getString(com.android.internal.R.styleable.TrustAgent_title);
201             cn = sa.getString(com.android.internal.R.styleable.TrustAgent_settingsActivity);
202             sa.recycle();
203         } catch (PackageManager.NameNotFoundException e) {
204             caughtException = e;
205         } catch (IOException e) {
206             caughtException = e;
207         } catch (XmlPullParserException e) {
208             caughtException = e;
209         } finally {
210             if (parser != null) parser.close();
211         }
212         if (caughtException != null) {
213             Slog.w(TAG, "Error parsing : " + resolveInfo.serviceInfo.packageName, caughtException);
214             return null;
215         }
216         if (cn != null && cn.indexOf('/') < 0) {
217             cn = resolveInfo.serviceInfo.packageName + "/" + cn;
218         }
219         trustAgentComponentInfo.componentName =
220                 (cn == null) ? null : ComponentName.unflattenFromString(cn);
221         return trustAgentComponentInfo;
222     }
223 }
224