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

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

Merge "Update AppFunction logs" into main

parents c6b1db22 27cdab49
Loading
Loading
Loading
Loading
+32 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.appfunctions;
import static com.android.server.appfunctions.AppFunctionExecutors.LOGGING_THREAD_EXECUTOR;

import android.annotation.NonNull;
import android.app.appfunctions.AppFunctionAttribution;
import android.app.appfunctions.ExecuteAppFunctionAidlRequest;
import android.app.appfunctions.ExecuteAppFunctionResponse;
import android.content.Context;
@@ -37,6 +38,11 @@ public class AppFunctionsLoggerWrapper {

    @VisibleForTesting static final int SUCCESS_RESPONSE_CODE = -1;

    @VisibleForTesting static final int INTERACTION_TYPE_UNSPECIFIED = 0;
    @VisibleForTesting static final int INTERACTION_TYPE_OTHER = 1;
    @VisibleForTesting static final int INTERACTION_TYPE_USER_QUERY = 2;
    @VisibleForTesting static final int INTERACTION_TYPE_USER_SCHEDULED = 3;

    private final PackageManager mPackageManager;
    private final Executor mLoggingExecutor;
    private final AppFunctionsLoggerClock mLoggerClock;
@@ -105,7 +111,8 @@ public class AppFunctionsLoggerWrapper {
                                        .getRequestDataSize(),
                                /* responseSizeBytes= */ responseSizeBytes,
                                /* requestDurationMs= */ e2eRequestLatencyMillis,
                                /* requestOverheadMs= */ requestOverheadMillis));
                                /* requestOverheadMs= */ requestOverheadMillis,
                                /* interactionType= */ getInteractionType(request)));
    }

    private int getPackageUid(String packageName) {
@@ -117,6 +124,30 @@ public class AppFunctionsLoggerWrapper {
        return 0;
    }

    private int getInteractionType(@NonNull ExecuteAppFunctionAidlRequest aidlRequest) {
        if (!accessCheckFlagsEnabled()) {
            return INTERACTION_TYPE_UNSPECIFIED;
        }

        final AppFunctionAttribution attribution = aidlRequest.getClientRequest().getAttribution();
        if (attribution == null) {
            return INTERACTION_TYPE_UNSPECIFIED;
        }
        final int interactionType = attribution.getInteractionType();
        return switch (interactionType) {
            case AppFunctionAttribution.INTERACTION_TYPE_OTHER -> INTERACTION_TYPE_OTHER;
            case AppFunctionAttribution.INTERACTION_TYPE_USER_QUERY -> INTERACTION_TYPE_USER_QUERY;
            case AppFunctionAttribution.INTERACTION_TYPE_USER_SCHEDULED ->
                    INTERACTION_TYPE_USER_SCHEDULED;
            default -> INTERACTION_TYPE_UNSPECIFIED;
        };
    }

    private boolean accessCheckFlagsEnabled() {
        return android.permission.flags.Flags.appFunctionAccessApiEnabled()
                && android.permission.flags.Flags.appFunctionAccessServiceEnabled();
    }

    /** Wraps a custom clock for easier testing. */
    interface AppFunctionsLoggerClock {
        long getCurrentTimeMillis();
+213 −22
Original line number Diff line number Diff line
@@ -17,18 +17,22 @@ package com.android.server.appfunctions

import android.app.IUriGrantsManager
import android.app.appfunctions.AppFunctionAccessServiceInterface
import android.app.appfunctions.AppFunctionAttribution
import android.app.appfunctions.AppFunctionException
import android.app.appfunctions.ExecuteAppFunctionAidlRequest
import android.app.appfunctions.ExecuteAppFunctionRequest
import android.app.appfunctions.ExecuteAppFunctionResponse
import android.app.appfunctions.IAppFunctionService
import android.app.appfunctions.IExecuteAppFunctionCallback
import android.app.appfunctions.SafeOneTimeExecuteAppFunctionCallback
import android.app.appsearch.GenericDocument
import android.content.Context
import android.content.pm.PackageManager
import android.content.pm.PackageManagerInternal
import android.os.UserHandle
import android.permission.flags.Flags.FLAG_APP_FUNCTION_ACCESS_API_ENABLED
import android.permission.flags.Flags.FLAG_APP_FUNCTION_ACCESS_SERVICE_ENABLED
import android.platform.test.annotations.RequiresFlagsDisabled
import android.platform.test.annotations.RequiresFlagsEnabled
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
@@ -64,7 +68,6 @@ class AppFunctionsLoggingTest {
            MoreExecutors.directExecutor(),
            { TEST_CURRENT_TIME_MILLIS },
        )
    private lateinit var mSafeCallback: SafeOneTimeExecuteAppFunctionCallback

    private val mServiceImpl =
        AppFunctionManagerServiceImpl(
@@ -81,29 +84,127 @@ class AppFunctionsLoggingTest {
            mock<DeviceSettingHelper>(),
        )

    private val mRequestInternal =
    @Before
    fun setup() {
        whenever(mMockPackageManager.getPackageUid(eq(TEST_TARGET_PACKAGE), any<Int>()))
            .thenReturn(TEST_TARGET_UID)
    }

    @RequiresFlagsDisabled(
        FLAG_APP_FUNCTION_ACCESS_SERVICE_ENABLED,
        FLAG_APP_FUNCTION_ACCESS_API_ENABLED,
    )
    @Test
    fun testOnSuccess_logsSuccessResponse_withoutAttribution() {
        val aidlRequest =
            ExecuteAppFunctionAidlRequest(
                ExecuteAppFunctionRequest.Builder(TEST_TARGET_PACKAGE, TEST_FUNCTION_ID).build(),
                UserHandle.CURRENT,
                TEST_CALLING_PKG,
                TEST_INITIAL_REQUEST_TIME_MILLIS,
            )
        val safeCallback =
            mServiceImpl.initializeSafeExecuteAppFunctionCallback(
                aidlRequest,
                mock<IExecuteAppFunctionCallback>(),
                TEST_CALLING_UID,
            )
        safeCallback.setExecutionStartTimeAfterBindMillis(TEST_EXECUTION_TIME_AFTER_BIND_MILLIS)
        val response =
            ExecuteAppFunctionResponse(
                GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
                    .setPropertyLong("longProperty", 42L)
                    .setPropertyString("stringProperty", "text")
                    .build()
            )

    @Before
    fun setup() {
        whenever(mMockPackageManager.getPackageUid(eq(TEST_TARGET_PACKAGE), any<Int>()))
            .thenReturn(TEST_TARGET_UID)
        mSafeCallback =
        safeCallback.onResult(response)

        ExtendedMockito.verify {
            AppFunctionsStatsLog.write(
                /* atomId= */ eq<Int>(AppFunctionsStatsLog.APP_FUNCTIONS_REQUEST_REPORTED),
                /* callerPackageUid= */ eq<Int>(TEST_CALLING_UID),
                /* targetPackageUid= */ eq<Int>(TEST_TARGET_UID),
                /* errorCode= */ eq<Int>(AppFunctionsLoggerWrapper.SUCCESS_RESPONSE_CODE),
                /* requestSizeBytes= */ eq<Int>(aidlRequest.clientRequest.requestDataSize),
                /* responseSizeBytes= */ eq<Int>(response.responseDataSize),
                /* requestDurationMs= */ eq<Long>(TEST_EXPECTED_E2E_DURATION_MILLIS),
                /* requestOverheadMs= */ eq<Long>(TEST_EXPECTED_OVERHEAD_DURATION_MILLIS),
                /* interactionType= */ eq<Int>(
                    AppFunctionsLoggerWrapper.INTERACTION_TYPE_UNSPECIFIED
                ),
            )
        }
    }

    @RequiresFlagsDisabled(
        FLAG_APP_FUNCTION_ACCESS_SERVICE_ENABLED,
        FLAG_APP_FUNCTION_ACCESS_API_ENABLED,
    )
    @Test
    fun testOnError_logsFailureResponse_withoutAttribution() {
        val aidlRequest =
            ExecuteAppFunctionAidlRequest(
                ExecuteAppFunctionRequest.Builder(TEST_TARGET_PACKAGE, TEST_FUNCTION_ID).build(),
                UserHandle.CURRENT,
                TEST_CALLING_PKG,
                TEST_INITIAL_REQUEST_TIME_MILLIS,
            )
        val safeCallback =
            mServiceImpl.initializeSafeExecuteAppFunctionCallback(
                mRequestInternal,
                aidlRequest,
                mock<IExecuteAppFunctionCallback>(),
                TEST_CALLING_UID,
            )
        mSafeCallback.setExecutionStartTimeAfterBindMillis(TEST_EXECUTION_TIME_AFTER_BIND_MILLIS)
        safeCallback.setExecutionStartTimeAfterBindMillis(TEST_EXECUTION_TIME_AFTER_BIND_MILLIS)
        safeCallback.onError(
            AppFunctionException(AppFunctionException.ERROR_DENIED, "Error: permission denied")
        )

        ExtendedMockito.verify {
            AppFunctionsStatsLog.write(
                /* atomId= */ eq<Int>(AppFunctionsStatsLog.APP_FUNCTIONS_REQUEST_REPORTED),
                /* callerPackageUid= */ eq<Int>(TEST_CALLING_UID),
                /* targetPackageUid= */ eq<Int>(TEST_TARGET_UID),
                /* errorCode= */ eq<Int>(AppFunctionException.ERROR_DENIED),
                /* requestSizeBytes= */ eq<Int>(aidlRequest.clientRequest.requestDataSize),
                /* responseSizeBytes= */ eq<Int>(0),
                /* requestDurationMs= */ eq<Long>(TEST_EXPECTED_E2E_DURATION_MILLIS),
                /* requestOverheadMs= */ eq<Long>(TEST_EXPECTED_OVERHEAD_DURATION_MILLIS),
                /* interactionType= */ eq<Int>(
                    AppFunctionsLoggerWrapper.INTERACTION_TYPE_UNSPECIFIED
                ),
            )
        }
    }

    @RequiresFlagsEnabled(
        FLAG_APP_FUNCTION_ACCESS_SERVICE_ENABLED,
        FLAG_APP_FUNCTION_ACCESS_API_ENABLED,
    )
    @Test
    fun testOnSuccess_logsSuccessResponse() {
    fun testOnSuccess_logsSuccessResponse_withUsrQueryAttribution() {
        val aidlRequest =
            ExecuteAppFunctionAidlRequest(
                ExecuteAppFunctionRequest.Builder(TEST_TARGET_PACKAGE, TEST_FUNCTION_ID)
                    .setAttribution(
                        AppFunctionAttribution.Builder(
                                AppFunctionAttribution.INTERACTION_TYPE_USER_QUERY
                            )
                            .build()
                    )
                    .build(),
                UserHandle.CURRENT,
                TEST_CALLING_PKG,
                TEST_INITIAL_REQUEST_TIME_MILLIS,
            )
        val safeCallback =
            mServiceImpl.initializeSafeExecuteAppFunctionCallback(
                aidlRequest,
                mock<IExecuteAppFunctionCallback>(),
                TEST_CALLING_UID,
            )
        safeCallback.setExecutionStartTimeAfterBindMillis(TEST_EXECUTION_TIME_AFTER_BIND_MILLIS)
        val response =
            ExecuteAppFunctionResponse(
                GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
@@ -112,7 +213,7 @@ class AppFunctionsLoggingTest {
                    .build()
            )

        mSafeCallback.onResult(response)
        safeCallback.onResult(response)

        ExtendedMockito.verify {
            AppFunctionsStatsLog.write(
@@ -120,30 +221,120 @@ class AppFunctionsLoggingTest {
                /* callerPackageUid= */ eq<Int>(TEST_CALLING_UID),
                /* targetPackageUid= */ eq<Int>(TEST_TARGET_UID),
                /* errorCode= */ eq<Int>(AppFunctionsLoggerWrapper.SUCCESS_RESPONSE_CODE),
                /* requestSizeBytes= */ eq<Int>(mRequestInternal.clientRequest.requestDataSize),
                /* requestSizeBytes= */ eq<Int>(aidlRequest.clientRequest.requestDataSize),
                /* responseSizeBytes= */ eq<Int>(response.responseDataSize),
                /* requestDurationMs= */ eq<Long>(TEST_EXPECTED_E2E_DURATION_MILLIS),
                /* requestOverheadMs= */ eq<Long>(TEST_EXPECTED_OVERHEAD_DURATION_MILLIS),
                /* interactionType= */ eq<Int>(
                    AppFunctionsLoggerWrapper.INTERACTION_TYPE_USER_QUERY
                ),
            )
        }
    }

    @RequiresFlagsEnabled(
        FLAG_APP_FUNCTION_ACCESS_SERVICE_ENABLED,
        FLAG_APP_FUNCTION_ACCESS_API_ENABLED,
    )
    @Test
    fun testOnError_logsFailureResponse() {
        mSafeCallback.onError(
            AppFunctionException(AppFunctionException.ERROR_DENIED, "Error: permission denied")
    fun testOnSuccess_logsSuccessResponse_withUserScheduledAttribution() {
        val aidlRequest =
            ExecuteAppFunctionAidlRequest(
                ExecuteAppFunctionRequest.Builder(TEST_TARGET_PACKAGE, TEST_FUNCTION_ID)
                    .setAttribution(
                        AppFunctionAttribution.Builder(
                                AppFunctionAttribution.INTERACTION_TYPE_USER_SCHEDULED
                            )
                            .build()
                    )
                    .build(),
                UserHandle.CURRENT,
                TEST_CALLING_PKG,
                TEST_INITIAL_REQUEST_TIME_MILLIS,
            )
        val safeCallback =
            mServiceImpl.initializeSafeExecuteAppFunctionCallback(
                aidlRequest,
                mock<IExecuteAppFunctionCallback>(),
                TEST_CALLING_UID,
            )
        safeCallback.setExecutionStartTimeAfterBindMillis(TEST_EXECUTION_TIME_AFTER_BIND_MILLIS)
        val response =
            ExecuteAppFunctionResponse(
                GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
                    .setPropertyLong("longProperty", 42L)
                    .setPropertyString("stringProperty", "text")
                    .build()
            )

        safeCallback.onResult(response)

        ExtendedMockito.verify {
            AppFunctionsStatsLog.write(
                /* atomId= */ eq<Int>(AppFunctionsStatsLog.APP_FUNCTIONS_REQUEST_REPORTED),
                /* callerPackageUid= */ eq<Int>(TEST_CALLING_UID),
                /* targetPackageUid= */ eq<Int>(TEST_TARGET_UID),
                /* errorCode= */ eq<Int>(AppFunctionException.ERROR_DENIED),
                /* requestSizeBytes= */ eq<Int>(mRequestInternal.clientRequest.requestDataSize),
                /* responseSizeBytes= */ eq<Int>(0),
                /* errorCode= */ eq<Int>(AppFunctionsLoggerWrapper.SUCCESS_RESPONSE_CODE),
                /* requestSizeBytes= */ eq<Int>(aidlRequest.clientRequest.requestDataSize),
                /* responseSizeBytes= */ eq<Int>(response.responseDataSize),
                /* requestDurationMs= */ eq<Long>(TEST_EXPECTED_E2E_DURATION_MILLIS),
                /* requestOverheadMs= */ eq<Long>(TEST_EXPECTED_OVERHEAD_DURATION_MILLIS),
                /* interactionType= */ eq<Int>(
                    AppFunctionsLoggerWrapper.INTERACTION_TYPE_USER_SCHEDULED
                ),
            )
        }
    }

    @RequiresFlagsEnabled(
        FLAG_APP_FUNCTION_ACCESS_SERVICE_ENABLED,
        FLAG_APP_FUNCTION_ACCESS_API_ENABLED,
    )
    @Test
    fun testOnSuccess_logsSuccessResponse_withCustomAttribution() {
        val aidlRequest =
            ExecuteAppFunctionAidlRequest(
                ExecuteAppFunctionRequest.Builder(TEST_TARGET_PACKAGE, TEST_FUNCTION_ID)
                    .setAttribution(
                        AppFunctionAttribution.Builder(
                                AppFunctionAttribution.INTERACTION_TYPE_OTHER
                            )
                            .setCustomInteractionType("TEST_INTERACTION_TYPE")
                            .build()
                    )
                    .build(),
                UserHandle.CURRENT,
                TEST_CALLING_PKG,
                TEST_INITIAL_REQUEST_TIME_MILLIS,
            )
        val safeCallback =
            mServiceImpl.initializeSafeExecuteAppFunctionCallback(
                aidlRequest,
                mock<IExecuteAppFunctionCallback>(),
                TEST_CALLING_UID,
            )
        safeCallback.setExecutionStartTimeAfterBindMillis(TEST_EXECUTION_TIME_AFTER_BIND_MILLIS)
        val response =
            ExecuteAppFunctionResponse(
                GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
                    .setPropertyLong("longProperty", 42L)
                    .setPropertyString("stringProperty", "text")
                    .build()
            )

        safeCallback.onResult(response)

        ExtendedMockito.verify {
            AppFunctionsStatsLog.write(
                /* atomId= */ eq<Int>(AppFunctionsStatsLog.APP_FUNCTIONS_REQUEST_REPORTED),
                /* callerPackageUid= */ eq<Int>(TEST_CALLING_UID),
                /* targetPackageUid= */ eq<Int>(TEST_TARGET_UID),
                /* errorCode= */ eq<Int>(AppFunctionsLoggerWrapper.SUCCESS_RESPONSE_CODE),
                /* requestSizeBytes= */ eq<Int>(aidlRequest.clientRequest.requestDataSize),
                /* responseSizeBytes= */ eq<Int>(response.responseDataSize),
                /* requestDurationMs= */ eq<Long>(TEST_EXPECTED_E2E_DURATION_MILLIS),
                /* requestOverheadMs= */ eq<Long>(TEST_EXPECTED_OVERHEAD_DURATION_MILLIS),
                /* interactionType= */ eq<Int>(AppFunctionsLoggerWrapper.INTERACTION_TYPE_OTHER),
            )
        }
    }