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