diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:31:44 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:31:44 -0800 |
commit | 9066cfe9886ac131c34d59ed0e2d287b0e3c0087 (patch) | |
tree | d88beb88001f2482911e3d28e43833b50e4b4e97 /core/java/android/widget/AppSecurityPermissions.java | |
parent | d83a98f4ce9cfa908f5c54bbd70f03eec07e7553 (diff) | |
download | frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.zip frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.gz frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'core/java/android/widget/AppSecurityPermissions.java')
-rwxr-xr-x | core/java/android/widget/AppSecurityPermissions.java | 504 |
1 files changed, 504 insertions, 0 deletions
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java new file mode 100755 index 0000000..5fa00e7 --- /dev/null +++ b/core/java/android/widget/AppSecurityPermissions.java @@ -0,0 +1,504 @@ +/* +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +package android.widget; + +import com.android.internal.R; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageParser; +import android.content.pm.PermissionGroupInfo; +import android.content.pm.PermissionInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; + +import java.io.File; +import java.text.Collator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * This class contains the SecurityPermissions view implementation. + * Initially the package's advanced or dangerous security permissions + * are displayed under categorized + * groups. Clicking on the additional permissions presents + * extended information consisting of all groups and permissions. + * To use this view define a LinearLayout or any ViewGroup and add this + * view by instantiating AppSecurityPermissions and invoking getPermissionsView. + * + * {@hide} + */ +public class AppSecurityPermissions implements View.OnClickListener { + + private enum State { + NO_PERMS, + DANGEROUS_ONLY, + NORMAL_ONLY, + BOTH + } + + private final static String TAG = "AppSecurityPermissions"; + private boolean localLOGV = false; + private Context mContext; + private LayoutInflater mInflater; + private PackageManager mPm; + private LinearLayout mPermsView; + private Map<String, String> mDangerousMap; + private Map<String, String> mNormalMap; + private List<PermissionInfo> mPermsList; + private String mDefaultGrpLabel; + private String mDefaultGrpName="DefaultGrp"; + private String mPermFormat; + private Drawable mNormalIcon; + private Drawable mDangerousIcon; + private boolean mExpanded; + private Drawable mShowMaxIcon; + private Drawable mShowMinIcon; + private View mShowMore; + private TextView mShowMoreText; + private ImageView mShowMoreIcon; + private State mCurrentState; + private LinearLayout mNonDangerousList; + private LinearLayout mDangerousList; + private HashMap<String, CharSequence> mGroupLabelCache; + private View mNoPermsView; + + public AppSecurityPermissions(Context context, List<PermissionInfo> permList) { + mContext = context; + mPm = mContext.getPackageManager(); + mPermsList = permList; + } + + public AppSecurityPermissions(Context context, String packageName) { + mContext = context; + mPm = mContext.getPackageManager(); + mPermsList = new ArrayList<PermissionInfo>(); + Set<PermissionInfo> permSet = new HashSet<PermissionInfo>(); + PackageInfo pkgInfo; + try { + pkgInfo = mPm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS); + } catch (NameNotFoundException e) { + Log.w(TAG, "Could'nt retrieve permissions for package:"+packageName); + return; + } + // Extract all user permissions + if((pkgInfo.applicationInfo != null) && (pkgInfo.applicationInfo.uid != -1)) { + getAllUsedPermissions(pkgInfo.applicationInfo.uid, permSet); + } + for(PermissionInfo tmpInfo : permSet) { + mPermsList.add(tmpInfo); + } + } + + public AppSecurityPermissions(Context context, PackageParser.Package pkg) { + mContext = context; + mPm = mContext.getPackageManager(); + mPermsList = new ArrayList<PermissionInfo>(); + Set<PermissionInfo> permSet = new HashSet<PermissionInfo>(); + if(pkg == null) { + return; + } + // Extract shared user permissions if any + if(pkg.mSharedUserId != null) { + int sharedUid; + try { + sharedUid = mPm.getUidForSharedUser(pkg.mSharedUserId); + } catch (NameNotFoundException e) { + Log.w(TAG, "Could'nt retrieve shared user id for:"+pkg.packageName); + return; + } + getAllUsedPermissions(sharedUid, permSet); + } else { + ArrayList<String> strList = pkg.requestedPermissions; + int size; + if((strList == null) || ((size = strList.size()) == 0)) { + return; + } + // Extract permissions defined in current package + extractPerms(strList.toArray(new String[size]), permSet); + } + for(PermissionInfo tmpInfo : permSet) { + mPermsList.add(tmpInfo); + } + } + + public PackageParser.Package getPackageInfo(Uri packageURI) { + final String archiveFilePath = packageURI.getPath(); + PackageParser packageParser = new PackageParser(archiveFilePath); + File sourceFile = new File(archiveFilePath); + DisplayMetrics metrics = new DisplayMetrics(); + metrics.setToDefaults(); + return packageParser.parsePackage(sourceFile, archiveFilePath, metrics, 0); + } + + private void getAllUsedPermissions(int sharedUid, Set<PermissionInfo> permSet) { + String sharedPkgList[] = mPm.getPackagesForUid(sharedUid); + if(sharedPkgList == null || (sharedPkgList.length == 0)) { + return; + } + for(String sharedPkg : sharedPkgList) { + getPermissionsForPackage(sharedPkg, permSet); + } + } + + private void getPermissionsForPackage(String packageName, + Set<PermissionInfo> permSet) { + PackageInfo pkgInfo; + try { + pkgInfo = mPm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS); + } catch (NameNotFoundException e) { + Log.w(TAG, "Could'nt retrieve permissions for package:"+packageName); + return; + } + if(pkgInfo == null) { + return; + } + String strList[] = pkgInfo.requestedPermissions; + if(strList == null) { + return; + } + extractPerms(strList, permSet); + } + + private void extractPerms(String strList[], Set<PermissionInfo> permSet) { + if((strList == null) || (strList.length == 0)) { + return; + } + for(String permName:strList) { + try { + PermissionInfo tmpPermInfo = mPm.getPermissionInfo(permName, 0); + if(tmpPermInfo != null) { + permSet.add(tmpPermInfo); + } + } catch (NameNotFoundException e) { + Log.i(TAG, "Ignoring unknown permission:"+permName); + } + } + } + + public int getPermissionCount() { + return mPermsList.size(); + } + + public View getPermissionsView() { + + mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mPermsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null); + mShowMore = mPermsView.findViewById(R.id.show_more); + mShowMoreIcon = (ImageView) mShowMore.findViewById(R.id.show_more_icon); + mShowMoreText = (TextView) mShowMore.findViewById(R.id.show_more_text); + mDangerousList = (LinearLayout) mPermsView.findViewById(R.id.dangerous_perms_list); + mNonDangerousList = (LinearLayout) mPermsView.findViewById(R.id.non_dangerous_perms_list); + mNoPermsView = mPermsView.findViewById(R.id.no_permissions); + + // Set up the LinearLayout that acts like a list item. + mShowMore.setClickable(true); + mShowMore.setOnClickListener(this); + mShowMore.setFocusable(true); + mShowMore.setBackgroundResource(android.R.drawable.list_selector_background); + + // Pick up from framework resources instead. + mDefaultGrpLabel = mContext.getString(R.string.default_permission_group); + mPermFormat = mContext.getString(R.string.permissions_format); + mNormalIcon = mContext.getResources().getDrawable(R.drawable.ic_text_dot); + mDangerousIcon = mContext.getResources().getDrawable(R.drawable.ic_bullet_key_permission); + mShowMaxIcon = mContext.getResources().getDrawable(R.drawable.expander_ic_maximized); + mShowMinIcon = mContext.getResources().getDrawable(R.drawable.expander_ic_minimized); + + // Set permissions view + setPermissions(mPermsList); + return mPermsView; + } + + /** + * Canonicalizes the group description before it is displayed to the user. + * + * TODO check for internationalization issues remove trailing '.' in str1 + */ + private String canonicalizeGroupDesc(String groupDesc) { + if ((groupDesc == null) || (groupDesc.length() == 0)) { + return null; + } + // Both str1 and str2 are non-null and are non-zero in size. + int len = groupDesc.length(); + if(groupDesc.charAt(len-1) == '.') { + groupDesc = groupDesc.substring(0, len-1); + } + return groupDesc; + } + + /** + * Utility method that concatenates two strings defined by mPermFormat. + * a null value is returned if both str1 and str2 are null, if one of the strings + * is null the other non null value is returned without formatting + * this is to placate initial error checks + */ + private String formatPermissions(String groupDesc, CharSequence permDesc) { + if(groupDesc == null) { + if(permDesc == null) { + return null; + } + return permDesc.toString(); + } + groupDesc = canonicalizeGroupDesc(groupDesc); + if(permDesc == null) { + return groupDesc; + } + // groupDesc and permDesc are non null + return String.format(mPermFormat, groupDesc, permDesc.toString()); + } + + private CharSequence getGroupLabel(String grpName) { + if (grpName == null) { + //return default label + return mDefaultGrpLabel; + } + CharSequence cachedLabel = mGroupLabelCache.get(grpName); + if (cachedLabel != null) { + return cachedLabel; + } + PermissionGroupInfo pgi; + try { + pgi = mPm.getPermissionGroupInfo(grpName, 0); + } catch (NameNotFoundException e) { + Log.i(TAG, "Invalid group name:" + grpName); + return null; + } + CharSequence label = pgi.loadLabel(mPm).toString(); + mGroupLabelCache.put(grpName, label); + return label; + } + + /** + * Utility method that displays permissions from a map containing group name and + * list of permission descriptions. + */ + private void displayPermissions(boolean dangerous) { + Map<String, String> permInfoMap = dangerous ? mDangerousMap : mNormalMap; + LinearLayout permListView = dangerous ? mDangerousList : mNonDangerousList; + permListView.removeAllViews(); + + Set<String> permInfoStrSet = permInfoMap.keySet(); + for (String loopPermGrpInfoStr : permInfoStrSet) { + CharSequence grpLabel = getGroupLabel(loopPermGrpInfoStr); + //guaranteed that grpLabel wont be null since permissions without groups + //will belong to the default group + if(localLOGV) Log.i(TAG, "Adding view group:" + grpLabel + ", desc:" + + permInfoMap.get(loopPermGrpInfoStr)); + permListView.addView(getPermissionItemView(grpLabel, + permInfoMap.get(loopPermGrpInfoStr), dangerous)); + } + } + + private void displayNoPermissions() { + mNoPermsView.setVisibility(View.VISIBLE); + } + + private View getPermissionItemView(CharSequence grpName, String permList, + boolean dangerous) { + View permView = mInflater.inflate(R.layout.app_permission_item, null); + Drawable icon = dangerous ? mDangerousIcon : mNormalIcon; + int grpColor = dangerous ? R.color.perms_dangerous_grp_color : + R.color.perms_normal_grp_color; + int permColor = dangerous ? R.color.perms_dangerous_perm_color : + R.color.perms_normal_perm_color; + + TextView permGrpView = (TextView) permView.findViewById(R.id.permission_group); + TextView permDescView = (TextView) permView.findViewById(R.id.permission_list); + permGrpView.setTextColor(mContext.getResources().getColor(grpColor)); + permDescView.setTextColor(mContext.getResources().getColor(permColor)); + + ImageView imgView = (ImageView)permView.findViewById(R.id.perm_icon); + imgView.setImageDrawable(icon); + if(grpName != null) { + permGrpView.setText(grpName); + permDescView.setText(permList); + } else { + permGrpView.setText(permList); + permDescView.setVisibility(View.GONE); + } + return permView; + } + + private void showPermissions() { + + switch(mCurrentState) { + case NO_PERMS: + displayNoPermissions(); + break; + + case DANGEROUS_ONLY: + displayPermissions(true); + break; + + case NORMAL_ONLY: + displayPermissions(false); + break; + + case BOTH: + displayPermissions(true); + if (mExpanded) { + displayPermissions(false); + mShowMoreIcon.setImageDrawable(mShowMaxIcon); + mShowMoreText.setText(R.string.perms_hide); + mNonDangerousList.setVisibility(View.VISIBLE); + } else { + mShowMoreIcon.setImageDrawable(mShowMinIcon); + mShowMoreText.setText(R.string.perms_show_all); + mNonDangerousList.setVisibility(View.GONE); + } + mShowMore.setVisibility(View.VISIBLE); + break; + } + } + + private boolean isDisplayablePermission(PermissionInfo pInfo) { + if(pInfo.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS || + pInfo.protectionLevel == PermissionInfo.PROTECTION_NORMAL) { + return true; + } + return false; + } + + /* + * Utility method that aggregates all permission descriptions categorized by group + * Say group1 has perm11, perm12, perm13, the group description will be + * perm11_Desc, perm12_Desc, perm13_Desc + */ + private void aggregateGroupDescs( + Map<String, List<PermissionInfo> > map, Map<String, String> retMap) { + if(map == null) { + return; + } + if(retMap == null) { + return; + } + Set<String> grpNames = map.keySet(); + Iterator<String> grpNamesIter = grpNames.iterator(); + while(grpNamesIter.hasNext()) { + String grpDesc = null; + String grpNameKey = grpNamesIter.next(); + List<PermissionInfo> grpPermsList = map.get(grpNameKey); + if(grpPermsList == null) { + continue; + } + for(PermissionInfo permInfo: grpPermsList) { + CharSequence permDesc = permInfo.loadLabel(mPm); + grpDesc = formatPermissions(grpDesc, permDesc); + } + // Insert grpDesc into map + if(grpDesc != null) { + if(localLOGV) Log.i(TAG, "Group:"+grpNameKey+" description:"+grpDesc.toString()); + retMap.put(grpNameKey, grpDesc.toString()); + } + } + } + + private static class PermissionInfoComparator implements Comparator<PermissionInfo> { + private PackageManager mPm; + private final Collator sCollator = Collator.getInstance(); + PermissionInfoComparator(PackageManager pm) { + mPm = pm; + } + public final int compare(PermissionInfo a, PermissionInfo b) { + CharSequence sa = a.loadLabel(mPm); + CharSequence sb = b.loadLabel(mPm); + return sCollator.compare(sa, sb); + } + } + + private void setPermissions(List<PermissionInfo> permList) { + mGroupLabelCache = new HashMap<String, CharSequence>(); + //add the default label so that uncategorized permissions can go here + mGroupLabelCache.put(mDefaultGrpName, mDefaultGrpLabel); + + // Map containing group names and a list of permissions under that group + // categorized as dangerous + mDangerousMap = new HashMap<String, String>(); + // Map containing group names and a list of permissions under that group + // categorized as normal + mNormalMap = new HashMap<String, String>(); + + // Additional structures needed to ensure that permissions are unique under + // each group + Map<String, List<PermissionInfo>> dangerousMap = + new HashMap<String, List<PermissionInfo>>(); + Map<String, List<PermissionInfo> > normalMap = + new HashMap<String, List<PermissionInfo>>(); + PermissionInfoComparator permComparator = new PermissionInfoComparator(mPm); + + if (permList != null) { + // First pass to group permissions + for (PermissionInfo pInfo : permList) { + if(localLOGV) Log.i(TAG, "Processing permission:"+pInfo.name); + if(!isDisplayablePermission(pInfo)) { + if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" is not displayable"); + continue; + } + Map<String, List<PermissionInfo> > permInfoMap = + (pInfo.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) ? + dangerousMap : normalMap; + String grpName = (pInfo.group == null) ? mDefaultGrpName : pInfo.group; + if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" belongs to group:"+grpName); + List<PermissionInfo> grpPermsList = permInfoMap.get(grpName); + if(grpPermsList == null) { + grpPermsList = new ArrayList<PermissionInfo>(); + permInfoMap.put(grpName, grpPermsList); + grpPermsList.add(pInfo); + } else { + int idx = Collections.binarySearch(grpPermsList, pInfo, permComparator); + if(localLOGV) Log.i(TAG, "idx="+idx+", list.size="+grpPermsList.size()); + if (idx < 0) { + idx = -idx-1; + grpPermsList.add(idx, pInfo); + } + } + } + // Second pass to actually form the descriptions + // Look at dangerous permissions first + aggregateGroupDescs(dangerousMap, mDangerousMap); + aggregateGroupDescs(normalMap, mNormalMap); + } + + mCurrentState = State.NO_PERMS; + if(mDangerousMap.size() > 0) { + mCurrentState = (mNormalMap.size() > 0) ? State.BOTH : State.DANGEROUS_ONLY; + } else if(mNormalMap.size() > 0) { + mCurrentState = State.NORMAL_ONLY; + } + if(localLOGV) Log.i(TAG, "mCurrentState=" + mCurrentState); + showPermissions(); + } + + public void onClick(View v) { + if(localLOGV) Log.i(TAG, "mExpanded="+mExpanded); + mExpanded = !mExpanded; + showPermissions(); + } +} |