• 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 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 
compareTo(DeviceAdminListItem other)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
getMetricsCategory()111     public int getMetricsCategory() {
112         return MetricsProto.MetricsEvent.DEVICE_ADMIN_SETTINGS;
113     }
114 
115     @Override
onCreate(Bundle icicle)116     public void onCreate(Bundle icicle) {
117         super.onCreate(icicle);
118         mVisibilityLoggerMixin = new VisibilityLoggerMixin(getMetricsCategory(),
119                 FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider());
120     }
121 
122     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)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
onActivityCreated(Bundle savedInstanceState)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
onResume()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
onPause()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      */
updateList()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
onListItemClick(ListView l, View v, int position, long id)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 
PolicyListAdapter()208         PolicyListAdapter() {
209             mInflater = (LayoutInflater)
210                     getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
211         }
212 
213         @Override
hasStableIds()214         public boolean hasStableIds() {
215             return false;
216         }
217 
218         @Override
getCount()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
getItem(int position)229         public Object getItem(int position) {
230             return ((DeviceAdminListItem) (mAdmins.get(position))).info;
231         }
232 
233         @Override
getItemId(int position)234         public long getItemId(int position) {
235             return position;
236         }
237 
238         @Override
areAllItemsEnabled()239         public boolean areAllItemsEnabled() {
240             return false;
241         }
242 
243         /**
244          * See {@link #getItemViewType} for the view types.
245          */
246         @Override
getViewTypeCount()247         public int getViewTypeCount() {
248             return 1;
249         }
250 
251         /**
252          * Returns 0 for all types.
253          */
254         @Override
getItemViewType(int position)255         public int getItemViewType(int position) {
256             return 0;
257         }
258 
259         @Override
isEnabled(int position)260         public boolean isEnabled(int position) {
261             Object o = getItem(position);
262             return isEnabled(o);
263         }
264 
isEnabled(Object o)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
getView(int position, View convertView, ViewGroup parent)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 
newDeviceAdminView(ViewGroup parent)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 
bindView(View view, DeviceAdminInfo item)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 
isDeviceOwner(DeviceAdminInfo item)316     private boolean isDeviceOwner(DeviceAdminInfo item) {
317         return getUserId(item) == UserHandle.myUserId()
318                 && item.getPackageName().equals(mDeviceOwnerPkg);
319     }
320 
isProfileOwner(DeviceAdminInfo item)321     private boolean isProfileOwner(DeviceAdminInfo item) {
322         ComponentName profileOwner = mProfileOwnerComponents.get(getUserId(item));
323         return item.getComponent().equals(profileOwner);
324     }
325 
isActiveAdmin(DeviceAdminInfo item)326     private boolean isActiveAdmin(DeviceAdminInfo item) {
327         return mDPM.isAdminActiveAsUser(item.getComponent(), getUserId(item));
328     }
329 
isRemovingAdmin(DeviceAdminInfo item)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      */
updateAvailableAdminsForProfile(final int profileId)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      */
addDeviceAdminBroadcastReceiversForProfile( Collection<ComponentName> alreadyAddedComponents, final int profileId)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      */
addActiveAdminsForProfile(final List<ComponentName> activeAdmins, final int profileId)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      */
createDeviceAdminInfo(ActivityInfo ai)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      */
getUserId(DeviceAdminInfo adminInfo)458     private int getUserId(DeviceAdminInfo adminInfo) {
459         return UserHandle.getUserId(adminInfo.getActivityInfo().applicationInfo.uid);
460     }
461 }
462