Added option do display and clear URI permissions.
BUG: 26447975
Change-Id: If5269a62908ce37501219ca8dba619041812d800
diff --git a/res/layout/headerless_preference_category.xml b/res/layout/headerless_preference_category.xml
new file mode 100644
index 0000000..5fdc1a0
--- /dev/null
+++ b/res/layout/headerless_preference_category.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<!-- Style for a preference category without a header title.
+ Based on tall_preference_category, but invisible and with some 0dp attributes. -->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+android:id/title"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginBottom="0dip"
+ android:textAppearance="@android:style/TextAppearance.Material.Body2"
+ android:textColor="?android:attr/colorAccent"
+ android:paddingBottom="8dp"
+ android:paddingTop="16dip"
+ android:visible="false" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index fb2b1e0..d783070 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3065,6 +3065,13 @@
<string name="clear_cache_btn_text">Clear cache</string>
<!-- Manage applications, label that appears next to the cache size -->
<string name="cache_size_label">Cache</string>
+ <!-- Manage applications, individual application info storage screen. Describes the number of URIs (directories or files) an app has been granted access (by another apps)-->
+ <plurals name="uri_permissions_text">
+ <item quantity="one">1 item</item>
+ <item quantity="other">%d items</item>
+ </plurals>
+ <!-- Manage applications, individual application info storage screen. Button below list of URIs. -->
+ <string name="clear_uri_btn_text">Clear access</string>
<!-- Manage applications, Header name used for other controls -->
<string name="controls_label">Controls</string>
<!-- Manage applications, text label for button to kill / force stop an application -->
diff --git a/res/xml/app_storage_settings.xml b/res/xml/app_storage_settings.xml
index 1620164..3faf9c8 100644
--- a/res/xml/app_storage_settings.xml
+++ b/res/xml/app_storage_settings.xml
@@ -86,4 +86,16 @@
android:selectable="false"
android:layout="@layout/single_button_panel" />
+ <com.android.settings.applications.SpacePreference
+ android:layout_height="8dp" />
+
+ <PreferenceCategory
+ android:key="uri_category"
+ android:layout="@layout/headerless_preference_category" >
+ <com.android.settings.applications.LayoutPreference
+ android:key="clear_uri_button"
+ android:layout="@layout/single_button_panel"
+ android:selectable="false" />
+ </PreferenceCategory>
+
</PreferenceScreen>
diff --git a/src/com/android/settings/applications/AppStorageSettings.java b/src/com/android/settings/applications/AppStorageSettings.java
index 01780f9..71d1667 100644
--- a/src/com/android/settings/applications/AppStorageSettings.java
+++ b/src/com/android/settings/applications/AppStorageSettings.java
@@ -22,9 +22,11 @@
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.UriPermission;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDataObserver;
-import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
@@ -37,6 +39,7 @@
import android.support.v7.preference.PreferenceCategory;
import android.text.format.Formatter;
import android.util.Log;
+import android.util.MutableInt;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
@@ -49,9 +52,12 @@
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.Callbacks;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.TreeMap;
public class AppStorageSettings extends AppInfoWithHeader
implements OnClickListener, Callbacks, DialogInterface.OnClickListener {
@@ -88,6 +94,9 @@
private static final String KEY_CLEAR_DATA = "clear_data_button";
private static final String KEY_CLEAR_CACHE = "clear_cache_button";
+ private static final String KEY_URI_CATEGORY = "uri_category";
+ private static final String KEY_CLEAR_URI = "clear_uri_button";
+
private Preference mTotalSize;
private Preference mAppSize;
private Preference mDataSize;
@@ -102,6 +111,11 @@
private Preference mStorageUsed;
private Button mChangeStorageButton;
+ // Views related to URI permissions
+ private Button mClearUriButton;
+ private LayoutPreference mClearUri;
+ private PreferenceCategory mUri;
+
private boolean mCanClearData = true;
private boolean mHaveSizes = false;
@@ -166,6 +180,13 @@
mClearCacheButton = (Button) ((LayoutPreference) findPreference(KEY_CLEAR_CACHE))
.findViewById(R.id.button);
mClearCacheButton.setText(R.string.clear_cache_btn_text);
+
+ // URI permissions section
+ mUri = (PreferenceCategory) findPreference(KEY_URI_CATEGORY);
+ mClearUri = (LayoutPreference) mUri.findPreference(KEY_CLEAR_URI);
+ mClearUriButton = (Button) mClearUri.findViewById(R.id.button);
+ mClearUriButton.setText(R.string.clear_uri_btn_text);
+ mClearUriButton.setOnClickListener(this);
}
@Override
@@ -189,6 +210,8 @@
}
} else if (v == mChangeStorageButton && mDialogBuilder != null && !isMoveInProgress()) {
mDialogBuilder.show();
+ } else if (v == mClearUriButton) {
+ clearUriPermissions();
}
}
@@ -239,7 +262,6 @@
}
mClearDataButton.setEnabled(false);
mClearCacheButton.setEnabled(false);
-
} else {
mHaveSizes = true;
long codeSize = mAppEntry.codeSize;
@@ -301,6 +323,7 @@
return false;
}
refreshSizeInfo();
+ refreshGrantedUriPermissions();
final VolumeInfo currentVol = getActivity().getPackageManager()
.getPackageCurrentVolume(mAppEntry.info);
@@ -413,6 +436,83 @@
}
}
+ private void refreshGrantedUriPermissions() {
+ // Clear UI first (in case the activity has been resumed)
+ removeUriPermissionsFromUi();
+
+ // Gets all URI permissions from am.
+ ActivityManager am = (ActivityManager) getActivity().getSystemService(
+ Context.ACTIVITY_SERVICE);
+ List<UriPermission> perms =
+ am.getGrantedUriPermissions(mAppEntry.info.packageName).getList();
+
+ if (perms.isEmpty()) {
+ mClearUriButton.setVisibility(View.GONE);
+ return;
+ }
+
+ PackageManager pm = getActivity().getPackageManager();
+
+ // Group number of URIs by app.
+ Map<CharSequence, MutableInt> uriCounters = new TreeMap<>();
+ for (UriPermission perm : perms) {
+ String authority = perm.getUri().getAuthority();
+ ProviderInfo provider = pm.resolveContentProvider(authority, 0);
+ CharSequence app = provider.applicationInfo.loadLabel(pm);
+ MutableInt count = uriCounters.get(app);
+ if (count == null) {
+ uriCounters.put(app, new MutableInt(1));
+ } else {
+ count.value++;
+ }
+ }
+
+ // Dynamically add the preferences, one per app.
+ int order = 0;
+ for (Map.Entry<CharSequence, MutableInt> entry : uriCounters.entrySet()) {
+ int numberResources = entry.getValue().value;
+ Preference pref = new Preference(getPrefContext());
+ pref.setTitle(entry.getKey());
+ pref.setSummary(getPrefContext().getResources()
+ .getQuantityString(R.plurals.uri_permissions_text, numberResources,
+ numberResources));
+ pref.setSelectable(false);
+ pref.setLayoutResource(R.layout.horizontal_preference);
+ pref.setOrder(order);
+ Log.v(TAG, "Adding preference '" + pref + "' at order " + order);
+ mUri.addPreference(pref);
+ }
+
+ if (mAppControlRestricted) {
+ mClearUriButton.setEnabled(false);
+ }
+
+ mClearUri.setOrder(order);
+ mClearUriButton.setVisibility(View.VISIBLE);
+
+ }
+
+ private void clearUriPermissions() {
+ // Synchronously revoke the permissions.
+ final ActivityManager am = (ActivityManager) getActivity().getSystemService(
+ Context.ACTIVITY_SERVICE);
+ am.clearGrantedUriPermissions(mAppEntry.info.packageName);
+
+ // Update UI
+ refreshGrantedUriPermissions();
+ }
+
+ private void removeUriPermissionsFromUi() {
+ // Remove all preferences but the clear button.
+ int count = mUri.getPreferenceCount();
+ for (int i = count - 1; i >= 0; i--) {
+ Preference pref = mUri.getPreference(i);
+ if (pref != mClearUri) {
+ mUri.removePreference(pref);
+ }
+ }
+ }
+
@Override
protected AlertDialog createDialog(int id, int errorCode) {
switch (id) {