2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings;
19 import org.xmlpull.v1.XmlPullParserException;
21 import android.app.Activity;
22 import android.app.AlertDialog;
23 import android.app.ListFragment;
24 import android.app.admin.DeviceAdminInfo;
25 import android.app.admin.DeviceAdminReceiver;
26 import android.app.admin.DevicePolicyManager;
27 import android.content.BroadcastReceiver;
28 import android.content.ComponentName;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.IntentFilter;
32 import android.content.pm.PackageManager;
33 import android.content.pm.ResolveInfo;
34 import android.content.res.Resources;
35 import android.content.res.TypedArray;
36 import android.graphics.drawable.Drawable;
37 import android.os.Bundle;
38 import android.os.UserHandle;
39 import android.os.UserManager;
40 import android.util.Log;
41 import android.util.SparseArray;
42 import android.view.LayoutInflater;
43 import android.view.View;
44 import android.view.ViewGroup;
45 import android.widget.BaseAdapter;
46 import android.widget.CheckBox;
47 import android.widget.ImageView;
48 import android.widget.ListView;
49 import android.widget.TextView;
51 import java.io.IOException;
52 import java.util.ArrayList;
53 import java.util.Collection;
54 import java.util.Collections;
55 import java.util.List;
57 public class DeviceAdminSettings extends ListFragment {
58 static final String TAG = "DeviceAdminSettings";
60 private DevicePolicyManager mDPM;
61 private UserManager mUm;
64 * Internal collection of device admin info objects for all profiles associated with the current
67 private final SparseArray<ArrayList<DeviceAdminInfo>>
68 mAdminsByProfile = new SparseArray<ArrayList<DeviceAdminInfo>>();
70 private String mDeviceOwnerPkg;
71 private SparseArray<ComponentName> mProfileOwnerComponents = new SparseArray<ComponentName>();
73 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
75 public void onReceive(Context context, Intent intent) {
76 // Refresh the list, if state change has been received. It could be that checkboxes
78 if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(
79 intent.getAction())) {
86 public void onCreate(Bundle icicle) {
87 super.onCreate(icicle);
91 public View onCreateView(LayoutInflater inflater, ViewGroup container,
92 Bundle savedInstanceState) {
93 mDPM = (DevicePolicyManager) getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
94 mUm = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
95 return inflater.inflate(R.layout.device_admin_settings, container, false);
99 public void onActivityCreated(Bundle savedInstanceState) {
100 super.onActivityCreated(savedInstanceState);
102 Utils.forceCustomPadding(getListView(), true /* additive padding */);
106 public void onResume() {
108 IntentFilter filter = new IntentFilter();
109 filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
110 getActivity().registerReceiverAsUser(
111 mBroadcastReceiver, UserHandle.ALL, filter, null, null);
112 mDeviceOwnerPkg = mDPM.getDeviceOwner();
113 if (mDeviceOwnerPkg != null && !mDPM.isDeviceOwner(mDeviceOwnerPkg)) {
114 mDeviceOwnerPkg = null;
116 mProfileOwnerComponents.clear();
117 final List<UserHandle> profiles = mUm.getUserProfiles();
118 final int profilesSize = profiles.size();
119 for (int i = 0; i < profilesSize; ++i) {
120 final int profileId = profiles.get(i).getIdentifier();
121 mProfileOwnerComponents.put(profileId, mDPM.getProfileOwnerAsUser(profileId));
127 public void onPause() {
128 getActivity().unregisterReceiver(mBroadcastReceiver);
133 * Update the internal collection of available admins for all profiles associated with the
137 mAdminsByProfile.clear();
139 final List<UserHandle> profiles = mUm.getUserProfiles();
140 final int profilesSize = profiles.size();
141 for (int i = 0; i < profilesSize; ++i) {
142 final int profileId = profiles.get(i).getIdentifier();
143 updateAvailableAdminsForProfile(profileId);
146 getListView().setAdapter(new PolicyListAdapter());
150 public void onListItemClick(ListView l, View v, int position, long id) {
151 Object o = l.getAdapter().getItem(position);
152 if (!(o instanceof DeviceAdminInfo)) {
153 // race conditions may cause this
156 DeviceAdminInfo dpi = (DeviceAdminInfo) o;
157 final Activity activity = getActivity();
158 final int userId = getUserId(dpi);
159 if (userId == UserHandle.myUserId() || !isProfileOwner(dpi)) {
160 Intent intent = new Intent();
161 intent.setClass(activity, DeviceAdminAdd.class);
162 intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, dpi.getComponent());
163 activity.startActivityAsUser(intent, new UserHandle(userId));
165 AlertDialog.Builder builder = new AlertDialog.Builder(activity);
166 builder.setMessage(getString(R.string.managed_profile_device_admin_info,
167 dpi.loadLabel(activity.getPackageManager())));
168 builder.setPositiveButton(android.R.string.ok, null);
169 builder.create().show();
173 static class ViewHolder {
177 TextView description;
180 class PolicyListAdapter extends BaseAdapter {
181 final LayoutInflater mInflater;
183 PolicyListAdapter() {
184 mInflater = (LayoutInflater)
185 getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
189 public boolean hasStableIds() {
194 public int getCount() {
196 final int profileCount = mAdminsByProfile.size();
197 for (int i = 0; i < profileCount; ++i) {
198 adminCount += mAdminsByProfile.valueAt(i).size();
200 // Add 'profileCount' for title items.
201 return adminCount + profileCount;
205 * The item for the given position in the list.
207 * @return a String object for title items and a DeviceAdminInfo object for actual device
211 public Object getItem(int position) {
213 throw new ArrayIndexOutOfBoundsException();
215 // The position of the item in the list of admins.
216 // We start from the given position and discount the length of the upper lists until we
217 // get the one for the right profile
218 int adminPosition = position;
219 final int n = mAdminsByProfile.size();
222 // The elements in that section including the title item (that's why adding one).
223 final int listSize = mAdminsByProfile.valueAt(i).size() + 1;
224 if (adminPosition < listSize) {
227 adminPosition -= listSize;
230 throw new ArrayIndexOutOfBoundsException();
232 // If countdown == 0 that means the title item
233 if (adminPosition == 0) {
234 Resources res = getActivity().getResources();
235 if (mAdminsByProfile.keyAt(i) == UserHandle.myUserId()) {
236 return res.getString(R.string.personal_device_admin_title);
238 return res.getString(R.string.managed_device_admin_title);
241 // Subtracting one for the title.
242 return mAdminsByProfile.valueAt(i).get(adminPosition - 1);
247 public long getItemId(int position) {
252 public boolean areAllItemsEnabled() {
257 * See {@link #getItemViewType} for the view types.
260 public int getViewTypeCount() {
265 * Returns 1 for title items and 0 for anything else.
268 public int getItemViewType(int position) {
269 Object o = getItem(position);
270 return (o instanceof String) ? 1 : 0;
274 public boolean isEnabled(int position) {
275 Object o = getItem(position);
279 private boolean isEnabled(Object o) {
280 if (!(o instanceof DeviceAdminInfo)) {
284 DeviceAdminInfo info = (DeviceAdminInfo) o;
285 if (isActiveAdmin(info) && getUserId(info) == UserHandle.myUserId()
286 && (isDeviceOwner(info) || isProfileOwner(info))) {
289 // Disable item if admin is being removed
290 if (isRemovingAdmin(info)) {
297 public View getView(int position, View convertView, ViewGroup parent) {
298 Object o = getItem(position);
299 if (o instanceof DeviceAdminInfo) {
300 if (convertView == null) {
301 convertView = newDeviceAdminView(parent);
303 bindView(convertView, (DeviceAdminInfo) o);
305 if (convertView == null) {
306 convertView = newTitleView(parent);
308 final TextView title = (TextView) convertView.findViewById(android.R.id.title);
309 title.setText((String)o);
314 private View newDeviceAdminView(ViewGroup parent) {
315 View v = mInflater.inflate(R.layout.device_admin_item, parent, false);
316 ViewHolder h = new ViewHolder();
317 h.icon = (ImageView)v.findViewById(R.id.icon);
318 h.name = (TextView)v.findViewById(R.id.name);
319 h.checkbox = (CheckBox)v.findViewById(R.id.checkbox);
320 h.description = (TextView)v.findViewById(R.id.description);
325 private View newTitleView(ViewGroup parent) {
326 final TypedArray a = mInflater.getContext().obtainStyledAttributes(null,
327 com.android.internal.R.styleable.Preference,
328 com.android.internal.R.attr.preferenceCategoryStyle, 0);
329 final int resId = a.getResourceId(com.android.internal.R.styleable.Preference_layout,
331 return mInflater.inflate(resId, parent, false);
334 private void bindView(View view, DeviceAdminInfo item) {
335 final Activity activity = getActivity();
336 ViewHolder vh = (ViewHolder) view.getTag();
337 Drawable activityIcon = item.loadIcon(activity.getPackageManager());
338 Drawable badgedIcon = activity.getPackageManager().getUserBadgedIcon(
339 activityIcon, new UserHandle(getUserId(item)));
340 vh.icon.setImageDrawable(badgedIcon);
341 vh.name.setText(item.loadLabel(activity.getPackageManager()));
342 vh.checkbox.setChecked(isActiveAdmin(item));
343 final boolean enabled = isEnabled(item);
345 vh.description.setText(item.loadDescription(activity.getPackageManager()));
346 } catch (Resources.NotFoundException e) {
348 vh.checkbox.setEnabled(enabled);
349 vh.name.setEnabled(enabled);
350 vh.description.setEnabled(enabled);
351 vh.icon.setEnabled(enabled);
355 private boolean isDeviceOwner(DeviceAdminInfo item) {
356 return getUserId(item) == UserHandle.myUserId()
357 && item.getPackageName().equals(mDeviceOwnerPkg);
360 private boolean isProfileOwner(DeviceAdminInfo item) {
361 ComponentName profileOwner = mProfileOwnerComponents.get(getUserId(item));
362 return item.getComponent().equals(profileOwner);
365 private boolean isActiveAdmin(DeviceAdminInfo item) {
366 return mDPM.isAdminActiveAsUser(item.getComponent(), getUserId(item));
369 private boolean isRemovingAdmin(DeviceAdminInfo item) {
370 return mDPM.isRemovingAdmin(item.getComponent(), getUserId(item));
374 * Add device admins to the internal collection that belong to a profile.
376 * @param profileId the profile identifier.
378 private void updateAvailableAdminsForProfile(final int profileId) {
379 // We are adding the union of two sets 'A' and 'B' of device admins to mAvailableAdmins.
380 // Set 'A' is the set of active admins for the profile whereas set 'B' is the set of
381 // listeners to DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED for the profile.
383 // Add all of set 'A' to mAvailableAdmins.
384 List<ComponentName> activeAdminsListForProfile = mDPM.getActiveAdminsAsUser(profileId);
385 addActiveAdminsForProfile(activeAdminsListForProfile, profileId);
387 // Collect set 'B' and add B-A to mAvailableAdmins.
388 addDeviceAdminBroadcastReceiversForProfile(activeAdminsListForProfile, profileId);
392 * Add a profile's device admins that are receivers of
393 * {@code DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED} to the internal collection if they
394 * haven't been added yet.
396 * @param alreadyAddedComponents the set of active admin component names. Receivers of
397 * {@code DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED} whose component is in this
398 * set are not added to the internal collection again.
399 * @param profileId the identifier of the profile
401 private void addDeviceAdminBroadcastReceiversForProfile(
402 Collection<ComponentName> alreadyAddedComponents, final int profileId) {
403 final PackageManager pm = getActivity().getPackageManager();
404 List<ResolveInfo> enabledForProfile = pm.queryBroadcastReceivers(
405 new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED),
406 PackageManager.GET_META_DATA | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
408 if (enabledForProfile == null) {
409 enabledForProfile = Collections.emptyList();
411 final int n = enabledForProfile.size();
412 ArrayList<DeviceAdminInfo> deviceAdmins = mAdminsByProfile.get(profileId);
413 if (deviceAdmins == null) {
414 deviceAdmins = new ArrayList<DeviceAdminInfo>(n);
416 for (int i = 0; i < n; ++i) {
417 ResolveInfo resolveInfo = enabledForProfile.get(i);
418 ComponentName riComponentName =
419 new ComponentName(resolveInfo.activityInfo.packageName,
420 resolveInfo.activityInfo.name);
421 if (alreadyAddedComponents == null
422 || !alreadyAddedComponents.contains(riComponentName)) {
423 DeviceAdminInfo deviceAdminInfo = createDeviceAdminInfo(resolveInfo);
424 // add only visible ones (note: active admins are added regardless of visibility)
425 if (deviceAdminInfo != null && deviceAdminInfo.isVisible()) {
426 deviceAdmins.add(deviceAdminInfo);
430 if (!deviceAdmins.isEmpty()) {
431 mAdminsByProfile.put(profileId, deviceAdmins);
436 * Add a {@link DeviceAdminInfo} object to the internal collection of available admins for all
437 * active admin components associated with a profile.
439 * @param profileId a profile identifier.
441 private void addActiveAdminsForProfile(final List<ComponentName> activeAdmins,
442 final int profileId) {
443 if (activeAdmins != null) {
444 final PackageManager packageManager = getActivity().getPackageManager();
445 final int n = activeAdmins.size();
446 ArrayList<DeviceAdminInfo> deviceAdmins = new ArrayList<DeviceAdminInfo>(n);
447 for (int i = 0; i < n; ++i) {
448 ComponentName activeAdmin = activeAdmins.get(i);
449 List<ResolveInfo> resolved = packageManager.queryBroadcastReceivers(
450 new Intent().setComponent(activeAdmin), PackageManager.GET_META_DATA
451 | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, profileId);
452 if (resolved != null) {
453 final int resolvedMax = resolved.size();
454 for (int j = 0; j < resolvedMax; ++j) {
455 DeviceAdminInfo deviceAdminInfo = createDeviceAdminInfo(resolved.get(j));
456 if (deviceAdminInfo != null) {
457 deviceAdmins.add(deviceAdminInfo);
462 if (!deviceAdmins.isEmpty()) {
463 mAdminsByProfile.put(profileId, deviceAdmins);
469 * Creates a device admin info object for the resolved intent that points to the component of
472 * @param resolved resolved intent.
473 * @return new {@link DeviceAdminInfo} object or null if there was an error.
475 private DeviceAdminInfo createDeviceAdminInfo(ResolveInfo resolved) {
477 return new DeviceAdminInfo(getActivity(), resolved);
478 } catch (XmlPullParserException e) {
479 Log.w(TAG, "Skipping " + resolved.activityInfo, e);
480 } catch (IOException e) {
481 Log.w(TAG, "Skipping " + resolved.activityInfo, e);
487 * Extracts the user id from a device admin info object.
488 * @param adminInfo the device administrator info.
489 * @return identifier of the user associated with the device admin.
491 private int getUserId(DeviceAdminInfo adminInfo) {
492 return UserHandle.getUserId(adminInfo.getActivityInfo().applicationInfo.uid);