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

Commit 895dfe14 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Persist AppFunctions agent allowlist to disk." into main

parents 9d955f0c 6448f441
Loading
Loading
Loading
Loading
+28 −7
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ import android.content.pm.SignedPackage;
import android.content.pm.SigningInfo;
import android.os.Binder;
import android.os.CancellationSignal;
import android.os.Environment;
import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.OutcomeReceiver;
@@ -93,6 +94,7 @@ import com.android.server.SystemService;
import com.android.server.SystemService.TargetUser;
import com.android.server.uri.UriGrantsManagerInternal;

import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -112,6 +114,8 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
    private static final String ALLOWLISTED_APP_FUNCTIONS_AGENTS =
            "allowlisted_app_functions_agents";
    private static final String NAMESPACE_MACHINE_LEARNING = "machine_learning";
    private static final String AGENT_ALLOWLIST_FILE_NAME = "agent_allowlist.txt";
    private static final String APP_FUNCTIONS_DIR = "appfunctions";

    private final RemoteServiceCaller<IAppFunctionService> mRemoteServiceCaller;
    private final CallerValidator mCallerValidator;
@@ -140,6 +144,10 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
    @GuardedBy("mAgentAllowlistLock")
    private List<SignedPackage> mUpdatableAgentAllowlist = new ArrayList<>();

    private final Executor mWorkerExecutor;

    private final AppFunctionAgentAllowlistStorage mAgentAllowlistStorage;

    public AppFunctionManagerServiceImpl(
            @NonNull Context context,
            @NonNull PackageManagerInternal packageManagerInternal,
@@ -161,7 +169,12 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
                appFunctionAccessServiceInterface,
                uriGrantsManager,
                uriGrantsManagerInternal,
                new DeviceSettingHelperImpl(context));
                new DeviceSettingHelperImpl(context),
                new AppFunctionAgentAllowlistStorage(
                        new File(
                                new File(Environment.getDataSystemDirectory(), APP_FUNCTIONS_DIR),
                                AGENT_ALLOWLIST_FILE_NAME)),
                THREAD_POOL_EXECUTOR);
    }

    @VisibleForTesting
@@ -176,7 +189,9 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
            AppFunctionAccessServiceInterface appFunctionAccessServiceInterface,
            IUriGrantsManager uriGrantsManager,
            UriGrantsManagerInternal uriGrantsManagerInternal,
            DeviceSettingHelper deviceSettingHelper) {
            DeviceSettingHelper deviceSettingHelper,
            AppFunctionAgentAllowlistStorage agentAllowlistStorage,
            Executor workerExecutor) {
        mContext = Objects.requireNonNull(context);
        mRemoteServiceCaller = Objects.requireNonNull(remoteServiceCaller);
        mCallerValidator = Objects.requireNonNull(callerValidator);
@@ -189,6 +204,8 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
        mUriGrantsManagerInternal = Objects.requireNonNull(uriGrantsManagerInternal);
        mPermissionOwner = mUriGrantsManagerInternal.newUriPermissionOwner("appfunctions");
        mDeviceSettingHelper = deviceSettingHelper;
        mAgentAllowlistStorage = Objects.requireNonNull(agentAllowlistStorage);
        mWorkerExecutor = Objects.requireNonNull(workerExecutor);
    }

    /** Called when the user is unlocked. */
