Loading core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java +2 −2 Original line number Diff line number Diff line Loading @@ -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(); Loading services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java +32 −14 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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) { Loading Loading @@ -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. */ Loading @@ -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 Loading @@ -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); Loading Loading @@ -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")); } Loading services/appfunctions/java/com/android/server/appfunctions/AppFunctionPackageMonitor.java 0 → 100644 +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; } } Loading
core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java +2 −2 Original line number Diff line number Diff line Loading @@ -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(); Loading
services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java +32 −14 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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) { Loading Loading @@ -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. */ Loading @@ -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 Loading @@ -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); Loading Loading @@ -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")); } Loading
services/appfunctions/java/com/android/server/appfunctions/AppFunctionPackageMonitor.java 0 → 100644 +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; } }