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

Commit ed618125 authored by Utkarsh Nigam's avatar Utkarsh Nigam
Browse files

Reset runtime metadata when package data is cleared.

Test: CTS
Bug: 414324999
Flag: EXEMPT Bugfix
Change-Id: I7ab9a8c071ad18d0320290589138f20f3e55ddf5
parent 664cee66
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;
    }
}