Merge changes from topic "apr17" into rvc-dev
* changes:
Consistent treatment of boolean-ish values.
Allow file movement via PermissionActivity.
diff --git a/apex/framework/java/android/provider/MediaStore.java b/apex/framework/java/android/provider/MediaStore.java
index 16a0892..84b2b10 100644
--- a/apex/framework/java/android/provider/MediaStore.java
+++ b/apex/framework/java/android/provider/MediaStore.java
@@ -57,7 +57,6 @@
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.text.TextUtils;
-import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -609,6 +608,15 @@
public static final String QUERY_ARG_RELATED_URI = "android:query-arg-related-uri";
/**
+ * Flag that can be used to enable movement of media items on disk through
+ * {@link ContentResolver#update} calls. This is typically true for
+ * third-party apps, but false for system components.
+ *
+ * @hide
+ */
+ public static final String QUERY_ARG_ALLOW_MOVEMENT = "android:query-arg-allow-movement";
+
+ /**
* Specify how {@link MediaColumns#IS_PENDING} items should be filtered when
* performing a {@link MediaStore} operation.
* <p>
@@ -719,7 +727,7 @@
/** @hide */
@Deprecated
public static boolean getIncludePending(@NonNull Uri uri) {
- return parseBoolean(uri.getQueryParameter(MediaStore.PARAM_INCLUDE_PENDING));
+ return uri.getBooleanQueryParameter(MediaStore.PARAM_INCLUDE_PENDING, false);
}
/**
@@ -749,7 +757,7 @@
* @see MediaStore#setRequireOriginal(Uri)
*/
public static boolean getRequireOriginal(@NonNull Uri uri) {
- return parseBoolean(uri.getQueryParameter(MediaStore.PARAM_REQUIRE_ORIGINAL));
+ return uri.getBooleanQueryParameter(MediaStore.PARAM_REQUIRE_ORIGINAL, false);
}
/**
@@ -3735,13 +3743,6 @@
return volumeName;
}
- private static boolean parseBoolean(@Nullable String value) {
- if (value == null) return false;
- if ("1".equals(value)) return true;
- if ("true".equalsIgnoreCase(value)) return true;
- return false;
- }
-
/**
* Uri for querying the state of the media scanner.
*/
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index 3089c19..89166b6 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -2444,7 +2444,7 @@
values.put(FileColumns.VOLUME_NAME, extractVolumeName(path));
values.put(FileColumns.RELATIVE_PATH, extractRelativePath(path));
values.put(FileColumns.DISPLAY_NAME, extractDisplayName(path));
- values.put(FileColumns.IS_DOWNLOAD, isDownload(path));
+ values.put(FileColumns.IS_DOWNLOAD, isDownload(path) ? 1 : 0);
File file = new File(path);
if (file.exists()) {
values.put(FileColumns.DATE_MODIFIED, file.lastModified() / 1000);
@@ -2814,7 +2814,7 @@
private boolean maybeMarkAsDownload(@NonNull ContentValues values) {
final String path = values.getAsString(MediaColumns.DATA);
if (path != null && isDownload(path)) {
- values.put(FileColumns.IS_DOWNLOAD, true);
+ values.put(FileColumns.IS_DOWNLOAD, 1);
return true;
}
return false;
@@ -3130,7 +3130,7 @@
case DOWNLOADS:
maybePut(initialValues, FileColumns.OWNER_PACKAGE_NAME, ownerPackageName);
- initialValues.put(FileColumns.IS_DOWNLOAD, true);
+ initialValues.put(FileColumns.IS_DOWNLOAD, 1);
rowId = insertFile(qb, helper, match, uri, extras, initialValues,
FileColumns.MEDIA_TYPE_NONE, false);
if (rowId > 0) {
@@ -3214,14 +3214,6 @@
}
}
- @VisibleForTesting
- static boolean parseBoolean(String value) {
- if (value == null) return false;
- if ("1".equals(value)) return true;
- if ("true".equalsIgnoreCase(value)) return true;
- return false;
- }
-
@Deprecated
private String getSharedPackages(String callingPackage) {
final String[] sharedPackageNames = mCallingIdentity.get().getSharedPackageNames();
@@ -3270,7 +3262,7 @@
}
final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- if (parseBoolean(uri.getQueryParameter("distinct"))) {
+ if (uri.getBooleanQueryParameter("distinct", false)) {
qb.setDistinct(true);
}
qb.setStrict(true);
@@ -4578,7 +4570,7 @@
final Uri playlistUri = ContentUris.withAppendedId(
MediaStore.Audio.Playlists.getContentUri(volumeName), playlistId);
- if (parseBoolean(uri.getQueryParameter("move"))) {
+ if (uri.getBooleanQueryParameter("move", false)) {
// Convert explicit request into query; sigh, moveItem()
// uses zero-based indexing instead of one-based indexing
final int from = Integer.parseInt(uri.getPathSegments().get(5)) + 1;
@@ -4735,16 +4727,14 @@
break;
}
- // TODO: remove this as part of fixing b/151768142
- final boolean isCallingPackageSystem = isCallingPackageSystem()
- && !"com.android.systemui".equals(getCallingPackageOrSelf());
-
// If we're touching columns that would change placement of a file,
// blend in current values and recalculate path
+ final boolean allowMovement = extras.getBoolean(MediaStore.QUERY_ARG_ALLOW_MOVEMENT,
+ !isCallingPackageSystem());
if (containsAny(initialValues.keySet(), sPlacementColumns)
&& !initialValues.containsKey(MediaColumns.DATA)
- && !isCallingPackageSystem
- && !isThumbnail) {
+ && !isThumbnail
+ && allowMovement) {
Trace.beginSection("movement");
// We only support movement under well-defined collections
diff --git a/src/com/android/providers/media/PermissionActivity.java b/src/com/android/providers/media/PermissionActivity.java
index 0c398dd..0696ef5 100644
--- a/src/com/android/providers/media/PermissionActivity.java
+++ b/src/com/android/providers/media/PermissionActivity.java
@@ -20,6 +20,7 @@
import static com.android.providers.media.MediaProvider.IMAGES_MEDIA_ID;
import static com.android.providers.media.MediaProvider.VIDEO_MEDIA_ID;
import static com.android.providers.media.MediaProvider.collectUris;
+import static com.android.providers.media.util.DatabaseUtils.getAsBoolean;
import static com.android.providers.media.util.Logging.TAG;
import android.app.Activity;
@@ -198,6 +199,7 @@
for (Uri uri : uris) {
ops.add(ContentProviderOperation.newUpdate(uri)
.withValues(values)
+ .withExtra(MediaStore.QUERY_ARG_ALLOW_MOVEMENT, true)
.withExceptionAllowed(true)
.build());
}
@@ -290,10 +292,10 @@
case MediaStore.CREATE_WRITE_REQUEST_CALL:
return VERB_WRITE;
case MediaStore.CREATE_TRASH_REQUEST_CALL:
- return (values.getAsInteger(MediaColumns.IS_TRASHED) != 0)
+ return getAsBoolean(values, MediaColumns.IS_TRASHED, false)
? VERB_TRASH : VERB_UNTRASH;
case MediaStore.CREATE_FAVORITE_REQUEST_CALL:
- return (values.getAsInteger(MediaColumns.IS_FAVORITE) != 0)
+ return getAsBoolean(values, MediaColumns.IS_FAVORITE, false)
? VERB_FAVORITE : VERB_UNFAVORITE;
case MediaStore.CREATE_DELETE_REQUEST_CALL:
return VERB_DELETE;
diff --git a/src/com/android/providers/media/util/DatabaseUtils.java b/src/com/android/providers/media/util/DatabaseUtils.java
index ef33b04..a5ab700 100644
--- a/src/com/android/providers/media/util/DatabaseUtils.java
+++ b/src/com/android/providers/media/util/DatabaseUtils.java
@@ -48,7 +48,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import java.util.Locale;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -532,10 +534,27 @@
return sb.toString();
}
+ public static boolean parseBoolean(@Nullable Object value, boolean def) {
+ if (value instanceof Boolean) {
+ return (Boolean) value;
+ } else if (value instanceof Number) {
+ return ((Number) value).intValue() != 0;
+ } else if (value instanceof String) {
+ final String stringValue = ((String) value).toLowerCase(Locale.ROOT);
+ return (!"false".equals(stringValue) && !"0".equals(stringValue));
+ } else {
+ return def;
+ }
+ }
+
+ public static boolean getAsBoolean(@NonNull Bundle extras,
+ @NonNull String key, boolean def) {
+ return parseBoolean(extras.get(key), def);
+ }
+
public static boolean getAsBoolean(@NonNull ContentValues values,
@NonNull String key, boolean def) {
- final Integer value = values.getAsInteger(key);
- return (value != null) ? (value != 0) : def;
+ return parseBoolean(values.get(key), def);
}
public static long getAsLong(@NonNull ContentValues values,
diff --git a/src/com/android/providers/media/util/FileUtils.java b/src/com/android/providers/media/util/FileUtils.java
index 78f2c6f..d7269ec 100644
--- a/src/com/android/providers/media/util/FileUtils.java
+++ b/src/com/android/providers/media/util/FileUtils.java
@@ -35,6 +35,7 @@
import static com.android.providers.media.util.DatabaseUtils.getAsBoolean;
import static com.android.providers.media.util.DatabaseUtils.getAsLong;
+import static com.android.providers.media.util.DatabaseUtils.parseBoolean;
import static com.android.providers.media.util.Logging.TAG;
import android.content.ClipDescription;
@@ -957,18 +958,18 @@
// Only define the field when this modification is actually adjusting
// one of the flags that should influence the expiration
- final Integer pending = values.getAsInteger(MediaColumns.IS_PENDING);
+ final Object pending = values.get(MediaColumns.IS_PENDING);
if (pending != null) {
- if (pending != 0) {
+ if (parseBoolean(pending, false)) {
values.put(MediaColumns.DATE_EXPIRES,
(System.currentTimeMillis() + DEFAULT_DURATION_PENDING) / 1000);
} else {
values.putNull(MediaColumns.DATE_EXPIRES);
}
}
- final Integer trashed = values.getAsInteger(MediaColumns.IS_TRASHED);
+ final Object trashed = values.get(MediaColumns.IS_TRASHED);
if (trashed != null) {
- if (trashed != 0) {
+ if (parseBoolean(trashed, false)) {
values.put(MediaColumns.DATE_EXPIRES,
(System.currentTimeMillis() + DEFAULT_DURATION_TRASHED) / 1000);
} else {
diff --git a/tests/src/com/android/providers/media/MediaProviderTest.java b/tests/src/com/android/providers/media/MediaProviderTest.java
index 0f0ed64..6e9fda9 100644
--- a/tests/src/com/android/providers/media/MediaProviderTest.java
+++ b/tests/src/com/android/providers/media/MediaProviderTest.java
@@ -741,18 +741,6 @@
}
@Test
- public void testParseBoolean() throws Exception {
- assertTrue(MediaProvider.parseBoolean("TRUE"));
- assertTrue(MediaProvider.parseBoolean("true"));
- assertTrue(MediaProvider.parseBoolean("1"));
-
- assertFalse(MediaProvider.parseBoolean("FALSE"));
- assertFalse(MediaProvider.parseBoolean("false"));
- assertFalse(MediaProvider.parseBoolean("0"));
- assertFalse(MediaProvider.parseBoolean(null));
- }
-
- @Test
public void testIsDownload() throws Exception {
assertTrue(isDownload("/storage/emulated/0/Download/colors.png"));
assertTrue(isDownload("/storage/emulated/0/Download/test.pdf"));
diff --git a/tests/src/com/android/providers/media/util/DatabaseUtilsTest.java b/tests/src/com/android/providers/media/util/DatabaseUtilsTest.java
index 3a0ea75..d4ff968 100644
--- a/tests/src/com/android/providers/media/util/DatabaseUtilsTest.java
+++ b/tests/src/com/android/providers/media/util/DatabaseUtilsTest.java
@@ -32,6 +32,7 @@
import static android.database.DatabaseUtils.escapeForLike;
import static com.android.providers.media.util.DatabaseUtils.maybeBalance;
+import static com.android.providers.media.util.DatabaseUtils.parseBoolean;
import static com.android.providers.media.util.DatabaseUtils.recoverAbusiveLimit;
import static com.android.providers.media.util.DatabaseUtils.recoverAbusiveSortOrder;
import static com.android.providers.media.util.DatabaseUtils.resolveQueryArgs;
@@ -368,6 +369,24 @@
escapeForLike("/path/to/fi%le.bin"));
}
+ @Test
+ public void testParseBoolean() throws Exception {
+ assertTrue(parseBoolean("TRUE", false));
+ assertTrue(parseBoolean("true", false));
+ assertTrue(parseBoolean("1", false));
+ assertTrue(parseBoolean(1, false));
+ assertTrue(parseBoolean(true, false));
+
+ assertFalse(parseBoolean("FALSE", true));
+ assertFalse(parseBoolean("false", true));
+ assertFalse(parseBoolean("0", true));
+ assertFalse(parseBoolean(0, true));
+ assertFalse(parseBoolean(false, true));
+
+ assertFalse(parseBoolean(null, false));
+ assertTrue(parseBoolean(null, true));
+ }
+
private static Pair<String, String> recoverAbusiveGroupBy(
Pair<String, String> selectionAndGroupBy) {
final Bundle queryArgs = new Bundle();