blob: 986cdc136153fd238d50e07cd48419e931f4e65f [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 Roos7a4f3d42014-05-02 12:12:20 +020027import android.app.ActivityManagerNative;
Adrian Roosca36b952014-05-16 18:52:29 +020028import android.app.admin.DevicePolicyManager;
Adrian Roos82142c22014-03-27 14:56:59 +010029import android.app.trust.ITrustListener;
30import android.app.trust.ITrustManager;
Adrian Roosca36b952014-05-16 18:52:29 +020031import android.content.BroadcastReceiver;
Adrian Roos82142c22014-03-27 14:56:59 +010032import android.content.ComponentName;
33import android.content.Context;
34import android.content.Intent;
Adrian Roosca36b952014-05-16 18:52:29 +020035import android.content.IntentFilter;
Adrian Roos82142c22014-03-27 14:56:59 +010036import android.content.pm.PackageManager;
37import android.content.pm.ResolveInfo;
38import android.content.pm.UserInfo;
39import android.content.res.Resources;
40import android.content.res.TypedArray;
41import android.content.res.XmlResourceParser;
42import android.graphics.drawable.Drawable;
43import android.os.Handler;
44import android.os.IBinder;
45import android.os.Message;
46import android.os.RemoteException;
47import android.os.UserHandle;
48import android.os.UserManager;
49import android.service.trust.TrustAgentService;
50import android.util.ArraySet;
51import android.util.AttributeSet;
52import android.util.Slog;
Adrian Roos7046bfd2014-05-16 21:20:54 +020053import android.util.SparseBooleanArray;
Adrian Roos82142c22014-03-27 14:56:59 +010054import android.util.Xml;
55
Adrian Roos7a4f3d42014-05-02 12:12:20 +020056import java.io.FileDescriptor;
Adrian Roos82142c22014-03-27 14:56:59 +010057import java.io.IOException;
Adrian Roos7a4f3d42014-05-02 12:12:20 +020058import java.io.PrintWriter;
Adrian Roos82142c22014-03-27 14:56:59 +010059import java.util.ArrayList;
60import java.util.List;
61
62/**
63 * Manages trust agents and trust listeners.
64 *
65 * It is responsible for binding to the enabled {@link android.service.trust.TrustAgentService}s
66 * of each user and notifies them about events that are relevant to them.
67 * It start and stops them based on the value of
68 * {@link com.android.internal.widget.LockPatternUtils#getEnabledTrustAgents(int)}.
69 *
70 * It also keeps a set of {@link android.app.trust.ITrustListener}s that are notified whenever the
71 * trust state changes for any user.
72 *
73 * Trust state and the setting of enabled agents is kept per user and each user has its own
74 * instance of a {@link android.service.trust.TrustAgentService}.
75 */
76public class TrustManagerService extends SystemService {
77
78 private static final boolean DEBUG = false;
79 private static final String TAG = "TrustManagerService";
80
81 private static final Intent TRUST_AGENT_INTENT =
82 new Intent(TrustAgentService.SERVICE_INTERFACE);
83
84 private static final int MSG_REGISTER_LISTENER = 1;
85 private static final int MSG_UNREGISTER_LISTENER = 2;
86 private static final int MSG_DISPATCH_UNLOCK_ATTEMPT = 3;
87 private static final int MSG_ENABLED_AGENTS_CHANGED = 4;
88
89 private final ArraySet<AgentInfo> mActiveAgents = new ArraySet<AgentInfo>();
90 private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<ITrustListener>();
Adrian Roosca36b952014-05-16 18:52:29 +020091 private final DevicePolicyReceiver mDevicePolicyReceiver = new DevicePolicyReceiver();
Adrian Roos7046bfd2014-05-16 21:20:54 +020092 private final SparseBooleanArray mUserHasAuthenticatedSinceBoot = new SparseBooleanArray();
Adrian Roos7a4f3d42014-05-02 12:12:20 +020093 /* package */ final TrustArchive mArchive = new TrustArchive();
Adrian Roos82142c22014-03-27 14:56:59 +010094 private final Context mContext;
95
96 private UserManager mUserManager;
97
98 /**
99 * Cache for {@link #refreshAgentList()}
100 */
101 private final ArraySet<AgentInfo> mObsoleteAgents = new ArraySet<AgentInfo>();
102
103
104 public TrustManagerService(Context context) {
105 super(context);
106 mContext = context;
107 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
108 }
109
110 @Override
111 public void onStart() {
112 publishBinderService(Context.TRUST_SERVICE, mService);
113 }
114
115 @Override
116 public void onBootPhase(int phase) {
117 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY && !isSafeMode()) {
Adrian Roos82142c22014-03-27 14:56:59 +0100118 mPackageMonitor.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
Adrian Roosca36b952014-05-16 18:52:29 +0200119 mDevicePolicyReceiver.register(mContext);
Adrian Roos82142c22014-03-27 14:56:59 +0100120 refreshAgentList();
121 }
122 }
123
124 // Agent management
125
126 private static final class AgentInfo {
127 CharSequence label;
128 Drawable icon;
129 ComponentName component; // service that implements ITrustAgent
130 ComponentName settings; // setting to launch to modify agent.
131 TrustAgentWrapper agent;
132 int userId;
133
134 @Override
135 public boolean equals(Object other) {
136 if (!(other instanceof AgentInfo)) {
137 return false;
138 }
139 AgentInfo o = (AgentInfo) other;
140 return component.equals(o.component) && userId == o.userId;
141 }
142
143 @Override
144 public int hashCode() {
145 return component.hashCode() * 31 + userId;
146 }
147 }
148
149 private void updateTrustAll() {
150 List<UserInfo> userInfos = mUserManager.getUsers(true /* excludeDying */);
151 for (UserInfo userInfo : userInfos) {
152 updateTrust(userInfo.id);
153 }
154 }
155
156 public void updateTrust(int userId) {
157 dispatchOnTrustChanged(aggregateIsTrusted(userId), userId);
158 }
159
160 protected void refreshAgentList() {
161 if (DEBUG) Slog.d(TAG, "refreshAgentList()");
162 PackageManager pm = mContext.getPackageManager();
163
164 List<UserInfo> userInfos = mUserManager.getUsers(true /* excludeDying */);
165 LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
166
167 mObsoleteAgents.clear();
168 mObsoleteAgents.addAll(mActiveAgents);
169
170 for (UserInfo userInfo : userInfos) {
Adrian Roosca36b952014-05-16 18:52:29 +0200171 int disabledFeatures = lockPatternUtils.getDevicePolicyManager()
172 .getKeyguardDisabledFeatures(null, userInfo.id);
173 boolean disableTrustAgents =
174 (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0;
175
Adrian Roos82142c22014-03-27 14:56:59 +0100176 List<ComponentName> enabledAgents = lockPatternUtils.getEnabledTrustAgents(userInfo.id);
Adrian Roosca36b952014-05-16 18:52:29 +0200177 if (disableTrustAgents || enabledAgents == null) {
Adrian Roos82142c22014-03-27 14:56:59 +0100178 continue;
179 }
180 List<ResolveInfo> resolveInfos = pm.queryIntentServicesAsUser(TRUST_AGENT_INTENT,
181 PackageManager.GET_META_DATA, userInfo.id);
182 for (ResolveInfo resolveInfo : resolveInfos) {
183 if (resolveInfo.serviceInfo == null) continue;
184 ComponentName name = getComponentName(resolveInfo);
185 if (!enabledAgents.contains(name)) continue;
186
187 AgentInfo agentInfo = new AgentInfo();
188 agentInfo.component = name;
189 agentInfo.userId = userInfo.id;
190 if (!mActiveAgents.contains(agentInfo)) {
191 agentInfo.label = resolveInfo.loadLabel(pm);
192 agentInfo.icon = resolveInfo.loadIcon(pm);
193 agentInfo.settings = getSettingsComponentName(pm, resolveInfo);
194 agentInfo.agent = new TrustAgentWrapper(mContext, this,
195 new Intent().setComponent(name), userInfo.getUserHandle());
196 mActiveAgents.add(agentInfo);
197 } else {
198 mObsoleteAgents.remove(agentInfo);
199 }
200 }
201 }
202
203 boolean trustMayHaveChanged = false;
204 for (int i = 0; i < mObsoleteAgents.size(); i++) {
Adrian Roos81e04662014-04-30 17:48:18 +0200205 AgentInfo info = mObsoleteAgents.valueAt(i);
Adrian Roos82142c22014-03-27 14:56:59 +0100206 if (info.agent.isTrusted()) {
207 trustMayHaveChanged = true;
208 }
209 info.agent.unbind();
Adrian Roosa5956422014-04-30 18:23:38 +0200210 mActiveAgents.remove(info);
Adrian Roos82142c22014-03-27 14:56:59 +0100211 }
212
213 if (trustMayHaveChanged) {
214 updateTrustAll();
215 }
216 }
217
218 private ComponentName getSettingsComponentName(PackageManager pm, ResolveInfo resolveInfo) {
219 if (resolveInfo == null || resolveInfo.serviceInfo == null
220 || resolveInfo.serviceInfo.metaData == null) return null;
221 String cn = null;
222 XmlResourceParser parser = null;
223 Exception caughtException = null;
224 try {
225 parser = resolveInfo.serviceInfo.loadXmlMetaData(pm,
226 TrustAgentService.TRUST_AGENT_META_DATA);
227 if (parser == null) {
228 Slog.w(TAG, "Can't find " + TrustAgentService.TRUST_AGENT_META_DATA + " meta-data");
229 return null;
230 }
231 Resources res = pm.getResourcesForApplication(resolveInfo.serviceInfo.applicationInfo);
232 AttributeSet attrs = Xml.asAttributeSet(parser);
233 int type;
234 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
235 && type != XmlPullParser.START_TAG) {
236 // Drain preamble.
237 }
238 String nodeName = parser.getName();
Adrian Roos7e03dfc2014-05-16 16:06:28 +0200239 if (!"trust-agent".equals(nodeName)) {
240 Slog.w(TAG, "Meta-data does not start with trust-agent tag");
Adrian Roos82142c22014-03-27 14:56:59 +0100241 return null;
242 }
243 TypedArray sa = res
244 .obtainAttributes(attrs, com.android.internal.R.styleable.TrustAgent);
245 cn = sa.getString(com.android.internal.R.styleable.TrustAgent_settingsActivity);
246 sa.recycle();
247 } catch (PackageManager.NameNotFoundException e) {
248 caughtException = e;
249 } catch (IOException e) {
250 caughtException = e;
251 } catch (XmlPullParserException e) {
252 caughtException = e;
253 } finally {
254 if (parser != null) parser.close();
255 }
256 if (caughtException != null) {
257 Slog.w(TAG, "Error parsing : " + resolveInfo.serviceInfo.packageName, caughtException);
258 return null;
259 }
260 if (cn == null) {
261 return null;
262 }
263 if (cn.indexOf('/') < 0) {
264 cn = resolveInfo.serviceInfo.packageName + "/" + cn;
265 }
266 return ComponentName.unflattenFromString(cn);
267 }
268
269 private ComponentName getComponentName(ResolveInfo resolveInfo) {
270 if (resolveInfo == null || resolveInfo.serviceInfo == null) return null;
271 return new ComponentName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
272 }
273
274 // Agent dispatch and aggregation
275
276 private boolean aggregateIsTrusted(int userId) {
Adrian Roos7046bfd2014-05-16 21:20:54 +0200277 if (!mUserHasAuthenticatedSinceBoot.get(userId)) {
278 return false;
279 }
Adrian Roos82142c22014-03-27 14:56:59 +0100280 for (int i = 0; i < mActiveAgents.size(); i++) {
281 AgentInfo info = mActiveAgents.valueAt(i);
282 if (info.userId == userId) {
283 if (info.agent.isTrusted()) {
284 return true;
285 }
286 }
287 }
288 return false;
289 }
290
291 private void dispatchUnlockAttempt(boolean successful, int userId) {
292 for (int i = 0; i < mActiveAgents.size(); i++) {
293 AgentInfo info = mActiveAgents.valueAt(i);
294 if (info.userId == userId) {
295 info.agent.onUnlockAttempt(successful);
296 }
297 }
Adrian Roos7046bfd2014-05-16 21:20:54 +0200298
299 if (successful && !mUserHasAuthenticatedSinceBoot.get(userId)) {
300 mUserHasAuthenticatedSinceBoot.put(userId, true);
301 updateTrust(userId);
302 }
Adrian Roos82142c22014-03-27 14:56:59 +0100303 }
304
305 // Listeners
306
307 private void addListener(ITrustListener listener) {
308 for (int i = 0; i < mTrustListeners.size(); i++) {
309 if (mTrustListeners.get(i).asBinder() == listener.asBinder()) {
310 return;
311 }
312 }
313 mTrustListeners.add(listener);
314 }
315
316 private void removeListener(ITrustListener listener) {
317 for (int i = 0; i < mTrustListeners.size(); i++) {
318 if (mTrustListeners.get(i).asBinder() == listener.asBinder()) {
319 mTrustListeners.get(i);
320 return;
321 }
322 }
323 }
324
325 private void dispatchOnTrustChanged(boolean enabled, int userId) {
326 for (int i = 0; i < mTrustListeners.size(); i++) {
327 try {
328 mTrustListeners.get(i).onTrustChanged(enabled, userId);
329 } catch (RemoteException e) {
330 Slog.e(TAG, "Exception while notifying TrustListener. Removing listener.", e);
Adrian Roos82142c22014-03-27 14:56:59 +0100331 }
332 }
333 }
334
335 // Plumbing
336
337 private final IBinder mService = new ITrustManager.Stub() {
338 @Override
339 public void reportUnlockAttempt(boolean authenticated, int userId) throws RemoteException {
340 enforceReportPermission();
341 mHandler.obtainMessage(MSG_DISPATCH_UNLOCK_ATTEMPT, authenticated ? 1 : 0, userId)
342 .sendToTarget();
343 }
344
345 @Override
346 public void reportEnabledTrustAgentsChanged(int userId) throws RemoteException {
347 enforceReportPermission();
348 // coalesce refresh messages.
349 mHandler.removeMessages(MSG_ENABLED_AGENTS_CHANGED);
350 mHandler.sendEmptyMessage(MSG_ENABLED_AGENTS_CHANGED);
351 }
352
353 @Override
354 public void registerTrustListener(ITrustListener trustListener) throws RemoteException {
355 enforceListenerPermission();
356 mHandler.obtainMessage(MSG_REGISTER_LISTENER, trustListener).sendToTarget();
357 }
358
359 @Override
360 public void unregisterTrustListener(ITrustListener trustListener) throws RemoteException {
361 enforceListenerPermission();
362 mHandler.obtainMessage(MSG_UNREGISTER_LISTENER, trustListener).sendToTarget();
363 }
364
365 private void enforceReportPermission() {
366 mContext.enforceCallingPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE,
367 "reporting trust events");
368 }
369
370 private void enforceListenerPermission() {
371 mContext.enforceCallingPermission(Manifest.permission.TRUST_LISTENER,
372 "register trust listener");
373 }
Adrian Roos7a4f3d42014-05-02 12:12:20 +0200374
375 @Override
376 protected void dump(FileDescriptor fd, final PrintWriter fout, String[] args) {
377 mContext.enforceCallingPermission(Manifest.permission.DUMP,
378 "dumping TrustManagerService");
379 final UserInfo currentUser;
380 final List<UserInfo> userInfos = mUserManager.getUsers(true /* excludeDying */);
381 try {
382 currentUser = ActivityManagerNative.getDefault().getCurrentUser();
383 } catch (RemoteException e) {
384 throw new RuntimeException(e);
385 }
386 mHandler.runWithScissors(new Runnable() {
387 @Override
388 public void run() {
389 fout.println("Trust manager state:");
390 for (UserInfo user : userInfos) {
391 dumpUser(fout, user, user.id == currentUser.id);
392 }
393 }
394 }, 1500);
395 }
396
397 private void dumpUser(PrintWriter fout, UserInfo user, boolean isCurrent) {
398 fout.printf(" User \"%s\" (id=%d, flags=%#x)",
399 user.name, user.id, user.flags);
400 if (isCurrent) {
401 fout.print(" (current)");
402 }
403 fout.print(": trusted=" + dumpBool(aggregateIsTrusted(user.id)));
404 fout.println();
405 fout.println(" Enabled agents:");
406 boolean duplicateSimpleNames = false;
407 ArraySet<String> simpleNames = new ArraySet<String>();
408 for (AgentInfo info : mActiveAgents) {
409 if (info.userId != user.id) { continue; }
410 boolean trusted = info.agent.isTrusted();
411 fout.print(" "); fout.println(info.component.flattenToShortString());
412 fout.print(" connected=" + dumpBool(info.agent.isConnected()));
413 fout.println(", trusted=" + dumpBool(trusted));
414 if (trusted) {
415 fout.println(" message=\"" + info.agent.getMessage() + "\"");
416 }
417 if (!simpleNames.add(TrustArchive.getSimpleName(info.component))) {
418 duplicateSimpleNames = true;
419 }
420 }
421 fout.println(" Events:");
422 mArchive.dump(fout, 50, user.id, " " /* linePrefix */, duplicateSimpleNames);
423 fout.println();
424 }
425
426 private String dumpBool(boolean b) {
427 return b ? "1" : "0";
428 }
Adrian Roos82142c22014-03-27 14:56:59 +0100429 };
430
431 private final Handler mHandler = new Handler() {
432 @Override
433 public void handleMessage(Message msg) {
434 switch (msg.what) {
435 case MSG_REGISTER_LISTENER:
436 addListener((ITrustListener) msg.obj);
437 break;
438 case MSG_UNREGISTER_LISTENER:
439 removeListener((ITrustListener) msg.obj);
440 break;
441 case MSG_DISPATCH_UNLOCK_ATTEMPT:
442 dispatchUnlockAttempt(msg.arg1 != 0, msg.arg2);
443 break;
444 case MSG_ENABLED_AGENTS_CHANGED:
445 refreshAgentList();
446 break;
447 }
448 }
449 };
450
451 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
452 @Override
453 public void onSomePackagesChanged() {
454 refreshAgentList();
455 }
456
457 @Override
458 public boolean onPackageChanged(String packageName, int uid, String[] components) {
459 // We're interested in all changes, even if just some components get enabled / disabled.
460 return true;
461 }
462 };
Adrian Roosca36b952014-05-16 18:52:29 +0200463
464 private class DevicePolicyReceiver extends BroadcastReceiver {
465
466 @Override
467 public void onReceive(Context context, Intent intent) {
468 if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(
469 intent.getAction())) {
470 refreshAgentList();
471 }
472 }
473
474 public void register(Context context) {
475 context.registerReceiverAsUser(this,
476 UserHandle.ALL,
477 new IntentFilter(
478 DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
479 null /* permission */,
480 null /* scheduler */);
481 }
482 }
Adrian Roos82142c22014-03-27 14:56:59 +0100483}