From 33fbdb643dbc7ff4e05fc05a8d690355c3d2840c Mon Sep 17 00:00:00 2001 From: Alexis THOMAS Date: Thu, 24 Aug 2023 23:15:41 +0200 Subject: [PATCH 1/6] feat(android): read-write is allowed for app dirs on external sd cards --- src/android/FileUtils.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/android/FileUtils.java b/src/android/FileUtils.java index de973da8..c0614dc9 100644 --- a/src/android/FileUtils.java +++ b/src/android/FileUtils.java @@ -625,6 +625,9 @@ private boolean needPermission(String nativeURL, int permissionType) throws JSON if(j.has("externalApplicationStorageDirectory")) { allowedStorageDirectories.add(j.getString("externalApplicationStorageDirectory")); } + ArrayList allowedExtraPatternStorageDirectories = new ArrayList(); + // basic pattern for usual application storage directory, to extend the allowed list to external SD cards for example + allowedExtraPatternStorageDirectories.add("/Android/data/" + cordova.getActivity().getPackageName() + "/"); if(permissionType == READ && hasReadPermission()) { return false; @@ -639,6 +642,11 @@ else if(permissionType == WRITE && hasWritePermission()) { return false; } } + for (String extraPatternDirectory : allowedExtraPatternStorageDirectories) { + if (nativeURL.contains(extraPatternDirectory)) { + return false; + } + } return true; } From c4c380b465ace4177a02a2d174bb436398838bb0 Mon Sep 17 00:00:00 2001 From: Alexis THOMAS Date: Thu, 24 Aug 2023 23:16:15 +0200 Subject: [PATCH 2/6] docs(android): update Device Path for applicationStorageDirectory --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 76a40234..cb0aedf9 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ the `cordova.file.*` properties map to physical paths on a real device. | Device Path | `cordova.file.*` | `AndroidExtraFileSystems` | r/w? | persistent? | OS clears | private | |:------------------------------------------------|:----------------------------|:--------------------------|:----:|:-----------:|:---------:|:-------:| | `file:///android_asset/` | applicationDirectory | assets | r | N/A | N/A | Yes | -| `/data/data//` | applicationStorageDirectory | - | r/w | N/A | N/A | Yes | +| `/Android/data//` | applicationStorageDirectory | - | r/w | N/A | N/A | Yes | |    `cache` | cacheDirectory | cache | r/w | Yes | Yes\* | Yes | |    `files` | dataDirectory | files | r/w | Yes | No | Yes | |       `Documents` | | documents | r/w | Yes | No | Yes | From 555e93ae19615ffae3018981a735e54acf84d8dc Mon Sep 17 00:00:00 2001 From: Alexis THOMAS Date: Thu, 31 Aug 2023 01:00:29 +0200 Subject: [PATCH 3/6] docs(android): correct Device Path for applicationStorageDirectory --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cb0aedf9..6d712470 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ the `cordova.file.*` properties map to physical paths on a real device. | Device Path | `cordova.file.*` | `AndroidExtraFileSystems` | r/w? | persistent? | OS clears | private | |:------------------------------------------------|:----------------------------|:--------------------------|:----:|:-----------:|:---------:|:-------:| | `file:///android_asset/` | applicationDirectory | assets | r | N/A | N/A | Yes | -| `/Android/data//` | applicationStorageDirectory | - | r/w | N/A | N/A | Yes | +| `//` | applicationStorageDirectory | - | r/w | N/A | N/A | Yes | |    `cache` | cacheDirectory | cache | r/w | Yes | Yes\* | Yes | |    `files` | dataDirectory | files | r/w | Yes | No | Yes | |       `Documents` | | documents | r/w | Yes | No | Yes | From a0e6f476da11c8d107bb6c5525eccf213cbec483 Mon Sep 17 00:00:00 2001 From: Alexis THOMAS Date: Thu, 31 Aug 2023 01:09:02 +0200 Subject: [PATCH 4/6] feat(android): use only cordova LOG in FileUtils --- src/android/FileUtils.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/android/FileUtils.java b/src/android/FileUtils.java index c0614dc9..15aa7f73 100644 --- a/src/android/FileUtils.java +++ b/src/android/FileUtils.java @@ -20,14 +20,12 @@ Licensed to the Apache Software Foundation (ASF) under one import android.Manifest; import android.app.Activity; -import android.content.ContentResolver; import android.content.Context; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.util.Base64; -import android.util.Log; import android.webkit.MimeTypeMap; import android.webkit.WebResourceResponse; @@ -46,17 +44,13 @@ Licensed to the Apache Software Foundation (ASF) under one import org.json.JSONException; import org.json.JSONObject; -import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.net.HttpURLConnection; import java.net.MalformedURLException; -import java.net.URL; -import java.security.Permission; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -1357,9 +1351,9 @@ public CordovaPluginPathHandler getPathHandler() { return new WebResourceResponse(fileMimeType, null, fileIS); } catch (FileNotFoundException e) { - Log.e(LOG_TAG, e.getMessage()); + LOG.e(LOG_TAG, e.getMessage()); } catch (IOException e) { - Log.e(LOG_TAG, e.getMessage()); + LOG.e(LOG_TAG, e.getMessage()); } } } From 5731347d8617c865a00af45b9064abfc68ba472f Mon Sep 17 00:00:00 2001 From: Alexis THOMAS Date: Thu, 31 Aug 2023 01:45:21 +0200 Subject: [PATCH 5/6] feat(android)!: add removable and media dirs to known paths, then add to allowed --- README.md | 18 ++++++++- src/android/FileUtils.java | 82 +++++++++++++++++++++++++++++--------- 2 files changed, 81 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 6d712470..a653219b 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,8 @@ Each URL is in the form _file:///path/to/spot/_, and can be converted to a * `cordova.file.externalCacheDirectory` - Application cache on external storage. (_Android_) +* `cordova.file.externalMediaDirectory` - Application media directory on external storage. (_Android_) + * `cordova.file.externalRootDirectory` - External storage (SD card) root. (_Android_, _BlackBerry 10_) * `cordova.file.tempDirectory` - Temp directory that the OS can clear at will. Do not @@ -117,6 +119,14 @@ Each URL is in the form _file:///path/to/spot/_, and can be converted to a * `cordova.file.sharedDirectory` - Files globally available to all applications (_BlackBerry 10_) +* `cordova.file.removableExternalApplicationStorageDirectories` - Application space on removable external storages. (_Android_) + +* `cordova.file.removableExternalDataDirectories` - Where to put app-specific data files on removable external storages. (_Android_) + +* `cordova.file.removableExternalCacheDirectories` - Application cache on removable external storages. (_Android_) + +* `cordova.file.removableExternalMediaDirectories` - Application media directory on removable external storages. (_Android_) + ## File System Layouts Although technically an implementation detail, it can be very useful to know how @@ -162,8 +172,14 @@ the `cordova.file.*` properties map to physical paths on a real device. |       `Documents` | | documents | r/w | Yes | No | Yes | | `/` | externalRootDirectory | sdcard | r/w\*\*\* | Yes | No | No | |    `Android/data//` | externalApplicationStorageDirectory | - | r/w | Yes | No | No | -|       `cache` | externalCacheDirectory | cache-external | r/w | Yes | No\*\*| No | +|       `cache` | externalCacheDirectory | cache-external | r/w | Yes | No\*\*| No | |       `files` | externalDataDirectory | files-external | r/w | Yes | No | No | +|    `Android/media//` | externalMediaDirectory | - | r/w | Yes | No | No | +| `/` (0 or more) | | - | r/w\*\*\* | Yes | No | No | +|    `Android/data//` | removableExternalApplicationStorageDirectories | - | r/w | Yes | No | No | +|       `cache` | removableExternalCacheDirectories | - | r/w | Yes | No\*\*| No | +|       `files` | removableExternalDataDirectories | - | r/w | Yes | No | No | +|    `Android/media//` | removableExternalMediaDirectories | - | r/w | Yes | No | No | \* The OS may periodically clear this directory, but do not rely on this behavior. Clear the contents of this directory as appropriate for your application. Should a user diff --git a/src/android/FileUtils.java b/src/android/FileUtils.java index 15aa7f73..ded911b3 100644 --- a/src/android/FileUtils.java +++ b/src/android/FileUtils.java @@ -619,9 +619,21 @@ private boolean needPermission(String nativeURL, int permissionType) throws JSON if(j.has("externalApplicationStorageDirectory")) { allowedStorageDirectories.add(j.getString("externalApplicationStorageDirectory")); } - ArrayList allowedExtraPatternStorageDirectories = new ArrayList(); - // basic pattern for usual application storage directory, to extend the allowed list to external SD cards for example - allowedExtraPatternStorageDirectories.add("/Android/data/" + cordova.getActivity().getPackageName() + "/"); + if (j.has("removableExternalApplicationStorageDirectories")) { + JSONArray array = j.getJSONArray("removableExternalApplicationStorageDirectories"); + for (int i = 0; i < array.length(); i++) { + allowedStorageDirectories.add(array.getString(i)); + } + } + if (j.has("removableExternalMediaDirectories")) { + JSONArray array = j.getJSONArray("removableExternalMediaDirectories"); + for (int i = 0; i < array.length(); i++) { + allowedStorageDirectories.add(array.getString(i)); + } + } + if (j.has("externalMediaDirectory")) { + allowedStorageDirectories.add(j.getString("externalMediaDirectory")); + } if(permissionType == READ && hasReadPermission()) { return false; @@ -636,11 +648,6 @@ else if(permissionType == WRITE && hasWritePermission()) { return false; } } - for (String extraPatternDirectory : allowedExtraPatternStorageDirectories) { - if (nativeURL.contains(extraPatternDirectory)) { - return false; - } - } return true; } @@ -1045,17 +1052,56 @@ private JSONObject requestAllPaths() throws JSONException { ret.put("applicationStorageDirectory", toDirUrl(context.getFilesDir().getParentFile())); ret.put("dataDirectory", toDirUrl(context.getFilesDir())); ret.put("cacheDirectory", toDirUrl(context.getCacheDir())); - if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - try { - ret.put("externalApplicationStorageDirectory", toDirUrl(context.getExternalFilesDir(null).getParentFile())); - ret.put("externalDataDirectory", toDirUrl(context.getExternalFilesDir(null))); - ret.put("externalCacheDirectory", toDirUrl(context.getExternalCacheDir())); - ret.put("externalRootDirectory", toDirUrl(Environment.getExternalStorageDirectory())); - } - catch(NullPointerException e) { + try { + if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + ret.put("externalApplicationStorageDirectory", toDirUrl(context.getExternalFilesDir(null).getParentFile())); + ret.put("externalDataDirectory", toDirUrl(context.getExternalFilesDir(null))); + ret.put("externalCacheDirectory", toDirUrl(context.getExternalCacheDir())); + ret.put("externalRootDirectory", toDirUrl(Environment.getExternalStorageDirectory())); + } + + JSONArray removableExternalApplicationStorageDirs = new JSONArray(); + JSONArray removableExternalDataDirs = new JSONArray(); + JSONArray removableExternalCacheDirs = new JSONArray(); + JSONArray removableExternalMediaDirs = new JSONArray(); + String externalMediaDir = null; + for (File filesDir : context.getExternalFilesDirs(null)) { + if (filesDir != null) { + if (Environment.isExternalStorageRemovable(filesDir)) { + removableExternalApplicationStorageDirs.put(toDirUrl(filesDir.getParentFile())); + removableExternalDataDirs.put(toDirUrl(filesDir)); + } + } + } + for (File cacheDir : context.getExternalCacheDirs()) { + if (cacheDir != null) { + if (Environment.isExternalStorageRemovable(cacheDir)) { + removableExternalCacheDirs.put(toDirUrl(cacheDir)); + } + } + } + for (File mediaDir : context.getExternalMediaDirs()) { + if (mediaDir != null) { + String dirUrl = toDirUrl(mediaDir); + if (Environment.isExternalStorageRemovable(mediaDir)) { + removableExternalMediaDirs.put(dirUrl); + } else { + if (externalMediaDir != null) { + LOG.w(LOG_TAG, "External media directory already found ; skip other value " + dirUrl); + continue; + } + externalMediaDir = dirUrl; + } + } + } + ret.put("removableExternalApplicationStorageDirectories", removableExternalApplicationStorageDirs); + ret.put("removableExternalDataDirectories", removableExternalDataDirs); + ret.put("removableExternalCacheDirectories", removableExternalCacheDirs); + ret.put("removableExternalMediaDirectories", removableExternalMediaDirs); + ret.put("externalMediaDirectory", externalMediaDir); + } catch (NullPointerException e) { /* If external storage is unavailable, context.getExternal* returns null */ - LOG.d(LOG_TAG, "Unable to access these paths, most liklely due to USB storage"); - } + LOG.d(LOG_TAG, "Unable to access these paths, most likely due to USB storage"); } return ret; } From 28f950e30eca208ae2d9556c66e079ee10d93db0 Mon Sep 17 00:00:00 2001 From: Alexis THOMAS Date: Sun, 24 Dec 2023 17:48:40 +0100 Subject: [PATCH 6/6] feat(android): revert Log, will be in a dedicated "cleanup" branch --- src/android/FileUtils.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/android/FileUtils.java b/src/android/FileUtils.java index c3ed3911..35281f7b 100644 --- a/src/android/FileUtils.java +++ b/src/android/FileUtils.java @@ -26,6 +26,7 @@ Licensed to the Apache Software Foundation (ASF) under one import android.os.Build; import android.os.Environment; import android.util.Base64; +import android.util.Log; import android.webkit.MimeTypeMap; import android.webkit.WebResourceResponse; @@ -1354,9 +1355,9 @@ public CordovaPluginPathHandler getPathHandler() { return new WebResourceResponse(fileMimeType, null, fileIS); } catch (FileNotFoundException e) { - LOG.e(LOG_TAG, e.getMessage()); + Log.e(LOG_TAG, e.getMessage()); } catch (IOException e) { - LOG.e(LOG_TAG, e.getMessage()); + Log.e(LOG_TAG, e.getMessage()); } } }