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

Commit c5a7432e authored by Tony Mak's avatar Tony Mak
Browse files

Make TCS.getDefaultTextClassifierImplementation returns a ...

SystemTextClassifier that is backed by ExtServices

1. The default textclassifier is always provided by
   config_servicesExtensionPackage (i.e. ExtServices)
2. OEM can specify a system text classifier by specifying
   config_defaultTextClassifierPackage.
3. System text classifiers can get an instance of the default textclassifier
   by calling TCS.getDefaultTextClassifierImplementation(), so that
   they can add their customization on top of the default TCS.
4. If config_systemTextClassifierPackage is set, the specified package
    is used to process requests from apps and the platform. Otherwise,
   the default textclassifier is used.
5. For testing and droidfooding purpose, text classifier service package
   can be overridden. If the overridden package is neither
   the default one nor the system one, the package is considered as a
   untrusted text classifier, which can only see requests from itself.

Test: m mts && mts-tradefed run mts-extservices
Test: Not setting config_defaultTextClassifierPackage. Select some text and
      make sure smart selection works. Run dumpsys textclassification
      to make sure the default TCS is bound.
Test: Setting config_defaultTextClassifierPackage and repeat the above.
      Make sure the specified OEM text classifier is used.
Test: Set the service override config to be ExtServices.
      Run dumpsys textclassification to make sure everything is unbound.
      Select some text and make sure ExtServices is bound.
Test: Set the service override to be AiAi and repeat the above.
Test: Set the service to be something invalid and repeat the above.
      Observe that no TC is bound and fallback to NO_OP.

BUG: 148049185

Change-Id: Ia2fb549fda49363e0d0ebc4b7e0d31cb76e11ee0
Make TCS.getDefaultTextClassifierImplementation returns a ...
SystemTextClassifier that is backed by ExtServices

1. The default textclassifier is always provided by
   config_servicesExtensionPackage (i.e. ExtServices)
2. OEM can specify a system text classifier by specifying
   config_defaultTextClassifierPackage.
3. System text classifiers can get an instance of the default textclassifier
   by calling TCS.getDefaultTextClassifierImplementation(), so that
   they can add their customization on top of the default TCS.
4. If config_systemTextClassifierPackage is set, the specified package
    is used to process requests from apps and the platform. Otherwise,
   the default textclassifier is used.
5. For testing and droidfooding purpose, text classifier service package
   can be overridden. If the overridden package is neither
   the default one nor the system one, the package is considered as a
   untrusted text classifier, which can only see requests from itself.

Test: m mts && mts-tradefed run mts-extservices
Test: Not setting config_defaultTextClassifierPackage. Select some text and
      make sure smart selection works. Run dumpsys textclassification
      to make sure the default TCS is bound.
Test: Setting config_defaultTextClassifierPackage and repeat the above.
      Make sure the specified OEM text classifier is used.
Test: Set the service override config to be ExtServices.
      Run dumpsys textclassification to make sure everything is unbound.
      Select some text and make sure ExtServices is bound.
Test: Set the service override to be AiAi and repeat the above.
Test: Set the service to be something invalid and repeat the above.
      Observe that no TC is bound and fallback to NO_OP.

BUG: 148049185

