summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSvet Ganov <svetoslavganov@google.com>2015-03-22 18:49:22 -0700
committerSvetoslav <svetoslavganov@google.com>2015-03-24 20:18:16 -0700
commit72b68774632f03289dc02d0e3dada06e9c984fd3 (patch)
treec427d0d5841da4391216b22728223508c0b49dd6
parent3c99a2b334eb00b6cfa078a9e9ed2d6a151bde46 (diff)
downloadandroid_packages_apps_PackageInstaller-72b68774632f03289dc02d0e3dada06e9c984fd3.tar.gz
android_packages_apps_PackageInstaller-72b68774632f03289dc02d0e3dada06e9c984fd3.tar.bz2
android_packages_apps_PackageInstaller-72b68774632f03289dc02d0e3dada06e9c984fd3.zip
Initial version of the grant permission UI.
Change-Id: Ieb311d29d122cfce130ae1a26889dd7794548833
-rw-r--r--AndroidManifest.xml15
-rw-r--r--res/layout/grant_permissions.xml82
-rw-r--r--res/values/strings.xml27
-rw-r--r--src/com/android/packageinstaller/permission/AppPermissions.java293
-rw-r--r--src/com/android/packageinstaller/permission/GrantPermissionFragment.java109
-rw-r--r--src/com/android/packageinstaller/permission/GrantPermissionsActivity.java246
-rw-r--r--src/com/android/packageinstaller/permission/ManagePermissionsActivity.java41
-rw-r--r--src/com/android/packageinstaller/permission/ManagePermissionsFragment.java128
8 files changed, 939 insertions, 2 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 781d60b3..3273ddef 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -62,7 +62,7 @@
android:configChanges="orientation|keyboardHidden|screenSize"
android:exported="false" />
- <activity android:name=".GrantActivity"
+ <activity android:name=".permission.GrantPermissionsActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:excludeFromRecents="true"
android:theme="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">
@@ -72,5 +72,16 @@
</intent-filter>
</activity>
+ <activity android:name=".permission.ManagePermissionsActivity"
+ android:configChanges="orientation|keyboardHidden|screenSize"
+ android:excludeFromRecents="true"
+ android:permission="android.permission.GRANT_REVOKE_PERMISSIONS">
+ <intent-filter>
+ <action android:name="android.intent.action.MANAGE_APP_PERMISSIONS" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
</application>
-</manifest>
+
+</manifest>
diff --git a/res/layout/grant_permissions.xml b/res/layout/grant_permissions.xml
new file mode 100644
index 00000000..7c6bfb7a
--- /dev/null
+++ b/res/layout/grant_permissions.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:paddingTop="16dip"
+ android:paddingBottom="12dip"
+ android:paddingStart="16dip"
+ android:paddingEnd="16dip"
+ android:minHeight="108dip">
+
+ <ImageView
+ android:id="@+id/permission_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="6dip"
+ android:paddingEnd="10dip">
+ </ImageView>
+
+ <TextView
+ android:id="@+id/permission_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/permission_icon"
+ style="?android:attr/textAppearanceMedium">
+ </TextView>
+
+ <CheckBox
+ android:id="@+id/do_not_ask_checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+ </CheckBox>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/permission_message"
+ android:orientation="horizontal"
+ android:paddingTop="4dip">
+
+ <TextView
+ android:id="@+id/current_page_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:visibility="invisible">
+ </TextView>
+
+ <Button
+ android:id="@+id/permission_deny_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="?android:attr/buttonBarButtonStyle"
+ android:text="@string/grant_dialog_button_deny">
+ </Button>
+
+ <Button
+ android:id="@+id/permission_allow_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="?android:attr/buttonBarButtonStyle"
+ android:text="@string/grant_dialog_button_allow">
+ </Button>
+
+ </LinearLayout>
+
+</RelativeLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c4327fbe..e807392f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -170,4 +170,31 @@
<string name="grant_confirm_question">Do you want to grant the following permissions?
It will get access to:</string>
+
+ <!-- Title for the dialog button to allow a permission grant. -->
+ <string name="grant_dialog_button_allow">Allow</string>
+
+ <!-- Title for the dialog button to deny a permission grant. -->
+ <string name="grant_dialog_button_deny">Deny</string>
+
+ <!-- Template for the current permission from the total number of permissions. -->
+ <string name="current_permission_template">
+ <xliff:g id="current_permission_index" example="1">%1$s</xliff:g> of
+ <xliff:g id="permission_count" example="2">%2$s</xliff:g></string>
+
+ <!-- Template for the warning message when an app requests a permission. -->
+ <string name="permission_warning_template">Allow
+ <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> to
+ <xliff:g id="action" example="do something">%2$s</xliff:g>?</string>
+
+ <!-- Permissions -->
+
+ <!-- Title of an application permission, listed so the user can choose whether they want
+ to allow the application to do this. -->
+ <string name="permlab_togglePermissions" translatable="false">toggle permissions</string>
+ <!-- Description of an application permission, listed so the user can choose whether
+ they want to allow the application to do this. -->
+ <string name="permdesc_togglePermissions" translatable="false">Allows the holder to launch
+ the UI for toggling app permissions. Should never be needed for normal apps.</string>
+
</resources>
diff --git a/src/com/android/packageinstaller/permission/AppPermissions.java b/src/com/android/packageinstaller/permission/AppPermissions.java
new file mode 100644
index 00000000..658accc1
--- /dev/null
+++ b/src/com/android/packageinstaller/permission/AppPermissions.java
@@ -0,0 +1,293 @@
+/*
+ * 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.NameNotFoundException;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+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();
+
+ 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 int iconResId = (groupInfo != null) ? groupInfo.icon : permInfo.icon;
+
+ group = new PermissionGroup(mContext, mPackageInfo.packageName,
+ groupName, groupLabel, 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 int mIconResId;
+
+ private boolean mHasRuntimePermissions;
+
+ public String getName() {
+ return mName;
+ }
+
+ public int getIconResId() {
+ return mIconResId;
+ }
+
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ public PermissionGroup(Context context, String packageName,
+ String name, CharSequence label, int iconResId) {
+ mPackageName = packageName;
+ mContext = context;
+ mName = name;
+ mLabel = label;
+ 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;
+ }
+ }
+}
diff --git a/src/com/android/packageinstaller/permission/GrantPermissionFragment.java b/src/com/android/packageinstaller/permission/GrantPermissionFragment.java
new file mode 100644
index 00000000..65b60687
--- /dev/null
+++ b/src/com/android/packageinstaller/permission/GrantPermissionFragment.java
@@ -0,0 +1,109 @@
+/*
+ * 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.app.DialogFragment;
+import android.os.Bundle;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.android.packageinstaller.R;
+
+public final class GrantPermissionFragment extends DialogFragment {
+ public static final String ARG_GROUP_NAME = "ARG_GROUP_NAME";
+ public static final String ARG_GROUP_COUNT = "ARG_GROUP_COUNT";
+ public static final String ARG_GROUP_INDEX = "ARG_GROUP_INDEX";
+ public static final String ARG_GROUP_ICON_RES_ID = "ARG_GROUP_ICON";
+ public static final String ARG_GROUP_MESSAGE = "ARG_GROUP_MESSAGE";
+
+ public interface OnRequestGrantPermissionGroupResult {
+ public void onRequestGrantPermissionGroupResult(String name, boolean granted);
+ }
+
+ public static GrantPermissionFragment newInstance(String groupName,
+ int groupCount, int groupIndex, int iconResId, CharSequence message) {
+ GrantPermissionFragment instance = new GrantPermissionFragment();
+ instance.setStyle(STYLE_NORMAL,
+ android.R.style.Theme_DeviceDefault_Light_Dialog_NoActionBar);
+
+ Bundle arguments = new Bundle();
+ arguments.putString(ARG_GROUP_NAME, groupName);
+ arguments.putInt(ARG_GROUP_COUNT, groupCount);
+ arguments.putInt(ARG_GROUP_INDEX, groupIndex);
+ arguments.putInt(ARG_GROUP_ICON_RES_ID, iconResId);
+ arguments.putCharSequence(ARG_GROUP_MESSAGE, message);
+ instance.setArguments(arguments);
+
+ return instance;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View content = inflater.inflate(R.layout.grant_permissions, container, false);
+
+ final String groupName = getArguments().getString(ARG_GROUP_NAME);
+ final String message = getArguments().getString(ARG_GROUP_MESSAGE);
+ final int iconResId = getArguments().getInt(ARG_GROUP_ICON_RES_ID);
+ final int groupCount = getArguments().getInt(ARG_GROUP_COUNT);
+ final int groupIndex = getArguments().getInt(ARG_GROUP_INDEX);
+
+ final ImageView iconView = (ImageView) content.findViewById(R.id.permission_icon);
+ final View allowButton = content.findViewById(R.id.permission_allow_button);
+ final View denyButton = content.findViewById(R.id.permission_deny_button);
+ final View doNotAskCheckbox = content.findViewById(R.id.do_not_ask_checkbox);
+ final TextView currentGroupView = (TextView) content.findViewById(R.id.current_page_text);
+ final TextView messageView = (TextView) content.findViewById(R.id.permission_message);
+
+ OnClickListener clickListener = new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (view == allowButton) {
+ ((OnRequestGrantPermissionGroupResult) getActivity())
+ .onRequestGrantPermissionGroupResult(groupName, true);
+ } else if (view == denyButton) {
+ ((OnRequestGrantPermissionGroupResult) getActivity())
+ .onRequestGrantPermissionGroupResult(groupName, false);
+ } else if (view == doNotAskCheckbox) {
+ //TODO: Implement me.
+ }
+ }
+ };
+
+ iconView.setImageResource(iconResId);
+
+ messageView.setText(message);
+
+ allowButton.setOnClickListener(clickListener);
+ denyButton.setOnClickListener(clickListener);
+ doNotAskCheckbox.setOnClickListener(clickListener);
+
+ if (groupCount > 1) {
+ currentGroupView.setVisibility(View.VISIBLE);
+ currentGroupView.setText(getString(R.string.current_permission_template,
+ groupIndex + 1, groupCount));
+ } else {
+ currentGroupView.setVisibility(View.INVISIBLE);
+ }
+
+ return content;
+ }
+}
diff --git a/src/com/android/packageinstaller/permission/GrantPermissionsActivity.java b/src/com/android/packageinstaller/permission/GrantPermissionsActivity.java
new file mode 100644
index 00000000..4c3eb247
--- /dev/null
+++ b/src/com/android/packageinstaller/permission/GrantPermissionsActivity.java
@@ -0,0 +1,246 @@
+/*
+ * 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.app.Activity;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PermissionInfo;
+import android.hardware.camera2.utils.ArrayUtils;
+import android.os.Bundle;
+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;
+
+public class GrantPermissionsActivity extends Activity implements
+ GrantPermissionFragment.OnRequestGrantPermissionGroupResult {
+ private static final String LOG_TAG = "GrantPermissionsActivity";
+
+ private static final String TAG_GRANT_PERMISSION_GROUP_FRAGMENT =
+ "TAG_GRANT_PERMISSION_GROUP_FRAGMENT";
+
+ private static final int PERMISSION_GRANTED = 1;
+ private static final int PERMISSION_DENIED = 2;
+ private static final int PERMISSION_DENIED_RUNTIME = 3;
+
+ private String[] mRequestedPermissions;
+ private int[] mGrantResults;
+ private final SparseArray<String> mRequestedRuntimePermissions = new SparseArray<>();
+
+ private ArrayMap<String, GroupState> mRequestGrantPermissionGroups = new ArrayMap<>();
+
+ private AppPermissions mAppPermissions;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ mRequestedPermissions = getIntent().getStringArrayExtra(
+ PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);
+ if (mRequestedPermissions == null) {
+ mRequestedPermissions = new String[0];
+ }
+
+ mGrantResults = new int[mRequestedPermissions.length];
+
+ final int requestedPermCount = mRequestedPermissions.length;
+ if (requestedPermCount == 0) {
+ setResultAndFinish();
+ return;
+ }
+
+ PackageInfo callingPackageInfo = getCallingPackageInfo();
+ if (callingPackageInfo == null) {
+ setResultAndFinish();
+ return;
+ }
+
+ updateDefaultResults(callingPackageInfo);
+
+ mAppPermissions = new AppPermissions(this, callingPackageInfo, mRequestedPermissions);
+
+ for (PermissionGroup group : mAppPermissions.getPermissionGroups()) {
+ if (group.hasRuntimePermissions() && !group.areRuntimePermissionsGranted()) {
+ mRequestGrantPermissionGroups.put(group.getName(), new GroupState(group));
+ }
+ }
+
+ if (!showNextPermissionGroupFragment()) {
+ setResultAndFinish();
+ }
+ }
+
+ private boolean showNextPermissionGroupFragment() {
+ final int groupCount = mRequestGrantPermissionGroups.size();
+
+ for (int i = 0; i < groupCount; i++) {
+ GroupState groupState = mRequestGrantPermissionGroups.valueAt(i);
+ if (groupState.mGroup.hasRuntimePermissions()
+ && !groupState.mGroup.areRuntimePermissionsGranted()
+ && groupState.mState == GroupState.STATE_UNKNOWN) {
+ // Make sure adding the fragment we will remove is not in flight.
+ getFragmentManager().executePendingTransactions();
+
+ // Remove old grant fragment if such exists.
+ FragmentTransaction transaction = getFragmentManager().beginTransaction();
+ Fragment oldFragment = getFragmentManager().findFragmentByTag(
+ TAG_GRANT_PERMISSION_GROUP_FRAGMENT);
+ if (oldFragment != null) {
+ transaction.remove(oldFragment);
+ }
+ transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE);
+ transaction.commit();
+
+ CharSequence message = getString(R.string.permission_warning_template,
+ mAppPermissions.getAppLabel(), groupState.mGroup.getLabel());
+
+ // Add the new grant fragment.
+ // TODO: Use a real message for the action and a real icon. We need group action APIs
+ DialogFragment newFragment = GrantPermissionFragment
+ .newInstance(groupState.mGroup.getName(), groupCount, i,
+ android.R.drawable.ic_dialog_info, message);
+
+ newFragment.show(getFragmentManager(), TAG_GRANT_PERMISSION_GROUP_FRAGMENT);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public void onRequestGrantPermissionGroupResult(String name, boolean granted) {
+ GroupState groupState = mRequestGrantPermissionGroups.get(name);
+ if (groupState.mGroup != null) {
+ if (granted) {
+ groupState.mGroup.grantRuntimePermissions();
+ groupState.mState = GroupState.STATE_ALLOWED;
+ updateGrantResults(groupState.mGroup);
+ } else {
+ groupState.mState = GroupState.STATE_DENIED;
+ }
+ }
+ if (!showNextPermissionGroupFragment()) {
+ setResultAndFinish();
+ }
+ }
+
+ private void updateGrantResults(PermissionGroup group) {
+ for (Permission permission : group.getPermissions()) {
+ if (permission.isGranted()) {
+ final int index = ArrayUtils.getArrayIndex(
+ mRequestedPermissions, permission.getName());
+ if (index >= 0) {
+ mGrantResults[index] = PackageManager.PERMISSION_GRANTED;
+ }
+ }
+ }
+ }
+
+ private int computePermissionGrantState(PackageInfo callingPackageInfo, String permission) {
+ boolean permissionRequested = false;
+
+ for (int i = 0; i < callingPackageInfo.requestedPermissions.length; i++) {
+ if (permission.equals(callingPackageInfo.requestedPermissions[i])) {
+ permissionRequested = true;
+ if ((callingPackageInfo.requestedPermissionsFlags[i]
+ & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0) {
+ return PERMISSION_GRANTED;
+ }
+ break;
+ }
+ }
+
+ if (!permissionRequested) {
+ return PERMISSION_DENIED;
+ }
+
+ try {
+ PermissionInfo pInfo = getPackageManager().getPermissionInfo(permission, 0);
+ if ((pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ != PermissionInfo.PROTECTION_DANGEROUS) {
+ return PERMISSION_DENIED;
+ }
+ } catch (NameNotFoundException e) {
+ /* ignore */
+ }
+
+ return PERMISSION_DENIED_RUNTIME;
+ }
+
+ private PackageInfo getCallingPackageInfo() {
+ try {
+ return getPackageManager().getPackageInfo(getCallingPackage(),
+ PackageManager.GET_PERMISSIONS);
+ } catch (NameNotFoundException e) {
+ Log.i(LOG_TAG, "No package:" + getCallingPackage(), e);
+ return null;
+ }
+ }
+
+ private void updateDefaultResults(PackageInfo callingPackageInfo) {
+ final int requestedPermCount = mRequestedPermissions.length;
+ for (int i = 0; i < requestedPermCount; i++) {
+ String permission = mRequestedPermissions[i];
+ final int state = computePermissionGrantState(callingPackageInfo, permission);
+ switch (state) {
+ case PERMISSION_GRANTED: {
+ mGrantResults[i] = PackageManager.PERMISSION_GRANTED;
+ } break;
+
+ case PERMISSION_DENIED: {
+ mGrantResults[i] = PackageManager.PERMISSION_DENIED;
+ } break;
+
+ case PERMISSION_DENIED_RUNTIME: {
+ mGrantResults[i] = PackageManager.PERMISSION_DENIED;
+ mRequestedRuntimePermissions.put(i, permission);
+ } break;
+ }
+ }
+ }
+
+ private void setResultAndFinish() {
+ Intent result = new Intent(PackageManager.ACTION_REQUEST_PERMISSIONS);
+ result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, mRequestedPermissions);
+ result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS, mGrantResults);
+ setResult(RESULT_OK, result);
+ finish();
+ }
+
+ private static final class GroupState {
+ public static final int STATE_UNKNOWN = 0;
+ public static final int STATE_ALLOWED = 1;
+ public static final int STATE_DENIED = 2;
+
+ public final PermissionGroup mGroup;
+ public int mState = STATE_UNKNOWN;
+
+ public GroupState(PermissionGroup group) {
+ mGroup = group;
+ }
+ }
+}
diff --git a/src/com/android/packageinstaller/permission/ManagePermissionsActivity.java b/src/com/android/packageinstaller/permission/ManagePermissionsActivity.java
new file mode 100644
index 00000000..598f5e02
--- /dev/null
+++ b/src/com/android/packageinstaller/permission/ManagePermissionsActivity.java
@@ -0,0 +1,41 @@
+/*
+* 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.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+public final class ManagePermissionsActivity extends Activity {
+ private static final String LOG_TAG = "ManagePermissionsActivity";
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+ if (packageName == null) {
+ Log.i(LOG_TAG, "Missing mandatory argument ARG_PACKAGE_NAME");
+ finish();
+ return;
+ }
+
+ ManagePermissionsFragment fragment = ManagePermissionsFragment.newInstance(packageName);
+ getFragmentManager().beginTransaction().replace(android.R.id.content, fragment).commit();
+ }
+}
diff --git a/src/com/android/packageinstaller/permission/ManagePermissionsFragment.java b/src/com/android/packageinstaller/permission/ManagePermissionsFragment.java
new file mode 100644
index 00000000..75e30aef
--- /dev/null
+++ b/src/com/android/packageinstaller/permission/ManagePermissionsFragment.java
@@ -0,0 +1,128 @@
+/*
+* 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.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceScreen;
+import android.preference.SwitchPreference;
+import android.util.Log;
+
+public final class ManagePermissionsFragment extends PreferenceFragment {
+ private static final String LOG_TAG = "ManagePermissionsFragment";
+
+ private AppPermissions mAppPermissions;
+
+ public static ManagePermissionsFragment newInstance(String packageName) {
+ ManagePermissionsFragment instance = new ManagePermissionsFragment();
+ Bundle arguments = new Bundle();
+ arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
+ instance.setArguments(arguments);
+ return instance;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ bindUi();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateUi();
+ }
+
+ private void bindUi() {
+ String packageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
+
+ final PackageInfo packageInfo = getPackageInfo(packageName);
+ if (packageInfo == null) {
+ getActivity().finish();
+ return;
+ }
+
+ PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(getActivity());
+ mAppPermissions = new AppPermissions(getActivity(),
+ packageInfo, null);
+
+ Preference.OnPreferenceChangeListener changeListener =
+ new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ String groupName = preference.getKey();
+ AppPermissions.PermissionGroup group = mAppPermissions
+ .getPermissionGroup(groupName);
+
+ if (group == null) {
+ return false;
+ }
+
+ if (newValue == Boolean.TRUE) {
+ group.grantRuntimePermissions();
+ } else {
+ group.revokeRuntimePermissions();
+ }
+
+ return true;
+ }
+ };
+
+ for (AppPermissions.PermissionGroup group : mAppPermissions.getPermissionGroups()) {
+ if (group.hasRuntimePermissions()) {
+ SwitchPreference preference = new SwitchPreference(getActivity());
+ preference.setOnPreferenceChangeListener(changeListener);
+ preference.setKey(group.getName());
+ preference.setIcon(group.getIconResId());
+ preference.setTitle(group.getLabel());
+ preference.setPersistent(false);
+ screen.addPreference(preference);
+ }
+ }
+
+ setPreferenceScreen(screen);
+ }
+
+ private void updateUi() {
+ mAppPermissions.refresh();
+
+ final int preferenceCount = getPreferenceScreen().getPreferenceCount();
+ for (int i = 0; i < preferenceCount; i++) {
+ SwitchPreference preference = (SwitchPreference)
+ getPreferenceScreen().getPreference(i);
+ AppPermissions.PermissionGroup group = mAppPermissions
+ .getPermissionGroup(preference.getKey());
+ if (group != null) {
+ preference.setChecked(group.areRuntimePermissionsGranted());
+ }
+ }
+ }
+
+ private PackageInfo getPackageInfo(String packageName) {
+ try {
+ return getActivity().getPackageManager().getPackageInfo(
+ packageName, PackageManager.GET_PERMISSIONS);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.i(LOG_TAG, "No package:" + getActivity().getCallingPackage(), e);
+ return null;
+ }
+ }
+} \ No newline at end of file