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 android.app.Activity;
     20 import android.app.AlertDialog;
     21 import android.app.AppGlobals;
     22 import android.app.ListFragment;
     23 import android.app.admin.DeviceAdminInfo;
     24 import android.app.admin.DeviceAdminReceiver;
     25 import android.app.admin.DevicePolicyManager;
     26 import android.content.BroadcastReceiver;
     27 import android.content.ComponentName;
     28 import android.content.Context;
     29 import android.content.Intent;
     30 import android.content.IntentFilter;
     31 import android.content.pm.ActivityInfo;
     32 import android.content.pm.IPackageManager;
     33 import android.content.pm.PackageManager;
     34 import android.content.pm.PackageManager.NameNotFoundException;
     35 import android.content.pm.ResolveInfo;
     36 import android.content.res.Resources;
     37 import android.graphics.drawable.Drawable;
     38 import android.os.Bundle;
     39 import android.os.RemoteException;
     40 import android.os.UserHandle;
     41 import android.os.UserManager;
     42 import android.util.Log;
     43 import android.util.SparseArray;
     44 import android.view.LayoutInflater;
     45 import android.view.View;
     46 import android.view.ViewGroup;
     47 import android.widget.BaseAdapter;
     48 import android.widget.CheckBox;
     49 import android.widget.ImageView;
     50 import android.widget.ListView;
     51 import android.widget.TextView;
     52 
     53 import org.xmlpull.v1.XmlPullParserException;
     54 
     55 import java.io.IOException;
     56 import java.util.ArrayList;
     57 import java.util.Collection;
     58 import java.util.Collections;
     59 import java.util.List;
     60 
     61 public class DeviceAdminSettings extends ListFragment {
     62     static final String TAG = "DeviceAdminSettings";
     63 
     64     private DevicePolicyManager mDPM;
     65     private UserManager mUm;
     66 
     67 
     68     private static class DeviceAdminListItem implements Comparable<DeviceAdminListItem> {
     69         public DeviceAdminInfo info;
     70 
     71         // These aren't updated so they keep a stable sort order if user activates / de-activates
     72         // an admin.
     73         public String name;
     74         public boolean active;
     75 
     76         public int compareTo(DeviceAdminListItem other)  {
     77             // Sort active admins first, then by name.
     78             if (this.active != other.active) {
     79                 return this.active ? -1 : 1;
     80             }
     81             return this.name.compareTo(other.name);
     82         }
     83     }
     84     /**
     85      * Internal collection of device admin info objects for all profiles associated with the current
     86      * user.
     87      */
     88     private final ArrayList<DeviceAdminListItem>
     89             mAdmins = new ArrayList<DeviceAdminListItem>();
     90 
     91     private String mDeviceOwnerPkg;
     92     private SparseArray<ComponentName> mProfileOwnerComponents = new SparseArray<ComponentName>();
     93 
     94     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
     95         @Override
     96         public void onReceive(Context context, Intent intent) {
     97             // Refresh the list, if state change has been received. It could be that checkboxes
     98             // need to be updated
     99             if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(
    100                     intent.getAction())) {
    101                 updateList();
    102             }
    103         }
    104     };
    105 
    106     @Override
    107     public void onCreate(Bundle icicle) {
    108         super.onCreate(icicle);
    109     }
    110 
    111     @Override
    112     public View onCreateView(LayoutInflater inflater, ViewGroup container,
    113             Bundle savedInstanceState) {
    114         mDPM = (DevicePolicyManager) getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
    115         mUm = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
    116         return inflater.inflate(R.layout.device_admin_settings, container, false);
    117     }
    118 
    119     @Override
    120     public void onActivityCreated(Bundle savedInstanceState) {
    121         super.onActivityCreated(savedInstanceState);
    122         setHasOptionsMenu(true);
    123         Utils.forceCustomPadding(getListView(), true /* additive padding */);
    124     }
    125 
    126     @Override
    127     public void onResume() {
    128         super.onResume();
    129         IntentFilter filter = new IntentFilter();
    130         filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
    131         getActivity().registerReceiverAsUser(
    132                 mBroadcastReceiver, UserHandle.ALL, filter, null, null);
    133 
    134         final ComponentName deviceOwnerComponent = mDPM.getDeviceOwnerComponentOnAnyUser();
    135         mDeviceOwnerPkg =
    136                 deviceOwnerComponent != null ? deviceOwnerComponent.getPackageName() : null;
    137         mProfileOwnerComponents.clear();
    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             mProfileOwnerComponents.put(profileId, mDPM.getProfileOwnerAsUser(profileId));
    143         }
    144         updateList();
    145     }
    146 
    147     @Override
    148     public void onPause() {
    149         getActivity().unregisterReceiver(mBroadcastReceiver);
    150         super.onPause();
    151     }
    152 
    153     /**
    154      * Update the internal collection of available admins for all profiles associated with the
    155      * current user.
    156      */
    157     void updateList() {
    158         mAdmins.clear();
    159 
    160         final List<UserHandle> profiles = mUm.getUserProfiles();
    161         final int profilesSize = profiles.size();
    162         for (int i = 0; i < profilesSize; ++i) {
    163             final int profileId = profiles.get(i).getIdentifier();
    164             updateAvailableAdminsForProfile(profileId);
    165         }
    166         Collections.sort(mAdmins);
    167 
    168         getListView().setAdapter(new PolicyListAdapter());
    169     }
    170 
    171     @Override
    172     public void onListItemClick(ListView l, View v, int position, long id) {
    173         Object o = l.getAdapter().getItem(position);
    174         DeviceAdminInfo dpi = (DeviceAdminInfo) o;
    175         final UserHandle user = new UserHandle(getUserId(dpi));
    176         final Activity activity = getActivity();
    177         Intent intent = new Intent(activity, DeviceAdminAdd.class);
    178         intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, dpi.getComponent());
    179         activity.startActivityAsUser(intent, user);
    180     }
    181 
    182     static class ViewHolder {
    183         ImageView icon;
    184         TextView name;
    185         CheckBox checkbox;
    186         TextView description;
    187     }
    188 
    189     class PolicyListAdapter extends BaseAdapter {
    190         final LayoutInflater mInflater;
    191 
    192         PolicyListAdapter() {
    193             mInflater = (LayoutInflater)
    194                     getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    195         }
    196 
    197         @Override
    198         public boolean hasStableIds() {
    199             return false;
    200         }
    201 
    202         @Override
    203         public int getCount() {
    204             return mAdmins.size();
    205         }
    206 
    207         /**
    208          * The item for the given position in the list.
    209          *
    210          * @return DeviceAdminInfo object for actual device admins.
    211          */
    212         @Override
    213         public Object getItem(int position) {
    214             return ((DeviceAdminListItem) (mAdmins.get(position))).info;
    215         }
    216 
    217         @Override
    218         public long getItemId(int position) {
    219             return position;
    220         }
    221 
    222         @Override
    223         public boolean areAllItemsEnabled() {
    224             return false;
    225         }
    226 
    227         /**
    228          * See {@link #getItemViewType} for the view types.
    229          */
    230         @Override
    231         public int getViewTypeCount() {
    232             return 1;
    233         }
    234 
    235         /**
    236          * Returns 0 for all types.
    237          */
    238         @Override
    239         public int getItemViewType(int position) {
    240             return 0;
    241         }
    242 
    243         @Override
    244         public boolean isEnabled(int position) {
    245             Object o = getItem(position);
    246             return isEnabled(o);
    247         }
    248 
    249         private boolean isEnabled(Object o) {
    250             DeviceAdminInfo info = (DeviceAdminInfo) o;
    251             // Disable item if admin is being removed
    252             if (isRemovingAdmin(info)) {
    253                 return false;
    254             }
    255             return true;
    256         }
    257 
    258         @Override
    259         public View getView(int position, View convertView, ViewGroup parent) {
    260             Object o = getItem(position);
    261             if (convertView == null) {
    262                 convertView = newDeviceAdminView(parent);
    263             }
    264             bindView(convertView, (DeviceAdminInfo) o);
    265             return convertView;
    266         }
    267 
    268         private View newDeviceAdminView(ViewGroup parent) {
    269             View v = mInflater.inflate(R.layout.device_admin_item, parent, false);
    270             ViewHolder h = new ViewHolder();
    271             h.icon = (ImageView) v.findViewById(R.id.icon);
    272             h.name = (TextView) v.findViewById(R.id.name);
    273             h.checkbox = (CheckBox) v.findViewById(R.id.checkbox);
    274             h.description = (TextView) v.findViewById(R.id.description);
    275             v.setTag(h);
    276             return v;
    277         }
    278 
    279         private void bindView(View view, DeviceAdminInfo item) {
    280             final Activity activity = getActivity();
    281             ViewHolder vh = (ViewHolder) view.getTag();
    282             Drawable activityIcon = item.loadIcon(activity.getPackageManager());
    283             Drawable badgedIcon = activity.getPackageManager().getUserBadgedIcon(
    284                     activityIcon, new UserHandle(getUserId(item)));
    285             vh.icon.setImageDrawable(badgedIcon);
    286             vh.name.setText(item.loadLabel(activity.getPackageManager()));
    287             vh.checkbox.setChecked(isActiveAdmin(item));
    288             final boolean enabled = isEnabled(item);
    289             try {
    290                 vh.description.setText(item.loadDescription(activity.getPackageManager()));
    291             } catch (Resources.NotFoundException e) {
    292             }
    293             vh.checkbox.setEnabled(enabled);
    294             vh.name.setEnabled(enabled);
    295             vh.description.setEnabled(enabled);
    296             vh.icon.setEnabled(enabled);
    297         }
    298     }
    299 
    300     private boolean isDeviceOwner(DeviceAdminInfo item) {
    301         return getUserId(item) == UserHandle.myUserId()
    302                 && item.getPackageName().equals(mDeviceOwnerPkg);
    303     }
    304 
    305     private boolean isProfileOwner(DeviceAdminInfo item) {
    306         ComponentName profileOwner = mProfileOwnerComponents.get(getUserId(item));
    307         return item.getComponent().equals(profileOwner);
    308     }
    309 
    310     private boolean isActiveAdmin(DeviceAdminInfo item) {
    311         return mDPM.isAdminActiveAsUser(item.getComponent(), getUserId(item));
    312     }
    313 
    314     private boolean isRemovingAdmin(DeviceAdminInfo item) {
    315         return mDPM.isRemovingAdmin(item.getComponent(), getUserId(item));
    316     }
    317 
    318     /**
    319      * Add device admins to the internal collection that belong to a profile.
    320      *
    321      * @param profileId the profile identifier.
    322      */
    323     private void updateAvailableAdminsForProfile(final int profileId) {
    324         // We are adding the union of two sets 'A' and 'B' of device admins to mAvailableAdmins.
    325         // Set 'A' is the set of active admins for the profile whereas set 'B' is the set of
    326         // listeners to DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED for the profile.
    327 
    328         // Add all of set 'A' to mAvailableAdmins.
    329         List<ComponentName> activeAdminsListForProfile = mDPM.getActiveAdminsAsUser(profileId);
    330         addActiveAdminsForProfile(activeAdminsListForProfile, profileId);
    331 
    332         // Collect set 'B' and add B-A to mAvailableAdmins.
    333         addDeviceAdminBroadcastReceiversForProfile(activeAdminsListForProfile, profileId);
    334     }
    335 
    336     /**
    337      * Add a profile's device admins that are receivers of
    338      * {@code DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED} to the internal collection if they
    339      * haven't been added yet.
    340      *
    341      * @param alreadyAddedComponents the set of active admin component names. Receivers of
    342      *            {@code DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED} whose component is in this
    343      *            set are not added to the internal collection again.
    344      * @param profileId the identifier of the profile
    345      */
    346     private void addDeviceAdminBroadcastReceiversForProfile(
    347             Collection<ComponentName> alreadyAddedComponents, final int profileId) {
    348         final PackageManager pm = getActivity().getPackageManager();
    349         List<ResolveInfo> enabledForProfile = pm.queryBroadcastReceiversAsUser(
    350                 new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED),
    351                 PackageManager.GET_META_DATA | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
    352                 profileId);
    353         if (enabledForProfile == null) {
    354             enabledForProfile = Collections.emptyList();
    355         }
    356         final int n = enabledForProfile.size();
    357         for (int i = 0; i < n; ++i) {
    358             ResolveInfo resolveInfo = enabledForProfile.get(i);
    359             ComponentName riComponentName =
    360                     new ComponentName(resolveInfo.activityInfo.packageName,
    361                             resolveInfo.activityInfo.name);
    362             if (alreadyAddedComponents == null
    363                     || !alreadyAddedComponents.contains(riComponentName)) {
    364                 DeviceAdminInfo deviceAdminInfo =  createDeviceAdminInfo(resolveInfo.activityInfo);
    365                 // add only visible ones (note: active admins are added regardless of visibility)
    366                 if (deviceAdminInfo != null && deviceAdminInfo.isVisible()) {
    367                     if (!deviceAdminInfo.getActivityInfo().applicationInfo.isInternal()) {
    368                         continue;
    369                     }
    370                     DeviceAdminListItem item = new DeviceAdminListItem();
    371                     item.info = deviceAdminInfo;
    372                     item.name = deviceAdminInfo.loadLabel(pm).toString();
    373                     // Active ones already added.
    374                     item.active = false;
    375                     mAdmins.add(item);
    376                 }
    377             }
    378         }
    379     }
    380 
    381     /**
    382      * Add a {@link DeviceAdminInfo} object to the internal collection of available admins for all
    383      * active admin components associated with a profile.
    384      *
    385      * @param profileId a profile identifier.
    386      */
    387     private void addActiveAdminsForProfile(final List<ComponentName> activeAdmins,
    388             final int profileId) {
    389         if (activeAdmins != null) {
    390             final PackageManager packageManager = getActivity().getPackageManager();
    391             final IPackageManager iPackageManager = AppGlobals.getPackageManager();
    392             final int n = activeAdmins.size();
    393             for (int i = 0; i < n; ++i) {
    394                 final ComponentName activeAdmin = activeAdmins.get(i);
    395                 final ActivityInfo ai;
    396                 try {
    397                     ai = iPackageManager.getReceiverInfo(activeAdmin,
    398                             PackageManager.GET_META_DATA |
    399                             PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS |
    400                             PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE, profileId);
    401                 } catch (RemoteException e) {
    402                     Log.w(TAG, "Unable to load component: " + activeAdmin);
    403                     continue;
    404                 }
    405                 final DeviceAdminInfo deviceAdminInfo = createDeviceAdminInfo(ai);
    406                 if (deviceAdminInfo == null) {
    407                     continue;
    408                 }
    409                 // Don't do the applicationInfo.isInternal() check here; if an active
    410                 // admin is already on SD card, just show it.
    411                 final DeviceAdminListItem item = new DeviceAdminListItem();
    412                 item.info = deviceAdminInfo;
    413                 item.name = deviceAdminInfo.loadLabel(packageManager).toString();
    414                 item.active = true;
    415                 mAdmins.add(item);
    416             }
    417         }
    418     }
    419 
    420     /**
    421      * Creates a device admin info object for the resolved intent that points to the component of
    422      * the device admin.
    423      *
    424      * @param ai ActivityInfo for the admin component.
    425      * @return new {@link DeviceAdminInfo} object or null if there was an error.
    426      */
    427     private DeviceAdminInfo createDeviceAdminInfo(ActivityInfo ai) {
    428         try {
    429             return new DeviceAdminInfo(getActivity(), ai);
    430         } catch (XmlPullParserException|IOException e) {
    431             Log.w(TAG, "Skipping " + ai, e);
    432         }
    433         return null;
    434     }
    435 
    436     /**
    437      * Extracts the user id from a device admin info object.
    438      * @param adminInfo the device administrator info.
    439      * @return identifier of the user associated with the device admin.
    440      */
    441     private int getUserId(DeviceAdminInfo adminInfo) {
    442         return UserHandle.getUserId(adminInfo.getActivityInfo().applicationInfo.uid);
    443     }
    444 }
    445