Home | History | Annotate | Download | only in settings
      1 /*
      2  * Copyright (C) 2010 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;
     18 
     19 import org.xmlpull.v1.XmlPullParser;
     20 import org.xmlpull.v1.XmlPullParserException;
     21 
     22 import android.app.Activity;
     23 import android.app.AlertDialog;
     24 import android.app.ListFragment;
     25 import android.app.admin.DeviceAdminInfo;
     26 import android.app.admin.DeviceAdminReceiver;
     27 import android.app.admin.DevicePolicyManager;
     28 import android.content.ComponentName;
     29 import android.content.Context;
     30 import android.content.Intent;
     31 import android.content.pm.PackageManager;
     32 import android.content.pm.ResolveInfo;
     33 import android.content.res.Resources;
     34 import android.content.res.TypedArray;
     35 import android.graphics.drawable.Drawable;
     36 import android.os.Bundle;
     37 import android.os.UserHandle;
     38 import android.os.UserManager;
     39 import android.util.Log;
     40 import android.util.SparseArray;
     41 import android.view.LayoutInflater;
     42 import android.view.View;
     43 import android.view.ViewGroup;
     44 import android.widget.BaseAdapter;
     45 import android.widget.CheckBox;
     46 import android.widget.ImageView;
     47 import android.widget.ListView;
     48 import android.widget.TextView;
     49 
     50 import java.io.IOException;
     51 import java.util.ArrayList;
     52 import java.util.Collections;
     53 import java.util.List;
     54 import java.util.Collection;
     55 
     56 public class DeviceAdminSettings extends ListFragment {
     57     static final String TAG = "DeviceAdminSettings";
     58 
     59     static final int DIALOG_WARNING = 1;
     60     private DevicePolicyManager mDPM;
     61     private UserManager mUm;
     62 
     63     /**
     64      * Internal collection of device admin info objects for all profiles associated with the current
     65      * user.
     66      */
     67     private final SparseArray<ArrayList<DeviceAdminInfo>>
     68             mAdminsByProfile = new SparseArray<ArrayList<DeviceAdminInfo>>();
     69 
     70     private String mDeviceOwnerPkg;
     71     private SparseArray<ComponentName> mProfileOwnerComponents = new SparseArray<ComponentName>();
     72 
     73     @Override
     74     public void onCreate(Bundle icicle) {
     75         super.onCreate(icicle);
     76     }
     77 
     78     @Override
     79     public View onCreateView(LayoutInflater inflater, ViewGroup container,
     80             Bundle savedInstanceState) {
     81         mDPM = (DevicePolicyManager) getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
     82         mUm = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
     83         return inflater.inflate(R.layout.device_admin_settings, container, false);
     84     }
     85 
     86     @Override
     87     public void onActivityCreated(Bundle savedInstanceState) {
     88         super.onActivityCreated(savedInstanceState);
     89 
     90         Utils.forceCustomPadding(getListView(), true /* additive padding */);
     91     }
     92 
     93     @Override
     94     public void onResume() {
     95         super.onResume();
     96         mDeviceOwnerPkg = mDPM.getDeviceOwner();
     97         if (mDeviceOwnerPkg != null && !mDPM.isDeviceOwner(mDeviceOwnerPkg)) {
     98             mDeviceOwnerPkg = null;
     99         }
    100         mProfileOwnerComponents.clear();
    101         final List<UserHandle> profiles = mUm.getUserProfiles();
    102         final int profilesSize = profiles.size();
    103         for (int i = 0; i < profilesSize; ++i) {
    104             final int profileId = profiles.get(i).getIdentifier();
    105             mProfileOwnerComponents.put(profileId, mDPM.getProfileOwnerAsUser(profileId));
    106         }
    107         updateList();
    108     }
    109 
    110     /**
    111      * Update the internal collection of available admins for all profiles associated with the
    112      * current user.
    113      */
    114     void updateList() {
    115         mAdminsByProfile.clear();
    116 
    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             updateAvailableAdminsForProfile(profileId);
    122         }
    123 
    124         getListView().setAdapter(new PolicyListAdapter());
    125     }
    126 
    127     @Override
    128     public void onListItemClick(ListView l, View v, int position, long id) {
    129         Object o = l.getAdapter().getItem(position);
    130         if (!(o instanceof DeviceAdminInfo)) {
    131             // race conditions may cause this
    132             return;
    133         }
    134         DeviceAdminInfo dpi = (DeviceAdminInfo) o;
    135         final Activity activity = getActivity();
    136         final int userId = getUserId(dpi);
    137         if (userId == UserHandle.myUserId() || !isProfileOwner(dpi)) {
    138             Intent intent = new Intent();
    139             intent.setClass(activity, DeviceAdminAdd.class);
    140             intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, dpi.getComponent());
    141             activity.startActivityAsUser(intent, new UserHandle(userId));
    142         } else {
    143             AlertDialog.Builder builder = new AlertDialog.Builder(activity);
    144             builder.setMessage(getString(R.string.managed_profile_device_admin_info,
    145                     dpi.loadLabel(activity.getPackageManager())));
    146             builder.setPositiveButton(android.R.string.ok, null);
    147             builder.create().show();
    148         }
    149     }
    150 
    151     static class ViewHolder {
    152         ImageView icon;
    153         TextView name;
    154         CheckBox checkbox;
    155         TextView description;
    156     }
    157 
    158     class PolicyListAdapter extends BaseAdapter {
    159         final LayoutInflater mInflater;
    160 
    161         PolicyListAdapter() {
    162             mInflater = (LayoutInflater)
    163                     getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    164         }
    165 
    166         @Override
    167         public boolean hasStableIds() {
    168             return false;
    169         }
    170 
    171         @Override
    172         public int getCount() {
    173             int adminCount = 0;
    174             final int profileCount = mAdminsByProfile.size();
    175             for (int i = 0; i < profileCount; ++i) {
    176                 adminCount += mAdminsByProfile.valueAt(i).size();
    177             }
    178             // Add 'profileCount' for title items.
    179             return adminCount + profileCount;
    180         }
    181 
    182         /**
    183          * The item for the given position in the list.
    184          *
    185          * @return a String object for title items and a DeviceAdminInfo object for actual device
    186          *         admins.
    187          */
    188         @Override
    189         public Object getItem(int position) {
    190             if (position < 0) {
    191                 throw new ArrayIndexOutOfBoundsException();
    192             }
    193             // The position of the item in the list of admins.
    194             // We start from the given position and discount the length of the upper lists until we
    195             // get the one for the right profile
    196             int adminPosition = position;
    197             final int n = mAdminsByProfile.size();
    198             int i = 0;
    199             for (; i < n; ++i) {
    200                 // The elements in that section including the title item (that's why adding one).
    201                 final int listSize = mAdminsByProfile.valueAt(i).size() + 1;
    202                 if (adminPosition < listSize) {
    203                     break;
    204                 }
    205                 adminPosition -= listSize;
    206             }
    207             if (i == n) {
    208                 throw new ArrayIndexOutOfBoundsException();
    209             }
    210             // If countdown == 0 that means the title item
    211             if (adminPosition == 0) {
    212                 Resources res = getActivity().getResources();
    213                 if (mAdminsByProfile.keyAt(i) == UserHandle.myUserId()) {
    214                     return res.getString(R.string.personal_device_admin_title);
    215                 } else {
    216                     return res.getString(R.string.managed_device_admin_title);
    217                 }
    218             } else {
    219                 // Subtracting one for the title.
    220                 return mAdminsByProfile.valueAt(i).get(adminPosition - 1);
    221             }
    222         }
    223 
    224         @Override
    225         public long getItemId(int position) {
    226             return position;
    227         }
    228 
    229         @Override
    230         public boolean areAllItemsEnabled() {
    231             return false;
    232         }
    233 
    234         /**
    235          * See {@link #getItemViewType} for the view types.
    236          */
    237         @Override
    238         public int getViewTypeCount() {
    239             return 2;
    240         }
    241 
    242         /**
    243          * Returns 1 for title items and 0 for anything else.
    244          */
    245         @Override
    246         public int getItemViewType(int position) {
    247             Object o = getItem(position);
    248             return (o instanceof String) ? 1 : 0;
    249         }
    250 
    251         @Override
    252         public boolean isEnabled(int position) {
    253             Object o = getItem(position);
    254             return isEnabled(o);
    255         }
    256 
    257         private boolean isEnabled(Object o) {
    258             if (!(o instanceof DeviceAdminInfo)) {
    259                 // Title item
    260                 return false;
    261             }
    262             DeviceAdminInfo info = (DeviceAdminInfo) o;
    263             if (isActiveAdmin(info) && getUserId(info) == UserHandle.myUserId()
    264                     && (isDeviceOwner(info) || isProfileOwner(info))) {
    265                 return false;
    266             }
    267             return true;
    268         }
    269 
    270         @Override
    271         public View getView(int position, View convertView, ViewGroup parent) {
    272             Object o = getItem(position);
    273             if (o instanceof DeviceAdminInfo) {
    274                 if (convertView == null) {
    275                     convertView = newDeviceAdminView(parent);
    276                 }
    277                 bindView(convertView, (DeviceAdminInfo) o);
    278             } else {
    279                 if (convertView == null) {
    280                     convertView = newTitleView(parent);
    281                 }
    282                 final TextView title = (TextView) convertView.findViewById(android.R.id.title);
    283                 title.setText((String)o);
    284             }
    285             return convertView;
    286         }
    287 
    288         private View newDeviceAdminView(ViewGroup parent) {
    289             View v = mInflater.inflate(R.layout.device_admin_item, parent, false);
    290             ViewHolder h = new ViewHolder();
    291             h.icon = (ImageView)v.findViewById(R.id.icon);
    292             h.name = (TextView)v.findViewById(R.id.name);
    293             h.checkbox = (CheckBox)v.findViewById(R.id.checkbox);
    294             h.description = (TextView)v.findViewById(R.id.description);
    295             v.setTag(h);
    296             return v;
    297         }
    298 
    299         private View newTitleView(ViewGroup parent) {
    300             final TypedArray a = mInflater.getContext().obtainStyledAttributes(null,
    301                     com.android.internal.R.styleable.Preference,
    302                     com.android.internal.R.attr.preferenceCategoryStyle, 0);
    303             final int resId = a.getResourceId(com.android.internal.R.styleable.Preference_layout,
    304                     0);
    305             return mInflater.inflate(resId, parent, false);
    306         }
    307 
    308         private void bindView(View view, DeviceAdminInfo item) {
    309             final Activity activity = getActivity();
    310             ViewHolder vh = (ViewHolder) view.getTag();
    311             Drawable activityIcon = item.loadIcon(activity.getPackageManager());
    312             Drawable badgedIcon = activity.getPackageManager().getUserBadgedIcon(
    313                     activityIcon, new UserHandle(getUserId(item)));
    314             vh.icon.setImageDrawable(badgedIcon);
    315             vh.name.setText(item.loadLabel(activity.getPackageManager()));
    316             vh.checkbox.setChecked(isActiveAdmin(item));
    317             final boolean enabled = isEnabled(item);
    318             try {
    319                 vh.description.setText(item.loadDescription(activity.getPackageManager()));
    320             } catch (Resources.NotFoundException e) {
    321             }
    322             vh.checkbox.setEnabled(enabled);
    323             vh.name.setEnabled(enabled);
    324             vh.description.setEnabled(enabled);
    325             vh.icon.setEnabled(enabled);
    326         }
    327     }
    328 
    329     private boolean isDeviceOwner(DeviceAdminInfo item) {
    330         return getUserId(item) == UserHandle.myUserId()
    331                 && item.getPackageName().equals(mDeviceOwnerPkg);
    332     }
    333 
    334     private boolean isProfileOwner(DeviceAdminInfo item) {
    335         ComponentName profileOwner = mProfileOwnerComponents.get(getUserId(item));
    336         return item.getComponent().equals(profileOwner);
    337     }
    338 
    339     private boolean isActiveAdmin(DeviceAdminInfo item) {
    340         return mDPM.isAdminActiveAsUser(item.getComponent(), getUserId(item));
    341     }
    342 
    343     /**
    344      * Add device admins to the internal collection that belong to a profile.
    345      *
    346      * @param profileId the profile identifier.
    347      */
    348     private void updateAvailableAdminsForProfile(final int profileId) {
    349         // We are adding the union of two sets 'A' and 'B' of device admins to mAvailableAdmins.
    350         // Set 'A' is the set of active admins for the profile whereas set 'B' is the set of
    351         // listeners to DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED for the profile.
    352 
    353         // Add all of set 'A' to mAvailableAdmins.
    354         List<ComponentName> activeAdminsListForProfile = mDPM.getActiveAdminsAsUser(profileId);
    355         addActiveAdminsForProfile(activeAdminsListForProfile, profileId);
    356 
    357         // Collect set 'B' and add B-A to mAvailableAdmins.
    358         addDeviceAdminBroadcastReceiversForProfile(activeAdminsListForProfile, profileId);
    359     }
    360 
    361     /**
    362      * Add a profile's device admins that are receivers of
    363      * {@code DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED} to the internal collection if they
    364      * haven't been added yet.
    365      *
    366      * @param alreadyAddedComponents the set of active admin component names. Receivers of
    367      *            {@code DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED} whose component is in this
    368      *            set are not added to the internal collection again.
    369      * @param profileId the identifier of the profile
    370      */
    371     private void addDeviceAdminBroadcastReceiversForProfile(
    372             Collection<ComponentName> alreadyAddedComponents, final int profileId) {
    373         final PackageManager pm = getActivity().getPackageManager();
    374         List<ResolveInfo> enabledForProfile = pm.queryBroadcastReceivers(
    375                 new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED),
    376                 PackageManager.GET_META_DATA | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
    377                 profileId);
    378         if (enabledForProfile == null) {
    379             enabledForProfile = Collections.emptyList();
    380         }
    381         final int n = enabledForProfile.size();
    382         ArrayList<DeviceAdminInfo> deviceAdmins = mAdminsByProfile.get(profileId);
    383         if (deviceAdmins == null) {
    384             deviceAdmins = new ArrayList<DeviceAdminInfo>(n);
    385         }
    386         for (int i = 0; i < n; ++i) {
    387             ResolveInfo resolveInfo = enabledForProfile.get(i);
    388             ComponentName riComponentName =
    389                     new ComponentName(resolveInfo.activityInfo.packageName,
    390                             resolveInfo.activityInfo.name);
    391             if (alreadyAddedComponents == null
    392                     || !alreadyAddedComponents.contains(riComponentName)) {
    393                 DeviceAdminInfo deviceAdminInfo = createDeviceAdminInfo(resolveInfo);
    394                 // add only visible ones (note: active admins are added regardless of visibility)
    395                 if (deviceAdminInfo != null && deviceAdminInfo.isVisible()) {
    396                     deviceAdmins.add(deviceAdminInfo);
    397                 }
    398             }
    399         }
    400         if (!deviceAdmins.isEmpty()) {
    401             mAdminsByProfile.put(profileId, deviceAdmins);
    402         }
    403     }
    404 
    405     /**
    406      * Add a {@link DeviceAdminInfo} object to the internal collection of available admins for all
    407      * active admin components associated with a profile.
    408      *
    409      * @param profileId a profile identifier.
    410      */
    411     private void addActiveAdminsForProfile(final List<ComponentName> activeAdmins,
    412             final int profileId) {
    413         if (activeAdmins != null) {
    414             final PackageManager packageManager = getActivity().getPackageManager();
    415             final int n = activeAdmins.size();
    416             ArrayList<DeviceAdminInfo> deviceAdmins = new ArrayList<DeviceAdminInfo>(n);
    417             for (int i = 0; i < n; ++i) {
    418                 ComponentName activeAdmin = activeAdmins.get(i);
    419                 List<ResolveInfo> resolved = packageManager.queryBroadcastReceivers(
    420                         new Intent().setComponent(activeAdmin), PackageManager.GET_META_DATA
    421                                 | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, profileId);
    422                 if (resolved != null) {
    423                     final int resolvedMax = resolved.size();
    424                     for (int j = 0; j < resolvedMax; ++j) {
    425                         DeviceAdminInfo deviceAdminInfo = createDeviceAdminInfo(resolved.get(j));
    426                         if (deviceAdminInfo != null) {
    427                             deviceAdmins.add(deviceAdminInfo);
    428                         }
    429                     }
    430                 }
    431             }
    432             if (!deviceAdmins.isEmpty()) {
    433                 mAdminsByProfile.put(profileId, deviceAdmins);
    434             }
    435         }
    436     }
    437 
    438     /**
    439      * Creates a device admin info object for the resolved intent that points to the component of
    440      * the device admin.
    441      *
    442      * @param resolved resolved intent.
    443      * @return new {@link DeviceAdminInfo} object or null if there was an error.
    444      */
    445     private DeviceAdminInfo createDeviceAdminInfo(ResolveInfo resolved) {
    446         try {
    447             return new DeviceAdminInfo(getActivity(), resolved);
    448         } catch (XmlPullParserException e) {
    449             Log.w(TAG, "Skipping " + resolved.activityInfo, e);
    450         } catch (IOException e) {
    451             Log.w(TAG, "Skipping " + resolved.activityInfo, e);
    452         }
    453         return null;
    454     }
    455 
    456     /**
    457      * Extracts the user id from a device admin info object.
    458      * @param adminInfo the device administrator info.
    459      * @return identifier of the user associated with the device admin.
    460      */
    461     private int getUserId(DeviceAdminInfo adminInfo) {
    462         return UserHandle.getUserId(adminInfo.getActivityInfo().applicationInfo.uid);
    463     }
    464 }
    465