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

Commit 59b81673 authored by MingWei Liao's avatar MingWei Liao Committed by Android (Google) Code Review
Browse files

Merge "Record AppFunction access after execution" into main

parents 7ddd5a4a cbf88466
Loading
Loading
Loading
Loading
+60 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.appfunctions;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
import android.app.appfunctions.ExecuteAppFunctionAidlRequest;
import android.database.Cursor;

/** Manages the AppFunction Access Histories. */
public interface AppFunctionAccessHistory extends AutoCloseable {
    /** Queries the AppFunction access histories. */
    @Nullable
    Cursor queryAppFunctionAccessHistory(
            @Nullable String[] projection,
            @Nullable String selection,
            @Nullable String[] selectionArgs,
            @Nullable String sortOrder);

    /**
     * Inserts an AppFunction access history.
     *
     * @return The row id or -1 if fail to insert.
     */
    @WorkerThread
    long insertAppFunctionAccessHistory(
            @NonNull ExecuteAppFunctionAidlRequest aidlRequest, long duration);

    /**
     * Deletes expired AppFunction access histories.
     *
     * @param retentionMillis The maximum age of records to keep, in milliseconds. Records older
     *     than this will be deleted.
     */
    @WorkerThread
    void deleteExpiredAppFunctionAccessHistories(long retentionMillis);

    /** Deletes AppFunction access histories that are associated with the given packageName. */
    @WorkerThread
    void deleteAppFunctionAccessHistories(@NonNull String packageName);

