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) {