@@ -267,7 +284,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
    public void onBootPhase(int phase) {
        if (!Flags.appFunctionAccessServiceEnabled()) return;
        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
            updateAgentAllowlist(/* readFromDeviceConfig */ true);
            mWorkerExecutor.execute(() -> updateAgentAllowlist(/* readFromDeviceConfig */ true));
            DeviceConfig.addOnPropertiesChangedListener(
                    NAMESPACE_MACHINE_LEARNING,
                    BackgroundThread.getExecutor(),
@@ -276,6 +293,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
    }

    // TODO(b/413093397): Merge allowlist agents from other sources
    @WorkerThread
    private void updateAgentAllowlist(boolean readFromDeviceConfig) {
        synchronized (mAgentAllowlistLock) {
            Set<SignedPackage> oldAgents = new HashSet<>();
@@ -305,15 +323,18 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
    }

    @Nullable
    @WorkerThread
    private List<SignedPackage> readDeviceConfigAgentAllowlist() {
        final String signatureString =
        final String allowlistString =
                DeviceConfig.getString(
                        NAMESPACE_MACHINE_LEARNING, ALLOWLISTED_APP_FUNCTIONS_AGENTS, "");
        try {
            return SignedPackageParser.parseList(signatureString);
            List<SignedPackage> parsedAllowlist = SignedPackageParser.parseList(allowlistString);
            mAgentAllowlistStorage.writeCurrentAllowlist(allowlistString);
            return parsedAllowlist;
        } catch (Exception e) {
            Slog.e(TAG, "Cannot parse signature string: " + signatureString, e);
            return null;
            Slog.e(TAG, "Cannot parse agent allowlist from config: " + allowlistString, e);
            return mAgentAllowlistStorage.readPreviousValidAllowlist();
        }
    }

+79 −9
Original line number Diff line number Diff line
@@ -18,21 +18,26 @@ package com.android.server.appfunctions

import android.app.IUriGrantsManager
import android.app.appfunctions.AppFunctionAccessServiceInterface
import android.app.appfunctions.IAppFunctionService
import android.app.appfunctions.flags.Flags
import android.content.pm.PackageManagerInternal
import android.content.pm.SignedPackage
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.RequiresFlagsEnabled
import android.platform.test.flag.junit.CheckFlagsRule
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.provider.DeviceConfig
import android.testing.TestableContext
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.internal.R
import com.android.modules.utils.testing.ExtendedMockitoRule
import com.android.server.LocalServices
import com.android.server.SystemService
import com.android.server.uri.UriGrantsManagerInternal
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
@@ -43,6 +48,7 @@ import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@@ -54,24 +60,41 @@ class AppFunctionManagerServiceImplTest {

    @get:Rule
    val extendedMockitoRule =
        ExtendedMockitoRule.Builder(this).mockStatic(LocalServices::class.java).build()
        ExtendedMockitoRule.Builder(this)
            .mockStatic(LocalServices::class.java)
            .mockStatic(DeviceConfig::class.java)
            .build()

    @get:Rule
    val context: TestableContext =
        spy(TestableContext(ApplicationProvider.getApplicationContext(), null))

    private val appFunctionAccessService = mock<AppFunctionAccessServiceInterface>()
    private val agentAllowlistStorage = mock<AppFunctionAgentAllowlistStorage>()

    private val serviceImpl =
    private lateinit var serviceImpl: AppFunctionManagerServiceImpl

    @Before
    fun setup() {
        serviceImpl =
            AppFunctionManagerServiceImpl(
                context,
                mock<RemoteServiceCaller<IAppFunctionService>>(),
                mock<CallerValidator>(),
                mock<ServiceHelper>(),
                mock<ServiceConfig>(),
                mock<AppFunctionsLoggerWrapper>(),
                mock<PackageManagerInternal>(),
                appFunctionAccessService,
                mock<IUriGrantsManager>(),
                mock<UriGrantsManagerInternal>(),
                DeviceSettingHelperImpl(context),
                agentAllowlistStorage,
                MoreExecutors.directExecutor(),
            )
        clearDeviceSettingPackages()
    }

    @Before
    @After
    fun clear() {
        clearDeviceSettingPackages()
@@ -272,6 +295,53 @@ class AppFunctionManagerServiceImplTest {
        assertThat(validTargets).containsExactly("android", nonDeviceSettingPackage)
    }

    @Test
    @RequiresFlagsEnabled(
        FLAG_APP_FUNCTION_ACCESS_SERVICE_ENABLED,
        FLAG_APP_FUNCTION_ACCESS_API_ENABLED,
    )
    fun onBootPhase_writeAllowlistToStorage_whenDeviceConfigValid() {
        val signatureString = "com.example.test1:abcdef0123456789"
        val expectedPackages = listOf(SignedPackage("com.test.package", byteArrayOf()))
        whenever(
                DeviceConfig.getString(
                    eq("machine_learning"),
                    eq("allowlisted_app_functions_agents"),
                    any(),
                )
            )
            .thenReturn(signatureString)

        serviceImpl.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY)
        verify(agentAllowlistStorage).writeCurrentAllowlist(signatureString)
    }

    @RequiresFlagsEnabled(
        FLAG_APP_FUNCTION_ACCESS_SERVICE_ENABLED,
        FLAG_APP_FUNCTION_ACCESS_API_ENABLED,
    )
    @Test
    fun onBootPhase_readPreviousAllowlist_whenDeviceConfigInvalid() {
        val validPackages = listOf(SignedPackage("com.valid.package", byteArrayOf()))

        val invalidSignatureString = "com.example.test1:invalid_certificate_string"
        whenever(
                DeviceConfig.getString(
                    eq("machine_learning"),
                    eq("allowlisted_app_functions_agents"),
                    any(),
                )
            )
            .thenReturn(invalidSignatureString)

        whenever(agentAllowlistStorage.readPreviousValidAllowlist()).thenReturn(validPackages)

        serviceImpl.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY)

        verify(agentAllowlistStorage).readPreviousValidAllowlist()
        verify(agentAllowlistStorage, never()).writeCurrentAllowlist(invalidSignatureString)
    }

    private fun setDeviceSettingPackages(deviceSettings: Array<String>) {
        context.orCreateTestableResources.addOverride(
            R.array.config_appFunctionDeviceSettingsPackages,
+2 −0
Original line number Diff line number Diff line
@@ -87,6 +87,8 @@ class AppFunctionsLoggingTest {
            mock<IUriGrantsManager>(),
            mock<UriGrantsManagerInternal>(),
            mock<DeviceSettingHelper>(),
            mock<AppFunctionAgentAllowlistStorage>(),
            MoreExecutors.directExecutor(),
        )

    @Before