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