diff options
author | Tony Mak <tonymak@google.com> | 2020-02-04 17:18:15 +0000 |
---|---|---|
committer | Tony Mak <tonymak@google.com> | 2020-02-04 20:36:52 +0000 |
commit | c5a7432ebf31a667f22ebdd8de4354f88675ced0 (patch) | |
tree | 0ae6d38151f88ff0d5ee0f93fd279c36c29b503c | |
parent | 73fb92365e1a431272715da437d70aa4e2c51af2 (diff) |
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
24 files changed, 597 insertions, 298 deletions
diff --git a/api/test-current.txt b/api/test-current.txt index cee560b00abe..fc15a7331651 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -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); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 71cb4a403365..cd05e2c948b1 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -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(); } diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 93126b8002ad..6552d1b5a824 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -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(); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index b64c001ea6e2..eb97070093d1 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -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"); } /** diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java index 8dca69f856e5..848868a5532f 100644 --- a/core/java/android/service/textclassifier/TextClassifierService.java +++ b/core/java/android/service/textclassifier/TextClassifierService.java @@ -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", diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java index 80027b1ed75d..6246b507ba59 100644 --- a/core/java/android/view/textclassifier/ConversationActions.java +++ b/core/java/android/view/textclassifier/ConversationActions.java @@ -323,6 +323,7 @@ public final class ConversationActions implements Parcelable { private int mUserId = UserHandle.USER_NULL; @NonNull private Bundle mExtras; + private boolean mUseDefaultTextClassifier; private Request( @NonNull List<Message> conversation, @@ -347,6 +348,8 @@ public final class ConversationActions implements Parcelable { String callingPackageName = in.readString(); int userId = in.readInt(); Bundle extras = in.readBundle(); + boolean useDefaultTextClassifier = in.readBoolean(); + Request request = new Request( conversation, typeConfig, @@ -355,6 +358,7 @@ public final class ConversationActions implements Parcelable { extras); request.setCallingPackageName(callingPackageName); request.setUserId(userId); + request.setUseDefaultTextClassifier(useDefaultTextClassifier); return request; } @@ -367,6 +371,7 @@ public final class ConversationActions implements Parcelable { parcel.writeString(mCallingPackageName); parcel.writeInt(mUserId); parcel.writeBundle(mExtras); + parcel.writeBoolean(mUseDefaultTextClassifier); } @Override @@ -455,6 +460,26 @@ public final class ConversationActions implements Parcelable { } /** + * Sets whether to use the default text classifier to handle this request. + * This will be ignored if it is not the system text classifier to handle this request. + * + * @hide + */ + void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) { + mUseDefaultTextClassifier = useDefaultTextClassifier; + } + + /** + * Returns whether to use the default text classifier to handle this request. This + * will be ignored if it is not the system text classifier to handle this request. + * + * @hide + */ + public boolean getUseDefaultTextClassifier() { + return mUseDefaultTextClassifier; + } + + /** * Returns the extended data related to this request. * * <p><b>NOTE: </b>Do not modify this bundle. diff --git a/core/java/android/view/textclassifier/SelectionEvent.java b/core/java/android/view/textclassifier/SelectionEvent.java index 09cb7a07faa8..e0f29a981d89 100644 --- a/core/java/android/view/textclassifier/SelectionEvent.java +++ b/core/java/android/view/textclassifier/SelectionEvent.java @@ -140,6 +140,7 @@ public final class SelectionEvent implements Parcelable { private int mEnd; private int mSmartStart; private int mSmartEnd; + private boolean mUseDefaultTextClassifier; SelectionEvent( int start, int end, @@ -175,6 +176,7 @@ public final class SelectionEvent implements Parcelable { mSmartStart = in.readInt(); mSmartEnd = in.readInt(); mUserId = in.readInt(); + mUseDefaultTextClassifier = in.readBoolean(); } @Override @@ -204,6 +206,7 @@ public final class SelectionEvent implements Parcelable { dest.writeInt(mSmartStart); dest.writeInt(mSmartEnd); dest.writeInt(mUserId); + dest.writeBoolean(mUseDefaultTextClassifier); } @Override @@ -428,6 +431,26 @@ public final class SelectionEvent implements Parcelable { } /** + * Sets whether to use the default text classifier to handle this request. + * This will be ignored if it is not the system text classifier to handle this request. + * + * @hide + */ + void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) { + mUseDefaultTextClassifier = useDefaultTextClassifier; + } + + /** + * Returns whether to use the default text classifier to handle this request. This + * will be ignored if it is not the system text classifier to handle this request. + * + * @hide + */ + public boolean getUseDefaultTextClassifier() { + return mUseDefaultTextClassifier; + } + + /** * Returns the type of widget that was involved in triggering this event. */ @WidgetType @@ -642,7 +665,8 @@ public final class SelectionEvent implements Parcelable { return Objects.hash(mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType, mWidgetVersion, mPackageName, mUserId, mWidgetType, mInvocationMethod, mResultId, mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent, - mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd); + mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd, + mUseDefaultTextClassifier); } @Override @@ -673,7 +697,8 @@ public final class SelectionEvent implements Parcelable { && mStart == other.mStart && mEnd == other.mEnd && mSmartStart == other.mSmartStart - && mSmartEnd == other.mSmartEnd; + && mSmartEnd == other.mSmartEnd + && mUseDefaultTextClassifier == other.mUseDefaultTextClassifier; } @Override @@ -683,12 +708,13 @@ public final class SelectionEvent implements Parcelable { + "widgetVersion=%s, packageName=%s, widgetType=%s, invocationMethod=%s, " + "userId=%d, resultId=%s, eventTime=%d, durationSinceSessionStart=%d, " + "durationSincePreviousEvent=%d, eventIndex=%d," - + "sessionId=%s, start=%d, end=%d, smartStart=%d, smartEnd=%d}", + + "sessionId=%s, start=%d, end=%d, smartStart=%d, smartEnd=%d, " + + "mUseDefaultTextClassifier=%b}", mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType, mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod, mUserId, mResultId, mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent, mEventIndex, - mSessionId, mStart, mEnd, mSmartStart, mSmartEnd); + mSessionId, mStart, mEnd, mSmartStart, mSmartEnd, mUseDefaultTextClassifier); } public static final @android.annotation.NonNull Creator<SelectionEvent> CREATOR = new Creator<SelectionEvent>() { diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java index 138d25dd7c83..fe5e8d658dc3 100644 --- a/core/java/android/view/textclassifier/SystemTextClassifier.java +++ b/core/java/android/view/textclassifier/SystemTextClassifier.java @@ -55,17 +55,20 @@ public final class SystemTextClassifier implements TextClassifier { // service will throw a remote exception. @UserIdInt private final int mUserId; + private final boolean mUseDefault; private TextClassificationSessionId mSessionId; - public SystemTextClassifier(Context context, TextClassificationConstants settings) - throws ServiceManager.ServiceNotFoundException { + public SystemTextClassifier( + Context context, + TextClassificationConstants settings, + boolean useDefault) throws ServiceManager.ServiceNotFoundException { mManagerService = ITextClassifierService.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.TEXT_CLASSIFICATION_SERVICE)); mSettings = Objects.requireNonNull(settings); - mFallback = context.getSystemService(TextClassificationManager.class) - .getTextClassifier(TextClassifier.LOCAL); + mFallback = TextClassifier.NO_OP; mPackageName = Objects.requireNonNull(context.getOpPackageName()); mUserId = context.getUserId(); + mUseDefault = useDefault; } /** @@ -79,6 +82,7 @@ public final class SystemTextClassifier implements TextClassifier { try { request.setCallingPackageName(mPackageName); request.setUserId(mUserId); + request.setUseDefaultTextClassifier(mUseDefault); final BlockingCallback<TextSelection> callback = new BlockingCallback<>("textselection"); mManagerService.onSuggestSelection(mSessionId, request, callback); @@ -103,6 +107,7 @@ public final class SystemTextClassifier implements TextClassifier { try { request.setCallingPackageName(mPackageName); request.setUserId(mUserId); + request.setUseDefaultTextClassifier(mUseDefault); final BlockingCallback<TextClassification> callback = new BlockingCallback<>("textclassification"); mManagerService.onClassifyText(mSessionId, request, callback); @@ -124,7 +129,9 @@ public final class SystemTextClassifier implements TextClassifier { public TextLinks generateLinks(@NonNull TextLinks.Request request) { Objects.requireNonNull(request); Utils.checkMainThread(); - + if (!Utils.checkTextLength(request.getText(), getMaxGenerateLinksTextLength())) { + return mFallback.generateLinks(request); + } if (!mSettings.isSmartLinkifyEnabled() && request.isLegacyFallback()) { return Utils.generateLegacyLinks(request); } @@ -132,6 +139,7 @@ public final class SystemTextClassifier implements TextClassifier { try { request.setCallingPackageName(mPackageName); request.setUserId(mUserId); + request.setUseDefaultTextClassifier(mUseDefault); final BlockingCallback<TextLinks> callback = new BlockingCallback<>("textlinks"); mManagerService.onGenerateLinks(mSessionId, request, callback); @@ -152,6 +160,7 @@ public final class SystemTextClassifier implements TextClassifier { try { event.setUserId(mUserId); + event.setUseDefaultTextClassifier(mUseDefault); mManagerService.onSelectionEvent(mSessionId, event); } catch (RemoteException e) { Log.e(LOG_TAG, "Error reporting selection event.", e); @@ -169,6 +178,7 @@ public final class SystemTextClassifier implements TextClassifier { .build() : event.getEventContext(); tcContext.setUserId(mUserId); + tcContext.setUseDefaultTextClassifier(mUseDefault); event.setEventContext(tcContext); mManagerService.onTextClassifierEvent(mSessionId, event); } catch (RemoteException e) { @@ -184,6 +194,7 @@ public final class SystemTextClassifier implements TextClassifier { try { request.setCallingPackageName(mPackageName); request.setUserId(mUserId); + request.setUseDefaultTextClassifier(mUseDefault); final BlockingCallback<TextLanguage> callback = new BlockingCallback<>("textlanguage"); mManagerService.onDetectLanguage(mSessionId, request, callback); @@ -205,6 +216,7 @@ public final class SystemTextClassifier implements TextClassifier { try { request.setCallingPackageName(mPackageName); request.setUserId(mUserId); + request.setUseDefaultTextClassifier(mUseDefault); final BlockingCallback<ConversationActions> callback = new BlockingCallback<>("conversation-actions"); mManagerService.onSuggestConversationActions(mSessionId, request, callback); @@ -225,7 +237,7 @@ public final class SystemTextClassifier implements TextClassifier { @WorkerThread public int getMaxGenerateLinksTextLength() { // TODO: retrieve this from the bound service. - return mFallback.getMaxGenerateLinksTextLength(); + return mSettings.getGenerateLinksMaxTextLength(); } @Override @@ -247,6 +259,7 @@ public final class SystemTextClassifier implements TextClassifier { printWriter.printPair("mPackageName", mPackageName); printWriter.printPair("mSessionId", mSessionId); printWriter.printPair("mUserId", mUserId); + printWriter.printPair("mUseDefault", mUseDefault); printWriter.decreaseIndent(); printWriter.println(); } diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java index 3628d2d40c1e..00f762b44a07 100644 --- a/core/java/android/view/textclassifier/TextClassification.java +++ b/core/java/android/view/textclassifier/TextClassification.java @@ -555,6 +555,7 @@ public final class TextClassification implements Parcelable { @Nullable private String mCallingPackageName; @UserIdInt private int mUserId = UserHandle.USER_NULL; + private boolean mUseDefaultTextClassifier; private Request( CharSequence text, @@ -654,6 +655,26 @@ public final class TextClassification implements Parcelable { } /** + * Sets whether to use the default text classifier to handle this request. + * This will be ignored if it is not the system text classifier to handle this request. + * + * @hide + */ + void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) { + mUseDefaultTextClassifier = useDefaultTextClassifier; + } + + /** + * Returns whether to use the default text classifier to handle this request. This + * will be ignored if it is not the system text classifier to handle this request. + * + * @hide + */ + public boolean getUseDefaultTextClassifier() { + return mUseDefaultTextClassifier; + } + + /** * Returns the extended data. * * <p><b>NOTE: </b>Do not modify this bundle. @@ -755,6 +776,7 @@ public final class TextClassification implements Parcelable { dest.writeString(mCallingPackageName); dest.writeInt(mUserId); dest.writeBundle(mExtras); + dest.writeBoolean(mUseDefaultTextClassifier); } private static Request readFromParcel(Parcel in) { @@ -768,11 +790,13 @@ public final class TextClassification implements Parcelable { final String callingPackageName = in.readString(); final int userId = in.readInt(); final Bundle extras = in.readBundle(); + final boolean useDefaultTextClassifier = in.readBoolean(); final Request request = new Request(text, startIndex, endIndex, defaultLocales, referenceTime, extras); request.setCallingPackageName(callingPackageName); request.setUserId(userId); + request.setUseDefaultTextClassifier(useDefaultTextClassifier); return request; } diff --git a/core/java/android/view/textclassifier/TextClassificationContext.java b/core/java/android/view/textclassifier/TextClassificationContext.java index 930765b29197..d58d175c9c93 100644 --- a/core/java/android/view/textclassifier/TextClassificationContext.java +++ b/core/java/android/view/textclassifier/TextClassificationContext.java @@ -38,6 +38,7 @@ public final class TextClassificationContext implements Parcelable { @Nullable private final String mWidgetVersion; @UserIdInt private int mUserId = UserHandle.USER_NULL; + private boolean mUseDefaultTextClassifier; private TextClassificationContext( String packageName, @@ -76,6 +77,26 @@ public final class TextClassificationContext implements Parcelable { } /** + * Sets whether to use the default text classifier to handle this request. + * This will be ignored if it is not the system text classifier to handle this request. + * + * @hide + */ + void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) { + mUseDefaultTextClassifier = useDefaultTextClassifier; + } + + /** + * Returns whether to use the default text classifier to handle this request. This + * will be ignored if it is not the system text classifier to handle this request. + * + * @hide + */ + public boolean getUseDefaultTextClassifier() { + return mUseDefaultTextClassifier; + } + + /** * Returns the widget type for this classification context. */ @NonNull @@ -156,6 +177,7 @@ public final class TextClassificationContext implements Parcelable { parcel.writeString(mWidgetType); parcel.writeString(mWidgetVersion); parcel.writeInt(mUserId); + parcel.writeBoolean(mUseDefaultTextClassifier); } private TextClassificationContext(Parcel in) { @@ -163,6 +185,7 @@ public final class TextClassificationContext implements Parcelable { mWidgetType = in.readString(); mWidgetVersion = in.readString(); mUserId = in.readInt(); + mUseDefaultTextClassifier = in.readBoolean(); } public static final @android.annotation.NonNull Parcelable.Creator<TextClassificationContext> CREATOR = diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java index bb96d5543b0b..a6c83a1cfa76 100644 --- a/core/java/android/view/textclassifier/TextClassificationManager.java +++ b/core/java/android/view/textclassifier/TextClassificationManager.java @@ -25,7 +25,7 @@ import android.content.Context; import android.os.ServiceManager; import android.provider.DeviceConfig; import android.provider.DeviceConfig.Properties; -import android.service.textclassifier.TextClassifierService; +import android.util.SparseArray; import android.view.textclassifier.TextClassifier.TextClassifierType; import com.android.internal.annotations.GuardedBy; @@ -62,8 +62,7 @@ public final class TextClassificationManager { @Nullable private TextClassifier mLocalTextClassifier; @GuardedBy("mLock") - @Nullable - private TextClassifier mSystemTextClassifier; + private SparseArray<TextClassifier> mSystemTextClassifiers = new SparseArray<>(); @GuardedBy("mLock") private TextClassificationSessionFactory mSessionFactory; @GuardedBy("mLock") @@ -91,8 +90,8 @@ public final class TextClassificationManager { synchronized (mLock) { if (mCustomTextClassifier != null) { return mCustomTextClassifier; - } else if (isSystemTextClassifierEnabled()) { - return getSystemTextClassifier(); + } else if (getSettings().isSystemTextClassifierEnabled()) { + return getSystemTextClassifier(SystemTextClassifier.SYSTEM); } else { return getLocalTextClassifier(); } @@ -116,6 +115,7 @@ public final class TextClassificationManager { * * @see TextClassifier#LOCAL * @see TextClassifier#SYSTEM + * @see TextClassifier#DEFAULT_SERVICE * @hide */ @UnsupportedAppUsage @@ -124,7 +124,7 @@ public final class TextClassificationManager { case TextClassifier.LOCAL: return getLocalTextClassifier(); default: - return getSystemTextClassifier(); + return getSystemTextClassifier(type); } } @@ -204,21 +204,28 @@ public final class TextClassificationManager { } } - private TextClassifier getSystemTextClassifier() { + /** @hide */ + private TextClassifier getSystemTextClassifier(@TextClassifierType int type) { synchronized (mLock) { - if (mSystemTextClassifier == null && isSystemTextClassifierEnabled()) { + if (mSystemTextClassifiers.get(type) == null + && getSettings().isSystemTextClassifierEnabled()) { try { - mSystemTextClassifier = new SystemTextClassifier(mContext, getSettings()); - Log.d(LOG_TAG, "Initialized SystemTextClassifier"); + mSystemTextClassifiers.put( + type, + new SystemTextClassifier( + mContext, + getSettings(), + /* useDefault= */ type == TextClassifier.DEFAULT_SERVICE)); + Log.d(LOG_TAG, "Initialized SystemTextClassifier, type = " + type); } catch (ServiceManager.ServiceNotFoundException e) { Log.e(LOG_TAG, "Could not initialize SystemTextClassifier", e); } } + if (mSystemTextClassifiers.get(type) != null) { + return mSystemTextClassifiers.get(type); + } + return TextClassifier.NO_OP; } - if (mSystemTextClassifier != null) { - return mSystemTextClassifier; - } - return TextClassifier.NO_OP; } /** @@ -240,11 +247,6 @@ public final class TextClassificationManager { } } - private boolean isSystemTextClassifierEnabled() { - return getSettings().isSystemTextClassifierEnabled() - && TextClassifierService.getServiceComponentName(mContext) != null; - } - /** @hide */ @VisibleForTesting public void invalidateForTesting() { @@ -261,7 +263,7 @@ public final class TextClassificationManager { private void invalidateTextClassifiers() { synchronized (mLock) { mLocalTextClassifier = null; - mSystemTextClassifier = null; + mSystemTextClassifiers.clear(); } } @@ -274,7 +276,8 @@ public final class TextClassificationManager { /** @hide **/ public void dump(IndentingPrintWriter pw) { getLocalTextClassifier().dump(pw); - getSystemTextClassifier().dump(pw); + getSystemTextClassifier(TextClassifier.DEFAULT_SERVICE).dump(pw); + getSystemTextClassifier(TextClassifier.SYSTEM).dump(pw); getSettings().dump(pw); } diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java index 9b3369390b6a..2cc226d5a601 100644 --- a/core/java/android/view/textclassifier/TextClassifier.java +++ b/core/java/android/view/textclassifier/TextClassifier.java @@ -66,12 +66,14 @@ public interface TextClassifier { /** @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(value = {LOCAL, SYSTEM}) + @IntDef(value = {LOCAL, SYSTEM, DEFAULT_SERVICE}) @interface TextClassifierType {} // TODO: Expose as system APIs. /** Specifies a TextClassifier that runs locally in the app's process. @hide */ int LOCAL = 0; /** Specifies a TextClassifier that runs in the system process and serves all apps. @hide */ int SYSTEM = 1; + /** Specifies the default TextClassifier that runs in the system process. @hide */ + int DEFAULT_SERVICE = 2; /** The TextClassifier failed to run. */ String TYPE_UNKNOWN = ""; @@ -667,8 +669,10 @@ public interface TextClassifier { Preconditions.checkArgument(endIndex > startIndex); } - static void checkTextLength(CharSequence text, int maxLength) { - Preconditions.checkArgumentInRange(text.length(), 0, maxLength, "text.length()"); + /** Returns if the length of the text is within the range. */ + static boolean checkTextLength(CharSequence text, int maxLength) { + int textLength = text.length(); + return textLength >= 0 && textLength <= maxLength; } /** diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java index 61bd7c72b80b..d7149ee05b57 100644 --- a/core/java/android/view/textclassifier/TextClassifierImpl.java +++ b/core/java/android/view/textclassifier/TextClassifierImpl.java @@ -286,8 +286,10 @@ public final class TextClassifierImpl implements TextClassifier { @WorkerThread public TextLinks generateLinks(@NonNull TextLinks.Request request) { Objects.requireNonNull(request); - Utils.checkTextLength(request.getText(), getMaxGenerateLinksTextLength()); Utils.checkMainThread(); + if (!Utils.checkTextLength(request.getText(), getMaxGenerateLinksTextLength())) { + return mFallback.generateLinks(request); + } if (!mSettings.isSmartLinkifyEnabled() && request.isLegacyFallback()) { return Utils.generateLegacyLinks(request); diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java index cc9109e6e9bb..58024dcc09b9 100644 --- a/core/java/android/view/textclassifier/TextLanguage.java +++ b/core/java/android/view/textclassifier/TextLanguage.java @@ -230,6 +230,7 @@ public final class TextLanguage implements Parcelable { @Nullable private String mCallingPackageName; @UserIdInt private int mUserId = UserHandle.USER_NULL; + private boolean mUseDefaultTextClassifier; private Request(CharSequence text, Bundle bundle) { mText = text; @@ -283,6 +284,26 @@ public final class TextLanguage implements Parcelable { } /** + * Sets whether to use the default text classifier to handle this request. + * This will be ignored if it is not the system text classifier to handle this request. + * + * @hide + */ + void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) { + mUseDefaultTextClassifier = useDefaultTextClassifier; + } + + /** + * Returns whether to use the default text classifier to handle this request. This + * will be ignored if it is not the system text classifier to handle this request. + * + * @hide + */ + public boolean getUseDefaultTextClassifier() { + return mUseDefaultTextClassifier; + } + + /** * Returns a bundle containing non-structured extra information about this request. * * <p><b>NOTE: </b>Do not modify this bundle. @@ -303,6 +324,7 @@ public final class TextLanguage implements Parcelable { dest.writeString(mCallingPackageName); dest.writeInt(mUserId); dest.writeBundle(mExtra); + dest.writeBoolean(mUseDefaultTextClassifier); } private static Request readFromParcel(Parcel in) { @@ -310,10 +332,12 @@ public final class TextLanguage implements Parcelable { final String callingPackageName = in.readString(); final int userId = in.readInt(); final Bundle extra = in.readBundle(); + final boolean useDefaultTextClassifier = in.readBoolean(); final Request request = new Request(text, extra); request.setCallingPackageName(callingPackageName); request.setUserId(userId); + request.setUseDefaultTextClassifier(useDefaultTextClassifier); return request; } diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java index bda12b0893d1..7430cb38b987 100644 --- a/core/java/android/view/textclassifier/TextLinks.java +++ b/core/java/android/view/textclassifier/TextLinks.java @@ -345,6 +345,7 @@ public final class TextLinks implements Parcelable { @Nullable private final ZonedDateTime mReferenceTime; @UserIdInt private int mUserId = UserHandle.USER_NULL; + private boolean mUseDefaultTextClassifier; private Request( CharSequence text, @@ -447,6 +448,26 @@ public final class TextLinks implements Parcelable { } /** + * Sets whether to use the default text classifier to handle this request. + * This will be ignored if it is not the system text classifier to handle this request. + * + * @hide + */ + void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) { + mUseDefaultTextClassifier = useDefaultTextClassifier; + } + + /** + * Returns whether to use the default text classifier to handle this request. This + * will be ignored if it is not the system text classifier to handle this request. + * + * @hide + */ + public boolean getUseDefaultTextClassifier() { + return mUseDefaultTextClassifier; + } + + /** * Returns the extended data. * * <p><b>NOTE: </b>Do not modify this bundle. @@ -568,6 +589,7 @@ public final class TextLinks implements Parcelable { dest.writeInt(mUserId); dest.writeBundle(mExtras); dest.writeString(mReferenceTime == null ? null : mReferenceTime.toString()); + dest.writeBoolean(mUseDefaultTextClassifier); } private static Request readFromParcel(Parcel in) { @@ -580,11 +602,13 @@ public final class TextLinks implements Parcelable { final String referenceTimeString = in.readString(); final ZonedDateTime referenceTime = referenceTimeString == null ? null : ZonedDateTime.parse(referenceTimeString); + final boolean useDefaultTextClassifier = in.readBoolean(); final Request request = new Request(text, defaultLocales, entityConfig, /* legacyFallback= */ true, referenceTime, extras); request.setCallingPackageName(callingPackageName); request.setUserId(userId); + request.setUseDefaultTextClassifier(useDefaultTextClassifier); return request; } diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java index 4a36cbfc35e5..575a072d7066 100644 --- a/core/java/android/view/textclassifier/TextSelection.java +++ b/core/java/android/view/textclassifier/TextSelection.java @@ -216,6 +216,7 @@ public final class TextSelection implements Parcelable { @Nullable private String mCallingPackageName; @UserIdInt private int mUserId = UserHandle.USER_NULL; + private boolean mUseDefaultTextClassifier; private Request( CharSequence text, @@ -316,6 +317,26 @@ public final class TextSelection implements Parcelable { } /** + * Sets whether to use the default text classifier to handle this request. + * This will be ignored if it is not the system text classifier to handle this request. + * + * @hide + */ + void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) { + mUseDefaultTextClassifier = useDefaultTextClassifier; + } + + /** + * Returns whether to use the default text classifier to handle this request. This + * will be ignored if it is not the system text classifier to handle this request. + * + * @hide + */ + public boolean getUseDefaultTextClassifier() { + return mUseDefaultTextClassifier; + } + + /** * Returns the extended data. * * <p><b>NOTE: </b>Do not modify this bundle. @@ -420,6 +441,7 @@ public final class TextSelection implements Parcelable { dest.writeString(mCallingPackageName); dest.writeInt(mUserId); dest.writeBundle(mExtras); + dest.writeBoolean(mUseDefaultTextClassifier); } private static Request readFromParcel(Parcel in) { @@ -430,11 +452,13 @@ public final class TextSelection implements Parcelable { final String callingPackageName = in.readString(); final int userId = in.readInt(); final Bundle extras = in.readBundle(); + final boolean systemTextClassifierType = in.readBoolean(); final Request request = new Request(text, startIndex, endIndex, defaultLocales, /* darkLaunchAllowed= */ false, extras); request.setCallingPackageName(callingPackageName); request.setUserId(userId); + request.setUseDefaultTextClassifier(systemTextClassifierType); return request; } diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index d240c9c18702..01f823b23cd9 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3556,25 +3556,16 @@ --> <string name="config_defaultAutofillService" translatable="false"></string> - <!-- The package name for the default system textclassifier service. + <!-- The package name for the OEM custom system textclassifier service. This service must be trusted, as it can be activated without explicit consent of the user. Example: "com.android.textclassifier" - If no textclassifier service with the specified name exists on the device (or if this is - set to empty string), a default textclassifier will be loaded in the calling app's process. + If this is empty, the default textclassifier service (i.e. config_servicesExtensionPackage) + will be used. + See android.view.textclassifier.TextClassificationManager. --> - <!-- TODO(b/144896755) remove the config --> <string name="config_defaultTextClassifierPackage" translatable="false"></string> - <!-- A list of supported system textClassifier service package names. Only one of the packages - will be activated. The first package in the list is the default system textClassifier - service. OS only tries to bind and grant permissions to the first trusted service and the - others can be selected via device config. These services must be trusted, as they can be - activated without explicit consent of the user. Example: "com.android.textclassifier" - --> - <string-array name="config_defaultTextClassifierPackages" translatable="false"> - <item>android.ext.services</item> - </string-array> <!-- The package name for the system companion device manager service. This service must be trusted, as it can be activated without explicit consent of the user. diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index f47da62654e2..2531c15d30ac 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3389,7 +3389,6 @@ <java-symbol type="string" name="notification_channel_do_not_disturb" /> <java-symbol type="string" name="config_defaultAutofillService" /> <java-symbol type="string" name="config_defaultTextClassifierPackage" /> - <java-symbol type="array" name="config_defaultTextClassifierPackages" /> <java-symbol type="string" name="config_defaultWellbeingPackage" /> <java-symbol type="string" name="config_telephonyPackages" /> <java-symbol type="string" name="config_defaultContentCaptureService" /> diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java index 8faf790549d5..1ca46491b363 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java @@ -25,7 +25,6 @@ import static org.mockito.Mockito.mock; import android.content.Context; import android.content.Intent; import android.os.LocaleList; -import android.service.textclassifier.TextClassifierService; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -64,9 +63,7 @@ public class TextClassificationManagerTest { @Test public void testGetSystemTextClassifier() { - assertTrue( - TextClassifierService.getServiceComponentName(mContext) == null - || mTcm.getTextClassifier(TextClassifier.SYSTEM) instanceof SystemTextClassifier); + assertTrue(mTcm.getTextClassifier(TextClassifier.SYSTEM) instanceof SystemTextClassifier); } @Test diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java index 2304ba6f6da4..372a4787cf1a 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java @@ -27,6 +27,7 @@ import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.LocaleList; +import android.service.textclassifier.TextClassifierService; import android.text.Spannable; import android.text.SpannableString; @@ -58,11 +59,12 @@ import java.util.List; public class TextClassifierTest { private static final String LOCAL = "local"; private static final String SESSION = "session"; + private static final String DEFAULT = "default"; // TODO: Add SYSTEM, which tests TextClassifier.SYSTEM. @Parameterized.Parameters(name = "{0}") public static Iterable<Object> textClassifierTypes() { - return Arrays.asList(LOCAL, SESSION); + return Arrays.asList(LOCAL, SESSION, DEFAULT); } @Parameterized.Parameter @@ -84,13 +86,15 @@ public class TextClassifierTest { if (mTextClassifierType.equals(LOCAL)) { mClassifier = mTcm.getTextClassifier(TextClassifier.LOCAL); - } else { + } else if (mTextClassifierType.equals(SESSION)) { mClassifier = mTcm.createTextClassificationSession( new TextClassificationContext.Builder( "android", TextClassifier.WIDGET_TYPE_NOTIFICATION) .build(), mTcm.getTextClassifier(TextClassifier.LOCAL)); + } else { + mClassifier = TextClassifierService.getDefaultTextClassifierImplementation(mContext); } } @@ -369,16 +373,14 @@ public class TextClassifierTest { mClassifier.generateLinks(request).apply(url, 0, null)); } - - @Test(expected = IllegalArgumentException.class) + @Test public void testGenerateLinks_tooLong() { - if (isTextClassifierDisabled()) { - throw new IllegalArgumentException("pass if disabled"); - } + if (isTextClassifierDisabled()) return; char[] manySpaces = new char[mClassifier.getMaxGenerateLinksTextLength() + 1]; Arrays.fill(manySpaces, ' '); TextLinks.Request request = new TextLinks.Request.Builder(new String(manySpaces)).build(); - mClassifier.generateLinks(request); + TextLinks links = mClassifier.generateLinks(request); + assertTrue(links.getLinks().isEmpty()); } @Test diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index db8d490d53b9..8ff0ec881b23 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1535,15 +1535,16 @@ public class PackageManagerService extends IPackageManager.Stub final @NonNull String mRequiredPermissionControllerPackage; final @Nullable String mSetupWizardPackage; final @Nullable String mStorageManagerPackage; - final @Nullable String mSystemTextClassifierPackage; + final @Nullable String mDefaultTextClassifierPackage; + final @Nullable String mSystemTextClassifierPackageName; final @Nullable String mWellbeingPackage; final @Nullable String mDocumenterPackage; final @Nullable String mConfiguratorPackage; final @Nullable String mAppPredictionServicePackage; final @Nullable String mIncidentReportApproverPackage; final @Nullable String[] mTelephonyPackages; - final @NonNull String mServicesExtensionPackageName; - final @NonNull String mSharedSystemSharedLibraryPackageName; + final @Nullable String mServicesExtensionPackageName; + final @Nullable String mSharedSystemSharedLibraryPackageName; final @Nullable String mRetailDemoPackage; private final PackageUsage mPackageUsage = new PackageUsage(); @@ -3153,8 +3154,8 @@ public class PackageManagerService extends IPackageManager.Stub mSetupWizardPackage = getSetupWizardPackageNameImpl(); mComponentResolver.fixProtectedFilterPriorities(); - mSystemTextClassifierPackage = getSystemTextClassifierPackageName(); - + mDefaultTextClassifierPackage = getDefaultTextClassifierPackageName(); + mSystemTextClassifierPackageName = getSystemTextClassifierPackageName(); mWellbeingPackage = getWellbeingPackageName(); mDocumenterPackage = getDocumenterPackageName(); mConfiguratorPackage = getDeviceConfiguratorPackageName(); @@ -3349,6 +3350,7 @@ public class PackageManagerService extends IPackageManager.Stub mServicesExtensionPackageName = null; mSharedSystemSharedLibraryPackageName = null; } + // PermissionController hosts default permission granting and role management, so it's a // critical part of the core system. mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr(); @@ -19586,15 +19588,15 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public String getSystemTextClassifierPackageName() { - return ensureSystemPackageName(mContext.getString( - R.string.config_defaultTextClassifierPackage)); + public String getDefaultTextClassifierPackageName() { + return ensureSystemPackageName( + mContext.getString(R.string.config_servicesExtensionPackage)); } @Override - public String[] getSystemTextClassifierPackages() { - return ensureSystemPackageNames(mContext.getResources().getStringArray( - R.array.config_defaultTextClassifierPackages)); + public String getSystemTextClassifierPackageName() { + return ensureSystemPackageName( + mContext.getString(R.string.config_defaultTextClassifierPackage)); } @Override @@ -23018,7 +23020,7 @@ public class PackageManagerService extends IPackageManager.Stub } private String[] getKnownPackageNamesInternal(int knownPackage, int userId) { - switch(knownPackage) { + switch (knownPackage) { case PackageManagerInternal.PACKAGE_BROWSER: return new String[]{mPermissionManager.getDefaultBrowser(userId)}; case PackageManagerInternal.PACKAGE_INSTALLER: @@ -23030,7 +23032,8 @@ public class PackageManagerService extends IPackageManager.Stub case PackageManagerInternal.PACKAGE_VERIFIER: return filterOnlySystemPackages(mRequiredVerifierPackage); case PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER: - return filterOnlySystemPackages(mSystemTextClassifierPackage); + return filterOnlySystemPackages( + mDefaultTextClassifierPackage, mSystemTextClassifierPackageName); case PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER: return filterOnlySystemPackages(mRequiredPermissionControllerPackage); case PackageManagerInternal.PACKAGE_WELLBEING: diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 46893b25de9a..e6eaf211a86a 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -720,12 +720,9 @@ public final class DefaultPermissionGrantPolicy { userId, STORAGE_PERMISSIONS); // TextClassifier Service - final String[] packages = mContext.getPackageManager().getSystemTextClassifierPackages(); - if (packages.length > 0) { - // We have a list of supported system TextClassifier package names, the first one - // package is the default system TextClassifier service. Grant permissions to default - // TextClassifier Service. - grantPermissionsToSystemPackage(packages[0], userId, + for (String textClassifierPackage : + getKnownPackages(PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER, userId)) { + grantPermissionsToSystemPackage(textClassifierPackage, userId, COARSE_BACKGROUND_LOCATION_PERMISSIONS, CONTACTS_PERMISSIONS); } diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java index 3dee853240a6..34d2c16ed0ac 100644 --- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java +++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java @@ -36,7 +36,7 @@ import android.service.textclassifier.ITextClassifierService; import android.service.textclassifier.TextClassifierService; import android.service.textclassifier.TextClassifierService.ConnectionState; import android.text.TextUtils; -import android.util.ArrayMap; +import android.util.LruCache; import android.util.Slog; import android.util.SparseArray; import android.view.textclassifier.ConversationActions; @@ -63,7 +63,8 @@ import com.android.server.SystemService; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayDeque; -import java.util.Map; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; import java.util.Queue; @@ -132,9 +133,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi synchronized (mManagerService.mLock) { UserState userState = mManagerService.peekUserStateLocked(userId); if (userState != null) { - if (userState.mConnection != null) { - userState.mConnection.cleanupService(); - } + userState.cleanupServiceLocked(); mManagerService.mUserStates.remove(userId); } } @@ -147,22 +146,31 @@ public final class TextClassificationManagerService extends ITextClassifierServi private final Object mLock; @GuardedBy("mLock") final SparseArray<UserState> mUserStates = new SparseArray<>(); + // SystemTextClassifier.onDestroy() is not guaranteed to be called, use LruCache here + // to avoid leak. @GuardedBy("mLock") - private final Map<TextClassificationSessionId, Integer> mSessionUserIds = new ArrayMap<>(); - @GuardedBy("mLock") - private TextClassificationConstants mSettings; + private final LruCache<TextClassificationSessionId, TextClassificationContext> + mSessionContextCache = new LruCache<>(40); + private final TextClassificationConstants mSettings; + @Nullable + private final String mDefaultTextClassifierPackage; + @Nullable + private final String mSystemTextClassifierPackage; private TextClassificationManagerService(Context context) { mContext = Objects.requireNonNull(context); mLock = new Object(); + mSettings = new TextClassificationConstants(); mSettingsListener = new TextClassifierSettingsListener(mContext); + PackageManager packageManager = mContext.getPackageManager(); + mDefaultTextClassifierPackage = packageManager.getDefaultTextClassifierPackageName(); + mSystemTextClassifierPackage = packageManager.getSystemTextClassifierPackageName(); } private void startListenSettings() { mSettingsListener.registerObserver(); } - @Override public void onConnectedStateChanged(@ConnectionState int connected) { } @@ -178,6 +186,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi request.getUserId(), request.getCallingPackageName(), /* attemptToBind= */ true, + request.getUseDefaultTextClassifier(), service -> service.onSuggestSelection(sessionId, request, callback), "onSuggestSelection", callback); @@ -194,6 +203,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi request.getUserId(), request.getCallingPackageName(), /* attemptToBind= */ true, + request.getUseDefaultTextClassifier(), service -> service.onClassifyText(sessionId, request, callback), "onClassifyText", callback); @@ -210,6 +220,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi request.getUserId(), request.getCallingPackageName(), /* attemptToBind= */ true, + request.getUseDefaultTextClassifier(), service -> service.onGenerateLinks(sessionId, request, callback), "onGenerateLinks", callback); @@ -223,28 +234,32 @@ public final class TextClassificationManagerService extends ITextClassifierServi handleRequest( event.getUserId(), - event.getPackageName(), + /* callingPackageName= */ null, /* attemptToBind= */ false, + event.getUseDefaultTextClassifier(), service -> service.onSelectionEvent(sessionId, event), "onSelectionEvent", NO_OP_CALLBACK); } + @Override public void onTextClassifierEvent( @Nullable TextClassificationSessionId sessionId, TextClassifierEvent event) throws RemoteException { Objects.requireNonNull(event); - final String packageName = event.getEventContext() == null - ? null - : event.getEventContext().getPackageName(); final int userId = event.getEventContext() == null ? UserHandle.getCallingUserId() : event.getEventContext().getUserId(); + final boolean useDefaultTextClassifier = + event.getEventContext() != null + ? event.getEventContext().getUseDefaultTextClassifier() + : true; handleRequest( userId, - packageName, + /* callingPackageName= */ null, /* attemptToBind= */ false, + useDefaultTextClassifier, service -> service.onTextClassifierEvent(sessionId, event), "onTextClassifierEvent", NO_OP_CALLBACK); @@ -261,6 +276,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi request.getUserId(), request.getCallingPackageName(), /* attemptToBind= */ true, + request.getUseDefaultTextClassifier(), service -> service.onDetectLanguage(sessionId, request, callback), "onDetectLanguage", callback); @@ -277,6 +293,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi request.getUserId(), request.getCallingPackageName(), /* attemptToBind= */ true, + request.getUseDefaultTextClassifier(), service -> service.onSuggestConversationActions(sessionId, request, callback), "onSuggestConversationActions", callback); @@ -294,9 +311,10 @@ public final class TextClassificationManagerService extends ITextClassifierServi userId, classificationContext.getPackageName(), /* attemptToBind= */ false, + classificationContext.getUseDefaultTextClassifier(), service -> { service.onCreateTextClassificationSession(classificationContext, sessionId); - mSessionUserIds.put(sessionId, userId); + mSessionContextCache.put(sessionId, classificationContext); }, "onCreateTextClassificationSession", NO_OP_CALLBACK); @@ -308,16 +326,23 @@ public final class TextClassificationManagerService extends ITextClassifierServi Objects.requireNonNull(sessionId); synchronized (mLock) { - final int userId = mSessionUserIds.containsKey(sessionId) - ? mSessionUserIds.get(sessionId) + TextClassificationContext textClassificationContext = + mSessionContextCache.get(sessionId); + final int userId = textClassificationContext != null + ? textClassificationContext.getUserId() : UserHandle.getCallingUserId(); + final boolean useDefaultTextClassifier = + textClassificationContext != null + ? textClassificationContext.getUseDefaultTextClassifier() + : true; handleRequest( userId, /* callingPackageName= */ null, /* attemptToBind= */ false, + useDefaultTextClassifier, service -> { service.onDestroyTextClassificationSession(sessionId); - mSessionUserIds.remove(sessionId); + mSessionContextCache.remove(sessionId); }, "onDestroyTextClassificationSession", NO_OP_CALLBACK); @@ -328,7 +353,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi private UserState getUserStateLocked(int userId) { UserState result = mUserStates.get(userId); if (result == null) { - result = new UserState(userId, mContext, mLock); + result = new UserState(userId); mUserStates.put(userId, result); } return result; @@ -339,6 +364,19 @@ public final class TextClassificationManagerService extends ITextClassifierServi return mUserStates.get(userId); } + private int resolvePackageToUid(@Nullable String packageName, @UserIdInt int userId) { + if (packageName == null) { + return Process.INVALID_UID; + } + final PackageManager pm = mContext.getPackageManager(); + try { + return pm.getPackageUidAsUser(packageName, userId); + } catch (PackageManager.NameNotFoundException e) { + Slog.e(LOG_TAG, "Could not get the UID for " + packageName); + } + return Process.INVALID_UID; + } + @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, fout)) return; @@ -353,18 +391,25 @@ public final class TextClassificationManagerService extends ITextClassifierServi pw.printPair("context", mContext); pw.println(); + pw.printPair("defaultTextClassifierPackage", mDefaultTextClassifierPackage); + pw.println(); + pw.printPair("systemTextClassifierPackage", mSystemTextClassifierPackage); + pw.println(); synchronized (mLock) { int size = mUserStates.size(); - pw.print("Number user states: "); pw.println(size); + pw.print("Number user states: "); + pw.println(size); if (size > 0) { for (int i = 0; i < size; i++) { pw.increaseIndent(); UserState userState = mUserStates.valueAt(i); - pw.print(i); pw.print(":"); userState.dump(pw); pw.println(); + pw.printPair("User", mUserStates.keyAt(i)); + pw.println(); + userState.dump(pw); pw.decreaseIndent(); } } - pw.println("Number of active sessions: " + mSessionUserIds.size()); + pw.println("Number of active sessions: " + mSessionContextCache.size()); } } @@ -372,57 +417,55 @@ public final class TextClassificationManagerService extends ITextClassifierServi @UserIdInt int userId, @Nullable String callingPackageName, boolean attemptToBind, + boolean useDefaultTextClassifier, @NonNull ThrowingConsumer<ITextClassifierService> textClassifierServiceConsumer, @NonNull String methodName, - @NonNull ITextClassifierCallback callback) - throws RemoteException { + @NonNull ITextClassifierCallback callback) throws RemoteException { Objects.requireNonNull(textClassifierServiceConsumer); Objects.requireNonNull(methodName); Objects.requireNonNull(callback); - validateInput(mContext, callingPackageName, userId); + try { + validateCallingPackage(callingPackageName); + validateUser(userId); + } catch (Exception e) { + throw new RemoteException("Invalid request: " + e.getMessage(), e, + /* enableSuppression */ true, /* writableStackTrace */ true); + } synchronized (mLock) { UserState userState = getUserStateLocked(userId); - if (attemptToBind && !userState.bindLocked()) { + ServiceState serviceState = + userState.getServiceStateLocked(useDefaultTextClassifier); + if (serviceState == null) { + Slog.d(LOG_TAG, "No configured system TextClassifierService"); + callback.onFailure(); + } else if (attemptToBind && !serviceState.bindLocked()) { Slog.d(LOG_TAG, "Unable to bind TextClassifierService at " + methodName); callback.onFailure(); - } else if (userState.isBoundLocked()) { - if (!userState.checkRequestAcceptedLocked(Binder.getCallingUid(), methodName)) { + } else if (serviceState.isBoundLocked()) { + if (!serviceState.checkRequestAcceptedLocked(Binder.getCallingUid(), methodName)) { return; } - textClassifierServiceConsumer.accept(userState.mService); + textClassifierServiceConsumer.accept(serviceState.mService); } else { - userState.mPendingRequests.add( + serviceState.mPendingRequests.add( new PendingRequest( methodName, - () -> textClassifierServiceConsumer.accept(userState.mService), + () -> textClassifierServiceConsumer.accept(serviceState.mService), callback::onFailure, callback.asBinder(), this, - userState, + serviceState, Binder.getCallingUid())); } } } - private void unbindServiceIfNecessary() { - final ComponentName serviceComponentName = - TextClassifierService.getServiceComponentName(mContext); - if (serviceComponentName == null) { - // It should not occur if we had defined default service names in config.xml - Slog.w(LOG_TAG, "No default configured system TextClassifierService."); - return; - } + private void onTextClassifierServicePackageOverrideChanged(String overriddenPackage) { synchronized (mLock) { final int size = mUserStates.size(); for (int i = 0; i < size; i++) { UserState userState = mUserStates.valueAt(i); - // Only unbind for a new service - if (userState.isCurrentlyBoundToComponentLocked(serviceComponentName)) { - return; - } - if (userState.isBoundLocked()) { - userState.unbindLocked(); - } + userState.onTextClassifierServicePackageOverrideChangedLocked(overriddenPackage); } } } @@ -430,27 +473,35 @@ public final class TextClassificationManagerService extends ITextClassifierServi private static final class PendingRequest implements IBinder.DeathRecipient { private final int mUid; - @Nullable private final String mName; - @Nullable private final IBinder mBinder; - @NonNull private final Runnable mRequest; - @Nullable private final Runnable mOnServiceFailure; + @Nullable + private final String mName; + @Nullable + private final IBinder mBinder; + @NonNull + private final Runnable mRequest; + @Nullable + private final Runnable mOnServiceFailure; @GuardedBy("mLock") - @NonNull private final UserState mOwningUser; - @NonNull private final TextClassificationManagerService mService; + @NonNull + private final ServiceState mServiceState; + @NonNull + private final TextClassificationManagerService mService; /** * Initializes a new pending request. - * @param request action to perform when the service is bound + * + * @param request action to perform when the service is bound * @param onServiceFailure action to perform when the service dies or disconnects - * @param binder binder to the process that made this pending request - * @param service - * @param owningUser + * @param binder binder to the process that made this pending request + * @parm service the TCMS instance. + * @param serviceState the service state of the service that will execute the request. + * @param uid the calling uid of the request. */ PendingRequest(@Nullable String name, @NonNull ThrowingRunnable request, @Nullable ThrowingRunnable onServiceFailure, @Nullable IBinder binder, - TextClassificationManagerService service, - UserState owningUser, int uid) { + @NonNull TextClassificationManagerService service, + @NonNull ServiceState serviceState, int uid) { mName = name; mRequest = logOnFailure(Objects.requireNonNull(request), "handling pending request"); @@ -458,7 +509,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi logOnFailure(onServiceFailure, "notifying callback of service failure"); mBinder = binder; mService = service; - mOwningUser = owningUser; + mServiceState = Objects.requireNonNull(serviceState); if (mBinder != null) { try { mBinder.linkToDeath(this, 0); @@ -479,7 +530,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi @GuardedBy("mLock") private void removeLocked() { - mOwningUser.mPendingRequests.remove(this); + mServiceState.mPendingRequests.remove(this); if (mBinder != null) { mBinder.unlinkToDeath(this, 0); } @@ -492,59 +543,172 @@ public final class TextClassificationManagerService extends ITextClassifierServi e -> Slog.d(LOG_TAG, "Error " + opDesc + ": " + e.getMessage())); } - private static void validateInput( - Context context, @Nullable String packageName, @UserIdInt int userId) - throws RemoteException { + private void validateCallingPackage(@Nullable String callingPackage) + throws PackageManager.NameNotFoundException { + if (callingPackage != null) { + final int packageUid = mContext.getPackageManager() + .getPackageUidAsUser(callingPackage, UserHandle.getCallingUserId()); + final int callingUid = Binder.getCallingUid(); + Preconditions.checkArgument( + callingUid == packageUid + // Trust the system process: + || callingUid == android.os.Process.SYSTEM_UID, + "Invalid package name. callingPackage=" + callingPackage + + ", callingUid=" + callingUid); + } + } - try { - if (packageName != null) { - final int packageUid = context.getPackageManager() - .getPackageUidAsUser(packageName, UserHandle.getCallingUserId()); - final int callingUid = Binder.getCallingUid(); - Preconditions.checkArgument(callingUid == packageUid - // Trust the system process: - || callingUid == android.os.Process.SYSTEM_UID, - "Invalid package name. Package=" + packageName - + ", CallingUid=" + callingUid); - } - - Preconditions.checkArgument(userId != UserHandle.USER_NULL, "Null userId"); - final int callingUserId = UserHandle.getCallingUserId(); - if (callingUserId != userId) { - context.enforceCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, - "Invalid userId. UserId=" + userId + ", CallingUserId=" + callingUserId); - } - } catch (Exception e) { - throw new RemoteException("Invalid request: " + e.getMessage(), e, - /* enableSuppression */ true, /* writableStackTrace */ true); + private void validateUser(@UserIdInt int userId) { + Preconditions.checkArgument(userId != UserHandle.USER_NULL, "Null userId"); + final int callingUserId = UserHandle.getCallingUserId(); + if (callingUserId != userId) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + "Invalid userId. UserId=" + userId + ", CallingUserId=" + callingUserId); } } private final class UserState { - @UserIdInt final int mUserId; + @UserIdInt + final int mUserId; + @Nullable + private final ServiceState mDefaultServiceState; + @Nullable + private final ServiceState mSystemServiceState; + @GuardedBy("mLock") + @Nullable + private ServiceState mUntrustedServiceState; + + private UserState(int userId) { + mUserId = userId; + mDefaultServiceState = TextUtils.isEmpty(mDefaultTextClassifierPackage) + ? null + : new ServiceState(userId, mDefaultTextClassifierPackage, /* isTrusted= */true); + mSystemServiceState = TextUtils.isEmpty(mSystemTextClassifierPackage) + ? null + : new ServiceState(userId, mSystemTextClassifierPackage, /* isTrusted= */ true); + } + + @GuardedBy("mLock") + @Nullable + ServiceState getServiceStateLocked(boolean useDefaultTextClassifier) { + if (useDefaultTextClassifier) { + return mDefaultServiceState; + } + String textClassifierServicePackageOverride = + mSettings.getTextClassifierServicePackageOverride(); + if (!TextUtils.isEmpty(textClassifierServicePackageOverride)) { + if (textClassifierServicePackageOverride.equals(mDefaultTextClassifierPackage)) { + return mDefaultServiceState; + } + if (textClassifierServicePackageOverride.equals(mSystemTextClassifierPackage) + && mSystemServiceState != null) { + return mSystemServiceState; + } + if (mUntrustedServiceState == null) { + mUntrustedServiceState = + new ServiceState( + mUserId, + textClassifierServicePackageOverride, + /* isTrusted= */false); + } + return mUntrustedServiceState; + } + return mSystemServiceState != null ? mSystemServiceState : mDefaultServiceState; + } + + @GuardedBy("mLock") + void onTextClassifierServicePackageOverrideChangedLocked(String overriddenPackageName) { + // The override config is just used for testing, and the flag value is not expected + // to change often. So, let's keep it simple and just unbind all the services here. The + // right service will be bound when the next request comes. + for (ServiceState serviceState : getAllServiceStatesLocked()) { + serviceState.unbindIfBoundLocked(); + } + mUntrustedServiceState = null; + } + @GuardedBy("mLock") - TextClassifierServiceConnection mConnection = null; + void bindIfHasPendingRequestsLocked() { + for (ServiceState serviceState : getAllServiceStatesLocked()) { + serviceState.bindIfHasPendingRequestsLocked(); + } + } + + @GuardedBy("mLock") + void cleanupServiceLocked() { + for (ServiceState serviceState : getAllServiceStatesLocked()) { + if (serviceState.mConnection != null) { + serviceState.mConnection.cleanupService(); + } + } + } + + @GuardedBy("mLock") + @NonNull + private List<ServiceState> getAllServiceStatesLocked() { + List<ServiceState> serviceStates = new ArrayList<>(); + if (mDefaultServiceState != null) { + serviceStates.add(mDefaultServiceState); + } + if (mSystemServiceState != null) { + serviceStates.add(mSystemServiceState); + } + if (mUntrustedServiceState != null) { + serviceStates.add(mUntrustedServiceState); + } + return serviceStates; + } + + void dump(IndentingPrintWriter pw) { + synchronized (mLock) { + pw.increaseIndent(); + dump(pw, mDefaultServiceState, "Default"); + dump(pw, mSystemServiceState, "System"); + dump(pw, mUntrustedServiceState, "Untrusted"); + pw.decreaseIndent(); + } + } + + private void dump( + IndentingPrintWriter pw, @Nullable ServiceState serviceState, String name) { + synchronized (mLock) { + if (serviceState != null) { + pw.print(name + ": "); + serviceState.dump(pw); + pw.println(); + } + } + } + } + + private final class ServiceState { + @UserIdInt + final int mUserId; + @NonNull + final String mPackageName; + @NonNull + final TextClassifierServiceConnection mConnection; + final boolean mIsTrusted; + @NonNull @GuardedBy("mLock") final Queue<PendingRequest> mPendingRequests = new ArrayDeque<>(); + @Nullable @GuardedBy("mLock") ITextClassifierService mService; @GuardedBy("mLock") boolean mBinding; + @Nullable @GuardedBy("mLock") ComponentName mBoundComponentName = null; @GuardedBy("mLock") - boolean mBoundToDefaultTrustService; - @GuardedBy("mLock") - int mBoundServiceUid; - - private final Context mContext; - private final Object mLock; + int mBoundServiceUid = Process.INVALID_UID; - private UserState(int userId, Context context, Object lock) { + private ServiceState(@UserIdInt int userId, String packageName, boolean isTrusted) { mUserId = userId; - mContext = Objects.requireNonNull(context); - mLock = Objects.requireNonNull(lock); + mPackageName = packageName; + mConnection = new TextClassifierServiceConnection(mUserId); + mIsTrusted = isTrusted; } @GuardedBy("mLock") @@ -581,18 +745,12 @@ public final class TextClassificationManagerService extends ITextClassifierServi } @GuardedBy("mLock") - private boolean isCurrentlyBoundToComponentLocked(@NonNull ComponentName componentName) { - return (mBoundComponentName != null - && mBoundComponentName.getPackageName().equals( - componentName.getPackageName())); - } - - @GuardedBy("mLock") - private void unbindLocked() { - Slog.v(LOG_TAG, "unbinding from " + mBoundComponentName + " for " + mUserId); - mContext.unbindService(mConnection); - mConnection.cleanupService(); - mConnection = null; + void unbindIfBoundLocked() { + if (isBoundLocked()) { + Slog.v(LOG_TAG, "Unbinding " + mBoundComponentName + " for " + mUserId); + mContext.unbindService(mConnection); + mConnection.cleanupService(); + } } /** @@ -609,8 +767,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi final boolean willBind; final long identity = Binder.clearCallingIdentity(); try { - final ComponentName componentName = - TextClassifierService.getServiceComponentName(mContext); + final ComponentName componentName = getTextClassifierServiceComponent(); if (componentName == null) { // Might happen if the storage is encrypted and the user is not unlocked return false; @@ -618,7 +775,6 @@ public final class TextClassificationManagerService extends ITextClassifierServi Intent serviceIntent = new Intent(TextClassifierService.SERVICE_INTERFACE) .setComponent(componentName); Slog.d(LOG_TAG, "Binding to " + serviceIntent.getComponent()); - mConnection = new TextClassifierServiceConnection(mUserId); willBind = mContext.bindServiceAsUser( serviceIntent, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE @@ -631,12 +787,21 @@ public final class TextClassificationManagerService extends ITextClassifierServi return willBind; } + @Nullable + private ComponentName getTextClassifierServiceComponent() { + return TextClassifierService.getServiceComponentName( + mContext, + mPackageName, + mIsTrusted ? PackageManager.MATCH_SYSTEM_ONLY : 0); + } + private void dump(IndentingPrintWriter pw) { pw.printPair("context", mContext); pw.printPair("userId", mUserId); synchronized (mLock) { + pw.printPair("packageName", mPackageName); pw.printPair("boundComponentName", mBoundComponentName); - pw.printPair("boundToDefaultTrustService", mBoundToDefaultTrustService); + pw.printPair("isTrusted", mIsTrusted); pw.printPair("boundServiceUid", mBoundServiceUid); pw.printPair("binding", mBinding); pw.printPair("numberRequests", mPendingRequests.size()); @@ -645,7 +810,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi @GuardedBy("mLock") private boolean checkRequestAcceptedLocked(int requestUid, @NonNull String methodName) { - if (mBoundToDefaultTrustService || (requestUid == mBoundServiceUid)) { + if (mIsTrusted || (requestUid == mBoundServiceUid)) { return true; } Slog.w(LOG_TAG, String.format( @@ -654,47 +819,19 @@ public final class TextClassificationManagerService extends ITextClassifierServi return false; } - private boolean isDefaultTrustService(@NonNull ComponentName currentService) { - final String[] defaultServiceNames = - mContext.getPackageManager().getSystemTextClassifierPackages(); - final String servicePackageName = currentService.getPackageName(); - - for (int i = 0; i < defaultServiceNames.length; i++) { - if (defaultServiceNames[i].equals(servicePackageName)) { - return true; - } - } - return false; - } - - private int getServiceUid(@Nullable ComponentName service, int userId) { - if (service == null) { - return Process.INVALID_UID; - } - final String servicePackageName = service.getPackageName(); - final PackageManager pm = mContext.getPackageManager(); - final int serviceUid; - - try { - serviceUid = pm.getPackageUidAsUser(servicePackageName, userId); - } catch (PackageManager.NameNotFoundException e) { - Slog.e(LOG_TAG, "Could not verify UID for " + service); - return Process.INVALID_UID; - } - return serviceUid; - } - @GuardedBy("mLock") private void updateServiceInfoLocked(int userId, @Nullable ComponentName componentName) { mBoundComponentName = componentName; - mBoundToDefaultTrustService = (mBoundComponentName != null && isDefaultTrustService( - mBoundComponentName)); - mBoundServiceUid = getServiceUid(mBoundComponentName, userId); + mBoundServiceUid = + mBoundComponentName == null + ? Process.INVALID_UID + : resolvePackageToUid(mBoundComponentName.getPackageName(), userId); } private final class TextClassifierServiceConnection implements ServiceConnection { - @UserIdInt private final int mUserId; + @UserIdInt + private final int mUserId; TextClassifierServiceConnection(int userId) { mUserId = userId; @@ -745,18 +882,18 @@ public final class TextClassificationManagerService extends ITextClassifierServi private final class TextClassifierSettingsListener implements DeviceConfig.OnPropertiesChangedListener { + @NonNull + private final Context mContext; + @Nullable + private String mServicePackageOverride; - @NonNull private final Context mContext; - @NonNull private final TextClassificationConstants mSettings; - @Nullable private String mServicePackageName; TextClassifierSettingsListener(Context context) { mContext = context; - mSettings = TextClassificationManager.getSettings(mContext); - mServicePackageName = mSettings.getTextClassifierServicePackageOverride(); + mServicePackageOverride = mSettings.getTextClassifierServicePackageOverride(); } - public void registerObserver() { + void registerObserver() { DeviceConfig.addOnPropertiesChangedListener( DeviceConfig.NAMESPACE_TEXTCLASSIFIER, mContext.getMainExecutor(), @@ -765,13 +902,13 @@ public final class TextClassificationManagerService extends ITextClassifierServi @Override public void onPropertiesChanged(DeviceConfig.Properties properties) { - final String overrideServiceName = mSettings.getTextClassifierServicePackageOverride(); - - if (TextUtils.equals(overrideServiceName, mServicePackageName)) { + final String currentServicePackageOverride = + mSettings.getTextClassifierServicePackageOverride(); + if (TextUtils.equals(currentServicePackageOverride, mServicePackageOverride)) { return; } - mServicePackageName = overrideServiceName; - unbindServiceIfNecessary(); + mServicePackageOverride = currentServicePackageOverride; + onTextClassifierServicePackageOverrideChanged(currentServicePackageOverride); } } } diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java index 14b847fa9ff6..5f95bc124de6 100644 --- a/test-mock/src/android/test/mock/MockPackageManager.java +++ b/test-mock/src/android/test/mock/MockPackageManager.java @@ -1249,12 +1249,4 @@ public class MockPackageManager extends PackageManager { int uid, byte[] certificate, @PackageManager.CertificateInputType int type) { throw new UnsupportedOperationException(); } - - /** - * @hide - */ - @Override - public String getSystemTextClassifierPackageName() { - throw new UnsupportedOperationException(); - } } |