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