Skip to content

Commit f0aabca

Browse files
authored
Merge pull request #6 from ath0mas/allowed-app-dirs-external-sdcards
Android!: allow read-write to app dirs in media dirs and on external sdcards too
2 parents 60212b2 + 28245e9 commit f0aabca

File tree

2 files changed

+80
-23
lines changed

2 files changed

+80
-23
lines changed

README.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ Each URL is in the form _file:///path/to/spot/_, and can be converted to a
103103
* `cordova.file.externalCacheDirectory` - Application cache on external storage.
104104
(_Android_)
105105

106+
* `cordova.file.externalMediaDirectory` - Application media directory on external storage. (_Android_)
107+
106108
* `cordova.file.externalRootDirectory` - External storage (SD card) root. (_Android_, _BlackBerry 10_)
107109

108110
* `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
117119

118120
* `cordova.file.sharedDirectory` - Files globally available to all applications (_BlackBerry 10_)
119121

122+
* `cordova.file.removableExternalApplicationStorageDirectories` - Application space on removable external storages. (_Android_)
123+
124+
* `cordova.file.removableExternalDataDirectories` - Where to put app-specific data files on removable external storages. (_Android_)
125+
126+
* `cordova.file.removableExternalCacheDirectories` - Application cache on removable external storages. (_Android_)
127+
128+
* `cordova.file.removableExternalMediaDirectories` - Application media directory on removable external storages. (_Android_)
129+
120130
## File System Layouts
121131

122132
Although technically an implementation detail, it can be very useful to know how
@@ -156,14 +166,20 @@ the `cordova.file.*` properties map to physical paths on a real device.
156166
| Device Path | `cordova.file.*` | `AndroidExtraFileSystems` | r/w? | persistent? | OS clears | private |
157167
|:------------------------------------------------|:----------------------------|:--------------------------|:----:|:-----------:|:---------:|:-------:|
158168
| `file:///android_asset/` | applicationDirectory | assets | r | N/A | N/A | Yes |
159-
| `<internal>/Android/data/<app-id>/` | applicationStorageDirectory | - | r/w | N/A | N/A | Yes |
169+
| `<internal>/<app-id>/` | applicationStorageDirectory | - | r/w | N/A | N/A | Yes |
160170
| &nbsp;&nbsp;&nbsp;`cache` | cacheDirectory | cache | r/w | Yes | Yes\* | Yes |
161171
| &nbsp;&nbsp;&nbsp;`files` | dataDirectory | files | r/w | Yes | No | Yes |
162172
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`Documents` | | documents | r/w | Yes | No | Yes |
163173
| `<sdcard>/` | externalRootDirectory | sdcard | r/w\*\*\* | Yes | No | No |
164174
| &nbsp;&nbsp;&nbsp;`Android/data/<app-id>/` | externalApplicationStorageDirectory | - | r/w | Yes | No | No |
165-
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`cache` | externalCacheDirectory | cache-external | r/w | Yes | No\*\*| No |
175+
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`cache` | externalCacheDirectory | cache-external | r/w | Yes | No\*\*| No |
166176
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`files` | externalDataDirectory | files-external | r/w | Yes | No | No |
177+
| &nbsp;&nbsp;&nbsp;`Android/media/<app-id>/` | externalMediaDirectory | - | r/w | Yes | No | No |
178+
| `<removable sdcard>/` (0 or more) | | - | r/w\*\*\* | Yes | No | No |
179+
| &nbsp;&nbsp;&nbsp;`Android/data/<app-id>/` | removableExternalApplicationStorageDirectories | - | r/w | Yes | No | No |
180+
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`cache` | removableExternalCacheDirectories | - | r/w | Yes | No\*\*| No |
181+
| &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;`files` | removableExternalDataDirectories | - | r/w | Yes | No | No |
182+
| &nbsp;&nbsp;&nbsp;`Android/media/<app-id>/` | removableExternalMediaDirectories | - | r/w | Yes | No | No |
167183

168184
\* The OS may periodically clear this directory, but do not rely on this behavior. Clear
169185
the contents of this directory as appropriate for your application. Should a user

src/android/FileUtils.java

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,12 @@ Licensed to the Apache Software Foundation (ASF) under one
2020

2121
import android.Manifest;
2222
import android.app.Activity;
23-
import android.content.ContentResolver;
2423
import android.content.Context;
2524
import android.content.pm.PackageManager;
2625
import android.net.Uri;
2726
import android.os.Build;
2827
import android.os.Environment;
2928
import android.util.Base64;
30-
import android.util.Log;
3129
import android.webkit.MimeTypeMap;
3230
import android.webkit.WebResourceResponse;
3331

@@ -46,17 +44,13 @@ Licensed to the Apache Software Foundation (ASF) under one
4644
import org.json.JSONException;
4745
import org.json.JSONObject;
4846

