Merge "Fix MediaDocumentsProvider notification bugs." into rvc-dev
diff --git a/apex/framework/Android.bp b/apex/framework/Android.bp
index bf35418..07f3451 100644
--- a/apex/framework/Android.bp
+++ b/apex/framework/Android.bp
@@ -52,6 +52,7 @@
 stubs_defaults {
     name: "framework-mediaprovider-stubs-srcs-defaults",
     srcs: [":framework-mediaprovider-sources"],
+    dist: { dest: "framework-mediaprovider.txt" },
 }
 
 droidstubs {
@@ -90,18 +91,21 @@
     name: "framework-mediaprovider-stubs-publicapi",
     srcs: [":framework-mediaprovider-stubs-srcs-publicapi"],
     defaults: ["framework-module-stubs-lib-defaults-publicapi"],
+    dist: { dest: "framework-mediaprovider.jar" },
 }
 
 java_library {
     name: "framework-mediaprovider-stubs-systemapi",
     srcs: [":framework-mediaprovider-stubs-srcs-systemapi"],
     defaults: ["framework-module-stubs-lib-defaults-systemapi"],
+    dist: { dest: "framework-mediaprovider.jar" },
 }
 
 java_library {
     name: "framework-mediaprovider-stubs-module_libs_api",
     srcs: [":framework-mediaprovider-stubs-srcs-module_libs_api"],
     defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
+    dist: { dest: "framework-mediaprovider.jar" },
 }
 
 java_library {
diff --git a/jni/FuseDaemon.cpp b/jni/FuseDaemon.cpp
index 105e087..342662a 100644
--- a/jni/FuseDaemon.cpp
+++ b/jni/FuseDaemon.cpp
@@ -247,29 +247,12 @@
     // because fuse_lowlevel_ops documents that the root inode is always one
     // (see FUSE_ROOT_ID in fuse_lowlevel.h). There are no particular requirements
     // on any of the other inodes in the FS.
-    // Also, if ctx is not nullptr, it will also check if the path can be accessible
-    // by the caller, and return nullptr if it cannot access.
-    inline node* FromInode(__u64 inode, const struct fuse_ctx* ctx) {
+    inline node* FromInode(__u64 inode) {
         if (inode == FUSE_ROOT_ID) {
             return root;
         }
 
-        node* result = node::FromInode(inode, &tracker);
-        if (ctx == nullptr || ctx->uid < AID_APP_START) {
-            return result;
-        }
-
-        string path = result->BuildPath();
-        std::smatch match;
-        if (std::regex_match(path, match, PATTERN_OWNED_PATH)) {
-            std::string const & pkg = match[1];
-            if (!mp->IsUidForPackage(pkg, ctx->uid)) {
-                PLOG(WARNING) << "Invalid file access from " << pkg << ": " << path;
-                return nullptr;
-            }
-        }
-
-        return result;
+        return node::FromInode(inode, &tracker);
     }
 
     inline __u64 ToInode(node* node) const {
@@ -465,17 +448,38 @@
     node::DeleteTree(fuse->root);
 }
 
+// Return true if the path is accessible for that uid.
+static bool is_app_accessible_path(MediaProviderWrapper* mp, const string& path, uid_t uid) {
+    if (uid < AID_APP_START) {
+        return true;
+    }
+
+    std::smatch match;
+    if (std::regex_match(path, match, PATTERN_OWNED_PATH)) {
+        const std::string& pkg = match[1];
+        if (!mp->IsUidForPackage(pkg, uid)) {
+            PLOG(WARNING) << "Invalid other package file access from " << pkg << "(: " << path;
+            return false;
+        }
+    }
+    return true;
+}
+
 static std::regex storage_emulated_regex("^\\/storage\\/emulated\\/([0-9]+)");
 static node* do_lookup(fuse_req_t req, fuse_ino_t parent, const char* name,
                        struct fuse_entry_param* e, int* error_code) {
     struct fuse* fuse = get_fuse(req);
-    const struct fuse_ctx* ctx = fuse_req_ctx(req);
-    node* parent_node = fuse->FromInode(parent, ctx);
+    node* parent_node = fuse->FromInode(parent);
     if (!parent_node) {
         *error_code = ENOENT;
         return nullptr;
     }
     string parent_path = parent_node->BuildPath();
+    if (!is_app_accessible_path(fuse->mp, parent_path, req->ctx.uid)) {
+        *error_code = ENOENT;
+        return nullptr;
+    }
+
     string child_path = parent_path + "/" + name;
 
     TRACE_NODE(parent_node);
@@ -504,8 +508,7 @@
 }
 
 static void do_forget(struct fuse* fuse, fuse_ino_t ino, uint64_t nlookup) {
-    // Always allow to forget so we put ctx as nullptr.
-    node* node = fuse->FromInode(ino, nullptr);
+    node* node = fuse->FromInode(ino);
     TRACE_NODE(node);
     if (node) {
         // This is a narrowing conversion from an unsigned 64bit to a 32bit value. For
@@ -516,6 +519,7 @@
 }
 
 static void pf_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) {
+    // Always allow to forget so no need to check is_app_accessible_path()
     ATRACE_CALL();
     node* node;
     struct fuse* fuse = get_fuse(req);
@@ -541,13 +545,16 @@
                        struct fuse_file_info* fi) {
     ATRACE_CALL();
     struct fuse* fuse = get_fuse(req);
-    const struct fuse_ctx* ctx = fuse_req_ctx(req);
-    node* node = fuse->FromInode(ino, ctx);
+    node* node = fuse->FromInode(ino);
     if (!node) {
         fuse_reply_err(req, ENOENT);
         return;
     }
     string path = node->BuildPath();
+    if (!is_app_accessible_path(fuse->mp, path, req->ctx.uid)) {
+        fuse_reply_err(req, ENOENT);
+        return;
+    }
     TRACE_NODE(node);
 
     struct stat s;
@@ -567,13 +574,16 @@
                        struct fuse_file_info* fi) {
     ATRACE_CALL();
     struct fuse* fuse = get_fuse(req);
-    const struct fuse_ctx* ctx = fuse_req_ctx(req);
-    node* node = fuse->FromInode(ino, ctx);
+    node* node = fuse->FromInode(ino);
     if (!node) {
         fuse_reply_err(req, ENOENT);
         return;
     }
     string path = node->BuildPath();
+    if (!is_app_accessible_path(fuse->mp, path, req->ctx.uid)) {
+        fuse_reply_err(req, ENOENT);
+        return;
+    }
     struct timespec times[2];
 
     TRACE_NODE(node);
@@ -626,11 +636,13 @@
 
 static void pf_canonical_path(fuse_req_t req, fuse_ino_t ino)
 {
-    node* node = get_fuse(req)->FromInode(ino, fuse_req_ctx(req));
+    struct fuse* fuse = get_fuse(req);
+    node* node = fuse->FromInode(ino);
+    string path = node ? node->BuildPath() : "";
 
-    if (node) {
+    if (node && is_app_accessible_path(fuse->mp, path, req->ctx.uid)) {
         // TODO(b/147482155): Check that uid has access to |path| and its contents
-        fuse_reply_canonical_path(req, node->BuildPath().c_str());
+        fuse_reply_canonical_path(req, path.c_str());
         return;
     }
     fuse_reply_err(req, ENOENT);
@@ -643,13 +655,16 @@
                      dev_t rdev) {
     ATRACE_CALL();
     struct fuse* fuse = get_fuse(req);
-    const struct fuse_ctx* ctx = fuse_req_ctx(req);
-    node* parent_node = fuse->FromInode(parent, ctx);
+    node* parent_node = fuse->FromInode(parent);
     if (!parent_node) {
         fuse_reply_err(req, ENOENT);
         return;
     }
     string parent_path = parent_node->BuildPath();
+    if (!is_app_accessible_path(fuse->mp, parent_path, req->ctx.uid)) {
+        fuse_reply_err(req, ENOENT);
+        return;
+    }
 
     TRACE_NODE(parent_node);
 
@@ -677,13 +692,17 @@
                      mode_t mode) {
     ATRACE_CALL();
     struct fuse* fuse = get_fuse(req);
-    const struct fuse_ctx* ctx = fuse_req_ctx(req);
-    node* parent_node = fuse->FromInode(parent, ctx);
+    node* parent_node = fuse->FromInode(parent);
     if (!parent_node) {
         fuse_reply_err(req, ENOENT);
         return;
     }
+    const struct fuse_ctx* ctx = fuse_req_ctx(req);
     const string parent_path = parent_node->BuildPath();
+    if (!is_app_accessible_path(fuse->mp, parent_path, ctx->uid)) {
+        fuse_reply_err(req, ENOENT);
+        return;
+    }
 
     TRACE_NODE(parent_node);
 
@@ -714,13 +733,17 @@
 static void pf_unlink(fuse_req_t req, fuse_ino_t parent, const char* name) {
     ATRACE_CALL();
     struct fuse* fuse = get_fuse(req);
-    const struct fuse_ctx* ctx = fuse_req_ctx(req);
-    node* parent_node = fuse->FromInode(parent, ctx);
+    node* parent_node = fuse->FromInode(parent);
     if (!parent_node) {
         fuse_reply_err(req, ENOENT);
         return;
     }
+    const struct fuse_ctx* ctx = fuse_req_ctx(req);
     const string parent_path = parent_node->BuildPath();
+    if (!is_app_accessible_path(fuse->mp, parent_path, ctx->uid)) {
+        fuse_reply_err(req, ENOENT);
+        return;
+    }
 
     TRACE_NODE(parent_node);
 
@@ -744,19 +767,21 @@
 static void pf_rmdir(fuse_req_t req, fuse_ino_t parent, const char* name) {
     ATRACE_CALL();
     struct fuse* fuse = get_fuse(req);
-    const struct fuse_ctx* ctx = fuse_req_ctx(req);
-    node* parent_node = fuse->FromInode(parent, ctx);
+    node* parent_node = fuse->FromInode(parent);
     if (!parent_node) {
         fuse_reply_err(req, ENOENT);
         return;
     }
     const string parent_path = parent_node->BuildPath();
-
+    if (!is_app_accessible_path(fuse->mp, parent_path, req->ctx.uid)) {
+        fuse_reply_err(req, ENOENT);
+        return;
+    }
     TRACE_NODE(parent_node);
 
     const string child_path = parent_path + "/" + name;
 
-    int status = fuse->mp->IsDeletingDirAllowed(child_path, ctx->uid);
+    int status = fuse->mp->IsDeletingDirAllowed(child_path, req->ctx.uid);
     if (status) {
         fuse_reply_err(req, status);
         return;
@@ -786,18 +811,25 @@
                      const char* new_name, unsigned int flags) {
     ATRACE_CALL();
     struct fuse* fuse = get_fuse(req);
-    const struct fuse_ctx* ctx = fuse_req_ctx(req);
 
     if (flags != 0) {
         return EINVAL;
     }
 
-    node* old_parent_node = fuse->FromInode(parent, ctx);
+    node* old_parent_node = fuse->FromInode(parent);
     if (!old_parent_node) return ENOENT;
+    const struct fuse_ctx* ctx = fuse_req_ctx(req);
     const string old_parent_path = old_parent_node->BuildPath();
-    node* new_parent_node = fuse->FromInode(new_parent, ctx);
+    if (!is_app_accessible_path(fuse->mp, old_parent_path, ctx->uid)) {
+        return ENOENT;
+    }
+
+    node* new_parent_node = fuse->FromInode(new_parent);
     if (!new_parent_node) return ENOENT;
     const string new_parent_path = new_parent_node->BuildPath();
+    if (!is_app_accessible_path(fuse->mp, new_parent_path, ctx->uid)) {
+        return ENOENT;
+    }
 
     if (!old_parent_node || !new_parent_node) {
         return ENOENT;
@@ -868,13 +900,17 @@
 static void pf_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
     ATRACE_CALL();
     struct fuse* fuse = get_fuse(req);
-    const struct fuse_ctx* ctx = fuse_req_ctx(req);
-    node* node = fuse->FromInode(ino, ctx);
+    node* node = fuse->FromInode(ino);
     if (!node) {
         fuse_reply_err(req, ENOENT);
         return;
     }
+    const struct fuse_ctx* ctx = fuse_req_ctx(req);
     const string path = node->BuildPath();
+    if (!is_app_accessible_path(fuse->mp, path, ctx->uid)) {
+        fuse_reply_err(req, ENOENT);
+        return;
+    }
 
     TRACE_NODE(node) << (is_requesting_write(fi->flags) ? "write" : "read");
 
@@ -1117,7 +1153,7 @@
     ATRACE_CALL();
     struct fuse* fuse = get_fuse(req);
 
-    node* node = fuse->FromInode(ino, fuse_req_ctx(req));
+    node* node = fuse->FromInode(ino);
     handle* h = reinterpret_cast<handle*>(fi->fh);
     TRACE_NODE(node);
 
@@ -1162,13 +1198,17 @@
                        struct fuse_file_info* fi) {
     ATRACE_CALL();
     struct fuse* fuse = get_fuse(req);
-    const struct fuse_ctx* ctx = fuse_req_ctx(req);
-    node* node = fuse->FromInode(ino, ctx);
+    node* node = fuse->FromInode(ino);
     if (!node) {
         fuse_reply_err(req, ENOENT);
         return;
     }
+    const struct fuse_ctx* ctx = fuse_req_ctx(req);
     const string path = node->BuildPath();
+    if (!is_app_accessible_path(fuse->mp, path, ctx->uid)) {
+        fuse_reply_err(req, ENOENT);
+        return;
+    }
 
     TRACE_NODE(node);
 
@@ -1209,12 +1249,16 @@
     struct fuse_entry_param e;
     size_t entry_size = 0;
 
-    node* node = fuse->FromInode(ino, fuse_req_ctx(req));
+    node* node = fuse->FromInode(ino);
     if (!node) {
         fuse_reply_err(req, ENOENT);
         return;
     }
     const string path = node->BuildPath();
+    if (!is_app_accessible_path(fuse->mp, path, req->ctx.uid)) {
+        fuse_reply_err(req, ENOENT);
+        return;
+    }
 
     TRACE_NODE(node);
     // Get all directory entries from MediaProvider on first readdir() call of
@@ -1301,7 +1345,8 @@
     ATRACE_CALL();
     struct fuse* fuse = get_fuse(req);
 
-    node* node = fuse->FromInode(ino, fuse_req_ctx(req));
+    node* node = fuse->FromInode(ino);
+
     dirhandle* h = reinterpret_cast<dirhandle*>(fi->fh);
     TRACE_NODE(node);
     if (node) {
@@ -1347,14 +1392,17 @@
 static void pf_access(fuse_req_t req, fuse_ino_t ino, int mask) {
     ATRACE_CALL();
     struct fuse* fuse = get_fuse(req);
-    const struct fuse_ctx* ctx = fuse_req_ctx(req);
 
-    node* node = fuse->FromInode(ino, ctx);
+    node* node = fuse->FromInode(ino);
     if (!node) {
         fuse_reply_err(req, ENOENT);
         return;
     }
     const string path = node->BuildPath();
+    if (!is_app_accessible_path(fuse->mp, path, req->ctx.uid)) {
+        fuse_reply_err(req, ENOENT);
+        return;
+    }
     TRACE_NODE(node);
 
     // exists() checks are always allowed.
@@ -1374,7 +1422,7 @@
     int status = 0;
     bool is_directory = S_ISDIR(stat.st_mode);
     if (is_directory) {
-        status = fuse->mp->IsOpendirAllowed(path, ctx->uid);
+        status = fuse->mp->IsOpendirAllowed(path, req->ctx.uid);
     } else {
         if (mask & X_OK) {
             // Fuse is mounted with MS_NOEXEC.
@@ -1383,7 +1431,7 @@
         }
 
         bool for_write = mask & W_OK;
-        status = fuse->mp->IsOpenAllowed(path, ctx->uid, for_write);
+        status = fuse->mp->IsOpenAllowed(path, req->ctx.uid, for_write);
     }
 
     fuse_reply_err(req, status);
@@ -1396,19 +1444,22 @@
                       struct fuse_file_info* fi) {
     ATRACE_CALL();
     struct fuse* fuse = get_fuse(req);
-    const struct fuse_ctx* ctx = fuse_req_ctx(req);
-    node* parent_node = fuse->FromInode(parent, ctx);
+    node* parent_node = fuse->FromInode(parent);
     if (!parent_node) {
         fuse_reply_err(req, ENOENT);
         return;
     }
     const string parent_path = parent_node->BuildPath();
+    if (!is_app_accessible_path(fuse->mp, parent_path, req->ctx.uid)) {
+        fuse_reply_err(req, ENOENT);
+        return;
+    }
 
     TRACE_NODE(parent_node);
 
     const string child_path = parent_path + "/" + name;
 
-    int mp_return_code = fuse->mp->InsertFile(child_path.c_str(), ctx->uid);
+    int mp_return_code = fuse->mp->InsertFile(child_path.c_str(), req->ctx.uid);
     if (mp_return_code) {
         fuse_reply_err(req, mp_return_code);
         return;
@@ -1428,7 +1479,7 @@
         int error_code = errno;
         // We've already inserted the file into the MP database before the
         // failed open(), so that needs to be rolled back here.
-        fuse->mp->DeleteFile(child_path.c_str(), ctx->uid);
+        fuse->mp->DeleteFile(child_path.c_str(), req->ctx.uid);
         fuse_reply_err(req, error_code);
         return;
     }
diff --git a/src/com/android/providers/media/util/FileUtils.java b/src/com/android/providers/media/util/FileUtils.java
index e711d0c..ca0c595 100644
--- a/src/com/android/providers/media/util/FileUtils.java
+++ b/src/com/android/providers/media/util/FileUtils.java
@@ -860,12 +860,6 @@
     }
 
     /**
-     * Regex that matches any valid path in external storage,
-     * and captures the top-level directory as the first group.
-     */
-    private static final Pattern PATTERN_TOP_LEVEL_DIR = Pattern.compile(
-            "(?i)^/storage/[^/]+/[0-9]+/([^/]+)(/.*)?");
-    /**
      * Regex that matches paths in all well-known package-specific directories,
      * and which captures the package name as the first group.
      */
@@ -979,11 +973,12 @@
      */
     @Nullable
     public static String extractTopLevelDir(String path) {
-        Matcher m = PATTERN_TOP_LEVEL_DIR.matcher(path);
-        if (m.matches()) {
-            return m.group(1);
+        final String relativePath = extractRelativePath(path);
+        if (relativePath == null) {
+            return null;
         }
-        return null;
+        final String[] relativePathSegments = relativePath.split("/");
+        return relativePathSegments.length > 0 ? relativePathSegments[0] : null;
     }
 
     /**
diff --git a/tests/src/com/android/providers/media/util/FileUtilsTest.java b/tests/src/com/android/providers/media/util/FileUtilsTest.java
index da8ddce..451f774 100644
--- a/tests/src/com/android/providers/media/util/FileUtilsTest.java
+++ b/tests/src/com/android/providers/media/util/FileUtilsTest.java
@@ -41,6 +41,7 @@
 import static com.android.providers.media.util.FileUtils.extractFileExtension;
 import static com.android.providers.media.util.FileUtils.extractFileName;
 import static com.android.providers.media.util.FileUtils.extractRelativePath;
+import static com.android.providers.media.util.FileUtils.extractTopLevelDir;
 import static com.android.providers.media.util.FileUtils.extractVolumeName;
 import static com.android.providers.media.util.FileUtils.extractVolumePath;
 import static com.android.providers.media.util.FileUtils.translateModeAccessToPosix;
@@ -454,6 +455,21 @@
     }
 
     @Test
+    public void testExtractTopLevelDir() throws Exception {
+        for (String prefix : new String[] {
+                "/storage/emulated/0/",
+                "/storage/0000-0000/"
+        }) {
+            assertEquals(null,
+                    extractTopLevelDir(prefix + "foo.jpg"));
+            assertEquals("DCIM",
+                    extractTopLevelDir(prefix + "DCIM/foo.jpg"));
+            assertEquals("DCIM",
+                    extractTopLevelDir(prefix + "DCIM/My Vacation/foo.jpg"));
+        }
+    }
+
+    @Test
     public void testExtractDisplayName() throws Exception {
         for (String probe : new String[] {
                 "foo.bar.baz",