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

Commit 27cdab49 authored by MingWei's avatar MingWei
Browse files

Update AppFunction logs

Include attribution related fields

Flag: android.permission.flags.app_function_access_api_enabled
Bug: 427989773
Test: existing tests
Change-Id: I5fabf096bc1c16ba45386bd34b862c960450c1a2
parent e7716756
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),
            )
        }
    }