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 
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     @Override
onAttach(Context context)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
getMetricsCategory()117     public int getMetricsCategory() {
118         return MetricsProto.MetricsEvent.DEVICE_ADMIN_SETTINGS;
119     }
120 
121     @Override
onCreate(Bundle icicle)122     public void onCreate(Bundle icicle) {
123         super.onCreate(icicle);
124     }
125 
126     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)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
onActivityCreated(Bundle savedInstanceState)135     public void onActivityCreated(Bundle savedInstanceState) {
136         super.onActivityCreated(savedInstanceState);
137         setHasOptionsMenu(true);
138         Utils.forceCustomPadding(getListView(), true /* additive padding */);
139     }
140 
141     @Override
onResume()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
onPause()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      */
updateList()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
onListItemClick(ListView l, View v, int position, long id)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 
PolicyListAdapter()211         PolicyListAdapter() {
212             mInflater = (LayoutInflater)
213                     getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
214         }
215 
216         @Override
hasStableIds()217         public boolean hasStableIds() {
218             return false;
219         }
220 
221         @Override
getCount()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
getItem(int position)232         public Object getItem(int position) {
233             return ((DeviceAdminListItem) (mAdmins.get(position))).info;
234         }
235 
236         @Override
getItemId(int position)237         public long getItemId(int position) {
238             return position;
239         }
240 
241         @Override
areAllItemsEnabled()242         public boolean areAllItemsEnabled() {
243             return false;
244         }
245 
246         /**
247          * See {@link #getItemViewType} for the view types.
248          */
249         @Override
getViewTypeCount()250         public int getViewTypeCount() {
251             return 1;
252         }
253 
254         /**
255          * Returns 0 for all types.
256          */
257         @Override
getItemViewType(int position)258         public int getItemViewType(int position) {
259             return 0;
260         }
261 
262         @Override
isEnabled(int position)263         public boolean isEnabled(int position) {
264             Object o = getItem(position);
265             return isEnabled(o);
266         }
267 
isEnabled(Object o)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
getView(int position, View convertView, ViewGroup parent)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 
newDeviceAdminView(ViewGroup parent)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 
bindView(View view, DeviceAdminInfo item)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 
isDeviceOwner(DeviceAdminInfo item)319     private boolean isDeviceOwner(DeviceAdminInfo item) {
320         return getUserId(item) == UserHandle.myUserId()
321                 && item.getPackageName().equals(mDeviceOwnerPkg);
322     }
323 
isProfileOwner(DeviceAdminInfo item)324     private boolean isProfileOwner(DeviceAdminInfo item) {
325         ComponentName profileOwner = mProfileOwnerComponents.get(getUserId(item));
326         return item.getComponent().equals(profileOwner);
327     }
328 
isActiveAdmin(DeviceAdminInfo item)329     private boolean isActiveAdmin(DeviceAdminInfo item) {
330         return mDPM.isAdminActiveAsUser(item.getComponent(), getUserId(item));
331     }
332 
isRemovingAdmin(DeviceAdminInfo item)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      */
updateAvailableAdminsForProfile(final int profileId)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      */
addDeviceAdminBroadcastReceiversForProfile( Collection<ComponentName> alreadyAddedComponents, final int profileId)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      */
addActiveAdminsForProfile(final List<ComponentName> activeAdmins, final int profileId)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      */
createDeviceAdminInfo(ActivityInfo ai)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      */
getUserId(DeviceAdminInfo adminInfo)461     private int getUserId(DeviceAdminInfo adminInfo) {
462         return UserHandle.getUserId(adminInfo.getActivityInfo().applicationInfo.uid);
463     }
464 }
465