Loading apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java +36 −6 Original line number Diff line number Diff line Loading @@ -297,11 +297,26 @@ public final class SetSchemaRequest { } /** * Sets the {@link Migrator}. * Sets the {@link Migrator} associated with the given SchemaType. * * <p>The {@link Migrator} migrates all {@link GenericDocument}s under given schema type * from the current version number stored in AppSearch to the final version set via {@link * #setVersion}. * * <p>A {@link Migrator} will be invoked if the current version number stored in AppSearch * is different from the final version set via {@link #setVersion} and {@link * Migrator#shouldMigrate} returns {@code true}. * * <p>The target schema type of the output {@link GenericDocument} of {@link * Migrator#onUpgrade} or {@link Migrator#onDowngrade} must exist in this {@link * SetSchemaRequest}. * * @param schemaType The schema type to set migrator on. * @param migrator The migrator translate a document from it's old version to a new * incompatible version. * @param migrator The migrator translates a document from its current version to the final * version set via {@link #setVersion}. * @see SetSchemaRequest.Builder#setVersion * @see SetSchemaRequest.Builder#addSchemas * @see AppSearchSession#setSchema */ @NonNull @SuppressLint("MissingGetterMatchingBuilder") // Getter return plural objects. Loading @@ -313,10 +328,25 @@ public final class SetSchemaRequest { } /** * Sets {@link Migrator}s. * Sets a Map of {@link Migrator}s. * * @param migrators A {@link Map} of migrators that translate a document from its old * version to a new incompatible version. * <p>The {@link Migrator} migrates all {@link GenericDocument}s under given schema type * from the current version number stored in AppSearch to the final version set via {@link * #setVersion}. * * <p>A {@link Migrator} will be invoked if the current version number stored in AppSearch * is different from the final version set via {@link #setVersion} and {@link * Migrator#shouldMigrate} returns {@code true}. * * <p>The target schema type of the output {@link GenericDocument} of {@link * Migrator#onUpgrade} or {@link Migrator#onDowngrade} must exist in this {@link * SetSchemaRequest}. * * @param migrators A {@link Map} of migrators that translate a document from it's current * version to the final version set via {@link #setVersion}. * @see SetSchemaRequest.Builder#setVersion * @see SetSchemaRequest.Builder#addSchemas * @see AppSearchSession#setSchema */ @NonNull public Builder setMigrators(@NonNull Map<String, Migrator> migrators) { Loading apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +55 −3 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.appsearch; import static android.app.appsearch.AppSearchResult.throwableToFailedResult; import static android.os.UserHandle.USER_NULL; import android.annotation.NonNull; import android.annotation.UserIdInt; Loading @@ -34,7 +35,10 @@ import android.app.appsearch.SearchResultPage; import android.app.appsearch.SearchSpec; import android.app.appsearch.SetSchemaResponse; import android.app.appsearch.StorageInfo; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManagerInternal; import android.os.Binder; import android.os.Bundle; Loading @@ -46,6 +50,7 @@ import android.os.UserManager; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; Loading @@ -67,6 +72,7 @@ import java.util.Set; /** TODO(b/142567528): add comments when implement this class */ public class AppSearchManagerService extends SystemService { private static final String TAG = "AppSearchManagerService"; private final Context mContext; private PackageManagerInternal mPackageManagerInternal; private ImplInstanceManager mImplInstanceManager; private UserManager mUserManager; Loading @@ -79,14 +85,60 @@ public class AppSearchManagerService extends SystemService { public AppSearchManagerService(Context context) { super(context); mContext = context; } @Override public void onStart() { publishBinderService(Context.APP_SEARCH_SERVICE, new Stub()); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); mImplInstanceManager = ImplInstanceManager.getInstance(getContext()); mUserManager = getContext().getSystemService(UserManager.class); mImplInstanceManager = ImplInstanceManager.getInstance(mContext); mUserManager = mContext.getSystemService(UserManager.class); registerReceivers(); } private void registerReceivers() { mContext.registerReceiverAsUser(new UserActionReceiver(), UserHandle.ALL, new IntentFilter(Intent.ACTION_USER_REMOVED), /*broadcastPermission=*/ null, /*scheduler=*/ null); } private class UserActionReceiver extends BroadcastReceiver { @Override public void onReceive(@NonNull Context context, @NonNull Intent intent) { switch (intent.getAction()) { case Intent.ACTION_USER_REMOVED: final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); if (userId == USER_NULL) { Slog.e(TAG, "userId is missing in the intent: " + intent); return; } handleUserRemoved(userId); break; default: Slog.e(TAG, "Received unknown intent: " + intent); } } } /** * Handles user removed action. * * <p>Only need to clear the AppSearchImpl instance. The data of AppSearch is saved in the * "credential encrypted" system directory of each user. That directory will be auto-deleted * when a user is removed. * * @param userId The multi-user userId of the user that need to be removed. * * @see android.os.Environment#getDataSystemCeDirectory */ private void handleUserRemoved(@UserIdInt int userId) { try { mImplInstanceManager.removeAppSearchImplForUser(userId); Slog.i(TAG, "Removed AppSearchImpl instance for user: " + userId); } catch (Throwable t) { Slog.e(TAG, "Unable to remove data for user: " + userId, t); } } @Override Loading Loading @@ -663,7 +715,7 @@ public class AppSearchManagerService extends SystemService { final long callingIdentity = Binder.clearCallingIdentity(); try { verifyUserUnlocked(callingUserId); mImplInstanceManager.getOrCreateAppSearchImpl(getContext(), callingUserId); mImplInstanceManager.getOrCreateAppSearchImpl(mContext, callingUserId); invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null)); } catch (Throwable t) { invokeCallbackOnError(callback, t); Loading apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java +17 −0 Original line number Diff line number Diff line Loading @@ -95,6 +95,23 @@ public final class ImplInstanceManager { } } /** * Remove an instance of {@link AppSearchImpl} for the given user. * * <p>This method should only be called if {@link AppSearchManagerService} receives an * ACTION_USER_REMOVED, which the instance of given user should be removed. * * <p>If the user is removed, the "credential encrypted" system directory where icing lives will * be auto-deleted. So we shouldn't worry about persist data or close the AppSearchImpl. * * @param userId The multi-user userId of the user that need to be removed. */ @NonNull public void removeAppSearchImplForUser(@UserIdInt int userId) { synchronized (mInstancesLocked) { mInstancesLocked.remove(userId); } } /** * Gets an instance of AppSearchImpl for the given user. Loading apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING +3 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,9 @@ { "name": "CtsAppSearchTestCases" }, { "name": "CtsAppSearchHostTestCases" }, { "name": "FrameworksServicesTests", "options": [ Loading apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java +40 −1 Original line number Diff line number Diff line Loading @@ -1126,6 +1126,40 @@ public final class AppSearchImpl implements Closeable { } } /** * Remove all {@link AppSearchSchema}s and {@link GenericDocument}s under the given package. * * @param packageName The name of package to be removed. * @throws AppSearchException if we cannot remove the data. */ public void clearPackageData(@NonNull String packageName) throws AppSearchException { mReadWriteLock.writeLock().lock(); try { throwIfClosedLocked(); SchemaProto existingSchema = getSchemaProtoLocked(); SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder(); String prefix = createPackagePrefix(packageName); for (int i = 0; i < existingSchema.getTypesCount(); i++) { if (!existingSchema.getTypes(i).getSchemaType().startsWith(prefix)) { newSchemaBuilder.addTypes(existingSchema.getTypes(i)); } } // Apply schema, set force override to true to remove all schemas and documents under // that package. SetSchemaResultProto setSchemaResultProto = mIcingSearchEngineLocked.setSchema( newSchemaBuilder.build(), /*ignoreErrorsAndDeleteDocuments=*/ true); // Determine whether it succeeded. checkSuccess(setSchemaResultProto.getStatus()); } finally { mReadWriteLock.writeLock().unlock(); } } /** * Clears documents and schema across all packages and databaseNames. * Loading Loading @@ -1624,7 +1658,12 @@ public final class AppSearchImpl implements Closeable { @NonNull static String createPrefix(@NonNull String packageName, @NonNull String databaseName) { return packageName + PACKAGE_DELIMITER + databaseName + DATABASE_DELIMITER; return createPackagePrefix(packageName) + databaseName + DATABASE_DELIMITER; } @NonNull private static String createPackagePrefix(@NonNull String packageName) { return packageName + PACKAGE_DELIMITER; } /** Loading Loading
apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java +36 −6 Original line number Diff line number Diff line Loading @@ -297,11 +297,26 @@ public final class SetSchemaRequest { } /** * Sets the {@link Migrator}. * Sets the {@link Migrator} associated with the given SchemaType. * * <p>The {@link Migrator} migrates all {@link GenericDocument}s under given schema type * from the current version number stored in AppSearch to the final version set via {@link * #setVersion}. * * <p>A {@link Migrator} will be invoked if the current version number stored in AppSearch * is different from the final version set via {@link #setVersion} and {@link * Migrator#shouldMigrate} returns {@code true}. * * <p>The target schema type of the output {@link GenericDocument} of {@link * Migrator#onUpgrade} or {@link Migrator#onDowngrade} must exist in this {@link * SetSchemaRequest}. * * @param schemaType The schema type to set migrator on. * @param migrator The migrator translate a document from it's old version to a new * incompatible version. * @param migrator The migrator translates a document from its current version to the final * version set via {@link #setVersion}. * @see SetSchemaRequest.Builder#setVersion * @see SetSchemaRequest.Builder#addSchemas * @see AppSearchSession#setSchema */ @NonNull @SuppressLint("MissingGetterMatchingBuilder") // Getter return plural objects. Loading @@ -313,10 +328,25 @@ public final class SetSchemaRequest { } /** * Sets {@link Migrator}s. * Sets a Map of {@link Migrator}s. * * @param migrators A {@link Map} of migrators that translate a document from its old * version to a new incompatible version. * <p>The {@link Migrator} migrates all {@link GenericDocument}s under given schema type * from the current version number stored in AppSearch to the final version set via {@link * #setVersion}. * * <p>A {@link Migrator} will be invoked if the current version number stored in AppSearch * is different from the final version set via {@link #setVersion} and {@link * Migrator#shouldMigrate} returns {@code true}. * * <p>The target schema type of the output {@link GenericDocument} of {@link * Migrator#onUpgrade} or {@link Migrator#onDowngrade} must exist in this {@link * SetSchemaRequest}. * * @param migrators A {@link Map} of migrators that translate a document from it's current * version to the final version set via {@link #setVersion}. * @see SetSchemaRequest.Builder#setVersion * @see SetSchemaRequest.Builder#addSchemas * @see AppSearchSession#setSchema */ @NonNull public Builder setMigrators(@NonNull Map<String, Migrator> migrators) { Loading
apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +55 −3 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.appsearch; import static android.app.appsearch.AppSearchResult.throwableToFailedResult; import static android.os.UserHandle.USER_NULL; import android.annotation.NonNull; import android.annotation.UserIdInt; Loading @@ -34,7 +35,10 @@ import android.app.appsearch.SearchResultPage; import android.app.appsearch.SearchSpec; import android.app.appsearch.SetSchemaResponse; import android.app.appsearch.StorageInfo; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManagerInternal; import android.os.Binder; import android.os.Bundle; Loading @@ -46,6 +50,7 @@ import android.os.UserManager; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; Loading @@ -67,6 +72,7 @@ import java.util.Set; /** TODO(b/142567528): add comments when implement this class */ public class AppSearchManagerService extends SystemService { private static final String TAG = "AppSearchManagerService"; private final Context mContext; private PackageManagerInternal mPackageManagerInternal; private ImplInstanceManager mImplInstanceManager; private UserManager mUserManager; Loading @@ -79,14 +85,60 @@ public class AppSearchManagerService extends SystemService { public AppSearchManagerService(Context context) { super(context); mContext = context; } @Override public void onStart() { publishBinderService(Context.APP_SEARCH_SERVICE, new Stub()); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); mImplInstanceManager = ImplInstanceManager.getInstance(getContext()); mUserManager = getContext().getSystemService(UserManager.class); mImplInstanceManager = ImplInstanceManager.getInstance(mContext); mUserManager = mContext.getSystemService(UserManager.class); registerReceivers(); } private void registerReceivers() { mContext.registerReceiverAsUser(new UserActionReceiver(), UserHandle.ALL, new IntentFilter(Intent.ACTION_USER_REMOVED), /*broadcastPermission=*/ null, /*scheduler=*/ null); } private class UserActionReceiver extends BroadcastReceiver { @Override public void onReceive(@NonNull Context context, @NonNull Intent intent) { switch (intent.getAction()) { case Intent.ACTION_USER_REMOVED: final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); if (userId == USER_NULL) { Slog.e(TAG, "userId is missing in the intent: " + intent); return; } handleUserRemoved(userId); break; default: Slog.e(TAG, "Received unknown intent: " + intent); } } } /** * Handles user removed action. * * <p>Only need to clear the AppSearchImpl instance. The data of AppSearch is saved in the * "credential encrypted" system directory of each user. That directory will be auto-deleted * when a user is removed. * * @param userId The multi-user userId of the user that need to be removed. * * @see android.os.Environment#getDataSystemCeDirectory */ private void handleUserRemoved(@UserIdInt int userId) { try { mImplInstanceManager.removeAppSearchImplForUser(userId); Slog.i(TAG, "Removed AppSearchImpl instance for user: " + userId); } catch (Throwable t) { Slog.e(TAG, "Unable to remove data for user: " + userId, t); } } @Override Loading Loading @@ -663,7 +715,7 @@ public class AppSearchManagerService extends SystemService { final long callingIdentity = Binder.clearCallingIdentity(); try { verifyUserUnlocked(callingUserId); mImplInstanceManager.getOrCreateAppSearchImpl(getContext(), callingUserId); mImplInstanceManager.getOrCreateAppSearchImpl(mContext, callingUserId); invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null)); } catch (Throwable t) { invokeCallbackOnError(callback, t); Loading
apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java +17 −0 Original line number Diff line number Diff line Loading @@ -95,6 +95,23 @@ public final class ImplInstanceManager { } } /** * Remove an instance of {@link AppSearchImpl} for the given user. * * <p>This method should only be called if {@link AppSearchManagerService} receives an * ACTION_USER_REMOVED, which the instance of given user should be removed. * * <p>If the user is removed, the "credential encrypted" system directory where icing lives will * be auto-deleted. So we shouldn't worry about persist data or close the AppSearchImpl. * * @param userId The multi-user userId of the user that need to be removed. */ @NonNull public void removeAppSearchImplForUser(@UserIdInt int userId) { synchronized (mInstancesLocked) { mInstancesLocked.remove(userId); } } /** * Gets an instance of AppSearchImpl for the given user. Loading
apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING +3 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,9 @@ { "name": "CtsAppSearchTestCases" }, { "name": "CtsAppSearchHostTestCases" }, { "name": "FrameworksServicesTests", "options": [ Loading
apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java +40 −1 Original line number Diff line number Diff line Loading @@ -1126,6 +1126,40 @@ public final class AppSearchImpl implements Closeable { } } /** * Remove all {@link AppSearchSchema}s and {@link GenericDocument}s under the given package. * * @param packageName The name of package to be removed. * @throws AppSearchException if we cannot remove the data. */ public void clearPackageData(@NonNull String packageName) throws AppSearchException { mReadWriteLock.writeLock().lock(); try { throwIfClosedLocked(); SchemaProto existingSchema = getSchemaProtoLocked(); SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder(); String prefix = createPackagePrefix(packageName); for (int i = 0; i < existingSchema.getTypesCount(); i++) { if (!existingSchema.getTypes(i).getSchemaType().startsWith(prefix)) { newSchemaBuilder.addTypes(existingSchema.getTypes(i)); } } // Apply schema, set force override to true to remove all schemas and documents under // that package. SetSchemaResultProto setSchemaResultProto = mIcingSearchEngineLocked.setSchema( newSchemaBuilder.build(), /*ignoreErrorsAndDeleteDocuments=*/ true); // Determine whether it succeeded. checkSuccess(setSchemaResultProto.getStatus()); } finally { mReadWriteLock.writeLock().unlock(); } } /** * Clears documents and schema across all packages and databaseNames. * Loading Loading @@ -1624,7 +1658,12 @@ public final class AppSearchImpl implements Closeable { @NonNull static String createPrefix(@NonNull String packageName, @NonNull String databaseName) { return packageName + PACKAGE_DELIMITER + databaseName + DATABASE_DELIMITER; return createPackagePrefix(packageName) + databaseName + DATABASE_DELIMITER; } @NonNull private static String createPackagePrefix(@NonNull String packageName) { return packageName + PACKAGE_DELIMITER; } /** Loading