From: Zoltan Szatmary-Ban Date: Wed, 2 Jul 2014 15:48:00 +0000 (+0100) Subject: Update Device Admins Settings screen X-Git-Tag: android-x86-6.0-r1~860^2~634 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=f3d72098;p=android-x86%2Fpackages-apps-Settings.git Update Device Admins Settings screen The MDM is a Device Admin for the managed profile. The users should be able to see that it exists and it is controlling the device, but they should not be able to change it. Bug: 15558397 Change-Id: Ief372b606f1085f73c98ace97f2c1d9ac5fe3257 --- diff --git a/res/values/strings.xml b/res/values/strings.xml index 6189781a4f..d802ba2c14 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4382,6 +4382,12 @@ Device administrators No available device administrators + + To stop %1$s from accessing your work profile, remove the profile under Settings > Accounts. + + Personal + + Work No available trust agents diff --git a/src/com/android/settings/DeviceAdminSettings.java b/src/com/android/settings/DeviceAdminSettings.java index f597b9a75c..1ea98590d1 100644 --- a/src/com/android/settings/DeviceAdminSettings.java +++ b/src/com/android/settings/DeviceAdminSettings.java @@ -20,6 +20,8 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.app.Activity; +import android.app.Dialog; +import android.app.AlertDialog; import android.app.ListFragment; import android.app.admin.DeviceAdminInfo; import android.app.admin.DeviceAdminReceiver; @@ -30,8 +32,13 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.UserHandle; +import android.os.UserManager; import android.util.Log; +import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -44,20 +51,25 @@ import android.widget.TextView; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Set; +import java.util.Collection; public class DeviceAdminSettings extends ListFragment { static final String TAG = "DeviceAdminSettings"; static final int DIALOG_WARNING = 1; + private DevicePolicyManager mDPM; + private UserManager mUm; - DevicePolicyManager mDPM; - final HashSet mActiveAdmins = new HashSet(); - final ArrayList mAvailableAdmins = new ArrayList(); - String mDeviceOwnerPkg; - ComponentName mProfileOwnerComponent; + /** + * Internal collection of device admin info objects for all profiles associated with the current + * user. + */ + private final SparseArray> + mAdminsByProfile = new SparseArray>(); + + private String mDeviceOwnerPkg; + private SparseArray mProfileOwnerComponents = new SparseArray(); @Override public void onCreate(Bundle icicle) { @@ -68,6 +80,7 @@ public class DeviceAdminSettings extends ListFragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mDPM = (DevicePolicyManager) getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE); + mUm = (UserManager) getActivity().getSystemService(Context.USER_SERVICE); return inflater.inflate(R.layout.device_admin_settings, container, false); } @@ -85,61 +98,28 @@ public class DeviceAdminSettings extends ListFragment { if (mDeviceOwnerPkg != null && !mDPM.isDeviceOwner(mDeviceOwnerPkg)) { mDeviceOwnerPkg = null; } - mProfileOwnerComponent = mDPM.getProfileOwner(); + mProfileOwnerComponents.clear(); + final List profiles = mUm.getUserProfiles(); + final int profilesSize = profiles.size(); + for (int i = 0; i < profilesSize; ++i) { + final int profileId = profiles.get(i).getIdentifier(); + mProfileOwnerComponents.put(profileId, mDPM.getProfileOwnerAsUser(profileId)); + } updateList(); } + /** + * Update the internal collection of available admins for all profiles associated with the + * current user. + */ void updateList() { - mActiveAdmins.clear(); - List cur = mDPM.getActiveAdmins(); - if (cur != null) { - for (int i=0; i avail = getActivity().getPackageManager().queryBroadcastReceivers( - new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED), - PackageManager.GET_META_DATA | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS); - if (avail == null) { - avail = Collections.emptyList(); - } - - // Some admins listed in mActiveAdmins may not have been found by the above query. - // We thus add them separately. - Set activeAdminsNotInAvail = new HashSet(mActiveAdmins); - for (ResolveInfo ri : avail) { - ComponentName riComponentName = - new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name); - activeAdminsNotInAvail.remove(riComponentName); - } - if (!activeAdminsNotInAvail.isEmpty()) { - avail = new ArrayList(avail); - PackageManager packageManager = getActivity().getPackageManager(); - for (ComponentName unlistedActiveAdmin : activeAdminsNotInAvail) { - List resolved = packageManager.queryBroadcastReceivers( - new Intent().setComponent(unlistedActiveAdmin), - PackageManager.GET_META_DATA - | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS); - if (resolved != null) { - avail.addAll(resolved); - } - } - } + mAdminsByProfile.clear(); - for (int i = 0, count = avail.size(); i < count; i++) { - ResolveInfo ri = avail.get(i); - try { - DeviceAdminInfo dpi = new DeviceAdminInfo(getActivity(), ri); - if (dpi.isVisible() || mActiveAdmins.contains(dpi.getComponent())) { - mAvailableAdmins.add(dpi); - } - } catch (XmlPullParserException e) { - Log.w(TAG, "Skipping " + ri.activityInfo, e); - } catch (IOException e) { - Log.w(TAG, "Skipping " + ri.activityInfo, e); - } + final List profiles = mUm.getUserProfiles(); + final int profilesSize = profiles.size(); + for (int i = 0; i < profilesSize; ++i) { + final int profileId = profiles.get(i).getIdentifier(); + updateAvailableAdminsForProfile(profileId); } getListView().setAdapter(new PolicyListAdapter()); @@ -147,11 +127,21 @@ public class DeviceAdminSettings extends ListFragment { @Override public void onListItemClick(ListView l, View v, int position, long id) { - DeviceAdminInfo dpi = (DeviceAdminInfo)l.getAdapter().getItem(position); - Intent intent = new Intent(); - intent.setClass(getActivity(), DeviceAdminAdd.class); - intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, dpi.getComponent()); - startActivity(intent); + DeviceAdminInfo dpi = (DeviceAdminInfo) l.getAdapter().getItem(position); + final Activity activity = getActivity(); + final int userId = getUserId(dpi); + if (userId == UserHandle.myUserId() || !isProfileOwner(dpi)) { + Intent intent = new Intent(); + intent.setClass(activity, DeviceAdminAdd.class); + intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, dpi.getComponent()); + activity.startActivityAsUser(intent, new UserHandle(userId)); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setMessage(getString(R.string.managed_profile_device_admin_info, + dpi.loadLabel(activity.getPackageManager()))); + builder.setPositiveButton(android.R.string.ok, null); + builder.create().show(); + } } static class ViewHolder { @@ -169,51 +159,129 @@ public class DeviceAdminSettings extends ListFragment { getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); } + @Override public boolean hasStableIds() { - return true; + return false; } + @Override public int getCount() { - return mAvailableAdmins.size(); + int adminCount = 0; + final int profileCount = mAdminsByProfile.size(); + for (int i = 0; i < profileCount; ++i) { + adminCount += mAdminsByProfile.valueAt(i).size(); + } + // Add 'profileCount' for title items. + return adminCount + profileCount; } + /** + * The item for the given position in the list. + * + * @return a String object for title items and a DeviceAdminInfo object for actual device + * admins. + */ + @Override public Object getItem(int position) { - return mAvailableAdmins.get(position); + if (position < 0) { + throw new ArrayIndexOutOfBoundsException(); + } + // The position of the item in the list of admins. + // We start from the given position and discount the length of the upper lists until we + // get the one for the right profile + int adminPosition = position; + final int n = mAdminsByProfile.size(); + int i = 0; + for (; i < n; ++i) { + // The elements in that section including the title item (that's why adding one). + final int listSize = mAdminsByProfile.valueAt(i).size() + 1; + if (adminPosition < listSize) { + break; + } + adminPosition -= listSize; + } + if (i == n) { + throw new ArrayIndexOutOfBoundsException(); + } + // If countdown == 0 that means the title item + if (adminPosition == 0) { + Resources res = getActivity().getResources(); + if (mAdminsByProfile.keyAt(i) == UserHandle.myUserId()) { + return res.getString(R.string.personal_device_admin_title); + } else { + return res.getString(R.string.managed_device_admin_title); + } + } else { + // Subtracting one for the title. + return mAdminsByProfile.valueAt(i).get(adminPosition - 1); + } } + @Override public long getItemId(int position) { return position; } + @Override public boolean areAllItemsEnabled() { return false; } + /** + * See {@link #getItemViewType} for the view types. + */ + @Override + public int getViewTypeCount() { + return 2; + } + + /** + * Returns 1 for title items and 0 for anything else. + */ + @Override + public int getItemViewType(int position) { + Object o = getItem(position); + return (o instanceof String) ? 1 : 0; + } + + @Override public boolean isEnabled(int position) { - DeviceAdminInfo info = mAvailableAdmins.get(position); - String packageName = info.getPackageName(); - ComponentName component = info.getComponent(); - if (mActiveAdmins.contains(component) - && (packageName.equals(mDeviceOwnerPkg) - || component.equals(mProfileOwnerComponent))) { + Object o = getItem(position); + return isEnabled(o); + } + + private boolean isEnabled(Object o) { + if (!(o instanceof DeviceAdminInfo)) { + // Title item + return false; + } + DeviceAdminInfo info = (DeviceAdminInfo) o; + if (isActiveAdmin(info) && getUserId(info) == UserHandle.myUserId() + && (isDeviceOwner(info) || isProfileOwner(info))) { return false; - } else { - return true; } + return true; } + @Override public View getView(int position, View convertView, ViewGroup parent) { - View v; - if (convertView == null) { - v = newView(parent); + Object o = getItem(position); + if (o instanceof DeviceAdminInfo) { + if (convertView == null) { + convertView = newDeviceAdminView(parent); + } + bindView(convertView, (DeviceAdminInfo) o); } else { - v = convertView; + if (convertView == null) { + convertView = newTitleView(parent); + } + final TextView title = (TextView) convertView.findViewById(android.R.id.title); + title.setText((String)o); } - bindView(v, position); - return v; + return convertView; } - public View newView(ViewGroup parent) { + private View newDeviceAdminView(ViewGroup parent) { View v = mInflater.inflate(R.layout.device_admin_item, parent, false); ViewHolder h = new ViewHolder(); h.icon = (ImageView)v.findViewById(R.id.icon); @@ -224,23 +292,169 @@ public class DeviceAdminSettings extends ListFragment { return v; } - public void bindView(View view, int position) { + private View newTitleView(ViewGroup parent) { + final TypedArray a = mInflater.getContext().obtainStyledAttributes(null, + com.android.internal.R.styleable.Preference, + com.android.internal.R.attr.preferenceCategoryStyle, 0); + final int resId = a.getResourceId(com.android.internal.R.styleable.Preference_layout, + 0); + return mInflater.inflate(resId, parent, false); + } + + private void bindView(View view, DeviceAdminInfo item) { final Activity activity = getActivity(); ViewHolder vh = (ViewHolder) view.getTag(); - DeviceAdminInfo item = mAvailableAdmins.get(position); - vh.icon.setImageDrawable(item.loadIcon(activity.getPackageManager())); + Drawable activityIcon = item.loadIcon(activity.getPackageManager()); + Drawable badgedIcon = mUm.getBadgedDrawableForUser(activityIcon, + new UserHandle(getUserId(item))); + vh.icon.setImageDrawable(badgedIcon); vh.name.setText(item.loadLabel(activity.getPackageManager())); - vh.checkbox.setChecked(mActiveAdmins.contains(item.getComponent())); - final boolean activeOwner = vh.checkbox.isChecked() - && item.getPackageName().equals(mDeviceOwnerPkg); + vh.checkbox.setChecked(isActiveAdmin(item)); + final boolean enabled = isEnabled(item); try { vh.description.setText(item.loadDescription(activity.getPackageManager())); } catch (Resources.NotFoundException e) { } - vh.checkbox.setEnabled(!activeOwner); - vh.name.setEnabled(!activeOwner); - vh.description.setEnabled(!activeOwner); - vh.icon.setEnabled(!activeOwner); + vh.checkbox.setEnabled(enabled); + vh.name.setEnabled(enabled); + vh.description.setEnabled(enabled); + vh.icon.setEnabled(enabled); + } + } + + private boolean isDeviceOwner(DeviceAdminInfo item) { + return getUserId(item) == UserHandle.myUserId() + && item.getPackageName().equals(mDeviceOwnerPkg); + } + + private boolean isProfileOwner(DeviceAdminInfo item) { + ComponentName profileOwner = mProfileOwnerComponents.get(getUserId(item)); + return item.getComponent().equals(profileOwner); + } + + private boolean isActiveAdmin(DeviceAdminInfo item) { + return mDPM.isAdminActiveAsUser(item.getComponent(), getUserId(item)); + } + + /** + * Add device admins to the internal collection that belong to a profile. + * + * @param profileId the profile identifier. + */ + private void updateAvailableAdminsForProfile(final int profileId) { + // We are adding the union of two sets 'A' and 'B' of device admins to mAvailableAdmins. + // Set 'A' is the set of active admins for the profile whereas set 'B' is the set of + // listeners to DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED for the profile. + + // Add all of set 'A' to mAvailableAdmins. + List activeAdminsListForProfile = mDPM.getActiveAdminsAsUser(profileId); + addActiveAdminsForProfile(activeAdminsListForProfile, profileId); + + // Collect set 'B' and add B-A to mAvailableAdmins. + addDeviceAdminBroadcastReceiversForProfile(activeAdminsListForProfile, profileId); + } + + /** + * Add a profile's device admins that are receivers of + * {@code DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED} to the internal collection if they + * haven't been added yet. + * + * @param alreadyAddedComponents the set of active admin component names. Receivers of + * {@code DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED} whose component is in this + * set are not added to the internal collection again. + * @param profileId the identifier of the profile + */ + private void addDeviceAdminBroadcastReceiversForProfile( + Collection alreadyAddedComponents, final int profileId) { + final PackageManager pm = getActivity().getPackageManager(); + List enabledForProfile = pm.queryBroadcastReceivers( + new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED), + PackageManager.GET_META_DATA | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, + profileId); + if (enabledForProfile == null) { + enabledForProfile = Collections.emptyList(); + } + final int n = enabledForProfile.size(); + ArrayList deviceAdmins = mAdminsByProfile.get(profileId); + if (deviceAdmins == null) { + deviceAdmins = new ArrayList(n); + } + for (int i = 0; i < n; ++i) { + ResolveInfo resolveInfo = enabledForProfile.get(i); + ComponentName riComponentName = + new ComponentName(resolveInfo.activityInfo.packageName, + resolveInfo.activityInfo.name); + if (alreadyAddedComponents == null + || !alreadyAddedComponents.contains(riComponentName)) { + DeviceAdminInfo deviceAdminInfo = createDeviceAdminInfo(resolveInfo); + // add only visible ones (note: active admins are added regardless of visibility) + if (deviceAdminInfo != null && deviceAdminInfo.isVisible()) { + deviceAdmins.add(deviceAdminInfo); + } + } } + if (!deviceAdmins.isEmpty()) { + mAdminsByProfile.put(profileId, deviceAdmins); + } + } + + /** + * Add a {@link DeviceAdminInfo} object to the internal collection of available admins for all + * active admin components associated with a profile. + * + * @param profileId a profile identifier. + */ + private void addActiveAdminsForProfile(final List activeAdmins, + final int profileId) { + if (activeAdmins != null) { + final PackageManager packageManager = getActivity().getPackageManager(); + final int n = activeAdmins.size(); + ArrayList deviceAdmins = new ArrayList(n); + for (int i = 0; i < n; ++i) { + ComponentName activeAdmin = activeAdmins.get(i); + List resolved = packageManager.queryBroadcastReceivers( + new Intent().setComponent(activeAdmin), PackageManager.GET_META_DATA + | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, profileId); + if (resolved != null) { + final int resolvedMax = resolved.size(); + for (int j = 0; j < resolvedMax; ++j) { + DeviceAdminInfo deviceAdminInfo = createDeviceAdminInfo(resolved.get(j)); + if (deviceAdminInfo != null) { + deviceAdmins.add(deviceAdminInfo); + } + } + } + } + if (!deviceAdmins.isEmpty()) { + mAdminsByProfile.put(profileId, deviceAdmins); + } + } + } + + /** + * Creates a device admin info object for the resolved intent that points to the component of + * the device admin. + * + * @param resolved resolved intent. + * @return new {@link DeviceAdminInfo} object or null if there was an error. + */ + private DeviceAdminInfo createDeviceAdminInfo(ResolveInfo resolved) { + try { + return new DeviceAdminInfo(getActivity(), resolved); + } catch (XmlPullParserException e) { + Log.w(TAG, "Skipping " + resolved.activityInfo, e); + } catch (IOException e) { + Log.w(TAG, "Skipping " + resolved.activityInfo, e); + } + return null; + } + + /** + * Extracts the user id from a device admin info object. + * @param adminInfo the device administrator info. + * @return identifier of the user associated with the device admin. + */ + private int getUserId(DeviceAdminInfo adminInfo) { + return UserHandle.getUserId(adminInfo.getActivityInfo().applicationInfo.uid); } }