diff options
author | Alexander Dorokhine <adorokhine@google.com> | 2021-06-18 10:48:58 -0700 |
---|---|---|
committer | Alexander Dorokhine <adorokhine@google.com> | 2021-06-18 10:48:58 -0700 |
commit | 9b26c3c9be866b1bf8b0199f20073e6eeab1a4dd (patch) | |
tree | 9fb9e7a2febcc4733c37eee870a898855957c7b8 /apex | |
parent | 5c86c0c10e71a6bc47582dd266da987198a0baa8 (diff) |
Guard system usage reporting by checking the caller has system access.
Adding this check in a testable way requires refactoring VisibilityStore
to pull it out of management by the AppSearchImpl object. It is now
initialized as a sibling of AppSearchImpl, managed by
AppSearchUserInstanceManager.
This has several benefits:
* Breaks the complicated initialization inter-dependency between
AppSearchImpl and VisibilityStore
* Reduces duplicative singleton managers
* Allows AppSearchImpl to be tested more easily by accepting a
"hasSystemSurfaceable" boolean that can be set in tests
* Reduces the number of times we have to call into VisStore; we can
determine whether the caller has system access in advance instead of
repeating the check for every schema type
Bug: 180058203
Bug: 183031844
Test: GlobalSearchSessionCtsTest#testReportSystemUsage_ForbiddenFromNonSystem
Test: GlobalSearchSessionPlatformCtsTest#testReportSystemUsage
Test: VisibilityStoreTest
Change-Id: I84c84819f287628ccf8af369f5481a8e90255f62
Diffstat (limited to 'apex')
9 files changed, 474 insertions, 589 deletions
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java index 31203c73c76a..400c24138060 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -37,6 +37,7 @@ import android.app.appsearch.aidl.AppSearchResultParcel; import android.app.appsearch.aidl.IAppSearchBatchResultCallback; import android.app.appsearch.aidl.IAppSearchManager; import android.app.appsearch.aidl.IAppSearchResultCallback; +import android.app.appsearch.exceptions.AppSearchException; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -58,10 +59,7 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.server.LocalManagerRegistry; import com.android.server.SystemService; -import com.android.server.appsearch.external.localstorage.AppSearchImpl; import com.android.server.appsearch.external.localstorage.stats.CallStats; -import com.android.server.appsearch.stats.LoggerInstanceManager; -import com.android.server.appsearch.stats.PlatformLogger; import com.android.server.appsearch.util.PackageUtil; import com.android.server.appsearch.visibilitystore.VisibilityStore; import com.android.server.usage.StorageStatsManagerLocal; @@ -89,9 +87,8 @@ public class AppSearchManagerService extends SystemService { private static final String TAG = "AppSearchManagerService"; private final Context mContext; private PackageManager mPackageManager; - private ImplInstanceManager mImplInstanceManager; private UserManager mUserManager; - private LoggerInstanceManager mLoggerInstanceManager; + private AppSearchUserInstanceManager mAppSearchUserInstanceManager; // Never call shutdownNow(). It will cancel the futures it's returned. And since // Executor#execute won't return anything, we will hang forever waiting for the execution. @@ -116,9 +113,8 @@ public class AppSearchManagerService extends SystemService { public void onStart() { publishBinderService(Context.APP_SEARCH_SERVICE, new Stub()); mPackageManager = getContext().getPackageManager(); - mImplInstanceManager = ImplInstanceManager.getInstance(mContext); + mAppSearchUserInstanceManager = AppSearchUserInstanceManager.getInstance(); mUserManager = mContext.getSystemService(UserManager.class); - mLoggerInstanceManager = LoggerInstanceManager.getInstance(); registerReceivers(); LocalManagerRegistry.getManager(StorageStatsManagerLocal.class) .registerStorageStatsAugmenter(new AppSearchStorageStatsAugmenter(), TAG); @@ -180,8 +176,7 @@ public class AppSearchManagerService extends SystemService { */ private void handleUserRemoved(@NonNull UserHandle userHandle) { try { - mImplInstanceManager.closeAndRemoveAppSearchImplForUser(userHandle); - mLoggerInstanceManager.removePlatformLoggerForUser(userHandle); + mAppSearchUserInstanceManager.closeAndRemoveUserInstance(userHandle); Log.i(TAG, "Removed AppSearchImpl instance for: " + userHandle); } catch (Throwable t) { Log.e(TAG, "Unable to remove data for: " + userHandle, t); @@ -224,14 +219,13 @@ public class AppSearchManagerService extends SystemService { return; } // Only clear the package's data if AppSearch exists for this user. - if (ImplInstanceManager.getAppSearchDir(userHandle).exists()) { - PlatformLogger logger = mLoggerInstanceManager.getOrCreatePlatformLogger(mContext, - userHandle, AppSearchConfig.getInstance(EXECUTOR)); - AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl(mContext, - userHandle, logger); + if (AppSearchUserInstanceManager.getAppSearchDir(userHandle).exists()) { + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getOrCreateUserInstance( + mContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); //TODO(b/145759910) clear visibility setting for package. - impl.clearPackageData(packageName); - logger.removeCachedUidForPackage(packageName); + instance.getAppSearchImpl().clearPackageData(packageName); + instance.getLogger().removeCachedUidForPackage(packageName); } } catch (Throwable t) { Log.e(TAG, "Unable to remove data for package: " + packageName, t); @@ -248,11 +242,10 @@ public class AppSearchManagerService extends SystemService { EXECUTOR.execute(() -> { try { // Only clear the package's data if AppSearch exists for this user. - if (ImplInstanceManager.getAppSearchDir(userHandle).exists()) { - PlatformLogger logger = mLoggerInstanceManager.getOrCreatePlatformLogger( - mContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); - AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl(mContext, - userHandle, logger); + if (AppSearchUserInstanceManager.getAppSearchDir(userHandle).exists()) { + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getOrCreateUserInstance( + mContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); List<PackageInfo> installedPackageInfos = mContext .createContextAsUser(userHandle, /*flags=*/0) .getPackageManager() @@ -263,7 +256,7 @@ public class AppSearchManagerService extends SystemService { } packagesToKeep.add(VisibilityStore.PACKAGE_NAME); //TODO(b/145759910) clear visibility setting for package. - impl.prunePackageData(packagesToKeep); + instance.getAppSearchImpl().prunePackageData(packagesToKeep); } } catch (Throwable t) { Log.e(TAG, "Unable to prune packages for " + user, t); @@ -279,7 +272,7 @@ public class AppSearchManagerService extends SystemService { UserHandle userHandle = user.getUserHandle(); mUnlockedUsersLocked.remove(userHandle); try { - mImplInstanceManager.closeAndRemoveAppSearchImplForUser(userHandle); + mAppSearchUserInstanceManager.closeAndRemoveUserInstance(userHandle); } catch (Throwable t) { Log.e(TAG, "Error handling user stopping.", t); } @@ -311,7 +304,7 @@ public class AppSearchManagerService extends SystemService { @NonNull String databaseName, @NonNull List<Bundle> schemaBundles, @NonNull List<String> schemasNotDisplayedBySystem, - @NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles, + @NonNull Map<String, List<Bundle>> schemasVisibleToPackagesBundles, boolean forceOverride, int schemaVersion, @NonNull UserHandle userHandle, @@ -321,7 +314,7 @@ public class AppSearchManagerService extends SystemService { Objects.requireNonNull(databaseName); Objects.requireNonNull(schemaBundles); Objects.requireNonNull(schemasNotDisplayedBySystem); - Objects.requireNonNull(schemasPackageAccessibleBundles); + Objects.requireNonNull(schemasVisibleToPackagesBundles); Objects.requireNonNull(userHandle); Objects.requireNonNull(callback); @@ -330,7 +323,7 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - PlatformLogger logger = null; + AppSearchUserInstance instance = null; int operationSuccessCount = 0; int operationFailureCount = 0; try { @@ -341,9 +334,9 @@ public class AppSearchManagerService extends SystemService { schemas.add(new AppSearchSchema(schemaBundles.get(i))); } Map<String, List<PackageIdentifier>> schemasPackageAccessible = - new ArrayMap<>(schemasPackageAccessibleBundles.size()); + new ArrayMap<>(schemasVisibleToPackagesBundles.size()); for (Map.Entry<String, List<Bundle>> entry : - schemasPackageAccessibleBundles.entrySet()) { + schemasVisibleToPackagesBundles.entrySet()) { List<PackageIdentifier> packageIdentifiers = new ArrayList<>(entry.getValue().size()); for (int i = 0; i < entry.getValue().size(); i++) { @@ -352,12 +345,12 @@ public class AppSearchManagerService extends SystemService { } schemasPackageAccessible.put(entry.getKey(), packageIdentifiers); } - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - logger = mLoggerInstanceManager.getPlatformLogger(callingUser); - SetSchemaResponse setSchemaResponse = impl.setSchema( + instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); + SetSchemaResponse setSchemaResponse = instance.getAppSearchImpl().setSchema( packageName, databaseName, schemas, + instance.getVisibilityStore(), schemasNotDisplayedBySystem, schemasPackageAccessible, forceOverride, @@ -370,12 +363,12 @@ public class AppSearchManagerService extends SystemService { statusCode = throwableToFailedResult(t).getResultCode(); invokeCallbackOnError(callback, t); } finally { - if (logger != null) { + if (instance != null) { int estimatedBinderLatencyMillis = 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); int totalLatencyMillis = (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - logger.logStats(new CallStats.Builder() + instance.getLogger().logStats(new CallStats.Builder() .setPackageName(packageName) .setDatabase(databaseName) .setStatusCode(statusCode) @@ -410,8 +403,10 @@ public class AppSearchManagerService extends SystemService { try { verifyUserUnlocked(callingUser); verifyCallingPackage(callingUser, callingUid, packageName); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - GetSchemaResponse response = impl.getSchema(packageName, databaseName); + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getUserInstance(callingUser); + GetSchemaResponse response = + instance.getAppSearchImpl().getSchema(packageName, databaseName); invokeCallbackOnResult( callback, AppSearchResult.newSuccessfulResult(response.getBundle())); @@ -438,10 +433,12 @@ public class AppSearchManagerService extends SystemService { try { verifyUserUnlocked(callingUser); verifyCallingPackage(callingUser, callingUid, packageName); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - List<String> namespaces = impl.getNamespaces(packageName, databaseName); - invokeCallbackOnResult(callback, - AppSearchResult.newSuccessfulResult(namespaces)); + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getUserInstance(callingUser); + List<String> namespaces = + instance.getAppSearchImpl().getNamespaces(packageName, databaseName); + invokeCallbackOnResult( + callback, AppSearchResult.newSuccessfulResult(namespaces)); } catch (Throwable t) { invokeCallbackOnError(callback, t); } @@ -467,7 +464,7 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - PlatformLogger logger = null; + AppSearchUserInstance instance = null; int operationSuccessCount = 0; int operationFailureCount = 0; try { @@ -475,17 +472,16 @@ public class AppSearchManagerService extends SystemService { verifyCallingPackage(callingUser, callingUid, packageName); AppSearchBatchResult.Builder<String, Void> resultBuilder = new AppSearchBatchResult.Builder<>(); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - logger = mLoggerInstanceManager.getPlatformLogger(callingUser); + instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); for (int i = 0; i < documentBundles.size(); i++) { GenericDocument document = new GenericDocument(documentBundles.get(i)); try { - impl.putDocument(packageName, databaseName, document, logger); - resultBuilder.setSuccess(document.getId(), /*result=*/ null); + instance.getAppSearchImpl().putDocument( + packageName, databaseName, document, instance.getLogger()); + resultBuilder.setSuccess(document.getId(), /*value=*/ null); ++operationSuccessCount; } catch (Throwable t) { - resultBuilder.setResult(document.getId(), - throwableToFailedResult(t)); + resultBuilder.setResult(document.getId(), throwableToFailedResult(t)); AppSearchResult<Void> result = throwableToFailedResult(t); resultBuilder.setResult(document.getId(), result); // Since we can only include one status code in the atom, @@ -495,19 +491,19 @@ public class AppSearchManagerService extends SystemService { } } // Now that the batch has been written. Persist the newly written data. - impl.persistToDisk(PersistType.Code.LITE); + instance.getAppSearchImpl().persistToDisk(PersistType.Code.LITE); invokeCallbackOnResult(callback, resultBuilder.build()); } catch (Throwable t) { ++operationFailureCount; statusCode = throwableToFailedResult(t).getResultCode(); invokeCallbackOnError(callback, t); } finally { - if (logger != null) { + if (instance != null) { int estimatedBinderLatencyMillis = 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); int totalLatencyMillis = (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - logger.logStats(new CallStats.Builder() + instance.getLogger().logStats(new CallStats.Builder() .setPackageName(packageName) .setDatabase(databaseName) .setStatusCode(statusCode) @@ -548,7 +544,7 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - PlatformLogger logger = null; + AppSearchUserInstance instance = null; int operationSuccessCount = 0; int operationFailureCount = 0; try { @@ -556,18 +552,16 @@ public class AppSearchManagerService extends SystemService { verifyCallingPackage(callingUser, callingUid, packageName); AppSearchBatchResult.Builder<String, Bundle> resultBuilder = new AppSearchBatchResult.Builder<>(); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - logger = mLoggerInstanceManager.getPlatformLogger(callingUser); + instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); for (int i = 0; i < ids.size(); i++) { String id = ids.get(i); try { - GenericDocument document = - impl.getDocument( - packageName, - databaseName, - namespace, - id, - typePropertyPaths); + GenericDocument document = instance.getAppSearchImpl().getDocument( + packageName, + databaseName, + namespace, + id, + typePropertyPaths); ++operationSuccessCount; resultBuilder.setSuccess(id, document.getBundle()); } catch (Throwable t) { @@ -585,12 +579,12 @@ public class AppSearchManagerService extends SystemService { statusCode = throwableToFailedResult(t).getResultCode(); invokeCallbackOnError(callback, t); } finally { - if (logger != null) { + if (instance != null) { int estimatedBinderLatencyMillis = 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); int totalLatencyMillis = (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - logger.logStats(new CallStats.Builder() + instance.getLogger().logStats(new CallStats.Builder() .setPackageName(packageName) .setDatabase(databaseName) .setStatusCode(statusCode) @@ -629,21 +623,19 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - PlatformLogger logger = null; + AppSearchUserInstance instance = null; int operationSuccessCount = 0; int operationFailureCount = 0; try { verifyUserUnlocked(callingUser); verifyCallingPackage(callingUser, callingUid, packageName); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - logger = mLoggerInstanceManager.getPlatformLogger(callingUser); - SearchResultPage searchResultPage = - impl.query( - packageName, - databaseName, - queryExpression, - new SearchSpec(searchSpecBundle), - logger); + instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); + SearchResultPage searchResultPage = instance.getAppSearchImpl().query( + packageName, + databaseName, + queryExpression, + new SearchSpec(searchSpecBundle), + instance.getLogger()); ++operationSuccessCount; invokeCallbackOnResult( callback, @@ -653,12 +645,12 @@ public class AppSearchManagerService extends SystemService { statusCode = throwableToFailedResult(t).getResultCode(); invokeCallbackOnError(callback, t); } finally { - if (logger != null) { + if (instance != null) { int estimatedBinderLatencyMillis = 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); int totalLatencyMillis = (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - logger.logStats(new CallStats.Builder() + instance.getLogger().logStats(new CallStats.Builder() .setPackageName(packageName) .setDatabase(databaseName) .setStatusCode(statusCode) @@ -695,21 +687,24 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - PlatformLogger logger = null; + AppSearchUserInstance instance = null; int operationSuccessCount = 0; int operationFailureCount = 0; try { verifyUserUnlocked(callingUser); verifyCallingPackage(callingUser, callingUid, packageName); - logger = mLoggerInstanceManager.getPlatformLogger(callingUser); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - SearchResultPage searchResultPage = - impl.globalQuery( - queryExpression, - new SearchSpec(searchSpecBundle), - packageName, - callingUid, - logger); + instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); + + boolean callerHasSystemAccess = + instance.getVisibilityStore().doesCallerHaveSystemAccess(packageName); + SearchResultPage searchResultPage = instance.getAppSearchImpl().globalQuery( + queryExpression, + new SearchSpec(searchSpecBundle), + packageName, + instance.getVisibilityStore(), + callingUid, + callerHasSystemAccess, + instance.getLogger()); ++operationSuccessCount; invokeCallbackOnResult( callback, @@ -719,12 +714,12 @@ public class AppSearchManagerService extends SystemService { statusCode = throwableToFailedResult(t).getResultCode(); invokeCallbackOnError(callback, t); } finally { - if (logger != null) { + if (instance != null) { int estimatedBinderLatencyMillis = 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); int totalLatencyMillis = (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - logger.logStats(new CallStats.Builder() + instance.getLogger().logStats(new CallStats.Builder() .setPackageName(packageName) .setStatusCode(statusCode) .setTotalLatencyMillis(totalLatencyMillis) @@ -756,8 +751,10 @@ public class AppSearchManagerService extends SystemService { EXECUTOR.execute(() -> { try { verifyUserUnlocked(callingUser); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - SearchResultPage searchResultPage = impl.getNextPage(nextPageToken); + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getUserInstance(callingUser); + SearchResultPage searchResultPage = + instance.getAppSearchImpl().getNextPage(nextPageToken); invokeCallbackOnResult( callback, AppSearchResult.newSuccessfulResult(searchResultPage.getBundle())); @@ -776,8 +773,9 @@ public class AppSearchManagerService extends SystemService { EXECUTOR.execute(() -> { try { verifyUserUnlocked(callingUser); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - impl.invalidateNextPageToken(nextPageToken); + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getUserInstance(callingUser); + instance.getAppSearchImpl().invalidateNextPageToken(nextPageToken); } catch (Throwable t) { Log.e(TAG, "Unable to invalidate the query page token", t); } @@ -806,11 +804,12 @@ public class AppSearchManagerService extends SystemService { EXECUTOR.execute(() -> { try { verifyCallingPackage(callingUser, callingUid, packageName); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getUserInstance(callingUser); // we don't need to append the file. The file is always brand new. try (DataOutputStream outputStream = new DataOutputStream( new FileOutputStream(fileDescriptor.getFileDescriptor()))) { - SearchResultPage searchResultPage = impl.query( + SearchResultPage searchResultPage = instance.getAppSearchImpl().query( packageName, databaseName, queryExpression, @@ -822,7 +821,7 @@ public class AppSearchManagerService extends SystemService { outputStream, searchResultPage.getResults().get(i) .getGenericDocument().getBundle()); } - searchResultPage = impl.getNextPage( + searchResultPage = instance.getAppSearchImpl().getNextPage( searchResultPage.getNextPageToken()); } } @@ -851,7 +850,8 @@ public class AppSearchManagerService extends SystemService { EXECUTOR.execute(() -> { try { verifyCallingPackage(callingUser, callingUid, packageName); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getUserInstance(callingUser); GenericDocument document; ArrayList<Bundle> migrationFailureBundles = new ArrayList<>(); @@ -866,8 +866,8 @@ public class AppSearchManagerService extends SystemService { break; } try { - impl.putDocument(packageName, databaseName, document, - /*logger=*/ null); + instance.getAppSearchImpl().putDocument( + packageName, databaseName, document, /*logger=*/ null); } catch (Throwable t) { migrationFailureBundles.add(new SetSchemaResponse.MigrationFailure( document.getNamespace(), @@ -878,7 +878,7 @@ public class AppSearchManagerService extends SystemService { } } } - impl.persistToDisk(PersistType.Code.FULL); + instance.getAppSearchImpl().persistToDisk(PersistType.Code.FULL); invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(migrationFailureBundles)); } catch (Throwable t) { @@ -909,17 +909,23 @@ public class AppSearchManagerService extends SystemService { EXECUTOR.execute(() -> { try { verifyUserUnlocked(callingUser); - - if (systemUsage) { - // TODO(b/183031844): Validate that the call comes from the system + verifyCallingPackage(callingUser, callingUid, packageName); + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getUserInstance(callingUser); + + if (systemUsage + && !instance.getVisibilityStore() + .doesCallerHaveSystemAccess(packageName)) { + throw new AppSearchException( + AppSearchResult.RESULT_SECURITY_ERROR, + packageName + " does not have access to report system usage"); } - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - impl.reportUsage( + instance.getAppSearchImpl().reportUsage( packageName, databaseName, namespace, documentId, usageTimeMillis, systemUsage); invokeCallbackOnResult( - callback, AppSearchResult.newSuccessfulResult(/*result=*/ null)); + callback, AppSearchResult.newSuccessfulResult(/*value=*/ null)); } catch (Throwable t) { invokeCallbackOnError(callback, t); } @@ -947,7 +953,7 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - PlatformLogger logger = null; + AppSearchUserInstance instance = null; int operationSuccessCount = 0; int operationFailureCount = 0; try { @@ -955,12 +961,11 @@ public class AppSearchManagerService extends SystemService { verifyCallingPackage(callingUser, callingUid, packageName); AppSearchBatchResult.Builder<String, Void> resultBuilder = new AppSearchBatchResult.Builder<>(); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - logger = mLoggerInstanceManager.getPlatformLogger(callingUser); + instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); for (int i = 0; i < ids.size(); i++) { String id = ids.get(i); try { - impl.remove( + instance.getAppSearchImpl().remove( packageName, databaseName, namespace, @@ -978,19 +983,19 @@ public class AppSearchManagerService extends SystemService { } } // Now that the batch has been written. Persist the newly written data. - impl.persistToDisk(PersistType.Code.LITE); + instance.getAppSearchImpl().persistToDisk(PersistType.Code.LITE); invokeCallbackOnResult(callback, resultBuilder.build()); } catch (Throwable t) { ++operationFailureCount; statusCode = throwableToFailedResult(t).getResultCode(); invokeCallbackOnError(callback, t); } finally { - if (logger != null) { + if (instance != null) { int estimatedBinderLatencyMillis = 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); int totalLatencyMillis = (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - logger.logStats(new CallStats.Builder() + instance.getLogger().logStats(new CallStats.Builder() .setPackageName(packageName) .setDatabase(databaseName) .setStatusCode(statusCode) @@ -1030,22 +1035,21 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - PlatformLogger logger = null; + AppSearchUserInstance instance = null; int operationSuccessCount = 0; int operationFailureCount = 0; try { verifyUserUnlocked(callingUser); verifyCallingPackage(callingUser, callingUid, packageName); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - logger = mLoggerInstanceManager.getPlatformLogger(callingUser); - impl.removeByQuery( + instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); + instance.getAppSearchImpl().removeByQuery( packageName, databaseName, queryExpression, new SearchSpec(searchSpecBundle), /*removeStatsBuilder=*/ null); // Now that the batch has been written. Persist the newly written data. - impl.persistToDisk(PersistType.Code.LITE); + instance.getAppSearchImpl().persistToDisk(PersistType.Code.LITE); ++operationSuccessCount; invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null)); } catch (Throwable t) { @@ -1053,12 +1057,12 @@ public class AppSearchManagerService extends SystemService { statusCode = throwableToFailedResult(t).getResultCode(); invokeCallbackOnError(callback, t); } finally { - if (logger != null) { + if (instance != null) { int estimatedBinderLatencyMillis = 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); int totalLatencyMillis = (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - logger.logStats(new CallStats.Builder() + instance.getLogger().logStats(new CallStats.Builder() .setPackageName(packageName) .setDatabase(databaseName) .setStatusCode(statusCode) @@ -1093,9 +1097,10 @@ public class AppSearchManagerService extends SystemService { try { verifyUserUnlocked(callingUser); verifyCallingPackage(callingUser, callingUid, packageName); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - StorageInfo storageInfo = impl.getStorageInfoForDatabase(packageName, - databaseName); + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getUserInstance(callingUser); + StorageInfo storageInfo = instance.getAppSearchImpl() + .getStorageInfoForDatabase(packageName, databaseName); Bundle storageInfoBundle = storageInfo.getBundle(); invokeCallbackOnResult( callback, AppSearchResult.newSuccessfulResult(storageInfoBundle)); @@ -1116,26 +1121,25 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - PlatformLogger logger = null; + AppSearchUserInstance instance = null; int operationSuccessCount = 0; int operationFailureCount = 0; try { verifyUserUnlocked(callingUser); - AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser); - logger = mLoggerInstanceManager.getPlatformLogger(callingUser); - impl.persistToDisk(PersistType.Code.FULL); + instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); + instance.getAppSearchImpl().persistToDisk(PersistType.Code.FULL); ++operationSuccessCount; } catch (Throwable t) { ++operationFailureCount; statusCode = throwableToFailedResult(t).getResultCode(); Log.e(TAG, "Unable to persist the data to disk", t); } finally { - if (logger != null) { + if (instance != null) { int estimatedBinderLatencyMillis = 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); int totalLatencyMillis = (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - logger.logStats(new CallStats.Builder() + instance.getLogger().logStats(new CallStats.Builder() .setStatusCode(statusCode) .setTotalLatencyMillis(totalLatencyMillis) .setCallType(CallStats.CALL_TYPE_FLUSH) @@ -1164,15 +1168,13 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - PlatformLogger logger = null; + AppSearchUserInstance instance = null; int operationSuccessCount = 0; int operationFailureCount = 0; try { verifyUserUnlocked(callingUser); - logger = mLoggerInstanceManager.getOrCreatePlatformLogger( - mContext, callingUser, - AppSearchConfig.getInstance(EXECUTOR)); - mImplInstanceManager.getOrCreateAppSearchImpl(mContext, callingUser, logger); + instance = mAppSearchUserInstanceManager.getOrCreateUserInstance( + mContext, callingUser, AppSearchConfig.getInstance(EXECUTOR)); ++operationSuccessCount; invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null)); } catch (Throwable t) { @@ -1180,12 +1182,12 @@ public class AppSearchManagerService extends SystemService { statusCode = throwableToFailedResult(t).getResultCode(); invokeCallbackOnError(callback, t); } finally { - if (logger != null) { + if (instance != null) { int estimatedBinderLatencyMillis = 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); int totalLatencyMillis = (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - logger.logStats(new CallStats.Builder() + instance.getLogger().logStats(new CallStats.Builder() .setStatusCode(statusCode) .setTotalLatencyMillis(totalLatencyMillis) .setCallType(CallStats.CALL_TYPE_INITIALIZE) @@ -1329,12 +1331,11 @@ public class AppSearchManagerService extends SystemService { try { verifyUserUnlocked(userHandle); - PlatformLogger logger = mLoggerInstanceManager.getOrCreatePlatformLogger( - mContext, userHandle, - AppSearchConfig.getInstance(EXECUTOR)); - AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl( - mContext, userHandle, logger); - stats.dataSize += impl.getStorageInfoForPackage(packageName).getSizeBytes(); + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getOrCreateUserInstance( + mContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); + stats.dataSize += instance.getAppSearchImpl() + .getStorageInfoForPackage(packageName).getSizeBytes(); } catch (Throwable t) { Log.e( TAG, @@ -1358,14 +1359,12 @@ public class AppSearchManagerService extends SystemService { if (packagesForUid == null) { return; } - PlatformLogger logger = mLoggerInstanceManager.getOrCreatePlatformLogger( - mContext, userHandle, - AppSearchConfig.getInstance(EXECUTOR)); - AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl( - mContext, userHandle, logger); + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getOrCreateUserInstance( + mContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); for (int i = 0; i < packagesForUid.length; i++) { - stats.dataSize += - impl.getStorageInfoForPackage(packagesForUid[i]).getSizeBytes(); + stats.dataSize += instance.getAppSearchImpl() + .getStorageInfoForPackage(packagesForUid[i]).getSizeBytes(); } } catch (Throwable t) { Log.e(TAG, "Unable to augment storage stats for uid " + uid, t); @@ -1388,14 +1387,13 @@ public class AppSearchManagerService extends SystemService { if (packagesForUser == null) { return; } - PlatformLogger logger = mLoggerInstanceManager.getOrCreatePlatformLogger( - mContext, userHandle, - AppSearchConfig.getInstance(EXECUTOR)); - AppSearchImpl impl = - mImplInstanceManager.getOrCreateAppSearchImpl(mContext, userHandle, logger); + AppSearchUserInstance instance = + mAppSearchUserInstanceManager.getOrCreateUserInstance( + mContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); for (int i = 0; i < packagesForUser.size(); i++) { String packageName = packagesForUser.get(i).packageName; - stats.dataSize += impl.getStorageInfoForPackage(packageName).getSizeBytes(); + stats.dataSize += instance.getAppSearchImpl() + .getStorageInfoForPackage(packageName).getSizeBytes(); } } catch (Throwable t) { Log.e(TAG, "Unable to augment storage stats for " + userHandle, t); diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java new file mode 100644 index 000000000000..7e743edaf3ee --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2021 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.appsearch; + +import android.annotation.NonNull; + +import com.android.server.appsearch.external.localstorage.AppSearchImpl; +import com.android.server.appsearch.stats.PlatformLogger; +import com.android.server.appsearch.visibilitystore.VisibilityStore; + +import java.util.Objects; + +/** + * Container for AppSearch classes that should only be initialized once per device-user and make up + * the core of the AppSearch system. + */ +public final class AppSearchUserInstance { + private final PlatformLogger mLogger; + private final AppSearchImpl mAppSearchImpl; + private final VisibilityStore mVisibilityStore; + + AppSearchUserInstance( + @NonNull PlatformLogger logger, + @NonNull AppSearchImpl appSearchImpl, + @NonNull VisibilityStore visibilityStore) { + mLogger = Objects.requireNonNull(logger); + mAppSearchImpl = Objects.requireNonNull(appSearchImpl); + mVisibilityStore = Objects.requireNonNull(visibilityStore); + } + + @NonNull + public PlatformLogger getLogger() { + return mLogger; + } + + @NonNull + public AppSearchImpl getAppSearchImpl() { + return mAppSearchImpl; + } + + @NonNull + public VisibilityStore getVisibilityStore() { + return mVisibilityStore; + } +} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java new file mode 100644 index 000000000000..cedc364f6072 --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2019 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.appsearch; + +import android.annotation.NonNull; +import android.app.appsearch.exceptions.AppSearchException; +import android.content.Context; +import android.os.Environment; +import android.os.SystemClock; +import android.os.UserHandle; +import android.util.ArrayMap; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.appsearch.external.localstorage.AppSearchImpl; +import com.android.server.appsearch.external.localstorage.FrameworkOptimizeStrategy; +import com.android.server.appsearch.external.localstorage.stats.InitializeStats; +import com.android.server.appsearch.stats.PlatformLogger; +import com.android.server.appsearch.visibilitystore.VisibilityStore; + +import java.io.File; +import java.util.Map; +import java.util.Objects; + +/** + * Manages the lifecycle of AppSearch classes that should only be initialized once per device-user + * and make up the core of the AppSearch system. + * + * @hide + */ +public final class AppSearchUserInstanceManager { + private static final String TAG = "AppSearchUserInstanceMa"; + + private static volatile AppSearchUserInstanceManager sAppSearchUserInstanceManager; + + @GuardedBy("mInstancesLocked") + private final Map<UserHandle, AppSearchUserInstance> mInstancesLocked = new ArrayMap<>(); + + private AppSearchUserInstanceManager() {} + + /** + * Gets an instance of AppSearchUserInstanceManager to be used. + * + * <p>If no instance has been initialized yet, a new one will be created. Otherwise, the + * existing instance will be returned. + */ + @NonNull + public static AppSearchUserInstanceManager getInstance() { + if (sAppSearchUserInstanceManager == null) { + synchronized (AppSearchUserInstanceManager.class) { + if (sAppSearchUserInstanceManager == null) { + sAppSearchUserInstanceManager = new AppSearchUserInstanceManager(); + } + } + } + return sAppSearchUserInstanceManager; + } + + /** + * Returns AppSearch directory in the credential encrypted system directory for the given user. + * + * <p>This folder should only be accessed after unlock. + */ + public static File getAppSearchDir(@NonNull UserHandle userHandle) { + // Duplicates the implementation of Environment#getDataSystemCeDirectory + // TODO(b/191059409): Unhide Environment#getDataSystemCeDirectory and switch to it. + File systemCeDir = new File(Environment.getDataDirectory(), "system_ce"); + File systemCeUserDir = new File(systemCeDir, String.valueOf(userHandle.getIdentifier())); + return new File(systemCeUserDir, "appsearch"); + } + + /** + * Gets an instance of AppSearchUserInstance for the given user, or creates one if none exists. + * + * <p>If no AppSearchUserInstance exists for the unlocked user, Icing will be initialized and + * one will be created. + * + * @param context The context + * @param userHandle The multi-user handle of the device user calling AppSearch + * @param config Flag manager for AppSearch + * @return An initialized {@link AppSearchUserInstance} for this user + */ + @NonNull + public AppSearchUserInstance getOrCreateUserInstance( + @NonNull Context context, + @NonNull UserHandle userHandle, + @NonNull AppSearchConfig config) + throws AppSearchException { + Objects.requireNonNull(context); + Objects.requireNonNull(userHandle); + Objects.requireNonNull(config); + + synchronized (mInstancesLocked) { + AppSearchUserInstance instance = mInstancesLocked.get(userHandle); + if (instance == null) { + Context userContext = context.createContextAsUser(userHandle, /*flags=*/ 0); + instance = createUserInstance(userContext, userHandle, config); + mInstancesLocked.put(userHandle, instance); + } + return instance; + } + } + + /** + * Closes and removes an {@link AppSearchUserInstance} for the given user. + * + * <p>All mutations applied to the underlying {@link AppSearchImpl} will be persisted to disk. + * + * @param userHandle The multi-user user handle of the user that need to be removed. + */ + public void closeAndRemoveUserInstance(@NonNull UserHandle userHandle) { + Objects.requireNonNull(userHandle); + synchronized (mInstancesLocked) { + AppSearchUserInstance instance = mInstancesLocked.remove(userHandle); + if (instance != null) { + instance.getAppSearchImpl().close(); + } + } + } + + /** + * Gets an {@link AppSearchUserInstance} for the given user. + * + * <p>This method should only be called by an initialized SearchSession, which has already + * called {@link #getOrCreateUserInstance} before. + * + * @param userHandle The multi-user handle of the device user calling AppSearch + * @return An initialized {@link AppSearchUserInstance} for this user + * @throws IllegalStateException if {@link AppSearchUserInstance} haven't created for the given + * user. + */ + @NonNull + public AppSearchUserInstance getUserInstance(@NonNull UserHandle userHandle) { + Objects.requireNonNull(userHandle); + synchronized (mInstancesLocked) { + AppSearchUserInstance instance = mInstancesLocked.get(userHandle); + if (instance == null) { + // Impossible scenario, user cannot call an uninitialized SearchSession, + // getInstance should always find the instance for the given user and never try to + // create an instance for this user again. + throw new IllegalStateException( + "AppSearchUserInstance has never been created for: " + userHandle); + } + return instance; + } + } + + @NonNull + private AppSearchUserInstance createUserInstance( + @NonNull Context userContext, + @NonNull UserHandle userHandle, + @NonNull AppSearchConfig config) + throws AppSearchException { + long totalLatencyStartMillis = SystemClock.elapsedRealtime(); + InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder(); + + // Initialize the classes that make up AppSearchUserInstance + PlatformLogger logger = new PlatformLogger(userContext, userHandle, config); + + File appSearchDir = getAppSearchDir(userHandle); + File icingDir = new File(appSearchDir, "icing"); + Log.i(TAG, "Creating new AppSearch instance at: " + icingDir); + AppSearchImpl appSearchImpl = + AppSearchImpl.create(icingDir, initStatsBuilder, new FrameworkOptimizeStrategy()); + + long prepareVisibilityStoreLatencyStartMillis = SystemClock.elapsedRealtime(); + VisibilityStore visibilityStore = VisibilityStore.create(appSearchImpl, userContext); + long prepareVisibilityStoreLatencyEndMillis = SystemClock.elapsedRealtime(); + + initStatsBuilder + .setTotalLatencyMillis( + (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis)) + .setPrepareVisibilityStoreLatencyMillis( + (int) + (prepareVisibilityStoreLatencyEndMillis + - prepareVisibilityStoreLatencyStartMillis)); + logger.logStats(initStatsBuilder.build()); + + return new AppSearchUserInstance(logger, appSearchImpl, visibilityStore); + } +} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java deleted file mode 100644 index 2181dab90681..000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2019 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.appsearch; - - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.appsearch.exceptions.AppSearchException; -import android.content.Context; -import android.os.Environment; -import android.os.UserHandle; -import android.util.ArrayMap; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; -import com.android.server.appsearch.external.localstorage.AppSearchImpl; -import com.android.server.appsearch.external.localstorage.AppSearchLogger; -import com.android.server.appsearch.external.localstorage.FrameworkOptimizeStrategy; - -import java.io.File; -import java.util.Map; -import java.util.Objects; - -/** - * Manages the lifecycle of instances of {@link AppSearchImpl}. - * - * <p>These instances are managed per unique device-user. - * @hide - */ -public final class ImplInstanceManager { - private static final String TAG = "AppSearchImplInstanceMa"; - - private static ImplInstanceManager sImplInstanceManager; - - @GuardedBy("mInstancesLocked") - private final Map<UserHandle, AppSearchImpl> mInstancesLocked = new ArrayMap<>(); - - /** - * Gets an instance of ImplInstanceManager to be used. - * - * <p>If no instance has been initialized yet, a new one will be created. Otherwise, the - * existing instance will be returned. - */ - @NonNull - public static ImplInstanceManager getInstance(@NonNull Context context) { - if (sImplInstanceManager == null) { - synchronized (ImplInstanceManager.class) { - if (sImplInstanceManager == null) { - sImplInstanceManager = new ImplInstanceManager(); - } - } - } - return sImplInstanceManager; - } - - /** - * Returns AppSearch directory in the credential encrypted system directory for the given user. - * - * <p>This folder should only be accessed after unlock. - */ - public static File getAppSearchDir(@NonNull UserHandle userHandle) { - // Duplicates the implementation of Environment#getDataSystemCeDirectory - // TODO(b/191059409): Unhide Environment#getDataSystemCeDirectory and switch to it. - File systemCeDir = new File(Environment.getDataDirectory(), "system_ce"); - File systemCeUserDir = new File(systemCeDir, String.valueOf(userHandle.getIdentifier())); - return new File(systemCeUserDir, "appSearch"); - } - - /** - * Gets an instance of AppSearchImpl for the given user, or creates one if none exists. - * - * <p>If no AppSearchImpl instance exists for the unlocked user, Icing will be initialized and - * one will be created. - * - * @param context The system context - * @param userHandle The multi-user handle of the device user calling AppSearch - * @return An initialized {@link AppSearchImpl} for this user - */ - @NonNull - public AppSearchImpl getOrCreateAppSearchImpl( - @NonNull Context context, - @NonNull UserHandle userHandle, - @Nullable AppSearchLogger logger) throws AppSearchException { - Objects.requireNonNull(context); - Objects.requireNonNull(userHandle); - - synchronized (mInstancesLocked) { - AppSearchImpl instance = mInstancesLocked.get(userHandle); - if (instance == null) { - Context userContext = context.createContextAsUser(userHandle, /*flags=*/ 0); - instance = createImpl(userContext, userHandle, logger); - mInstancesLocked.put(userHandle, instance); - } - return instance; - } - } - - /** - * Close and remove an instance of {@link AppSearchImpl} for the given user. - * - * <p>All mutation apply to this {@link AppSearchImpl} will be persisted to disk. - * - * @param userHandle The multi-user user handle of the user that need to be removed. - */ - public void closeAndRemoveAppSearchImplForUser(@NonNull UserHandle userHandle) { - Objects.requireNonNull(userHandle); - synchronized (mInstancesLocked) { - AppSearchImpl appSearchImpl = mInstancesLocked.get(userHandle); - if (appSearchImpl != null) { - appSearchImpl.close(); - mInstancesLocked.remove(userHandle); - } - } - } - - /** - * Gets an instance of AppSearchImpl for the given user. - * - * <p>This method should only be called by an initialized SearchSession, which has been already - * created the AppSearchImpl instance for the given user. - * - * @param userHandle The multi-user handle of the device user calling AppSearch - * @return An initialized {@link AppSearchImpl} for this user - * @throws IllegalStateException if {@link AppSearchImpl} haven't created for the given user. - */ - @NonNull - public AppSearchImpl getAppSearchImpl(@NonNull UserHandle userHandle) { - Objects.requireNonNull(userHandle); - synchronized (mInstancesLocked) { - AppSearchImpl instance = mInstancesLocked.get(userHandle); - if (instance == null) { - // Impossible scenario, user cannot call an uninitialized SearchSession, - // getInstance should always find the instance for the given user and never try to - // create an instance for this user again. - throw new IllegalStateException( - "AppSearchImpl has never been created for: " + userHandle); - } - return instance; - } - } - - private AppSearchImpl createImpl( - @NonNull Context userContext, - @NonNull UserHandle userHandle, - @Nullable AppSearchLogger logger) - throws AppSearchException { - File appSearchDir = getAppSearchDir(userHandle); - File icingDir = new File(appSearchDir, "icing"); - Log.i(TAG, "Creating new AppSearch instance at: " + icingDir); - return AppSearchImpl.create( - icingDir, - userContext, - /*logger=*/ null, - new FrameworkOptimizeStrategy()); - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java index 4a1a9ae3546b..77a1bb402bd3 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java @@ -39,7 +39,6 @@ import android.app.appsearch.SetSchemaResponse; import android.app.appsearch.StorageInfo; import android.app.appsearch.exceptions.AppSearchException; import android.app.appsearch.util.LogUtil; -import android.content.Context; import android.os.Bundle; import android.os.SystemClock; import android.util.ArrayMap; @@ -157,9 +156,6 @@ public final class AppSearchImpl implements Closeable { @VisibleForTesting final IcingSearchEngine mIcingSearchEngineLocked; - @GuardedBy("mReadWriteLock") - private final VisibilityStore mVisibilityStoreLocked; - // This map contains schema types and SchemaTypeConfigProtos for all package-database // prefixes. It maps each package-database prefix to an inner-map. The inner-map maps each // prefixed schema type to its respective SchemaTypeConfigProto. @@ -195,56 +191,27 @@ public final class AppSearchImpl implements Closeable { * <p>Instead, logger instance needs to be passed to each individual method, like create, query * and putDocument. * - * @param logger collects stats for initialization if provided. + * @param initStatsBuilder collects stats for initialization if provided. */ @NonNull public static AppSearchImpl create( @NonNull File icingDir, - @NonNull Context userContext, - @Nullable AppSearchLogger logger, + @Nullable InitializeStats.Builder initStatsBuilder, @NonNull OptimizeStrategy optimizeStrategy) throws AppSearchException { - Objects.requireNonNull(icingDir); - Objects.requireNonNull(userContext); - Objects.requireNonNull(optimizeStrategy); - - long totalLatencyStartMillis = SystemClock.elapsedRealtime(); - InitializeStats.Builder initStatsBuilder = null; - if (logger != null) { - initStatsBuilder = new InitializeStats.Builder(); - } - - AppSearchImpl appSearchImpl = - new AppSearchImpl( - icingDir, userContext, initStatsBuilder, optimizeStrategy); - - long prepareVisibilityStoreLatencyStartMillis = SystemClock.elapsedRealtime(); - appSearchImpl.initializeVisibilityStore(); - long prepareVisibilityStoreLatencyEndMillis = SystemClock.elapsedRealtime(); - - if (logger != null) { - initStatsBuilder - .setTotalLatencyMillis( - (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis)) - .setPrepareVisibilityStoreLatencyMillis( - (int) - (prepareVisibilityStoreLatencyEndMillis - - prepareVisibilityStoreLatencyStartMillis)); - logger.logStats(initStatsBuilder.build()); - } - - return appSearchImpl; + return new AppSearchImpl(icingDir, initStatsBuilder, optimizeStrategy); } /** @param initStatsBuilder collects stats for initialization if provided. */ private AppSearchImpl( @NonNull File icingDir, - @NonNull Context userContext, @Nullable InitializeStats.Builder initStatsBuilder, @NonNull OptimizeStrategy optimizeStrategy) throws AppSearchException { - mReadWriteLock.writeLock().lock(); + Objects.requireNonNull(icingDir); + mOptimizeStrategy = Objects.requireNonNull(optimizeStrategy); + mReadWriteLock.writeLock().lock(); try { // We synchronize here because we don't want to call IcingSearchEngine.initialize() more // than once. It's unnecessary and can be a costly operation. @@ -258,9 +225,6 @@ public final class AppSearchImpl implements Closeable { "Constructing IcingSearchEngine, response", Objects.hashCode(mIcingSearchEngineLocked)); - mVisibilityStoreLocked = new VisibilityStore(this, userContext); - mOptimizeStrategy = optimizeStrategy; - // The core initialization procedure. If any part of this fails, we bail into // resetLocked(), deleting all data (but hopefully allowing AppSearchImpl to come up). try { @@ -342,23 +306,6 @@ public final class AppSearchImpl implements Closeable { } } - /** - * Initialize the visibility store in AppSearchImpl. - * - * @throws AppSearchException on IcingSearchEngine error. - */ - void initializeVisibilityStore() throws AppSearchException { - mReadWriteLock.writeLock().lock(); - try { - throwIfClosedLocked(); - mLogUtil.piiTrace("Initializing VisibilityStore, request"); - mVisibilityStoreLocked.initialize(); - mLogUtil.piiTrace("Initializing VisibilityStore, response"); - } finally { - mReadWriteLock.writeLock().unlock(); - } - } - @GuardedBy("mReadWriteLock") private void throwIfClosedLocked() { if (mClosedLocked) { @@ -399,6 +346,8 @@ public final class AppSearchImpl implements Closeable { * @param packageName The package name that owns the schemas. * @param databaseName The name of the database where this schema lives. * @param schemas Schemas to set for this app. + * @param visibilityStore If set, {@code schemasNotPlatformSurfaceable} and {@code + * schemasPackageAccessible} will be saved here if the schema is successfully applied. * @param schemasNotPlatformSurfaceable Schema types that should not be surfaced on platform * surfaces. * @param schemasPackageAccessible Schema types that are visible to the specified packages. @@ -416,6 +365,7 @@ public final class AppSearchImpl implements Closeable { @NonNull String packageName, @NonNull String databaseName, @NonNull List<AppSearchSchema> schemas, + @Nullable VisibilityStore visibilityStore, @NonNull List<String> schemasNotPlatformSurfaceable, @NonNull Map<String, List<PackageIdentifier>> schemasPackageAccessible, boolean forceOverride, @@ -479,25 +429,27 @@ public final class AppSearchImpl implements Closeable { removeFromMap(mSchemaMapLocked, prefix, schemaType); } - Set<String> prefixedSchemasNotPlatformSurfaceable = - new ArraySet<>(schemasNotPlatformSurfaceable.size()); - for (int i = 0; i < schemasNotPlatformSurfaceable.size(); i++) { - prefixedSchemasNotPlatformSurfaceable.add( - prefix + schemasNotPlatformSurfaceable.get(i)); - } + if (visibilityStore != null) { + Set<String> prefixedSchemasNotPlatformSurfaceable = + new ArraySet<>(schemasNotPlatformSurfaceable.size()); + for (int i = 0; i < schemasNotPlatformSurfaceable.size(); i++) { + prefixedSchemasNotPlatformSurfaceable.add( + prefix + schemasNotPlatformSurfaceable.get(i)); + } - Map<String, List<PackageIdentifier>> prefixedSchemasPackageAccessible = - new ArrayMap<>(schemasPackageAccessible.size()); - for (Map.Entry<String, List<PackageIdentifier>> entry : - schemasPackageAccessible.entrySet()) { - prefixedSchemasPackageAccessible.put(prefix + entry.getKey(), entry.getValue()); - } + Map<String, List<PackageIdentifier>> prefixedSchemasPackageAccessible = + new ArrayMap<>(schemasPackageAccessible.size()); + for (Map.Entry<String, List<PackageIdentifier>> entry : + schemasPackageAccessible.entrySet()) { + prefixedSchemasPackageAccessible.put(prefix + entry.getKey(), entry.getValue()); + } - mVisibilityStoreLocked.setVisibility( - packageName, - databaseName, - prefixedSchemasNotPlatformSurfaceable, - prefixedSchemasPackageAccessible); + visibilityStore.setVisibility( + packageName, + databaseName, + prefixedSchemasNotPlatformSurfaceable, + prefixedSchemasPackageAccessible); + } return SetSchemaResponseToProtoConverter.toSetSchemaResponse( setSchemaResultProto, prefix); @@ -817,7 +769,11 @@ public final class AppSearchImpl implements Closeable { * @param searchSpec Spec for setting filters, raw query etc. * @param callerPackageName Package name of the caller, should belong to the {@code * userContext}. + * @param visibilityStore Optional visibility store to obtain system and package visibility + * settings from * @param callerUid UID of the client making the globalQuery call. + * @param callerHasSystemAccess Whether the caller has been positively identified as having + * access to schemas marked system surfaceable. * @param logger logger to collect globalQuery stats * @return The results of performing this search. It may contain an empty list of results if no * documents matched the query. @@ -828,7 +784,9 @@ public final class AppSearchImpl implements Closeable { @NonNull String queryExpression, @NonNull SearchSpec searchSpec, @NonNull String callerPackageName, + @Nullable VisibilityStore visibilityStore, int callerUid, + boolean callerHasSystemAccess, @Nullable AppSearchLogger logger) throws AppSearchException { long totalLatencyStartMillis = SystemClock.elapsedRealtime(); @@ -885,15 +843,18 @@ public final class AppSearchImpl implements Closeable { if (packageName.equals(callerPackageName)) { // Callers can always retrieve their own data allow = true; + } else if (visibilityStore == null) { + // If there's no visibility store, there's no extra access + allow = false; } else { String databaseName = getDatabaseName(prefixedSchema); allow = - mVisibilityStoreLocked.isSchemaSearchableByCaller( + visibilityStore.isSchemaSearchableByCaller( packageName, databaseName, prefixedSchema, - callerPackageName, - callerUid); + callerUid, + callerHasSystemAccess); } if (!allow) { @@ -1488,9 +1449,6 @@ public final class AppSearchImpl implements Closeable { /** * Clears documents and schema across all packages and databaseNames. * - * <p>This method also clear all data in {@link VisibilityStore}, an {@link - * #initializeVisibilityStore()} must be called after this. - * * <p>This method belongs to mutate group. * * @throws AppSearchException on IcingSearchEngine error. @@ -1514,9 +1472,6 @@ public final class AppSearchImpl implements Closeable { .setResetStatusCode(statusProtoToResultCode(resetResultProto.getStatus())); } - // Must be called after everything else since VisibilityStore may repopulate - // IcingSearchEngine with an initial schema. - mVisibilityStoreLocked.handleReset(); checkSuccess(resetResultProto.getStatus()); } @@ -2075,13 +2030,6 @@ public final class AppSearchImpl implements Closeable { return result; } - @GuardedBy("mReadWriteLock") - @NonNull - @VisibleForTesting - VisibilityStore getVisibilityStoreLocked() { - return mVisibilityStoreLocked; - } - /** * Converts an erroneous status code from the Icing status enums to the AppSearchResult enums. * diff --git a/apex/appsearch/service/java/com/android/server/appsearch/stats/LoggerInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/stats/LoggerInstanceManager.java deleted file mode 100644 index ea00f506b47f..000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/stats/LoggerInstanceManager.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2021 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.appsearch.stats; - -import android.annotation.NonNull; -import android.content.Context; -import android.os.UserHandle; -import android.util.ArrayMap; - -import com.android.internal.annotations.GuardedBy; -import com.android.server.appsearch.AppSearchConfig; -import com.android.server.appsearch.AppSearchManagerService; - -import java.util.Map; -import java.util.Objects; - -/** - * Manages the lifecycle of instances of {@link PlatformLogger}. - * - * <p>These instances are managed per unique device-user. - */ -public final class LoggerInstanceManager { - private static volatile LoggerInstanceManager sLoggerInstanceManager; - - @GuardedBy("mInstancesLocked") - private final Map<UserHandle, PlatformLogger> mInstancesLocked = new ArrayMap<>(); - - private LoggerInstanceManager() { - } - - /** - * Gets an instance of {@link LoggerInstanceManager} to be used. - * - * <p>If no instance has been initialized yet, a new one will be created. Otherwise, the - * existing instance will be returned. - */ - @NonNull - public static LoggerInstanceManager getInstance() { - if (sLoggerInstanceManager == null) { - synchronized (LoggerInstanceManager.class) { - if (sLoggerInstanceManager == null) { - sLoggerInstanceManager = - new LoggerInstanceManager(); - } - } - } - return sLoggerInstanceManager; - } - - /** - * Gets an instance of PlatformLogger for the given user, or creates one if none exists. - * - * @param context The context - * @param userHandle The multi-user handle of the device user calling AppSearch - * @return An initialized {@link PlatformLogger} for this user - */ - @NonNull - public PlatformLogger getOrCreatePlatformLogger( - @NonNull Context context, @NonNull UserHandle userHandle, - @NonNull AppSearchConfig config) { - Objects.requireNonNull(userHandle); - synchronized (mInstancesLocked) { - PlatformLogger instance = mInstancesLocked.get(userHandle); - if (instance == null) { - instance = new PlatformLogger(context, userHandle, config); - mInstancesLocked.put(userHandle, instance); - } - return instance; - } - } - - /** - * Gets an instance of PlatformLogger for the given user. - * - * <p>This method should only be called by an initialized SearchSession, which has been already - * created the PlatformLogger instance for the given user. - * - * @param userHandle The multi-user handle of the device user calling AppSearch - * @return An initialized {@link PlatformLogger} for this user - * @throws IllegalStateException if {@link PlatformLogger} haven't created for the given user. - */ - @NonNull - public PlatformLogger getPlatformLogger(@NonNull UserHandle userHandle) { - Objects.requireNonNull(userHandle); - synchronized (mInstancesLocked) { - PlatformLogger instance = mInstancesLocked.get(userHandle); - if (instance == null) { - // Impossible scenario, user cannot call an uninitialized SearchSession, - // getInstance should always find the instance for the given user and never try to - // create an instance for this user again. - throw new IllegalStateException( - "PlatformLogger has never been created for: " + userHandle); - } - return instance; - } - } - - /** - * Remove an instance of {@link PlatformLogger} for the given user. - * - * <p>This method should only be called if {@link AppSearchManagerService} receives an - * ACTION_USER_REMOVED, which the logger instance of given user should be removed. - * - * @param userHandle The multi-user handle of the user that need to be removed. - */ - public void removePlatformLoggerForUser(@NonNull UserHandle userHandle) { - Objects.requireNonNull(userHandle); - synchronized (mInstancesLocked) { - mInstancesLocked.remove(userHandle); - } - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java index 5ad4276d9318..9e36fd02569f 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java @@ -73,9 +73,4 @@ class NotPlatformSurfaceableMap { // isn't one of those opt-outs, it's surfaceable. return !schemaTypes.contains(prefixedSchema); } - - /** Discards all data in the map. */ - public void clear() { - mMap.clear(); - } } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java index 2b3934718aed..cff729aa4e8a 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java @@ -82,9 +82,4 @@ class PackageAccessibleMap { } return accessiblePackages; } - - /** Discards all data in the map. */ - public void clear() { - mMap.clear(); - } } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStore.java index a9f6adb20225..ae1ec56b4ee6 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStore.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStore.java @@ -99,28 +99,23 @@ public class VisibilityStore { private final PackageAccessibleMap mPackageAccessibleMap = new PackageAccessibleMap(); /** - * Creates an uninitialized VisibilityStore object. Callers must also call {@link #initialize()} - * before using the object. + * Creates and initializes VisibilityStore. * * @param appSearchImpl AppSearchImpl instance * @param userContext Context of the user that the call is being made as */ - public VisibilityStore(@NonNull AppSearchImpl appSearchImpl, @NonNull Context userContext) { + @NonNull + public static VisibilityStore create( + @NonNull AppSearchImpl appSearchImpl, @NonNull Context userContext) + throws AppSearchException { + return new VisibilityStore(appSearchImpl, userContext); + } + + private VisibilityStore(@NonNull AppSearchImpl appSearchImpl, @NonNull Context userContext) + throws AppSearchException { mAppSearchImpl = Objects.requireNonNull(appSearchImpl); mUserContext = Objects.requireNonNull(userContext); - } - /** - * Initializes schemas and member variables to track visibility settings. - * - * <p>This is kept separate from the constructor because this will call methods on - * AppSearchImpl. Some may even then recursively call back into VisibilityStore (for example, - * {@link AppSearchImpl#setSchema} will call {@link #setVisibility}. We need to have both - * AppSearchImpl and VisibilityStore fully initialized for this call flow to work. - * - * @throws AppSearchException AppSearchException on AppSearchImpl error. - */ - public void initialize() throws AppSearchException { GetSchemaResponse getSchemaResponse = mAppSearchImpl.getSchema(PACKAGE_NAME, DATABASE_NAME); boolean hasVisibilityType = false; boolean hasPackageAccessibleType = false; @@ -142,6 +137,7 @@ public class VisibilityStore { PACKAGE_NAME, DATABASE_NAME, Arrays.asList(VisibilityDocument.SCHEMA, PackageAccessibleDocument.SCHEMA), + /*visibilityStore=*/ null, // Avoid recursive calls /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -149,7 +145,6 @@ public class VisibilityStore { } // Populate visibility settings set - mNotPlatformSurfaceableMap.clear(); for (Map.Entry<String, Set<String>> entry : mAppSearchImpl.getPackageToDatabases().entrySet()) { String packageName = entry.getKey(); @@ -281,38 +276,45 @@ public class VisibilityStore { } /** + * Checks whether the given package has access to system-surfaceable schemas. + * + * @param callerPackageName Package name of the caller. + */ + public boolean doesCallerHaveSystemAccess(@NonNull String callerPackageName) { + Objects.requireNonNull(callerPackageName); + return mUserContext.getPackageManager() + .checkPermission(READ_GLOBAL_APP_SEARCH_DATA, callerPackageName) + == PackageManager.PERMISSION_GRANTED; + } + + /** * Checks whether {@code prefixedSchema} can be searched over by the {@code callerUid}. * * @param packageName Package that owns the schema. * @param databaseName Database within the package that owns the schema. * @param prefixedSchema Prefixed schema type the caller is trying to access. - * @param callerPackageName Package name of the caller. - * @param callerUid Uid of the caller. + * @param callerUid UID of the client making the globalQuery call. + * @param callerHasSystemAccess Whether the caller has been identified as having + * access to schemas marked system surfaceable by {@link + * #doesCallerHaveSystemAccess}. */ public boolean isSchemaSearchableByCaller( @NonNull String packageName, @NonNull String databaseName, @NonNull String prefixedSchema, - @NonNull String callerPackageName, - int callerUid) { + int callerUid, + boolean callerHasSystemAccess) { Objects.requireNonNull(packageName); Objects.requireNonNull(databaseName); Objects.requireNonNull(prefixedSchema); - Objects.requireNonNull(callerPackageName); if (packageName.equals(PACKAGE_NAME)) { return false; // VisibilityStore schemas are for internal bookkeeping. } - // TODO(b/180058203): If we can cache or pass in that a caller has the - // READ_GLOBAL_SEARCH_DATA permission, then we can save this package manager lookup for - // each schema we may check in the loop. - if (mNotPlatformSurfaceableMap.isSchemaPlatformSurfaceable( - packageName, databaseName, prefixedSchema) - && mUserContext - .getPackageManager() - .checkPermission(READ_GLOBAL_APP_SEARCH_DATA, callerPackageName) - == PackageManager.PERMISSION_GRANTED) { + if (callerHasSystemAccess + && mNotPlatformSurfaceableMap.isSchemaPlatformSurfaceable( + packageName, databaseName, prefixedSchema)) { return true; } @@ -373,16 +375,6 @@ public class VisibilityStore { } /** - * Handles an {@code AppSearchImpl#reset()} by clearing any cached state. - * - * <p>{@link #initialize()} must be called after this. - */ - public void handleReset() { - mNotPlatformSurfaceableMap.clear(); - mPackageAccessibleMap.clear(); - } - - /** * Adds a prefix to create a visibility store document's id. * * @param packageName Package to which the visibility doc refers |