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
This commit is contained in:
@@ -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);
|
||||
verifyCallingPackage(callingUser, callingUid, packageName);
|
||||
AppSearchUserInstance instance =
|
||||
mAppSearchUserInstanceManager.getUserInstance(callingUser);
|
||||
|
||||
if (systemUsage) {
|
||||
// TODO(b/183031844): Validate that the call comes from the system
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,9 +82,4 @@ class PackageAccessibleMap {
|
||||
}
|
||||
return accessiblePackages;
|
||||
}
|
||||
|
||||
/** Discards all data in the map. */
|
||||
public void clear() {
|
||||
mMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
mAppSearchImpl = Objects.requireNonNull(appSearchImpl);
|
||||
mUserContext = Objects.requireNonNull(userContext);
|
||||
@NonNull
|
||||
public static VisibilityStore create(
|
||||
@NonNull AppSearchImpl appSearchImpl, @NonNull Context userContext)
|
||||
throws AppSearchException {
|
||||
return new VisibilityStore(appSearchImpl, 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 {
|
||||
private VisibilityStore(@NonNull AppSearchImpl appSearchImpl, @NonNull Context userContext)
|
||||
throws AppSearchException {
|
||||
mAppSearchImpl = Objects.requireNonNull(appSearchImpl);
|
||||
mUserContext = Objects.requireNonNull(userContext);
|
||||
|
||||
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();
|
||||
@@ -280,39 +275,46 @@ public class VisibilityStore {
|
||||
packageName, databaseName, schemaToPackageIdentifierMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
@@ -372,16 +374,6 @@ public class VisibilityStore {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
||||
@@ -42,6 +42,7 @@ import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.compatibility.common.util.SystemUtil;
|
||||
import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
|
||||
import com.android.server.appsearch.visibilitystore.VisibilityStore;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
@@ -66,6 +67,7 @@ public class AppSearchImplPlatformTest {
|
||||
private final Map<UserHandle, PackageManager> mMockPackageManagers = new ArrayMap<>();
|
||||
private Context mContext;
|
||||
private AppSearchImpl mAppSearchImpl;
|
||||
private VisibilityStore mVisibilityStore;
|
||||
private int mGlobalQuerierUid;
|
||||
|
||||
@Before
|
||||
@@ -89,13 +91,9 @@ public class AppSearchImplPlatformTest {
|
||||
};
|
||||
|
||||
// Give ourselves global query permissions
|
||||
mAppSearchImpl =
|
||||
AppSearchImpl.create(
|
||||
mTemporaryFolder.newFolder(),
|
||||
mContext,
|
||||
/*logger=*/ null,
|
||||
ALWAYS_OPTIMIZE);
|
||||
|
||||
mAppSearchImpl = AppSearchImpl.create(
|
||||
mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
|
||||
mVisibilityStore = VisibilityStore.create(mAppSearchImpl, mContext);
|
||||
mGlobalQuerierUid =
|
||||
mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
|
||||
}
|
||||
@@ -128,6 +126,7 @@ public class AppSearchImplPlatformTest {
|
||||
"package",
|
||||
"database",
|
||||
Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
|
||||
mVisibilityStore,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
|
||||
/*schemasPackageAccessible=*/ ImmutableMap.of(
|
||||
"schema1",
|
||||
@@ -136,26 +135,20 @@ public class AppSearchImplPlatformTest {
|
||||
/*schemaVersion=*/ 0);
|
||||
|
||||
// "schema1" is platform hidden now and package visible to package1
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema1",
|
||||
mContext.getPackageName(),
|
||||
mGlobalQuerierUid))
|
||||
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema1",
|
||||
mGlobalQuerierUid,
|
||||
/*callerHasSystemAccess=*/ true))
|
||||
.isFalse();
|
||||
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema1",
|
||||
packageNameFoo,
|
||||
uidFoo))
|
||||
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema1",
|
||||
uidFoo,
|
||||
/*callerHasSystemAccess=*/ false))
|
||||
.isTrue();
|
||||
|
||||
// Add a new schema, and include the already-existing "schema1"
|
||||
@@ -165,6 +158,7 @@ public class AppSearchImplPlatformTest {
|
||||
ImmutableList.of(
|
||||
new AppSearchSchema.Builder("schema1").build(),
|
||||
new AppSearchSchema.Builder("schema2").build()),
|
||||
mVisibilityStore,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
|
||||
/*schemasPackageAccessible=*/ ImmutableMap.of(
|
||||
"schema1",
|
||||
@@ -174,50 +168,40 @@ public class AppSearchImplPlatformTest {
|
||||
|
||||
// Check that "schema1" still has the same visibility settings
|
||||
SystemUtil.runWithShellPermissionIdentity(() -> assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema1",
|
||||
mContext.getPackageName(),
|
||||
mGlobalQuerierUid))
|
||||
mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema1",
|
||||
mGlobalQuerierUid,
|
||||
/*callerHasSystemAccess=*/ true))
|
||||
.isFalse(),
|
||||
READ_GLOBAL_APP_SEARCH_DATA);
|
||||
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema1",
|
||||
packageNameFoo,
|
||||
uidFoo))
|
||||
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema1",
|
||||
uidFoo,
|
||||
/*callerHasSystemAccess=*/ false))
|
||||
.isTrue();
|
||||
|
||||
// "schema2" has default visibility settings
|
||||
SystemUtil.runWithShellPermissionIdentity(() -> assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema2",
|
||||
mContext.getPackageName(),
|
||||
mGlobalQuerierUid))
|
||||
mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema2",
|
||||
mGlobalQuerierUid,
|
||||
/*callerHasSystemAccess=*/ true))
|
||||
.isTrue(),
|
||||
READ_GLOBAL_APP_SEARCH_DATA);
|
||||
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema2",
|
||||
packageNameFoo,
|
||||
uidFoo))
|
||||
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema2",
|
||||
uidFoo,
|
||||
/*callerHasSystemAccess=*/ false))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@@ -248,6 +232,7 @@ public class AppSearchImplPlatformTest {
|
||||
"package",
|
||||
"database",
|
||||
Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
|
||||
mVisibilityStore,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
|
||||
/*schemasPackageAccessible=*/ ImmutableMap.of(
|
||||
"schema1",
|
||||
@@ -256,26 +241,20 @@ public class AppSearchImplPlatformTest {
|
||||
/*schemaVersion=*/ 0);
|
||||
|
||||
// "schema1" is platform hidden now and package accessible
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema1",
|
||||
mContext.getPackageName(),
|
||||
mGlobalQuerierUid))
|
||||
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema1",
|
||||
mGlobalQuerierUid,
|
||||
/*callerHasSystemAccess=*/ true))
|
||||
.isFalse();
|
||||
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema1",
|
||||
packageNameFoo,
|
||||
uidFoo))
|
||||
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema1",
|
||||
uidFoo,
|
||||
/*callerHasSystemAccess=*/ false))
|
||||
.isTrue();
|
||||
|
||||
// Remove "schema1" by force overriding
|
||||
@@ -283,32 +262,27 @@ public class AppSearchImplPlatformTest {
|
||||
"package",
|
||||
"database",
|
||||
/*schemas=*/ Collections.emptyList(),
|
||||
mVisibilityStore,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ true,
|
||||
/*schemaVersion=*/ 0);
|
||||
|
||||
// Check that "schema1" is no longer considered platform hidden or package accessible
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema1",
|
||||
mContext.getPackageName(),
|
||||
mGlobalQuerierUid))
|
||||
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema1",
|
||||
mGlobalQuerierUid,
|
||||
/*callerHasSystemAccess=*/ true))
|
||||
.isTrue();
|
||||
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema1",
|
||||
packageNameFoo,
|
||||
uidFoo))
|
||||
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema1",
|
||||
uidFoo,
|
||||
/*callerHasSystemAccess=*/ false))
|
||||
.isFalse();
|
||||
|
||||
// Add "schema1" back, it gets default visibility settings which means it's not platform
|
||||
@@ -317,30 +291,25 @@ public class AppSearchImplPlatformTest {
|
||||
"package",
|
||||
"database",
|
||||
Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
|
||||
mVisibilityStore,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
/*schemaVersion=*/ 0);
|
||||
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema1",
|
||||
mContext.getPackageName(),
|
||||
mGlobalQuerierUid))
|
||||
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema1",
|
||||
mGlobalQuerierUid,
|
||||
/*callerHasSystemAccess=*/ true))
|
||||
.isTrue();
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema1",
|
||||
packageNameFoo,
|
||||
uidFoo))
|
||||
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "schema1",
|
||||
uidFoo,
|
||||
/*callerHasSystemAccess=*/ false))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@@ -357,20 +326,18 @@ public class AppSearchImplPlatformTest {
|
||||
"package",
|
||||
"database",
|
||||
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
|
||||
mVisibilityStore,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
/*schemaVersion=*/ 0);
|
||||
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "Schema",
|
||||
mContext.getPackageName(),
|
||||
mGlobalQuerierUid))
|
||||
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "Schema",
|
||||
mGlobalQuerierUid,
|
||||
/*callerHasSystemAccess=*/ true))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@@ -387,20 +354,18 @@ public class AppSearchImplPlatformTest {
|
||||
"package",
|
||||
"database",
|
||||
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
|
||||
mVisibilityStore,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.singletonList("Schema"),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
/*schemaVersion=*/ 0);
|
||||
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "Schema",
|
||||
mContext.getPackageName(),
|
||||
mGlobalQuerierUid))
|
||||
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "Schema",
|
||||
mGlobalQuerierUid,
|
||||
/*callerHasSystemAccess=*/ true))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@@ -418,19 +383,18 @@ public class AppSearchImplPlatformTest {
|
||||
"package",
|
||||
"database",
|
||||
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
|
||||
mVisibilityStore,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
/*schemaVersion=*/ 0);
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
assertThat(mVisibilityStore
|
||||
.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "Schema",
|
||||
packageName,
|
||||
/*callerUid=*/ 42))
|
||||
/*callerUid=*/ 42,
|
||||
/*callerHasSystemAccess=*/ false))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@@ -458,21 +422,20 @@ public class AppSearchImplPlatformTest {
|
||||
"package",
|
||||
"database",
|
||||
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
|
||||
mVisibilityStore,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ ImmutableMap.of(
|
||||
"Schema",
|
||||
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
|
||||
/*forceOverride=*/ false,
|
||||
/*schemaVersion=*/ 0);
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
assertThat(mVisibilityStore
|
||||
.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
prefix + "Schema",
|
||||
packageNameFoo,
|
||||
uidFoo))
|
||||
uidFoo,
|
||||
/*callerHasSystemAccess=*/ false))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
|
||||
@@ -88,15 +88,10 @@ public class VisibilityStoreTest {
|
||||
};
|
||||
|
||||
// Give ourselves global query permissions
|
||||
AppSearchImpl appSearchImpl =
|
||||
AppSearchImpl.create(
|
||||
mTemporaryFolder.newFolder(),
|
||||
mContext,
|
||||
/*logger=*/ null,
|
||||
ALWAYS_OPTIMIZE);
|
||||
|
||||
AppSearchImpl appSearchImpl = AppSearchImpl.create(
|
||||
mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
|
||||
mVisibilityStore = VisibilityStore.create(appSearchImpl, mContext);
|
||||
mUid = mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
|
||||
mVisibilityStore = appSearchImpl.getVisibilityStoreLocked();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,6 +116,21 @@ public class VisibilityStoreTest {
|
||||
.doesNotContain(String.valueOf(PrefixUtil.DATABASE_DELIMITER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoesCallerHaveSystemAccess() {
|
||||
PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
|
||||
when(mockPackageManager
|
||||
.checkPermission(READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName()))
|
||||
.thenReturn(PERMISSION_GRANTED);
|
||||
assertThat(mVisibilityStore.doesCallerHaveSystemAccess(mContext.getPackageName())).isTrue();
|
||||
|
||||
when(mockPackageManager
|
||||
.checkPermission(READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName()))
|
||||
.thenReturn(PERMISSION_DENIED);
|
||||
assertThat(mVisibilityStore.doesCallerHaveSystemAccess(mContext.getPackageName()))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetVisibility_platformSurfaceable() throws Exception {
|
||||
// Make sure we have global query privileges
|
||||
@@ -128,6 +138,7 @@ public class VisibilityStoreTest {
|
||||
when(mockPackageManager
|
||||
.checkPermission(READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName()))
|
||||
.thenReturn(PERMISSION_GRANTED);
|
||||
assertThat(mVisibilityStore.doesCallerHaveSystemAccess(mContext.getPackageName())).isTrue();
|
||||
|
||||
mVisibilityStore.setVisibility(
|
||||
"package",
|
||||
@@ -140,16 +151,16 @@ public class VisibilityStoreTest {
|
||||
"package",
|
||||
"database",
|
||||
"prefix/schema1",
|
||||
mContext.getPackageName(),
|
||||
mUid))
|
||||
mUid,
|
||||
/*callerHasSystemAccess=*/ true))
|
||||
.isFalse();
|
||||
assertThat(
|
||||
mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
"prefix/schema2",
|
||||
mContext.getPackageName(),
|
||||
mUid))
|
||||
mUid,
|
||||
/*callerHasSystemAccess=*/ true))
|
||||
.isFalse();
|
||||
|
||||
// New .setVisibility() call completely overrides previous visibility settings.
|
||||
@@ -165,24 +176,24 @@ public class VisibilityStoreTest {
|
||||
"package",
|
||||
"database",
|
||||
"prefix/schema1",
|
||||
mContext.getPackageName(),
|
||||
mUid))
|
||||
mUid,
|
||||
/*callerHasSystemAccess=*/ true))
|
||||
.isFalse();
|
||||
assertThat(
|
||||
mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
"prefix/schema2",
|
||||
mContext.getPackageName(),
|
||||
mUid))
|
||||
mUid,
|
||||
/*callerHasSystemAccess=*/ true))
|
||||
.isTrue();
|
||||
assertThat(
|
||||
mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
"prefix/schema3",
|
||||
mContext.getPackageName(),
|
||||
mUid))
|
||||
mUid,
|
||||
/*callerHasSystemAccess=*/ true))
|
||||
.isFalse();
|
||||
|
||||
// Everything defaults to visible again.
|
||||
@@ -196,24 +207,24 @@ public class VisibilityStoreTest {
|
||||
"package",
|
||||
"database",
|
||||
"prefix/schema1",
|
||||
mContext.getPackageName(),
|
||||
mUid))
|
||||
mUid,
|
||||
/*callerHasSystemAccess=*/ true))
|
||||
.isTrue();
|
||||
assertThat(
|
||||
mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
"prefix/schema2",
|
||||
mContext.getPackageName(),
|
||||
mUid))
|
||||
mUid,
|
||||
/*callerHasSystemAccess=*/ true))
|
||||
.isTrue();
|
||||
assertThat(
|
||||
mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
"prefix/schema3",
|
||||
mContext.getPackageName(),
|
||||
mUid))
|
||||
mUid,
|
||||
/*callerHasSystemAccess=*/ true))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@@ -242,13 +253,19 @@ public class VisibilityStoreTest {
|
||||
.thenReturn(PERMISSION_DENIED);
|
||||
|
||||
// By default, a schema isn't package accessible.
|
||||
assertThat(
|
||||
mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package", "database", "prefix/schemaFoo", packageNameFoo, uidFoo))
|
||||
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
"prefix/schemaFoo",
|
||||
uidFoo,
|
||||
/*callerHasSystemAccess=*/ false))
|
||||
.isFalse();
|
||||
assertThat(
|
||||
mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package", "database", "prefix/schemaBar", packageNameBar, uidBar))
|
||||
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
"prefix/schemaBar",
|
||||
uidBar,
|
||||
/*callerHasSystemAccess=*/ false))
|
||||
.isFalse();
|
||||
|
||||
// Grant package access
|
||||
@@ -268,9 +285,12 @@ public class VisibilityStoreTest {
|
||||
when(mockPackageManager.hasSigningCertificate(
|
||||
packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
|
||||
.thenReturn(false);
|
||||
assertThat(
|
||||
mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package", "database", "prefix/schemaFoo", packageNameFoo, uidFoo))
|
||||
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
"prefix/schemaFoo",
|
||||
uidFoo,
|
||||
/*callerHasSystemAccess=*/ false))
|
||||
.isFalse();
|
||||
|
||||
// Should fail if PackageManager doesn't think the package belongs to the uid
|
||||
@@ -279,9 +299,12 @@ public class VisibilityStoreTest {
|
||||
when(mockPackageManager.hasSigningCertificate(
|
||||
packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
|
||||
.thenReturn(true);
|
||||
assertThat(
|
||||
mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package", "database", "prefix/schemaFoo", packageNameFoo, uidFoo))
|
||||
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
"prefix/schemaFoo",
|
||||
uidFoo,
|
||||
/*callerHasSystemAccess=*/ false))
|
||||
.isFalse();
|
||||
|
||||
// But if uid and certificate match, then we should have access
|
||||
@@ -290,9 +313,12 @@ public class VisibilityStoreTest {
|
||||
when(mockPackageManager.hasSigningCertificate(
|
||||
packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
|
||||
.thenReturn(true);
|
||||
assertThat(
|
||||
mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package", "database", "prefix/schemaFoo", packageNameFoo, uidFoo))
|
||||
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
"prefix/schemaFoo",
|
||||
uidFoo,
|
||||
/*callerHasSystemAccess=*/ false))
|
||||
.isTrue();
|
||||
|
||||
when(mockPackageManager.getPackageUid(eq(packageNameBar), /*flags=*/ anyInt()))
|
||||
@@ -300,9 +326,12 @@ public class VisibilityStoreTest {
|
||||
when(mockPackageManager.hasSigningCertificate(
|
||||
packageNameBar, sha256CertBar, PackageManager.CERT_INPUT_SHA256))
|
||||
.thenReturn(true);
|
||||
assertThat(
|
||||
mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package", "database", "prefix/schemaBar", packageNameBar, uidBar))
|
||||
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
"prefix/schemaBar",
|
||||
uidBar,
|
||||
/*callerHasSystemAccess=*/ false))
|
||||
.isTrue();
|
||||
|
||||
// New .setVisibility() call completely overrides previous visibility settings. So
|
||||
@@ -320,9 +349,12 @@ public class VisibilityStoreTest {
|
||||
when(mockPackageManager.hasSigningCertificate(
|
||||
packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
|
||||
.thenReturn(true);
|
||||
assertThat(
|
||||
mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package", "database", "prefix/schemaFoo", packageNameFoo, uidFoo))
|
||||
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
"prefix/schemaFoo",
|
||||
uidFoo,
|
||||
/*callerHasSystemAccess=*/ false))
|
||||
.isTrue();
|
||||
|
||||
when(mockPackageManager.getPackageUid(eq(packageNameBar), /*flags=*/ anyInt()))
|
||||
@@ -330,9 +362,12 @@ public class VisibilityStoreTest {
|
||||
when(mockPackageManager.hasSigningCertificate(
|
||||
packageNameBar, sha256CertBar, PackageManager.CERT_INPUT_SHA256))
|
||||
.thenReturn(true);
|
||||
assertThat(
|
||||
mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package", "database", "prefix/schemaBar", packageNameBar, uidBar))
|
||||
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
"prefix/schemaBar",
|
||||
uidBar,
|
||||
/*callerHasSystemAccess=*/ false))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@@ -363,9 +398,12 @@ public class VisibilityStoreTest {
|
||||
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))));
|
||||
|
||||
// If we can't verify the Foo package that has access, assume it doesn't have access.
|
||||
assertThat(
|
||||
mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package", "database", "prefix/schemaFoo", packageNameFoo, uidFoo))
|
||||
assertThat(mVisibilityStore.isSchemaSearchableByCaller(
|
||||
"package",
|
||||
"database",
|
||||
"prefix/schemaFoo",
|
||||
uidFoo,
|
||||
/*callerHasSystemAccess=*/ false))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@@ -397,8 +435,8 @@ public class VisibilityStoreTest {
|
||||
/*packageName=*/ "",
|
||||
/*databaseName=*/ "",
|
||||
"schema",
|
||||
mContext.getPackageName(),
|
||||
mUid))
|
||||
mUid,
|
||||
/*callerHasSystemAccess=*/ true))
|
||||
.isTrue();
|
||||
|
||||
when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt()))
|
||||
@@ -411,8 +449,8 @@ public class VisibilityStoreTest {
|
||||
/*packageName=*/ "",
|
||||
/*databaseName=*/ "",
|
||||
"schema",
|
||||
packageNameFoo,
|
||||
uidFoo))
|
||||
uidFoo,
|
||||
/*callerHasSystemAccess=*/ false))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
|
||||
@@ -83,15 +83,9 @@ public class AppSearchImplTest {
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
// Give ourselves global query permissions.
|
||||
mAppSearchImpl =
|
||||
AppSearchImpl.create(
|
||||
mTemporaryFolder.newFolder(),
|
||||
context,
|
||||
/*logger=*/ null,
|
||||
ALWAYS_OPTIMIZE);
|
||||
mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,7 +143,7 @@ public class AppSearchImplTest {
|
||||
.build();
|
||||
|
||||
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
|
||||
mAppSearchImpl.rewriteSchema(
|
||||
AppSearchImpl.rewriteSchema(
|
||||
createPrefix("package", "newDatabase"), existingSchemaBuilder, newSchema);
|
||||
|
||||
// We rewrote all the new types that were added. And nothing was removed.
|
||||
@@ -249,7 +243,7 @@ public class AppSearchImplTest {
|
||||
.build();
|
||||
|
||||
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
|
||||
mAppSearchImpl.rewriteSchema(
|
||||
AppSearchImpl.rewriteSchema(
|
||||
createPrefix("package", "existingDatabase"),
|
||||
existingSchemaBuilder,
|
||||
newSchema);
|
||||
@@ -284,7 +278,7 @@ public class AppSearchImplTest {
|
||||
.build();
|
||||
|
||||
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
|
||||
mAppSearchImpl.rewriteSchema(
|
||||
AppSearchImpl.rewriteSchema(
|
||||
createPrefix("package", "existingDatabase"),
|
||||
existingSchemaBuilder,
|
||||
newSchema);
|
||||
@@ -386,7 +380,7 @@ public class AppSearchImplTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveDatabasesFromDocumentThrowsException() throws Exception {
|
||||
public void testRemoveDatabasesFromDocumentThrowsException() {
|
||||
// Set two different database names in the document, which should never happen
|
||||
DocumentProto documentProto =
|
||||
DocumentProto.newBuilder()
|
||||
@@ -403,7 +397,7 @@ public class AppSearchImplTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedRemoveDatabasesFromDocumentThrowsException() throws Exception {
|
||||
public void testNestedRemoveDatabasesFromDocumentThrowsException() {
|
||||
// Set two different database names in the outer and inner document, which should never
|
||||
// happen.
|
||||
DocumentProto insideDocument =
|
||||
@@ -436,6 +430,7 @@ public class AppSearchImplTest {
|
||||
"package",
|
||||
"database",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -473,11 +468,7 @@ public class AppSearchImplTest {
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
File appsearchDir = mTemporaryFolder.newFolder();
|
||||
AppSearchImpl appSearchImpl =
|
||||
AppSearchImpl.create(
|
||||
appsearchDir,
|
||||
context,
|
||||
/*logger=*/ null,
|
||||
ALWAYS_OPTIMIZE);
|
||||
AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
|
||||
|
||||
// Insert schema
|
||||
List<AppSearchSchema> schemas =
|
||||
@@ -488,6 +479,7 @@ public class AppSearchImplTest {
|
||||
context.getPackageName(),
|
||||
"database1",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -506,7 +498,9 @@ public class AppSearchImplTest {
|
||||
/*queryExpression=*/ "",
|
||||
new SearchSpec.Builder().addFilterSchemas("Type1").build(),
|
||||
context.getPackageName(),
|
||||
/*visibilityStore=*/ null,
|
||||
VisibilityStore.NO_OP_UID,
|
||||
/*callerHasSystemAccess=*/ false,
|
||||
/*logger=*/ null);
|
||||
assertThat(results.getResults()).hasSize(1);
|
||||
assertThat(results.getResults().get(0).getGenericDocument()).isEqualTo(validDoc);
|
||||
@@ -532,18 +526,13 @@ public class AppSearchImplTest {
|
||||
PutResultProto putResultProto = appSearchImpl.mIcingSearchEngineLocked.put(invalidDoc);
|
||||
assertThat(putResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.OK);
|
||||
|
||||
// Create a logger for capturing initialization to make sure we are logging the recovery
|
||||
// process correctly.
|
||||
AppSearchLoggerTest.TestLogger testLogger = new AppSearchLoggerTest.TestLogger();
|
||||
|
||||
// Initialize AppSearchImpl. This should cause a reset.
|
||||
InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
|
||||
appSearchImpl.close();
|
||||
appSearchImpl =
|
||||
AppSearchImpl.create(
|
||||
appsearchDir, context, testLogger, ALWAYS_OPTIMIZE);
|
||||
appSearchImpl = AppSearchImpl.create(appsearchDir, initStatsBuilder, ALWAYS_OPTIMIZE);
|
||||
|
||||
// Check recovery state
|
||||
InitializeStats initStats = testLogger.mInitializeStats;
|
||||
InitializeStats initStats = initStatsBuilder.build();
|
||||
assertThat(initStats).isNotNull();
|
||||
assertThat(initStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_INTERNAL_ERROR);
|
||||
assertThat(initStats.hasDeSync()).isFalse();
|
||||
@@ -568,7 +557,9 @@ public class AppSearchImplTest {
|
||||
/*queryExpression=*/ "",
|
||||
new SearchSpec.Builder().addFilterSchemas("Type1").build(),
|
||||
context.getPackageName(),
|
||||
/*visibilityStore=*/ null,
|
||||
VisibilityStore.NO_OP_UID,
|
||||
/*callerHasSystemAccess=*/ false,
|
||||
/*logger=*/ null);
|
||||
assertThat(results.getResults()).isEmpty();
|
||||
|
||||
@@ -577,6 +568,7 @@ public class AppSearchImplTest {
|
||||
context.getPackageName(),
|
||||
"database1",
|
||||
Collections.singletonList(new AppSearchSchema.Builder("Type1").build()),
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -592,7 +584,9 @@ public class AppSearchImplTest {
|
||||
/*queryExpression=*/ "",
|
||||
new SearchSpec.Builder().addFilterSchemas("Type1").build(),
|
||||
context.getPackageName(),
|
||||
/*visibilityStore=*/ null,
|
||||
VisibilityStore.NO_OP_UID,
|
||||
/*callerHasSystemAccess=*/ false,
|
||||
/*logger=*/ null);
|
||||
assertThat(results.getResults()).hasSize(1);
|
||||
assertThat(results.getResults().get(0).getGenericDocument()).isEqualTo(validDoc);
|
||||
@@ -609,6 +603,7 @@ public class AppSearchImplTest {
|
||||
"package",
|
||||
"database",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -642,6 +637,7 @@ public class AppSearchImplTest {
|
||||
"package",
|
||||
"database1",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -650,6 +646,7 @@ public class AppSearchImplTest {
|
||||
"package",
|
||||
"database2",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -694,6 +691,7 @@ public class AppSearchImplTest {
|
||||
"package",
|
||||
"database",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -735,6 +733,7 @@ public class AppSearchImplTest {
|
||||
"package1",
|
||||
"database1",
|
||||
schema1,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -747,6 +746,7 @@ public class AppSearchImplTest {
|
||||
"package2",
|
||||
"database2",
|
||||
schema2,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -789,6 +789,7 @@ public class AppSearchImplTest {
|
||||
"package1",
|
||||
"database1",
|
||||
schema1,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -801,6 +802,7 @@ public class AppSearchImplTest {
|
||||
"package2",
|
||||
"database2",
|
||||
schema2,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -848,7 +850,9 @@ public class AppSearchImplTest {
|
||||
"",
|
||||
searchSpec,
|
||||
/*callerPackageName=*/ "",
|
||||
/*visibilityStore=*/ null,
|
||||
VisibilityStore.NO_OP_UID,
|
||||
/*callerHasSystemAccess=*/ false,
|
||||
/*logger=*/ null);
|
||||
assertThat(searchResultPage.getResults()).isEmpty();
|
||||
}
|
||||
@@ -888,6 +892,7 @@ public class AppSearchImplTest {
|
||||
"package",
|
||||
"database1",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -911,9 +916,6 @@ public class AppSearchImplTest {
|
||||
|
||||
@Test
|
||||
public void testSetSchema_incompatible() throws Exception {
|
||||
List<SchemaTypeConfigProto> existingSchemas =
|
||||
mAppSearchImpl.getSchemaProtoLocked().getTypesList();
|
||||
|
||||
List<AppSearchSchema> oldSchemas = new ArrayList<>();
|
||||
oldSchemas.add(
|
||||
new AppSearchSchema.Builder("Email")
|
||||
@@ -935,6 +937,7 @@ public class AppSearchImplTest {
|
||||
"package",
|
||||
"database1",
|
||||
oldSchemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -950,6 +953,7 @@ public class AppSearchImplTest {
|
||||
"package",
|
||||
"database1",
|
||||
newSchemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ true,
|
||||
@@ -972,6 +976,7 @@ public class AppSearchImplTest {
|
||||
"package",
|
||||
"database1",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -1004,6 +1009,7 @@ public class AppSearchImplTest {
|
||||
"package",
|
||||
"database1",
|
||||
finalSchemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -1017,6 +1023,7 @@ public class AppSearchImplTest {
|
||||
"package",
|
||||
"database1",
|
||||
finalSchemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ true,
|
||||
@@ -1054,6 +1061,7 @@ public class AppSearchImplTest {
|
||||
"package",
|
||||
"database1",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -1062,6 +1070,7 @@ public class AppSearchImplTest {
|
||||
"package",
|
||||
"database2",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -1101,6 +1110,7 @@ public class AppSearchImplTest {
|
||||
"package",
|
||||
"database1",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ true,
|
||||
@@ -1145,6 +1155,7 @@ public class AppSearchImplTest {
|
||||
"package",
|
||||
"database",
|
||||
schema,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -1206,6 +1217,7 @@ public class AppSearchImplTest {
|
||||
"packageA",
|
||||
"database",
|
||||
schema,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -1214,6 +1226,7 @@ public class AppSearchImplTest {
|
||||
"packageB",
|
||||
"database",
|
||||
schema,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -1259,6 +1272,7 @@ public class AppSearchImplTest {
|
||||
"package1",
|
||||
"database1",
|
||||
Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -1272,6 +1286,7 @@ public class AppSearchImplTest {
|
||||
"package1",
|
||||
"database2",
|
||||
Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -1285,6 +1300,7 @@ public class AppSearchImplTest {
|
||||
"package2",
|
||||
"database1",
|
||||
Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -1343,6 +1359,7 @@ public class AppSearchImplTest {
|
||||
"package",
|
||||
"database",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -1493,6 +1510,7 @@ public class AppSearchImplTest {
|
||||
"package1",
|
||||
"database",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -1515,6 +1533,7 @@ public class AppSearchImplTest {
|
||||
"package1",
|
||||
"database",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -1530,6 +1549,7 @@ public class AppSearchImplTest {
|
||||
"package2",
|
||||
"database",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -1578,6 +1598,7 @@ public class AppSearchImplTest {
|
||||
"package1",
|
||||
"database",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -1600,6 +1621,7 @@ public class AppSearchImplTest {
|
||||
"package1",
|
||||
"database1",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -1621,6 +1643,7 @@ public class AppSearchImplTest {
|
||||
"package1",
|
||||
"database1",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -1629,6 +1652,7 @@ public class AppSearchImplTest {
|
||||
"package1",
|
||||
"database2",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -1664,13 +1688,9 @@ public class AppSearchImplTest {
|
||||
|
||||
@Test
|
||||
public void testThrowsExceptionIfClosed() throws Exception {
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
AppSearchImpl appSearchImpl =
|
||||
AppSearchImpl.create(
|
||||
mTemporaryFolder.newFolder(),
|
||||
context,
|
||||
/*logger=*/ null,
|
||||
ALWAYS_OPTIMIZE);
|
||||
mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
|
||||
|
||||
// Initial check that we could do something at first.
|
||||
List<AppSearchSchema> schemas =
|
||||
@@ -1679,6 +1699,7 @@ public class AppSearchImplTest {
|
||||
"package",
|
||||
"database",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -1689,140 +1710,115 @@ public class AppSearchImplTest {
|
||||
// Check all our public APIs
|
||||
expectThrows(
|
||||
IllegalStateException.class,
|
||||
() -> {
|
||||
appSearchImpl.setSchema(
|
||||
"package",
|
||||
"database",
|
||||
schemas,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
/*version=*/ 0);
|
||||
});
|
||||
() ->
|
||||
appSearchImpl.setSchema(
|
||||
"package",
|
||||
"database",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
/*version=*/ 0));
|
||||
|
||||
expectThrows(
|
||||
IllegalStateException.class, () -> appSearchImpl.getSchema("package", "database"));
|
||||
|
||||
expectThrows(
|
||||
IllegalStateException.class,
|
||||
() -> {
|
||||
appSearchImpl.getSchema("package", "database");
|
||||
});
|
||||
() ->
|
||||
appSearchImpl.putDocument(
|
||||
"package",
|
||||
"database",
|
||||
new GenericDocument.Builder<>("namespace", "id", "type").build(),
|
||||
/*logger=*/ null));
|
||||
|
||||
expectThrows(
|
||||
IllegalStateException.class,
|
||||
() -> {
|
||||
appSearchImpl.putDocument(
|
||||
"package",
|
||||
"database",
|
||||
new GenericDocument.Builder<>("namespace", "id", "type").build(),
|
||||
/*logger=*/ null);
|
||||
});
|
||||
() ->
|
||||
appSearchImpl.getDocument(
|
||||
"package", "database", "namespace", "id", Collections.emptyMap()));
|
||||
|
||||
expectThrows(
|
||||
IllegalStateException.class,
|
||||
() -> {
|
||||
appSearchImpl.getDocument(
|
||||
"package", "database", "namespace", "id", Collections.emptyMap());
|
||||
});
|
||||
() ->
|
||||
appSearchImpl.query(
|
||||
"package",
|
||||
"database",
|
||||
"query",
|
||||
new SearchSpec.Builder().build(),
|
||||
/*logger=*/ null));
|
||||
|
||||
expectThrows(
|
||||
IllegalStateException.class,
|
||||
() -> {
|
||||
appSearchImpl.query(
|
||||
"package",
|
||||
"database",
|
||||
"query",
|
||||
new SearchSpec.Builder()
|
||||
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
|
||||
.build(),
|
||||
/*logger=*/ null);
|
||||
});
|
||||
() ->
|
||||
appSearchImpl.globalQuery(
|
||||
"query",
|
||||
new SearchSpec.Builder().build(),
|
||||
"package",
|
||||
/*visibilityStore=*/ null,
|
||||
VisibilityStore.NO_OP_UID,
|
||||
/*callerHasSystemAccess=*/ false,
|
||||
/*logger=*/ null));
|
||||
|
||||
expectThrows(
|
||||
IllegalStateException.class,
|
||||
() -> {
|
||||
appSearchImpl.globalQuery(
|
||||
"query",
|
||||
new SearchSpec.Builder()
|
||||
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
|
||||
.build(),
|
||||
"package",
|
||||
VisibilityStore.NO_OP_UID,
|
||||
/*logger=*/ null);
|
||||
});
|
||||
() -> appSearchImpl.getNextPage(/*nextPageToken=*/ 1L));
|
||||
|
||||
expectThrows(
|
||||
IllegalStateException.class,
|
||||
() -> {
|
||||
appSearchImpl.getNextPage(/*nextPageToken=*/ 1L);
|
||||
});
|
||||
() -> appSearchImpl.invalidateNextPageToken(/*nextPageToken=*/ 1L));
|
||||
|
||||
expectThrows(
|
||||
IllegalStateException.class,
|
||||
() -> {
|
||||
appSearchImpl.invalidateNextPageToken(/*nextPageToken=*/ 1L);
|
||||
});
|
||||
() ->
|
||||
appSearchImpl.reportUsage(
|
||||
"package",
|
||||
"database",
|
||||
"namespace",
|
||||
"id",
|
||||
/*usageTimestampMillis=*/ 1000L,
|
||||
/*systemUsage=*/ false));
|
||||
|
||||
expectThrows(
|
||||
IllegalStateException.class,
|
||||
() -> {
|
||||
appSearchImpl.reportUsage(
|
||||
"package",
|
||||
"database",
|
||||
"namespace",
|
||||
"id",
|
||||
/*usageTimestampMillis=*/ 1000L,
|
||||
/*systemUsage=*/ false);
|
||||
});
|
||||
() ->
|
||||
appSearchImpl.remove(
|
||||
"package",
|
||||
"database",
|
||||
"namespace",
|
||||
"id",
|
||||
/*removeStatsBuilder=*/ null));
|
||||
|
||||
expectThrows(
|
||||
IllegalStateException.class,
|
||||
() -> {
|
||||
appSearchImpl.remove(
|
||||
"package", "database", "namespace", "id", /*statsBuilder=*/ null);
|
||||
});
|
||||
() ->
|
||||
appSearchImpl.removeByQuery(
|
||||
"package",
|
||||
"database",
|
||||
"query",
|
||||
new SearchSpec.Builder().build(),
|
||||
/*removeStatsBuilder=*/ null));
|
||||
|
||||
expectThrows(
|
||||
IllegalStateException.class,
|
||||
() -> {
|
||||
appSearchImpl.removeByQuery(
|
||||
"package",
|
||||
"database",
|
||||
"query",
|
||||
new SearchSpec.Builder()
|
||||
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
|
||||
.build(),
|
||||
/*statsBuilder=*/ null);
|
||||
});
|
||||
() -> appSearchImpl.getStorageInfoForPackage("package"));
|
||||
|
||||
expectThrows(
|
||||
IllegalStateException.class,
|
||||
() -> {
|
||||
appSearchImpl.getStorageInfoForPackage("package");
|
||||
});
|
||||
() -> appSearchImpl.getStorageInfoForDatabase("package", "database"));
|
||||
|
||||
expectThrows(
|
||||
IllegalStateException.class,
|
||||
() -> {
|
||||
appSearchImpl.getStorageInfoForDatabase("package", "database");
|
||||
});
|
||||
|
||||
expectThrows(
|
||||
IllegalStateException.class,
|
||||
() -> {
|
||||
appSearchImpl.persistToDisk(PersistType.Code.FULL);
|
||||
});
|
||||
() -> appSearchImpl.persistToDisk(PersistType.Code.FULL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutPersistsWithLiteFlush() throws Exception {
|
||||
// Setup the index
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
File appsearchDir = mTemporaryFolder.newFolder();
|
||||
AppSearchImpl appSearchImpl =
|
||||
AppSearchImpl.create(
|
||||
appsearchDir,
|
||||
context,
|
||||
/*logger=*/ null,
|
||||
ALWAYS_OPTIMIZE);
|
||||
AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
|
||||
|
||||
List<AppSearchSchema> schemas =
|
||||
Collections.singletonList(new AppSearchSchema.Builder("type").build());
|
||||
@@ -1830,6 +1826,7 @@ public class AppSearchImplTest {
|
||||
"package",
|
||||
"database",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -1848,11 +1845,7 @@ public class AppSearchImplTest {
|
||||
|
||||
// That document should be visible even from another instance.
|
||||
AppSearchImpl appSearchImpl2 =
|
||||
AppSearchImpl.create(
|
||||
appsearchDir,
|
||||
context,
|
||||
/*logger=*/ null,
|
||||
ALWAYS_OPTIMIZE);
|
||||
AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
|
||||
getResult =
|
||||
appSearchImpl2.getDocument(
|
||||
"package", "database", "namespace1", "id1", Collections.emptyMap());
|
||||
@@ -1862,14 +1855,9 @@ public class AppSearchImplTest {
|
||||
@Test
|
||||
public void testDeletePersistsWithLiteFlush() throws Exception {
|
||||
// Setup the index
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
File appsearchDir = mTemporaryFolder.newFolder();
|
||||
AppSearchImpl appSearchImpl =
|
||||
AppSearchImpl.create(
|
||||
appsearchDir,
|
||||
context,
|
||||
/*logger=*/ null,
|
||||
ALWAYS_OPTIMIZE);
|
||||
AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
|
||||
|
||||
List<AppSearchSchema> schemas =
|
||||
Collections.singletonList(new AppSearchSchema.Builder("type").build());
|
||||
@@ -1877,6 +1865,7 @@ public class AppSearchImplTest {
|
||||
"package",
|
||||
"database",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -1919,11 +1908,7 @@ public class AppSearchImplTest {
|
||||
|
||||
// Only the second document should be retrievable from another instance.
|
||||
AppSearchImpl appSearchImpl2 =
|
||||
AppSearchImpl.create(
|
||||
appsearchDir,
|
||||
context,
|
||||
/*logger=*/ null,
|
||||
ALWAYS_OPTIMIZE);
|
||||
AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
|
||||
expectThrows(
|
||||
AppSearchException.class,
|
||||
() ->
|
||||
@@ -1942,14 +1927,9 @@ public class AppSearchImplTest {
|
||||
@Test
|
||||
public void testDeleteByQueryPersistsWithLiteFlush() throws Exception {
|
||||
// Setup the index
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
File appsearchDir = mTemporaryFolder.newFolder();
|
||||
AppSearchImpl appSearchImpl =
|
||||
AppSearchImpl.create(
|
||||
appsearchDir,
|
||||
context,
|
||||
/*logger=*/ null,
|
||||
ALWAYS_OPTIMIZE);
|
||||
AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
|
||||
|
||||
List<AppSearchSchema> schemas =
|
||||
Collections.singletonList(new AppSearchSchema.Builder("type").build());
|
||||
@@ -1957,6 +1937,7 @@ public class AppSearchImplTest {
|
||||
"package",
|
||||
"database",
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -2007,11 +1988,7 @@ public class AppSearchImplTest {
|
||||
|
||||
// Only the second document should be retrievable from another instance.
|
||||
AppSearchImpl appSearchImpl2 =
|
||||
AppSearchImpl.create(
|
||||
appsearchDir,
|
||||
context,
|
||||
/*logger=*/ null,
|
||||
ALWAYS_OPTIMIZE);
|
||||
AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
|
||||
expectThrows(
|
||||
AppSearchException.class,
|
||||
() ->
|
||||
|
||||
@@ -25,9 +25,6 @@ import android.app.appsearch.AppSearchSchema;
|
||||
import android.app.appsearch.GenericDocument;
|
||||
import android.app.appsearch.SearchResultPage;
|
||||
import android.app.appsearch.SearchSpec;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.server.appsearch.external.localstorage.stats.CallStats;
|
||||
import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
|
||||
@@ -60,15 +57,9 @@ public class AppSearchLoggerTest {
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
// Give ourselves global query permissions
|
||||
mAppSearchImpl =
|
||||
AppSearchImpl.create(
|
||||
mTemporaryFolder.newFolder(),
|
||||
context,
|
||||
/*logger=*/ null,
|
||||
ALWAYS_OPTIMIZE);
|
||||
mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
|
||||
mLogger = new TestLogger();
|
||||
}
|
||||
|
||||
@@ -289,20 +280,15 @@ public class AppSearchLoggerTest {
|
||||
//
|
||||
@Test
|
||||
public void testLoggingStats_initialize() throws Exception {
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
// Create an unused AppSearchImpl to generated an InitializeStats.
|
||||
AppSearchImpl appSearchImpl =
|
||||
AppSearchImpl.create(
|
||||
mTemporaryFolder.newFolder(),
|
||||
context,
|
||||
mLogger,
|
||||
ALWAYS_OPTIMIZE);
|
||||
InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
|
||||
AppSearchImpl.create(mTemporaryFolder.newFolder(), initStatsBuilder, ALWAYS_OPTIMIZE);
|
||||
InitializeStats iStats = initStatsBuilder.build();
|
||||
|
||||
InitializeStats iStats = mLogger.mInitializeStats;
|
||||
assertThat(iStats).isNotNull();
|
||||
assertThat(iStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
|
||||
assertThat(iStats.getTotalLatencyMillis()).isGreaterThan(0);
|
||||
// Total latency captured in LocalStorage
|
||||
assertThat(iStats.getTotalLatencyMillis()).isEqualTo(0);
|
||||
assertThat(iStats.hasDeSync()).isFalse();
|
||||
assertThat(iStats.getNativeLatencyMillis()).isGreaterThan(0);
|
||||
assertThat(iStats.getDocumentStoreDataStatus())
|
||||
@@ -322,6 +308,7 @@ public class AppSearchLoggerTest {
|
||||
testPackageName,
|
||||
testDatabase,
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -350,10 +337,12 @@ public class AppSearchLoggerTest {
|
||||
testPackageName,
|
||||
testDatabase,
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
/*version=*/ 0);
|
||||
|
||||
GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build();
|
||||
mAppSearchImpl.putDocument(testPackageName, testDatabase, document, mLogger);
|
||||
|
||||
@@ -401,6 +390,7 @@ public class AppSearchLoggerTest {
|
||||
testPackageName,
|
||||
testDatabase,
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
@@ -432,6 +422,7 @@ public class AppSearchLoggerTest {
|
||||
testPackageName,
|
||||
testDatabase,
|
||||
schemas,
|
||||
/*visibilityStore=*/ null,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false,
|
||||
|
||||
Reference in New Issue
Block a user