package com.iwedia.service.system.application;
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;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.util.Log;
import com.iwedia.comm.system.application.AppPermission;
import com.iwedia.service.R;
/**
* This class contains the SecurityPermissions view implementation. Initially
* the package's advanced or dangerous security permissions are displayed under
* categorized groups. {@hide}
*/
public class AppSecurityPermissions {
private enum State {
NO_PERMS, DANGEROUS_ONLY, NORMAL_ONLY, BOTH
}
private final static String TAG = "AppSecurityPermissions";
private boolean localLOGV = false;
private Context mContext;
private PackageManager mPm;
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 State mCurrentState;
private HashMap<String, String> mGroupLabelCache;
private List<AppPermission> appPermissions;
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>();
appPermissions = new ArrayList<AppPermission>();
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);
}
}
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) && (pkgInfo.requestedPermissions != null)) {
extractPerms(pkgInfo.requestedPermissions, 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.e(TAG, "Ignoring unknown permission:" + permName);
}
}
}
public int getPermissionCount() {
return mPermsList.size();
}
public List<AppPermission> getPermissions() {
mDefaultGrpLabel = "Default";
mPermFormat = mContext.getString(R.string.permissions_format);
setPermissions(mPermsList);
return appPermissions;
}
/**
* Canonicalizes the group description before it is displayed to the user.
*/
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 String getGroupLabel(String grpName) {
if (grpName == null) {
// return default label
return mDefaultGrpLabel;
}
String 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;
}
String 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 List<AppPermission> displayPermissions(boolean dangerous) {
Map<String, String> permInfoMap = dangerous ? mDangerousMap
: mNormalMap;
List<AppPermission> appPermissions = new ArrayList<AppPermission>();
AppPermission appPermission;
Set<String> permInfoStrSet = permInfoMap.keySet();
for (String loopPermGrpInfoStr : permInfoStrSet) {
appPermission = new AppPermission();
String grpLabel = getGroupLabel(loopPermGrpInfoStr);
Log.e(grpLabel, permInfoMap.get(loopPermGrpInfoStr));
appPermission.setPermissionGroup(grpLabel);
appPermission.setDescription(permInfoMap.get(loopPermGrpInfoStr));
appPermissions.add(appPermission);
}
return appPermissions;
}
private void displayNoPermissions() {
Log.e(TAG, "no perms");
}
private void showPermissions() {
switch (mCurrentState) {
case NO_PERMS:
displayNoPermissions();
break;
case DANGEROUS_ONLY:
appPermissions = displayPermissions(true);
break;
case NORMAL_ONLY:
appPermissions = displayPermissions(false);
break;
case BOTH:
appPermissions = displayPermissions(true);
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) {
retMap.put(grpNameKey, grpDesc);
}
}
}
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, String>();
// 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.e(TAG, "Processing permission:" + pInfo.name);
}
if (!isDisplayablePermission(pInfo)) {
if (localLOGV)
Log.e(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();
}
}