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

Commit 1f40027e authored by Oluwarotimi Adesina's avatar Oluwarotimi Adesina
Browse files

Grant target implicit visibility onExecuteAppFunctions

Flag: android.app.appfunctions.flags.enable_app_function_manager
Test: atest CtsAppFunctionTestCases -c
Bug: 386324561
Change-Id: I4fd8bdad13f69f2e4837b29b5cee5c2c9552152c
parent e9f92c9e
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -19,7 +19,9 @@ package com.android.server.appfunctions;
import android.annotation.NonNull;
import android.app.appfunctions.AppFunctionManagerConfiguration;
import android.content.Context;
import android.content.pm.PackageManagerInternal;

import com.android.server.LocalServices;
import com.android.server.SystemService;

/** Service that manages app functions. */
@@ -28,7 +30,9 @@ public class AppFunctionManagerService extends SystemService {

    public AppFunctionManagerService(Context context) {
        super(context);
        mServiceImpl = new AppFunctionManagerServiceImpl(context);
        mServiceImpl =
                new AppFunctionManagerServiceImpl(
                        context, LocalServices.getService(PackageManagerInternal.class));
    }

    @Override
+27 −3
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import android.app.appsearch.observer.ObserverSpec;
import android.app.appsearch.observer.SchemaChangeInfo;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
import android.os.CancellationSignal;
import android.os.IBinder;
@@ -87,8 +88,10 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
    private final Context mContext;
    private final Map<String, Object> mLocks = new WeakHashMap<>();
    private final AppFunctionsLoggerWrapper mLoggerWrapper;
    private final PackageManagerInternal mPackageManagerInternal;

    public AppFunctionManagerServiceImpl(@NonNull Context context) {
    public AppFunctionManagerServiceImpl(
            @NonNull Context context, @NonNull PackageManagerInternal packageManagerInternal) {
        this(
                context,
                new RemoteServiceCallerImpl<>(
@@ -96,7 +99,8 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
                new CallerValidatorImpl(context),
                new ServiceHelperImpl(context),
                new ServiceConfigImpl(),
                new AppFunctionsLoggerWrapper(context));
                new AppFunctionsLoggerWrapper(context),
                packageManagerInternal);
    }

    @VisibleForTesting
@@ -106,13 +110,15 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
            CallerValidator callerValidator,
            ServiceHelper appFunctionInternalServiceHelper,
            ServiceConfig serviceConfig,
            AppFunctionsLoggerWrapper loggerWrapper) {
            AppFunctionsLoggerWrapper loggerWrapper,
            PackageManagerInternal packageManagerInternal) {
        mContext = Objects.requireNonNull(context);
        mRemoteServiceCaller = Objects.requireNonNull(remoteServiceCaller);
        mCallerValidator = Objects.requireNonNull(callerValidator);
        mInternalServiceHelper = Objects.requireNonNull(appFunctionInternalServiceHelper);
        mServiceConfig = serviceConfig;
        mLoggerWrapper = loggerWrapper;
        mPackageManagerInternal = Objects.requireNonNull(packageManagerInternal);
    }

    /** Called when the user is unlocked. */
@@ -260,6 +266,24 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
                                                "Cannot find the target service."));
                                return;
                            }
                            // Grant target app implicit visibility to the caller
                            final int grantRecipientUserId = targetUser.getIdentifier();
                            final int grantRecipientAppId =
                                    UserHandle.getAppId(
                                            mPackageManagerInternal.getPackageUid(
                                                    requestInternal
                                                            .getClientRequest()
                                                            .getTargetPackageName(),
                                                    /* flags= */ 0,
                                                    /* userId= */ grantRecipientUserId));
                            if (grantRecipientAppId > 0) {
                                mPackageManagerInternal.grantImplicitAccess(
                                        grantRecipientUserId,
                                        serviceIntent,
                                        grantRecipientAppId,
                                        callingUid,
                                        /* direct= */ true);
                            }
                            bindAppFunctionServiceUnchecked(
                                    requestInternal,
                                    serviceIntent,
+0 −2
Original line number Diff line number Diff line
@@ -31,8 +31,6 @@ import java.util.Objects;
class ServiceHelperImpl implements ServiceHelper {
    private final Context mContext;

    // TODO(b/357551503): Keep track of unlocked users.

    ServiceHelperImpl(@NonNull Context context) {
        mContext = Objects.requireNonNull(context);
    }
+9 −2
Original line number Diff line number Diff line
@@ -18,28 +18,35 @@ package com.android.server.appfunctions

import android.app.appfunctions.flags.Flags
import android.content.Context
import android.content.pm.PackageManagerInternal
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.CheckFlagsRule
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.modules.utils.testing.ExtendedMockitoRule
import com.android.server.LocalServices
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock

@RunWith(AndroidJUnit4::class)
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
class AppFunctionManagerServiceImplTest {
    @get:Rule val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()

    @get:Rule
    val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
    val extendedMockitoRule =
        ExtendedMockitoRule.Builder(this).mockStatic(LocalServices::class.java).build()

    private val context: Context
        get() = ApplicationProvider.getApplicationContext()

    private val serviceImpl = AppFunctionManagerServiceImpl(context)
    private val serviceImpl = AppFunctionManagerServiceImpl(context, mock<PackageManagerInternal>())

    @Test
    fun testGetLockForPackage_samePackage() {
+38 −18
Original line number Diff line number Diff line
@@ -25,11 +25,13 @@ 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 androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.modules.utils.testing.ExtendedMockitoRule
import com.android.server.LocalServices
import com.google.common.util.concurrent.MoreExecutors
import org.junit.Before
import org.junit.Rule
@@ -40,24 +42,25 @@ import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever


/**
 * Tests that AppFunctionsStatsLog logs AppFunctionsRequestReported with the expected values.
 */
/** Tests that AppFunctionsStatsLog logs AppFunctionsRequestReported with the expected values. */
@RunWith(AndroidJUnit4::class)
class AppFunctionsLoggingTest {
    @get:Rule
    val mExtendedMockitoRule: ExtendedMockitoRule =
        ExtendedMockitoRule.Builder(this)
            .mockStatic(AppFunctionsStatsLog::class.java)
            .mockStatic(LocalServices::class.java)
            .build()
    private val mContext: Context get() = ApplicationProvider.getApplicationContext()
    private val mContext: Context
        get() = ApplicationProvider.getApplicationContext()

    private val mMockPackageManager = mock<PackageManager>()
    private val mAppFunctionsLoggerWrapper =
        AppFunctionsLoggerWrapper(
            mMockPackageManager,
            MoreExecutors.directExecutor(),
            { TEST_CURRENT_TIME_MILLIS })
            { TEST_CURRENT_TIME_MILLIS },
        )
    private lateinit var mSafeCallback: SafeOneTimeExecuteAppFunctionCallback

    private val mServiceImpl =
@@ -67,25 +70,40 @@ class AppFunctionsLoggingTest {
            mock<CallerValidator>(),
            mock<ServiceHelper>(),
            ServiceConfigImpl(),
            mAppFunctionsLoggerWrapper)
            mAppFunctionsLoggerWrapper,
            mock<PackageManagerInternal>(),
        )

    private val mRequestInternal = ExecuteAppFunctionAidlRequest(
    private val mRequestInternal =
        ExecuteAppFunctionAidlRequest(
            ExecuteAppFunctionRequest.Builder(TEST_TARGET_PACKAGE, TEST_FUNCTION_ID).build(),
        UserHandle.CURRENT, TEST_CALLING_PKG, TEST_INITIAL_REQUEST_TIME_MILLIS
            UserHandle.CURRENT,
            TEST_CALLING_PKG,
            TEST_INITIAL_REQUEST_TIME_MILLIS,
        )

    @Before
    fun setup() {
        whenever(mMockPackageManager.getPackageUid(eq(TEST_TARGET_PACKAGE), any<Int>())).thenReturn(TEST_TARGET_UID)
        mSafeCallback = mServiceImpl.initializeSafeExecuteAppFunctionCallback(mRequestInternal, mock<IExecuteAppFunctionCallback>(), TEST_CALLING_UID)
        whenever(mMockPackageManager.getPackageUid(eq(TEST_TARGET_PACKAGE), any<Int>()))
            .thenReturn(TEST_TARGET_UID)
        mSafeCallback =
            mServiceImpl.initializeSafeExecuteAppFunctionCallback(
                mRequestInternal,
                mock<IExecuteAppFunctionCallback>(),
                TEST_CALLING_UID,
            )
        mSafeCallback.setExecutionStartTimeAfterBindMillis(TEST_EXECUTION_TIME_AFTER_BIND_MILLIS)
    }

    @Test
    fun testOnSuccess_logsSuccessResponse() {
        val response =
            ExecuteAppFunctionResponse(GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
                .setPropertyLong("longProperty", 42L).setPropertyString("stringProperty", "text").build())
            ExecuteAppFunctionResponse(
                GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
                    .setPropertyLong("longProperty", 42L)
                    .setPropertyString("stringProperty", "text")
                    .build()
            )

        mSafeCallback.onResult(response)

@@ -98,14 +116,16 @@ class AppFunctionsLoggingTest {
                /* requestSizeBytes= */ eq<Int>(mRequestInternal.clientRequest.requestDataSize),
                /* responseSizeBytes= */ eq<Int>(response.responseDataSize),
                /* requestDurationMs= */ eq<Long>(TEST_EXPECTED_E2E_DURATION_MILLIS),
                /* requestOverheadMs= */ eq<Long>(TEST_EXPECTED_OVERHEAD_DURATION_MILLIS)
                /* requestOverheadMs= */ eq<Long>(TEST_EXPECTED_OVERHEAD_DURATION_MILLIS),
            )
        }
    }

    @Test
    fun testOnError_logsFailureResponse() {
        mSafeCallback.onError(AppFunctionException(AppFunctionException.ERROR_DENIED, "Error: permission denied"))
        mSafeCallback.onError(
            AppFunctionException(AppFunctionException.ERROR_DENIED, "Error: permission denied")
        )

        ExtendedMockito.verify {
            AppFunctionsStatsLog.write(
@@ -116,7 +136,7 @@ class AppFunctionsLoggingTest {
                /* requestSizeBytes= */ eq<Int>(mRequestInternal.clientRequest.requestDataSize),
                /* responseSizeBytes= */ eq<Int>(0),
                /* requestDurationMs= */ eq<Long>(TEST_EXPECTED_E2E_DURATION_MILLIS),
                /* requestOverheadMs= */ eq<Long>(TEST_EXPECTED_OVERHEAD_DURATION_MILLIS)
                /* requestOverheadMs= */ eq<Long>(TEST_EXPECTED_OVERHEAD_DURATION_MILLIS),
            )
        }
    }