AppSecurityPermissions.java revision e639da7baa23121e35aa06d6e182558e0e755696
1/* 2** 3** Copyright 2007, The Android Open Source Project 4** 5** Licensed under the Apache License, Version 2.0 (the "License"); 6** you may not use this file except in compliance with the License. 7** You may obtain a copy of the License at 8** 9** http://www.apache.org/licenses/LICENSE-2.0 10** 11** Unless required by applicable law or agreed to in writing, software 12** distributed under the License is distributed on an "AS IS" BASIS, 13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14** See the License for the specific language governing permissions and 15** limitations under the License. 16*/ 17package android.widget; 18 19import com.android.internal.R; 20 21import android.content.Context; 22import android.content.pm.PackageInfo; 23import android.content.pm.PackageManager; 24import android.content.pm.PackageManager.NameNotFoundException; 25import android.content.pm.PackageParser; 26import android.content.pm.PermissionGroupInfo; 27import android.content.pm.PermissionInfo; 28import android.graphics.drawable.Drawable; 29import android.os.Parcel; 30import android.text.Spannable; 31import android.text.SpannableString; 32import android.text.SpannableStringBuilder; 33import android.text.TextUtils; 34import android.text.style.ForegroundColorSpan; 35import android.util.Log; 36import android.view.LayoutInflater; 37import android.view.View; 38 39import java.text.Collator; 40import java.util.ArrayList; 41import java.util.Collections; 42import java.util.Comparator; 43import java.util.HashMap; 44import java.util.HashSet; 45import java.util.Iterator; 46import java.util.List; 47import java.util.Map; 48import java.util.Set; 49 50/** 51 * This class contains the SecurityPermissions view implementation. 52 * Initially the package's advanced or dangerous security permissions 53 * are displayed under categorized 54 * groups. Clicking on the additional permissions presents 55 * extended information consisting of all groups and permissions. 56 * To use this view define a LinearLayout or any ViewGroup and add this 57 * view by instantiating AppSecurityPermissions and invoking getPermissionsView. 58 * 59 * {@hide} 60 */ 61public class AppSecurityPermissions implements View.OnClickListener { 62 63 private enum State { 64 NO_PERMS, 65 DANGEROUS_ONLY, 66 NORMAL_ONLY, 67 BOTH 68 } 69 70 static class MyPermissionInfo extends PermissionInfo { 71 /** 72 * PackageInfo.requestedPermissionsFlags for the new package being installed. 73 */ 74 int mNewReqFlags; 75 76 /** 77 * PackageInfo.requestedPermissionsFlags for the currently installed 78 * package, if it is installed. 79 */ 80 int mExistingReqFlags; 81 82 /** 83 * True if this should be considered a new permission. 84 */ 85 boolean mNew; 86 87 MyPermissionInfo() { 88 } 89 90 MyPermissionInfo(PermissionInfo info) { 91 super(info); 92 } 93 94 MyPermissionInfo(MyPermissionInfo info) { 95 super(info); 96 mNewReqFlags = info.mNewReqFlags; 97 mExistingReqFlags = info.mExistingReqFlags; 98 mNew = info.mNew; 99 } 100 } 101 102 private final static String TAG = "AppSecurityPermissions"; 103 private boolean localLOGV = false; 104 private Context mContext; 105 private LayoutInflater mInflater; 106 private PackageManager mPm; 107 private LinearLayout mPermsView; 108 private Map<String, CharSequence> mNewMap; 109 private Map<String, CharSequence> mDangerousMap; 110 private Map<String, CharSequence> mNormalMap; 111 private List<MyPermissionInfo> mPermsList; 112 private String mDefaultGrpLabel; 113 private String mDefaultGrpName="DefaultGrp"; 114 private String mPermFormat; 115 private CharSequence mNewPermPrefix; 116 private Drawable mNormalIcon; 117 private Drawable mDangerousIcon; 118 private boolean mExpanded; 119 private Drawable mShowMaxIcon; 120 private Drawable mShowMinIcon; 121 private View mShowMore; 122 private TextView mShowMoreText; 123 private ImageView mShowMoreIcon; 124 private State mCurrentState; 125 private LinearLayout mNonDangerousList; 126 private LinearLayout mDangerousList; 127 private LinearLayout mNewList; 128 private HashMap<String, CharSequence> mGroupLabelCache; 129 private View mNoPermsView; 130 131 public AppSecurityPermissions(Context context, List<PermissionInfo> permList) { 132 mContext = context; 133 mPm = mContext.getPackageManager(); 134 for (PermissionInfo pi : permList) { 135 mPermsList.add(new MyPermissionInfo(pi)); 136 } 137 } 138 139 public AppSecurityPermissions(Context context, String packageName) { 140 mContext = context; 141 mPm = mContext.getPackageManager(); 142 mPermsList = new ArrayList<MyPermissionInfo>(); 143 Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>(); 144 PackageInfo pkgInfo; 145 try { 146 pkgInfo = mPm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS); 147 } catch (NameNotFoundException e) { 148 Log.w(TAG, "Could'nt retrieve permissions for package:"+packageName); 149 return; 150 } 151 // Extract all user permissions 152 if((pkgInfo.applicationInfo != null) && (pkgInfo.applicationInfo.uid != -1)) { 153 getAllUsedPermissions(pkgInfo.applicationInfo.uid, permSet); 154 } 155 for(MyPermissionInfo tmpInfo : permSet) { 156 mPermsList.add(tmpInfo); 157 } 158 } 159 160 public AppSecurityPermissions(Context context, PackageParser.Package pkg) { 161 mContext = context; 162 mPm = mContext.getPackageManager(); 163 mPermsList = new ArrayList<MyPermissionInfo>(); 164 Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>(); 165 if(pkg == null) { 166 return; 167 } 168 169 // Convert to a PackageInfo 170 PackageInfo info = PackageParser.generatePackageInfo(pkg, null, 171 PackageManager.GET_PERMISSIONS, 0, 0, null); 172 PackageInfo installedPkgInfo = null; 173 // Get requested permissions 174 if (info.requestedPermissions != null) { 175 try { 176 installedPkgInfo = mPm.getPackageInfo(info.packageName, 177 PackageManager.GET_PERMISSIONS); 178 } catch (NameNotFoundException e) { 179 } 180 extractPerms(info, permSet, installedPkgInfo); 181 } 182 // Get permissions related to shared user if any 183 if (pkg.mSharedUserId != null) { 184 int sharedUid; 185 try { 186 sharedUid = mPm.getUidForSharedUser(pkg.mSharedUserId); 187 getAllUsedPermissions(sharedUid, permSet); 188 } catch (NameNotFoundException e) { 189 Log.w(TAG, "Could'nt retrieve shared user id for:"+pkg.packageName); 190 } 191 } 192 // Retrieve list of permissions 193 for (MyPermissionInfo tmpInfo : permSet) { 194 mPermsList.add(tmpInfo); 195 } 196 } 197 198 /** 199 * Utility to retrieve a view displaying a single permission. 200 */ 201 public static View getPermissionItemView(Context context, 202 CharSequence grpName, CharSequence description, boolean dangerous) { 203 LayoutInflater inflater = (LayoutInflater)context.getSystemService( 204 Context.LAYOUT_INFLATER_SERVICE); 205 Drawable icon = context.getResources().getDrawable(dangerous 206 ? R.drawable.ic_bullet_key_permission : R.drawable.ic_text_dot); 207 return getPermissionItemView(context, inflater, grpName, 208 description, dangerous, icon); 209 } 210 211 private void getAllUsedPermissions(int sharedUid, Set<MyPermissionInfo> permSet) { 212 String sharedPkgList[] = mPm.getPackagesForUid(sharedUid); 213 if(sharedPkgList == null || (sharedPkgList.length == 0)) { 214 return; 215 } 216 for(String sharedPkg : sharedPkgList) { 217 getPermissionsForPackage(sharedPkg, permSet); 218 } 219 } 220 221 private void getPermissionsForPackage(String packageName, 222 Set<MyPermissionInfo> permSet) { 223 PackageInfo pkgInfo; 224 try { 225 pkgInfo = mPm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS); 226 } catch (NameNotFoundException e) { 227 Log.w(TAG, "Couldn't retrieve permissions for package:"+packageName); 228 return; 229 } 230 if ((pkgInfo != null) && (pkgInfo.requestedPermissions != null)) { 231 extractPerms(pkgInfo, permSet, pkgInfo); 232 } 233 } 234 235 private void extractPerms(PackageInfo info, Set<MyPermissionInfo> permSet, 236 PackageInfo installedPkgInfo) { 237 String[] strList = info.requestedPermissions; 238 int[] flagsList = info.requestedPermissionsFlags; 239 if ((strList == null) || (strList.length == 0)) { 240 return; 241 } 242 for (int i=0; i<strList.length; i++) { 243 String permName = strList[i]; 244 // If we are only looking at an existing app, then we only 245 // care about permissions that have actually been granted to it. 246 if (installedPkgInfo != null && info == installedPkgInfo) { 247 if ((flagsList[i]&PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0) { 248 continue; 249 } 250 } 251 try { 252 PermissionInfo tmpPermInfo = mPm.getPermissionInfo(permName, 0); 253 if (tmpPermInfo == null) { 254 continue; 255 } 256 int existingIndex = -1; 257 if (installedPkgInfo != null 258 && installedPkgInfo.requestedPermissions != null) { 259 for (int j=0; j<installedPkgInfo.requestedPermissions.length; j++) { 260 if (permName.equals(installedPkgInfo.requestedPermissions[j])) { 261 existingIndex = j; 262 break; 263 } 264 } 265 } 266 final int existingFlags = existingIndex >= 0 ? 267 installedPkgInfo.requestedPermissionsFlags[existingIndex] : 0; 268 if (!isDisplayablePermission(tmpPermInfo, flagsList[i], existingFlags)) { 269 // This is not a permission that is interesting for the user 270 // to see, so skip it. 271 continue; 272 } 273 MyPermissionInfo myPerm = new MyPermissionInfo(tmpPermInfo); 274 myPerm.mNewReqFlags = flagsList[i]; 275 myPerm.mExistingReqFlags = existingFlags; 276 // This is a new permission if the app is already installed and 277 // doesn't currently hold this permission. 278 myPerm.mNew = installedPkgInfo != null 279 && (existingFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0; 280 permSet.add(myPerm); 281 } catch (NameNotFoundException e) { 282 Log.i(TAG, "Ignoring unknown permission:"+permName); 283 } 284 } 285 } 286 287 public int getPermissionCount() { 288 return mPermsList.size(); 289 } 290 291 public View getPermissionsView() { 292 293 mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 294 mPermsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null); 295 mShowMore = mPermsView.findViewById(R.id.show_more); 296 mShowMoreIcon = (ImageView) mShowMore.findViewById(R.id.show_more_icon); 297 mShowMoreText = (TextView) mShowMore.findViewById(R.id.show_more_text); 298 mNewList = (LinearLayout) mPermsView.findViewById(R.id.new_perms_list); 299 mDangerousList = (LinearLayout) mPermsView.findViewById(R.id.dangerous_perms_list); 300 mNonDangerousList = (LinearLayout) mPermsView.findViewById(R.id.non_dangerous_perms_list); 301 mNoPermsView = mPermsView.findViewById(R.id.no_permissions); 302 303 // Set up the LinearLayout that acts like a list item. 304 mShowMore.setClickable(true); 305 mShowMore.setOnClickListener(this); 306 mShowMore.setFocusable(true); 307 308 // Pick up from framework resources instead. 309 mDefaultGrpLabel = mContext.getString(R.string.default_permission_group); 310 mPermFormat = mContext.getString(R.string.permissions_format); 311 mNewPermPrefix = mContext.getText(R.string.perms_new_perm_prefix); 312 mNormalIcon = mContext.getResources().getDrawable(R.drawable.ic_text_dot); 313 mDangerousIcon = mContext.getResources().getDrawable(R.drawable.ic_bullet_key_permission); 314 mShowMaxIcon = mContext.getResources().getDrawable(R.drawable.expander_close_holo_dark); 315 mShowMinIcon = mContext.getResources().getDrawable(R.drawable.expander_open_holo_dark); 316 317 // Set permissions view 318 setPermissions(mPermsList); 319 return mPermsView; 320 } 321 322 /** 323 * Utility method that concatenates two strings defined by mPermFormat. 324 * a null value is returned if both str1 and str2 are null, if one of the strings 325 * is null the other non null value is returned without formatting 326 * this is to placate initial error checks 327 */ 328 private CharSequence formatPermissions(CharSequence groupDesc, CharSequence permDesc, 329 boolean newPerms) { 330 if (permDesc == null) { 331 return groupDesc; 332 } 333 // Sometimes people write permission names with a trailing period; 334 // strip that if it appears. 335 int len = permDesc.length(); 336 if (len > 0 && permDesc.charAt(len-1) == '.') { 337 permDesc = (permDesc.toString()).substring(0, len-1); 338 } 339 if (newPerms) { 340 if (true) { 341 // If this is a new permission, format it appropriately. 342 SpannableStringBuilder builder = new SpannableStringBuilder(); 343 if (groupDesc != null) { 344 // The previous permissions go in front, with a newline 345 // separating them. 346 builder.append(groupDesc); 347 builder.append("\n"); 348 } 349 Parcel parcel = Parcel.obtain(); 350 TextUtils.writeToParcel(mNewPermPrefix, parcel, 0); 351 parcel.setDataPosition(0); 352 CharSequence newStr = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); 353 parcel.recycle(); 354 builder.append(newStr); 355 builder.append(permDesc); 356 return builder; 357 } else { 358 // If this is a new permission, format it appropriately. 359 SpannableStringBuilder builder = new SpannableStringBuilder(permDesc); 360 builder.insert(0, mNewPermPrefix); 361 if (groupDesc != null) { 362 // The previous permissions go in front, with a newline 363 // separating them. 364 builder.insert(0, "\n"); 365 builder.insert(0, groupDesc); 366 } 367 return builder; 368 } 369 } 370 if (groupDesc == null) { 371 return permDesc; 372 } 373 // groupDesc and permDesc are non null 374 return String.format(mPermFormat, groupDesc, permDesc.toString()); 375 } 376 377 private CharSequence getGroupLabel(String grpName) { 378 if (grpName == null) { 379 //return default label 380 return mDefaultGrpLabel; 381 } 382 CharSequence cachedLabel = mGroupLabelCache.get(grpName); 383 if (cachedLabel != null) { 384 return cachedLabel; 385 } 386 PermissionGroupInfo pgi; 387 try { 388 pgi = mPm.getPermissionGroupInfo(grpName, 0); 389 } catch (NameNotFoundException e) { 390 Log.i(TAG, "Invalid group name:" + grpName); 391 return null; 392 } 393 CharSequence label = pgi.loadLabel(mPm).toString(); 394 mGroupLabelCache.put(grpName, label); 395 return label; 396 } 397 398 /** 399 * Utility method that displays permissions from a map containing group name and 400 * list of permission descriptions. 401 */ 402 private void displayPermissions(Map<String, CharSequence> permInfoMap, 403 LinearLayout permListView, boolean dangerous) { 404 permListView.removeAllViews(); 405 406 Set<String> permInfoStrSet = permInfoMap.keySet(); 407 for (String loopPermGrpInfoStr : permInfoStrSet) { 408 CharSequence grpLabel = getGroupLabel(loopPermGrpInfoStr); 409 //guaranteed that grpLabel wont be null since permissions without groups 410 //will belong to the default group 411 if(localLOGV) Log.i(TAG, "Adding view group:" + grpLabel + ", desc:" 412 + permInfoMap.get(loopPermGrpInfoStr)); 413 permListView.addView(getPermissionItemView(grpLabel, 414 permInfoMap.get(loopPermGrpInfoStr), dangerous)); 415 } 416 } 417 418 private void displayNoPermissions() { 419 mNoPermsView.setVisibility(View.VISIBLE); 420 } 421 422 private View getPermissionItemView(CharSequence grpName, CharSequence permList, 423 boolean dangerous) { 424 return getPermissionItemView(mContext, mInflater, grpName, permList, 425 dangerous, dangerous ? mDangerousIcon : mNormalIcon); 426 } 427 428 private static View getPermissionItemView(Context context, LayoutInflater inflater, 429 CharSequence grpName, CharSequence permList, boolean dangerous, Drawable icon) { 430 View permView = inflater.inflate(R.layout.app_permission_item, null); 431 432 TextView permGrpView = (TextView) permView.findViewById(R.id.permission_group); 433 TextView permDescView = (TextView) permView.findViewById(R.id.permission_list); 434 435 ImageView imgView = (ImageView)permView.findViewById(R.id.perm_icon); 436 imgView.setImageDrawable(icon); 437 if(grpName != null) { 438 permGrpView.setText(grpName); 439 permDescView.setText(permList); 440 } else { 441 permGrpView.setText(permList); 442 permDescView.setVisibility(View.GONE); 443 } 444 return permView; 445 } 446 447 private void showPermissions() { 448 449 switch(mCurrentState) { 450 case NO_PERMS: 451 displayNoPermissions(); 452 break; 453 454 case DANGEROUS_ONLY: 455 displayPermissions(mNewMap, mNewList, true); 456 displayPermissions(mDangerousMap, mDangerousList, true); 457 break; 458 459 case NORMAL_ONLY: 460 displayPermissions(mNewMap, mNewList, true); 461 displayPermissions(mNormalMap, mNonDangerousList, false); 462 break; 463 464 case BOTH: 465 displayPermissions(mNewMap, mNewList, true); 466 displayPermissions(mDangerousMap, mDangerousList, true); 467 if (mExpanded) { 468 displayPermissions(mNormalMap, mNonDangerousList, false); 469 mShowMoreIcon.setImageDrawable(mShowMaxIcon); 470 mShowMoreText.setText(R.string.perms_hide); 471 mNonDangerousList.setVisibility(View.VISIBLE); 472 } else { 473 mShowMoreIcon.setImageDrawable(mShowMinIcon); 474 mShowMoreText.setText(R.string.perms_show_all); 475 mNonDangerousList.setVisibility(View.GONE); 476 } 477 mShowMore.setVisibility(View.VISIBLE); 478 break; 479 } 480 } 481 482 private boolean isDisplayablePermission(PermissionInfo pInfo, int newReqFlags, 483 int existingReqFlags) { 484 final int base = pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE; 485 // Dangerous and normal permissions are always shown to the user. 486 if (base == PermissionInfo.PROTECTION_DANGEROUS || 487 base == PermissionInfo.PROTECTION_NORMAL) { 488 return true; 489 } 490 // Development permissions are only shown to the user if they are already 491 // granted to the app -- if we are installing an app and they are not 492 // already granted, they will not be granted as part of the install. 493 // Note we also need the app to have specified this permission is not 494 // required -- this is not technically needed, but it helps various things 495 // if we ensure apps always mark development permissions as option, so that 496 // even not knowing what a permission is we can still know whether it will 497 // be granted to the app when it is installed. 498 if ((existingReqFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0 499 && (newReqFlags&PackageInfo.REQUESTED_PERMISSION_REQUIRED) == 0 500 && (pInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) { 501 return true; 502 } 503 return false; 504 } 505 506 /* 507 * Utility method that aggregates all permission descriptions categorized by group 508 * Say group1 has perm11, perm12, perm13, the group description will be 509 * perm11_Desc, perm12_Desc, perm13_Desc 510 */ 511 private void aggregateGroupDescs(Map<String, List<MyPermissionInfo> > map, 512 Map<String, CharSequence> retMap, boolean newPerms) { 513 if(map == null) { 514 return; 515 } 516 if(retMap == null) { 517 return; 518 } 519 Set<String> grpNames = map.keySet(); 520 Iterator<String> grpNamesIter = grpNames.iterator(); 521 while(grpNamesIter.hasNext()) { 522 CharSequence grpDesc = null; 523 String grpNameKey = grpNamesIter.next(); 524 List<MyPermissionInfo> grpPermsList = map.get(grpNameKey); 525 if(grpPermsList == null) { 526 continue; 527 } 528 for(PermissionInfo permInfo: grpPermsList) { 529 CharSequence permDesc = permInfo.loadLabel(mPm); 530 grpDesc = formatPermissions(grpDesc, permDesc, newPerms); 531 } 532 // Insert grpDesc into map 533 if(grpDesc != null) { 534 if(localLOGV) Log.i(TAG, "Group:"+grpNameKey+" description:"+grpDesc.toString()); 535 retMap.put(grpNameKey, grpDesc); 536 } 537 } 538 } 539 540 private static class PermissionInfoComparator implements Comparator<PermissionInfo> { 541 private PackageManager mPm; 542 private final Collator sCollator = Collator.getInstance(); 543 PermissionInfoComparator(PackageManager pm) { 544 mPm = pm; 545 } 546 public final int compare(PermissionInfo a, PermissionInfo b) { 547 CharSequence sa = a.loadLabel(mPm); 548 CharSequence sb = b.loadLabel(mPm); 549 return sCollator.compare(sa, sb); 550 } 551 } 552 553 private void setPermissions(List<MyPermissionInfo> permList) { 554 mGroupLabelCache = new HashMap<String, CharSequence>(); 555 //add the default label so that uncategorized permissions can go here 556 mGroupLabelCache.put(mDefaultGrpName, mDefaultGrpLabel); 557 558 // Map containing group names and a list of permissions under that group 559 // that are new from the current install 560 mNewMap = new HashMap<String, CharSequence>(); 561 // Map containing group names and a list of permissions under that group 562 // categorized as dangerous 563 mDangerousMap = new HashMap<String, CharSequence>(); 564 // Map containing group names and a list of permissions under that group 565 // categorized as normal 566 mNormalMap = new HashMap<String, CharSequence>(); 567 568 // Additional structures needed to ensure that permissions are unique under 569 // each group 570 Map<String, List<MyPermissionInfo>> newMap = 571 new HashMap<String, List<MyPermissionInfo>>(); 572 Map<String, List<MyPermissionInfo>> dangerousMap = 573 new HashMap<String, List<MyPermissionInfo>>(); 574 Map<String, List<MyPermissionInfo> > normalMap = 575 new HashMap<String, List<MyPermissionInfo>>(); 576 PermissionInfoComparator permComparator = new PermissionInfoComparator(mPm); 577 578 if (permList != null) { 579 // First pass to group permissions 580 for (MyPermissionInfo pInfo : permList) { 581 if(localLOGV) Log.i(TAG, "Processing permission:"+pInfo.name); 582 if(!isDisplayablePermission(pInfo, pInfo.mNewReqFlags, pInfo.mExistingReqFlags)) { 583 if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" is not displayable"); 584 continue; 585 } 586 Map<String, List<MyPermissionInfo> > permInfoMap; 587 if (pInfo.mNew) { 588 permInfoMap = newMap; 589 } else if ((pInfo.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) 590 == PermissionInfo.PROTECTION_DANGEROUS) { 591 permInfoMap = dangerousMap; 592 } else { 593 permInfoMap = normalMap; 594 } 595 String grpName = (pInfo.group == null) ? mDefaultGrpName : pInfo.group; 596 if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" belongs to group:"+grpName); 597 List<MyPermissionInfo> grpPermsList = permInfoMap.get(grpName); 598 if(grpPermsList == null) { 599 grpPermsList = new ArrayList<MyPermissionInfo>(); 600 permInfoMap.put(grpName, grpPermsList); 601 grpPermsList.add(pInfo); 602 } else { 603 int idx = Collections.binarySearch(grpPermsList, pInfo, permComparator); 604 if(localLOGV) Log.i(TAG, "idx="+idx+", list.size="+grpPermsList.size()); 605 if (idx < 0) { 606 idx = -idx-1; 607 grpPermsList.add(idx, pInfo); 608 } 609 } 610 } 611 // Second pass to actually form the descriptions 612 // Look at dangerous permissions first 613 aggregateGroupDescs(newMap, mNewMap, true); 614 aggregateGroupDescs(dangerousMap, mDangerousMap, false); 615 aggregateGroupDescs(normalMap, mNormalMap, false); 616 } 617 618 mCurrentState = State.NO_PERMS; 619 if (mNewMap.size() > 0 || mDangerousMap.size() > 0) { 620 mCurrentState = (mNormalMap.size() > 0) ? State.BOTH : State.DANGEROUS_ONLY; 621 } else if(mNormalMap.size() > 0) { 622 mCurrentState = State.NORMAL_ONLY; 623 } 624 if(localLOGV) Log.i(TAG, "mCurrentState=" + mCurrentState); 625 showPermissions(); 626 } 627 628 public void onClick(View v) { 629 if(localLOGV) Log.i(TAG, "mExpanded="+mExpanded); 630 mExpanded = !mExpanded; 631 showPermissions(); 632 } 633} 634