Merge "Enable apps to exclude their own roots from the DocumentsUI roots list." into mnc-dev
diff --git a/api/current.txt b/api/current.txt
index 049d790..6cd3932 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -26099,6 +26099,7 @@
     method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
     method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String);
     field public static final java.lang.String EXTRA_ERROR = "error";
+    field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
     field public static final java.lang.String EXTRA_INFO = "info";
     field public static final java.lang.String EXTRA_LOADING = "loading";
     field public static final java.lang.String PROVIDER_INTERFACE = "android.content.action.DOCUMENTS_PROVIDER";
diff --git a/api/system-current.txt b/api/system-current.txt
index 8b3c181..810bed4 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -28029,6 +28029,7 @@
     method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
     method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String);
     field public static final java.lang.String EXTRA_ERROR = "error";
+    field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
     field public static final java.lang.String EXTRA_INFO = "info";
     field public static final java.lang.String EXTRA_LOADING = "loading";
     field public static final java.lang.String PROVIDER_INTERFACE = "android.content.action.DOCUMENTS_PROVIDER";
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 69338b0..30535ff 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -93,6 +93,12 @@
     public static final String EXTRA_SHOW_ADVANCED = "android.content.extra.SHOW_ADVANCED";
 
     /**
+     * Set this in a DocumentsUI intent to cause a package's own roots to be
+     * excluded from the roots list.
+     */
+    public static final String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
+
+    /**
      * Included in {@link AssetFileDescriptor#getExtras()} when returned
      * thumbnail should be rotated.
      *
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index cb21131..8ea5816 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -25,6 +25,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.concurrent.Executor;
 
@@ -32,6 +33,10 @@
 import android.app.Activity;
 import android.app.Fragment;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -232,9 +237,38 @@
         invalidateOptionsMenu();
     }
 
+    final List<String> getExcludedAuthorities() {
+        List<String> authorities = new ArrayList<>();
+        if (getIntent().getBooleanExtra(DocumentsContract.EXTRA_EXCLUDE_SELF, false)) {
+            // Exclude roots provided by the calling package.
+            String packageName = getCallingPackageMaybeExtra();
+            try {
+                PackageInfo pkgInfo = getPackageManager().getPackageInfo(packageName,
+                        PackageManager.GET_PROVIDERS);
+                for (ProviderInfo provider: pkgInfo.providers) {
+                    authorities.add(provider.authority);
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.e(mTag, "Calling package name does not resolve: " + packageName);
+            }
+        }
+        return authorities;
+    }
+
     final String getCallingPackageMaybeExtra() {
-        final String extra = getIntent().getStringExtra(DocumentsContract.EXTRA_PACKAGE_NAME);
-        return (extra != null) ? extra : getCallingPackage();
+        String callingPackage = getCallingPackage();
+        // System apps can set the calling package name using an extra.
+        try {
+            ApplicationInfo info = getPackageManager().getApplicationInfo(callingPackage, 0);
+            if (info.isSystemApp() || info.isUpdatedSystemApp()) {
+                final String extra = getIntent().getStringExtra(DocumentsContract.EXTRA_PACKAGE_NAME);
+                if (extra != null) {
+                    callingPackage = extra;
+                }
+            }
+        } finally {
+            return callingPackage;
+        }
     }
 
     public static BaseActivity get(Fragment fragment) {
@@ -287,6 +321,9 @@
         /** Currently copying file */
         public List<DocumentInfo> selectedDocumentsForCopy = new ArrayList<DocumentInfo>();
 
+        /** Name of the package that started DocsUI */
+        public List<String> excludedAuthorities = new ArrayList<>();
+
         public static final int ACTION_OPEN = 1;
         public static final int ACTION_CREATE = 2;
         public static final int ACTION_GET_CONTENT = 3;
@@ -327,6 +364,7 @@
             out.writeString(currentSearch);
             out.writeMap(dirState);
             out.writeList(selectedDocumentsForCopy);
+            out.writeList(excludedAuthorities);
         }
 
         public static final Creator<State> CREATOR = new Creator<State>() {
@@ -348,6 +386,7 @@
                 state.currentSearch = in.readString();
                 in.readMap(state.dirState, null);
                 in.readList(state.selectedDocumentsForCopy, null);
+                in.readList(state.excludedAuthorities, null);
                 return state;
             }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index da59d0e..69ae34e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -256,6 +256,8 @@
                     BaseActivity.DocumentsIntent.EXTRA_DIRECTORY_COPY, false);
         }
 
+        state.excludedAuthorities = getExcludedAuthorities();
+
         return state;
     }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index 27e8f20..fbcb938 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -383,6 +383,12 @@
                 continue;
             }
 
+            // Exclude roots from the calling package.
+            if (state.excludedAuthorities.contains(root.authority)) {
+                if (LOGD) Log.d(TAG, "Excluding root " + root.authority + " from calling package.");
+                continue;
+            }
+
             matching.add(root);
         }
         return matching;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index 97d8ed0..ecf4d6c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -55,7 +55,6 @@
     public String mimeTypes;
 
     /** Derived fields that aren't persisted */
-    public String derivedPackageName;
     public String[] derivedMimeTypes;
     public int derivedIcon;
 
@@ -75,7 +74,6 @@
         availableBytes = -1;
         mimeTypes = null;
 
-        derivedPackageName = null;
         derivedMimeTypes = null;
         derivedIcon = 0;
     }
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java
index 7faa3ce..8c5bac1 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java
@@ -114,6 +114,31 @@
                 RootsCache.getMatchingRoots(mRoots, mState));
     }
 
+    public void testExcludedAuthorities() throws Exception {
+        final List<RootInfo> roots = Lists.newArrayList();
+
+        // Set up some roots
+        for (int i = 0; i < 5; ++i) {
+            RootInfo root = new RootInfo();
+            root.authority = "authority" + i;
+            roots.add(root);
+        }
+        // Make some allowed authorities
+        List<RootInfo> allowedRoots = Lists.newArrayList(
+            roots.get(0), roots.get(2), roots.get(4));
+        // Set up the excluded authority list
+        for (RootInfo root: roots) {
+            if (!allowedRoots.contains(root)) {
+                mState.excludedAuthorities.add(root.authority);
+            }
+        }
+        mState.acceptMimes = new String[] { "*/*" };
+
+        assertContainsExactly(
+            allowedRoots,
+            RootsCache.getMatchingRoots(roots, mState));
+    }
+
     private static void assertContainsExactly(List<?> expected, List<?> actual) {
         assertEquals(expected.size(), actual.size());
         for (Object o : expected) {