blob: c1b9a33ea45801334e7faa607d66af0ba15e02e0 [file] [log] [blame]
Adrian Roos82142c22014-03-27 14:56:59 +01001/*
2 * Copyright (C) 2014 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
17package com.android.server.trust;
18
19import com.android.internal.content.PackageMonitor;
20import com.android.internal.widget.LockPatternUtils;
21import com.android.server.SystemService;
22
23import org.xmlpull.v1.XmlPullParser;
24import org.xmlpull.v1.XmlPullParserException;
25
26import android.Manifest;
Adrian Roosca36b952014-05-16 18:52:29 +020027import android.app.admin.DevicePolicyManager;
Adrian Roos82142c22014-03-27 14:56:59 +010028import android.app.trust.ITrustListener;
29import android.app.trust.ITrustManager;
Adrian Roosca36b952014-05-16 18:52:29 +020030import android.content.BroadcastReceiver;
Adrian Roos82142c22014-03-27 14:56:59 +010031import android.content.ComponentName;
32import android.content.Context;
33import android.content.Intent;
Adrian Roosca36b952014-05-16 18:52:29 +020034import android.content.IntentFilter;
Adrian Roos82142c22014-03-27 14:56:59 +010035import android.content.pm.PackageManager;
36import android.content.pm.ResolveInfo;
37import android.content.pm.UserInfo;
38import android.content.res.Resources;
39import android.content.res.TypedArray;
40import android.content.res.XmlResourceParser;
41import android.graphics.drawable.Drawable;
42import android.os.Handler;
43import android.os.IBinder;
44import android.os.Message;
45import android.os.RemoteException;
46import android.os.UserHandle;
47import android.os.UserManager;
48import android.service.trust.TrustAgentService;
49import android.util.ArraySet;
50import android.util.AttributeSet;
51import android.util.Slog;
Adrian Roos7046bfd2014-05-16 21:20:54 +020052import android.util.SparseBooleanArray;
Adrian Roos82142c22014-03-27 14:56:59 +010053import android.util.Xml;
54
55import java.io.IOException;
56import java.util.ArrayList;
57import java.util.List;
58
59/**
60 * Manages trust agents and trust listeners.
61 *
62 * It is responsible for binding to the enabled {@link android.service.trust.TrustAgentService}s
63 * of each user and notifies them about events that are relevant to them.
64 * It start and stops them based on the value of
65 * {@link com.android.internal.widget.LockPatternUtils#getEnabledTrustAgents(int)}.
66 *
67 * It also keeps a set of {@link android.app.trust.ITrustListener}s that are notified whenever the
68 * trust state changes for any user.
69 *
70 * Trust state and the setting of enabled agents is kept per user and each user has its own
71 * instance of a {@link android.service.trust.TrustAgentService}.
72 */
73public class TrustManagerService extends SystemService {
74
75 private static final boolean DEBUG = false;
76 private static final String TAG = "TrustManagerService";
77
78 private static final Intent TRUST_AGENT_INTENT =
79 new Intent(TrustAgentService.SERVICE_INTERFACE);
80
81 private static final int MSG_REGISTER_LISTENER = 1;
82 private static final int MSG_UNREGISTER_LISTENER = 2;
83 private static final int MSG_DISPATCH_UNLOCK_ATTEMPT = 3;
84 private static final int MSG_ENABLED_AGENTS_CHANGED = 4;
85
86 private final ArraySet<AgentInfo> mActiveAgents = new ArraySet<AgentInfo>();
87 private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<ITrustListener>();
Adrian Roosca36b952014-05-16 18:52:29 +020088 private final DevicePolicyReceiver mDevicePolicyReceiver = new DevicePolicyReceiver();
Adrian Roos7046bfd2014-05-16 21:20:54 +020089 private final SparseBooleanArray mUserHasAuthenticatedSinceBoot = new SparseBooleanArray();
Adrian Roos82142c22014-03-27 14:56:59 +010090 private final Context mContext;
91
92 private UserManager mUserManager;
93
94 /**
95 * Cache for {@link #refreshAgentList()}
96 */
97 private final ArraySet<AgentInfo> mObsoleteAgents = new ArraySet<AgentInfo>();
98
99
100 public TrustManagerService(Context context) {
101 super(context);
102 mContext = context;
103 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
104 }
105
106 @Override
107 public void onStart() {
108 publishBinderService(Context.TRUST_SERVICE, mService);
109 }
110
111 @Override
112 public void onBootPhase(int phase) {
113 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY && !isSafeMode()) {
Adrian Roos82142c22014-03-27 14:56:59 +0100114 mPackageMonitor.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
Adrian Roosca36b952014-05-16 18:52:29 +0200115 mDevicePolicyReceiver.register(mContext);
Adrian Roos82142c22014-03-27 14:56:59 +0100116 refreshAgentList();
117 }
118 }
119
120 // Agent management
121
122 private static final class AgentInfo {
123 CharSequence label;
124 Drawable icon;
125 ComponentName component; // service that implements ITrustAgent
126 ComponentName settings; // setting to launch to modify agent.
127 TrustAgentWrapper agent;
128 int userId;
129
130 @Override
131 public boolean equals(Object other) {
132 if (!(other instanceof AgentInfo)) {
133 return false;
134 }
135 AgentInfo o = (AgentInfo) other;
136 return component.equals(o.component) && userId == o.userId;
137 }
138
139 @Override
140 public int hashCode() {
141 return component.hashCode() * 31 + userId;
142 }
143 }
144
145 private void updateTrustAll() {
146 List<UserInfo> userInfos = mUserManager.getUsers(true /* excludeDying */);
147 for (UserInfo userInfo : userInfos) {
148 updateTrust(userInfo.id);
149 }
150 }
151
152 public void updateTrust(int userId) {
153 dispatchOnTrustChanged(aggregateIsTrusted(userId), userId);
154 }
155
156 protected void refreshAgentList() {
157 if (DEBUG) Slog.d(TAG, "refreshAgentList()");
158 PackageManager pm = mContext.getPackageManager();
159
160 List<UserInfo> userInfos = mUserManager.getUsers(true /* excludeDying */);
161 LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
162
163 mObsoleteAgents.clear();
164 mObsoleteAgents.addAll(mActiveAgents);
165
166 for (UserInfo userInfo : userInfos) {
Adrian Roosca36b952014-05-16 18:52:29 +0200167 int disabledFeatures = lockPatternUtils.getDevicePolicyManager()
168 .getKeyguardDisabledFeatures(null, userInfo.id);
169 boolean disableTrustAgents =
170 (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0;
171
Adrian Roos82142c22014-03-27 14:56:59 +0100172 List<ComponentName> enabledAgents = lockPatternUtils.getEnabledTrustAgents(userInfo.id);
Adrian Roosca36b952014-05-16 18:52:29 +0200173 if (disableTrustAgents || enabledAgents == null) {
Adrian Roos82142c22014-03-27 14:56:59 +0100174 continue;
175 }
176 List<ResolveInfo> resolveInfos = pm.queryIntentServicesAsUser(TRUST_AGENT_INTENT,
177 PackageManager.GET_META_DATA, userInfo.id);
178 for (ResolveInfo resolveInfo : resolveInfos) {
179 if (resolveInfo.serviceInfo == null) continue;
180 ComponentName name = getComponentName(resolveInfo);
181 if (!enabledAgents.contains(name)) continue;
182
183 AgentInfo agentInfo = new AgentInfo();
184 agentInfo.component = name;
185 agentInfo.userId = userInfo.id;
186 if (!mActiveAgents.contains(agentInfo)) {
187 agentInfo.label = resolveInfo.loadLabel(pm);
188 agentInfo.icon = resolveInfo.loadIcon(pm);
189 agentInfo.settings = getSettingsComponentName(pm, resolveInfo);
190 agentInfo.agent = new TrustAgentWrapper(mContext, this,
191 new Intent().setComponent(name), userInfo.getUserHandle());
192 mActiveAgents.add(agentInfo);
193 } else {
194 mObsoleteAgents.remove(agentInfo);
195 }
196 }
197 }
198
199 boolean trustMayHaveChanged = false;
200 for (int i = 0; i < mObsoleteAgents.size(); i++) {
Adrian Roos81e04662014-04-30 17:48:18 +0200201 AgentInfo info = mObsoleteAgents.valueAt(i);
Adrian Roos82142c22014-03-27 14:56:59 +0100202 if (info.agent.isTrusted()) {
203 trustMayHaveChanged = true;
204 }
205 info.agent.unbind();
Adrian Roosa5956422014-04-30 18:23:38 +0200206 mActiveAgents.remove(info);
Adrian Roos82142c22014-03-27 14:56:59 +0100207 }
208
209 if (trustMayHaveChanged) {
210 updateTrustAll();
211 }
212 }
213
214 private ComponentName getSettingsComponentName(PackageManager pm, ResolveInfo resolveInfo) {
215 if (resolveInfo == null || resolveInfo.serviceInfo == null
216 || resolveInfo.serviceInfo.metaData == null) return null;
217 String cn = null;
218 XmlResourceParser parser = null;
219 Exception caughtException = null;
220 try {
221 parser = resolveInfo.serviceInfo.loadXmlMetaData(pm,
222 TrustAgentService.TRUST_AGENT_META_DATA);
223 if (parser == null) {
224 Slog.w(TAG, "Can't find " + TrustAgentService.TRUST_AGENT_META_DATA + " meta-data");
225 return null;
226 }
227 Resources res = pm.getResourcesForApplication(resolveInfo.serviceInfo.applicationInfo);
228 AttributeSet attrs = Xml.asAttributeSet(parser);
229 int type;
230 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
231 && type != XmlPullParser.START_TAG) {
232 // Drain preamble.
233 }
234 String nodeName = parser.getName();
Adrian Roos7e03dfc2014-05-16 16:06:28 +0200235 if (!"trust-agent".equals(nodeName)) {
236 Slog.w(TAG, "Meta-data does not start with trust-agent tag");
Adrian Roos82142c22014-03-27 14:56:59 +0100237 return null;
238 }
239 TypedArray sa = res
240 .obtainAttributes(attrs, com.android.internal.R.styleable.TrustAgent);
241 cn = sa.getString(com.android.internal.R.styleable.TrustAgent_settingsActivity);
242 sa.recycle();
243 } catch (PackageManager.NameNotFoundException e) {
244 caughtException = e;
245 } catch (IOException e) {
246 caughtException = e;
247 } catch (XmlPullParserException e) {
248 caughtException = e;
249 } finally {
250 if (parser != null) parser.close();
251 }
252 if (caughtException != null) {
253 Slog.w(TAG, "Error parsing : " + resolveInfo.serviceInfo.packageName, caughtException);
254 return null;
255 }
256 if (cn == null) {
257 return null;
258 }
259 if (cn.indexOf('/') < 0) {
260 cn = resolveInfo.serviceInfo.packageName + "/" + cn;
261 }
262 return ComponentName.unflattenFromString(cn);
263 }
264
265 private ComponentName getComponentName(ResolveInfo resolveInfo) {
266 if (resolveInfo == null || resolveInfo.serviceInfo == null) return null;
267 return new ComponentName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
268 }
269
270 // Agent dispatch and aggregation
271
272 private boolean aggregateIsTrusted(int userId) {
Adrian Roos7046bfd2014-05-16 21:20:54 +0200273 if (!mUserHasAuthenticatedSinceBoot.get(userId)) {
274 return false;
275 }
Adrian Roos82142c22014-03-27 14:56:59 +0100276 for (int i = 0; i < mActiveAgents.size(); i++) {
277 AgentInfo info = mActiveAgents.valueAt(i);
278 if (info.userId == userId) {
279 if (info.agent.isTrusted()) {
280 return true;
281 }
282 }
283 }
284 return false;
285 }
286
287 private void dispatchUnlockAttempt(boolean successful, int userId) {
288 for (int i = 0; i < mActiveAgents.size(); i++) {
289 AgentInfo info = mActiveAgents.valueAt(i);
290 if (info.userId == userId) {
291 info.agent.onUnlockAttempt(successful);
292 }
293 }
Adrian Roos7046bfd2014-05-16 21:20:54 +0200294
295 if (successful && !mUserHasAuthenticatedSinceBoot.get(userId)) {
296 mUserHasAuthenticatedSinceBoot.put(userId, true);
297 updateTrust(userId);
298 }
Adrian Roos82142c22014-03-27 14:56:59 +0100299 }
300
301 // Listeners
302
303 private void addListener(ITrustListener listener) {
304 for (int i = 0; i < mTrustListeners.size(); i++) {
305 if (mTrustListeners.get(i).asBinder() == listener.asBinder()) {
306 return;
307 }
308 }
309 mTrustListeners.add(listener);
310 }
311
312 private void removeListener(ITrustListener listener) {
313 for (int i = 0; i < mTrustListeners.size(); i++) {
314 if (mTrustListeners.get(i).asBinder() == listener.asBinder()) {
315 mTrustListeners.get(i);
316 return;
317 }
318 }
319 }
320
321 private void dispatchOnTrustChanged(boolean enabled, int userId) {
322 for (int i = 0; i < mTrustListeners.size(); i++) {
323 try {
324 mTrustListeners.get(i).onTrustChanged(enabled, userId);
325 } catch (RemoteException e) {
326 Slog.e(TAG, "Exception while notifying TrustListener. Removing listener.", e);
327 mTrustListeners.get(i);
328 i--;
329 }
330 }
331 }
332
333 // Plumbing
334
335 private final IBinder mService = new ITrustManager.Stub() {
336 @Override
337 public void reportUnlockAttempt(boolean authenticated, int userId) throws RemoteException {
338 enforceReportPermission();
339 mHandler.obtainMessage(MSG_DISPATCH_UNLOCK_ATTEMPT, authenticated ? 1 : 0, userId)
340 .sendToTarget();
341 }
342
343 @Override
344 public void reportEnabledTrustAgentsChanged(int userId) throws RemoteException {
345 enforceReportPermission();
346 // coalesce refresh messages.
347 mHandler.removeMessages(MSG_ENABLED_AGENTS_CHANGED);
348 mHandler.sendEmptyMessage(MSG_ENABLED_AGENTS_CHANGED);
349 }
350
351 @Override
352 public void registerTrustListener(ITrustListener trustListener) throws RemoteException {
353 enforceListenerPermission();
354 mHandler.obtainMessage(MSG_REGISTER_LISTENER, trustListener).sendToTarget();
355 }
356
357 @Override
358 public void unregisterTrustListener(ITrustListener trustListener) throws RemoteException {
359 enforceListenerPermission();
360 mHandler.obtainMessage(MSG_UNREGISTER_LISTENER, trustListener).sendToTarget();
361 }
362
363 private void enforceReportPermission() {
364 mContext.enforceCallingPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE,
365 "reporting trust events");
366 }
367
368 private void enforceListenerPermission() {
369 mContext.enforceCallingPermission(Manifest.permission.TRUST_LISTENER,
370 "register trust listener");
371 }
372 };
373
374 private final Handler mHandler = new Handler() {
375 @Override
376 public void handleMessage(Message msg) {
377 switch (msg.what) {
378 case MSG_REGISTER_LISTENER:
379 addListener((ITrustListener) msg.obj);
380 break;
381 case MSG_UNREGISTER_LISTENER:
382 removeListener((ITrustListener) msg.obj);
383 break;
384 case MSG_DISPATCH_UNLOCK_ATTEMPT:
385 dispatchUnlockAttempt(msg.arg1 != 0, msg.arg2);
386 break;
387 case MSG_ENABLED_AGENTS_CHANGED:
388 refreshAgentList();
389 break;
390 }
391 }
392 };
393
394 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
395 @Override
396 public void onSomePackagesChanged() {
397 refreshAgentList();
398 }
399
400 @Override
401 public boolean onPackageChanged(String packageName, int uid, String[] components) {
402 // We're interested in all changes, even if just some components get enabled / disabled.
403 return true;
404 }
405 };
Adrian Roosca36b952014-05-16 18:52:29 +0200406
407 private class DevicePolicyReceiver extends BroadcastReceiver {
408
409 @Override
410 public void onReceive(Context context, Intent intent) {
411 if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(
412 intent.getAction())) {
413 refreshAgentList();
414 }
415 }
416
417 public void register(Context context) {
418 context.registerReceiverAsUser(this,
419 UserHandle.ALL,
420 new IntentFilter(
421 DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
422 null /* permission */,
423 null /* scheduler */);
424 }
425 }
Adrian Roos82142c22014-03-27 14:56:59 +0100426}