    /** Deletes all AppFunction access histories. */
    @WorkerThread
    void deleteAll();
}
+6 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.appfunctions;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@@ -42,6 +43,11 @@ public final class AppFunctionExecutors {
            Executors.newSingleThreadExecutor(
                    new NamedThreadFactory("AppFunctionsLoggingExecutors"));

    /** Executor for scheduled tasks. */
    public static final ScheduledExecutorService SCHEDULED_EXECUTOR_SERVICE =
            Executors.newSingleThreadScheduledExecutor(
                    new NamedThreadFactory("AppFunctionScheduledExecutors"));

    static {
        THREAD_POOL_EXECUTOR.allowCoreThreadTimeOut(true);
    }
+36 −2
Original line number Diff line number Diff line
@@ -16,29 +16,63 @@

package com.android.server.appfunctions;

import static com.android.server.appfunctions.AppFunctionExecutors.SCHEDULED_EXECUTOR_SERVICE;

import android.annotation.NonNull;
import android.app.UriGrantsManager;
import android.app.appfunctions.AppFunctionAccessServiceInterface;
import android.app.appfunctions.AppFunctionManagerConfiguration;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
import android.os.Environment;
import android.os.UserHandle;

import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.uri.UriGrantsManagerInternal;

import java.io.File;
import java.util.Objects;
import java.util.function.Function;

/** Service that manages app functions. */
public class AppFunctionManagerService extends SystemService {
    private static final String AGENT_ALLOWLIST_FILE_NAME = "agent_allowlist.txt";
    private static final String APP_FUNCTIONS_DIR = "appfunctions";
    private final AppFunctionManagerServiceImpl mServiceImpl;

    public AppFunctionManagerService(Context context) {
        super(context);
        mServiceImpl =
                new AppFunctionManagerServiceImpl(
                        context, LocalServices.getService(PackageManagerInternal.class),
                        context,
                        LocalServices.getService(PackageManagerInternal.class),
                        LocalServices.getService(AppFunctionAccessServiceInterface.class),
                        UriGrantsManager.getService(),
                        LocalServices.getService(UriGrantsManagerInternal.class));
                        LocalServices.getService(UriGrantsManagerInternal.class),
                        new AppFunctionsLoggerWrapper(context),
                        new AppFunctionAgentAllowlistStorage(
                                new File(
                                        new File(
                                                Environment.getDataSystemDirectory(),
                                                APP_FUNCTIONS_DIR),
                                        AGENT_ALLOWLIST_FILE_NAME)),
                        new MultiUserAppFunctionAccessHistory(
                                new ServiceConfigImpl(),
                                SCHEDULED_EXECUTOR_SERVICE,
                                /* userAccessHistoryFactory */ new Function<>() {
                                    @Override
                                    @NonNull
                                    public AppFunctionAccessHistory apply(
                                            @NonNull UserHandle userHandle) {
                                        Objects.requireNonNull(userHandle);
                                        return new AppFunctionSQLiteAccessHistory(
                                                context.createContextAsUser(
                                                        userHandle, /* flags= */ 0));
                                    }
                                }),
                        BackgroundThread.getExecutor());
    }

    @Override
+55 −28
Original line number Diff line number Diff line
@@ -72,7 +72,6 @@ import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
import android.os.CancellationSignal;
import android.os.Environment;
import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.OutcomeReceiver;
@@ -81,6 +80,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.permission.flags.Flags;
@@ -95,14 +95,12 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.server.FgThread;
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;
@@ -120,8 +118,6 @@ 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 static final String SHELL_PKG = "com.android.shell";

    private static final Uri ADDITIONAL_AGENTS_URI = Settings.Secure.getUriFor(
@@ -148,6 +144,8 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {

    private final DeviceSettingHelper mDeviceSettingHelper;

    private final MultiUserAppFunctionAccessHistory mMultiUserAppFunctionAccessHistory;

    private final Object mAgentAllowlistLock = new Object();

    // Any agents hardcoded by the system
@@ -179,7 +177,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
                }
            };

    private final Executor mWorkerExecutor;
    private final Executor mBackgroundExecutor;

    private final AppFunctionAgentAllowlistStorage mAgentAllowlistStorage;

@@ -188,7 +186,11 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
            @NonNull PackageManagerInternal packageManagerInternal,
            @NonNull AppFunctionAccessServiceInterface appFunctionAccessServiceInterface,
            @NonNull IUriGrantsManager uriGrantsManager,
            @NonNull UriGrantsManagerInternal uriGrantsManagerInternal) {
            @NonNull UriGrantsManagerInternal uriGrantsManagerInternal,
            @NonNull AppFunctionsLoggerWrapper loggerWrapper,
            @NonNull AppFunctionAgentAllowlistStorage agentAllowlistStorage,
            @NonNull MultiUserAppFunctionAccessHistory multiUserAppFunctionAccessHistory,
            @NonNull Executor backgroundExecutor) {
        this(
                context,
                new RemoteServiceCallerImpl<>(
@@ -199,21 +201,18 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
                        Objects.requireNonNull(context.getSystemService(UserManager.class))),
                new ServiceHelperImpl(context),
                new ServiceConfigImpl(),
                new AppFunctionsLoggerWrapper(context),
                loggerWrapper,
                packageManagerInternal,
                appFunctionAccessServiceInterface,
                uriGrantsManager,
                uriGrantsManagerInternal,
                new DeviceSettingHelperImpl(context),
                new AppFunctionAgentAllowlistStorage(
                        new File(
                                new File(Environment.getDataSystemDirectory(), APP_FUNCTIONS_DIR),
                                AGENT_ALLOWLIST_FILE_NAME)),
                THREAD_POOL_EXECUTOR);
                agentAllowlistStorage,
                multiUserAppFunctionAccessHistory,
                backgroundExecutor);
    }

    @VisibleForTesting
    AppFunctionManagerServiceImpl(
    private AppFunctionManagerServiceImpl(
            Context context,
            RemoteServiceCaller<IAppFunctionService> remoteServiceCaller,
            CallerValidator callerValidator,
@@ -226,21 +225,26 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
            UriGrantsManagerInternal uriGrantsManagerInternal,
            DeviceSettingHelper deviceSettingHelper,
            AppFunctionAgentAllowlistStorage agentAllowlistStorage,
            Executor workerExecutor) {
            MultiUserAppFunctionAccessHistory multiUserAppFunctionAccessHistory,
            Executor backgroundExecutor) {
        mContext = Objects.requireNonNull(context);
        mRemoteServiceCaller = Objects.requireNonNull(remoteServiceCaller);
        mCallerValidator = Objects.requireNonNull(callerValidator);
        mInternalServiceHelper = Objects.requireNonNull(appFunctionInternalServiceHelper);
        mServiceConfig = serviceConfig;
        mLoggerWrapper = loggerWrapper;
        mServiceConfig = Objects.requireNonNull(serviceConfig);
        mLoggerWrapper = Objects.requireNonNull(loggerWrapper);
        mPackageManagerInternal = Objects.requireNonNull(packageManagerInternal);
        mAppFunctionAccessService = Objects.requireNonNull(appFunctionAccessServiceInterface);
        mUriGrantsManager = Objects.requireNonNull(uriGrantsManager);
        mUriGrantsManagerInternal = Objects.requireNonNull(uriGrantsManagerInternal);
        mPermissionOwner = mUriGrantsManagerInternal.newUriPermissionOwner("appfunctions");
        mDeviceSettingHelper = deviceSettingHelper;
        mPermissionOwner =
                Objects.requireNonNull(
                        mUriGrantsManagerInternal.newUriPermissionOwner("appfunctions"));
        mDeviceSettingHelper = Objects.requireNonNull(deviceSettingHelper);
        mAgentAllowlistStorage = Objects.requireNonNull(agentAllowlistStorage);
        mWorkerExecutor = Objects.requireNonNull(workerExecutor);
        mMultiUserAppFunctionAccessHistory =
                Objects.requireNonNull(multiUserAppFunctionAccessHistory);
        mBackgroundExecutor = Objects.requireNonNull(backgroundExecutor);
    }

    /** Called when the user is unlocked. */
@@ -251,6 +255,9 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
        PackageMonitor pkgMonitorForUser =
                AppFunctionPackageMonitor.registerPackageMonitorForUser(mContext, user);
        mPackageMonitors.append(user.getUserIdentifier(), pkgMonitorForUser);
        if (accessCheckFlagsEnabled()) {
            mMultiUserAppFunctionAccessHistory.onUserUnlocked(user);
        }
    }

    /** Called when the user is stopping. */