Change-Id: Ia2fb549fda49363e0d0ebc4b7e0d31cb76e11ee0
parent 73fb9236
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -885,6 +885,7 @@ package android.content.pm {
    method public abstract boolean arePermissionsIndividuallyControlled();
    method @Nullable public String getContentCaptureServicePackageName();
    method @Nullable @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL") public abstract String getDefaultBrowserPackageNameAsUser(int);
    method @Nullable public String getDefaultTextClassifierPackageName();
    method @Nullable public String getIncidentReportApproverPackageName();
    method public abstract int getInstallReason(@NonNull String, @NonNull android.os.UserHandle);
    method @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
@@ -894,6 +895,7 @@ package android.content.pm {
    method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS", "android.permission.GET_RUNTIME_PERMISSIONS"}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
    method @NonNull public abstract String getServicesSystemSharedLibraryPackageName();
    method @NonNull public abstract String getSharedSystemSharedLibraryPackageName();
    method @Nullable public String getSystemTextClassifierPackageName();
    method @Nullable public String[] getTelephonyPackageNames();
    method @Nullable public String getWellbeingPackageName();
    method @RequiresPermission("android.permission.GRANT_RUNTIME_PERMISSIONS") public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
+4 −4
Original line number Diff line number Diff line
@@ -3229,18 +3229,18 @@ public class ApplicationPackageManager extends PackageManager {
    }

    @Override
    public String getSystemTextClassifierPackageName() {
    public String getDefaultTextClassifierPackageName() {
        try {
            return mPM.getSystemTextClassifierPackageName();
            return mPM.getDefaultTextClassifierPackageName();
        } catch (RemoteException e) {
            throw e.rethrowAsRuntimeException();
        }
    }

    @Override
    public String[] getSystemTextClassifierPackages() {
    public String getSystemTextClassifierPackageName() {
        try {
            return mPM.getSystemTextClassifierPackages();
            return mPM.getSystemTextClassifierPackageName();
        } catch (RemoteException e) {
            throw e.rethrowAsRuntimeException();
        }
+2 −2
Original line number Diff line number Diff line
@@ -679,9 +679,9 @@ interface IPackageManager {

    boolean hasUidSigningCertificate(int uid, in byte[] signingCertificate, int flags);

    String getSystemTextClassifierPackageName();
    String getDefaultTextClassifierPackageName();

    String[] getSystemTextClassifierPackages();
    String getSystemTextClassifierPackageName();

    String getAttentionServicePackageName();

+8 −6
Original line number Diff line number Diff line
@@ -7592,14 +7592,15 @@ public abstract class PackageManager {
    }

    /**
     * @return the system defined text classifier package name, or null if there's none.
     * @return the default text classifier package name, or null if there's none.
     *
     * @hide
     */
    @Nullable
    public String getSystemTextClassifierPackageName() {
    @TestApi
    public String getDefaultTextClassifierPackageName() {
        throw new UnsupportedOperationException(
                "getSystemTextClassifierPackageName not implemented in subclass");
                "getDefaultTextClassifierPackageName not implemented in subclass");
    }

    /**
@@ -7607,10 +7608,11 @@ public abstract class PackageManager {
     *
     * @hide
     */
    @NonNull
    public String[] getSystemTextClassifierPackages() {
    @Nullable
    @TestApi
    public String getSystemTextClassifierPackageName() {
        throw new UnsupportedOperationException(
                "getSystemTextClassifierPackages not implemented in subclass");
                "getSystemTextClassifierPackageName not implemented in subclass");
    }

    /**
+24 −39
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Bundle;
@@ -42,7 +41,6 @@ import android.util.Slog;
import android.view.textclassifier.ConversationActions;
import android.view.textclassifier.SelectionEvent;
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassificationConstants;
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassificationSessionId;
@@ -394,19 +392,32 @@ public abstract class TextClassifierService extends Service {
     */
    @Deprecated
    public final TextClassifier getLocalTextClassifier() {
        // Deprecated: In the future, we may not guarantee that this runs in the service's process.
        return getDefaultTextClassifierImplementation(this);
        return TextClassifier.NO_OP;
    }

    /**
     * Returns the platform's default TextClassifier implementation.
     *
     * @throws RuntimeException if the TextClassifier from
     *                          PackageManager#getDefaultTextClassifierPackageName() calls
     *                          this method.
     */
    @NonNull
    public static TextClassifier getDefaultTextClassifierImplementation(@NonNull Context context) {
        final String defaultTextClassifierPackageName =
                context.getPackageManager().getDefaultTextClassifierPackageName();
        if (TextUtils.isEmpty(defaultTextClassifierPackageName)) {
            return TextClassifier.NO_OP;
        }
        if (defaultTextClassifierPackageName.equals(context.getPackageName())) {
            throw new RuntimeException(
                    "The default text classifier itself should not call the"
                            + "getDefaultTextClassifierImplementation() method.");
        }
        final TextClassificationManager tcm =
                context.getSystemService(TextClassificationManager.class);
        if (tcm != null) {
            return tcm.getTextClassifier(TextClassifier.LOCAL);
            return tcm.getTextClassifier(TextClassifier.DEFAULT_SERVICE);
        }
        return TextClassifier.NO_OP;
    }
@@ -434,46 +445,20 @@ public abstract class TextClassifierService extends Service {
    }

    /**
     * Returns the component name of the system default textclassifier service if it can be found
     * on the system. Otherwise, returns null.
     * Returns the component name of the textclassifier service from the given package.
     * Otherwise, returns null.
     *
     * @param context the text classification context
     * @param context
     * @param packageName  the package to look for.
     * @param resolveFlags the flags that are used by PackageManager to resolve the component name.
     * @hide
     */
    @Nullable
    public static ComponentName getServiceComponentName(@NonNull Context context) {
        final TextClassificationConstants settings = TextClassificationManager.getSettings(context);
        // get override TextClassifierService package name
        String packageName = settings.getTextClassifierServicePackageOverride();

        ComponentName serviceComponent = null;
        final boolean isOverrideService = !TextUtils.isEmpty(packageName);
        if (isOverrideService) {
            serviceComponent = getServiceComponentNameByPackage(context, packageName,
                    isOverrideService);
        }
        if (serviceComponent != null) {
            return serviceComponent;
        }
        // If no TextClassifierService override or invalid override package name, read the first
        // package defined in the config
        final String[] packages = context.getPackageManager().getSystemTextClassifierPackages();
        if (packages.length == 0 || TextUtils.isEmpty(packages[0])) {
            Slog.d(LOG_TAG, "No configured system TextClassifierService");
            return null;
        }
        packageName = packages[0];
        serviceComponent = getServiceComponentNameByPackage(context, packageName,
                isOverrideService);
        return serviceComponent;
    }

    private static ComponentName getServiceComponentNameByPackage(Context context,
            String packageName, boolean isOverrideService) {
    public static ComponentName getServiceComponentName(
            Context context, String packageName, int resolveFlags) {
        final Intent intent = new Intent(SERVICE_INTERFACE).setPackage(packageName);

        final int flags = isOverrideService ? 0 : PackageManager.MATCH_SYSTEM_ONLY;
        final ResolveInfo ri = context.getPackageManager().resolveService(intent, flags);
        final ResolveInfo ri = context.getPackageManager().resolveService(intent, resolveFlags);

        if ((ri == null) || (ri.serviceInfo == null)) {
            Slog.w(LOG_TAG, String.format("Package or service not found in package %s for user %d",
Loading