AppRestrictionsFragment.java revision 928191881df05c6520c1da8c208b6a7f47f52d45
1/* 2 * Copyright (C) 2013 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 17package com.android.settings.users; 18 19import android.app.Activity; 20import android.app.AppGlobals; 21import android.appwidget.AppWidgetManager; 22import android.content.BroadcastReceiver; 23import android.content.Context; 24import android.content.Intent; 25import android.content.IntentFilter; 26import android.content.RestrictionEntry; 27import android.content.pm.ApplicationInfo; 28import android.content.pm.IPackageManager; 29import android.content.pm.PackageInfo; 30import android.content.pm.PackageManager; 31import android.content.pm.PackageManager.NameNotFoundException; 32import android.content.pm.ResolveInfo; 33import android.content.res.Resources; 34import android.graphics.Bitmap; 35import android.graphics.ColorFilter; 36import android.graphics.ColorMatrix; 37import android.graphics.ColorMatrixColorFilter; 38import android.graphics.drawable.Drawable; 39import android.os.AsyncTask; 40import android.os.Bundle; 41import android.os.RemoteException; 42import android.os.ServiceManager; 43import android.os.UserHandle; 44import android.os.UserManager; 45import android.preference.CheckBoxPreference; 46import android.preference.ListPreference; 47import android.preference.MultiSelectListPreference; 48import android.preference.Preference; 49import android.preference.Preference.OnPreferenceChangeListener; 50import android.preference.Preference.OnPreferenceClickListener; 51import android.preference.PreferenceGroup; 52import android.preference.SwitchPreference; 53import android.text.TextUtils; 54import android.util.Log; 55import android.view.View; 56import android.view.View.OnClickListener; 57import android.view.inputmethod.InputMethodInfo; 58import android.view.inputmethod.InputMethodManager; 59import android.view.ViewGroup; 60import android.widget.CheckBox; 61import android.widget.CompoundButton; 62import android.widget.CompoundButton.OnCheckedChangeListener; 63import android.widget.Switch; 64 65import com.android.settings.R; 66import com.android.settings.SettingsPreferenceFragment; 67 68import java.util.ArrayList; 69import java.util.Collections; 70import java.util.Comparator; 71import java.util.HashMap; 72import java.util.HashSet; 73import java.util.List; 74import java.util.Map; 75import java.util.Set; 76import java.util.StringTokenizer; 77 78public class AppRestrictionsFragment extends SettingsPreferenceFragment implements 79 OnPreferenceChangeListener, OnClickListener, OnPreferenceClickListener { 80 81 private static final String TAG = AppRestrictionsFragment.class.getSimpleName(); 82 83 private static final boolean DEBUG = false; 84 85 private static final String PKG_PREFIX = "pkg_"; 86 87 protected PackageManager mPackageManager; 88 protected UserManager mUserManager; 89 protected IPackageManager mIPm; 90 protected UserHandle mUser; 91 private PackageInfo mSysPackageInfo; 92 93 private PreferenceGroup mAppList; 94 95 private static final int MAX_APP_RESTRICTIONS = 100; 96 97 private static final String DELIMITER = ";"; 98 99 /** Key for extra passed in from calling fragment for the userId of the user being edited */ 100 public static final String EXTRA_USER_ID = "user_id"; 101 102 /** Key for extra passed in from calling fragment to indicate if this is a newly created user */ 103 public static final String EXTRA_NEW_USER = "new_user"; 104 105 HashMap<String,Boolean> mSelectedPackages = new HashMap<String,Boolean>(); 106 private boolean mFirstTime = true; 107 private boolean mNewUser; 108 private boolean mAppListChanged; 109 protected boolean mRestrictedProfile; 110 111 private static final int CUSTOM_REQUEST_CODE_START = 1000; 112 private int mCustomRequestCode = CUSTOM_REQUEST_CODE_START; 113 114 private HashMap<Integer, AppRestrictionsPreference> mCustomRequestMap = 115 new HashMap<Integer,AppRestrictionsPreference>(); 116 117 private List<SelectableAppInfo> mVisibleApps; 118 private List<ApplicationInfo> mUserApps; 119 private AsyncTask mAppLoadingTask; 120 121 private BroadcastReceiver mUserBackgrounding = new BroadcastReceiver() { 122 @Override 123 public void onReceive(Context context, Intent intent) { 124 // Update the user's app selection right away without waiting for a pause 125 // onPause() might come in too late, causing apps to disappear after broadcasts 126 // have been scheduled during user startup. 127 if (mAppListChanged) { 128 if (DEBUG) Log.d(TAG, "User backgrounding, update app list"); 129 applyUserAppsStates(); 130 if (DEBUG) Log.d(TAG, "User backgrounding, done updating app list"); 131 } 132 } 133 }; 134 135 private BroadcastReceiver mPackageObserver = new BroadcastReceiver() { 136 @Override 137 public void onReceive(Context context, Intent intent) { 138 onPackageChanged(intent); 139 } 140 }; 141 142 static class SelectableAppInfo { 143 String packageName; 144 CharSequence appName; 145 CharSequence activityName; 146 Drawable icon; 147 SelectableAppInfo masterEntry; 148 149 @Override 150 public String toString() { 151 return packageName + ": appName=" + appName + "; activityName=" + activityName 152 + "; icon=" + icon + "; masterEntry=" + masterEntry; 153 } 154 } 155 156 static class AppRestrictionsPreference extends SwitchPreference { 157 private boolean hasSettings; 158 private OnClickListener listener; 159 private ArrayList<RestrictionEntry> restrictions; 160 private boolean mPanelOpen; 161 private boolean immutable; 162 private List<Preference> mChildren = new ArrayList<Preference>(); 163 private final ColorFilter grayscaleFilter; 164 165 AppRestrictionsPreference(Context context, OnClickListener listener) { 166 super(context); 167 setLayoutResource(R.layout.preference_app_restrictions); 168 this.listener = listener; 169 170 ColorMatrix colorMatrix = new ColorMatrix(); 171 colorMatrix.setSaturation(0f); 172 float[] matrix = colorMatrix.getArray(); 173 matrix[18] = 0.5f; 174 grayscaleFilter = new ColorMatrixColorFilter(colorMatrix); 175 } 176 177 private void setSettingsEnabled(boolean enable) { 178 hasSettings = enable; 179 } 180 181 @Override 182 public void setChecked(boolean checked) { 183 if (checked) { 184 getIcon().setColorFilter(null); 185 } else { 186 getIcon().setColorFilter(grayscaleFilter); 187 } 188 super.setChecked(checked); 189 } 190 191 void setRestrictions(ArrayList<RestrictionEntry> restrictions) { 192 this.restrictions = restrictions; 193 } 194 195 void setImmutable(boolean immutable) { 196 this.immutable = immutable; 197 } 198 199 boolean isImmutable() { 200 return immutable; 201 } 202 203 RestrictionEntry getRestriction(String key) { 204 if (restrictions == null) return null; 205 for (RestrictionEntry entry : restrictions) { 206 if (entry.getKey().equals(key)) { 207 return entry; 208 } 209 } 210 return null; 211 } 212 213 ArrayList<RestrictionEntry> getRestrictions() { 214 return restrictions; 215 } 216 217 boolean isPanelOpen() { 218 return mPanelOpen; 219 } 220 221 List<Preference> getChildren() { 222 return mChildren; 223 } 224 225 @Override 226 protected void onBindView(View view) { 227 super.onBindView(view); 228 229 View appRestrictionsSettings = view.findViewById(R.id.app_restrictions_settings); 230 appRestrictionsSettings.setVisibility(hasSettings ? View.VISIBLE : View.GONE); 231 view.findViewById(R.id.settings_divider).setVisibility( 232 hasSettings ? View.VISIBLE : View.GONE); 233 appRestrictionsSettings.setOnClickListener(listener); 234 appRestrictionsSettings.setTag(this); 235 236 View appRestrictionsPref = view.findViewById(R.id.app_restrictions_pref); 237 appRestrictionsPref.setOnClickListener(listener); 238 appRestrictionsPref.setTag(this); 239 240 ViewGroup widget = (ViewGroup) view.findViewById(android.R.id.widget_frame); 241 widget.setEnabled(!isImmutable()); 242 if (widget.getChildCount() > 0) { 243 final Switch toggle = (Switch) widget.getChildAt(0); 244 toggle.setEnabled(!isImmutable()); 245 toggle.setTag(this); 246 toggle.setOnCheckedChangeListener(new OnCheckedChangeListener() { 247 @Override 248 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 249 listener.onClick(toggle); 250 } 251 }); 252 } 253 } 254 } 255 256 protected void init(Bundle icicle) { 257 if (icicle != null) { 258 mUser = new UserHandle(icicle.getInt(EXTRA_USER_ID)); 259 } else { 260 Bundle args = getArguments(); 261 if (args != null) { 262 if (args.containsKey(EXTRA_USER_ID)) { 263 mUser = new UserHandle(args.getInt(EXTRA_USER_ID)); 264 } 265 mNewUser = args.getBoolean(EXTRA_NEW_USER, false); 266 } 267 } 268 269 if (mUser == null) { 270 mUser = android.os.Process.myUserHandle(); 271 } 272 273 mPackageManager = getActivity().getPackageManager(); 274 mIPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); 275 mUserManager = (UserManager) getActivity().getSystemService(Context.USER_SERVICE); 276 mRestrictedProfile = mUserManager.getUserInfo(mUser.getIdentifier()).isRestricted(); 277 try { 278 mSysPackageInfo = mPackageManager.getPackageInfo("android", 279 PackageManager.GET_SIGNATURES); 280 } catch (NameNotFoundException nnfe) { 281 // ? 282 } 283 addPreferencesFromResource(R.xml.app_restrictions); 284 mAppList = getAppPreferenceGroup(); 285 } 286 287 @Override 288 public void onSaveInstanceState(Bundle outState) { 289 super.onSaveInstanceState(outState); 290 outState.putInt(EXTRA_USER_ID, mUser.getIdentifier()); 291 } 292 293 @Override 294 public void onResume() { 295 super.onResume(); 296 297 getActivity().registerReceiver(mUserBackgrounding, 298 new IntentFilter(Intent.ACTION_USER_BACKGROUND)); 299 IntentFilter packageFilter = new IntentFilter(); 300 packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 301 packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 302 packageFilter.addDataScheme("package"); 303 getActivity().registerReceiver(mPackageObserver, packageFilter); 304 305 mAppListChanged = false; 306 if (mAppLoadingTask == null || mAppLoadingTask.getStatus() == AsyncTask.Status.FINISHED) { 307 mAppLoadingTask = new AppLoadingTask().execute((Void[]) null); 308 } 309 } 310 311 @Override 312 public void onPause() { 313 super.onPause(); 314 mNewUser = false; 315 getActivity().unregisterReceiver(mUserBackgrounding); 316 getActivity().unregisterReceiver(mPackageObserver); 317 if (mAppListChanged) { 318 new Thread() { 319 public void run() { 320 applyUserAppsStates(); 321 } 322 }.start(); 323 } 324 } 325 326 private void onPackageChanged(Intent intent) { 327 String action = intent.getAction(); 328 String packageName = intent.getData().getSchemeSpecificPart(); 329 // Package added, check if the preference needs to be enabled 330 AppRestrictionsPreference pref = (AppRestrictionsPreference) 331 findPreference(getKeyForPackage(packageName)); 332 if (pref == null) return; 333 334 if ((Intent.ACTION_PACKAGE_ADDED.equals(action) && pref.isChecked()) 335 || (Intent.ACTION_PACKAGE_REMOVED.equals(action) && !pref.isChecked())) { 336 pref.setEnabled(true); 337 } 338 } 339 340 protected PreferenceGroup getAppPreferenceGroup() { 341 return getPreferenceScreen(); 342 } 343 344 protected Drawable getCircularUserIcon() { 345 Bitmap userIcon = mUserManager.getUserIcon(mUser.getIdentifier()); 346 CircleFramedDrawable circularIcon = 347 CircleFramedDrawable.getInstance(this.getActivity(), userIcon); 348 return circularIcon; 349 } 350 351 protected void clearSelectedApps() { 352 mSelectedPackages.clear(); 353 } 354 355 private void applyUserAppsStates() { 356 final int userId = mUser.getIdentifier(); 357 if (!mUserManager.getUserInfo(userId).isRestricted() && userId != UserHandle.myUserId()) { 358 Log.e(TAG, "Cannot apply application restrictions on another user!"); 359 return; 360 } 361 for (Map.Entry<String,Boolean> entry : mSelectedPackages.entrySet()) { 362 String packageName = entry.getKey(); 363 boolean enabled = entry.getValue(); 364 applyUserAppState(packageName, enabled); 365 } 366 } 367 368 private void applyUserAppState(String packageName, boolean enabled) { 369 final int userId = mUser.getIdentifier(); 370 if (enabled) { 371 // Enable selected apps 372 try { 373 ApplicationInfo info = mIPm.getApplicationInfo(packageName, 374 PackageManager.GET_UNINSTALLED_PACKAGES, userId); 375 if (info == null || info.enabled == false 376 || (info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) { 377 mIPm.installExistingPackageAsUser(packageName, mUser.getIdentifier()); 378 if (DEBUG) { 379 Log.d(TAG, "Installing " + packageName); 380 } 381 } 382 if (info != null && (info.flags&ApplicationInfo.FLAG_BLOCKED) != 0 383 && (info.flags&ApplicationInfo.FLAG_INSTALLED) != 0) { 384 disableUiForPackage(packageName); 385 mIPm.setApplicationBlockedSettingAsUser(packageName, false, userId); 386 if (DEBUG) { 387 Log.d(TAG, "Unblocking " + packageName); 388 } 389 } 390 } catch (RemoteException re) { 391 } 392 } else { 393 // Blacklist all other apps, system or downloaded 394 try { 395 ApplicationInfo info = mIPm.getApplicationInfo(packageName, 0, userId); 396 if (info != null) { 397 if (mRestrictedProfile) { 398 mIPm.deletePackageAsUser(packageName, null, mUser.getIdentifier(), 399 PackageManager.DELETE_SYSTEM_APP); 400 if (DEBUG) { 401 Log.d(TAG, "Uninstalling " + packageName); 402 } 403 } else { 404 disableUiForPackage(packageName); 405 mIPm.setApplicationBlockedSettingAsUser(packageName, true, userId); 406 if (DEBUG) { 407 Log.d(TAG, "Blocking " + packageName); 408 } 409 } 410 } 411 } catch (RemoteException re) { 412 } 413 } 414 } 415 416 private void disableUiForPackage(String packageName) { 417 AppRestrictionsPreference pref = (AppRestrictionsPreference) findPreference( 418 getKeyForPackage(packageName)); 419 if (pref != null) { 420 pref.setEnabled(false); 421 } 422 } 423 424 private boolean isSystemPackage(String packageName) { 425 try { 426 final PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0); 427 if (pi.applicationInfo == null) return false; 428 final int flags = pi.applicationInfo.flags; 429 if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0 430 || (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { 431 return true; 432 } 433 } catch (NameNotFoundException nnfe) { 434 // Missing package? 435 } 436 return false; 437 } 438 439 /** 440 * Find all pre-installed input methods that are marked as default 441 * and add them to an exclusion list so that they aren't 442 * presented to the user for toggling. 443 * Don't add non-default ones, as they may include other stuff that we 444 * don't need to auto-include. 445 * @param excludePackages the set of package names to append to 446 */ 447 private void addSystemImes(Set<String> excludePackages) { 448 final Context context = getActivity(); 449 if (context == null) return; 450 InputMethodManager imm = (InputMethodManager) 451 context.getSystemService(Context.INPUT_METHOD_SERVICE); 452 List<InputMethodInfo> imis = imm.getInputMethodList(); 453 for (InputMethodInfo imi : imis) { 454 try { 455 if (imi.isDefault(context) && isSystemPackage(imi.getPackageName())) { 456 excludePackages.add(imi.getPackageName()); 457 } 458 } catch (Resources.NotFoundException rnfe) { 459 // Not default 460 } 461 } 462 } 463 464 /** 465 * Add system apps that match an intent to the list, excluding any packages in the exclude list. 466 * @param visibleApps list of apps to append the new list to 467 * @param intent the intent to match 468 * @param excludePackages the set of package names to be excluded, since they're required 469 */ 470 private void addSystemApps(List<SelectableAppInfo> visibleApps, Intent intent, 471 Set<String> excludePackages) { 472 if (getActivity() == null) return; 473 final PackageManager pm = mPackageManager; 474 List<ResolveInfo> launchableApps = pm.queryIntentActivities(intent, 475 PackageManager.GET_DISABLED_COMPONENTS | PackageManager.GET_UNINSTALLED_PACKAGES); 476 for (ResolveInfo app : launchableApps) { 477 if (app.activityInfo != null && app.activityInfo.applicationInfo != null) { 478 final String packageName = app.activityInfo.packageName; 479 int flags = app.activityInfo.applicationInfo.flags; 480 if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0 481 || (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { 482 // System app 483 // Skip excluded packages 484 if (excludePackages.contains(packageName)) continue; 485 int enabled = pm.getApplicationEnabledSetting(packageName); 486 if (enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED 487 || enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) { 488 // Check if the app is already enabled for the target user 489 ApplicationInfo targetUserAppInfo = getAppInfoForUser(packageName, 490 0, mUser); 491 if (targetUserAppInfo == null 492 || (targetUserAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) { 493 continue; 494 } 495 } 496 SelectableAppInfo info = new SelectableAppInfo(); 497 info.packageName = app.activityInfo.packageName; 498 info.appName = app.activityInfo.applicationInfo.loadLabel(pm); 499 info.icon = app.activityInfo.loadIcon(pm); 500 info.activityName = app.activityInfo.loadLabel(pm); 501 if (info.activityName == null) info.activityName = info.appName; 502 503 visibleApps.add(info); 504 } 505 } 506 } 507 } 508 509 private ApplicationInfo getAppInfoForUser(String packageName, int flags, UserHandle user) { 510 try { 511 ApplicationInfo targetUserAppInfo = mIPm.getApplicationInfo(packageName, flags, 512 user.getIdentifier()); 513 return targetUserAppInfo; 514 } catch (RemoteException re) { 515 return null; 516 } 517 } 518 519 private class AppLoadingTask extends AsyncTask<Void, Void, Void> { 520 521 @Override 522 protected Void doInBackground(Void... params) { 523 fetchAndMergeApps(); 524 return null; 525 } 526 527 @Override 528 protected void onPostExecute(Void result) { 529 populateApps(); 530 } 531 532 @Override 533 protected void onPreExecute() { 534 } 535 } 536 537 private void fetchAndMergeApps() { 538 mAppList.setOrderingAsAdded(false); 539 mVisibleApps = new ArrayList<SelectableAppInfo>(); 540 final Context context = getActivity(); 541 if (context == null) return; 542 final PackageManager pm = mPackageManager; 543 final IPackageManager ipm = mIPm; 544 545 final HashSet<String> excludePackages = new HashSet<String>(); 546 addSystemImes(excludePackages); 547 548 // Add launchers 549 Intent launcherIntent = new Intent(Intent.ACTION_MAIN); 550 launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER); 551 addSystemApps(mVisibleApps, launcherIntent, excludePackages); 552 553 // Add widgets 554 Intent widgetIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); 555 addSystemApps(mVisibleApps, widgetIntent, excludePackages); 556 557 List<ApplicationInfo> installedApps = pm.getInstalledApplications( 558 PackageManager.GET_UNINSTALLED_PACKAGES); 559 for (ApplicationInfo app : installedApps) { 560 // If it's not installed, skip 561 if ((app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) continue; 562 563 if ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 564 && (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) { 565 // Downloaded app 566 SelectableAppInfo info = new SelectableAppInfo(); 567 info.packageName = app.packageName; 568 info.appName = app.loadLabel(pm); 569 info.activityName = info.appName; 570 info.icon = app.loadIcon(pm); 571 mVisibleApps.add(info); 572 } else { 573 try { 574 PackageInfo pi = pm.getPackageInfo(app.packageName, 0); 575 // If it's a system app that requires an account and doesn't see restricted 576 // accounts, mark for removal. It might get shown in the UI if it has an icon 577 // but will still be marked as false and immutable. 578 if (mRestrictedProfile 579 && pi.requiredAccountType != null && pi.restrictedAccountType == null) { 580 mSelectedPackages.put(app.packageName, false); 581 } 582 } catch (NameNotFoundException re) { 583 } 584 } 585 } 586 587 // Get the list of apps already installed for the user 588 mUserApps = null; 589 try { 590 mUserApps = ipm.getInstalledApplications( 591 PackageManager.GET_UNINSTALLED_PACKAGES, mUser.getIdentifier()).getList(); 592 } catch (RemoteException re) { 593 } 594 595 if (mUserApps != null) { 596 for (ApplicationInfo app : mUserApps) { 597 if ((app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) continue; 598 599 if ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 600 && (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) { 601 // Downloaded app 602 SelectableAppInfo info = new SelectableAppInfo(); 603 info.packageName = app.packageName; 604 info.appName = app.loadLabel(pm); 605 info.activityName = info.appName; 606 info.icon = app.loadIcon(pm); 607 mVisibleApps.add(info); 608 } 609 } 610 } 611 612 // Sort the list of visible apps 613 Collections.sort(mVisibleApps, new AppLabelComparator()); 614 615 // Remove dupes 616 Set<String> dedupPackageSet = new HashSet<String>(); 617 for (int i = mVisibleApps.size() - 1; i >= 0; i--) { 618 SelectableAppInfo info = mVisibleApps.get(i); 619 if (DEBUG) Log.i(TAG, info.toString()); 620 String both = info.packageName + "+" + info.activityName; 621 if (!TextUtils.isEmpty(info.packageName) 622 && !TextUtils.isEmpty(info.activityName) 623 && dedupPackageSet.contains(both)) { 624 mVisibleApps.remove(i); 625 } else { 626 dedupPackageSet.add(both); 627 } 628 } 629 630 // Establish master/slave relationship for entries that share a package name 631 HashMap<String,SelectableAppInfo> packageMap = new HashMap<String,SelectableAppInfo>(); 632 for (SelectableAppInfo info : mVisibleApps) { 633 if (packageMap.containsKey(info.packageName)) { 634 info.masterEntry = packageMap.get(info.packageName); 635 } else { 636 packageMap.put(info.packageName, info); 637 } 638 } 639 } 640 641 private boolean isPlatformSigned(PackageInfo pi) { 642 return (pi != null && pi.signatures != null && 643 mSysPackageInfo.signatures[0].equals(pi.signatures[0])); 644 } 645 646 private boolean isAppEnabledForUser(PackageInfo pi) { 647 if (pi == null) return false; 648 final int flags = pi.applicationInfo.flags; 649 // Return true if it is installed and not blocked 650 return ((flags&ApplicationInfo.FLAG_INSTALLED) != 0 651 && (flags&ApplicationInfo.FLAG_BLOCKED) == 0); 652 } 653 654 private void populateApps() { 655 final Context context = getActivity(); 656 if (context == null) return; 657 final PackageManager pm = mPackageManager; 658 final IPackageManager ipm = mIPm; 659 660 mAppList.removeAll(); 661 Intent restrictionsIntent = new Intent(Intent.ACTION_GET_RESTRICTION_ENTRIES); 662 final List<ResolveInfo> receivers = pm.queryBroadcastReceivers(restrictionsIntent, 0); 663 int i = 0; 664 if (mVisibleApps.size() > 0) { 665 for (SelectableAppInfo app : mVisibleApps) { 666 String packageName = app.packageName; 667 if (packageName == null) continue; 668 final boolean isSettingsApp = packageName.equals(context.getPackageName()); 669 AppRestrictionsPreference p = new AppRestrictionsPreference(context, this); 670 final boolean hasSettings = resolveInfoListHasPackage(receivers, packageName); 671 p.setIcon(app.icon != null ? app.icon.mutate() : null); 672 p.setChecked(false); 673 p.setTitle(app.activityName); 674 if (app.masterEntry != null) { 675 p.setSummary(context.getString(R.string.user_restrictions_controlled_by, 676 app.masterEntry.activityName)); 677 } 678 p.setKey(getKeyForPackage(packageName)); 679 p.setSettingsEnabled(hasSettings || isSettingsApp); 680 p.setPersistent(false); 681 p.setOnPreferenceChangeListener(this); 682 p.setOnPreferenceClickListener(this); 683 PackageInfo pi = null; 684 try { 685 pi = ipm.getPackageInfo(packageName, 686 PackageManager.GET_UNINSTALLED_PACKAGES 687 | PackageManager.GET_SIGNATURES, mUser.getIdentifier()); 688 } catch (RemoteException e) { 689 } 690 if (pi != null && (pi.requiredForAllUsers || isPlatformSigned(pi))) { 691 p.setChecked(true); 692 p.setImmutable(true); 693 // If the app is required and has no restrictions, skip showing it 694 if (!hasSettings && !isSettingsApp) continue; 695 // Get and populate the defaults, since the user is not going to be 696 // able to toggle this app ON (it's ON by default and immutable). 697 // Only do this for restricted profiles, not single-user restrictions 698 if (hasSettings) { 699 requestRestrictionsForApp(packageName, p); 700 } 701 } else if (!mNewUser && isAppEnabledForUser(pi)) { 702 p.setChecked(true); 703 } 704 if (mRestrictedProfile 705 && pi.requiredAccountType != null && pi.restrictedAccountType == null) { 706 p.setChecked(false); 707 p.setImmutable(true); 708 p.setSummary(R.string.app_not_supported_in_limited); 709 } 710 if (mRestrictedProfile && pi.restrictedAccountType != null) { 711 p.setSummary(R.string.app_sees_restricted_accounts); 712 } 713 if (app.masterEntry != null) { 714 p.setImmutable(true); 715 p.setChecked(mSelectedPackages.get(packageName)); 716 } 717 mAppList.addPreference(p); 718 if (isSettingsApp) { 719 p.setOrder(MAX_APP_RESTRICTIONS * 1); 720 } else { 721 p.setOrder(MAX_APP_RESTRICTIONS * (i + 2)); 722 } 723 mSelectedPackages.put(packageName, p.isChecked()); 724 mAppListChanged = true; 725 i++; 726 } 727 } 728 // If this is the first time for a new profile, install/uninstall default apps for profile 729 // to avoid taking the hit in onPause(), which can cause race conditions on user switch. 730 if (mNewUser && mFirstTime) { 731 mFirstTime = false; 732 applyUserAppsStates(); 733 } 734 } 735 736 private String getKeyForPackage(String packageName) { 737 return PKG_PREFIX + packageName; 738 } 739 740 private class AppLabelComparator implements Comparator<SelectableAppInfo> { 741 742 @Override 743 public int compare(SelectableAppInfo lhs, SelectableAppInfo rhs) { 744 String lhsLabel = lhs.activityName.toString(); 745 String rhsLabel = rhs.activityName.toString(); 746 return lhsLabel.toLowerCase().compareTo(rhsLabel.toLowerCase()); 747 } 748 } 749 750 private boolean resolveInfoListHasPackage(List<ResolveInfo> receivers, String packageName) { 751 for (ResolveInfo info : receivers) { 752 if (info.activityInfo.packageName.equals(packageName)) { 753 return true; 754 } 755 } 756 return false; 757 } 758 759 private void updateAllEntries(String prefKey, boolean checked) { 760 for (int i = 0; i < mAppList.getPreferenceCount(); i++) { 761 Preference pref = mAppList.getPreference(i); 762 if (pref instanceof AppRestrictionsPreference) { 763 if (prefKey.equals(pref.getKey())) { 764 ((AppRestrictionsPreference) pref).setChecked(checked); 765 } 766 } 767 } 768 } 769 770 @Override 771 public void onClick(View v) { 772 if (v.getTag() instanceof AppRestrictionsPreference) { 773 AppRestrictionsPreference pref = (AppRestrictionsPreference) v.getTag(); 774 if (v.getId() == R.id.app_restrictions_settings) { 775 toggleAppPanel(pref); 776 } else if (!pref.isImmutable()) { 777 pref.setChecked(!pref.isChecked()); 778 final String packageName = pref.getKey().substring(PKG_PREFIX.length()); 779 mSelectedPackages.put(packageName, pref.isChecked()); 780 if (pref.isChecked() && pref.hasSettings 781 && pref.restrictions == null) { 782 // The restrictions have not been initialized, get and save them 783 requestRestrictionsForApp(packageName, pref); 784 } 785 mAppListChanged = true; 786 // If it's not a restricted profile, apply the changes immediately 787 if (!mRestrictedProfile) { 788 applyUserAppState(packageName, pref.isChecked()); 789 } 790 updateAllEntries(pref.getKey(), pref.isChecked()); 791 } 792 } 793 } 794 795 @Override 796 public boolean onPreferenceChange(Preference preference, Object newValue) { 797 String key = preference.getKey(); 798 if (key != null && key.contains(DELIMITER)) { 799 StringTokenizer st = new StringTokenizer(key, DELIMITER); 800 final String packageName = st.nextToken(); 801 final String restrictionKey = st.nextToken(); 802 AppRestrictionsPreference appPref = (AppRestrictionsPreference) 803 mAppList.findPreference(PKG_PREFIX+packageName); 804 ArrayList<RestrictionEntry> restrictions = appPref.getRestrictions(); 805 if (restrictions != null) { 806 for (RestrictionEntry entry : restrictions) { 807 if (entry.getKey().equals(restrictionKey)) { 808 switch (entry.getType()) { 809 case RestrictionEntry.TYPE_BOOLEAN: 810 entry.setSelectedState((Boolean) newValue); 811 break; 812 case RestrictionEntry.TYPE_CHOICE: 813 case RestrictionEntry.TYPE_CHOICE_LEVEL: 814 ListPreference listPref = (ListPreference) preference; 815 entry.setSelectedString((String) newValue); 816 String readable = findInArray(entry.getChoiceEntries(), 817 entry.getChoiceValues(), (String) newValue); 818 listPref.setSummary(readable); 819 break; 820 case RestrictionEntry.TYPE_MULTI_SELECT: 821 Set<String> set = (Set<String>) newValue; 822 String [] selectedValues = new String[set.size()]; 823 set.toArray(selectedValues); 824 entry.setAllSelectedStrings(selectedValues); 825 break; 826 default: 827 continue; 828 } 829 if (packageName.equals(getActivity().getPackageName())) { 830 RestrictionUtils.setRestrictions(getActivity(), restrictions, mUser); 831 } else { 832 mUserManager.setApplicationRestrictions(packageName, 833 RestrictionUtils.restrictionsToBundle(restrictions), 834 mUser); 835 } 836 break; 837 } 838 } 839 } 840 } 841 return true; 842 } 843 844 private void toggleAppPanel(AppRestrictionsPreference preference) { 845 if (preference.getKey().startsWith(PKG_PREFIX)) { 846 if (preference.mPanelOpen) { 847 for (Preference p : preference.mChildren) { 848 mAppList.removePreference(p); 849 } 850 preference.mChildren.clear(); 851 } else { 852 String packageName = preference.getKey().substring(PKG_PREFIX.length()); 853 if (packageName.equals(getActivity().getPackageName())) { 854 // Settings, fake it by using user restrictions 855 ArrayList<RestrictionEntry> restrictions = RestrictionUtils.getRestrictions( 856 getActivity(), mUser); 857 onRestrictionsReceived(preference, packageName, restrictions); 858 } else { 859 requestRestrictionsForApp(packageName, preference); 860 } 861 } 862 preference.mPanelOpen = !preference.mPanelOpen; 863 } 864 } 865 866 private void requestRestrictionsForApp(String packageName, 867 AppRestrictionsPreference preference) { 868 Bundle oldEntries = 869 mUserManager.getApplicationRestrictions(packageName, mUser); 870 Intent intent = new Intent(Intent.ACTION_GET_RESTRICTION_ENTRIES); 871 intent.setPackage(packageName); 872 intent.putExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE, oldEntries); 873 intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); 874 getActivity().sendOrderedBroadcast(intent, null, 875 new RestrictionsResultReceiver(packageName, preference), 876 null, Activity.RESULT_OK, null, null); 877 } 878 879 class RestrictionsResultReceiver extends BroadcastReceiver { 880 881 private static final String CUSTOM_RESTRICTIONS_INTENT = Intent.EXTRA_RESTRICTIONS_INTENT; 882 String packageName; 883 AppRestrictionsPreference preference; 884 885 RestrictionsResultReceiver(String packageName, AppRestrictionsPreference preference) { 886 super(); 887 this.packageName = packageName; 888 this.preference = preference; 889 } 890 891 @Override 892 public void onReceive(Context context, Intent intent) { 893 Bundle results = getResultExtras(true); 894 final ArrayList<RestrictionEntry> restrictions = results.getParcelableArrayList( 895 Intent.EXTRA_RESTRICTIONS_LIST); 896 Intent restrictionsIntent = (Intent) results.getParcelable(CUSTOM_RESTRICTIONS_INTENT); 897 if (restrictions != null && restrictionsIntent == null) { 898 onRestrictionsReceived(preference, packageName, restrictions); 899 if (mRestrictedProfile) { 900 mUserManager.setApplicationRestrictions(packageName, 901 RestrictionUtils.restrictionsToBundle(restrictions), mUser); 902 } 903 } else if (restrictionsIntent != null) { 904 final Intent customIntent = restrictionsIntent; 905 if (restrictions != null) { 906 customIntent.putExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE, 907 RestrictionUtils.restrictionsToBundle(restrictions)); 908 } 909 Preference p = new Preference(context); 910 p.setTitle(R.string.app_restrictions_custom_label); 911 p.setOnPreferenceClickListener(new OnPreferenceClickListener() { 912 @Override 913 public boolean onPreferenceClick(Preference preference) { 914 int requestCode = generateCustomActivityRequestCode( 915 RestrictionsResultReceiver.this.preference); 916 AppRestrictionsFragment.this.startActivityForResult( 917 customIntent, requestCode); 918 return false; 919 } 920 }); 921 p.setPersistent(false); 922 p.setOrder(preference.getOrder() + 1); 923 preference.mChildren.add(p); 924 mAppList.addPreference(p); 925 preference.setRestrictions(restrictions); 926 } 927 } 928 } 929 930 private void onRestrictionsReceived(AppRestrictionsPreference preference, String packageName, 931 ArrayList<RestrictionEntry> restrictions) { 932 // Non-custom-activity case - expand the restrictions in-place 933 final Context context = preference.getContext(); 934 int count = 1; 935 for (RestrictionEntry entry : restrictions) { 936 Preference p = null; 937 switch (entry.getType()) { 938 case RestrictionEntry.TYPE_BOOLEAN: 939 p = new CheckBoxPreference(context); 940 p.setTitle(entry.getTitle()); 941 p.setSummary(entry.getDescription()); 942 ((CheckBoxPreference)p).setChecked(entry.getSelectedState()); 943 break; 944 case RestrictionEntry.TYPE_CHOICE: 945 case RestrictionEntry.TYPE_CHOICE_LEVEL: 946 p = new ListPreference(context); 947 p.setTitle(entry.getTitle()); 948 String value = entry.getSelectedString(); 949 if (value == null) { 950 value = entry.getDescription(); 951 } 952 p.setSummary(findInArray(entry.getChoiceEntries(), entry.getChoiceValues(), 953 value)); 954 ((ListPreference)p).setEntryValues(entry.getChoiceValues()); 955 ((ListPreference)p).setEntries(entry.getChoiceEntries()); 956 ((ListPreference)p).setValue(value); 957 ((ListPreference)p).setDialogTitle(entry.getTitle()); 958 break; 959 case RestrictionEntry.TYPE_MULTI_SELECT: 960 p = new MultiSelectListPreference(context); 961 p.setTitle(entry.getTitle()); 962 ((MultiSelectListPreference)p).setEntryValues(entry.getChoiceValues()); 963 ((MultiSelectListPreference)p).setEntries(entry.getChoiceEntries()); 964 HashSet<String> set = new HashSet<String>(); 965 for (String s : entry.getAllSelectedStrings()) { 966 set.add(s); 967 } 968 ((MultiSelectListPreference)p).setValues(set); 969 ((MultiSelectListPreference)p).setDialogTitle(entry.getTitle()); 970 break; 971 case RestrictionEntry.TYPE_NULL: 972 default: 973 } 974 if (p != null) { 975 p.setPersistent(false); 976 p.setOrder(preference.getOrder() + count); 977 // Store the restrictions key string as a key for the preference 978 p.setKey(preference.getKey().substring(PKG_PREFIX.length()) + DELIMITER 979 + entry.getKey()); 980 mAppList.addPreference(p); 981 p.setOnPreferenceChangeListener(AppRestrictionsFragment.this); 982 preference.mChildren.add(p); 983 count++; 984 } 985 } 986 preference.setRestrictions(restrictions); 987 if (count == 1 // No visible restrictions 988 && preference.isImmutable() 989 && preference.isChecked()) { 990 // Special case of required app with no visible restrictions. Remove it 991 mAppList.removePreference(preference); 992 } 993 } 994 995 /** 996 * Generates a request code that is stored in a map to retrieve the associated 997 * AppRestrictionsPreference. 998 * @param preference 999 * @return 1000 */ 1001 private int generateCustomActivityRequestCode(AppRestrictionsPreference preference) { 1002 mCustomRequestCode++; 1003 mCustomRequestMap.put(mCustomRequestCode, preference); 1004 return mCustomRequestCode; 1005 } 1006 1007 @Override 1008 public void onActivityResult(int requestCode, int resultCode, Intent data) { 1009 super.onActivityResult(requestCode, resultCode, data); 1010 1011 AppRestrictionsPreference pref = mCustomRequestMap.get(requestCode); 1012 if (pref == null) { 1013 Log.w(TAG, "Unknown requestCode " + requestCode); 1014 return; 1015 } 1016 1017 if (resultCode == Activity.RESULT_OK) { 1018 String packageName = pref.getKey().substring(PKG_PREFIX.length()); 1019 ArrayList<RestrictionEntry> list = 1020 data.getParcelableArrayListExtra(Intent.EXTRA_RESTRICTIONS_LIST); 1021 Bundle bundle = data.getBundleExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE); 1022 if (list != null) { 1023 // If there's a valid result, persist it to the user manager. 1024 pref.setRestrictions(list); 1025 mUserManager.setApplicationRestrictions(packageName, 1026 RestrictionUtils.restrictionsToBundle(list), mUser); 1027 } else if (bundle != null) { 1028 // If there's a valid result, persist it to the user manager. 1029 mUserManager.setApplicationRestrictions(packageName, bundle, mUser); 1030 } 1031 toggleAppPanel(pref); 1032 } 1033 // Remove request from the map 1034 mCustomRequestMap.remove(requestCode); 1035 } 1036 1037 private String findInArray(String[] choiceEntries, String[] choiceValues, 1038 String selectedString) { 1039 for (int i = 0; i < choiceValues.length; i++) { 1040 if (choiceValues[i].equals(selectedString)) { 1041 return choiceEntries[i]; 1042 } 1043 } 1044 return selectedString; 1045 } 1046 1047 @Override 1048 public boolean onPreferenceClick(Preference preference) { 1049 if (preference.getKey().startsWith(PKG_PREFIX)) { 1050 AppRestrictionsPreference arp = (AppRestrictionsPreference) preference; 1051 if (!arp.isImmutable()) { 1052 final String packageName = arp.getKey().substring(PKG_PREFIX.length()); 1053 final boolean newEnabledState = !arp.isChecked(); 1054 arp.setChecked(newEnabledState); 1055 mSelectedPackages.put(packageName, newEnabledState); 1056 updateAllEntries(arp.getKey(), newEnabledState); 1057 mAppListChanged = true; 1058 applyUserAppState(packageName, newEnabledState); 1059 } 1060 return true; 1061 } 1062 return false; 1063 } 1064 1065} 1066