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

Commit bd7bca2d authored by Alexander Dorokhine's avatar Alexander Dorokhine
Browse files

Merge AppSearchImpl Jetpack work from last two quarters.

This switches AppSearch from FakeIcing to the real libicing.

Bug: 162450968
Test: AppSearchManagerTest, AppSearchImplTest
Change-Id: I9ecbe4ce229e4ac9756aa187ad82ba60420a644e
parent bf10852a
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -47,6 +47,10 @@ import java.util.Objects;
public class AppSearchDocument {
    private static final String TAG = "AppSearchDocument";

    /** The default empty namespace.*/
    // TODO(adorokhine): Allow namespace to be specified in the document.
    public static final String DEFAULT_NAMESPACE = "";

    /**
     * The maximum number of elements in a repeatable field. Will reject the request if exceed
     * this limit.
@@ -450,7 +454,7 @@ public class AppSearchDocument {
         */
        public Builder(@NonNull String uri, @NonNull String schemaType) {
            mBuilderTypeInstance = (BuilderType) this;
            mProtoBuilder.setUri(uri).setSchema(schemaType);
            mProtoBuilder.setUri(uri).setSchema(schemaType).setNamespace(DEFAULT_NAMESPACE);
            // Set current timestamp for creation timestamp by default.
            setCreationTimestampMillis(System.currentTimeMillis());
        }
+15 −8
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 * Copyright 2020 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.
@@ -14,31 +14,38 @@
 * limitations under the License.
 */

package com.android.server.appsearch;
package android.app.appsearch.exceptions;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.appsearch.AppSearchResult;

/**
 * An exception thrown by {@link com.android.server.appsearch.AppSearchManagerService} or a
 * subcomponent.
 * An exception thrown by {@code android.app.appsearch.AppSearchManager} or a subcomponent.
 *
 * <p>These exceptions can be converted into a failed {@link android.app.appsearch.AppSearchResult}
 * <p>These exceptions can be converted into a failed {@link AppSearchResult}
 * for propagating to the client.
 * @hide
 */
//TODO(b/157082794): Linkify to AppSearchManager once that API is public
public class AppSearchException extends Exception {
    private final @AppSearchResult.ResultCode int mResultCode;

    /** Initializes an {@link com.android.server.appsearch.AppSearchException} with no message. */
    /**
     * Initializes an {@link AppSearchException} with no message.
     * @hide
     */
    public AppSearchException(@AppSearchResult.ResultCode int resultCode) {
        this(resultCode, /*message=*/ null);
    }

    /** @hide */
    public AppSearchException(
            @AppSearchResult.ResultCode int resultCode, @Nullable String message) {
        this(resultCode, message, /*cause=*/ null);
    }

    /** @hide */
    public AppSearchException(
            @AppSearchResult.ResultCode int resultCode,
            @Nullable String message,
@@ -48,9 +55,9 @@ public class AppSearchException extends Exception {
    }

    /**
     * Converts this {@link java.lang.Exception} into a failed
     * {@link android.app.appsearch.AppSearchResult}
     * Converts this {@link java.lang.Exception} into a failed {@link AppSearchResult}
     */
    @NonNull
    public <T> AppSearchResult<T> toAppSearchResult() {
        return AppSearchResult.newFailedResult(mResultCode, getMessage());
    }
+7 −1
Original line number Diff line number Diff line
@@ -20,7 +20,13 @@ java_library {
        "framework-appsearch",
        "services.core",
    ],
    static_libs: ["icing-java-proto-lite"],
    static_libs: [
        "icing-java-proto-lite",
        "libicing-java",
    ],
    required: [
        "libicing",
    ],
    jarjar_rules: "jarjar-rules.txt",
    apex_available: ["com.android.appsearch"],
}
+41 −22
Original line number Diff line number Diff line
@@ -17,8 +17,10 @@ package com.android.server.appsearch;

import android.annotation.NonNull;
import android.app.appsearch.AppSearchBatchResult;
import android.app.appsearch.AppSearchDocument;
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.IAppSearchManager;
import android.app.appsearch.exceptions.AppSearchException;
import android.content.Context;
import android.os.Binder;
import android.os.UserHandle;
@@ -26,8 +28,7 @@ import android.os.UserHandle;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.Preconditions;
import com.android.server.SystemService;
import com.android.server.appsearch.impl.AppSearchImpl;
import com.android.server.appsearch.impl.ImplInstanceManager;
import com.android.server.appsearch.external.localbackend.AppSearchImpl;

import com.google.android.icing.proto.DocumentProto;
import com.google.android.icing.proto.ResultSpecProto;
@@ -44,6 +45,7 @@ import java.util.List;
 * TODO(b/142567528): add comments when implement this class
 */
public class AppSearchManagerService extends SystemService {
    private static final String TAG = "AppSearchManagerService";

