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

Commit 7857d5ab authored by MingWei Liao's avatar MingWei Liao Committed by Android (Google) Code Review
Browse files

Merge "Add AppFunctionUriGrant API" into main

parents e7864abe 54e7036e
Loading
Loading
Loading
Loading
+11 −0
Original line number Original line Diff line number Diff line
@@ -9111,6 +9111,15 @@ package android.app.appfunctions {
    field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
    field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
  }
  }
  @FlaggedApi("android.permission.flags.app_function_access_api_enabled") public final class AppFunctionUriGrant implements android.os.Parcelable {
    ctor public AppFunctionUriGrant(@NonNull android.net.Uri, int);
    method public int describeContents();
    method public int getModeFlags();
    method @NonNull public android.net.Uri getUri();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.app.appfunctions.AppFunctionUriGrant> CREATOR;
  }
  @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class ExecuteAppFunctionRequest implements android.os.Parcelable {
  @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class ExecuteAppFunctionRequest implements android.os.Parcelable {
    method public int describeContents();
    method public int describeContents();
    method @FlaggedApi("android.permission.flags.app_function_access_api_enabled") @Nullable public android.app.appfunctions.AppFunctionAttribution getAttribution();
    method @FlaggedApi("android.permission.flags.app_function_access_api_enabled") @Nullable public android.app.appfunctions.AppFunctionAttribution getAttribution();
@@ -9133,9 +9142,11 @@ package android.app.appfunctions {
  @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class ExecuteAppFunctionResponse implements android.os.Parcelable {
  @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class ExecuteAppFunctionResponse implements android.os.Parcelable {
    ctor public ExecuteAppFunctionResponse(@NonNull android.app.appsearch.GenericDocument);
    ctor public ExecuteAppFunctionResponse(@NonNull android.app.appsearch.GenericDocument);
    ctor public ExecuteAppFunctionResponse(@NonNull android.app.appsearch.GenericDocument, @NonNull android.os.Bundle);
    ctor public ExecuteAppFunctionResponse(@NonNull android.app.appsearch.GenericDocument, @NonNull android.os.Bundle);
    ctor @FlaggedApi("android.permission.flags.app_function_access_api_enabled") public ExecuteAppFunctionResponse(@NonNull android.app.appsearch.GenericDocument, @NonNull android.os.Bundle, @NonNull java.util.List<android.app.appfunctions.AppFunctionUriGrant>);
    method public int describeContents();
    method public int describeContents();
    method @NonNull public android.os.Bundle getExtras();
    method @NonNull public android.os.Bundle getExtras();
    method @NonNull public android.app.appsearch.GenericDocument getResultDocument();
    method @NonNull public android.app.appsearch.GenericDocument getResultDocument();
    method @FlaggedApi("android.permission.flags.app_function_access_api_enabled") @NonNull public java.util.List<android.app.appfunctions.AppFunctionUriGrant> getUriGrants();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.app.appfunctions.ExecuteAppFunctionResponse> CREATOR;
    field @NonNull public static final android.os.Parcelable.Creator<android.app.appfunctions.ExecuteAppFunctionResponse> CREATOR;
    field public static final String PROPERTY_RETURN_VALUE = "androidAppfunctionsReturnValue";
    field public static final String PROPERTY_RETURN_VALUE = "androidAppfunctionsReturnValue";
+181 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.app.appfunctions;

import static android.content.Intent.FLAG_GRANT_PREFIX_URI_PERMISSION;
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;

import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.app.appsearch.GenericDocument;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.permission.flags.Flags;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Objects;

/**
 * Represents a {@link android.net.Uri} for which temporary access permission is to be granted to
 * the caller of an AppFunction execution.
 *
 * <p>This class encapsulates a {@link android.net.Uri} along with the specific access mode flags
 * (e.g., {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION}) that define the type of
 * temporary access to be granted for that URI. However, {@link
 * android.content.Intent#FLAG_GRANT_PERSISTABLE_URI_PERMISSION} is not allowed as only the
 * temporary access can be granted.
 *
 * <p>When an AppFunction implementation returns an {@link ExecuteAppFunctionResponse} containing
 * a {@link Uri}, the {@link Uri} itself must be placed in either
 * {@link ExecuteAppFunctionResponse#getResultDocument()} ()} or
 * {@link ExecuteAppFunctionResponse#getExtras()}. Concurrently, a corresponding
 * {@link AppFunctionUriGrant} detailing the intended permissions must be added to
 * {@link ExecuteAppFunctionResponse#getUriGrants()}. This ensures the App Function's caller
 * receives the necessary access rights to the returned {@link Uri}.
 *
 * <p>To succeed, the content provider owning the Uri must have set the {@link
 * android.R.styleable#AndroidManifestProvider_grantUriPermissions grantUriPermissions} attribute in
 * its manifest or included the {@link android.R.styleable#AndroidManifestGrantUriPermission
 * &lt;grant-uri-permissions&gt;} tag.
 *
 * @see ExecuteAppFunctionResponse#ExecuteAppFunctionResponse(GenericDocument, Bundle, List)
 * @see android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION
 * @see android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION
 * @see android.content.Intent#FLAG_GRANT_PREFIX_URI_PERMISSION
 */
@FlaggedApi(Flags.FLAG_APP_FUNCTION_ACCESS_API_ENABLED)
public final class AppFunctionUriGrant implements Parcelable {
    private static final int ALLOWED_MODE_FLAG_MASK = FLAG_GRANT_READ_URI_PERMISSION
            | FLAG_GRANT_WRITE_URI_PERMISSION
            | FLAG_GRANT_PREFIX_URI_PERMISSION;

    @NonNull
    public static final Creator<AppFunctionUriGrant> CREATOR =
            new Creator<AppFunctionUriGrant>() {
                @Override
                public AppFunctionUriGrant createFromParcel(Parcel parcel) {
                    final Uri uri = Objects.requireNonNull(Uri.CREATOR.createFromParcel(parcel));
                    final int modeFlags = parcel.readInt();
                    return new AppFunctionUriGrant(uri, modeFlags);
                }

                @Override
                public AppFunctionUriGrant[] newArray(int size) {
                    return new AppFunctionUriGrant[size];
                }
            };

    /** The {@link android.net.Uri} to be granted. */
    @NonNull private final Uri mUri;

    /** The access mode flags. */
    @GrantUriMode private final int mModeFlags;

    /** @hide */
    @IntDef(
            flag = true,
            prefix = {"FLAG_GRANT_"},
            value = {
                FLAG_GRANT_READ_URI_PERMISSION,
                FLAG_GRANT_WRITE_URI_PERMISSION,
                FLAG_GRANT_PREFIX_URI_PERMISSION
            })
    @Retention(RetentionPolicy.SOURCE)
    public @interface GrantUriMode {}

    /**
     * Create a new {@link AppFunctionUriGrant}
     *
     * @param uri The {@link Uri} to be granted.
     * @param modeFlags The access mode flags.
     *     This value must include at least one of
     *     {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION} or
     *     {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION}.
     *     It may optionally also include
     *     {@link android.content.Intent#FLAG_GRANT_PREFIX_URI_PERMISSION}.
     */
    public AppFunctionUriGrant(@NonNull Uri uri, @GrantUriMode int modeFlags) {
        mUri = Objects.requireNonNull(uri);
        if ((modeFlags & ~ALLOWED_MODE_FLAG_MASK) != 0) {
            throw new IllegalArgumentException(
                    "Contains invalid flags: Allowed flags are FLAG_GRANT_READ_URI_PERMISSION, "
                            + "FLAG_GRANT_WRITE_URI_PERMISSION and "
                            + "FLAG_GRANT_PREFIX_URI_PERMISSION");
        }
        if (!Intent.isAccessUriMode(modeFlags)) {
            throw new IllegalArgumentException(
                    "Must set either FLAG_GRANT_READ_URI_PERMISSION or "
                            + "FLAG_GRANT_WRITE_URI_PERMISSION to specify the access mode");
        }
        mModeFlags = modeFlags;
    }

    /** Return the {@link Uri} to be granted. */
    @NonNull
    public Uri getUri() {
        return mUri;
    }

    /** Return the access mode flags. */
    @GrantUriMode
    public int getModeFlags() {
        return mModeFlags;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        AppFunctionUriGrant that = (AppFunctionUriGrant) o;
        return mModeFlags == that.mModeFlags && mUri.equals(that.mUri);
    }

    @Override
    public int hashCode() {
        return Objects.hash(mUri, mModeFlags);
    }

    @NonNull
    @Override
    public String toString() {
        return "AppFunctionGrantUri("
                + "uri="
                + mUri.toString()
                + ","
                + "modeFlags"
                + mModeFlags
                + ")";
    }

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        Uri.writeToParcel(dest, mUri);
        dest.writeInt(mModeFlags);
    }

    @Override
    public int describeContents() {
        return 0;
    }
}
+48 −1
Original line number Original line Diff line number Diff line
@@ -24,7 +24,10 @@ import android.app.appsearch.GenericDocument;
import android.os.Bundle;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Parcelable;
import android.permission.flags.Flags;


import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Objects;


/**
/**
@@ -50,8 +53,16 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
                    Bundle extras =
                    Bundle extras =
                            Objects.requireNonNull(
                            Objects.requireNonNull(
                                    parcel.readBundle(Bundle.class.getClassLoader()));
                                    parcel.readBundle(Bundle.class.getClassLoader()));
                    if (Flags.appFunctionAccessApiEnabled()) {
                        List<AppFunctionUriGrant> uriGrants =
                                Objects.requireNonNull(
                                        parcel.createTypedArrayList(AppFunctionUriGrant.CREATOR));
                        return new ExecuteAppFunctionResponse(
                                resultWrapper.getValue(), extras, uriGrants);
                    } else {
                        return new ExecuteAppFunctionResponse(resultWrapper.getValue(), extras);
                        return new ExecuteAppFunctionResponse(resultWrapper.getValue(), extras);
                    }
                    }
                }


                @Override
                @Override
                public ExecuteAppFunctionResponse[] newArray(int size) {
                public ExecuteAppFunctionResponse[] newArray(int size) {
@@ -88,6 +99,12 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
    /** Returns the additional metadata data relevant to this function execution response. */
    /** Returns the additional metadata data relevant to this function execution response. */
    @NonNull private final Bundle mExtras;
    @NonNull private final Bundle mExtras;


    /**
     * The list of {@link AppFunctionUriGrant} to which the caller of this
     * app function execution should have temporary access granted.
     */
    @NonNull private final List<AppFunctionUriGrant> mUriGrants;

    /**
    /**
     * @param resultDocument The return value of the executed function.
     * @param resultDocument The return value of the executed function.
     */
     */
@@ -103,6 +120,23 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
            @NonNull GenericDocument resultDocument, @NonNull Bundle extras) {
            @NonNull GenericDocument resultDocument, @NonNull Bundle extras) {
        mResultDocumentWrapper = new GenericDocumentWrapper(Objects.requireNonNull(resultDocument));
        mResultDocumentWrapper = new GenericDocumentWrapper(Objects.requireNonNull(resultDocument));
        mExtras = Objects.requireNonNull(extras);
        mExtras = Objects.requireNonNull(extras);
        mUriGrants = Collections.emptyList();
    }

    /**
     * @param resultDocument The return value of the executed function.
     * @param extras The additional metadata for this function execution response.
     * @param uriGrants The list of {@link AppFunctionUriGrant} to which
     *     the caller of this app function execution should have temporary access granted.
     */
    @FlaggedApi(Flags.FLAG_APP_FUNCTION_ACCESS_API_ENABLED)
    public ExecuteAppFunctionResponse(
            @NonNull GenericDocument resultDocument,
            @NonNull Bundle extras,
            @NonNull List<AppFunctionUriGrant> uriGrants) {
        mResultDocumentWrapper = new GenericDocumentWrapper(Objects.requireNonNull(resultDocument));
        mExtras = Objects.requireNonNull(extras);
        mUriGrants = Objects.requireNonNull(uriGrants);
    }
    }


    /**
    /**
@@ -135,6 +169,16 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
        return mExtras;
        return mExtras;
    }
    }


    /**
     * The list of {@link AppFunctionUriGrant} to which the caller of this
     * app function execution should have temporary access granted.
     */
    @FlaggedApi(Flags.FLAG_APP_FUNCTION_ACCESS_API_ENABLED)
    @NonNull
    public List<AppFunctionUriGrant> getUriGrants() {
        return mUriGrants;
    }

    /**
    /**
     * Returns the size of the response in bytes.
     * Returns the size of the response in bytes.
     *
     *
@@ -153,5 +197,8 @@ public final class ExecuteAppFunctionResponse implements Parcelable {
    public void writeToParcel(@NonNull Parcel dest, int flags) {
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        mResultDocumentWrapper.writeToParcel(dest, flags);
        mResultDocumentWrapper.writeToParcel(dest, flags);
        dest.writeBundle(mExtras);
        dest.writeBundle(mExtras);
        if (Flags.appFunctionAccessApiEnabled()) {
            dest.writeTypedList(mUriGrants, flags);
        }
    }
    }
}
}