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