    public AppSearchManagerService(Context context) {
        super(context);
@@ -68,7 +70,8 @@ public class AppSearchManagerService extends SystemService {
            try {
                SchemaProto schema = SchemaProto.parseFrom(schemaBytes);
                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
                impl.setSchema(callingUid, schema, forceOverride);
                String databaseName = makeDatabaseName(callingUid);
                impl.setSchema(databaseName, schema, forceOverride);
                callback.complete(AppSearchResult.newSuccessfulResult(/*value=*/ null));
            } catch (Throwable t) {
                callback.complete(throwableToFailedResult(t));
@@ -88,13 +91,14 @@ public class AppSearchManagerService extends SystemService {
            long callingIdentity = Binder.clearCallingIdentity();
            try {
                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
                String databaseName = makeDatabaseName(callingUid);
                AppSearchBatchResult.Builder<String, Void> resultBuilder =
                        new AppSearchBatchResult.Builder<>();
                for (int i = 0; i < documentsBytes.size(); i++) {
                    byte[] documentBytes = (byte[]) documentsBytes.get(i);
                    DocumentProto document = DocumentProto.parseFrom(documentBytes);
                    try {
                        impl.putDocument(callingUid, document);
                        impl.putDocument(databaseName, document);
                        resultBuilder.setSuccess(document.getUri(), /*value=*/ null);
                    } catch (Throwable t) {
                        resultBuilder.setResult(document.getUri(), throwableToFailedResult(t));
@@ -118,12 +122,14 @@ public class AppSearchManagerService extends SystemService {
            long callingIdentity = Binder.clearCallingIdentity();
            try {
                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
                String databaseName = makeDatabaseName(callingUid);
                AppSearchBatchResult.Builder<String, byte[]> resultBuilder =
                        new AppSearchBatchResult.Builder<>();
                for (int i = 0; i < uris.size(); i++) {
                    String uri = uris.get(i);
                    try {
                        DocumentProto document = impl.getDocument(callingUid, uri);
                        DocumentProto document = impl.getDocument(
                                databaseName, AppSearchDocument.DEFAULT_NAMESPACE, uri);
                        if (document == null) {
                            resultBuilder.setFailure(
                                    uri, AppSearchResult.RESULT_NOT_FOUND, /*errorMessage=*/ null);
@@ -161,8 +167,9 @@ public class AppSearchManagerService extends SystemService {
                ResultSpecProto resultSpecProto = ResultSpecProto.parseFrom(resultSpecBytes);
                ScoringSpecProto scoringSpecProto = ScoringSpecProto.parseFrom(scoringSpecBytes);
                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
                SearchResultProto searchResultProto =
                        impl.query(callingUid, searchSpecProto, resultSpecProto, scoringSpecProto);
                String databaseName = makeDatabaseName(callingUid);
                SearchResultProto searchResultProto = impl.query(
                        databaseName, searchSpecProto, resultSpecProto, scoringSpecProto);
                // TODO(sidchhabra): Translate SearchResultProto errors into error codes. This might
                //     better be done in AppSearchImpl by throwing an AppSearchException.
                if (searchResultProto.getStatus().getCode() != StatusProto.Code.OK) {
@@ -190,17 +197,14 @@ public class AppSearchManagerService extends SystemService {
            long callingIdentity = Binder.clearCallingIdentity();
            try {
                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
                String databaseName = makeDatabaseName(callingUid);
                AppSearchBatchResult.Builder<String, Void> resultBuilder =
                        new AppSearchBatchResult.Builder<>();
                for (int i = 0; i < uris.size(); i++) {
                    String uri = uris.get(i);
                    try {
                        if (!impl.delete(callingUid, uri)) {
                            resultBuilder.setFailure(
                                    uri, AppSearchResult.RESULT_NOT_FOUND, /*errorMessage=*/ null);
                        } else {
                        impl.remove(databaseName, AppSearchDocument.DEFAULT_NAMESPACE, uri);
                        resultBuilder.setSuccess(uri, /*value= */null);
                        }
                    } catch (Throwable t) {
                        resultBuilder.setResult(uri, throwableToFailedResult(t));
                    }
@@ -223,19 +227,14 @@ public class AppSearchManagerService extends SystemService {
            long callingIdentity = Binder.clearCallingIdentity();
            try {
                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
                String databaseName = makeDatabaseName(callingUid);
                AppSearchBatchResult.Builder<String, Void> resultBuilder =
                        new AppSearchBatchResult.Builder<>();
                for (int i = 0; i < schemaTypes.size(); i++) {
                    String schemaType = schemaTypes.get(i);
                    try {
                        if (!impl.deleteByType(callingUid, schemaType)) {
                            resultBuilder.setFailure(
                                    schemaType,
                                    AppSearchResult.RESULT_NOT_FOUND,
                                    /*errorMessage=*/ null);
                        } else {
                        impl.removeByType(databaseName, schemaType);
                        resultBuilder.setSuccess(schemaType, /*value=*/ null);
                        }
                    } catch (Throwable t) {
                        resultBuilder.setResult(schemaType, throwableToFailedResult(t));
                    }
@@ -256,7 +255,8 @@ public class AppSearchManagerService extends SystemService {
            long callingIdentity = Binder.clearCallingIdentity();
            try {
                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
                impl.deleteAll(callingUid);
                String databaseName = makeDatabaseName(callingUid);
                impl.removeAll(databaseName);
                callback.complete(AppSearchResult.newSuccessfulResult(null));
            } catch (Throwable t) {
                callback.complete(throwableToFailedResult(t));
@@ -265,6 +265,25 @@ public class AppSearchManagerService extends SystemService {
            }
        }

        /**
         * Returns a unique database name for the given uid.
         *
         * <p>The current implementation returns the package name of the app with this uid in a
         * format like {@code com.example.package} or {@code com.example.sharedname:5678}.
         */
        @NonNull
        private String makeDatabaseName(int callingUid) {
            // For regular apps, this call will return the package name. If callingUid is an
            // android:sharedUserId, this value may be another type of name and have a :uid suffix.
            String callingUidName = getContext().getPackageManager().getNameForUid(callingUid);
            if (callingUidName == null) {
                // Not sure how this is possible --- maybe app was uninstalled?
                throw new IllegalStateException(
                        "Failed to look up package name for uid " + callingUid);
            }
            return callingUidName;
        }

        private <ValueType> AppSearchResult<ValueType> throwableToFailedResult(
                @NonNull Throwable t) {
            if (t instanceof AppSearchException) {
+30 −3
Original line number Diff line number Diff line
@@ -14,21 +14,32 @@
 * limitations under the License.
 */

package com.android.server.appsearch.impl;
package com.android.server.appsearch;

import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.appsearch.exceptions.AppSearchException;
import android.content.Context;
import android.os.Environment;
import android.os.storage.StorageManager;
import android.util.SparseArray;

import com.android.server.appsearch.external.localbackend.AppSearchImpl;

import java.io.File;

/**
 * Manages the lifecycle of instances of {@link AppSearchImpl}.
 *
 * <p>These instances are managed per unique device-user.
 */
public final class ImplInstanceManager {
    private static final String APP_SEARCH_DIR = "appSearch";

    private static final SparseArray<AppSearchImpl> sInstances = new SparseArray<>();

    private ImplInstanceManager() {}

    /**
     * Gets an instance of AppSearchImpl for the given user.
     *
@@ -40,17 +51,33 @@ public final class ImplInstanceManager {
     * @return An initialized {@link AppSearchImpl} for this user
     */
    @NonNull
    public static AppSearchImpl getInstance(@NonNull Context context, @UserIdInt int userId) {
    public static AppSearchImpl getInstance(@NonNull Context context, @UserIdInt int userId)
            throws AppSearchException {
        AppSearchImpl instance = sInstances.get(userId);
        if (instance == null) {
            synchronized (ImplInstanceManager.class) {
                instance = sInstances.get(userId);
                if (instance == null) {
                    instance = new AppSearchImpl(context, userId);
                    instance = createImpl(context, userId);
                    sInstances.put(userId, instance);
                }
            }
        }
        return instance;
    }

    private static AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId)
            throws AppSearchException {
        File appSearchDir = getAppSearchDir(context, userId);
        AppSearchImpl appSearchImpl = new AppSearchImpl(appSearchDir);
        appSearchImpl.initialize();
        return appSearchImpl;
    }

    private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) {
        // See com.android.internal.app.ChooserActivity::getPinnedSharedPrefs
        File userCeDir = Environment.getDataUserCePackageDirectory(
                StorageManager.UUID_PRIVATE_INTERNAL, userId, context.getPackageName());
        return new File(userCeDir, APP_SEARCH_DIR);
    }
}
Loading