@@ -264,6 +271,9 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
            mPackageMonitors.get(userIdentifier).unregister();
            mPackageMonitors.delete(userIdentifier);
        }
        if (accessCheckFlagsEnabled()) {
            mMultiUserAppFunctionAccessHistory.onUserStopping(user);
        }
    }

    @Override
@@ -305,23 +315,18 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
    /**
     * Called during different phases of the system boot process.
     *
     * <p>This method is used to initialize AppFunctionManagerService components that depend on
     * other system services being ready. Specifically, it handles reading DeviceConfig properties
     * related to allowed agent package signatures and registers a listener for changes to these
     * properties.
     *
     * @param phase The current boot phase, as defined in {@link SystemService}. This method
     *     specifically acts on {@link SystemService#PHASE_SYSTEM_SERVICES_READY}.
     */
    public void onBootPhase(int phase) {
        if (!Flags.appFunctionAccessServiceEnabled()) return;
        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
            mWorkerExecutor.execute(() ->
            mBackgroundExecutor.execute(() ->
                    updateAgentAllowlist(/* readFromDeviceConfig */ true,
                            /* readFromSecureSetting= */ true));
            DeviceConfig.addOnPropertiesChangedListener(
                    NAMESPACE_MACHINE_LEARNING,
                    BackgroundThread.getExecutor(),
                    mBackgroundExecutor,
                    mDeviceConfigListener);
            mContext.getContentResolver().registerContentObserver(ADDITIONAL_AGENTS_URI, false,
                    mAdbAgentObserver);
@@ -1093,6 +1098,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
                            long executionStartTimeMillis) {
                        mLoggerWrapper.logAppFunctionSuccess(
                                requestInternal, result, callingUid, executionStartTimeMillis);
                        recordAppFunctionAccess(requestInternal, executionStartTimeMillis);
                    }

                    @Override
@@ -1103,6 +1109,27 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
                                error.getErrorCode(),
                                callingUid,
                                executionStartTimeMillis);
                        recordAppFunctionAccess(requestInternal, executionStartTimeMillis);
                    }
                });
    }

    private void recordAppFunctionAccess(
            @NonNull ExecuteAppFunctionAidlRequest aidlRequest, long executionStartTimeMillis) {
        if (!accessCheckFlagsEnabled()) return;
        final long duration = SystemClock.elapsedRealtime() - executionStartTimeMillis;
        mBackgroundExecutor.execute(
                () -> {
                    try {
                        mMultiUserAppFunctionAccessHistory
                                .asUser(aidlRequest.getUserHandle())
                                .insertAppFunctionAccessHistory(aidlRequest, duration);
                    } catch (IllegalStateException e) {
                        Slog.e(
                                TAG,
                                "Fail to insert new access history to user "
                                        + aidlRequest.getUserHandle().getIdentifier(),
                                e);
                    }
                });
    }
