Merge "Add api tracking files for MediaProvider" into rvc-dev
diff --git a/gen_strings.py b/gen_strings.py
old mode 100644
new mode 100755
index ee06841..99bba8a
--- a/gen_strings.py
+++ b/gen_strings.py
@@ -29,7 +29,7 @@
for verb in verbs:
verblabel = verb
if verb == "write":
- verblabel = "change"
+ verblabel = "modify"
verblabelcaps = verblabel[0].upper() + verblabel[1:]
if verb == "trash":
@@ -45,15 +45,8 @@
print Template('''
<!-- Dialog title asking if user will allow $verb permission to the $data item displayed below this string. [CHAR LIMIT=128] -->
<plurals name="permission_${verb}_${data}">
- <item quantity="one">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> move this $datalabel to trash?</item>
- <item quantity="other">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> move <xliff:g id="count" example="42">^2</xliff:g> ${datalabel}s to trash?</item>
-</plurals>
-''').substitute(vars()).strip("\n")
- print Template('''
-<!-- Dialog body text explaining that this $data item will be permanently deleted after the shown duration. [CHAR LIMIT=128] -->
-<plurals name="permission_${verb}_${data}_info">
- <item quantity="one">This $datalabel will be permanently deleted after <xliff:g id="duration" example="42">^3</xliff:g> days</item>
- <item quantity="other">These ${datalabel}s will be permanently deleted after <xliff:g id="duration" example="42">^3</xliff:g> days</item>
+ <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this $datalabel to trash?</item>
+ <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> ${datalabel}s to trash?</item>
</plurals>
''').substitute(vars()).strip("\n")
@@ -61,8 +54,8 @@
print Template('''
<!-- Dialog title asking if user will allow $verb permission to the $data item displayed below this string. [CHAR LIMIT=128] -->
<plurals name="permission_${verb}_${data}">
- <item quantity="one">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> move this $datalabel out of trash?</item>
- <item quantity="other">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> move <xliff:g id="count" example="42">^2</xliff:g> ${datalabel}s out of trash?</item>
+ <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this $datalabel out of trash?</item>
+ <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> ${datalabel}s out of trash?</item>
</plurals>
''').substitute(vars()).strip("\n")
@@ -70,19 +63,11 @@
print Template('''
<!-- Dialog title asking if user will allow $verb permission to the $data item displayed below this string. [CHAR LIMIT=128] -->
<plurals name="permission_${verb}_${data}">
- <item quantity="one">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> $verblabel this $datalabel?</item>
- <item quantity="other">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> $verblabel <xliff:g id="count" example="42">^2</xliff:g> ${datalabel}s?</item>
+ <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to $verblabel this $datalabel?</item>
+ <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to $verblabel <xliff:g id="count" example="42">^2</xliff:g> ${datalabel}s?</item>
</plurals>
''').substitute(vars()).strip("\n")
-
- print Template('''
-<!-- Positive dialog button confirming that $verb permission should be granted. [CHAR LIMIT=32] -->
-<string name="permission_${verb}_grant">${verblabelcaps}</string>
-<!-- Negative dialog button confirming that $verb permission should not be granted. [CHAR LIMIT=32] -->
-<string name="permission_${verb}_deny">Cancel</string>
-''').substitute(vars()).strip("\n")
-
print '''
<!-- ========================= END AUTO-GENERATED BY gen_strings.py ========================= -->
'''
diff --git a/jni/FuseDaemon.cpp b/jni/FuseDaemon.cpp
index a014489..d014a8e 100644
--- a/jni/FuseDaemon.cpp
+++ b/jni/FuseDaemon.cpp
@@ -816,7 +816,7 @@
// FUSE after that write may be served from cache
bool direct_io = ri->isRedactionNeeded() || is_file_locked(fd, path);
- handle* h = new handle(path, fd, ri, /*owner_uid*/ -1, !direct_io);
+ handle* h = new handle(path, fd, ri, !direct_io);
node->AddHandle(h);
return h;
}
@@ -1080,11 +1080,6 @@
fuse->fadviser.Close(h->fd);
if (node) {
- // TODO(b/145737191) Legacy apps don't expect FuseDaemon to update database.
- // Inserting/deleting the database entry might break app's functionality.
- // if (h->owner_uid != -1) {
- // fuse->mp->ScanFile(h->path, h->owner_uid);
- // }
node->DestroyHandle(h);
}
diff --git a/jni/MediaProviderWrapper.cpp b/jni/MediaProviderWrapper.cpp
index 0cdfd87..77e1470 100644
--- a/jni/MediaProviderWrapper.cpp
+++ b/jni/MediaProviderWrapper.cpp
@@ -116,9 +116,9 @@
}
void scanFileInternal(JNIEnv* env, jobject media_provider_object, jmethodID mid_scan_file,
- const string& path, uid_t uid) {
+ const string& path) {
ScopedLocalRef<jstring> j_path(env, env->NewStringUTF(path.c_str()));
- env->CallVoidMethod(media_provider_object, mid_scan_file, j_path.get(), uid);
+ env->CallVoidMethod(media_provider_object, mid_scan_file, j_path.get());
CheckForJniException(env);
}
@@ -241,7 +241,7 @@
mid_delete_file_ = CacheMethod(env, "deleteFile", "(Ljava/lang/String;I)I", /*is_static*/ false);
mid_is_open_allowed_ = CacheMethod(env, "isOpenAllowed", "(Ljava/lang/String;IZ)I",
/*is_static*/ false);
- mid_scan_file_ = CacheMethod(env, "scanFile", "(Ljava/lang/String;I)V",
+ mid_scan_file_ = CacheMethod(env, "scanFile", "(Ljava/lang/String;)V",
/*is_static*/ false);
mid_is_mkdir_or_rmdir_allowed_ = CacheMethod(env, "isDirectoryCreationOrDeletionAllowed",
"(Ljava/lang/String;IZ)I", /*is_static*/ false);
@@ -290,7 +290,7 @@
int MediaProviderWrapper::DeleteFile(const string& path, uid_t uid) {
if (shouldBypassMediaProvider(uid)) {
int res = unlink(path.c_str());
- ScanFile(path, uid);
+ ScanFile(path);
return res;
}
@@ -308,15 +308,9 @@
for_write);
}
-void MediaProviderWrapper::ScanFile(const string& path, uid_t uid) {
- if (uid == 0) {
- // uid = 0 is kernel or adb shell running as root and doesn't have a package associated with
- // it. This uid should get global access hence use MediaProvider's uid.
- uid = getuid();
- }
-
+void MediaProviderWrapper::ScanFile(const string& path) {
JNIEnv* env = MaybeAttachCurrentThread();
- scanFileInternal(env, media_provider_object_, mid_scan_file_, path, uid);
+ scanFileInternal(env, media_provider_object_, mid_scan_file_, path);
}
int MediaProviderWrapper::IsCreatingDirAllowed(const string& path, uid_t uid) {
diff --git a/jni/MediaProviderWrapper.h b/jni/MediaProviderWrapper.h
index 22c5b3e..016696f 100644
--- a/jni/MediaProviderWrapper.h
+++ b/jni/MediaProviderWrapper.h
@@ -105,9 +105,8 @@
* MediaProvider database.
*
* @param path the path of the file to be scanned
- * @param uid UID of the app that owns the file on the given path
*/
- void ScanFile(const std::string& path, uid_t uid);
+ void ScanFile(const std::string& path);
/**
* Determines if the given UID is allowed to create a directory with the given path.
diff --git a/jni/node-inl.h b/jni/node-inl.h
index 5098316..a146d20 100644
--- a/jni/node-inl.h
+++ b/jni/node-inl.h
@@ -36,16 +36,14 @@
namespace fuse {
struct handle {
- explicit handle(const std::string& path, int fd, const RedactionInfo* ri, uid_t owner_uid,
- bool cached)
- : path(path), fd(fd), ri(ri), owner_uid(owner_uid), cached(cached) {
+ explicit handle(const std::string& path, int fd, const RedactionInfo* ri, bool cached)
+ : path(path), fd(fd), ri(ri), cached(cached) {
CHECK(ri != nullptr);
}
const std::string path;
const int fd;
const std::unique_ptr<const RedactionInfo> ri;
- const uid_t owner_uid;
const bool cached;
~handle() { close(fd); }
diff --git a/jni/node_test.cpp b/jni/node_test.cpp
index 423af3d..fd56f49 100644
--- a/jni/node_test.cpp
+++ b/jni/node_test.cpp
@@ -213,8 +213,7 @@
TEST_F(NodeTest, AddDestroyHandle) {
unique_node_ptr node = CreateNode(nullptr, "/path");
- handle* h = new handle("/path", -1, new mediaprovider::fuse::RedactionInfo, getuid(),
- true /* cached */);
+ handle* h = new handle("/path", -1, new mediaprovider::fuse::RedactionInfo, true /* cached */);
node->AddHandle(h);
ASSERT_TRUE(node->HasCachedHandle());
@@ -225,7 +224,7 @@
// the node in question.
EXPECT_DEATH(node->DestroyHandle(h), "");
EXPECT_DEATH(node->DestroyHandle(nullptr), "");
- std::unique_ptr<handle> h2(new handle("/path2", -1, new mediaprovider::fuse::RedactionInfo,
- getuid(), true /* cached */));
+ std::unique_ptr<handle> h2(
+ new handle("/path2", -1, new mediaprovider::fuse::RedactionInfo, true /* cached */));
EXPECT_DEATH(node->DestroyHandle(h2.get()), "");
}
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 04cab66..ef51112 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -51,6 +51,7 @@
<!-- Text placed over a visual thumbnail indicating that there are more items beyond the number currently displayed on the screen. [CHAR LIMIT=6] -->
<plurals name="permission_more_thumb">
+ <item quantity="one">+<xliff:g id="count" example="1">^1</xliff:g></item>
<item quantity="other">+<xliff:g id="count" example="42">^1</xliff:g></item>
</plurals>
@@ -61,10 +62,10 @@
</plurals>
<!-- Cache clearing permission dialog warning title. [CHAR LIMIT=NONE] -->
- <string name="cache_clearing_dialog_title">Clear app cache with <xliff:g id="app_seeking_permission" example="File manager">%s</xliff:g>?</string>
+ <string name="cache_clearing_dialog_title">Clear temporary app files?</string>
<!-- Cache clearing permission dialog warning text. [CHAR LIMIT=NONE] -->
- <string name="cache_clearing_dialog_text"><xliff:g id="app_seeking_permission" example="File manager">%s</xliff:g> would like to clear some temporary files. Accepting may increase battery or data use.</string>
+ <string name="cache_clearing_dialog_text"><xliff:g id="app_seeking_permission" example="File manager">%s</xliff:g> would like to clear some temporary files. This may result in an increased usage of battery or cellular data.</string>
<!-- Allow dialog action text. [CHAR LIMIT=30] -->
<string name="allow">Allow</string>
@@ -78,129 +79,93 @@
<!-- Dialog title asking if user will allow write permission to the audio item displayed below this string. [CHAR LIMIT=128] -->
<plurals name="permission_write_audio">
- <item quantity="one">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> change this audio file?</item>
- <item quantity="other">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> change <xliff:g id="count" example="42">^2</xliff:g> audio files?</item>
+ <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify this audio file?</item>
+ <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify <xliff:g id="count" example="42">^2</xliff:g> audio files?</item>
</plurals>
<!-- Dialog title asking if user will allow write permission to the video item displayed below this string. [CHAR LIMIT=128] -->
<plurals name="permission_write_video">
- <item quantity="one">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> change this video?</item>
- <item quantity="other">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> change <xliff:g id="count" example="42">^2</xliff:g> videos?</item>
+ <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify this video?</item>
+ <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify <xliff:g id="count" example="42">^2</xliff:g> videos?</item>
</plurals>
<!-- Dialog title asking if user will allow write permission to the image item displayed below this string. [CHAR LIMIT=128] -->
<plurals name="permission_write_image">
- <item quantity="one">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> change this photo?</item>
- <item quantity="other">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> change <xliff:g id="count" example="42">^2</xliff:g> photos?</item>
+ <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify this photo?</item>
+ <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify <xliff:g id="count" example="42">^2</xliff:g> photos?</item>
</plurals>
<!-- Dialog title asking if user will allow write permission to the generic item displayed below this string. [CHAR LIMIT=128] -->
<plurals name="permission_write_generic">
- <item quantity="one">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> change this item?</item>
- <item quantity="other">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> change <xliff:g id="count" example="42">^2</xliff:g> items?</item>
+ <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify this item?</item>
+ <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to modify <xliff:g id="count" example="42">^2</xliff:g> items?</item>
</plurals>
- <!-- Positive dialog button confirming that write permission should be granted. [CHAR LIMIT=32] -->
- <string name="permission_write_grant">Change</string>
- <!-- Negative dialog button confirming that write permission should not be granted. [CHAR LIMIT=32] -->
- <string name="permission_write_deny">Cancel</string>
<!-- ========================= TRASH STRINGS ========================= -->
<!-- Dialog title asking if user will allow trash permission to the audio item displayed below this string. [CHAR LIMIT=128] -->
<plurals name="permission_trash_audio">
- <item quantity="one">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> move this audio file to trash?</item>
- <item quantity="other">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> move <xliff:g id="count" example="42">^2</xliff:g> audio files to trash?</item>
- </plurals>
- <!-- Dialog body text explaining that this audio item will be permanently deleted after the shown duration. [CHAR LIMIT=128] -->
- <plurals name="permission_trash_audio_info">
- <item quantity="one">This audio file will be permanently deleted after <xliff:g id="duration" example="42">^3</xliff:g> days</item>
- <item quantity="other">These audio files will be permanently deleted after <xliff:g id="duration" example="42">^3</xliff:g> days</item>
+ <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this audio file to trash?</item>
+ <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> audio files to trash?</item>
</plurals>
<!-- Dialog title asking if user will allow trash permission to the video item displayed below this string. [CHAR LIMIT=128] -->
<plurals name="permission_trash_video">
- <item quantity="one">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> move this video to trash?</item>
- <item quantity="other">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> move <xliff:g id="count" example="42">^2</xliff:g> videos to trash?</item>
- </plurals>
- <!-- Dialog body text explaining that this video item will be permanently deleted after the shown duration. [CHAR LIMIT=128] -->
- <plurals name="permission_trash_video_info">
- <item quantity="one">This video will be permanently deleted after <xliff:g id="duration" example="42">^3</xliff:g> days</item>
- <item quantity="other">These videos will be permanently deleted after <xliff:g id="duration" example="42">^3</xliff:g> days</item>
+ <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this video to trash?</item>
+ <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> videos to trash?</item>
</plurals>
<!-- Dialog title asking if user will allow trash permission to the image item displayed below this string. [CHAR LIMIT=128] -->
<plurals name="permission_trash_image">
- <item quantity="one">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> move this photo to trash?</item>
- <item quantity="other">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> move <xliff:g id="count" example="42">^2</xliff:g> photos to trash?</item>
- </plurals>
- <!-- Dialog body text explaining that this image item will be permanently deleted after the shown duration. [CHAR LIMIT=128] -->
- <plurals name="permission_trash_image_info">
- <item quantity="one">This photo will be permanently deleted after <xliff:g id="duration" example="42">^3</xliff:g> days</item>
- <item quantity="other">These photos will be permanently deleted after <xliff:g id="duration" example="42">^3</xliff:g> days</item>
+ <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this photo to trash?</item>
+ <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> photos to trash?</item>
</plurals>
<!-- Dialog title asking if user will allow trash permission to the generic item displayed below this string. [CHAR LIMIT=128] -->
<plurals name="permission_trash_generic">
- <item quantity="one">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> move this item to trash?</item>
- <item quantity="other">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> move <xliff:g id="count" example="42">^2</xliff:g> items to trash?</item>
+ <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this item to trash?</item>
+ <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> items to trash?</item>
</plurals>
- <!-- Dialog body text explaining that this generic item will be permanently deleted after the shown duration. [CHAR LIMIT=128] -->
- <plurals name="permission_trash_generic_info">
- <item quantity="one">This item will be permanently deleted after <xliff:g id="duration" example="42">^3</xliff:g> days</item>
- <item quantity="other">These items will be permanently deleted after <xliff:g id="duration" example="42">^3</xliff:g> days</item>
- </plurals>
- <!-- Positive dialog button confirming that trash permission should be granted. [CHAR LIMIT=32] -->
- <string name="permission_trash_grant">Move to trash</string>
- <!-- Negative dialog button confirming that trash permission should not be granted. [CHAR LIMIT=32] -->
- <string name="permission_trash_deny">Cancel</string>
<!-- ========================= UNTRASH STRINGS ========================= -->
<!-- Dialog title asking if user will allow untrash permission to the audio item displayed below this string. [CHAR LIMIT=128] -->
<plurals name="permission_untrash_audio">
- <item quantity="one">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> move this audio file out of trash?</item>
- <item quantity="other">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> move <xliff:g id="count" example="42">^2</xliff:g> audio files out of trash?</item>
+ <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this audio file out of trash?</item>
+ <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> audio files out of trash?</item>
</plurals>
<!-- Dialog title asking if user will allow untrash permission to the video item displayed below this string. [CHAR LIMIT=128] -->
<plurals name="permission_untrash_video">
- <item quantity="one">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> move this video out of trash?</item>
- <item quantity="other">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> move <xliff:g id="count" example="42">^2</xliff:g> videos out of trash?</item>
+ <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this video out of trash?</item>
+ <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> videos out of trash?</item>
</plurals>
<!-- Dialog title asking if user will allow untrash permission to the image item displayed below this string. [CHAR LIMIT=128] -->
<plurals name="permission_untrash_image">
- <item quantity="one">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> move this photo out of trash?</item>
- <item quantity="other">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> move <xliff:g id="count" example="42">^2</xliff:g> photos out of trash?</item>
+ <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this photo out of trash?</item>
+ <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> photos out of trash?</item>
</plurals>
<!-- Dialog title asking if user will allow untrash permission to the generic item displayed below this string. [CHAR LIMIT=128] -->
<plurals name="permission_untrash_generic">
- <item quantity="one">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> move this item out of trash?</item>
- <item quantity="other">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> move <xliff:g id="count" example="42">^2</xliff:g> items out of trash?</item>
+ <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move this item out of trash?</item>
+ <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to move <xliff:g id="count" example="42">^2</xliff:g> items out of trash?</item>
</plurals>
- <!-- Positive dialog button confirming that untrash permission should be granted. [CHAR LIMIT=32] -->
- <string name="permission_untrash_grant">Move out of trash</string>
- <!-- Negative dialog button confirming that untrash permission should not be granted. [CHAR LIMIT=32] -->
- <string name="permission_untrash_deny">Cancel</string>
<!-- ========================= DELETE STRINGS ========================= -->
<!-- Dialog title asking if user will allow delete permission to the audio item displayed below this string. [CHAR LIMIT=128] -->
<plurals name="permission_delete_audio">
- <item quantity="one">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> delete this audio file?</item>
- <item quantity="other">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> delete <xliff:g id="count" example="42">^2</xliff:g> audio files?</item>
+ <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete this audio file?</item>
+ <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete <xliff:g id="count" example="42">^2</xliff:g> audio files?</item>
</plurals>
<!-- Dialog title asking if user will allow delete permission to the video item displayed below this string. [CHAR LIMIT=128] -->
<plurals name="permission_delete_video">
- <item quantity="one">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> delete this video?</item>
- <item quantity="other">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> delete <xliff:g id="count" example="42">^2</xliff:g> videos?</item>
+ <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete this video?</item>
+ <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete <xliff:g id="count" example="42">^2</xliff:g> videos?</item>
</plurals>
<!-- Dialog title asking if user will allow delete permission to the image item displayed below this string. [CHAR LIMIT=128] -->
<plurals name="permission_delete_image">
- <item quantity="one">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> delete this photo?</item>
- <item quantity="other">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> delete <xliff:g id="count" example="42">^2</xliff:g> photos?</item>
+ <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete this photo?</item>
+ <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete <xliff:g id="count" example="42">^2</xliff:g> photos?</item>
</plurals>
<!-- Dialog title asking if user will allow delete permission to the generic item displayed below this string. [CHAR LIMIT=128] -->
<plurals name="permission_delete_generic">
- <item quantity="one">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> delete this item?</item>
- <item quantity="other">Let <xliff:g id="app_name" example="Gmail">^1</xliff:g> delete <xliff:g id="count" example="42">^2</xliff:g> items?</item>
+ <item quantity="one">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete this item?</item>
+ <item quantity="other">Allow <xliff:g id="app_name" example="Gmail">^1</xliff:g> to delete <xliff:g id="count" example="42">^2</xliff:g> items?</item>
</plurals>
- <!-- Positive dialog button confirming that delete permission should be granted. [CHAR LIMIT=32] -->
- <string name="permission_delete_grant">Delete</string>
- <!-- Negative dialog button confirming that delete permission should not be granted. [CHAR LIMIT=32] -->
- <string name="permission_delete_deny">Cancel</string>
<!-- ========================= END AUTO-GENERATED BY gen_strings.py ========================= -->
diff --git a/src/com/android/providers/media/LocalCallingIdentity.java b/src/com/android/providers/media/LocalCallingIdentity.java
index ef6c27f..f767dcd 100644
--- a/src/com/android/providers/media/LocalCallingIdentity.java
+++ b/src/com/android/providers/media/LocalCallingIdentity.java
@@ -37,8 +37,6 @@
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.compat.CompatChanges;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.Disabled;
import android.content.ContentProvider;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -69,39 +67,13 @@
}
/**
- * Scoped Storage is on by default. However, it is not strictly enforced and there are multiple
- * ways to opt out of scoped storage:
- * <ul>
- * <li>Target Sdk < Q</li>
- * <li>Target Sdk = Q and has `requestLegacyExternalStorage` set in AndroidManifest.xml</li>
- * <li>Target Sdk > Q: Upgrading from an app that was opted out of scoped storage and has
- * `preserveLegacyExternalStorage` set in AndroidManifest.xml</li>
- * </ul>
- * This flag is enabled for all apps by default as Scoped Storage is enabled by default.
- * Developers can disable this flag to opt out of Scoped Storage and have legacy storage
- * workflow.
- *
- * Note: {@code FORCE_ENABLE_SCOPED_STORAGE} should also be disabled for apps to opt out of
- * scoped storage.
- * See https://developer.android.com/training/data-storage#scoped-storage for more information.
+ * See definition in {@link android.os.Environment}
*/
- @ChangeId
private static final long DEFAULT_SCOPED_STORAGE = 149924527L;
/**
- * Setting this flag strictly enforces Scoped Storage regardless of:
- * <ul>
- * <li>The value of Target Sdk</li>
- * <li>The value of `requestLegacyExternalStorage` in AndroidManifest.xml</li>
- * <li>The value of `preserveLegacyExternalStorage` in AndroidManifest.xml</li>
- * </ul>
- *
- * Note: {@code DEFAULT_SCOPED_STORAGE} should also be enabled for apps to be enforced into
- * scoped storage.
- * See https://developer.android.com/training/data-storage#scoped-storage for more information.
+ * See definition in {@link android.os.Environment}
*/
- @ChangeId
- @Disabled
private static final long FORCE_ENABLE_SCOPED_STORAGE = 132649864L;
public static LocalCallingIdentity fromBinder(Context context, ContentProvider provider) {
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index 67b7d86..7507c0b 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -64,7 +64,10 @@
import static com.android.providers.media.util.FileUtils.extractRelativePathForDirectory;
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.getAbsoluteSanitizedPath;
import static com.android.providers.media.util.FileUtils.isDownload;
+import static com.android.providers.media.util.FileUtils.sanitizeDisplayName;
+import static com.android.providers.media.util.FileUtils.sanitizePath;
import static com.android.providers.media.util.Logging.LOGV;
import static com.android.providers.media.util.Logging.TAG;
import static com.android.providers.media.util.PermissionUtils.checkPermissionManageExternalStorage;
@@ -256,6 +259,8 @@
private static final String INCLUDED_DEFAULT_DIRECTORIES =
"android:included-default-directories";
+ private static final int UNKNOWN_UID = -1;
+
/**
* Set of {@link Cursor} columns that refer to raw filesystem paths.
*/
@@ -343,8 +348,8 @@
/**
* Map from UID to cached {@link LocalCallingIdentity}. Values are only
- * maintained in this map while the UID is actively working with a
- * performance-critical component, such as camera.
+ * maintained in this map until there's any change in the appops needed or packages
+ * used in the {@link LocalCallingIdentity}.
*/
@GuardedBy("mCachedCallingIdentity")
private final SparseArray<LocalCallingIdentity> mCachedCallingIdentity = new SparseArray<>();
@@ -362,24 +367,28 @@
}
};
- /**
- * Map from UID to cached {@link LocalCallingIdentity}. Values are only
- * maintained in this map until there's any change in the appops needed or packages
- * used in the {@link LocalCallingIdentity}.
- */
- @GuardedBy("mCachedCallingIdentityForFuse")
- private final SparseArray<LocalCallingIdentity> mCachedCallingIdentityForFuse =
- new SparseArray<>();
-
private OnOpChangedListener mModeListener =
- (op, packageName) -> invalidateLocalCallingIdentityCache(packageName, "op " + op);
+ (op, packageName) -> invalidateLocalCallingIdentityCache(UNKNOWN_UID, packageName,
+ "op " + op);
private LocalCallingIdentity getCachedCallingIdentityForFuse(int uid) {
- synchronized (mCachedCallingIdentityForFuse) {
- LocalCallingIdentity ident = mCachedCallingIdentityForFuse.get(uid);
+ synchronized (mCachedCallingIdentity) {
+ LocalCallingIdentity ident = mCachedCallingIdentity.get(uid);
if (ident == null) {
- ident = LocalCallingIdentity.fromExternal(getContext(), uid);
- mCachedCallingIdentityForFuse.put(uid, ident);
+ ident = LocalCallingIdentity.fromExternal(getContext(), uid);
+ mCachedCallingIdentity.put(uid, ident);
+ }
+ return ident;
+ }
+ }
+
+ private LocalCallingIdentity getCachedCallingIdentityForBinder() {
+ int uid = Binder.getCallingUid();
+ synchronized (mCachedCallingIdentity) {
+ LocalCallingIdentity ident = mCachedCallingIdentity.get(uid);
+ if (ident == null) {
+ ident = LocalCallingIdentity.fromBinder(getContext(), this);
+ mCachedCallingIdentity.put(uid, ident);
}
return ident;
}
@@ -391,14 +400,7 @@
* call is finished.
*/
private final ThreadLocal<LocalCallingIdentity> mCallingIdentity = ThreadLocal
- .withInitial(() -> {
- synchronized (mCachedCallingIdentity) {
- final LocalCallingIdentity cached = mCachedCallingIdentity
- .get(Binder.getCallingUid());
- return (cached != null) ? cached
- : LocalCallingIdentity.fromBinder(getContext(), this);
- }
- });
+ .withInitial(this::getCachedCallingIdentityForBinder);
/**
* We simply propagate the UID that is being tracked by
@@ -480,7 +482,9 @@
Uri uri = intent.getData();
String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
if (pkg != null) {
- invalidateLocalCallingIdentityCache(pkg, "package " + intent.getAction());
+ int uid = intent.getIntExtra(Intent.EXTRA_UID, UNKNOWN_UID);
+ invalidateLocalCallingIdentityCache(uid, pkg,
+ "package " + intent.getAction());
} else {
Log.w(TAG, "Failed to retrieve package from intent: " + intent.getAction());
}
@@ -489,15 +493,21 @@
}
};
- private void invalidateLocalCallingIdentityCache(String packageName, String reason) {
- synchronized (mCachedCallingIdentityForFuse) {
- try {
- Log.i(TAG, "Invalidating LocalCallingIdentity cache for package " + packageName
- + ". Reason: " + reason);
- mCachedCallingIdentityForFuse.remove(
- getContext().getPackageManager().getPackageUid(packageName, 0));
- } catch (NameNotFoundException ignored) {
+ private void invalidateLocalCallingIdentityCache(int uid, String packageName, String reason) {
+ try {
+ if (uid == UNKNOWN_UID) {
+ uid = getContext().getPackageManager().getPackageUid(packageName, 0);
}
+ } catch (NameNotFoundException e) {
+ Log.wtf(TAG, "Failed to invalidate LocalCallingIdentity cache for package: "
+ + packageName, e);
+ return;
+ }
+
+ synchronized (mCachedCallingIdentity) {
+ Log.i(TAG, "Invalidating LocalCallingIdentity cache for package " + packageName
+ + ". Uid: " + uid + ". Reason: " + reason);
+ mCachedCallingIdentity.remove(uid);
}
}
@@ -816,7 +826,6 @@
AppOpsManager.OPSTR_CAMERA
}, context.getMainExecutor(), mActiveListener);
-
mAppOpsManager.startWatchingMode(AppOpsManager.OPSTR_READ_EXTERNAL_STORAGE,
null /* all packages */, mModeListener);
mAppOpsManager.startWatchingMode(AppOpsManager.OPSTR_WRITE_EXTERNAL_STORAGE,
@@ -962,6 +971,11 @@
mDirectoryCache.clear();
}
+ // Purge any per uid caches
+ synchronized (mCachedCallingIdentity) {
+ mCachedCallingIdentity.clear();
+ }
+
final long durationMillis = (SystemClock.elapsedRealtime() - startTime);
Metrics.logIdleMaintenance(MediaStore.VOLUME_EXTERNAL, helper.getItemCount(),
durationMillis, staleThumbnails, expiredMedia);
@@ -997,15 +1011,12 @@
/**
* Makes MediaScanner scan the given file.
* @param file path of the file to be scanned
- * @param uid UID of the app that owns the file on the given path. If the file is scanned
- * on create, this UID will be used for updating owner package.
*
* Called from JNI in jni/MediaProviderWrapper.cpp
*/
@Keep
- public void scanFileForFuse(String file, int uid) {
- final String callingPackage = getCachedCallingIdentityForFuse(uid).getPackageName();
- scanFile(new File(file), REASON_DEMAND, callingPackage);
+ public void scanFileForFuse(String file) {
+ scanFile(new File(file), REASON_DEMAND);
}
/**
@@ -2301,34 +2312,6 @@
Trace.endSection();
}
- private static @NonNull String[] sanitizePath(@Nullable String path) {
- if (path == null) {
- return new String[0];
- } else {
- final String[] segments = path.split("/");
- // If the path corresponds to the top level directory, then we return an empty path
- // which denotes the top level directory
- if (segments.length == 0) {
- return new String[] { "" };
- }
- for (int i = 0; i < segments.length; i++) {
- segments[i] = sanitizeDisplayName(segments[i]);
- }
- return segments;
- }
- }
-
- private static @Nullable String sanitizeDisplayName(@Nullable String name) {
- if (name == null) {
- return null;
- } else if (name.startsWith(".")) {
- // The resulting file must not be hidden.
- return FileUtils.buildValidFatFilename("_" + name);
- } else {
- return FileUtils.buildValidFatFilename(name);
- }
- }
-
/**
* Sanity check that any requested {@link MediaColumns#DATA} paths actually
* live on the storage volume being targeted.
@@ -5426,16 +5409,6 @@
}
}
- @Nullable
- private String getAbsoluteSanitizedPath(String path) {
- final String[] pathSegments = sanitizePath(path);
- if (pathSegments.length == 0) {
- return null;
- }
- return path = "/" + String.join("/",
- Arrays.copyOfRange(pathSegments, 1, pathSegments.length));
- }
-
/**
* Calculates the ranges that need to be redacted for the given file and user that wants to
* access the file.
diff --git a/src/com/android/providers/media/PermissionActivity.java b/src/com/android/providers/media/PermissionActivity.java
index 49a0062..371522a 100644
--- a/src/com/android/providers/media/PermissionActivity.java
+++ b/src/com/android/providers/media/PermissionActivity.java
@@ -147,8 +147,8 @@
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(resolveTitleText());
- builder.setPositiveButton(resolvePositiveText(), this::onPositiveAction);
- builder.setNegativeButton(resolveNegativeText(), this::onNegativeAction);
+ builder.setPositiveButton(R.string.allow, this::onPositiveAction);
+ builder.setNegativeButton(R.string.deny, this::onNegativeAction);
builder.setCancelable(false);
builder.setView(bodyView);
diff --git a/src/com/android/providers/media/scan/ModernMediaScanner.java b/src/com/android/providers/media/scan/ModernMediaScanner.java
index 81a6a34..955c027 100644
--- a/src/com/android/providers/media/scan/ModernMediaScanner.java
+++ b/src/com/android/providers/media/scan/ModernMediaScanner.java
@@ -384,7 +384,8 @@
final Bundle queryArgs = new Bundle();
queryArgs.putString(ContentResolver.QUERY_ARG_SQL_SELECTION,
formatClause + " AND " + dataClause + " AND " + generationClause);
- final String pathEscapedForLike = DatabaseUtils.escapeForLike(mRoot.getAbsolutePath());
+ final String pathEscapedForLike = DatabaseUtils.escapeForLike(
+ FileUtils.getAbsoluteSanitizedPath(mRoot.getAbsolutePath()));
queryArgs.putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS,
new String[] {pathEscapedForLike + "/%", pathEscapedForLike});
queryArgs.putString(ContentResolver.QUERY_ARG_SQL_SORT_ORDER,
diff --git a/src/com/android/providers/media/util/FileUtils.java b/src/com/android/providers/media/util/FileUtils.java
index b247f2e..c9cf1e2 100644
--- a/src/com/android/providers/media/util/FileUtils.java
+++ b/src/com/android/providers/media/util/FileUtils.java
@@ -701,4 +701,49 @@
}
}
}
+
+ /** {@hide} **/
+ @Nullable
+ public static String getAbsoluteSanitizedPath(String path) {
+ final String[] pathSegments = sanitizePath(path);
+ if (pathSegments.length == 0) {
+ return null;
+ }
+ return path = "/" + String.join("/",
+ Arrays.copyOfRange(pathSegments, 1, pathSegments.length));
+ }
+
+ /** {@hide} */
+ public static @NonNull String[] sanitizePath(@Nullable String path) {
+ if (path == null) {
+ return new String[0];
+ } else {
+ final String[] segments = path.split("/");
+ // If the path corresponds to the top level directory, then we return an empty path
+ // which denotes the top level directory
+ if (segments.length == 0) {
+ return new String[] { "" };
+ }
+ for (int i = 0; i < segments.length; i++) {
+ segments[i] = sanitizeDisplayName(segments[i]);
+ }
+ return segments;
+ }
+ }
+
+ /**
+ * Sanitizes given name by appending '_' to make it non-hidden and mutating the file
+ * name to make it valid for a FAT filesystem.
+ * @hide
+ */
+ public static @Nullable String sanitizeDisplayName(@Nullable String name) {
+ if (name == null) {
+ return null;
+ } else if (name.startsWith(".")) {
+ // The resulting file must not be hidden.
+ return buildValidFatFilename("_" + name);
+ } else {
+ return buildValidFatFilename(name);
+ }
+ }
}
diff --git a/tests/jni/FuseDaemonTest/FilePathAccessTestHelper/src/com/android/tests/fused/FilePathAccessTestHelper.java b/tests/jni/FuseDaemonTest/FilePathAccessTestHelper/src/com/android/tests/fused/FilePathAccessTestHelper.java
index f7af146..d13886a 100644
--- a/tests/jni/FuseDaemonTest/FilePathAccessTestHelper/src/com/android/tests/fused/FilePathAccessTestHelper.java
+++ b/tests/jni/FuseDaemonTest/FilePathAccessTestHelper/src/com/android/tests/fused/FilePathAccessTestHelper.java
@@ -25,7 +25,9 @@
import static com.android.tests.fused.lib.TestUtils.OPEN_FILE_FOR_READ_QUERY;
import static com.android.tests.fused.lib.TestUtils.OPEN_FILE_FOR_WRITE_QUERY;
import static com.android.tests.fused.lib.TestUtils.QUERY_TYPE;
+import static com.android.tests.fused.lib.TestUtils.canOpenWithMediaProvider;
import static com.android.tests.fused.lib.TestUtils.canOpen;
+import static com.android.tests.fused.lib.TestUtils.setContext;
import android.app.Activity;
import android.content.Intent;
@@ -49,6 +51,7 @@
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setContext(this);
String queryType = getIntent().getStringExtra(QUERY_TYPE);
queryType = queryType == null ? "null" : queryType;
switch (queryType) {
@@ -115,9 +118,11 @@
} else if (queryType.equals(DELETE_FILE_QUERY)) {
returnStatus = file.delete();
} else if (queryType.equals(OPEN_FILE_FOR_READ_QUERY)) {
- returnStatus = canOpen(file, false /* forWrite */);
+ returnStatus = canOpen(file, /* forWrite */ false)
+ && canOpenWithMediaProvider(file, /* forWrite */ false);
} else if (queryType.equals(OPEN_FILE_FOR_WRITE_QUERY)) {
- returnStatus = canOpen(file, true /* forWrite */);
+ returnStatus = canOpen(file, /* forWrite */ true)
+ && canOpenWithMediaProvider(file, /* forWrite */ true);
}
} catch(IOException e) {
Log.e(TAG, "Failed to access file: " + filePath + ". Query type: " + queryType, e);
diff --git a/tests/jni/FuseDaemonTest/libs/FuseDaemonTestLib/src/com/android/tests/fused/lib/TestUtils.java b/tests/jni/FuseDaemonTest/libs/FuseDaemonTestLib/src/com/android/tests/fused/lib/TestUtils.java
index 97e39d4..2bbb4b3 100644
--- a/tests/jni/FuseDaemonTest/libs/FuseDaemonTestLib/src/com/android/tests/fused/lib/TestUtils.java
+++ b/tests/jni/FuseDaemonTest/libs/FuseDaemonTestLib/src/com/android/tests/fused/lib/TestUtils.java
@@ -93,6 +93,8 @@
private static final long POLLING_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(10);
private static final long POLLING_SLEEP_MILLIS = 100;
+ @Nullable private static Context sContext;
+
/**
* Grants {@link Manifest.permission#GRANT_RUNTIME_PERMISSIONS} to the given package.
*/
@@ -255,9 +257,16 @@
}
public static ContentResolver getContentResolver() {
+ if (sContext != null) {
+ return sContext.getContentResolver();
+ }
return getContext().getContentResolver();
}
+ public static void setContext(Context context) {
+ sContext = context;
+ }
+
/**
* Queries {@link ContentResolver} for a file and returns the corresponding {@link Uri} for its
* entry in the database. Returns {@code null} if file doesn't exist in the database.
@@ -381,7 +390,6 @@
assertThat(fileUri).isNotNull();
Log.i(TAG, "Uri: " + fileUri + ". Data: " + file.getPath());
ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(fileUri, mode);
- assertThat(pfd).isNotNull();
return pfd;
}
@@ -486,6 +494,19 @@
}
}
+ public static boolean canOpenWithMediaProvider(File file, boolean forWrite) {
+ try {
+ final Uri fileUri = getFileUri(file);
+ if (fileUri == null) {
+ return false;
+ }
+ getContentResolver().openFileDescriptor(fileUri, forWrite ? "w" : "r");
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
public static void pollForExternalStorageState() throws Exception {
for (int i = 0; i < POLLING_TIMEOUT_MILLIS / POLLING_SLEEP_MILLIS; i++) {
if(Environment.getExternalStorageState(Environment.getExternalStorageDirectory())