Loading services/appfunctions/java/com/android/server/appfunctions/AppFunctionsLoggerWrapper.java +32 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -105,7 +111,8 @@ public class AppFunctionsLoggerWrapper { .getRequestDataSize(), /* responseSizeBytes= */ responseSizeBytes, /* requestDurationMs= */ e2eRequestLatencyMillis, /* requestOverheadMs= */ requestOverheadMillis)); /* requestOverheadMs= */ requestOverheadMillis, /* interactionType= */ getInteractionType(request))); } private int getPackageUid(String packageName) { Loading @@ -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(); Loading services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionsLoggingTest.kt +213 −22 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -64,7 +68,6 @@ class AppFunctionsLoggingTest { MoreExecutors.directExecutor(), { TEST_CURRENT_TIME_MILLIS }, ) private lateinit var mSafeCallback: SafeOneTimeExecuteAppFunctionCallback private val mServiceImpl = AppFunctionManagerServiceImpl( Loading @@ -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<*>>("", "", "") Loading @@ -112,7 +213,7 @@ class AppFunctionsLoggingTest { .build() ) mSafeCallback.onResult(response) safeCallback.onResult(response) ExtendedMockito.verify { AppFunctionsStatsLog.write( Loading @@ -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), ) } } Loading Loading
services/appfunctions/java/com/android/server/appfunctions/AppFunctionsLoggerWrapper.java +32 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -105,7 +111,8 @@ public class AppFunctionsLoggerWrapper { .getRequestDataSize(), /* responseSizeBytes= */ responseSizeBytes, /* requestDurationMs= */ e2eRequestLatencyMillis, /* requestOverheadMs= */ requestOverheadMillis)); /* requestOverheadMs= */ requestOverheadMillis, /* interactionType= */ getInteractionType(request))); } private int getPackageUid(String packageName) { Loading @@ -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(); Loading
services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionsLoggingTest.kt +213 −22 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -64,7 +68,6 @@ class AppFunctionsLoggingTest { MoreExecutors.directExecutor(), { TEST_CURRENT_TIME_MILLIS }, ) private lateinit var mSafeCallback: SafeOneTimeExecuteAppFunctionCallback private val mServiceImpl = AppFunctionManagerServiceImpl( Loading @@ -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<*>>("", "", "") Loading @@ -112,7 +213,7 @@ class AppFunctionsLoggingTest { .build() ) mSafeCallback.onResult(response) safeCallback.onResult(response) ExtendedMockito.verify { AppFunctionsStatsLog.write( Loading @@ -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), ) } } Loading