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

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

Merge "Add preloaded fallback for App Functions agent allowlist." into main

parents 72856988 6520c1d3
Loading
Loading
Loading
Loading
+36 −9
Original line number Diff line number Diff line
@@ -305,7 +305,8 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
            properties -> {
                if (Flags.appFunctionAccessServiceEnabled()) {
                    if (properties.getKeyset().contains(ALLOWLISTED_APP_FUNCTIONS_AGENTS)) {
                        updateAgentAllowlist(/* readFromDeviceConfig= */ true,
                        updateAgentAllowlist(
                                /* readFromDeviceConfig= */ true,
                                /* readFromSecureSetting= */ false);
                    }
                }
@@ -731,14 +732,42 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
    private List<SignedPackage> readDeviceConfigAgentAllowlist() {
        final String allowlistString =
                DeviceConfig.getString(
                        NAMESPACE_MACHINE_LEARNING, ALLOWLISTED_APP_FUNCTIONS_AGENTS, "");
                        NAMESPACE_MACHINE_LEARNING, ALLOWLISTED_APP_FUNCTIONS_AGENTS, null);
        if (allowlistString != null) {
            try {
            List<SignedPackage> parsedAllowlist = SignedPackageParser.parseList(allowlistString);
                List<SignedPackage> parsedAllowlist =
                        SignedPackageParser.parseList(allowlistString);
                mAgentAllowlistStorage.writeCurrentAllowlist(allowlistString);
                return parsedAllowlist;
            } catch (Exception e) {
                Slog.e(TAG, "Cannot parse agent allowlist from config: " + allowlistString, e);
            return mAgentAllowlistStorage.readPreviousValidAllowlist();
            }
        }
        List<SignedPackage> stored = mAgentAllowlistStorage.readPreviousValidAllowlist();
        if (stored != null) {
            Slog.i(TAG, "Using previously stored valid allowlist.");
            return stored;
        }
        Slog.i(TAG, "No valid stored allowlist, falling back to static list.");
        return readPreloadedAgentAllowlist();
    }

    @NonNull
    private List<SignedPackage> readPreloadedAgentAllowlist() {
        final String[] preloadedAllowlistArray =
                mContext.getResources()
                        .getStringArray(
                                com.android.internal.R.array
                                        .config_defaultAppFunctionAgentAllowlist);
        if (preloadedAllowlistArray.length == 0) {
            return Collections.emptyList();
        }
        final String preloadedAllowlistString = String.join(";", preloadedAllowlistArray);
        try {
            return SignedPackageParser.parseList(preloadedAllowlistString);
        } catch (Exception e) {
            Slog.e(TAG, "Cannot parse preloaded allowlist: " + preloadedAllowlistString, e);
            return Collections.emptyList();
        }
    }

@@ -757,8 +786,6 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
            return Collections.emptyList();
        }
    }


    private boolean accessCheckFlagsEnabled() {
        return android.permission.flags.Flags.appFunctionAccessApiEnabled()
                && android.permission.flags.Flags.appFunctionAccessServiceEnabled();
+164 −20
Original line number Diff line number Diff line
@@ -16,10 +16,13 @@

package com.android.server.appfunctions

import android.Manifest
import android.annotation.RequiresPermission
import android.app.IUriGrantsManager
import android.app.appfunctions.AppFunctionAccessServiceInterface
import android.app.appfunctions.flags.Flags
import android.content.pm.PackageManagerInternal
import android.content.pm.Signature
import android.content.pm.SignedPackage
import android.content.pm.UserInfo
import android.os.IBinder
@@ -32,6 +35,8 @@ import android.provider.DeviceConfig
import android.testing.TestableContext
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity
import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
import com.android.internal.R
import com.android.modules.utils.testing.ExtendedMockitoRule
import com.android.server.LocalServices
@@ -42,11 +47,15 @@ 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
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.clearInvocations
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
@@ -74,6 +83,8 @@ class AppFunctionManagerServiceImplTest {
    private val appFunctionAccessService = mock<AppFunctionAccessServiceInterface>()
    private val agentAllowlistStorage = mock<AppFunctionAgentAllowlistStorage>()
    private val multiUserAccessHistory = mock<MultiUserAppFunctionAccessHistory>()
    private val agentAllowlistCaptor = argumentCaptor<Set<SignedPackage>>()
    private var allowlistWasEnabled: Boolean = true

    private val serviceImpl =
        AppFunctionManagerServiceImpl(
@@ -90,9 +101,30 @@ class AppFunctionManagerServiceImplTest {
            MoreExecutors.directExecutor(),
        )

    @Before
    fun setup() {
        allowlistWasEnabled = callWithShellPermissionIdentity {
            serviceImpl.isAgentAllowlistEnabled()
        }
        if (!allowlistWasEnabled) {
            runWithShellPermissionIdentity {
                serviceImpl.setAgentAllowlistEnabled(true)
                clearInvocations(appFunctionAccessService)
            }
        }
    }

    @After
    fun clear() {
    fun clearState() {
        if (!allowlistWasEnabled) {
            runWithShellPermissionIdentity {
                serviceImpl.setAgentAllowlistEnabled(false)
                clearInvocations(appFunctionAccessService)
            }
        }
        clearDeviceSettingPackages()
        clearPreloadedAllowlist()
        setDeviceConfigAllowlist(null)
    }

    @Test
@@ -290,23 +322,17 @@ class AppFunctionManagerServiceImplTest {
        assertThat(validTargets).containsExactly("android", nonDeviceSettingPackage)
    }

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

        serviceImpl.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY)

        verify(agentAllowlistStorage).writeCurrentAllowlist(signatureString)
    }

@@ -317,17 +343,8 @@ class AppFunctionManagerServiceImplTest {
    @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)

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

        serviceImpl.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY)
@@ -336,6 +353,109 @@ class AppFunctionManagerServiceImplTest {
        verify(agentAllowlistStorage, never()).writeCurrentAllowlist(invalidSignatureString)
    }

    @RequiresFlagsEnabled(
        FLAG_APP_FUNCTION_ACCESS_SERVICE_ENABLED,
        FLAG_APP_FUNCTION_ACCESS_API_ENABLED,
    )
    @Test
    @RequiresPermission(Manifest.permission.MANAGE_APP_FUNCTION_ACCESS)
    fun onBootPhase_initiateAgentAllowlistToDeviceConfig_whenDeviceConfigHasValue() {
        setPreloadedAllowlist(arrayOf("com.example.preload1:111111", "com.example.preload2:222222"))
        setDeviceConfigAllowlist("com.example.device1:aaaaaa;com.example.device2:bbbbbb")

        serviceImpl.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY)

        verify(appFunctionAccessService).setAgentAllowlist(agentAllowlistCaptor.capture())
        val capturedSet = agentAllowlistCaptor.firstValue
        val expectedPackage1 =
            SignedPackage("com.example.device1", Signature("aaaaaa").toByteArray())
        val expectedPackage2 =
            SignedPackage("com.example.device2", Signature("bbbbbb").toByteArray())
        assertThat(capturedSet).containsAtLeast(expectedPackage1, expectedPackage2)
    }

    @RequiresFlagsEnabled(
        FLAG_APP_FUNCTION_ACCESS_SERVICE_ENABLED,
        FLAG_APP_FUNCTION_ACCESS_API_ENABLED,
    )
    @Test
    fun onBootPhase_initiateAgentAllowlistToStored_whenDeviceConfigInvalid() {
        setDeviceConfigAllowlist("invalid-pkg:invalid_certificate_string")
        val stored = SignedPackageParser.parseList("com.example.test2:abcdef0123456789")
        whenever(agentAllowlistStorage.readPreviousValidAllowlist()).thenReturn(stored)
        setPreloadedAllowlist(arrayOf("com.example.preload1:111111", "com.example.preload2:222222"))

        serviceImpl.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY)

        verify(appFunctionAccessService).setAgentAllowlist(agentAllowlistCaptor.capture())
        val capturedSet = agentAllowlistCaptor.firstValue
        val expectedPackage =
            SignedPackage("com.example.test2", Signature("abcdef0123456789").toByteArray())
        assertThat(capturedSet).contains(expectedPackage)
    }

    @RequiresFlagsEnabled(
        FLAG_APP_FUNCTION_ACCESS_SERVICE_ENABLED,
        FLAG_APP_FUNCTION_ACCESS_API_ENABLED,
    )
    @Test
    fun onBootPhase_initiateAgentAllowlistToStored_whenDeviceConfigNull() {
        setDeviceConfigAllowlist(null)
        val stored = SignedPackageParser.parseList("com.example.test2:abcdef0123456789")
        whenever(agentAllowlistStorage.readPreviousValidAllowlist()).thenReturn(stored)
        setPreloadedAllowlist(arrayOf("com.example.preload1:111111", "com.example.preload2:222222"))

        serviceImpl.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY)

        verify(appFunctionAccessService).setAgentAllowlist(agentAllowlistCaptor.capture())
        val capturedSet = agentAllowlistCaptor.firstValue
        val expectedPackages =
            SignedPackage("com.example.test2", Signature("abcdef0123456789").toByteArray())
        assertThat(capturedSet).contains(expectedPackages)
    }

    @RequiresFlagsEnabled(
        FLAG_APP_FUNCTION_ACCESS_SERVICE_ENABLED,
        FLAG_APP_FUNCTION_ACCESS_API_ENABLED,
    )
    @Test
    fun onBootPhase_initiateAgentAllowlistToPreload_whenDeviceConfigNullNoStored() {
        setDeviceConfigAllowlist(null)
        setPreloadedAllowlist(arrayOf("com.example.preload1:111111", "com.example.preload2:222222"))
        whenever(agentAllowlistStorage.readPreviousValidAllowlist()).thenReturn(null)

        serviceImpl.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY)

        verify(appFunctionAccessService).setAgentAllowlist(agentAllowlistCaptor.capture())
        val capturedSet = agentAllowlistCaptor.firstValue
        val expectedPackage1 =
            SignedPackage("com.example.preload1", Signature("111111").toByteArray())
        val expectedPackage2 =
            SignedPackage("com.example.preload2", Signature("222222").toByteArray())
        assertThat(capturedSet).containsAtLeast(expectedPackage1, expectedPackage2)
    }

    @RequiresFlagsEnabled(
        FLAG_APP_FUNCTION_ACCESS_SERVICE_ENABLED,
        FLAG_APP_FUNCTION_ACCESS_API_ENABLED,
    )
    @Test
    fun onBootPhase_initiateAgentAllowlistToPreload_whenDeviceConfigInvalidAndNoStored() {
        setDeviceConfigAllowlist("invalid-pkg:invalid_certificate_string")
        whenever(agentAllowlistStorage.readPreviousValidAllowlist()).thenReturn(null)
        setPreloadedAllowlist(arrayOf("com.example.preload1:111111", "com.example.preload2:222222"))

        serviceImpl.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY)

        verify(appFunctionAccessService).setAgentAllowlist(agentAllowlistCaptor.capture())
        val capturedSet = agentAllowlistCaptor.firstValue
        val expectedPackage1 =
            SignedPackage("com.example.preload1", Signature("111111").toByteArray())
        val expectedPackage2 =
            SignedPackage("com.example.preload2", Signature("222222").toByteArray())
        assertThat(capturedSet).containsAtLeast(expectedPackage1, expectedPackage2)
    }

    @RequiresFlagsEnabled(
        FLAG_APP_FUNCTION_ACCESS_SERVICE_ENABLED,
        FLAG_APP_FUNCTION_ACCESS_API_ENABLED,
@@ -375,4 +495,28 @@ class AppFunctionManagerServiceImplTest {
            R.array.config_appFunctionDeviceSettingsPackages
        )
    }

    private fun setPreloadedAllowlist(allowlist: Array<String>) {
        context.orCreateTestableResources.addOverride(
            com.android.internal.R.array.config_defaultAppFunctionAgentAllowlist,
            allowlist,
        )
    }

    private fun clearPreloadedAllowlist() {
        context.orCreateTestableResources.removeOverride(
            com.android.internal.R.array.config_defaultAppFunctionAgentAllowlist
        )
    }

    private fun setDeviceConfigAllowlist(allowlist: String?) {
        whenever(
                DeviceConfig.getString(
                    eq("machine_learning"),
                    eq("allowlisted_app_functions_agents"),
                    anyOrNull(),
                )
            )
            .thenReturn(allowlist)
    }
}