Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 07323a98 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add structured sort data to ContentResolver.query."

parents a9ef7f47 29c3f68c
Loading
Loading
Loading
Loading
+11 −3
Original line number Diff line number Diff line
@@ -8202,9 +8202,14 @@ package android.content {
    field public static final java.lang.String EXTRA_SIZE = "android.content.extra.SIZE";
    field public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 2; // 0x2
    field public static final int NOTIFY_SYNC_TO_NETWORK = 1; // 0x1
    field public static final java.lang.String QUERY_ARG_SELECTION = "android:query-selection";
    field public static final java.lang.String QUERY_ARG_SELECTION_ARGS = "android:query-selection-args";
    field public static final java.lang.String QUERY_ARG_SORT_ORDER = "android:query-sort-order";
    field public static final java.lang.String QUERY_ARG_SORT_COLLATION = "android:query-sort-collation";
    field public static final java.lang.String QUERY_ARG_SORT_COLUMNS = "android:query-sort-columns";
    field public static final java.lang.String QUERY_ARG_SORT_DIRECTION = "android:query-sort-direction";
    field public static final java.lang.String QUERY_ARG_SQL_SELECTION = "android:query-sql-selection";
    field public static final java.lang.String QUERY_ARG_SQL_SELECTION_ARGS = "android:query-sql-selection-args";
    field public static final java.lang.String QUERY_ARG_SQL_SORT_ORDER = "android:query-sql-sort-order";
    field public static final int QUERY_SORT_DIRECTION_ASCENDING = 0; // 0x0
    field public static final int QUERY_SORT_DIRECTION_DESCENDING = 1; // 0x1
    field public static final java.lang.String SCHEME_ANDROID_RESOURCE = "android.resource";
    field public static final java.lang.String SCHEME_CONTENT = "content";
    field public static final java.lang.String SCHEME_FILE = "file";
@@ -32689,7 +32694,10 @@ package android.provider {
    method public final android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
    method public android.content.res.AssetFileDescriptor openTypedDocument(java.lang.String, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
    method public abstract android.database.Cursor queryChildDocuments(java.lang.String, java.lang.String[], java.lang.String) throws java.io.FileNotFoundException;
    method public android.database.Cursor queryChildDocuments(java.lang.String, java.lang.String[], android.os.Bundle) throws java.io.FileNotFoundException;
    method public abstract android.database.Cursor queryDocument(java.lang.String, java.lang.String[]) throws java.io.FileNotFoundException;
    method public android.database.Cursor queryRecentDocuments(java.lang.String, java.lang.String[]) throws java.io.FileNotFoundException;
    method public abstract android.database.Cursor queryRoots(java.lang.String[]) throws java.io.FileNotFoundException;
+11 −3
Original line number Diff line number Diff line
@@ -8552,9 +8552,14 @@ package android.content {
    field public static final java.lang.String EXTRA_SIZE = "android.content.extra.SIZE";
    field public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 2; // 0x2
    field public static final int NOTIFY_SYNC_TO_NETWORK = 1; // 0x1
    field public static final java.lang.String QUERY_ARG_SELECTION = "android:query-selection";
    field public static final java.lang.String QUERY_ARG_SELECTION_ARGS = "android:query-selection-args";
    field public static final java.lang.String QUERY_ARG_SORT_ORDER = "android:query-sort-order";
    field public static final java.lang.String QUERY_ARG_SORT_COLLATION = "android:query-sort-collation";
    field public static final java.lang.String QUERY_ARG_SORT_COLUMNS = "android:query-sort-columns";
    field public static final java.lang.String QUERY_ARG_SORT_DIRECTION = "android:query-sort-direction";
    field public static final java.lang.String QUERY_ARG_SQL_SELECTION = "android:query-sql-selection";
    field public static final java.lang.String QUERY_ARG_SQL_SELECTION_ARGS = "android:query-sql-selection-args";
    field public static final java.lang.String QUERY_ARG_SQL_SORT_ORDER = "android:query-sql-sort-order";
    field public static final int QUERY_SORT_DIRECTION_ASCENDING = 0; // 0x0
    field public static final int QUERY_SORT_DIRECTION_DESCENDING = 1; // 0x1
    field public static final java.lang.String SCHEME_ANDROID_RESOURCE = "android.resource";
    field public static final java.lang.String SCHEME_CONTENT = "content";
    field public static final java.lang.String SCHEME_FILE = "file";
@@ -35445,7 +35450,10 @@ package android.provider {
    method public final android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
    method public android.content.res.AssetFileDescriptor openTypedDocument(java.lang.String, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
    method public abstract android.database.Cursor queryChildDocuments(java.lang.String, java.lang.String[], java.lang.String) throws java.io.FileNotFoundException;
    method public android.database.Cursor queryChildDocuments(java.lang.String, java.lang.String[], android.os.Bundle) throws java.io.FileNotFoundException;
    method public abstract android.database.Cursor queryDocument(java.lang.String, java.lang.String[]) throws java.io.FileNotFoundException;
    method public android.database.Cursor queryRecentDocuments(java.lang.String, java.lang.String[]) throws java.io.FileNotFoundException;
    method public abstract android.database.Cursor queryRoots(java.lang.String[]) throws java.io.FileNotFoundException;
+11 −3
Original line number Diff line number Diff line
@@ -8225,9 +8225,14 @@ package android.content {
    field public static final java.lang.String EXTRA_SIZE = "android.content.extra.SIZE";
    field public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 2; // 0x2
    field public static final int NOTIFY_SYNC_TO_NETWORK = 1; // 0x1
    field public static final java.lang.String QUERY_ARG_SELECTION = "android:query-selection";
    field public static final java.lang.String QUERY_ARG_SELECTION_ARGS = "android:query-selection-args";
    field public static final java.lang.String QUERY_ARG_SORT_ORDER = "android:query-sort-order";
    field public static final java.lang.String QUERY_ARG_SORT_COLLATION = "android:query-sort-collation";
    field public static final java.lang.String QUERY_ARG_SORT_COLUMNS = "android:query-sort-columns";
    field public static final java.lang.String QUERY_ARG_SORT_DIRECTION = "android:query-sort-direction";
    field public static final java.lang.String QUERY_ARG_SQL_SELECTION = "android:query-sql-selection";
    field public static final java.lang.String QUERY_ARG_SQL_SELECTION_ARGS = "android:query-sql-selection-args";
    field public static final java.lang.String QUERY_ARG_SQL_SORT_ORDER = "android:query-sql-sort-order";
    field public static final int QUERY_SORT_DIRECTION_ASCENDING = 0; // 0x0
    field public static final int QUERY_SORT_DIRECTION_DESCENDING = 1; // 0x1
    field public static final java.lang.String SCHEME_ANDROID_RESOURCE = "android.resource";
    field public static final java.lang.String SCHEME_CONTENT = "content";
    field public static final java.lang.String SCHEME_FILE = "file";
@@ -32802,7 +32807,10 @@ package android.provider {
    method public final android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
    method public android.content.res.AssetFileDescriptor openTypedDocument(java.lang.String, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
    method public abstract android.database.Cursor queryChildDocuments(java.lang.String, java.lang.String[], java.lang.String) throws java.io.FileNotFoundException;
    method public android.database.Cursor queryChildDocuments(java.lang.String, java.lang.String[], android.os.Bundle) throws java.io.FileNotFoundException;
    method public abstract android.database.Cursor queryDocument(java.lang.String, java.lang.String[]) throws java.io.FileNotFoundException;
    method public android.database.Cursor queryRecentDocuments(java.lang.String, java.lang.String[]) throws java.io.FileNotFoundException;
    method public abstract android.database.Cursor queryRoots(java.lang.String[]) throws java.io.FileNotFoundException;
+26 −8
Original line number Diff line number Diff line
@@ -915,7 +915,12 @@ public abstract class ContentProvider implements ComponentCallbacks2 {

    /**
     * Implement this to handle query requests from clients.
     * This method can be called from multiple threads, as described in
     *
     * <p>Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher should override
     * {@link #query(Uri, String[], Bundle, CancellationSignal)} and provide a stub
     * implementation of this method.
     *
     * <p>This method can be called from multiple threads, as described in
     * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
     * and Threads</a>.
     * <p>
@@ -974,7 +979,11 @@ public abstract class ContentProvider implements ComponentCallbacks2 {

    /**
     * Implement this to handle query requests from clients with support for cancellation.
     * This method can be called from multiple threads, as described in
     *
     * <p>Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher should override
     * {@link #query(Uri, String[], Bundle, CancellationSignal)} instead of this method.
     *
     * <p>This method can be called from multiple threads, as described in
     * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
     * and Threads</a>.
     * <p>
@@ -1048,9 +1057,9 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
     * {@link #query(Uri, String[], String, String[], String, CancellationSignal).
     *
     * <p>Traditional SQL arguments can be found in the bundle using the following keys:
     * <li>{@link ContentResolver#QUERY_ARG_SELECTION}
     * <li>{@link ContentResolver#QUERY_ARG_SELECTION_ARGS}
     * <li>{@link ContentResolver#QUERY_ARG_SORT_ORDER}
     * <li>{@link ContentResolver#QUERY_ARG_SQL_SELECTION}
     * <li>{@link ContentResolver#QUERY_ARG_SQL_SELECTION_ARGS}
     * <li>{@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER}
     *
     * @see #query(Uri, String[], String, String[], String, CancellationSignal) for
     *     implementation details.
@@ -1071,12 +1080,21 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
    public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
            @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) {
        queryArgs = queryArgs != null ? queryArgs : Bundle.EMPTY;

        String sortClause = queryArgs.getString(ContentResolver.QUERY_ARG_SQL_SORT_ORDER);

        // if client didn't explicitly supply and sql sort order argument, we try to build
        // one from sort columns if present.
        if (sortClause == null && queryArgs.containsKey(ContentResolver.QUERY_ARG_SORT_COLUMNS)) {
            sortClause = ContentResolver.createSqlSortClause(queryArgs);
        }

        return query(
                uri,
                projection,
                queryArgs.getString(ContentResolver.QUERY_ARG_SELECTION),
                queryArgs.getStringArray(ContentResolver.QUERY_ARG_SELECTION_ARGS),
                queryArgs.getString(ContentResolver.QUERY_ARG_SORT_ORDER),
                queryArgs.getString(ContentResolver.QUERY_ARG_SQL_SELECTION),
                queryArgs.getStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS),
                sortClause,
                cancellationSignal);
    }

+140 −10
Original line number Diff line number Diff line
@@ -205,21 +205,111 @@ public abstract class ContentResolver {
     * Key for an SQL style selection string that may be present in the query Bundle argument
     * passed to {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)}
     * when called by a legacy client.
     *
     * <p>Clients should never include user supplied values directly in the selection string,
     * as this presents an avenue for SQL injection attacks. In lieu of this, a client
     * should use standard placeholder notation to represent values in a selection string,
     * then supply a corresponding value in {@value #QUERY_ARG_SQL_SELECTION_ARGS}.
     *
     * <p><b>Clients targeting Android O or higher are strongly encourage to use structured
     * query arguments in lieu of opaque SQL query clauses.</b> See:
     * {@link #QUERY_ARG_SORT_COLUMNS}, {@link #QUERY_ARG_SORT_DIRECTION}, and
     * {@link #QUERY_ARG_SORT_COLLATION}.
     */
    public static final String QUERY_ARG_SELECTION = "android:query-selection";
    public static final String QUERY_ARG_SQL_SELECTION = "android:query-sql-selection";

    /**
     * Key for sql selection string arguments list.
     * @see #QUERY_ARG_SELECTION
     * Key for SQL selection string arguments list.
     *
     * <p>Clients should never include user supplied values directly in the selection string,
     * as this presents an avenue for SQL injection attacks. In lieu of this, a client
     * should use standard placeholder notation to represent values in a selection string,
     * then supply a corresponding value in {@value #QUERY_ARG_SQL_SELECTION_ARGS}.
     *
     * <p><b>Clients targeting Android O or higher are strongly encourage to use structured
     * query arguments in lieu of opaque SQL query clauses.</b> See:
     * {@link #QUERY_ARG_SORT_COLUMNS}, {@link #QUERY_ARG_SORT_DIRECTION}, and
     * {@link #QUERY_ARG_SORT_COLLATION}.
     */
    public static final String QUERY_ARG_SELECTION_ARGS = "android:query-selection-args";
    public static final String QUERY_ARG_SQL_SELECTION_ARGS = "android:query-sql-selection-args";

    /**
     * Key for an SQL style sort string that may be present in the query Bundle argument
     * passed to {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)}
     * when called by a legacy client.
     *
     * <p><b>Clients targeting Android O or higher are strongly encourage to use structured
     * query arguments in lieu of opaque SQL query clauses.</b> See:
     * {@link #QUERY_ARG_SORT_COLUMNS}, {@link #QUERY_ARG_SORT_DIRECTION}, and
     * {@link #QUERY_ARG_SORT_COLLATION}.
     */
    public static final String QUERY_ARG_SQL_SORT_ORDER = "android:query-sql-sort-order";

    /**
     * Identifies the list columns against which to sort results.
     *
     * <p>Columns present in this list must also be included in the projection
     * supplied to {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}.
     *
     * <p>Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher are strongly
     * encouraged to include an entry in Cursor extras under this same key as an indication
     * to the client that column sorting was honored.
     *
     * <p>QUERY_SORT* values are exclusive from QUERY_ARG_SQL* arguments.
     * When any QUERY_SORT arguments are present, any QUERY_ARG_SQL* values will be ignored.
     */
    public static final String QUERY_ARG_SORT_COLUMNS = "android:query-sort-columns";

    /**
     * Specifies desired sort order. When unspecified a provider may provide a default
     * sort direction, or choose to return unsorted results.
     *
     * <p>Apps targeting {@link android.os.Build.VERSION_CODES#O} or higher are strongly
     * encouraged to include an entry in Cursor extras under this same key as an indication
     * to the client that sort direction was honored.
     *
     * @see #QUERY_SORT_DIRECTION_ASCENDING
     * @see #QUERY_SORT_DIRECTION_DESCENDING
     */
    public static final String QUERY_ARG_SORT_DIRECTION = "android:query-sort-direction";

    /**
     * Allows client to specify a hint to the provider as to which collation
     * to use when sorting text values.
     *
     * <p>Providers may provide their own collators. When selecting a custom collator
     * the value will be determined by the Provider.
     *
     * <p>apps targeting {@link android.os.Build.VERSION_CODES#O} or higher are strongly
     * encouraged to include an entry in Cursor extras under this same key as an indication
     * to the client that collation was honored.
     *
     * @see #QUERY_COLLATOR_MODE_NOCASE
     */
    public static final String QUERY_ARG_SORT_COLLATION = "android:query-sort-collation";

    /** @hide */
    @IntDef(flag = false, value = {
            QUERY_SORT_DIRECTION_ASCENDING,
            QUERY_SORT_DIRECTION_DESCENDING
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface SortDirection {}
    public static final int QUERY_SORT_DIRECTION_ASCENDING = 0;
    public static final int QUERY_SORT_DIRECTION_DESCENDING = 1;

    /**
     * @see {@link java.text.Collector} for details on respective collation strength.
     * @hide
     */
    public static final String QUERY_ARG_SORT_ORDER = "android:query-sort-order";
    @IntDef(flag = false, value = {
            java.text.Collator.PRIMARY,
            java.text.Collator.SECONDARY,
            java.text.Collator.TERTIARY,
            java.text.Collator.IDENTICAL
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface QueryCollator {}

    /**
     * This is the Android platform's base MIME type for a content: URI
@@ -2685,8 +2775,8 @@ public abstract class ContentResolver {
            EventLogTags.CONTENT_QUERY_SAMPLE,
            uri.toString(),
            projectionBuffer.toString(),
            queryArgs.getString(QUERY_ARG_SELECTION, ""),
            queryArgs.getString(QUERY_ARG_SORT_ORDER, ""),
            queryArgs.getString(QUERY_ARG_SQL_SELECTION, ""),
            queryArgs.getString(QUERY_ARG_SQL_SORT_ORDER, ""),
            durationMillis,
            blockingPackage != null ? blockingPackage : "",
            samplePercent);
@@ -2815,14 +2905,54 @@ public abstract class ContentResolver {

        Bundle queryArgs = new Bundle();
        if (selection != null) {
            queryArgs.putString(QUERY_ARG_SELECTION, selection);
            queryArgs.putString(QUERY_ARG_SQL_SELECTION, selection);
        }
        if (selectionArgs != null) {
            queryArgs.putStringArray(QUERY_ARG_SELECTION_ARGS, selectionArgs);
            queryArgs.putStringArray(QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs);
        }
        if (sortOrder != null) {
            queryArgs.putString(QUERY_ARG_SORT_ORDER, sortOrder);
            queryArgs.putString(QUERY_ARG_SQL_SORT_ORDER, sortOrder);
        }
        return queryArgs;
    }

    /**
     * Returns structured sort args formatted as an SQL sort clause.
     *
     * Collator clauses are not included as column information is unknown, and
     * collate clauses should only be included on text fields.
     *
     * TODO: Should we explicitly validate that colums are present in the projection?
     *
     * @hide
     */
    public static String createSqlSortClause(Bundle queryArgs) {
        String[] columns = queryArgs.getStringArray(QUERY_ARG_SORT_COLUMNS);
        if (columns == null || columns.length == 0) {
            throw new IllegalArgumentException("Can't create sort clause without columns.");
        }

        String query = TextUtils.join(", ", columns);

        switch (queryArgs.getInt(
                QUERY_ARG_SORT_DIRECTION, Integer.MIN_VALUE)) {
            case QUERY_SORT_DIRECTION_ASCENDING:
                query += " ASC";
                break;
            case QUERY_SORT_DIRECTION_DESCENDING:
                query += " DESC";
                break;
            default:
                throw new IllegalArgumentException("Unsupported sort direction value."
                        + " See ContentResolver documentation for details.");
        }

        // Interpret PRIMARY collation strength as no-case collation.
        int collation = queryArgs.getInt(
                ContentResolver.QUERY_ARG_SORT_COLLATION, java.text.Collator.IDENTICAL);
        if (collation == java.text.Collator.PRIMARY || collation == java.text.Collator.SECONDARY) {
            query += " COLLATE NOCASE";
        }
        return query;
    }
}
Loading