Loading api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -37297,6 +37297,7 @@ package android.provider { method public static java.lang.String getVolumeName(android.net.Uri); method public static android.provider.MediaStore.PendingSession openPending(android.content.Context, android.net.Uri); method public static android.net.Uri setIncludePending(android.net.Uri); method public static android.net.Uri setRequireOriginal(android.net.Uri); field public static final java.lang.String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE"; field public static final java.lang.String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE"; field public static final java.lang.String ACTION_REVIEW = "android.provider.action.REVIEW"; cmds/content/src/com/android/commands/content/Content.java +4 −1 Original line number Diff line number Diff line Loading @@ -77,7 +77,7 @@ public class Content { + " <BINDING> binds a typed value to a column and is formatted:\n" + " <COLUMN_NAME>:<TYPE>:<COLUMN_VALUE> where:\n" + " <TYPE> specifies data type such as:\n" + " b - boolean, s - string, i - integer, l - long, f - float, d - double\n" + " b - boolean, s - string, i - integer, l - long, f - float, d - double, n - null\n" + " Note: Omit the value for passing an empty string, e.g column:s:\n" + " Example:\n" + " # Add \"new_setting\" secure setting with value \"new_value\".\n" Loading Loading @@ -153,6 +153,7 @@ public class Content { private static final String TYPE_LONG = "l"; private static final String TYPE_FLOAT = "f"; private static final String TYPE_DOUBLE = "d"; private static final String TYPE_NULL = "n"; private static final String COLON = ":"; private static final String ARGUMENT_PREFIX = "--"; Loading Loading @@ -410,6 +411,8 @@ public class Content { values.put(column, Long.parseLong(value)); } else if (TYPE_FLOAT.equalsIgnoreCase(type) || TYPE_DOUBLE.equalsIgnoreCase(type)) { values.put(column, Double.parseDouble(value)); } else if (TYPE_NULL.equalsIgnoreCase(type)) { values.putNull(column); } else { throw new IllegalArgumentException("Unsupported type: " + type); } Loading core/java/android/os/ParcelFileDescriptor.java +10 −8 Original line number Diff line number Diff line Loading @@ -17,12 +17,6 @@ package android.os; import static android.system.OsConstants.AF_UNIX; import static android.system.OsConstants.O_APPEND; import static android.system.OsConstants.O_CREAT; import static android.system.OsConstants.O_RDONLY; import static android.system.OsConstants.O_RDWR; import static android.system.OsConstants.O_TRUNC; import static android.system.OsConstants.O_WRONLY; import static android.system.OsConstants.SEEK_SET; import static android.system.OsConstants.SOCK_SEQPACKET; import static android.system.OsConstants.SOCK_STREAM; Loading Loading @@ -254,8 +248,16 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { } /** {@hide} */ public static ParcelFileDescriptor fromFd( FileDescriptor fd, Handler handler, final OnCloseListener listener) throws IOException { public static ParcelFileDescriptor fromPfd(ParcelFileDescriptor pfd, Handler handler, final OnCloseListener listener) throws IOException { final FileDescriptor original = new FileDescriptor(); original.setInt$(pfd.detachFd()); return fromFd(original, handler, listener); } /** {@hide} */ public static ParcelFileDescriptor fromFd(FileDescriptor fd, Handler handler, final OnCloseListener listener) throws IOException { if (handler == null) { throw new IllegalArgumentException("Handler must not be null"); } Loading core/java/android/os/RedactingFileDescriptor.java +76 −10 Original line number Diff line number Diff line Loading @@ -20,15 +20,18 @@ import android.content.Context; import android.os.storage.StorageManager; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import libcore.io.IoUtils; import libcore.util.EmptyArray; import java.io.File; import java.io.FileDescriptor; import java.io.IOException; import java.io.InterruptedIOException; import java.util.Arrays; /** * Variant of {@link FileDescriptor} that allows its creator to specify regions Loading @@ -40,20 +43,21 @@ public class RedactingFileDescriptor { private static final String TAG = "RedactingFileDescriptor"; private static final boolean DEBUG = true; private final long[] mRedactRanges; private volatile long[] mRedactRanges; private FileDescriptor mInner = null; private ParcelFileDescriptor mOuter = null; private RedactingFileDescriptor(Context context, File file, long[] redactRanges) private RedactingFileDescriptor(Context context, File file, int mode, long[] redactRanges) throws IOException { mRedactRanges = checkRangesArgument(redactRanges); try { try { mInner = Os.open(file.getAbsolutePath(), OsConstants.O_RDONLY, 0); mInner = Os.open(file.getAbsolutePath(), FileUtils.translateModePfdToPosix(mode), 0); mOuter = context.getSystemService(StorageManager.class) .openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_ONLY, mCallback); .openProxyFileDescriptor(mode, mCallback); } catch (ErrnoException e) { throw e.rethrowAsIOException(); } Loading @@ -78,16 +82,61 @@ public class RedactingFileDescriptor { /** * Open the given {@link File} and returns a {@link ParcelFileDescriptor} * that offers a redacted, read-only view of the underlying data. * that offers a redacted view of the underlying data. If a redacted region * is written to, the newly written data can be read back correctly instead * of continuing to be redacted. * * @param file The underlying file to open. * @param mode The {@link ParcelFileDescriptor} mode to open with. * @param redactRanges List of file offsets that should be redacted, stored * as {@code [start1, end1, start2, end2, ...]}. Start values are * inclusive and end values are exclusive. */ public static ParcelFileDescriptor open(Context context, File file, long[] redactRanges) throws IOException { return new RedactingFileDescriptor(context, file, redactRanges).mOuter; public static ParcelFileDescriptor open(Context context, File file, int mode, long[] redactRanges) throws IOException { return new RedactingFileDescriptor(context, file, mode, redactRanges).mOuter; } /** * Update the given ranges argument to remove any references to the given * offset and length. This is typically used when a caller has written over * a previously redacted region. */ @VisibleForTesting public static long[] removeRange(long[] ranges, long start, long end) { if (start == end) { return ranges; } else if (start > end) { throw new IllegalArgumentException(); } long[] res = EmptyArray.LONG; for (int i = 0; i < ranges.length; i += 2) { if (start <= ranges[i] && end >= ranges[i + 1]) { // Range entirely covered; remove it } else if (start >= ranges[i] && end <= ranges[i + 1]) { // Range partially covered; punch a hole res = Arrays.copyOf(res, res.length + 4); res[res.length - 4] = ranges[i]; res[res.length - 3] = start; res[res.length - 2] = end; res[res.length - 1] = ranges[i + 1]; } else { // Range might covered; adjust edges if needed res = Arrays.copyOf(res, res.length + 2); if (end >= ranges[i] && end <= ranges[i + 1]) { res[res.length - 2] = Math.max(ranges[i], end); } else { res[res.length - 2] = ranges[i]; } if (start >= ranges[i] && start <= ranges[i + 1]) { res[res.length - 1] = Math.min(ranges[i + 1], start); } else { res[res.length - 1] = ranges[i + 1]; } } } return res; } private final ProxyFileDescriptorCallback mCallback = new ProxyFileDescriptorCallback() { Loading Loading @@ -126,7 +175,24 @@ public class RedactingFileDescriptor { @Override public int onWrite(long offset, int size, byte[] data) throws ErrnoException { throw new ErrnoException(TAG, OsConstants.EBADF); int n = 0; while (n < size) { try { final int res = Os.pwrite(mInner, data, n, size - n, offset + n); if (res == 0) { break; } else { n += res; } } catch (InterruptedIOException e) { n += e.bytesTransferred; } } // Clear any relevant redaction ranges before returning, since the // writer should have access to see the data they just overwrote mRedactRanges = removeRange(mRedactRanges, offset, offset + n); return n; } @Override Loading core/java/android/provider/MediaStore.java +20 −0 Original line number Diff line number Diff line Loading @@ -121,6 +121,8 @@ public final class MediaStore { public static final String PARAM_INCLUDE_PENDING = "includePending"; /** {@hide} */ public static final String PARAM_PROGRESS = "progress"; /** {@hide} */ public static final String PARAM_REQUIRE_ORIGINAL = "requireOriginal"; /** * Activity Action: Launch a music player. Loading Loading @@ -477,6 +479,24 @@ public final class MediaStore { return uri.buildUpon().appendQueryParameter(PARAM_INCLUDE_PENDING, "1").build(); } /** * Update the given {@link Uri} to indicate that the caller requires the * original file contents when calling * {@link ContentResolver#openFileDescriptor(Uri, String)}. * <p> * This can be useful when the caller wants to ensure they're backing up the * exact bytes of the underlying media, without any Exif redaction being * performed. * <p> * If the original file contents cannot be provided, a * {@link UnsupportedOperationException} will be thrown when the returned * {@link Uri} is used, such as when the caller doesn't hold * {@link android.Manifest.permission#ACCESS_MEDIA_LOCATION}. */ public static @NonNull Uri setRequireOriginal(@NonNull Uri uri) { return uri.buildUpon().appendQueryParameter(PARAM_REQUIRE_ORIGINAL, "1").build(); } /** * Create a new pending media item using the given parameters. Pending items * are expected to have a short lifetime, and owners should either Loading Loading
api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -37297,6 +37297,7 @@ package android.provider { method public static java.lang.String getVolumeName(android.net.Uri); method public static android.provider.MediaStore.PendingSession openPending(android.content.Context, android.net.Uri); method public static android.net.Uri setIncludePending(android.net.Uri); method public static android.net.Uri setRequireOriginal(android.net.Uri); field public static final java.lang.String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE"; field public static final java.lang.String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE"; field public static final java.lang.String ACTION_REVIEW = "android.provider.action.REVIEW";
cmds/content/src/com/android/commands/content/Content.java +4 −1 Original line number Diff line number Diff line Loading @@ -77,7 +77,7 @@ public class Content { + " <BINDING> binds a typed value to a column and is formatted:\n" + " <COLUMN_NAME>:<TYPE>:<COLUMN_VALUE> where:\n" + " <TYPE> specifies data type such as:\n" + " b - boolean, s - string, i - integer, l - long, f - float, d - double\n" + " b - boolean, s - string, i - integer, l - long, f - float, d - double, n - null\n" + " Note: Omit the value for passing an empty string, e.g column:s:\n" + " Example:\n" + " # Add \"new_setting\" secure setting with value \"new_value\".\n" Loading Loading @@ -153,6 +153,7 @@ public class Content { private static final String TYPE_LONG = "l"; private static final String TYPE_FLOAT = "f"; private static final String TYPE_DOUBLE = "d"; private static final String TYPE_NULL = "n"; private static final String COLON = ":"; private static final String ARGUMENT_PREFIX = "--"; Loading Loading @@ -410,6 +411,8 @@ public class Content { values.put(column, Long.parseLong(value)); } else if (TYPE_FLOAT.equalsIgnoreCase(type) || TYPE_DOUBLE.equalsIgnoreCase(type)) { values.put(column, Double.parseDouble(value)); } else if (TYPE_NULL.equalsIgnoreCase(type)) { values.putNull(column); } else { throw new IllegalArgumentException("Unsupported type: " + type); } Loading
core/java/android/os/ParcelFileDescriptor.java +10 −8 Original line number Diff line number Diff line Loading @@ -17,12 +17,6 @@ package android.os; import static android.system.OsConstants.AF_UNIX; import static android.system.OsConstants.O_APPEND; import static android.system.OsConstants.O_CREAT; import static android.system.OsConstants.O_RDONLY; import static android.system.OsConstants.O_RDWR; import static android.system.OsConstants.O_TRUNC; import static android.system.OsConstants.O_WRONLY; import static android.system.OsConstants.SEEK_SET; import static android.system.OsConstants.SOCK_SEQPACKET; import static android.system.OsConstants.SOCK_STREAM; Loading Loading @@ -254,8 +248,16 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { } /** {@hide} */ public static ParcelFileDescriptor fromFd( FileDescriptor fd, Handler handler, final OnCloseListener listener) throws IOException { public static ParcelFileDescriptor fromPfd(ParcelFileDescriptor pfd, Handler handler, final OnCloseListener listener) throws IOException { final FileDescriptor original = new FileDescriptor(); original.setInt$(pfd.detachFd()); return fromFd(original, handler, listener); } /** {@hide} */ public static ParcelFileDescriptor fromFd(FileDescriptor fd, Handler handler, final OnCloseListener listener) throws IOException { if (handler == null) { throw new IllegalArgumentException("Handler must not be null"); } Loading
core/java/android/os/RedactingFileDescriptor.java +76 −10 Original line number Diff line number Diff line Loading @@ -20,15 +20,18 @@ import android.content.Context; import android.os.storage.StorageManager; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import libcore.io.IoUtils; import libcore.util.EmptyArray; import java.io.File; import java.io.FileDescriptor; import java.io.IOException; import java.io.InterruptedIOException; import java.util.Arrays; /** * Variant of {@link FileDescriptor} that allows its creator to specify regions Loading @@ -40,20 +43,21 @@ public class RedactingFileDescriptor { private static final String TAG = "RedactingFileDescriptor"; private static final boolean DEBUG = true; private final long[] mRedactRanges; private volatile long[] mRedactRanges; private FileDescriptor mInner = null; private ParcelFileDescriptor mOuter = null; private RedactingFileDescriptor(Context context, File file, long[] redactRanges) private RedactingFileDescriptor(Context context, File file, int mode, long[] redactRanges) throws IOException { mRedactRanges = checkRangesArgument(redactRanges); try { try { mInner = Os.open(file.getAbsolutePath(), OsConstants.O_RDONLY, 0); mInner = Os.open(file.getAbsolutePath(), FileUtils.translateModePfdToPosix(mode), 0); mOuter = context.getSystemService(StorageManager.class) .openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_ONLY, mCallback); .openProxyFileDescriptor(mode, mCallback); } catch (ErrnoException e) { throw e.rethrowAsIOException(); } Loading @@ -78,16 +82,61 @@ public class RedactingFileDescriptor { /** * Open the given {@link File} and returns a {@link ParcelFileDescriptor} * that offers a redacted, read-only view of the underlying data. * that offers a redacted view of the underlying data. If a redacted region * is written to, the newly written data can be read back correctly instead * of continuing to be redacted. * * @param file The underlying file to open. * @param mode The {@link ParcelFileDescriptor} mode to open with. * @param redactRanges List of file offsets that should be redacted, stored * as {@code [start1, end1, start2, end2, ...]}. Start values are * inclusive and end values are exclusive. */ public static ParcelFileDescriptor open(Context context, File file, long[] redactRanges) throws IOException { return new RedactingFileDescriptor(context, file, redactRanges).mOuter; public static ParcelFileDescriptor open(Context context, File file, int mode, long[] redactRanges) throws IOException { return new RedactingFileDescriptor(context, file, mode, redactRanges).mOuter; } /** * Update the given ranges argument to remove any references to the given * offset and length. This is typically used when a caller has written over * a previously redacted region. */ @VisibleForTesting public static long[] removeRange(long[] ranges, long start, long end) { if (start == end) { return ranges; } else if (start > end) { throw new IllegalArgumentException(); } long[] res = EmptyArray.LONG; for (int i = 0; i < ranges.length; i += 2) { if (start <= ranges[i] && end >= ranges[i + 1]) { // Range entirely covered; remove it } else if (start >= ranges[i] && end <= ranges[i + 1]) { // Range partially covered; punch a hole res = Arrays.copyOf(res, res.length + 4); res[res.length - 4] = ranges[i]; res[res.length - 3] = start; res[res.length - 2] = end; res[res.length - 1] = ranges[i + 1]; } else { // Range might covered; adjust edges if needed res = Arrays.copyOf(res, res.length + 2); if (end >= ranges[i] && end <= ranges[i + 1]) { res[res.length - 2] = Math.max(ranges[i], end); } else { res[res.length - 2] = ranges[i]; } if (start >= ranges[i] && start <= ranges[i + 1]) { res[res.length - 1] = Math.min(ranges[i + 1], start); } else { res[res.length - 1] = ranges[i + 1]; } } } return res; } private final ProxyFileDescriptorCallback mCallback = new ProxyFileDescriptorCallback() { Loading Loading @@ -126,7 +175,24 @@ public class RedactingFileDescriptor { @Override public int onWrite(long offset, int size, byte[] data) throws ErrnoException { throw new ErrnoException(TAG, OsConstants.EBADF); int n = 0; while (n < size) { try { final int res = Os.pwrite(mInner, data, n, size - n, offset + n); if (res == 0) { break; } else { n += res; } } catch (InterruptedIOException e) { n += e.bytesTransferred; } } // Clear any relevant redaction ranges before returning, since the // writer should have access to see the data they just overwrote mRedactRanges = removeRange(mRedactRanges, offset, offset + n); return n; } @Override Loading
core/java/android/provider/MediaStore.java +20 −0 Original line number Diff line number Diff line Loading @@ -121,6 +121,8 @@ public final class MediaStore { public static final String PARAM_INCLUDE_PENDING = "includePending"; /** {@hide} */ public static final String PARAM_PROGRESS = "progress"; /** {@hide} */ public static final String PARAM_REQUIRE_ORIGINAL = "requireOriginal"; /** * Activity Action: Launch a music player. Loading Loading @@ -477,6 +479,24 @@ public final class MediaStore { return uri.buildUpon().appendQueryParameter(PARAM_INCLUDE_PENDING, "1").build(); } /** * Update the given {@link Uri} to indicate that the caller requires the * original file contents when calling * {@link ContentResolver#openFileDescriptor(Uri, String)}. * <p> * This can be useful when the caller wants to ensure they're backing up the * exact bytes of the underlying media, without any Exif redaction being * performed. * <p> * If the original file contents cannot be provided, a * {@link UnsupportedOperationException} will be thrown when the returned * {@link Uri} is used, such as when the caller doesn't hold * {@link android.Manifest.permission#ACCESS_MEDIA_LOCATION}. */ public static @NonNull Uri setRequireOriginal(@NonNull Uri uri) { return uri.buildUpon().appendQueryParameter(PARAM_REQUIRE_ORIGINAL, "1").build(); } /** * Create a new pending media item using the given parameters. Pending items * are expected to have a short lifetime, and owners should either Loading