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