49-
import java.io.BufferedInputStream;
5047
import java.io.ByteArrayOutputStream;
5148
import java.io.File;
5249
import java.io.FileInputStream;
5350
import java.io.FileNotFoundException;
5451
import java.io.IOException;
5552
import java.io.InputStream;
56-
import java.net.HttpURLConnection;
5753
import java.net.MalformedURLException;
58-
import java.net.URL;
59-
import java.security.Permission;
6054
import java.util.ArrayList;
6155
import java.util.HashMap;
6256
import java.util.HashSet;
@@ -575,9 +569,21 @@ private boolean needPermission(String nativeURL, int permissionType) throws JSON
575569
if (j.has("externalApplicationStorageDirectory")) {
576570
allowedStorageDirectories.add(j.getString("externalApplicationStorageDirectory"));
577571
}
578-
ArrayList<String> allowedExtraPatternStorageDirectories = new ArrayList<String>();
579-
// basic pattern for usual application storage directory, to extend the allowed list to external SD cards for example
580-
allowedExtraPatternStorageDirectories.add("/Android/data/" + cordova.getActivity().getPackageName() + "/");
572+
if (j.has("removableExternalApplicationStorageDirectories")) {
573+
JSONArray array = j.getJSONArray("removableExternalApplicationStorageDirectories");
574+
for (int i = 0; i < array.length(); i++) {
575+
allowedStorageDirectories.add(array.getString(i));
576+
}
577+
}
578+
if (j.has("removableExternalMediaDirectories")) {
579+
JSONArray array = j.getJSONArray("removableExternalMediaDirectories");
580+
for (int i = 0; i < array.length(); i++) {
581+
allowedStorageDirectories.add(array.getString(i));
582+
}
583+
}
584+
if (j.has("externalMediaDirectory")) {
585+
allowedStorageDirectories.add(j.getString("externalMediaDirectory"));
586+
}
581587

582588
if (permissionType == READ && hasReadPermission()) {
583589
return false;
@@ -591,11 +597,6 @@ private boolean needPermission(String nativeURL, int permissionType) throws JSON
591597
return false;
592598
}
593599
}
594-
for (String extraPatternDirectory : allowedExtraPatternStorageDirectories) {
595-
if (nativeURL.contains(extraPatternDirectory)) {
596-
return false;
597-
}
598-
}
599600
return true;
600601
}
601602

@@ -1000,16 +1001,56 @@ private JSONObject requestAllPaths() throws JSONException {
10001001
ret.put("applicationStorageDirectory", toDirUrl(context.getFilesDir().getParentFile()));
10011002
ret.put("dataDirectory", toDirUrl(context.getFilesDir()));
10021003
ret.put("cacheDirectory", toDirUrl(context.getCacheDir()));
1003-
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
1004-
try {
1004+
try {
1005+
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
10051006
ret.put("externalApplicationStorageDirectory", toDirUrl(context.getExternalFilesDir(null).getParentFile()));
10061007
ret.put("externalDataDirectory", toDirUrl(context.getExternalFilesDir(null)));
10071008
ret.put("externalCacheDirectory", toDirUrl(context.getExternalCacheDir()));
10081009
ret.put("externalRootDirectory", toDirUrl(Environment.getExternalStorageDirectory()));
1009-
} catch (NullPointerException e) {
1010-
/* If external storage is unavailable, context.getExternal* returns null */
1011-
LOG.d(LOG_TAG, "Unable to access these paths, most liklely due to USB storage");
10121010
}
1011+
1012+
JSONArray removableExternalApplicationStorageDirs = new JSONArray();
1013+
JSONArray removableExternalDataDirs = new JSONArray();
1014+
JSONArray removableExternalCacheDirs = new JSONArray();
1015+
JSONArray removableExternalMediaDirs = new JSONArray();
1016+
String externalMediaDir = null;
1017+
for (File filesDir : context.getExternalFilesDirs(null)) {
1018+
if (filesDir != null) {
1019+
if (Environment.isExternalStorageRemovable(filesDir)) {
1020+
removableExternalApplicationStorageDirs.put(toDirUrl(filesDir.getParentFile()));
1021+
removableExternalDataDirs.put(toDirUrl(filesDir));
1022+
}
1023+
}
1024+
}
1025+
for (File cacheDir : context.getExternalCacheDirs()) {
1026+
if (cacheDir != null) {
1027+
if (Environment.isExternalStorageRemovable(cacheDir)) {
1028+
removableExternalCacheDirs.put(toDirUrl(cacheDir));
1029+
}
1030+
}
1031+
}
1032+
for (File mediaDir : context.getExternalMediaDirs()) {
1033+
if (mediaDir != null) {
1034+
String dirUrl = toDirUrl(mediaDir);
1035+
if (Environment.isExternalStorageRemovable(mediaDir)) {
1036+
removableExternalMediaDirs.put(dirUrl);
1037+
} else {
1038+
if (externalMediaDir != null) {
1039+
LOG.w(LOG_TAG, "External media directory already found ; skip other value " + dirUrl);
1040+
continue;
1041+
}
1042+
externalMediaDir = dirUrl;
1043+
}
1044+
}
1045+
}
1046+
ret.put("removableExternalApplicationStorageDirectories", removableExternalApplicationStorageDirs);
1047+
ret.put("removableExternalDataDirectories", removableExternalDataDirs);
1048+
ret.put("removableExternalCacheDirectories", removableExternalCacheDirs);
1049+
ret.put("removableExternalMediaDirectories", removableExternalMediaDirs);
1050+
ret.put("externalMediaDirectory", externalMediaDir);
1051+
} catch (NullPointerException e) {
1052+
/* If external storage is unavailable, context.getExternal* returns null */
1053+
LOG.d(LOG_TAG, "Unable to access these paths, most likely due to USB storage");
10131054
}
10141055
return ret;
10151056
}
@@ -1302,9 +1343,9 @@ public CordovaPluginPathHandler getPathHandler() {
13021343

13031344
return new WebResourceResponse(fileMimeType, null, fileIS);
13041345
} catch (FileNotFoundException e) {
1305-
Log.e(LOG_TAG, e.getMessage());
1346+
LOG.e(LOG_TAG, e.getMessage());
13061347
} catch (IOException e) {
1307-
Log.e(LOG_TAG, e.getMessage());
1348+
LOG.e(LOG_TAG, e.getMessage());
13081349
}
13091350
}
13101351
}

0 commit comments

Comments
 (0)