• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static android.Manifest.permission.ACCESS_TEXT_CLASSIFIER_BY_TYPE;
20 
21 import android.annotation.FlaggedApi;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.RequiresPermission;
25 import android.annotation.SystemApi;
26 import android.annotation.SystemService;
27 import android.compat.annotation.UnsupportedAppUsage;
28 import android.content.Context;
29 import android.content.pm.PackageManager;
30 import android.os.Build;
31 import android.os.ServiceManager;
32 import android.permission.flags.Flags;
33 import android.view.textclassifier.TextClassifier.TextClassifierType;
34 
35 import com.android.internal.annotations.GuardedBy;
36 import com.android.internal.util.IndentingPrintWriter;
37 
38 import java.util.Objects;
39 
40 /**
41  * Interface to the text classification service.
42  */
43 @SystemService(Context.TEXT_CLASSIFICATION_SERVICE)
44 public final class TextClassificationManager {
45 
46     private static final String LOG_TAG = TextClassifier.LOG_TAG;
47 
48     private static final TextClassificationConstants sDefaultSettings =
49             new TextClassificationConstants();
50 
51     private final Object mLock = new Object();
52     private final TextClassificationSessionFactory mDefaultSessionFactory =
53             classificationContext -> new TextClassificationSession(
54                     classificationContext, getTextClassifier());
55 
56     private final Context mContext;
57 
58     @GuardedBy("mLock")
59     @Nullable
60     private TextClassifier mCustomTextClassifier;
61     @GuardedBy("mLock")
62     private TextClassificationSessionFactory mSessionFactory;
63     @GuardedBy("mLock")
64     private TextClassificationConstants mSettings;
65 
66     /** @hide */
TextClassificationManager(Context context)67     public TextClassificationManager(Context context) {
68         mContext = Objects.requireNonNull(context);
69         mSessionFactory = mDefaultSessionFactory;
70     }
71 
72     /**
73      * Returns the text classifier that was set via {@link #setTextClassifier(TextClassifier)}.
74      * If this is null, this method returns a default text classifier (i.e. either the system text
75      * classifier if one exists, or a local text classifier running in this process.)
76      * <p>
77      * Note that requests to the TextClassifier may be handled in an OEM-provided process rather
78      * than in the calling app's process.
79      *
80      * @see #setTextClassifier(TextClassifier)
81      */
82     @NonNull
getTextClassifier()83     public TextClassifier getTextClassifier() {
84         synchronized (mLock) {
85             if (mCustomTextClassifier != null) {
86                 return mCustomTextClassifier;
87             } else if (getSettings().isSystemTextClassifierEnabled()) {
88                 return getSystemTextClassifier(SystemTextClassifier.SYSTEM);
89             } else {
90                 return getLocalTextClassifier();
91             }
92         }
93     }
94 
95     /**
96      * Sets the text classifier.
97      * Set to null to use the system default text classifier.
98      * Set to {@link TextClassifier#NO_OP} to disable text classifier features.
99      */
setTextClassifier(@ullable TextClassifier textClassifier)100     public void setTextClassifier(@Nullable TextClassifier textClassifier) {
101         synchronized (mLock) {
102             mCustomTextClassifier = textClassifier;
103         }
104     }
105 
106     /**
107      * Returns a specific type of text classifier.
108      * If the specified text classifier cannot be found, this returns {@link TextClassifier#NO_OP}.
109      *
110      * @see TextClassifier#LOCAL
111      * @see TextClassifier#SYSTEM
112      * @see TextClassifier#DEFAULT_SYSTEM
113      * @hide
114      */
115     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
getTextClassifier(@extClassifierType int type)116     public TextClassifier getTextClassifier(@TextClassifierType int type) {
117         switch (type) {
118             case TextClassifier.LOCAL:
119                 return getLocalTextClassifier();
120             default:
121                 return getSystemTextClassifier(type);
122         }
123     }
124 
125     /**
126      * Returns a specific type of text classifier.
127      * If the specified text classifier cannot be found, this returns {@link TextClassifier#NO_OP}.
128      * <p>
129      *
130      * @see TextClassifier#CLASSIFIER_TYPE_SELF_PROVIDED
131      * @see TextClassifier#CLASSIFIER_TYPE_DEVICE_DEFAULT
132      * @see TextClassifier#CLASSIFIER_TYPE_ANDROID_DEFAULT
133      * @hide
134      */
135     @SystemApi
136     @NonNull
137     @FlaggedApi(Flags.FLAG_TEXT_CLASSIFIER_CHOICE_API_ENABLED)
138     @RequiresPermission(ACCESS_TEXT_CLASSIFIER_BY_TYPE)
getClassifier(@extClassifierType int type)139     public TextClassifier getClassifier(@TextClassifierType int type) {
140         if (mContext.checkCallingOrSelfPermission(ACCESS_TEXT_CLASSIFIER_BY_TYPE)
141                 != PackageManager.PERMISSION_GRANTED) {
142             throw new SecurityException(
143                     "Caller does not have permission " + ACCESS_TEXT_CLASSIFIER_BY_TYPE);
144         }
145         return getTextClassifier(type);
146     }
147 
getSettings()148     private TextClassificationConstants getSettings() {
149         synchronized (mLock) {
150             if (mSettings == null) {
151                 mSettings = new TextClassificationConstants();
152             }
153             return mSettings;
154         }
155     }
156 
157     /**
158      * Call this method to start a text classification session with the given context.
159      * A session is created with a context helping the classifier better understand
160      * what the user needs and consists of queries and feedback events. The queries
161      * are directly related to providing useful functionality to the user and the events
162      * are a feedback loop back to the classifier helping it learn and better serve
163      * future queries.
164      *
165      * <p> All interactions with the returned classifier are considered part of a single
166      * session and are logically grouped. For example, when a text widget is focused
167      * all user interactions around text editing (selection, editing, etc) can be
168      * grouped together to allow the classifier get better.
169      *
170      * @param classificationContext The context in which classification would occur
171      *
172      * @return An instance to perform classification in the given context
173      */
174     @NonNull
createTextClassificationSession( @onNull TextClassificationContext classificationContext)175     public TextClassifier createTextClassificationSession(
176             @NonNull TextClassificationContext classificationContext) {
177         Objects.requireNonNull(classificationContext);
178         final TextClassifier textClassifier =
179                 mSessionFactory.createTextClassificationSession(classificationContext);
180         Objects.requireNonNull(textClassifier, "Session Factory should never return null");
181         return textClassifier;
182     }
183 
184     /**
185      * @see #createTextClassificationSession(TextClassificationContext, TextClassifier)
186      * @hide
187      */
createTextClassificationSession( TextClassificationContext classificationContext, TextClassifier textClassifier)188     public TextClassifier createTextClassificationSession(
189             TextClassificationContext classificationContext, TextClassifier textClassifier) {
190         Objects.requireNonNull(classificationContext);
191         Objects.requireNonNull(textClassifier);
192         return new TextClassificationSession(classificationContext, textClassifier);
193     }
194 
195     /**
196      * Sets a TextClassificationSessionFactory to be used to create session-aware TextClassifiers.
197      *
198      * @param factory the textClassification session factory. If this is null, the default factory
199      *      will be used.
200      */
setTextClassificationSessionFactory( @ullable TextClassificationSessionFactory factory)201     public void setTextClassificationSessionFactory(
202             @Nullable TextClassificationSessionFactory factory) {
203         synchronized (mLock) {
204             if (factory != null) {
205                 mSessionFactory = factory;
206             } else {
207                 mSessionFactory = mDefaultSessionFactory;
208             }
209         }
210     }
211 
212     /** @hide */
getSystemTextClassifier(@extClassifierType int type)213     private TextClassifier getSystemTextClassifier(@TextClassifierType int type) {
214         synchronized (mLock) {
215             if (getSettings().isSystemTextClassifierEnabled()) {
216                 try {
217                     Log.d(LOG_TAG, "Initializing SystemTextClassifier, type = "
218                             + TextClassifier.typeToString(type));
219                     return new SystemTextClassifier(
220                             mContext,
221                             getSettings(),
222                             /* useDefault= */ type == TextClassifier.DEFAULT_SYSTEM);
223                 } catch (ServiceManager.ServiceNotFoundException e) {
224                     Log.e(LOG_TAG, "Could not initialize SystemTextClassifier", e);
225                 }
226             }
227             return TextClassifier.NO_OP;
228         }
229     }
230 
231     /**
232      * Returns a local textclassifier, which is running in this process.
233      */
234     @NonNull
getLocalTextClassifier()235     private TextClassifier getLocalTextClassifier() {
236         Log.d(LOG_TAG, "Local text-classifier not supported. Returning a no-op text-classifier.");
237         return TextClassifier.NO_OP;
238     }
239 
240     /** @hide **/
dump(IndentingPrintWriter pw)241     public void dump(IndentingPrintWriter pw) {
242         getSystemTextClassifier(TextClassifier.DEFAULT_SYSTEM).dump(pw);
243         getSystemTextClassifier(TextClassifier.SYSTEM).dump(pw);
244         getSettings().dump(pw);
245     }
246 
247     /** @hide */
getSettings(Context context)248     public static TextClassificationConstants getSettings(Context context) {
249         Objects.requireNonNull(context);
250         final TextClassificationManager tcm =
251                 context.getSystemService(TextClassificationManager.class);
252         if (tcm != null) {
253             return tcm.getSettings();
254         } else {
255             // Use default settings if there is no tcm.
256             return sDefaultSettings;
257         }
258     }
259 }
260