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.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;
     50 
     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;
     56 
     57 public class DeviceAdminSettings extends ListFragment {
     58     static final String TAG = "DeviceAdminSettings";
     59 
     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     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
     74         @Override
     75         public void onReceive(Context context, Intent intent) {
     76             // Refresh the list, if state change has been received. It could be that checkboxes
     77             // need to be updated
     78             if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(
     79                     intent.getAction())) {
     80                 updateList();
     81             }
     82         }
     83     };
     84 
     85     @Override
     86     public void onCreate(Bundle icicle) {
     87         super.onCreate(icicle);
     88     }
     89 
     90     @Override
     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);
     96     }
     97 
     98     @Override
     99     public void onActivityCreated(Bundle savedInstanceState) {
    100         super.onActivityCreated(savedInstanceState);
    101 
    102         Utils.forceCustomPadding(getListView(), true /* additive padding */);
    103     }
    104 
    105     @Override
    106     public void onResume() {
    107         super.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;
    115         }
    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));
    122         }
    123         updateList();
    124     }
    125 
    126     @Override
    127     public void onPause() {
    128         getActivity().unregisterReceiver(mBroadcastReceiver);
    129         super.onPause();
    130     }
    131 
    132     /**
    133      * Update the internal collection of available admins for all profiles associated with the
    134      * current user.
    135      */
    136     void updateList() {
    137         mAdminsByProfile.clear();
    138 
    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);
    144         }
    145 
    146         getListView().setAdapter(new PolicyListAdapter());
    147     }
    148 
    149     @Override
    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
    154             return;
    155         }
    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));
    164         } else {
    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();
    170         }
    171     }
    172 
    173     static class ViewHolder {
    174         ImageView icon;
    175         TextView name;
    176         CheckBox checkbox;
    177         TextView description;
    178     }
    179 
    180     class PolicyListAdapter extends BaseAdapter {
    181         final LayoutInflater mInflater;
    182 
    183         PolicyListAdapter() {
    184             mInflater = (LayoutInflater)
    185                     getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    186         }
    187 
    188         @Override
    189         public boolean hasStableIds() {
    190             return false;
    191         }
    192 
    193         @Override
    194         public int getCount() {
    195             int adminCount = 0;
    196             final int profileCount = mAdminsByProfile.size();
    197             for (int i = 0; i < profileCount; ++i) {
    198                 adminCount += mAdminsByProfile.valueAt(i).size();
    199             }
    200             // Add 'profileCount' for title items.
    201             return adminCount + profileCount;
    202         }
    203 
    204         /**
    205          * The item for the given position in the list.
    206          *
    207          * @return a String object for title items and a DeviceAdminInfo object for actual device
    208          *         admins.
    209          */
    210         @Override
    211         public Object getItem(int position) {
    212             if (position < 0) {
    213                 throw new ArrayIndexOutOfBoundsException();
    214             }
    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();
    220             int i = 0;
    221             for (; i < n; ++i) {
    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) {
    225                     break;
    226                 }
    227                 adminPosition -= listSize;
    228             }
    229             if (i == n) {
    230                 throw new ArrayIndexOutOfBoundsException();
    231             }
    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);
    237                 } else {
    238                     return res.getString(R.string.managed_device_admin_title);
    239                 }
    240             } else {
    241                 // Subtracting one for the title.
    242                 return mAdminsByProfile.valueAt(i).get(adminPosition - 1);
    243             }
    244         }
    245 
    246         @Override
    247         public long getItemId(int position) {
    248             return position;
    249         }
    250 
    251         @Override
    252         public boolean areAllItemsEnabled() {
    253             return false;
    254         }
    255 
    256         /**
    257          * See {@link #getItemViewType} for the view types.
    258          */
    259         @Override
    260         public int getViewTypeCount() {
    261             return 2;
    262         }
    263 
    264         /**
    265          * Returns 1 for title items and 0 for anything else.
    266          */
    267         @Override
    268         public int getItemViewType(int position) {
    269             Object o = getItem(position);
    270             return (o instanceof String) ? 1 : 0;
    271         }
    272 
    273         @Override
    274         public boolean isEnabled(int position) {
    275             Object o = getItem(position);
    276             return isEnabled(o);
    277         }
    278 
    279         private boolean isEnabled(Object o) {
    280             if (!(o instanceof DeviceAdminInfo)) {
    281                 // Title item
    282                 return false;
    283             }
    284             DeviceAdminInfo info = (DeviceAdminInfo) o;
    285             if (isActiveAdmin(info) && getUserId(info) == UserHandle.myUserId()
    286                     && (isDeviceOwner(info) || isProfileOwner(info))) {
    287                 return false;
    288             }
    289             // Disable item if admin is being removed
    290             if (isRemovingAdmin(info)) {
    291                 return false;
    292             }
    293             return true;
    294         }
    295 
    296         @Override
    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);
    302                 }
    303                 bindView(convertView, (DeviceAdminInfo) o);
    304             } else {
    305                 if (convertView == null) {
    306                     convertView = newTitleView(parent);
    307                 }
    308                 final TextView title = (TextView) convertView.findViewById(android.R.id.title);
    309                 title.setText((String)o);
    310             }
    311             return convertView;
    312         }
    313 
    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);
    321             v.setTag(h);
    322             return v;
    323         }
    324 
    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,
    330                     0);
    331             return mInflater.inflate(resId, parent, false);
    332         }
    333 
    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);
    344             try {
    345                 vh.description.setText(item.loadDescription(activity.getPackageManager()));
    346             } catch (Resources.NotFoundException e) {
    347             }
    348             vh.checkbox.setEnabled(enabled);
    349             vh.name.setEnabled(enabled);
    350             vh.description.setEnabled(enabled);
    351             vh.icon.setEnabled(enabled);
    352         }
    353     }
    354 
    355     private boolean isDeviceOwner(DeviceAdminInfo item) {
    356         return getUserId(item) == UserHandle.myUserId()
    357                 && item.getPackageName().equals(mDeviceOwnerPkg);
    358     }
    359 
    360     private boolean isProfileOwner(DeviceAdminInfo item) {
    361         ComponentName profileOwner = mProfileOwnerComponents.get(getUserId(item));
    362         return item.getComponent().equals(profileOwner);
    363     }
    364 
    365     private boolean isActiveAdmin(DeviceAdminInfo item) {
    366         return mDPM.isAdminActiveAsUser(item.getComponent(), getUserId(item));
    367     }
    368 
    369     private boolean isRemovingAdmin(DeviceAdminInfo item) {
    370         return mDPM.isRemovingAdmin(item.getComponent(), getUserId(item));
    371     }
    372 
    373     /**
    374      * Add device admins to the internal collection that belong to a profile.
    375      *
    376      * @param profileId the profile identifier.
    377      */
    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.
    382 
    383         // Add all of set 'A' to mAvailableAdmins.
    384         List<ComponentName> activeAdminsListForProfile = mDPM.getActiveAdminsAsUser(profileId);
    385         addActiveAdminsForProfile(activeAdminsListForProfile, profileId);
    386 
    387         // Collect set 'B' and add B-A to mAvailableAdmins.
    388         addDeviceAdminBroadcastReceiversForProfile(activeAdminsListForProfile, profileId);
    389     }
    390 
    391     /**
    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.
    395      *
    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
    400      */
    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,
    407                 profileId);
    408         if (enabledForProfile == null) {
    409             enabledForProfile = Collections.emptyList();
    410         }
    411         final int n = enabledForProfile.size();
    412         ArrayList<DeviceAdminInfo> deviceAdmins = mAdminsByProfile.get(profileId);
    413         if (deviceAdmins == null) {
    414             deviceAdmins = new ArrayList<DeviceAdminInfo>(n);
    415         }
    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);
    427                 }
    428             }
    429         }
    430         if (!deviceAdmins.isEmpty()) {
    431             mAdminsByProfile.put(profileId, deviceAdmins);
    432         }
    433     }
    434 
    435     /**
    436      * Add a {@link DeviceAdminInfo} object to the internal collection of available admins for all
    437      * active admin components associated with a profile.
    438      *
    439      * @param profileId a profile identifier.
    440      */
    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);
    458                         }
    459                     }
    460                 }
    461             }
    462             if (!deviceAdmins.isEmpty()) {
    463                 mAdminsByProfile.put(profileId, deviceAdmins);
    464             }
    465         }
    466     }
    467 
    468     /**
    469      * Creates a device admin info object for the resolved intent that points to the component of
    470      * the device admin.
    471      *
    472      * @param resolved resolved intent.
    473      * @return new {@link DeviceAdminInfo} object or null if there was an error.
    474      */
    475     private DeviceAdminInfo createDeviceAdminInfo(ResolveInfo resolved) {
    476         try {
    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);
    482         }
    483         return null;
    484     }
    485 
    486     /**
    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.
    490      */
    491     private int getUserId(DeviceAdminInfo adminInfo) {
    492         return UserHandle.getUserId(adminInfo.getActivityInfo().applicationInfo.uid);
    493     }
    494 }
    495