OSDN Git Service

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