diff options
author | Svet Ganov <svetoslavganov@google.com> | 2015-05-04 11:10:02 -0700 |
---|---|---|
committer | Svet Ganov <svetoslavganov@google.com> | 2015-05-05 09:35:44 -0700 |
commit | a7a0406958991f7a964370295821d8e477f503e9 (patch) | |
tree | 56fc0376f80952a1434f72559613ab661dd60246 /src | |
parent | 7b1843783d28e3a102cf6109841e5782d8afdf13 (diff) | |
download | android_packages_apps_PackageInstaller-a7a0406958991f7a964370295821d8e477f503e9.tar.gz android_packages_apps_PackageInstaller-a7a0406958991f7a964370295821d8e477f503e9.tar.bz2 android_packages_apps_PackageInstaller-a7a0406958991f7a964370295821d8e477f503e9.zip |
Permission UI - legacy apps support - package installer
Change-Id: I23a7b4e42968df44d2dc3415bff2d15627653089
Diffstat (limited to 'src')
12 files changed, 744 insertions, 457 deletions
diff --git a/src/com/android/packageinstaller/permission/AppPermissions.java b/src/com/android/packageinstaller/permission/AppPermissions.java deleted file mode 100644 index 47ec42f7..00000000 --- a/src/com/android/packageinstaller/permission/AppPermissions.java +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright (C) 2015 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 com.android.packageinstaller.permission; - -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.content.res.Resources.NotFoundException; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.os.UserHandle; -import android.util.ArrayMap; -import android.util.Log; - -import com.android.internal.util.ArrayUtils; - -import java.util.ArrayList; -import java.util.List; - -public final class AppPermissions { - private static final String LOG_TAG = "AppPermissions"; - - private final ArrayMap<String, PermissionGroup> mGroups = new ArrayMap<>(); - - private final Context mContext; - - private final PackageInfo mPackageInfo; - - private final String[] mFilterPermissions; - - private final CharSequence mAppLabel; - - public AppPermissions(Context context, PackageInfo packageInfo, String[] permissions) { - mContext = context; - mPackageInfo = packageInfo; - mFilterPermissions = permissions; - mAppLabel = packageInfo.applicationInfo.loadLabel(context.getPackageManager()); - loadPermissionGroups(); - } - - public void refresh() { - loadPermissionGroups(); - } - - public CharSequence getAppLabel() { - return mAppLabel; - } - - public PermissionGroup getPermissionGroup(String name) { - return mGroups.get(name); - } - - public List<PermissionGroup> getPermissionGroups() { - return new ArrayList<>(mGroups.values()); - } - - private void loadPermissionGroups() { - mGroups.clear(); - if (mPackageInfo.requestedPermissions == null) { - return; - } - - final boolean appSupportsRuntimePermissions = mPackageInfo.applicationInfo.targetSdkVersion - > Build.VERSION_CODES.LOLLIPOP_MR1; - - for (int i = 0; i < mPackageInfo.requestedPermissions.length; i++) { - String requestedPerm = mPackageInfo.requestedPermissions[i]; - - final PermissionInfo permInfo; - try { - permInfo = mContext.getPackageManager().getPermissionInfo(requestedPerm, 0); - } catch (NameNotFoundException e) { - Log.w(LOG_TAG, "Unknown permission: " + requestedPerm); - continue; - } - - String permName = permInfo.name; - String groupName = permInfo.group != null ? permInfo.group : permName; - - PermissionGroup group = mGroups.get(groupName); - if (group == null) { - PermissionGroupInfo groupInfo = null; - if (permInfo.group != null) { - try { - groupInfo = mContext.getPackageManager().getPermissionGroupInfo( - permInfo.group, 0); - } catch (NameNotFoundException e) { - Log.w(LOG_TAG, "Unknown group: " + permInfo.group); - } - } - - CharSequence groupLabel = (groupInfo != null) - ? groupInfo.loadLabel(mContext.getPackageManager()) - : permInfo.loadLabel(mContext.getPackageManager()); - - if (groupLabel == null) { - Log.w(LOG_TAG, "Neither permission nor group have name." - + " Ignoring permission: " + permInfo.name); - continue; - } - - final String iconPkg = (groupInfo != null) - ? groupInfo.packageName : permInfo.packageName; - final int iconResId = (groupInfo != null) ? groupInfo.icon : permInfo.icon; - - group = new PermissionGroup(mContext, mPackageInfo.packageName, - groupName, groupLabel, iconPkg, iconResId); - mGroups.put(groupName, group); - } - - final boolean runtime = appSupportsRuntimePermissions - && permInfo.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS; - final boolean granted = (mPackageInfo.requestedPermissionsFlags[i] - & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0; - - Permission permission = new Permission(permName, runtime, granted); - group.addPermission(permission); - } - - if (ArrayUtils.isEmpty(mFilterPermissions)) { - return; - } - - final int groupCount = mGroups.size(); - for (int i = groupCount - 1; i >= 0; i--) { - PermissionGroup group = mGroups.valueAt(i); - boolean groupHasPermission = false; - for (String filterPerm : mFilterPermissions) { - if (group.mPermissions.containsKey(filterPerm)) { - groupHasPermission = true; - break; - } - } - if (!groupHasPermission) { - mGroups.removeAt(i); - } - } - } - - public static final class PermissionGroup { - private final Context mContext; - private final String mPackageName; - - private final String mName; - private final CharSequence mLabel; - private final ArrayMap<String, Permission> mPermissions = new ArrayMap<>(); - private final String mIconPkg; - private final int mIconResId; - - private boolean mHasRuntimePermissions; - - public String getName() { - return mName; - } - - public String getIconPkg() { - return mIconPkg; - } - - public int getIconResId() { - return mIconResId; - } - - public CharSequence getLabel() { - return mLabel; - } - - public PermissionGroup(Context context, String packageName, - String name, CharSequence label, String iconPkg, int iconResId) { - mPackageName = packageName; - mContext = context; - mName = name; - mLabel = label; - mIconPkg = iconPkg; - mIconResId = iconResId; - } - - public boolean hasRuntimePermissions() { - return mHasRuntimePermissions; - } - - public boolean areRuntimePermissionsGranted() { - final int permissionCount = mPermissions.size(); - for (int i = 0; i < permissionCount; i++) { - Permission permission = mPermissions.valueAt(i); - if (permission.mRuntime && !permission.mGranted) { - return false; - } - } - return true; - } - - public boolean grantRuntimePermissions() { - for (Permission permission : mPermissions.values()) { - if (permission.mRuntime && !permission.mGranted) { - mContext.getPackageManager().grantPermission(mPackageName, - permission.mName, new UserHandle(mContext.getUserId())); - permission.mGranted = true; - } - } - return true; - } - - public boolean revokeRuntimePermissions() { - for (Permission permission : mPermissions.values()) { - if (permission.mRuntime && permission.mGranted) { - mContext.getPackageManager().revokePermission(mPackageName, - permission.mName, new UserHandle(mContext.getUserId())); - permission.mGranted = false; - } - } - return true; - } - - public List<Permission> getPermissions() { - return new ArrayList<>(mPermissions.values()); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - if (obj == null) { - return false; - } - - if (getClass() != obj.getClass()) { - return false; - } - - PermissionGroup other = (PermissionGroup) obj; - - if (mName == null) { - if (other.mName != null) { - return false; - } - } else if (!mName.equals(other.mName)) { - return false; - } - - return true; - } - - @Override - public int hashCode() { - return mName != null ? mName.hashCode() : 0; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append(getClass().getSimpleName()); - builder.append("{name=").append(mName); - if (!mPermissions.isEmpty()) { - builder.append(", <has permissions>}"); - } else { - builder.append('}'); - } - return builder.toString(); - } - - void addPermission(Permission permission) { - mPermissions.put(permission.mName, permission); - if (permission.mRuntime) { - mHasRuntimePermissions = true; - } - } - } - - public static final class Permission { - private final String mName; - private final boolean mRuntime; - private boolean mGranted; - - public Permission(String name, boolean runtime, boolean granted) { - mName = name; - mRuntime = runtime; - mGranted = granted; - } - - public String getName() { - return mName; - } - - public boolean isGranted() { - return mGranted; - } - - public void setGranted(boolean granted) { - mGranted = granted; - } - } - - public static Drawable loadDrawable(PackageManager pm, String pkg, int resId) { - try { - return pm.getResourcesForApplication(pkg).getDrawable(resId, null); - } catch (NotFoundException | NameNotFoundException e) { - Log.d(LOG_TAG, "Couldn't get resource", e); - return null; - } - } -} diff --git a/src/com/android/packageinstaller/permission/model/AppPermissions.java b/src/com/android/packageinstaller/permission/model/AppPermissions.java new file mode 100644 index 00000000..9480ce90 --- /dev/null +++ b/src/com/android/packageinstaller/permission/model/AppPermissions.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2015 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 com.android.packageinstaller.permission.model; + +import android.content.Context; +import android.content.pm.PackageInfo; +import android.util.ArrayMap; + +import com.android.internal.util.ArrayUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class AppPermissions { + private final ArrayMap<String, PermissionGroup> mGroups = new ArrayMap<>(); + + private final Context mContext; + + private final PackageInfo mPackageInfo; + + private final String[] mFilterPermissions; + + private final CharSequence mAppLabel; + + public AppPermissions(Context context, PackageInfo packageInfo, String[] permissions) { + mContext = context; + mPackageInfo = packageInfo; + mFilterPermissions = permissions; + mAppLabel = packageInfo.applicationInfo.loadLabel(context.getPackageManager()); + loadPermissionGroups(); + } + + public PackageInfo getPackageInfo() { + return mPackageInfo; + } + + public void refresh() { + loadPermissionGroups(); + } + + public CharSequence getAppLabel() { + return mAppLabel; + } + + public PermissionGroup getPermissionGroup(String name) { + return mGroups.get(name); + } + + public List<PermissionGroup> getPermissionGroups() { + return new ArrayList<>(mGroups.values()); + } + + private void loadPermissionGroups() { + List<PermissionGroup> groups = new ArrayList<>(); + + if (mPackageInfo.requestedPermissions == null) { + return; + } + + for (int i = 0; i < mPackageInfo.requestedPermissions.length; i++) { + String requestedPerm = mPackageInfo.requestedPermissions[i]; + + if (hasGroupForPermission(requestedPerm, groups)) { + continue; + } + + PermissionGroup group = PermissionGroup.create(mContext, + mPackageInfo, requestedPerm); + if (group == null) { + continue; + } + + groups.add(group); + } + + if (!ArrayUtils.isEmpty(mFilterPermissions)) { + final int groupCount = groups.size(); + for (int i = groupCount - 1; i >= 0; i--) { + PermissionGroup group = groups.get(i); + boolean groupHasPermission = false; + for (String filterPerm : mFilterPermissions) { + if (group.hasPermission(filterPerm)) { + groupHasPermission = true; + break; + } + } + if (!groupHasPermission) { + groups.remove(i); + } + } + } + + Collections.sort(groups); + + mGroups.clear(); + for (PermissionGroup group : groups) { + mGroups.put(group.getName(), group); + } + } + + private static boolean hasGroupForPermission(String permission, + List<PermissionGroup> groups) { + for (PermissionGroup group : groups) { + if (group.hasPermission(permission)) { + return true; + } + } + return false; + } +} diff --git a/src/com/android/packageinstaller/permission/model/Permission.java b/src/com/android/packageinstaller/permission/model/Permission.java new file mode 100644 index 00000000..1f51fea9 --- /dev/null +++ b/src/com/android/packageinstaller/permission/model/Permission.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 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 com.android.packageinstaller.permission.model; + +public final class Permission { + private final String mName; + private final int mAppOp; + + private boolean mGranted; + private boolean mAppOpAllowed; + + public Permission(String name, boolean granted, + int appOp, boolean appOpAllowed) { + mName = name; + mGranted = granted; + mAppOp = appOp; + mAppOpAllowed = appOpAllowed; + } + + public String getName() { + return mName; + } + + public int getAppOp() { + return mAppOp; + } + + public boolean isGranted() { + return mGranted; + } + + public void setGranted(boolean mGranted) { + this.mGranted = mGranted; + } + + public boolean isAppOpAllowed() { + return mAppOpAllowed; + } + + public void setAppOpAllowed(boolean mAppOpAllowed) { + this.mAppOpAllowed = mAppOpAllowed; + } +}
\ No newline at end of file diff --git a/src/com/android/packageinstaller/permission/PermissionApps.java b/src/com/android/packageinstaller/permission/model/PermissionApps.java index b441d613..8f28433b 100644 --- a/src/com/android/packageinstaller/permission/PermissionApps.java +++ b/src/com/android/packageinstaller/permission/model/PermissionApps.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.packageinstaller.permission; +package com.android.packageinstaller.permission.model; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -21,27 +21,22 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; import android.graphics.LightingColorFilter; import android.graphics.drawable.Drawable; import android.os.AsyncTask; -import android.os.Build; import android.os.UserHandle; import android.os.UserManager; import android.util.ArrayMap; import android.util.Log; -import com.android.packageinstaller.permission.AppPermissions.Permission; - import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; public class PermissionApps { - - private static final String LOG_TAG = "PermissionGroup"; + private static final String LOG_TAG = "PermissionApps"; private final Context mContext; private final String mGroupName; @@ -50,7 +45,7 @@ public class PermissionApps { private CharSequence mLabel; private Drawable mIcon; - private ArrayList<PermissionApp> mAppPerms; + private List<PermissionApp> mPermApps; // Map (pkg|uid) -> AppPermission private ArrayMap<String, PermissionApp> mAppLookup; @@ -68,7 +63,7 @@ public class PermissionApps { } public Collection<PermissionApp> getApps() { - return mAppPerms; + return mPermApps; } public PermissionApp getApp(String key) { @@ -84,10 +79,10 @@ public class PermissionApps { } private void loadGroupInfo() { - PackageItemInfo info = null; + PackageItemInfo info; try { info = mPm.getPermissionGroupInfo(mGroupName, 0); - } catch (NameNotFoundException e) { + } catch (PackageManager.NameNotFoundException e) { try { PermissionInfo permInfo = mPm.getPermissionInfo(mGroupName, 0); if (permInfo.protectionLevel != PermissionInfo.PROTECTION_DANGEROUS) { @@ -107,25 +102,19 @@ public class PermissionApps { } public static class PermissionApp implements Comparable<PermissionApp> { - private final Context mContext; + private final PermissionGroup mPermissionGroup; private final String mLabel; private final Drawable mIcon; - private final String mPkg; - private final int mUid; - private final List<Permission> mPermissions = new ArrayList<>(); - private boolean mHasPermission; - public PermissionApp(Context context, String pkg, int uid, String label, + public PermissionApp(PermissionGroup permissionGroup, String label, Drawable icon) { - mContext = context; - mPkg = pkg; - mUid = uid; + mPermissionGroup = permissionGroup; mLabel = label; mIcon = icon; } public String getKey() { - return Integer.toString(mUid); + return Integer.toString(getUid()); } public String getLabel() { @@ -136,104 +125,122 @@ public class PermissionApps { return mIcon; } - public boolean hasRuntimePermissions() { - return mHasPermission; + public boolean areRuntimePermissionsGranted() { + return mPermissionGroup.areRuntimePermissionsGranted(); } - /** - * Note: This class only expects to have runtime permissions added. - */ - public void addPermission(Permission permission) { - mPermissions.add(permission); - if (permission.isGranted()) { - mHasPermission = true; - } + public void grantRuntimePermissions() { + mPermissionGroup.grantRuntimePermissions(); } - public boolean grantRuntimePermissions() { - for (Permission permission : mPermissions) { - if (!permission.isGranted()) { - mContext.getPackageManager().grantPermission(mPkg, - permission.getName(), new UserHandle(UserHandle.getUserId(mUid))); - permission.setGranted(true); - } - } - return true; - } - - public boolean revokeRuntimePermissions() { - for (Permission permission : mPermissions) { - if (permission.isGranted()) { - mContext.getPackageManager().revokePermission(mPkg, - permission.getName(), new UserHandle(UserHandle.getUserId(mUid))); - permission.setGranted(false); - } - } - return true; + public void revokeRuntimePermissions() { + mPermissionGroup.revokeRuntimePermissions(); } @Override public int compareTo(PermissionApp another) { - int result = mLabel.compareTo(another.mLabel); + final int result = mLabel.compareTo(another.mLabel); if (result == 0) { // Unbadged before badged. - return mUid - another.mUid; + return getUid() - another.getUid(); } return result; } + private int getUid() { + return mPermissionGroup.getApp().applicationInfo.uid; + } } - private class PermissionAppsLoader extends AsyncTask<Void, Void, ArrayList<PermissionApp>> { - + private class PermissionAppsLoader extends AsyncTask<Void, Void, List<PermissionApp>> { @Override - protected ArrayList<PermissionApp> doInBackground(Void... params) { - ArrayList<String> permStrs = new ArrayList<>(); - try { - List<PermissionInfo> permissions = mPm.queryPermissionsByGroup(mGroupName, 0); - for (PermissionInfo info : permissions) { - if (info.protectionLevel != PermissionInfo.PROTECTION_DANGEROUS) { - continue; - } - permStrs.add(info.name); - } - } catch (NameNotFoundException e) { - permStrs.add(mGroupName); + protected List<PermissionApp> doInBackground(Void... args) { + PackageItemInfo groupInfo = getGroupInfo(mGroupName); + if (groupInfo == null) { + return Collections.emptyList(); } - ArrayList<PermissionApp> appPerms = new ArrayList<>(); + + List<PermissionInfo> groupPermInfos = getGroupPermissionInfos(mGroupName); + if (groupPermInfos == null) { + return Collections.emptyList(); + } + + ArrayList<PermissionApp> permApps = new ArrayList<>(); + for (UserHandle user : UserManager.get(mContext).getUserProfiles()) { List<PackageInfo> apps = mPm.getInstalledPackages( PackageManager.GET_PERMISSIONS, user.getIdentifier()); + final int N = apps.size(); for (int i = 0; i < N; i++) { PackageInfo app = apps.get(i); - if (app.applicationInfo.targetSdkVersion - <= Build.VERSION_CODES.LOLLIPOP_MR1) { - // Only care about apps that support runtime permissions here. - continue; - } if (app.requestedPermissions == null) { continue; } - PermissionApp appPermission = new PermissionApp(mContext, - app.packageName, app.applicationInfo.uid, - app.applicationInfo.loadLabel(mPm).toString(), - getBadgedIcon(app.applicationInfo)); + for (int j = 0; j < app.requestedPermissions.length; j++) { - if (permStrs.contains(app.requestedPermissions[j])) { - boolean granted = (app.requestedPermissionsFlags[j] - & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0; - appPermission.addPermission(new Permission( - app.requestedPermissions[j], true, granted)); + String requestedPerm = app.requestedPermissions[j]; + + boolean requestsPermissionInGroup = false; + + for (PermissionInfo groupPermInfo : groupPermInfos) { + if (groupPermInfo.name.equals(requestedPerm)) { + requestsPermissionInGroup = true; + break; + } } - } - if (appPermission.mPermissions.size() != 0) { - appPerms.add(appPermission); + + if (!requestsPermissionInGroup) { + continue; + } + + PermissionGroup group = PermissionGroup.create(mContext, + app, groupInfo, groupPermInfos); + + PermissionApp permApp = new PermissionApp(group, + app.applicationInfo.loadLabel(mPm).toString(), + getBadgedIcon(app.applicationInfo)); + + permApps.add(permApp); } } } - Collections.sort(appPerms); - return appPerms; + + Collections.sort(permApps); + + return permApps; + } + + private PackageItemInfo getGroupInfo(String groupName) { + try { + return mContext.getPackageManager().getPermissionGroupInfo(groupName, 0); + } catch (NameNotFoundException e) { + /* ignore */ + } + try { + return mContext.getPackageManager().getPermissionInfo(groupName, 0); + } catch (NameNotFoundException e2) { + /* ignore */ + } + return null; + } + + private List<PermissionInfo> getGroupPermissionInfos(String groupName) { + try { + return mContext.getPackageManager().queryPermissionsByGroup(groupName, 0); + } catch (NameNotFoundException e) { + /* ignore */ + } + try { + PermissionInfo permissionInfo = mContext.getPackageManager() + .getPermissionInfo(groupName, 0); + List<PermissionInfo> permissions = new ArrayList<>(); + permissions.add(permissionInfo); + return permissions; + } catch (NameNotFoundException e2) { + /* ignore */ + } + return null; } private Drawable getBadgedIcon(ApplicationInfo appInfo) { @@ -243,12 +250,12 @@ public class PermissionApps { } @Override - protected void onPostExecute(ArrayList<PermissionApp> result) { + protected void onPostExecute(List<PermissionApp> result) { mAppLookup = new ArrayMap<>(); for (PermissionApp app : result) { mAppLookup.put(app.getKey(), app); } - mAppPerms = result; + mPermApps = result; mCallback.onPermissionsLoaded(); } } @@ -256,5 +263,4 @@ public class PermissionApps { public interface Callback { void onPermissionsLoaded(); } - } diff --git a/src/com/android/packageinstaller/permission/model/PermissionGroup.java b/src/com/android/packageinstaller/permission/model/PermissionGroup.java new file mode 100644 index 00000000..9a5291d8 --- /dev/null +++ b/src/com/android/packageinstaller/permission/model/PermissionGroup.java @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2015 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 com.android.packageinstaller.permission.model; + +import android.app.ActivityManager; +import android.app.AppOpsManager; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageItemInfo; +import android.content.pm.PackageManager; +import android.content.pm.PermissionGroupInfo; +import android.content.pm.PermissionInfo; +import android.os.Build; +import android.os.UserHandle; +import android.util.ArrayMap; + +import java.util.ArrayList; +import java.util.List; + +public final class PermissionGroup implements Comparable<PermissionGroup> { + private static final String PLATFORM_PACKAGE_NAME = "android"; + + private static final String KILL_REASON_APP_OP_CHANGE = "Permission related app op changed"; + + private final Context mContext; + private final AppOpsManager mAppOps; + private final ActivityManager mActivityManager; + + private final PackageInfo mPackageInfo; + private final String mName; + private final CharSequence mLabel; + private final ArrayMap<String, Permission> mPermissions = new ArrayMap<>(); + private final String mIconPkg; + private final int mIconResId; + + private final boolean mAppSupportsRuntimePermissions; + + public static PermissionGroup create(Context context, PackageInfo packageInfo, + String permissionName) { + PermissionInfo permissionInfo; + try { + permissionInfo = context.getPackageManager().getPermissionInfo(permissionName, 0); + } catch (PackageManager.NameNotFoundException e) { + return null; + } + + if (permissionInfo.protectionLevel != PermissionInfo.PROTECTION_DANGEROUS) { + return null; + } + + PackageItemInfo groupInfo = permissionInfo; + if (permissionInfo.group != null) { + try { + groupInfo = context.getPackageManager().getPermissionGroupInfo( + permissionInfo.group, 0); + } catch (PackageManager.NameNotFoundException e) { + /* ignore */ + } + } + + List<PermissionInfo> permissionInfos = null; + if (groupInfo instanceof PermissionGroupInfo) { + try { + permissionInfos = context.getPackageManager().queryPermissionsByGroup( + groupInfo.name, 0); + } catch (PackageManager.NameNotFoundException e) { + /* ignore */ + } + } + + return create(context, packageInfo, groupInfo, permissionInfos); + + } + + public static PermissionGroup create(Context context, PackageInfo packageInfo, + PackageItemInfo groupInfo, List<PermissionInfo> permissionInfos) { + + PermissionGroup group = new PermissionGroup(context, packageInfo, groupInfo.name, + groupInfo.loadLabel(context.getPackageManager()), groupInfo.packageName, + groupInfo.icon); + + if (groupInfo instanceof PermissionInfo) { + permissionInfos = new ArrayList<>(); + permissionInfos.add((PermissionInfo) groupInfo); + } + + if (permissionInfos == null || permissionInfos.isEmpty()) { + return null; + } + + final int permissionCount = packageInfo.requestedPermissions.length; + for (int i = 0; i < permissionCount; i++) { + String requestedPermission = packageInfo.requestedPermissions[i]; + + PermissionInfo requestedPermissionInfo = null; + + for (PermissionInfo permissionInfo : permissionInfos) { + if (requestedPermission.equals(permissionInfo.name)) { + requestedPermissionInfo = permissionInfo; + break; + } + } + + if (requestedPermissionInfo == null) { + continue; + } + + // Collect only runtime permissions. + if (requestedPermissionInfo.protectionLevel != PermissionInfo.PROTECTION_DANGEROUS) { + continue; + } + + // Don't allow toggle of non platform defined permissions for legacy apps via app ops. + if (packageInfo.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1 + && !PLATFORM_PACKAGE_NAME.equals(requestedPermissionInfo.packageName)) { + continue; + } + + + final boolean granted = (packageInfo.requestedPermissionsFlags[i] + & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0; + + final int appOp; + final boolean appOpAllowed; + + if (group.mAppSupportsRuntimePermissions) { + appOp = AppOpsManager.OP_NONE; + appOpAllowed = false; + } else { + appOp = PLATFORM_PACKAGE_NAME.equals(requestedPermissionInfo.packageName) + ? AppOpsManager.permissionToOpCode(requestedPermissionInfo.name) + : AppOpsManager.OP_NONE; + appOpAllowed = appOp != AppOpsManager.OP_NONE + && context.getSystemService(AppOpsManager.class).checkOp(appOp, + packageInfo.applicationInfo.uid, packageInfo.packageName) + == AppOpsManager.MODE_ALLOWED; + } + + Permission permission = new Permission(requestedPermission, granted, + appOp, appOpAllowed); + group.addPermission(permission); + } + + return group; + } + + private PermissionGroup(Context context, PackageInfo packageInfo, String name, + CharSequence label, String iconPkg, int iconResId) { + mContext = context; + mPackageInfo = packageInfo; + mAppSupportsRuntimePermissions = packageInfo.applicationInfo + .targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1; + mAppOps = context.getSystemService(AppOpsManager.class); + mActivityManager = context.getSystemService(ActivityManager.class); + mName = name; + mLabel = label; + mIconPkg = iconPkg; + mIconResId = iconResId; + } + + public PackageInfo getApp() { + return mPackageInfo; + } + + public String getName() { + return mName; + } + + public String getIconPkg() { + return mIconPkg; + } + + public int getIconResId() { + return mIconResId; + } + + public CharSequence getLabel() { + return mLabel; + } + + public boolean hasPermission(String permission) { + return mPermissions.get(permission) != null; + } + + public boolean areRuntimePermissionsGranted() { + final int permissionCount = mPermissions.size(); + for (int i = 0; i < permissionCount; i++) { + Permission permission = mPermissions.valueAt(i); + if (mAppSupportsRuntimePermissions) { + if (!permission.isGranted()) { + return false; + } + } else if (permission.isGranted() && permission.getAppOp() + != AppOpsManager.OP_NONE && !permission.isAppOpAllowed()) { + return false; + } + } + return true; + } + + public boolean grantRuntimePermissions() { + final boolean isSharedUser = mPackageInfo.sharedUserId != null; + final int uid = mPackageInfo.applicationInfo.uid; + + // We toggle permissions only to apps that support runtime + // permissions, otherwise we toggle the app op corresponding + // to the permission if the permission is granted to the app. + for (Permission permission : mPermissions.values()) { + if (mAppSupportsRuntimePermissions) { + if (!permission.isGranted()) { + mContext.getPackageManager().grantPermission(mPackageInfo.packageName, + permission.getName(), new UserHandle(mContext.getUserId())); + permission.setGranted(true); + } + } else { + // Legacy apps cannot have a not granted permission but just in case. + // Also if the permissions has no corresponding app op, then it is a + // third-party one and we do not offer toggling of such permissions. + if (!permission.isGranted() || permission.getAppOp() == AppOpsManager.OP_NONE) { + continue; + } + + if (!permission.isAppOpAllowed()) { + // It this is a shared user we want to enable the app op for all + // packages in the shared user to match the behavior of this + // shared user having a runtime permission. + if (isSharedUser) { + String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid); + for (String packageName : packageNames) { + mAppOps.setMode(permission.getAppOp(), uid, packageName, + AppOpsManager.MODE_ALLOWED); + } + } else { + mAppOps.setMode(permission.getAppOp(), uid, mPackageInfo.packageName, + AppOpsManager.MODE_ALLOWED); + } + + // Legacy apps do not know that they have to retry access to a + // resource due to changes in runtime permissions (app ops in this + // case). Therefore, we restart them on app op change, so they + // can pick up the change. + mActivityManager.killUid(uid, KILL_REASON_APP_OP_CHANGE); + + permission.setAppOpAllowed(true); + } + } + } + + return true; + } + + public boolean revokeRuntimePermissions() { + final boolean isSharedUser = mPackageInfo.sharedUserId != null; + final int uid = mPackageInfo.applicationInfo.uid; + + // We toggle permissions only to apps that support runtime + // permissions, otherwise we toggle the app op corresponding + // to the permission if the permission is granted to the app. + for (Permission permission : mPermissions.values()) { + if (mAppSupportsRuntimePermissions) { + if (permission.isGranted()) { + mContext.getPackageManager().revokePermission(mPackageInfo.packageName, + permission.getName(), new UserHandle(mContext.getUserId())); + permission.setGranted(false); + } + } else { + // Legacy apps cannot have a non-granted permission but just in case. + // Also if the permission has no corresponding app op, then it is a + // third-party one and we do not offer toggling of such permissions. + if (!permission.isGranted() || permission.getAppOp() == AppOpsManager.OP_NONE) { + continue; + } + + if (permission.isAppOpAllowed()) { + // It this is a shared user we want to enable the app op for all + // packages the the shared user to match the behavior of this + // shared user having a runtime permission. + if (isSharedUser) { + String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid); + for (String packageName : packageNames) { + mAppOps.setMode(permission.getAppOp(), uid, + packageName, AppOpsManager.MODE_IGNORED); + } + } else { + mAppOps.setMode(permission.getAppOp(), uid, + mPackageInfo.packageName, AppOpsManager.MODE_IGNORED); + } + + // Disabling an app op may put the app in a situation in which it + // has a handle to state it shouldn't have, so we have to kill the + // app. This matches the revoke runtime permission behavior. + mActivityManager.killUid(uid, KILL_REASON_APP_OP_CHANGE); + + permission.setAppOpAllowed(false); + } + } + } + + return true; + } + + public List<Permission> getPermissions() { + return new ArrayList<>(mPermissions.values()); + } + + @Override + public int compareTo(PermissionGroup another) { + final int result = mLabel.toString().compareTo(another.mLabel.toString()); + if (result == 0) { + // Unbadged before badged. + return mPackageInfo.applicationInfo.uid + - another.mPackageInfo.applicationInfo.uid; + } + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj == null) { + return false; + } + + if (getClass() != obj.getClass()) { + return false; + } + + PermissionGroup other = (PermissionGroup) obj; + + if (mName == null) { + if (other.mName != null) { + return false; + } + } else if (!mName.equals(other.mName)) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return mName != null ? mName.hashCode() : 0; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(getClass().getSimpleName()); + builder.append("{name=").append(mName); + if (!mPermissions.isEmpty()) { + builder.append(", <has permissions>}"); + } else { + builder.append('}'); + } + return builder.toString(); + } + + void addPermission(Permission permission) { + mPermissions.put(permission.getName(), permission); + } +} + diff --git a/src/com/android/packageinstaller/permission/ManagePermissionsFragment.java b/src/com/android/packageinstaller/permission/ui/AppPermissionsFragment.java index 7ce13f83..a74f08c8 100644 --- a/src/com/android/packageinstaller/permission/ManagePermissionsFragment.java +++ b/src/com/android/packageinstaller/permission/ui/AppPermissionsFragment.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.packageinstaller.permission; +package com.android.packageinstaller.permission.ui; import android.annotation.Nullable; import android.app.ActionBar; @@ -40,15 +40,18 @@ import android.widget.TextView; import android.widget.Toast; import com.android.packageinstaller.R; +import com.android.packageinstaller.permission.utils.Utils; +import com.android.packageinstaller.permission.model.AppPermissions; +import com.android.packageinstaller.permission.model.PermissionGroup; -public final class ManagePermissionsFragment extends SettingsWithHeader +public final class AppPermissionsFragment extends SettingsWithHeader implements OnPreferenceChangeListener { private static final String LOG_TAG = "ManagePermsFragment"; private AppPermissions mAppPermissions; - public static ManagePermissionsFragment newInstance(String packageName) { - ManagePermissionsFragment instance = new ManagePermissionsFragment(); + public static AppPermissionsFragment newInstance(String packageName) { + AppPermissionsFragment instance = new AppPermissionsFragment(); Bundle arguments = new Bundle(); arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName); instance.setArguments(arguments); @@ -136,17 +139,15 @@ public final class ManagePermissionsFragment extends SettingsWithHeader PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(activity); mAppPermissions = new AppPermissions(activity, packageInfo, null); - for (AppPermissions.PermissionGroup group : mAppPermissions.getPermissionGroups()) { - if (group.hasRuntimePermissions()) { - SwitchPreference preference = new SwitchPreference(activity); - preference.setOnPreferenceChangeListener(this); - preference.setKey(group.getName()); - preference.setIcon(AppPermissions.loadDrawable(pm, group.getIconPkg(), - group.getIconResId())); - preference.setTitle(group.getLabel()); - preference.setPersistent(false); - screen.addPreference(preference); - } + for (PermissionGroup group : mAppPermissions.getPermissionGroups()) { + SwitchPreference preference = new SwitchPreference(activity); + preference.setOnPreferenceChangeListener(this); + preference.setKey(group.getName()); + preference.setIcon(Utils.loadDrawable(pm, group.getIconPkg(), + group.getIconResId())); + preference.setTitle(group.getLabel()); + preference.setPersistent(false); + screen.addPreference(preference); } setPreferenceScreen(screen); @@ -155,7 +156,7 @@ public final class ManagePermissionsFragment extends SettingsWithHeader @Override public boolean onPreferenceChange(Preference preference, Object newValue) { String groupName = preference.getKey(); - AppPermissions.PermissionGroup group = mAppPermissions.getPermissionGroup(groupName); + PermissionGroup group = mAppPermissions.getPermissionGroup(groupName); if (group == null) { return false; @@ -177,7 +178,7 @@ public final class ManagePermissionsFragment extends SettingsWithHeader for (int i = 0; i < preferenceCount; i++) { SwitchPreference preference = (SwitchPreference) getPreferenceScreen().getPreference(i); - AppPermissions.PermissionGroup group = mAppPermissions + PermissionGroup group = mAppPermissions .getPermissionGroup(preference.getKey()); if (group != null) { preference.setChecked(group.areRuntimePermissionsGranted()); diff --git a/src/com/android/packageinstaller/permission/GrantPermissionFragment.java b/src/com/android/packageinstaller/permission/ui/GrantPermissionFragment.java index e751338f..0458f9b9 100644 --- a/src/com/android/packageinstaller/permission/GrantPermissionFragment.java +++ b/src/com/android/packageinstaller/permission/ui/GrantPermissionFragment.java @@ -14,10 +14,9 @@ * limitations under the License. */ -package com.android.packageinstaller.permission; +package com.android.packageinstaller.permission.ui; import android.app.Activity; -import android.app.Dialog; import android.app.DialogFragment; import android.content.DialogInterface; import android.graphics.drawable.Drawable; @@ -30,6 +29,7 @@ import android.widget.ImageView; import android.widget.TextView; import com.android.packageinstaller.R; +import com.android.packageinstaller.permission.utils.Utils; public final class GrantPermissionFragment extends DialogFragment { public static final String ARG_GROUP_NAME = "ARG_GROUP_NAME"; @@ -97,7 +97,7 @@ public final class GrantPermissionFragment extends DialogFragment { } }; - Drawable icon = AppPermissions.loadDrawable(getActivity().getPackageManager(), iconPkg, + Drawable icon = Utils.loadDrawable(getActivity().getPackageManager(), iconPkg, iconResId); iconView.setImageDrawable(icon); diff --git a/src/com/android/packageinstaller/permission/GrantPermissionsActivity.java b/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java index 287d324d..02951e9f 100644 --- a/src/com/android/packageinstaller/permission/GrantPermissionsActivity.java +++ b/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.packageinstaller.permission; +package com.android.packageinstaller.permission.ui; import android.app.Activity; import android.app.DialogFragment; @@ -25,19 +25,18 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PermissionInfo; -import android.graphics.Color; import android.hardware.camera2.utils.ArrayUtils; import android.os.Bundle; import android.text.SpannableString; import android.text.style.ForegroundColorSpan; -import android.text.style.StyleSpan; import android.util.ArrayMap; import android.util.Log; import android.util.SparseArray; import com.android.packageinstaller.R; -import com.android.packageinstaller.permission.AppPermissions.Permission; -import com.android.packageinstaller.permission.AppPermissions.PermissionGroup; +import com.android.packageinstaller.permission.model.AppPermissions; +import com.android.packageinstaller.permission.model.Permission; +import com.android.packageinstaller.permission.model.PermissionGroup; public class GrantPermissionsActivity extends Activity implements GrantPermissionFragment.OnRequestGrantPermissionGroupResult { @@ -87,7 +86,7 @@ public class GrantPermissionsActivity extends Activity implements mAppPermissions = new AppPermissions(this, callingPackageInfo, mRequestedPermissions); for (PermissionGroup group : mAppPermissions.getPermissionGroups()) { - if (group.hasRuntimePermissions() && !group.areRuntimePermissionsGranted()) { + if (!group.areRuntimePermissionsGranted()) { mRequestGrantPermissionGroups.put(group.getName(), new GroupState(group)); } } @@ -102,8 +101,7 @@ public class GrantPermissionsActivity extends Activity implements for (int i = 0; i < groupCount; i++) { GroupState groupState = mRequestGrantPermissionGroups.valueAt(i); - if (groupState.mGroup.hasRuntimePermissions() - && !groupState.mGroup.areRuntimePermissionsGranted() + if (!groupState.mGroup.areRuntimePermissionsGranted() && groupState.mState == GroupState.STATE_UNKNOWN) { // Make sure adding the fragment we will remove is not in flight. getFragmentManager().executePendingTransactions(); diff --git a/src/com/android/packageinstaller/permission/ManagePermissionsActivity.java b/src/com/android/packageinstaller/permission/ui/ManagePermissionsActivity.java index c9b9daf2..ef117a9c 100644 --- a/src/com/android/packageinstaller/permission/ManagePermissionsActivity.java +++ b/src/com/android/packageinstaller/permission/ui/ManagePermissionsActivity.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.packageinstaller.permission; +package com.android.packageinstaller.permission.ui; import android.app.Activity; import android.app.Fragment; @@ -38,7 +38,7 @@ public final class ManagePermissionsActivity extends Activity { finish(); return; } - fragment = ManagePermissionsFragment.newInstance(packageName); + fragment = AppPermissionsFragment.newInstance(packageName); } else if (Intent.ACTION_MANAGE_PERMISSION_APPS.equals(action)) { String permissionName = getIntent().getStringExtra(Intent.EXTRA_PERMISSION_NAME); if (permissionName == null) { @@ -46,7 +46,7 @@ public final class ManagePermissionsActivity extends Activity { finish(); return; } - fragment = PermissionManagementFragment.newInstance(permissionName); + fragment = PermissionAppsFragment.newInstance(permissionName); } else { Log.w(LOG_TAG, "Unrecognized action " + action); finish(); diff --git a/src/com/android/packageinstaller/permission/PermissionManagementFragment.java b/src/com/android/packageinstaller/permission/ui/PermissionAppsFragment.java index fbefdc68..f5d7d711 100644 --- a/src/com/android/packageinstaller/permission/PermissionManagementFragment.java +++ b/src/com/android/packageinstaller/permission/ui/PermissionAppsFragment.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.packageinstaller.permission; +package com.android.packageinstaller.permission.ui; import android.annotation.Nullable; import android.app.ActionBar; @@ -33,20 +33,22 @@ import android.widget.ImageView; import android.widget.TextView; import com.android.packageinstaller.R; -import com.android.packageinstaller.permission.PermissionApps.Callback; -import com.android.packageinstaller.permission.PermissionApps.PermissionApp; +import com.android.packageinstaller.permission.model.PermissionApps; +import com.android.packageinstaller.permission.model.PermissionApps.Callback; +import com.android.packageinstaller.permission.model.PermissionApps.PermissionApp; -public class PermissionManagementFragment extends SettingsWithHeader implements Callback, OnPreferenceChangeListener { +public final class PermissionAppsFragment extends SettingsWithHeader implements Callback, + OnPreferenceChangeListener { - public static PermissionManagementFragment newInstance(String permissionName) { - PermissionManagementFragment instance = new PermissionManagementFragment(); + public static PermissionAppsFragment newInstance(String permissionName) { + PermissionAppsFragment instance = new PermissionAppsFragment(); Bundle arguments = new Bundle(); arguments.putString(Intent.EXTRA_PERMISSION_NAME, permissionName); instance.setArguments(arguments); return instance; } - private PermissionApps mPermissionGroup; + private PermissionApps mPermissionApps; @Override public void onCreate(Bundle savedInstanceState) { @@ -61,7 +63,7 @@ public class PermissionManagementFragment extends SettingsWithHeader implements @Override public void onResume() { super.onResume(); - mPermissionGroup.refresh(); + mPermissionApps.refresh(); } @Override @@ -99,9 +101,9 @@ public class PermissionManagementFragment extends SettingsWithHeader implements private void bindUi() { String groupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME); - mPermissionGroup = new PermissionApps(getActivity(), groupName, this); - final Drawable icon = mPermissionGroup.getIcon(); - final CharSequence label = mPermissionGroup.getLabel(); + mPermissionApps = new PermissionApps(getActivity(), groupName, this); + final Drawable icon = mPermissionApps.getIcon(); + final CharSequence label = mPermissionApps.getLabel(); setHeader(icon, label, null); final ViewGroup rootView = (ViewGroup) getView(); @@ -128,7 +130,7 @@ public class PermissionManagementFragment extends SettingsWithHeader implements setPreferenceScreen(preferences); } preferences.removeAll(); - for (PermissionApp app : mPermissionGroup.getApps()) { + for (PermissionApp app : mPermissionApps.getApps()) { SwitchPreference pref = (SwitchPreference) findPreference(app.getKey()); if (pref == null) { pref = new SwitchPreference(context); @@ -139,14 +141,14 @@ public class PermissionManagementFragment extends SettingsWithHeader implements pref.setPersistent(false); preferences.addPreference(pref); } - pref.setChecked(app.hasRuntimePermissions()); + pref.setChecked(app.areRuntimePermissionsGranted()); } } @Override public boolean onPreferenceChange(Preference preference, Object newValue) { String pkg = preference.getKey(); - PermissionApp app = mPermissionGroup.getApp(pkg); + PermissionApp app = mPermissionApps.getApp(pkg); if (app == null) { return false; diff --git a/src/com/android/packageinstaller/permission/SettingsWithHeader.java b/src/com/android/packageinstaller/permission/ui/SettingsWithHeader.java index c074f6ca..e1e3abe2 100644 --- a/src/com/android/packageinstaller/permission/SettingsWithHeader.java +++ b/src/com/android/packageinstaller/permission/ui/SettingsWithHeader.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.packageinstaller.permission; +package com.android.packageinstaller.permission.ui; import android.content.Intent; import android.graphics.drawable.Drawable; diff --git a/src/com/android/packageinstaller/permission/utils/Utils.java b/src/com/android/packageinstaller/permission/utils/Utils.java new file mode 100644 index 00000000..0dea8f34 --- /dev/null +++ b/src/com/android/packageinstaller/permission/utils/Utils.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2015 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 com.android.packageinstaller.permission.utils; + +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.util.Log; + +public class Utils { + private static final String LOG_TAG = "Utils"; + + private Utils() { + /* do nothing - hide constructor */ + } + + public static Drawable loadDrawable(PackageManager pm, String pkg, int resId) { + try { + return pm.getResourcesForApplication(pkg).getDrawable(resId, null); + } catch (Resources.NotFoundException | PackageManager.NameNotFoundException e) { + Log.d(LOG_TAG, "Couldn't get resource", e); + return null; + } + } +} |