+8 −21
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package com.android.server.appfunctions;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
import android.app.appfunctions.AppFunctionAttribution;
import android.app.appfunctions.AppFunctionManager.AccessHistory;
import android.app.appfunctions.ExecuteAppFunctionAidlRequest;
@@ -35,7 +34,8 @@ import android.util.Slog;
import java.util.Objects;

/** The database helper for managing AppFunction access histories. */
public final class AppFunctionAccessDatabaseHelper extends SQLiteOpenHelper {
public final class AppFunctionSQLiteAccessHistory extends SQLiteOpenHelper
        implements AppFunctionAccessHistory {

    private static final class AccessHistoryTable {
        static final String DB_TABLE = "appfunction_access";
@@ -85,7 +85,7 @@ public final class AppFunctionAccessDatabaseHelper extends SQLiteOpenHelper {

    private static final String TAG = "AppFunctionDatabase";

    AppFunctionAccessDatabaseHelper(@NonNull Context context) {
    AppFunctionSQLiteAccessHistory(@NonNull Context context) {
        super(context, DB_NAME, /* factory= */ null, DB_VERSION);
    }

@@ -102,7 +102,7 @@ public final class AppFunctionAccessDatabaseHelper extends SQLiteOpenHelper {
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}

    /** Queries the AppFunction access histories. */
    @Override
    @Nullable
    public Cursor queryAppFunctionAccessHistory(
            @Nullable String[] projection,
@@ -134,12 +134,7 @@ public final class AppFunctionAccessDatabaseHelper extends SQLiteOpenHelper {
        }
    }

    /**
     * Inserts an AppFunction access history to the given database.
     *
     * @return The row id or -1 if fail to insert.
     */
    @WorkerThread
    @Override
    public long insertAppFunctionAccessHistory(
            @NonNull ExecuteAppFunctionAidlRequest aidlRequest, long duration) {
        Objects.requireNonNull(aidlRequest);
@@ -193,13 +188,7 @@ public final class AppFunctionAccessDatabaseHelper extends SQLiteOpenHelper {
        return values;
    }

    /**
     * Deletes expired AppFunction access histories from the database.
     *
     * @param retentionMillis The maximum age of records to keep, in milliseconds. Records older
     *     than this will be deleted.
     */
    @WorkerThread
    @Override
    public void deleteExpiredAppFunctionAccessHistories(long retentionMillis) {
        try {
            final SQLiteDatabase db = getWritableDatabase();
@@ -212,8 +201,7 @@ public final class AppFunctionAccessDatabaseHelper extends SQLiteOpenHelper {
        }
    }

    /** Deletes AppFunction access histories that are associated with the given packageName. */
    @WorkerThread
    @Override
    public void deleteAppFunctionAccessHistories(@NonNull String packageName) {
        Objects.requireNonNull(packageName);

@@ -233,8 +221,7 @@ public final class AppFunctionAccessDatabaseHelper extends SQLiteOpenHelper {
        }
    }

    /** Deletes all AppFunction access histories. */
    @WorkerThread
    @Override
    public void deleteAll() {
        try {
            final SQLiteDatabase db = getWritableDatabase();
Loading