1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.view.textclassifier; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.SystemService; 22 import android.compat.annotation.UnsupportedAppUsage; 23 import android.content.Context; 24 import android.os.Build; 25 import android.os.ServiceManager; 26 import android.view.textclassifier.TextClassifier.TextClassifierType; 27 28 import com.android.internal.annotations.GuardedBy; 29 import com.android.internal.util.IndentingPrintWriter; 30 31 import java.util.Objects; 32 33 /** 34 * Interface to the text classification service. 35 */ 36 @SystemService(Context.TEXT_CLASSIFICATION_SERVICE) 37 public final class TextClassificationManager { 38 39 private static final String LOG_TAG = TextClassifier.LOG_TAG; 40 41 private static final TextClassificationConstants sDefaultSettings = 42 new TextClassificationConstants(); 43 44 private final Object mLock = new Object(); 45 private final TextClassificationSessionFactory mDefaultSessionFactory = 46 classificationContext -> new TextClassificationSession( 47 classificationContext, getTextClassifier()); 48 49 private final Context mContext; 50 51 @GuardedBy("mLock") 52 @Nullable 53 private TextClassifier mCustomTextClassifier; 54 @GuardedBy("mLock") 55 private TextClassificationSessionFactory mSessionFactory; 56 @GuardedBy("mLock") 57 private TextClassificationConstants mSettings; 58 59 /** @hide */ TextClassificationManager(Context context)60 public TextClassificationManager(Context context) { 61 mContext = Objects.requireNonNull(context); 62 mSessionFactory = mDefaultSessionFactory; 63 } 64 65 /** 66 * Returns the text classifier that was set via {@link #setTextClassifier(TextClassifier)}. 67 * If this is null, this method returns a default text classifier (i.e. either the system text 68 * classifier if one exists, or a local text classifier running in this process.) 69 * <p> 70 * Note that requests to the TextClassifier may be handled in an OEM-provided process rather 71 * than in the calling app's process. 72 * 73 * @see #setTextClassifier(TextClassifier) 74 */ 75 @NonNull getTextClassifier()76 public TextClassifier getTextClassifier() { 77 synchronized (mLock) { 78 if (mCustomTextClassifier != null) { 79 return mCustomTextClassifier; 80 } else if (getSettings().isSystemTextClassifierEnabled()) { 81 return getSystemTextClassifier(SystemTextClassifier.SYSTEM); 82 } else { 83 return getLocalTextClassifier(); 84 } 85 } 86 } 87 88 /** 89 * Sets the text classifier. 90 * Set to null to use the system default text classifier. 91 * Set to {@link TextClassifier#NO_OP} to disable text classifier features. 92 */ setTextClassifier(@ullable TextClassifier textClassifier)93 public void setTextClassifier(@Nullable TextClassifier textClassifier) { 94 synchronized (mLock) { 95 mCustomTextClassifier = textClassifier; 96 } 97 } 98 99 /** 100 * Returns a specific type of text classifier. 101 * If the specified text classifier cannot be found, this returns {@link TextClassifier#NO_OP}. 102 * 103 * @see TextClassifier#LOCAL 104 * @see TextClassifier#SYSTEM 105 * @see TextClassifier#DEFAULT_SYSTEM 106 * @hide 107 */ 108 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) getTextClassifier(@extClassifierType int type)109 public TextClassifier getTextClassifier(@TextClassifierType int type) { 110 switch (type) { 111 case TextClassifier.LOCAL: 112 return getLocalTextClassifier(); 113 default: 114 return getSystemTextClassifier(type); 115 } 116 } 117 getSettings()118 private TextClassificationConstants getSettings() { 119 synchronized (mLock) { 120 if (mSettings == null) { 121 mSettings = new TextClassificationConstants(); 122 } 123 return mSettings; 124 } 125 } 126 127 /** 128 * Call this method to start a text classification session with the given context. 129 * A session is created with a context helping the classifier better understand 130 * what the user needs and consists of queries and feedback events. The queries 131 * are directly related to providing useful functionality to the user and the events 132 * are a feedback loop back to the classifier helping it learn and better serve 133 * future queries. 134 * 135 * <p> All interactions with the returned classifier are considered part of a single 136 * session and are logically grouped. For example, when a text widget is focused 137 * all user interactions around text editing (selection, editing, etc) can be 138 * grouped together to allow the classifier get better. 139 * 140 * @param classificationContext The context in which classification would occur 141 * 142 * @return An instance to perform classification in the given context 143 */ 144 @NonNull createTextClassificationSession( @onNull TextClassificationContext classificationContext)145 public TextClassifier createTextClassificationSession( 146 @NonNull TextClassificationContext classificationContext) { 147 Objects.requireNonNull(classificationContext); 148 final TextClassifier textClassifier = 149 mSessionFactory.createTextClassificationSession(classificationContext); 150 Objects.requireNonNull(textClassifier, "Session Factory should never return null"); 151 return textClassifier; 152 } 153 154 /** 155 * @see #createTextClassificationSession(TextClassificationContext, TextClassifier) 156 * @hide 157 */ createTextClassificationSession( TextClassificationContext classificationContext, TextClassifier textClassifier)158 public TextClassifier createTextClassificationSession( 159 TextClassificationContext classificationContext, TextClassifier textClassifier) { 160 Objects.requireNonNull(classificationContext); 161 Objects.requireNonNull(textClassifier); 162 return new TextClassificationSession(classificationContext, textClassifier); 163 } 164 165 /** 166 * Sets a TextClassificationSessionFactory to be used to create session-aware TextClassifiers. 167 * 168 * @param factory the textClassification session factory. If this is null, the default factory 169 * will be used. 170 */ setTextClassificationSessionFactory( @ullable TextClassificationSessionFactory factory)171 public void setTextClassificationSessionFactory( 172 @Nullable TextClassificationSessionFactory factory) { 173 synchronized (mLock) { 174 if (factory != null) { 175 mSessionFactory = factory; 176 } else { 177 mSessionFactory = mDefaultSessionFactory; 178 } 179 } 180 } 181 182 /** @hide */ getSystemTextClassifier(@extClassifierType int type)183 private TextClassifier getSystemTextClassifier(@TextClassifierType int type) { 184 synchronized (mLock) { 185 if (getSettings().isSystemTextClassifierEnabled()) { 186 try { 187 Log.d(LOG_TAG, "Initializing SystemTextClassifier, type = " 188 + TextClassifier.typeToString(type)); 189 return new SystemTextClassifier( 190 mContext, 191 getSettings(), 192 /* useDefault= */ type == TextClassifier.DEFAULT_SYSTEM); 193 } catch (ServiceManager.ServiceNotFoundException e) { 194 Log.e(LOG_TAG, "Could not initialize SystemTextClassifier", e); 195 } 196 } 197 return TextClassifier.NO_OP; 198 } 199 } 200 201 /** 202 * Returns a local textclassifier, which is running in this process. 203 */ 204 @NonNull getLocalTextClassifier()205 private TextClassifier getLocalTextClassifier() { 206 Log.d(LOG_TAG, "Local text-classifier not supported. Returning a no-op text-classifier."); 207 return TextClassifier.NO_OP; 208 } 209 210 /** @hide **/ dump(IndentingPrintWriter pw)211 public void dump(IndentingPrintWriter pw) { 212 getSystemTextClassifier(TextClassifier.DEFAULT_SYSTEM).dump(pw); 213 getSystemTextClassifier(TextClassifier.SYSTEM).dump(pw); 214 getSettings().dump(pw); 215 } 216 217 /** @hide */ getSettings(Context context)218 public static TextClassificationConstants getSettings(Context context) { 219 Objects.requireNonNull(context); 220 final TextClassificationManager tcm = 221 context.getSystemService(TextClassificationManager.class); 222 if (tcm != null) { 223 return tcm.getSettings(); 224 } else { 225 // Use default settings if there is no tcm. 226 return sDefaultSettings; 227 } 228 } 229 } 230