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

Commit 81a28fd2 authored by Utkarsh Nigam's avatar Utkarsh Nigam Committed by Android (Google) Code Review
Browse files

Merge "Reset runtime metadata when package data is cleared." into main

parents ac913474 ed618125
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -104,8 +104,8 @@ public class AppFunctionRuntimeMetadata extends GenericDocument {
    /**
     * Creates a parent schema for all app function runtime schemas.
     *
     * <p>This schema should be set visible to the owner itself and for callers with
     * the permission {@link android.permission.EXECUTE_APP_FUNCTIONS}.
     * <p>This schema should be set visible to the owner itself and for callers with the permission
     * {@link android.permission.EXECUTE_APP_FUNCTIONS}.
     */
    public static AppSearchSchema createParentAppFunctionRuntimeSchema() {
        return getAppFunctionRuntimeSchemaBuilder(RUNTIME_SCHEMA_TYPE).build();
+32 −14
Original line number Diff line number Diff line
@@ -70,9 +70,11 @@ import android.os.ShellCallback;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseArray;

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.util.DumpUtils;
import com.android.server.SystemService.TargetUser;
@@ -98,6 +100,8 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
    private final Map<String, Object> mLocks = new WeakHashMap<>();
    private final AppFunctionsLoggerWrapper mLoggerWrapper;
    private final PackageManagerInternal mPackageManagerInternal;
    // Not Guarded by lock since this is only accessed in main thread.
    private final SparseArray<PackageMonitor> mPackageMonitors = new SparseArray<>();

    public AppFunctionManagerServiceImpl(
            @NonNull Context context, @NonNull PackageManagerInternal packageManagerInternal) {
@@ -136,6 +140,9 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {

        registerAppSearchObserver(user);
        trySyncRuntimeMetadata(user);
        PackageMonitor pkgMonitorForUser =
                AppFunctionPackageMonitor.registerPackageMonitorForUser(mContext, user);
        mPackageMonitors.append(user.getUserIdentifier(), pkgMonitorForUser);
    }

    /** Called when the user is stopping. */
@@ -143,6 +150,12 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
        Objects.requireNonNull(user);

        MetadataSyncPerUser.removeUserSyncAdapter(user.getUserHandle());

        int userIdentifier = user.getUserIdentifier();
        if (mPackageMonitors.contains(userIdentifier)) {
            mPackageMonitors.get(userIdentifier).unregister();
            mPackageMonitors.delete(userIdentifier);
        }
    }

    @Override
@@ -160,8 +173,12 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
    }

    @Override
    public void onShellCommand(FileDescriptor in, FileDescriptor out,
            FileDescriptor err, @NonNull String[] args, ShellCallback callback,
    public void onShellCommand(
            FileDescriptor in,
            FileDescriptor out,
            FileDescriptor err,
            @NonNull String[] args,
            ShellCallback callback,
            @NonNull ResultReceiver resultReceiver) {
        new AppFunctionManagerServiceShellCommand(this)
                .exec(this, in, out, err, args, callback, resultReceiver);
@@ -252,7 +269,8 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
                .thenCompose(
                        canExecuteResult -> {
                            if (canExecuteResult == CAN_EXECUTE_APP_FUNCTIONS_DENIED) {
                                return AndroidFuture.failedFuture(new SecurityException(
                                return AndroidFuture.failedFuture(
                                        new SecurityException(
                                                "Caller does not have permission to execute the"
                                                        + " appfunction"));
                            }
+145 −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.MainThread;
import android.annotation.NonNull;
import android.app.appfunctions.AppFunctionRuntimeMetadata;
import android.app.appsearch.AppSearchManager;
import android.app.appsearch.PutDocumentsRequest;
import android.app.appsearch.SearchResult;
import android.app.appsearch.SearchSpec;
import android.content.Context;
import android.os.HandlerThread;
import android.os.Process;
import android.os.UserHandle;
import android.util.Slog;

import com.android.internal.content.PackageMonitor;
import com.android.server.SystemService;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * Monitors package events and manages {@link AppFunctionRuntimeMetadata} entries stored in
 * AppSearch when package data is cleared.
 */
public class AppFunctionPackageMonitor extends PackageMonitor {
    private static final String TAG = AppFunctionPackageMonitor.class.getSimpleName();
    private static final HandlerThread BACKGROUND_HANDLER_THREAD =
            new HandlerThread(TAG + "-HandlerThread", Process.THREAD_PRIORITY_BACKGROUND);

    private final AppSearchManager mAppSearchManager;

    /**
     * Creates a new AppFunctionPackageMonitor instance.
     *
     * @param context The context used to retrieve {@link AppSearchManager}.
     * @param userHandle The user whose package events should be monitored.
     */
    public AppFunctionPackageMonitor(@NonNull Context context, @NonNull UserHandle userHandle) {
        super(/* supportsPackageRestartQuery= */ true);
        mAppSearchManager =
                context.createContextAsUser(userHandle, /* flags= */ 0)
                        .getSystemService(AppSearchManager.class);
    }

    /**
     * Callback triggered when an app's data is cleared. This will attempt to find all {@link
     * AppFunctionRuntimeMetadata} entries associated with the given package and reset them in
     * AppSearch.
     *
     * @param packageName The name of the package whose data was cleared.
     * @param uid The UID of the package.
     */
    @Override
    public void onPackageDataCleared(String packageName, int uid) {
        String packageQuery =
                AppFunctionRuntimeMetadata.PROPERTY_PACKAGE_NAME + ":\"" + packageName + "\"";
        SearchSpec runtimeSearchSpec =
                new SearchSpec.Builder()
                        .addFilterSchemas(AppFunctionRuntimeMetadata.RUNTIME_SCHEMA_TYPE)
                        .setVerbatimSearchEnabled(true)
                        .build();
        AppSearchManager.SearchContext searchContext =
                new AppSearchManager.SearchContext.Builder(
                                AppFunctionRuntimeMetadata.APP_FUNCTION_RUNTIME_METADATA_DB)
                        .build();

        try (FutureAppSearchSession searchSession =
                new FutureAppSearchSessionImpl(mAppSearchManager, Runnable::run, searchContext)) {
            List<AppFunctionRuntimeMetadata> updatedRuntimeMetadataList = new ArrayList<>();

            try (FutureSearchResults futureSearchResults =
                    searchSession.search(packageQuery, runtimeSearchSpec).get()) {

                List<SearchResult> results;
                do {
                    results = futureSearchResults.getNextPage().get();
                    for (SearchResult result : results) {
                        String functionId =
                                result.getGenericDocument()
                                        .getPropertyString(
                                                AppFunctionRuntimeMetadata.PROPERTY_FUNCTION_ID);
                        updatedRuntimeMetadataList.add(
                                new AppFunctionRuntimeMetadata.Builder(
                                                packageName, Objects.requireNonNull(functionId))
                                        .build());
                    }
                } while (!results.isEmpty());

                PutDocumentsRequest putRequest =
                        new PutDocumentsRequest.Builder()
                                .addGenericDocuments(updatedRuntimeMetadataList)
                                .build();

                searchSession.put(putRequest).get();
            } catch (Exception e) {
                Slog.e(
                        TAG,
                        "Unable to reset the AppFunctionRuntimeMetadata when clearing data for "
                                + "package: "
                                + packageName,
                        e);
            }
        }
    }

    /**
     * Registers a {@link AppFunctionPackageMonitor} instance for the given user context. This
     * allows it to observe package-related events and react accordingly.
     *
     * @param context The base context.
     * @param user The target user whose package events should be monitored.
     */
    @MainThread
    public static PackageMonitor registerPackageMonitorForUser(
            @NonNull Context context, @NonNull SystemService.TargetUser user) {
        AppFunctionPackageMonitor monitor =
                new AppFunctionPackageMonitor(context, user.getUserHandle());

        if (BACKGROUND_HANDLER_THREAD.getState() == Thread.State.NEW) {
            BACKGROUND_HANDLER_THREAD.start();
        }

        monitor.register(
                context, user.getUserHandle(), BACKGROUND_HANDLER_THREAD.getThreadHandler());
        return monitor;
    }
}