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 
compareTo(DeviceAdminListItem other)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
onCreate(Bundle icicle)107     public void onCreate(Bundle icicle) {
108         super.onCreate(icicle);
109     }
110 
111     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)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
onActivityCreated(Bundle savedInstanceState)120     public void onActivityCreated(Bundle savedInstanceState) {
121         super.onActivityCreated(savedInstanceState);
122         setHasOptionsMenu(true);
123         Utils.forceCustomPadding(getListView(), true /* additive padding */);
124     }
125 
126     @Override
onResume()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
onPause()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      */
updateList()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
onListItemClick(ListView l, View v, int position, long id)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 
PolicyListAdapter()192         PolicyListAdapter() {
193             mInflater = (LayoutInflater)
194                     getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
195         }
196 
197         @Override
hasStableIds()198         public boolean hasStableIds() {
199             return false;
200         }
201 
202         @Override
getCount()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
getItem(int position)213         public Object getItem(int position) {
214             return ((DeviceAdminListItem) (mAdmins.get(position))).info;
215         }
216 
217         @Override
getItemId(int position)218         public long getItemId(int position) {
219             return position;
220         }
221 
222         @Override
areAllItemsEnabled()223         public boolean areAllItemsEnabled() {
224             return false;
225         }
226 
227         /**
228          * See {@link #getItemViewType} for the view types.
229          */
230         @Override
getViewTypeCount()231         public int getViewTypeCount() {
232             return 1;
233         }
234 
235         /**
236          * Returns 0 for all types.
237          */
238         @Override
getItemViewType(int position)239         public int getItemViewType(int position) {
240             return 0;
241         }
242 
243         @Override
isEnabled(int position)244         public boolean isEnabled(int position) {
245             Object o = getItem(position);
246             return isEnabled(o);
247         }
248 
isEnabled(Object o)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
getView(int position, View convertView, ViewGroup parent)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 
newDeviceAdminView(ViewGroup parent)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 
bindView(View view, DeviceAdminInfo item)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 
isDeviceOwner(DeviceAdminInfo item)300     private boolean isDeviceOwner(DeviceAdminInfo item) {
301         return getUserId(item) == UserHandle.myUserId()
302                 && item.getPackageName().equals(mDeviceOwnerPkg);
303     }
304 
isProfileOwner(DeviceAdminInfo item)305     private boolean isProfileOwner(DeviceAdminInfo item) {
306         ComponentName profileOwner = mProfileOwnerComponents.get(getUserId(item));
307         return item.getComponent().equals(profileOwner);
308     }
309 
isActiveAdmin(DeviceAdminInfo item)310     private boolean isActiveAdmin(DeviceAdminInfo item) {
311         return mDPM.isAdminActiveAsUser(item.getComponent(), getUserId(item));
312     }
313 
isRemovingAdmin(DeviceAdminInfo item)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      */
updateAvailableAdminsForProfile(final int profileId)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      */
addDeviceAdminBroadcastReceiversForProfile( Collection<ComponentName> alreadyAddedComponents, final int profileId)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      */
addActiveAdminsForProfile(final List<ComponentName> activeAdmins, final int profileId)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      */
createDeviceAdminInfo(ActivityInfo ai)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      */
getUserId(DeviceAdminInfo adminInfo)441     private int getUserId(DeviceAdminInfo adminInfo) {
442         return UserHandle.getUserId(adminInfo.getActivityInfo().applicationInfo.uid);
443     }
444 }
445