blob: f6ff359d8c1925b69607ea6e80a64a1174674fab [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 * use this file except in compliance with the License. You may obtain a copy of
5 * the License at
Doug Zongkerab5c49c2009-12-04 10:31:43 -08006 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007 * http://www.apache.org/licenses/LICENSE-2.0
Doug Zongkerab5c49c2009-12-04 10:31:43 -08008 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08009 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 * License for the specific language governing permissions and limitations under
13 * the License.
14 */
15
16package com.android.server;
17
Wale Ogunwaleac2561e2016-11-01 15:43:46 -070018import static android.view.Display.DEFAULT_DISPLAY;
Wale Ogunwale3a931692016-11-02 16:49:48 -070019import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
Wale Ogunwaleac2561e2016-11-01 15:43:46 -070020import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
Wale Ogunwale3a931692016-11-02 16:49:48 -070021import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
Yohei Yukawafa0e47e2016-04-05 09:55:56 -070022import static java.lang.annotation.RetentionPolicy.SOURCE;
23
Yohei Yukawac4e44912017-02-09 19:30:22 -080024import com.android.internal.annotations.GuardedBy;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -080025import com.android.internal.content.PackageMonitor;
Yohei Yukawa25e08132016-06-22 16:31:41 -070026import com.android.internal.inputmethod.IInputContentUriToken;
Satoshi Kataokad7443c82013-10-15 17:45:43 +090027import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController;
Satoshi Kataokad787f692013-10-26 04:44:21 +090028import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +090029import com.android.internal.inputmethod.InputMethodUtils;
30import com.android.internal.inputmethod.InputMethodUtils.InputMethodSettings;
Chris Wren282cfef2017-03-27 15:01:44 -040031import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
Geoffrey Pitschaf759c52017-02-15 09:35:38 -050032import com.android.internal.notification.SystemNotificationChannels;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import com.android.internal.os.HandlerCaller;
Svetoslav Ganov758143e2012-08-06 16:40:27 -070034import com.android.internal.os.SomeArgs;
Jeff Sharkey850c83e2016-11-09 12:25:44 -070035import com.android.internal.os.TransferPipe;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060036import com.android.internal.util.DumpUtils;
satoke7c6998e2011-06-03 17:57:59 +090037import com.android.internal.util.FastXmlSerializer;
Yohei Yukawacac97722017-12-15 16:52:05 -080038import com.android.internal.util.IndentingPrintWriter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import com.android.internal.view.IInputContext;
40import com.android.internal.view.IInputMethod;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import com.android.internal.view.IInputMethodClient;
42import com.android.internal.view.IInputMethodManager;
43import com.android.internal.view.IInputMethodSession;
Yohei Yukawa6ab1a822015-08-25 14:01:28 -070044import com.android.internal.view.IInputSessionCallback;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import com.android.internal.view.InputBindResult;
Yohei Yukawa33e81792015-11-17 21:14:42 -080046import com.android.internal.view.InputMethodClient;
Adam Lesinskief2ea1f2013-12-05 16:48:06 -080047import com.android.server.statusbar.StatusBarManagerService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048
satoke7c6998e2011-06-03 17:57:59 +090049import org.xmlpull.v1.XmlPullParser;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import org.xmlpull.v1.XmlPullParserException;
satoke7c6998e2011-06-03 17:57:59 +090051import org.xmlpull.v1.XmlSerializer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052
Yohei Yukawa926488d2017-12-11 17:24:55 -080053import android.Manifest;
Tarandeep Singh75a92392018-01-12 14:58:59 -080054import android.annotation.AnyThread;
Yohei Yukawad6475a62017-04-17 10:35:27 -070055import android.annotation.BinderThread;
Tadashi G. Takaoka01065a52017-07-19 14:10:24 +090056import android.annotation.ColorInt;
Yohei Yukawafa0e47e2016-04-05 09:55:56 -070057import android.annotation.IntDef;
Yohei Yukawa930328c2017-10-18 20:19:53 -070058import android.annotation.MainThread;
Yohei Yukawa6ab1a822015-08-25 14:01:28 -070059import android.annotation.NonNull;
Yohei Yukawae13a20fa2015-09-30 19:11:32 -070060import android.annotation.Nullable;
Yohei Yukawa926488d2017-12-11 17:24:55 -080061import android.annotation.RequiresPermission;
Tarandeep Singheb570612018-01-29 16:20:32 -080062import android.annotation.TestApi;
Yohei Yukawa7b18aec2016-03-07 13:04:32 -080063import android.annotation.UserIdInt;
Sudheer Shankadc589ac2016-11-10 15:30:17 -080064import android.app.ActivityManager;
Sudheer Shankafc46e9b2016-10-21 17:55:27 -070065import android.app.ActivityManagerInternal;
Andrew Sapperstein8a3b4cb2017-04-28 14:35:31 -070066import android.app.ActivityThread;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067import android.app.AlertDialog;
Yohei Yukawa6ab1a822015-08-25 14:01:28 -070068import android.app.AppGlobals;
Yohei Yukawae63b5fa2014-09-19 13:14:55 +090069import android.app.AppOpsManager;
satokf90a33e2011-07-19 11:55:52 +090070import android.app.KeyguardManager;
satok7cfc0ed2011-06-20 21:29:36 +090071import android.app.Notification;
72import android.app.NotificationManager;
Dianne Hackborndd9b82c2009-09-03 00:18:47 -070073import android.app.PendingIntent;
satok5b927c432012-05-01 20:09:34 +090074import android.content.BroadcastReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075import android.content.ComponentName;
Yohei Yukawa3933a6e2016-11-10 00:47:48 -080076import android.content.ContentProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077import android.content.ContentResolver;
78import android.content.Context;
79import android.content.DialogInterface;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080import android.content.DialogInterface.OnCancelListener;
Tadashi G. Takaokad130b802014-08-01 14:05:47 +090081import android.content.DialogInterface.OnClickListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082import android.content.Intent;
satoke7c6998e2011-06-03 17:57:59 +090083import android.content.IntentFilter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084import android.content.ServiceConnection;
Brandon Ballinger6da35a02009-10-21 00:38:13 -070085import android.content.pm.ApplicationInfo;
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +090086import android.content.pm.IPackageManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087import android.content.pm.PackageManager;
88import android.content.pm.ResolveInfo;
89import android.content.pm.ServiceInfo;
Amith Yamasanie861ec12010-03-24 21:39:27 -070090import android.content.res.Configuration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091import android.content.res.Resources;
92import android.content.res.TypedArray;
93import android.database.ContentObserver;
Alan Viverette505e3ab2014-11-24 15:22:11 -080094import android.graphics.drawable.Drawable;
Joe Onorato857fd9b2011-01-27 15:08:35 -080095import android.inputmethodservice.InputMethodService;
Michael Wright7b5a96b2014-08-09 19:28:42 -070096import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097import android.os.Binder;
Chris Wren1ce4b6d2015-06-11 10:19:43 -040098import android.os.Bundle;
Seigo Nonakae27dc2b2015-08-14 18:21:27 -070099import android.os.Debug;
satoke7c6998e2011-06-03 17:57:59 +0900100import android.os.Environment;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101import android.os.Handler;
102import android.os.IBinder;
103import android.os.IInterface;
104import android.os.Message;
Yohei Yukawa23cbe852016-05-17 16:42:58 -0700105import android.os.LocaleList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106import android.os.Parcel;
Yohei Yukawa6ab1a822015-08-25 14:01:28 -0700107import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108import android.os.RemoteException;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800109import android.os.ResultReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110import android.os.ServiceManager;
Yohei Yukawa926488d2017-12-11 17:24:55 -0800111import android.os.ShellCallback;
112import android.os.ShellCommand;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113import android.os.SystemClock;
Tarandeep Singh75a92392018-01-12 14:58:59 -0800114import android.os.SystemProperties;
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +0900115import android.os.UserHandle;
Amith Yamasani734983f2014-03-04 16:48:05 -0800116import android.os.UserManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117import android.provider.Settings;
Tarandeep Singh89a6c482017-11-21 14:26:11 -0800118import android.service.vr.IVrManager;
119import android.service.vr.IVrStateCallbacks;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120import android.text.TextUtils;
satokf9f01002011-05-19 21:31:50 +0900121import android.text.style.SuggestionSpan;
Christopher Tate7b9a28c2015-03-18 13:06:16 -0700122import android.util.ArraySet;
Dianne Hackborn39606a02012-07-31 17:54:35 -0700123import android.util.AtomicFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124import android.util.EventLog;
satokf9f01002011-05-19 21:31:50 +0900125import android.util.LruCache;
satokab751aa2010-09-14 19:17:36 +0900126import android.util.Pair;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127import android.util.PrintWriterPrinter;
128import android.util.Printer;
satoke7c6998e2011-06-03 17:57:59 +0900129import android.util.Slog;
130import android.util.Xml;
Tadashi G. Takaokad130b802014-08-01 14:05:47 +0900131import android.view.ContextThemeWrapper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132import android.view.IWindowManager;
Jeff Brownc28867a2013-03-26 15:42:39 -0700133import android.view.InputChannel;
Ken Wakasa05dbb652011-08-22 15:22:43 +0900134import android.view.LayoutInflater;
135import android.view.View;
136import android.view.ViewGroup;
Wale Ogunwale3a931692016-11-02 16:49:48 -0700137import android.view.Window;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138import android.view.WindowManager;
satokab751aa2010-09-14 19:17:36 +0900139import android.view.inputmethod.EditorInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140import android.view.inputmethod.InputBinding;
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800141import android.view.inputmethod.InputConnection;
Yohei Yukawa19a80a12016-03-14 22:57:37 -0700142import android.view.inputmethod.InputConnectionInspector;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143import android.view.inputmethod.InputMethod;
144import android.view.inputmethod.InputMethodInfo;
145import android.view.inputmethod.InputMethodManager;
Yohei Yukawafa6e0a82015-07-23 15:08:59 -0700146import android.view.inputmethod.InputMethodManagerInternal;
satokab751aa2010-09-14 19:17:36 +0900147import android.view.inputmethod.InputMethodSubtype;
Yohei Yukawa443c2ba2014-09-10 14:10:30 +0900148import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
Ken Wakasa05dbb652011-08-22 15:22:43 +0900149import android.widget.ArrayAdapter;
satok01038492012-04-09 21:08:27 +0900150import android.widget.CompoundButton;
151import android.widget.CompoundButton.OnCheckedChangeListener;
Ken Wakasa05dbb652011-08-22 15:22:43 +0900152import android.widget.RadioButton;
satok01038492012-04-09 21:08:27 +0900153import android.widget.Switch;
Ken Wakasa05dbb652011-08-22 15:22:43 +0900154import android.widget.TextView;
Yohei Yukawaebda7d72016-04-02 17:39:23 -0700155import android.widget.Toast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156
Adrian Roose99bc052017-11-20 17:55:31 +0100157import com.android.server.wm.WindowManagerInternal;
158
satoke7c6998e2011-06-03 17:57:59 +0900159import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160import java.io.FileDescriptor;
satoke7c6998e2011-06-03 17:57:59 +0900161import java.io.FileInputStream;
162import java.io.FileOutputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163import java.io.IOException;
164import java.io.PrintWriter;
Yohei Yukawafa0e47e2016-04-05 09:55:56 -0700165import java.lang.annotation.Retention;
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100166import java.nio.charset.StandardCharsets;
Yohei Yukawa25e08132016-06-22 16:31:41 -0700167import java.security.InvalidParameterException;
Yohei Yukawa357b2f62017-02-14 09:40:03 -0800168import java.text.SimpleDateFormat;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169import java.util.ArrayList;
satok688bd472012-02-09 20:09:17 +0900170import java.util.Collections;
Yohei Yukawa357b2f62017-02-14 09:40:03 -0800171import java.util.Date;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172import java.util.HashMap;
173import java.util.List;
Yohei Yukawa357b2f62017-02-14 09:40:03 -0800174import java.util.Locale;
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800175import java.util.WeakHashMap;
Yohei Yukawa357b2f62017-02-14 09:40:03 -0800176import java.util.concurrent.atomic.AtomicInteger;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177
178/**
179 * This class provides a system service that manages input methods.
180 */
181public class InputMethodManagerService extends IInputMethodManager.Stub
182 implements ServiceConnection, Handler.Callback {
183 static final boolean DEBUG = false;
Jeff Brown6ec6f792012-04-17 16:52:41 -0700184 static final String TAG = "InputMethodManagerService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185
Yohei Yukawa926488d2017-12-11 17:24:55 -0800186 @Retention(SOURCE)
187 @IntDef({ShellCommandResult.SUCCESS, ShellCommandResult.FAILURE})
188 private @interface ShellCommandResult {
189 int SUCCESS = 0;
190 int FAILURE = -1;
191 }
192
Seigo Nonakad4474cb2015-05-04 16:53:24 -0700193 static final int MSG_SHOW_IM_SUBTYPE_PICKER = 1;
194 static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 2;
195 static final int MSG_SHOW_IM_CONFIG = 3;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800196
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197 static final int MSG_UNBIND_INPUT = 1000;
198 static final int MSG_BIND_INPUT = 1010;
199 static final int MSG_SHOW_SOFT_INPUT = 1020;
200 static final int MSG_HIDE_SOFT_INPUT = 1030;
Jorim Jaggi3c5d0f12016-05-24 19:04:30 -0700201 static final int MSG_HIDE_CURRENT_INPUT_METHOD = 1035;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 static final int MSG_ATTACH_TOKEN = 1040;
203 static final int MSG_CREATE_SESSION = 1050;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800204
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 static final int MSG_START_INPUT = 2000;
Tarandeep Singh89a6c482017-11-21 14:26:11 -0800206 static final int MSG_START_VR_INPUT = 2010;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800207
Yohei Yukawa33e81792015-11-17 21:14:42 -0800208 static final int MSG_UNBIND_CLIENT = 3000;
209 static final int MSG_BIND_CLIENT = 3010;
Dianne Hackborna6e41342012-05-22 16:30:34 -0700210 static final int MSG_SET_ACTIVE = 3020;
Yohei Yukawafa6e0a82015-07-23 15:08:59 -0700211 static final int MSG_SET_INTERACTIVE = 3030;
Yohei Yukawa3d1e8122014-06-06 19:12:47 +0900212 static final int MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 3040;
Yohei Yukawa2bc66172017-02-08 11:13:25 -0800213 static final int MSG_REPORT_FULLSCREEN_MODE = 3045;
Yohei Yukawaae61f712015-12-09 13:00:10 -0800214 static final int MSG_SWITCH_IME = 3050;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800215
satok01038492012-04-09 21:08:27 +0900216 static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
217
Fyodor Kupolov7877b8a2016-06-29 14:39:19 -0700218 static final int MSG_SYSTEM_UNLOCK_USER = 5000;
219
Satoshi Kataokabcacc322013-10-21 17:57:27 -0700220 static final long TIME_TO_RECONNECT = 3 * 1000;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800221
satokf9f01002011-05-19 21:31:50 +0900222 static final int SECURE_SUGGESTION_SPANS_MAX_SIZE = 20;
223
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +0900224 private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
satokb6359412011-06-28 17:47:41 +0900225 private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +0900226
Yohei Yukawaa67a4592017-03-30 15:57:02 -0700227 /**
228 * Binding flags for establishing connection to the {@link InputMethodService}.
229 */
230 private static final int IME_CONNECTION_BIND_FLAGS =
231 Context.BIND_AUTO_CREATE
232 | Context.BIND_NOT_VISIBLE
233 | Context.BIND_NOT_FOREGROUND
Yohei Yukawaad78a612017-08-04 01:57:27 -0700234 | Context.BIND_IMPORTANT_BACKGROUND;
Yohei Yukawaa67a4592017-03-30 15:57:02 -0700235
236 /**
237 * Binding flags used only while the {@link InputMethodService} is showing window.
238 */
239 private static final int IME_VISIBLE_BIND_FLAGS =
240 Context.BIND_AUTO_CREATE
241 | Context.BIND_TREAT_LIKE_ACTIVITY
Yohei Yukawaad78a612017-08-04 01:57:27 -0700242 | Context.BIND_FOREGROUND_SERVICE
243 | Context.BIND_SHOWING_UI;
Yohei Yukawaa67a4592017-03-30 15:57:02 -0700244
Yohei Yukawafa0e47e2016-04-05 09:55:56 -0700245 @Retention(SOURCE)
246 @IntDef({HardKeyboardBehavior.WIRELESS_AFFORDANCE, HardKeyboardBehavior.WIRED_AFFORDANCE})
247 private @interface HardKeyboardBehavior {
248 int WIRELESS_AFFORDANCE = 0;
249 int WIRED_AFFORDANCE = 1;
250 }
satok4e4569d2010-11-19 18:45:53 +0900251
Tadashi G. Takaoka01065a52017-07-19 14:10:24 +0900252 /**
253 * A protected broadcast intent action for internal use for {@link PendingIntent} in
254 * the notification.
255 */
256 private static final String ACTION_SHOW_INPUT_METHOD_PICKER =
257 "com.android.server.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER";
258
Tarandeep Singh75a92392018-01-12 14:58:59 -0800259 /**
260 * Debug flag for overriding runtime {@link SystemProperties}.
261 */
262 @AnyThread
263 private static final class DebugFlag {
264 private static final Object LOCK = new Object();
265 private final String mKey;
266 @GuardedBy("LOCK")
267 private boolean mValue;
268
269 public DebugFlag(String key) {
270 mKey = key;
271 refresh();
272 }
273
274 void refresh() {
275 synchronized (LOCK) {
276 mValue = SystemProperties.getBoolean(mKey, true);
277 }
278 }
279
280 boolean value() {
281 synchronized (LOCK) {
282 return mValue;
283 }
284 }
285 }
286
287 /**
288 * Debug flags that can be overridden using "adb shell setprop <key>"
289 * Note: These flags are cached. To refresh, run "adb shell ime refresh_debug_properties".
290 */
291 private static final class DebugFlags {
292 static final DebugFlag FLAG_OPTIMIZE_START_INPUT =
293 new DebugFlag("debug.optimize_startinput");
294 }
295
296
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 final Context mContext;
Dianne Hackborn7d3a5bc2010-11-29 22:52:12 -0800298 final Resources mRes;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800299 final Handler mHandler;
satokd87c2592010-09-29 11:52:06 +0900300 final InputMethodSettings mSettings;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 final SettingsObserver mSettingsObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 final IWindowManager mIWindowManager;
Seigo Nonaka7309b122015-08-17 18:34:13 -0700303 final WindowManagerInternal mWindowManagerInternal;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 final HandlerCaller mCaller;
Dianne Hackborn119bbc32013-03-22 17:27:25 -0700305 final boolean mHasFeature;
Satoshi Kataoka5ade83b2012-09-26 22:59:41 +0900306 private InputMethodFileManager mFileManager;
satok01038492012-04-09 21:08:27 +0900307 private final HardKeyboardListener mHardKeyboardListener;
Yohei Yukawae63b5fa2014-09-19 13:14:55 +0900308 private final AppOpsManager mAppOpsManager;
Yohei Yukawaed4952a2016-02-17 07:57:25 -0800309 private final UserManager mUserManager;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800310
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 // All known input methods. mMethodMap also serves as the global
312 // lock for this class.
Yohei Yukawab0377bb2015-08-10 21:06:30 -0700313 final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
314 final HashMap<String, InputMethodInfo> mMethodMap = new HashMap<>();
satokf9f01002011-05-19 21:31:50 +0900315 private final LruCache<SuggestionSpan, InputMethodInfo> mSecureSuggestionSpans =
Yohei Yukawab0377bb2015-08-10 21:06:30 -0700316 new LruCache<>(SECURE_SUGGESTION_SPANS_MAX_SIZE);
Satoshi Kataokad787f692013-10-26 04:44:21 +0900317 private final InputMethodSubtypeSwitchingController mSwitchingController;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800318
Yohei Yukawae0733062017-02-09 22:49:35 -0800319 /**
320 * Tracks how many times {@link #mMethodMap} was updated.
321 */
322 @GuardedBy("mMethodMap")
323 private int mMethodMapUpdateCount = 0;
324
Dianne Hackborn2c84cfc2011-10-31 15:39:59 -0700325 // Used to bring IME service up to visible adjustment while it is being shown.
326 final ServiceConnection mVisibleConnection = new ServiceConnection() {
327 @Override public void onServiceConnected(ComponentName name, IBinder service) {
328 }
329
330 @Override public void onServiceDisconnected(ComponentName name) {
331 }
332 };
333 boolean mVisibleBound = false;
334
satok7cfc0ed2011-06-20 21:29:36 +0900335 // Ongoing notification
Dianne Hackborn661cd522011-08-22 00:26:20 -0700336 private NotificationManager mNotificationManager;
337 private KeyguardManager mKeyguardManager;
Griff Hazen6090c262016-03-25 08:11:24 -0700338 private @Nullable StatusBarManagerService mStatusBar;
Chris Wren1ce4b6d2015-06-11 10:19:43 -0400339 private Notification.Builder mImeSwitcherNotification;
Dianne Hackborn661cd522011-08-22 00:26:20 -0700340 private PendingIntent mImeSwitchPendingIntent;
satokb858c732011-07-22 19:54:34 +0900341 private boolean mShowOngoingImeSwitcherForPhones;
satok7cfc0ed2011-06-20 21:29:36 +0900342 private boolean mNotificationShown;
343
Tadashi G. Takaoka8c6d4772014-08-05 15:29:17 +0900344 static class SessionState {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345 final ClientState client;
346 final IInputMethod method;
Jeff Brownc28867a2013-03-26 15:42:39 -0700347
348 IInputMethodSession session;
349 InputChannel channel;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800350
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 @Override
352 public String toString() {
353 return "SessionState{uid " + client.uid + " pid " + client.pid
354 + " method " + Integer.toHexString(
355 System.identityHashCode(method))
356 + " session " + Integer.toHexString(
357 System.identityHashCode(session))
Jeff Brownc28867a2013-03-26 15:42:39 -0700358 + " channel " + channel
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 + "}";
360 }
361
362 SessionState(ClientState _client, IInputMethod _method,
Jeff Brownc28867a2013-03-26 15:42:39 -0700363 IInputMethodSession _session, InputChannel _channel) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 client = _client;
365 method = _method;
366 session = _session;
Jeff Brownc28867a2013-03-26 15:42:39 -0700367 channel = _channel;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368 }
369 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800370
Tarandeep Singh89a6c482017-11-21 14:26:11 -0800371 /**
372 * VR state callback.
373 * Listens for when VR mode finishes.
374 */
375 private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
376 @Override
377 public void onVrStateChanged(boolean enabled) {
378 if (!enabled) {
379 restoreNonVrImeFromSettingsNoCheck();
380 }
381 }
382 };
383
384 private void restoreNonVrImeFromSettingsNoCheck() {
385 // switch back to non-VR InputMethod from settings.
386 synchronized (mMethodMap) {
387 final String lastInputId = mSettings.getSelectedInputMethod();
388 setInputMethodLocked(lastInputId,
389 mSettings.getSelectedInputMethodSubtypeId(lastInputId));
390 }
391 }
392
Jeff Brownc28867a2013-03-26 15:42:39 -0700393 static final class ClientState {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 final IInputMethodClient client;
395 final IInputContext inputContext;
396 final int uid;
397 final int pid;
398 final InputBinding binding;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800399
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 boolean sessionRequested;
401 SessionState curSession;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800402
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800403 @Override
404 public String toString() {
405 return "ClientState{" + Integer.toHexString(
406 System.identityHashCode(this)) + " uid " + uid
407 + " pid " + pid + "}";
408 }
409
410 ClientState(IInputMethodClient _client, IInputContext _inputContext,
411 int _uid, int _pid) {
412 client = _client;
413 inputContext = _inputContext;
414 uid = _uid;
415 pid = _pid;
416 binding = new InputBinding(null, inputContext.asBinder(), uid, pid);
417 }
418 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800419
Yohei Yukawab0377bb2015-08-10 21:06:30 -0700420 final HashMap<IBinder, ClientState> mClients = new HashMap<>();
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800421
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 /**
Dianne Hackborna34f1ad2009-09-02 13:26:28 -0700423 * Set once the system is ready to run third party code.
424 */
425 boolean mSystemReady;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800426
Dianne Hackborna34f1ad2009-09-02 13:26:28 -0700427 /**
Yohei Yukawae13a20fa2015-09-30 19:11:32 -0700428 * Id obtained with {@link InputMethodInfo#getId()} for the currently selected input method.
429 * method. This is to be synchronized with the secure settings keyed with
430 * {@link Settings.Secure#DEFAULT_INPUT_METHOD}.
431 *
432 * <p>This can be transiently {@code null} when the system is re-initializing input method
433 * settings, e.g., the system locale is just changed.</p>
434 *
435 * <p>Note that {@link #mCurId} is used to track which IME is being connected to
436 * {@link InputMethodManagerService}.</p>
437 *
438 * @see #mCurId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800439 */
Yohei Yukawae13a20fa2015-09-30 19:11:32 -0700440 @Nullable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800441 String mCurMethodId;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800442
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800443 /**
444 * The current binding sequence number, incremented every time there is
445 * a new bind performed.
446 */
447 int mCurSeq;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800448
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800449 /**
450 * The client that is currently bound to an input method.
451 */
452 ClientState mCurClient;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800453
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454 /**
Yohei Yukawae39d4ed2015-11-19 03:38:49 -0800455 * The last window token that we confirmed to be focused. This is always updated upon reports
456 * from the input method client. If the window state is already changed before the report is
457 * handled, this field just keeps the last value.
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700458 */
459 IBinder mCurFocusedWindow;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800460
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700461 /**
Yohei Yukawa22a89232017-02-12 16:38:59 -0800462 * {@link WindowManager.LayoutParams#softInputMode} of {@link #mCurFocusedWindow}.
463 *
464 * @see #mCurFocusedWindow
465 */
466 int mCurFocusedWindowSoftInputMode;
467
468 /**
Tarandeep Singheb570612018-01-29 16:20:32 -0800469 * The client by which {@link #mCurFocusedWindow} was reported.
Yohei Yukawae39d4ed2015-11-19 03:38:49 -0800470 */
471 ClientState mCurFocusedWindowClient;
472
473 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 * The input context last provided by the current client.
475 */
476 IInputContext mCurInputContext;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800477
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 /**
Yohei Yukawa19a80a12016-03-14 22:57:37 -0700479 * The missing method flags for the input context last provided by the current client.
480 *
481 * @see android.view.inputmethod.InputConnectionInspector.MissingMethodFlags
482 */
483 int mCurInputContextMissingMethods;
484
485 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486 * The attributes last provided by the current client.
487 */
488 EditorInfo mCurAttribute;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800489
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 /**
Yohei Yukawae13a20fa2015-09-30 19:11:32 -0700491 * Id obtained with {@link InputMethodInfo#getId()} for the input method that we are currently
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 * connected to or in the process of connecting to.
Yohei Yukawae13a20fa2015-09-30 19:11:32 -0700493 *
494 * <p>This can be {@code null} when no input method is connected.</p>
495 *
496 * @see #mCurMethodId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497 */
Yohei Yukawae13a20fa2015-09-30 19:11:32 -0700498 @Nullable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800499 String mCurId;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800500
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501 /**
satokab751aa2010-09-14 19:17:36 +0900502 * The current subtype of the current input method.
503 */
504 private InputMethodSubtype mCurrentSubtype;
505
satok4e4569d2010-11-19 18:45:53 +0900506 // This list contains the pairs of InputMethodInfo and InputMethodSubtype.
satokf3db1af2010-11-23 13:34:33 +0900507 private final HashMap<InputMethodInfo, ArrayList<InputMethodSubtype>>
Yohei Yukawab0377bb2015-08-10 21:06:30 -0700508 mShortcutInputMethodsAndSubtypes = new HashMap<>();
satokab751aa2010-09-14 19:17:36 +0900509
John Spurlocke0980502013-10-25 11:59:29 -0400510 // Was the keyguard locked when this client became current?
511 private boolean mCurClientInKeyguard;
512
satokab751aa2010-09-14 19:17:36 +0900513 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 * Set to true if our ServiceConnection is currently actively bound to
515 * a service (whether or not we have gotten its IBinder back yet).
516 */
517 boolean mHaveConnection;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800518
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800519 /**
520 * Set if the client has asked for the input method to be shown.
521 */
522 boolean mShowRequested;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800523
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 /**
525 * Set if we were explicitly told to show the input method.
526 */
527 boolean mShowExplicitlyRequested;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800528
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 /**
530 * Set if we were forced to be shown.
531 */
532 boolean mShowForced;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800533
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800534 /**
535 * Set if we last told the input method to show itself.
536 */
537 boolean mInputShown;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800538
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 /**
Yohei Yukawa2bc66172017-02-08 11:13:25 -0800540 * {@code true} if the current input method is in fullscreen mode.
541 */
542 boolean mInFullscreenMode;
543
544 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800545 * The Intent used to connect to the current input method.
546 */
547 Intent mCurIntent;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800548
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800549 /**
550 * The token we have made for the currently active input method, to
551 * identify it in the future.
552 */
553 IBinder mCurToken;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800554
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 /**
556 * If non-null, this is the input method service we are currently connected
557 * to.
558 */
559 IInputMethod mCurMethod;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800560
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800561 /**
562 * Time that we last initiated a bind to the input method, to determine
563 * if we should try to disconnect and reconnect to it.
564 */
565 long mLastBindTime;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800566
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800567 /**
568 * Have we called mCurMethod.bindInput()?
569 */
570 boolean mBoundToMethod;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800571
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800572 /**
573 * Currently enabled session. Only touched by service thread, not
574 * protected by a lock.
575 */
576 SessionState mEnabledSession;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800577
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 /**
Yohei Yukawafa6e0a82015-07-23 15:08:59 -0700579 * True if the device is currently interactive with user. The value is true initially.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800580 */
Yohei Yukawafa6e0a82015-07-23 15:08:59 -0700581 boolean mIsInteractive = true;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800582
Yohei Yukawa3d1e8122014-06-06 19:12:47 +0900583 int mCurUserActionNotificationSequenceNumber = 0;
584
Joe Onorato857fd9b2011-01-27 15:08:35 -0800585 int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
Seigo Nonakad9eb9112015-05-26 20:54:43 +0900586
587 /**
588 * A set of status bits regarding the active IME.
589 *
590 * <p>This value is a combination of following two bits:</p>
591 * <dl>
592 * <dt>{@link InputMethodService#IME_ACTIVE}</dt>
593 * <dd>
594 * If this bit is ON, connected IME is ready to accept touch/key events.
595 * </dd>
596 * <dt>{@link InputMethodService#IME_VISIBLE}</dt>
597 * <dd>
598 * If this bit is ON, some of IME view, e.g. software input, candidate view, is visible.
599 * </dd>
600 * </dl>
601 * <em>Do not update this value outside of setImeWindowStatus.</em>
602 */
Joe Onorato857fd9b2011-01-27 15:08:35 -0800603 int mImeWindowVis;
604
Ken Wakasa05dbb652011-08-22 15:22:43 +0900605 private AlertDialog.Builder mDialogBuilder;
606 private AlertDialog mSwitchingDialog;
Wale Ogunwale3a931692016-11-02 16:49:48 -0700607 private IBinder mSwitchingDialogToken = new Binder();
satok01038492012-04-09 21:08:27 +0900608 private View mSwitchingDialogTitleView;
Yohei Yukawaebda7d72016-04-02 17:39:23 -0700609 private Toast mSubtypeSwitchedByShortCutToast;
Ken Wakasa05dbb652011-08-22 15:22:43 +0900610 private InputMethodInfo[] mIms;
611 private int[] mSubtypeIds;
Yohei Yukawae985c242016-02-24 18:27:04 -0800612 private LocaleList mLastSystemLocales;
Michael Wright7b5a96b2014-08-09 19:28:42 -0700613 private boolean mShowImeWithHardKeyboard;
Anna Galusza9b278112016-01-04 11:37:37 -0800614 private boolean mAccessibilityRequestingNoSoftKeyboard;
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +0900615 private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor();
616 private final IPackageManager mIPackageManager;
Jason Monk3e189872016-01-12 09:10:34 -0500617 private final String mSlotIme;
Yohei Yukawafa0e47e2016-04-05 09:55:56 -0700618 @HardKeyboardBehavior
619 private final int mHardKeyboardBehavior;
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800620
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800621 /**
622 * Internal state snapshot when {@link #MSG_START_INPUT} message is about to be posted to the
623 * internal message queue. Any subsequent state change inside {@link InputMethodManagerService}
624 * will not affect those tasks that are already posted.
625 *
626 * <p>Posting {@link #MSG_START_INPUT} message basically means that
627 * {@link InputMethodService#doStartInput(InputConnection, EditorInfo, boolean)} will be called
628 * back in the current IME process shortly, which will also affect what the current IME starts
629 * receiving from {@link InputMethodService#getCurrentInputConnection()}. In other words, this
630 * snapshot will be taken every time when {@link InputMethodManagerService} is initiating a new
631 * logical input session between the client application and the current IME.</p>
632 *
633 * <p>Be careful to not keep strong references to this object forever, which can prevent
634 * {@link StartInputInfo#mImeToken} and {@link StartInputInfo#mTargetWindow} from being GC-ed.
635 * </p>
636 */
637 private static class StartInputInfo {
Yohei Yukawa357b2f62017-02-14 09:40:03 -0800638 private static final AtomicInteger sSequenceNumber = new AtomicInteger(0);
639
640 final int mSequenceNumber;
641 final long mTimestamp;
642 final long mWallTime;
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800643 @NonNull
644 final IBinder mImeToken;
Yohei Yukawa357b2f62017-02-14 09:40:03 -0800645 @NonNull
646 final String mImeId;
647 // @InputMethodClient.StartInputReason
648 final int mStartInputReason;
649 final boolean mRestarting;
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800650 @Nullable
651 final IBinder mTargetWindow;
Yohei Yukawa357b2f62017-02-14 09:40:03 -0800652 @NonNull
653 final EditorInfo mEditorInfo;
654 final int mTargetWindowSoftInputMode;
655 final int mClientBindSequenceNumber;
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800656
Yohei Yukawa357b2f62017-02-14 09:40:03 -0800657 StartInputInfo(@NonNull IBinder imeToken, @NonNull String imeId,
658 /* @InputMethodClient.StartInputReason */ int startInputReason, boolean restarting,
659 @Nullable IBinder targetWindow, @NonNull EditorInfo editorInfo,
660 int targetWindowSoftInputMode, int clientBindSequenceNumber) {
661 mSequenceNumber = sSequenceNumber.getAndIncrement();
662 mTimestamp = SystemClock.uptimeMillis();
663 mWallTime = System.currentTimeMillis();
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800664 mImeToken = imeToken;
Yohei Yukawa357b2f62017-02-14 09:40:03 -0800665 mImeId = imeId;
666 mStartInputReason = startInputReason;
667 mRestarting = restarting;
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800668 mTargetWindow = targetWindow;
Yohei Yukawa357b2f62017-02-14 09:40:03 -0800669 mEditorInfo = editorInfo;
670 mTargetWindowSoftInputMode = targetWindowSoftInputMode;
671 mClientBindSequenceNumber = clientBindSequenceNumber;
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800672 }
673 }
674
Yohei Yukawab37d8bd2017-02-13 18:29:05 -0800675 @GuardedBy("mMethodMap")
676 private final WeakHashMap<IBinder, StartInputInfo> mStartInputMap = new WeakHashMap<>();
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -0800677
Yohei Yukawa357b2f62017-02-14 09:40:03 -0800678 /**
679 * A ring buffer to store the history of {@link StartInputInfo}.
680 */
681 private static final class StartInputHistory {
682 /**
683 * Entry size for non low-RAM devices.
684 *
685 * <p>TODO: Consider to follow what other system services have been doing to manage
686 * constants (e.g. {@link android.provider.Settings.Global#ACTIVITY_MANAGER_CONSTANTS}).</p>
687 */
688 private final static int ENTRY_SIZE_FOR_HIGH_RAM_DEVICE = 16;
689
690 /**
691 * Entry size for non low-RAM devices.
692 *
693 * <p>TODO: Consider to follow what other system services have been doing to manage
694 * constants (e.g. {@link android.provider.Settings.Global#ACTIVITY_MANAGER_CONSTANTS}).</p>
695 */
696 private final static int ENTRY_SIZE_FOR_LOW_RAM_DEVICE = 5;
697
698 private static int getEntrySize() {
699 if (ActivityManager.isLowRamDeviceStatic()) {
700 return ENTRY_SIZE_FOR_LOW_RAM_DEVICE;
701 } else {
702 return ENTRY_SIZE_FOR_HIGH_RAM_DEVICE;
703 }
704 }
705
706 /**
707 * Backing store for the ring bugger.
708 */
709 private final Entry[] mEntries = new Entry[getEntrySize()];
710
711 /**
712 * An index of {@link #mEntries}, to which next {@link #addEntry(StartInputInfo)} should
713 * write.
714 */
715 private int mNextIndex = 0;
716
717 /**
718 * Recyclable entry to store the information in {@link StartInputInfo}.
719 */
720 private static final class Entry {
721 int mSequenceNumber;
722 long mTimestamp;
723 long mWallTime;
724 @NonNull
725 String mImeTokenString;
726 @NonNull
727 String mImeId;
728 /* @InputMethodClient.StartInputReason */
729 int mStartInputReason;
730 boolean mRestarting;
731 @NonNull
732 String mTargetWindowString;
733 @NonNull
734 EditorInfo mEditorInfo;
735 int mTargetWindowSoftInputMode;
736 int mClientBindSequenceNumber;
737
738 Entry(@NonNull StartInputInfo original) {
739 set(original);
740 }
741
742 void set(@NonNull StartInputInfo original) {
743 mSequenceNumber = original.mSequenceNumber;
744 mTimestamp = original.mTimestamp;
745 mWallTime = original.mWallTime;
746 // Intentionally convert to String so as not to keep a strong reference to a Binder
747 // object.
748 mImeTokenString = String.valueOf(original.mImeToken);
749 mImeId = original.mImeId;
750 mStartInputReason = original.mStartInputReason;
751 mRestarting = original.mRestarting;
752 // Intentionally convert to String so as not to keep a strong reference to a Binder
753 // object.
754 mTargetWindowString = String.valueOf(original.mTargetWindow);
755 mEditorInfo = original.mEditorInfo;
756 mTargetWindowSoftInputMode = original.mTargetWindowSoftInputMode;
757 mClientBindSequenceNumber = original.mClientBindSequenceNumber;
758 }
759 }
760
761 /**
762 * Add a new entry and discard the oldest entry as needed.
763 * @param info {@lin StartInputInfo} to be added.
764 */
765 void addEntry(@NonNull StartInputInfo info) {
766 final int index = mNextIndex;
767 if (mEntries[index] == null) {
768 mEntries[index] = new Entry(info);
769 } else {
770 mEntries[index].set(info);
771 }
772 mNextIndex = (mNextIndex + 1) % mEntries.length;
773 }
774
775 void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
776 final SimpleDateFormat dataFormat =
777 new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
778
779 for (int i = 0; i < mEntries.length; ++i) {
780 final Entry entry = mEntries[(i + mNextIndex) % mEntries.length];
781 if (entry == null) {
782 continue;
783 }
784 pw.print(prefix);
785 pw.println("StartInput #" + entry.mSequenceNumber + ":");
786
787 pw.print(prefix);
788 pw.println(" time=" + dataFormat.format(new Date(entry.mWallTime))
789 + " (timestamp=" + entry.mTimestamp + ")"
790 + " reason="
791 + InputMethodClient.getStartInputReason(entry.mStartInputReason)
792 + " restarting=" + entry.mRestarting);
793
794 pw.print(prefix);
795 pw.println(" imeToken=" + entry.mImeTokenString + " [" + entry.mImeId + "]");
796
797 pw.print(prefix);
798 pw.println(" targetWin=" + entry.mTargetWindowString
799 + " [" + entry.mEditorInfo.packageName + "]"
800 + " clientBindSeq=" + entry.mClientBindSequenceNumber);
801
802 pw.print(prefix);
803 pw.println(" softInputMode=" + InputMethodClient.softInputModeToString(
804 entry.mTargetWindowSoftInputMode));
805
806 pw.print(prefix);
807 pw.println(" inputType=0x" + Integer.toHexString(entry.mEditorInfo.inputType)
808 + " imeOptions=0x" + Integer.toHexString(entry.mEditorInfo.imeOptions)
809 + " fieldId=0x" + Integer.toHexString(entry.mEditorInfo.fieldId)
810 + " fieldName=" + entry.mEditorInfo.fieldName
811 + " actionId=" + entry.mEditorInfo.actionId
812 + " actionLabel=" + entry.mEditorInfo.actionLabel);
813 }
814 }
815 }
816
817 @GuardedBy("mMethodMap")
818 @NonNull
819 private final StartInputHistory mStartInputHistory = new StartInputHistory();
820
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800821 class SettingsObserver extends ContentObserver {
Yohei Yukawa81482972015-06-04 00:58:59 -0700822 int mUserId;
823 boolean mRegistered = false;
Yohei Yukawa7b574cb2016-03-16 17:22:22 -0700824 @NonNull
Dianne Hackbornfd7aded2013-01-22 17:10:23 -0800825 String mLastEnabled = "";
826
Yohei Yukawa81482972015-06-04 00:58:59 -0700827 /**
828 * <em>This constructor must be called within the lock.</em>
829 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800830 SettingsObserver(Handler handler) {
831 super(handler);
Yohei Yukawa81482972015-06-04 00:58:59 -0700832 }
833
Yohei Yukawa7b18aec2016-03-07 13:04:32 -0800834 public void registerContentObserverLocked(@UserIdInt int userId) {
Yohei Yukawa81482972015-06-04 00:58:59 -0700835 if (mRegistered && mUserId == userId) {
836 return;
837 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838 ContentResolver resolver = mContext.getContentResolver();
Yohei Yukawa81482972015-06-04 00:58:59 -0700839 if (mRegistered) {
840 mContext.getContentResolver().unregisterContentObserver(this);
841 mRegistered = false;
842 }
843 if (mUserId != userId) {
844 mLastEnabled = "";
845 mUserId = userId;
846 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800847 resolver.registerContentObserver(Settings.Secure.getUriFor(
Yohei Yukawa81482972015-06-04 00:58:59 -0700848 Settings.Secure.DEFAULT_INPUT_METHOD), false, this, userId);
satokab751aa2010-09-14 19:17:36 +0900849 resolver.registerContentObserver(Settings.Secure.getUriFor(
Yohei Yukawa81482972015-06-04 00:58:59 -0700850 Settings.Secure.ENABLED_INPUT_METHODS), false, this, userId);
satokb6109bb2011-02-03 22:24:54 +0900851 resolver.registerContentObserver(Settings.Secure.getUriFor(
Yohei Yukawa81482972015-06-04 00:58:59 -0700852 Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this, userId);
Michael Wright7b5a96b2014-08-09 19:28:42 -0700853 resolver.registerContentObserver(Settings.Secure.getUriFor(
Yohei Yukawa81482972015-06-04 00:58:59 -0700854 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD), false, this, userId);
Anna Galusza9b278112016-01-04 11:37:37 -0800855 resolver.registerContentObserver(Settings.Secure.getUriFor(
856 Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE), false, this, userId);
Yohei Yukawa81482972015-06-04 00:58:59 -0700857 mRegistered = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800858 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800859
Michael Wright7b5a96b2014-08-09 19:28:42 -0700860 @Override public void onChange(boolean selfChange, Uri uri) {
Anna Galusza9b278112016-01-04 11:37:37 -0800861 final Uri showImeUri = Settings.Secure.getUriFor(
862 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
863 final Uri accessibilityRequestingNoImeUri = Settings.Secure.getUriFor(
864 Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800865 synchronized (mMethodMap) {
Michael Wright7b5a96b2014-08-09 19:28:42 -0700866 if (showImeUri.equals(uri)) {
867 updateKeyboardFromSettingsLocked();
Anna Galusza9b278112016-01-04 11:37:37 -0800868 } else if (accessibilityRequestingNoImeUri.equals(uri)) {
869 mAccessibilityRequestingNoSoftKeyboard = Settings.Secure.getIntForUser(
870 mContext.getContentResolver(),
871 Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
872 0, mUserId) == 1;
873 if (mAccessibilityRequestingNoSoftKeyboard) {
874 final boolean showRequested = mShowRequested;
875 hideCurrentInputLocked(0, null);
876 mShowRequested = showRequested;
877 } else if (mShowRequested) {
878 showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
879 }
Michael Wright7b5a96b2014-08-09 19:28:42 -0700880 } else {
881 boolean enabledChanged = false;
882 String newEnabled = mSettings.getEnabledInputMethodsStr();
883 if (!mLastEnabled.equals(newEnabled)) {
884 mLastEnabled = newEnabled;
885 enabledChanged = true;
886 }
887 updateInputMethodsFromSettingsLocked(enabledChanged);
Dianne Hackbornfd7aded2013-01-22 17:10:23 -0800888 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800889 }
890 }
Yohei Yukawa81482972015-06-04 00:58:59 -0700891
892 @Override
893 public String toString() {
894 return "SettingsObserver{mUserId=" + mUserId + " mRegistered=" + mRegistered
895 + " mLastEnabled=" + mLastEnabled + "}";
896 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800897 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800898
Yohei Yukawa79247822017-01-23 15:26:15 -0800899 class ImmsBroadcastReceiver extends BroadcastReceiver {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +0900900 @Override
901 public void onReceive(Context context, Intent intent) {
902 final String action = intent.getAction();
Yohei Yukawafa6e0a82015-07-23 15:08:59 -0700903 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +0900904 hideInputMethodMenu();
Yohei Yukawafa6e0a82015-07-23 15:08:59 -0700905 // No need to update mIsInteractive
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +0900906 return;
Amith Yamasani734983f2014-03-04 16:48:05 -0800907 } else if (Intent.ACTION_USER_ADDED.equals(action)
908 || Intent.ACTION_USER_REMOVED.equals(action)) {
Kenny Guy2a764942014-04-02 13:29:20 +0100909 updateCurrentProfileIds();
Amith Yamasani734983f2014-03-04 16:48:05 -0800910 return;
Yohei Yukawa79247822017-01-23 15:26:15 -0800911 } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
Yohei Yukawa0d7aff82017-02-10 00:40:51 -0800912 onActionLocaleChanged();
Tadashi G. Takaoka01065a52017-07-19 14:10:24 +0900913 } else if (ACTION_SHOW_INPUT_METHOD_PICKER.equals(action)) {
914 // ACTION_SHOW_INPUT_METHOD_PICKER action is a protected-broadcast and it is
915 // guaranteed to be send only from the system, so that there is no need for extra
916 // security check such as
917 // {@link #canShowInputMethodPickerLocked(IInputMethodClient)}.
918 mHandler.obtainMessage(
919 MSG_SHOW_IM_SUBTYPE_PICKER,
920 InputMethodManager.SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES,
921 0 /* arg2 */)
922 .sendToTarget();
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +0900923 } else {
924 Slog.w(TAG, "Unexpected intent " + intent);
925 }
926 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800927 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -0800928
Yohei Yukawa0d7aff82017-02-10 00:40:51 -0800929 /**
Tarandeep Singh89a6c482017-11-21 14:26:11 -0800930 * Start a VR InputMethod that matches IME with package name of {@param component}.
Yohei Yukawa7a46c282017-12-10 18:15:19 -0800931 * Note: This method is called from {@link android.app.VrManager}.
Tarandeep Singh89a6c482017-11-21 14:26:11 -0800932 */
933 private void startVrInputMethodNoCheck(@Nullable ComponentName component) {
934 if (component == null) {
935 // clear the current VR-only IME (if any) and restore normal IME.
936 restoreNonVrImeFromSettingsNoCheck();
937 return;
938 }
939
940 synchronized (mMethodMap) {
941 String packageName = component.getPackageName();
942 for (InputMethodInfo info : mMethodList) {
943 if (TextUtils.equals(info.getPackageName(), packageName) && info.isVrOnly()) {
944 // set this is as current inputMethod without updating settings.
Yohei Yukawa7a46c282017-12-10 18:15:19 -0800945 setInputMethodEnabledLocked(info.getId(), true);
Tarandeep Singh89a6c482017-11-21 14:26:11 -0800946 setInputMethodLocked(info.getId(), NOT_A_SUBTYPE_ID);
947 break;
948 }
949 }
950 }
951 }
952
953 /**
Yohei Yukawa0d7aff82017-02-10 00:40:51 -0800954 * Handles {@link Intent#ACTION_LOCALE_CHANGED}.
955 *
956 * <p>Note: For historical reasons, {@link Intent#ACTION_LOCALE_CHANGED} has been sent to all
957 * the users. We should ignore this event if this is about any background user's locale.</p>
958 *
959 * <p>Caution: This method must not be called when system is not ready.</p>
960 */
961 void onActionLocaleChanged() {
962 synchronized (mMethodMap) {
963 final LocaleList possibleNewLocale = mRes.getConfiguration().getLocales();
964 if (possibleNewLocale != null && possibleNewLocale.equals(mLastSystemLocales)) {
965 return;
966 }
967 buildInputMethodListLocked(true);
968 // If the locale is changed, needs to reset the default ime
969 resetDefaultImeLocked(mContext);
970 updateFromSettingsLocked(true);
971 mLastSystemLocales = possibleNewLocale;
972 }
973 }
974
Yohei Yukawac4e44912017-02-09 19:30:22 -0800975 final class MyPackageMonitor extends PackageMonitor {
976 /**
Yohei Yukawa5e3e8a52017-02-14 20:05:17 -0800977 * Package names that are known to contain {@link InputMethodService}.
Yohei Yukawac4e44912017-02-09 19:30:22 -0800978 *
979 * <p>No need to include packages because of direct-boot unaware IMEs since we always rescan
980 * all the packages when the user is unlocked, and direct-boot awareness will not be changed
981 * dynamically unless the entire package is updated, which also always triggers package
982 * rescanning.</p>
983 */
984 @GuardedBy("mMethodMap")
Yohei Yukawa5e3e8a52017-02-14 20:05:17 -0800985 final private ArraySet<String> mKnownImePackageNames = new ArraySet<>();
986
987 /**
988 * Packages that are appeared, disappeared, or modified for whatever reason.
989 *
990 * <p>Note: For now we intentionally use {@link ArrayList} instead of {@link ArraySet}
991 * because 1) the number of elements is almost always 1 or so, and 2) we do not care
992 * duplicate elements for our use case.</p>
993 *
994 * <p>This object must be accessed only from callback methods in {@link PackageMonitor},
995 * which should be bound to {@link #getRegisteredHandler()}.</p>
996 */
997 private final ArrayList<String> mChangedPackages = new ArrayList<>();
998
999 /**
1000 * {@code true} if one or more packages that contain {@link InputMethodService} appeared.
1001 *
1002 * <p>This field must be accessed only from callback methods in {@link PackageMonitor},
1003 * which should be bound to {@link #getRegisteredHandler()}.</p>
1004 */
1005 private boolean mImePackageAppeared = false;
Yohei Yukawac4e44912017-02-09 19:30:22 -08001006
1007 @GuardedBy("mMethodMap")
Yohei Yukawa5e3e8a52017-02-14 20:05:17 -08001008 void clearKnownImePackageNamesLocked() {
1009 mKnownImePackageNames.clear();
Yohei Yukawac4e44912017-02-09 19:30:22 -08001010 }
1011
1012 @GuardedBy("mMethodMap")
Yohei Yukawa5e3e8a52017-02-14 20:05:17 -08001013 final void addKnownImePackageNameLocked(@NonNull String packageName) {
1014 mKnownImePackageNames.add(packageName);
Yohei Yukawac4e44912017-02-09 19:30:22 -08001015 }
1016
Yohei Yukawa278f2ab2017-02-14 19:51:33 -08001017 @GuardedBy("mMethodMap")
1018 private boolean isChangingPackagesOfCurrentUserLocked() {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001019 final int userId = getChangingUserId();
1020 final boolean retval = userId == mSettings.getCurrentUserId();
1021 if (DEBUG) {
satok81f8b7c2012-12-04 20:42:56 +09001022 if (!retval) {
1023 Slog.d(TAG, "--- ignore this call back from a background user: " + userId);
1024 }
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001025 }
1026 return retval;
1027 }
1028
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001029 @Override
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001030 public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001031 synchronized (mMethodMap) {
Yohei Yukawa278f2ab2017-02-14 19:51:33 -08001032 if (!isChangingPackagesOfCurrentUserLocked()) {
1033 return false;
1034 }
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001035 String curInputMethodId = mSettings.getSelectedInputMethod();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001036 final int N = mMethodList.size();
1037 if (curInputMethodId != null) {
1038 for (int i=0; i<N; i++) {
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001039 InputMethodInfo imi = mMethodList.get(i);
1040 if (imi.getId().equals(curInputMethodId)) {
1041 for (String pkg : packages) {
1042 if (imi.getPackageName().equals(pkg)) {
1043 if (!doit) {
1044 return true;
1045 }
satok723a27e2010-11-11 14:58:11 +09001046 resetSelectedInputMethodAndSubtypeLocked("");
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001047 chooseNewDefaultIMELocked();
1048 return true;
1049 }
1050 }
1051 }
1052 }
1053 }
1054 }
1055 return false;
1056 }
1057
1058 @Override
Yohei Yukawa5e3e8a52017-02-14 20:05:17 -08001059 public void onBeginPackageChanges() {
1060 clearPackageChangeState();
1061 }
1062
1063 @Override
1064 public void onPackageAppeared(String packageName, int reason) {
1065 if (!mImePackageAppeared) {
1066 final PackageManager pm = mContext.getPackageManager();
1067 final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
1068 new Intent(InputMethod.SERVICE_INTERFACE).setPackage(packageName),
1069 PackageManager.MATCH_DISABLED_COMPONENTS, getChangingUserId());
1070 // No need to lock this because we access it only on getRegisteredHandler().
1071 if (!services.isEmpty()) {
1072 mImePackageAppeared = true;
1073 }
1074 }
1075 // No need to lock this because we access it only on getRegisteredHandler().
1076 mChangedPackages.add(packageName);
1077 }
1078
1079 @Override
1080 public void onPackageDisappeared(String packageName, int reason) {
1081 // No need to lock this because we access it only on getRegisteredHandler().
1082 mChangedPackages.add(packageName);
1083 }
1084
1085 @Override
1086 public void onPackageModified(String packageName) {
1087 // No need to lock this because we access it only on getRegisteredHandler().
1088 mChangedPackages.add(packageName);
1089 }
1090
1091 @Override
1092 public void onPackagesSuspended(String[] packages) {
1093 // No need to lock this because we access it only on getRegisteredHandler().
1094 for (String packageName : packages) {
1095 mChangedPackages.add(packageName);
Yohei Yukawac4e44912017-02-09 19:30:22 -08001096 }
1097 }
1098
1099 @Override
Yohei Yukawa5e3e8a52017-02-14 20:05:17 -08001100 public void onPackagesUnsuspended(String[] packages) {
1101 // No need to lock this because we access it only on getRegisteredHandler().
1102 for (String packageName : packages) {
1103 mChangedPackages.add(packageName);
1104 }
1105 }
1106
1107 @Override
1108 public void onFinishPackageChanges() {
1109 onFinishPackageChangesInternal();
1110 clearPackageChangeState();
1111 }
1112
1113 private void clearPackageChangeState() {
1114 // No need to lock them because we access these fields only on getRegisteredHandler().
1115 mChangedPackages.clear();
1116 mImePackageAppeared = false;
1117 }
1118
Andreas Gampea36dc622018-02-05 17:19:22 -08001119 @GuardedBy("mMethodMap")
Yohei Yukawa5e3e8a52017-02-14 20:05:17 -08001120 private boolean shouldRebuildInputMethodListLocked() {
1121 // This method is guaranteed to be called only by getRegisteredHandler().
1122
1123 // If there is any new package that contains at least one IME, then rebuilt the list
1124 // of IMEs.
1125 if (mImePackageAppeared) {
1126 return true;
1127 }
1128
1129 // Otherwise, check if mKnownImePackageNames and mChangedPackages have any intersection.
1130 // TODO: Consider to create a utility method to do the following test. List.retainAll()
1131 // is an option, but it may still do some extra operations that we do not need here.
1132 final int N = mChangedPackages.size();
1133 for (int i = 0; i < N; ++i) {
1134 final String packageName = mChangedPackages.get(i);
1135 if (mKnownImePackageNames.contains(packageName)) {
1136 return true;
1137 }
1138 }
1139 return false;
1140 }
1141
1142 private void onFinishPackageChangesInternal() {
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001143 synchronized (mMethodMap) {
Yohei Yukawa278f2ab2017-02-14 19:51:33 -08001144 if (!isChangingPackagesOfCurrentUserLocked()) {
1145 return;
1146 }
Yohei Yukawa5e3e8a52017-02-14 20:05:17 -08001147 if (!shouldRebuildInputMethodListLocked()) {
1148 return;
1149 }
1150
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001151 InputMethodInfo curIm = null;
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001152 String curInputMethodId = mSettings.getSelectedInputMethod();
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001153 final int N = mMethodList.size();
1154 if (curInputMethodId != null) {
1155 for (int i=0; i<N; i++) {
1156 InputMethodInfo imi = mMethodList.get(i);
satoke7c6998e2011-06-03 17:57:59 +09001157 final String imiId = imi.getId();
1158 if (imiId.equals(curInputMethodId)) {
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001159 curIm = imi;
1160 }
satoke7c6998e2011-06-03 17:57:59 +09001161
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001162 int change = isPackageDisappearing(imi.getPackageName());
satoke7c6998e2011-06-03 17:57:59 +09001163 if (isPackageModified(imi.getPackageName())) {
1164 mFileManager.deleteAllInputMethodSubtypes(imiId);
1165 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001166 if (change == PACKAGE_TEMPORARY_CHANGE
1167 || change == PACKAGE_PERMANENT_CHANGE) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001168 Slog.i(TAG, "Input method uninstalled, disabling: "
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001169 + imi.getComponent());
1170 setInputMethodEnabledLocked(imi.getId(), false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001171 }
1172 }
1173 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001174
Yohei Yukawa94e33302016-02-12 19:37:03 -08001175 buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001176
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 boolean changed = false;
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001178
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001179 if (curIm != null) {
Anna Galusza9b278112016-01-04 11:37:37 -08001180 int change = isPackageDisappearing(curIm.getPackageName());
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001181 if (change == PACKAGE_TEMPORARY_CHANGE
1182 || change == PACKAGE_PERMANENT_CHANGE) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001183 ServiceInfo si = null;
1184 try {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001185 si = mIPackageManager.getServiceInfo(
1186 curIm.getComponent(), 0, mSettings.getCurrentUserId());
1187 } catch (RemoteException ex) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001188 }
1189 if (si == null) {
1190 // Uh oh, current input method is no longer around!
1191 // Pick another one...
Joe Onorato8a9b2202010-02-26 18:56:32 -08001192 Slog.i(TAG, "Current input method removed: " + curInputMethodId);
Seigo Nonakad9eb9112015-05-26 20:54:43 +09001193 updateSystemUiLocked(mCurToken, 0 /* vis */, mBackDisposition);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001194 if (!chooseNewDefaultIMELocked()) {
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001195 changed = true;
1196 curIm = null;
Joe Onorato8a9b2202010-02-26 18:56:32 -08001197 Slog.i(TAG, "Unsetting current input method");
satok723a27e2010-11-11 14:58:11 +09001198 resetSelectedInputMethodAndSubtypeLocked("");
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001199 }
1200 }
Suchi Amalapurapu08675a32010-01-28 09:57:30 -08001201 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001202 }
satokab751aa2010-09-14 19:17:36 +09001203
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001204 if (curIm == null) {
1205 // We currently don't have a default input method... is
1206 // one now available?
1207 changed = chooseNewDefaultIMELocked();
Yohei Yukawa54d512c2015-05-19 22:15:02 -07001208 } else if (!changed && isPackageModified(curIm.getPackageName())) {
1209 // Even if the current input method is still available, mCurrentSubtype could
1210 // be obsolete when the package is modified in practice.
1211 changed = true;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001212 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001213
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08001214 if (changed) {
Dianne Hackbornfd7aded2013-01-22 17:10:23 -08001215 updateFromSettingsLocked(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001216 }
1217 }
1218 }
1219 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001220
Jeff Brownc28867a2013-03-26 15:42:39 -07001221 private static final class MethodCallback extends IInputSessionCallback.Stub {
Jean Chalarde0d32a62011-10-20 20:36:07 +09001222 private final InputMethodManagerService mParentIMMS;
Jeff Brownc28867a2013-03-26 15:42:39 -07001223 private final IInputMethod mMethod;
1224 private final InputChannel mChannel;
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001225
Jeff Brownc28867a2013-03-26 15:42:39 -07001226 MethodCallback(InputMethodManagerService imms, IInputMethod method,
1227 InputChannel channel) {
Jean Chalarde0d32a62011-10-20 20:36:07 +09001228 mParentIMMS = imms;
Jeff Brownc28867a2013-03-26 15:42:39 -07001229 mMethod = method;
1230 mChannel = channel;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001231 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001232
satoke7c6998e2011-06-03 17:57:59 +09001233 @Override
Jeff Brownc28867a2013-03-26 15:42:39 -07001234 public void sessionCreated(IInputMethodSession session) {
Dianne Hackborn6b6b3fd2014-03-24 11:27:18 -07001235 long ident = Binder.clearCallingIdentity();
1236 try {
1237 mParentIMMS.onSessionCreated(mMethod, session, mChannel);
1238 } finally {
1239 Binder.restoreCallingIdentity(ident);
1240 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001241 }
1242 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001243
satok01038492012-04-09 21:08:27 +09001244 private class HardKeyboardListener
Seigo Nonaka7309b122015-08-17 18:34:13 -07001245 implements WindowManagerInternal.OnHardKeyboardStatusChangeListener {
satok01038492012-04-09 21:08:27 +09001246 @Override
Michael Wright7b5a96b2014-08-09 19:28:42 -07001247 public void onHardKeyboardStatusChange(boolean available) {
1248 mHandler.sendMessage(mHandler.obtainMessage(MSG_HARD_KEYBOARD_SWITCH_CHANGED,
1249 available ? 1 : 0));
satok01038492012-04-09 21:08:27 +09001250 }
1251
Michael Wright7b5a96b2014-08-09 19:28:42 -07001252 public void handleHardKeyboardStatusChange(boolean available) {
satok01038492012-04-09 21:08:27 +09001253 if (DEBUG) {
Michael Wright7b5a96b2014-08-09 19:28:42 -07001254 Slog.w(TAG, "HardKeyboardStatusChanged: available=" + available);
satok01038492012-04-09 21:08:27 +09001255 }
1256 synchronized(mMethodMap) {
1257 if (mSwitchingDialog != null && mSwitchingDialogTitleView != null
1258 && mSwitchingDialog.isShowing()) {
1259 mSwitchingDialogTitleView.findViewById(
1260 com.android.internal.R.id.hard_keyboard_section).setVisibility(
1261 available ? View.VISIBLE : View.GONE);
1262 }
1263 }
1264 }
1265 }
1266
Fyodor Kupolov1e33dc82016-01-21 12:02:58 -08001267 public static final class Lifecycle extends SystemService {
1268 private InputMethodManagerService mService;
1269
1270 public Lifecycle(Context context) {
1271 super(context);
1272 mService = new InputMethodManagerService(context);
1273 }
1274
1275 @Override
1276 public void onStart() {
1277 LocalServices.addService(InputMethodManagerInternal.class,
1278 new LocalServiceImpl(mService.mHandler));
1279 publishBinderService(Context.INPUT_METHOD_SERVICE, mService);
1280 }
1281
1282 @Override
Yohei Yukawa7b18aec2016-03-07 13:04:32 -08001283 public void onSwitchUser(@UserIdInt int userHandle) {
Fyodor Kupolov7877b8a2016-06-29 14:39:19 -07001284 // Called on ActivityManager thread.
Yohei Yukawa7b18aec2016-03-07 13:04:32 -08001285 // TODO: Dispatch this to a worker thread as needed.
1286 mService.onSwitchUser(userHandle);
1287 }
1288
1289 @Override
Fyodor Kupolov1e33dc82016-01-21 12:02:58 -08001290 public void onBootPhase(int phase) {
Fyodor Kupolov7877b8a2016-06-29 14:39:19 -07001291 // Called on ActivityManager thread.
Yohei Yukawa7b18aec2016-03-07 13:04:32 -08001292 // TODO: Dispatch this to a worker thread as needed.
Fyodor Kupolov1e33dc82016-01-21 12:02:58 -08001293 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
1294 StatusBarManagerService statusBarService = (StatusBarManagerService) ServiceManager
1295 .getService(Context.STATUS_BAR_SERVICE);
1296 mService.systemRunning(statusBarService);
1297 }
1298 }
Yohei Yukawaed4952a2016-02-17 07:57:25 -08001299
1300 @Override
Fyodor Kupolov7877b8a2016-06-29 14:39:19 -07001301 public void onUnlockUser(final @UserIdInt int userHandle) {
1302 // Called on ActivityManager thread.
1303 mService.mHandler.sendMessage(mService.mHandler.obtainMessage(MSG_SYSTEM_UNLOCK_USER,
Fyodor Kupolov0f57cce2016-09-09 10:36:30 -07001304 userHandle /* arg1 */, 0 /* arg2 */));
Yohei Yukawaed4952a2016-02-17 07:57:25 -08001305 }
1306 }
1307
Yohei Yukawa7b18aec2016-03-07 13:04:32 -08001308 void onUnlockUser(@UserIdInt int userId) {
Yohei Yukawaed4952a2016-02-17 07:57:25 -08001309 synchronized(mMethodMap) {
1310 final int currentUserId = mSettings.getCurrentUserId();
1311 if (DEBUG) {
1312 Slog.d(TAG, "onUnlockUser: userId=" + userId + " curUserId=" + currentUserId);
1313 }
1314 if (userId != currentUserId) {
1315 return;
1316 }
1317 mSettings.switchCurrentUser(currentUserId, !mSystemReady);
Yohei Yukawa79247822017-01-23 15:26:15 -08001318 if (mSystemReady) {
1319 // We need to rebuild IMEs.
1320 buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
1321 updateInputMethodsFromSettingsLocked(true /* enabledChanged */);
1322 }
Yohei Yukawaed4952a2016-02-17 07:57:25 -08001323 }
Fyodor Kupolov1e33dc82016-01-21 12:02:58 -08001324 }
1325
Yohei Yukawa7b18aec2016-03-07 13:04:32 -08001326 void onSwitchUser(@UserIdInt int userId) {
1327 synchronized (mMethodMap) {
1328 switchUserLocked(userId);
1329 }
1330 }
1331
Seigo Nonaka7309b122015-08-17 18:34:13 -07001332 public InputMethodManagerService(Context context) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001333 mIPackageManager = AppGlobals.getPackageManager();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001334 mContext = context;
Dianne Hackborn7d3a5bc2010-11-29 22:52:12 -08001335 mRes = context.getResources();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001336 mHandler = new Handler(this);
Yohei Yukawa81482972015-06-04 00:58:59 -07001337 // Note: SettingsObserver doesn't register observers in its constructor.
1338 mSettingsObserver = new SettingsObserver(mHandler);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001339 mIWindowManager = IWindowManager.Stub.asInterface(
1340 ServiceManager.getService(Context.WINDOW_SERVICE));
Seigo Nonaka7309b122015-08-17 18:34:13 -07001341 mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
Mita Yuned218c72012-12-06 17:18:25 -08001342 mCaller = new HandlerCaller(context, null, new HandlerCaller.Callback() {
satoke7c6998e2011-06-03 17:57:59 +09001343 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001344 public void executeMessage(Message msg) {
1345 handleMessage(msg);
1346 }
Mita Yuned218c72012-12-06 17:18:25 -08001347 }, true /*asyncHandler*/);
Yohei Yukawad34e1482016-02-11 08:03:52 -08001348 mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
Yohei Yukawaed4952a2016-02-17 07:57:25 -08001349 mUserManager = mContext.getSystemService(UserManager.class);
satok01038492012-04-09 21:08:27 +09001350 mHardKeyboardListener = new HardKeyboardListener();
Dianne Hackborn119bbc32013-03-22 17:27:25 -07001351 mHasFeature = context.getPackageManager().hasSystemFeature(
1352 PackageManager.FEATURE_INPUT_METHODS);
Jason Monk3e189872016-01-12 09:10:34 -05001353 mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime);
Yohei Yukawafa0e47e2016-04-05 09:55:56 -07001354 mHardKeyboardBehavior = mContext.getResources().getInteger(
1355 com.android.internal.R.integer.config_externalHardKeyboardBehavior);
satok7cfc0ed2011-06-20 21:29:36 +09001356
Chris Wren1ce4b6d2015-06-11 10:19:43 -04001357 Bundle extras = new Bundle();
1358 extras.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, true);
Tadashi G. Takaoka01065a52017-07-19 14:10:24 +09001359 @ColorInt final int accentColor = mContext.getColor(
1360 com.android.internal.R.color.system_notification_accent_color);
Geoffrey Pitschaf759c52017-02-15 09:35:38 -05001361 mImeSwitcherNotification =
1362 new Notification.Builder(mContext, SystemNotificationChannels.VIRTUAL_KEYBOARD)
1363 .setSmallIcon(com.android.internal.R.drawable.ic_notification_ime_default)
1364 .setWhen(0)
1365 .setOngoing(true)
1366 .addExtras(extras)
1367 .setCategory(Notification.CATEGORY_SYSTEM)
Tadashi G. Takaoka01065a52017-07-19 14:10:24 +09001368 .setColor(accentColor);
Daniel Sandler590d5152012-06-14 16:10:13 -04001369
Tadashi G. Takaoka01065a52017-07-19 14:10:24 +09001370 Intent intent = new Intent(ACTION_SHOW_INPUT_METHOD_PICKER)
1371 .setPackage(mContext.getPackageName());
satok683e2382011-07-12 08:28:52 +09001372 mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
satokb858c732011-07-22 19:54:34 +09001373
1374 mShowOngoingImeSwitcherForPhones = false;
satok7cfc0ed2011-06-20 21:29:36 +09001375
satok7cfc0ed2011-06-20 21:29:36 +09001376 mNotificationShown = false;
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001377 int userId = 0;
1378 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -08001379 userId = ActivityManager.getService().getCurrentUser().id;
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001380 } catch (RemoteException e) {
1381 Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e);
1382 }
satok913a8922010-08-26 21:53:41 +09001383
satokd87c2592010-09-29 11:52:06 +09001384 // mSettings should be created before buildInputMethodListLocked
satokdf31ae62011-01-15 06:19:44 +09001385 mSettings = new InputMethodSettings(
Yohei Yukawa68645a62016-02-17 07:54:20 -08001386 mRes, context.getContentResolver(), mMethodMap, mMethodList, userId, !mSystemReady);
Svet Ganovadc1cf42015-06-15 16:36:24 -07001387
Kenny Guy2a764942014-04-02 13:29:20 +01001388 updateCurrentProfileIds();
Satoshi Kataoka5ade83b2012-09-26 22:59:41 +09001389 mFileManager = new InputMethodFileManager(mMethodMap, userId);
Yohei Yukawa79247822017-01-23 15:26:15 -08001390 mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
1391 mSettings, context);
Tarandeep Singh89a6c482017-11-21 14:26:11 -08001392 // Register VR-state listener.
1393 IVrManager vrManager = (IVrManager) ServiceManager.getService(Context.VR_SERVICE);
1394 if (vrManager != null) {
1395 try {
1396 vrManager.registerListener(mVrStateCallbacks);
1397 } catch (RemoteException e) {
1398 Slog.e(TAG, "Failed to register VR mode state listener.");
1399 }
1400 }
satok5b927c432012-05-01 20:09:34 +09001401 }
1402
satok5b927c432012-05-01 20:09:34 +09001403 private void resetDefaultImeLocked(Context context) {
1404 // Do not reset the default (current) IME when it is a 3rd-party IME
Yohei Yukawac2393ac2016-02-18 00:30:45 -08001405 if (mCurMethodId != null && !InputMethodUtils.isSystemIme(mMethodMap.get(mCurMethodId))) {
satok5b927c432012-05-01 20:09:34 +09001406 return;
1407 }
Yohei Yukawac2393ac2016-02-18 00:30:45 -08001408 final List<InputMethodInfo> suitableImes = InputMethodUtils.getDefaultEnabledImes(
Yohei Yukawaaf5cee82017-01-23 16:17:11 -08001409 context, mSettings.getEnabledInputMethodListLocked());
Yohei Yukawac2393ac2016-02-18 00:30:45 -08001410 if (suitableImes.isEmpty()) {
1411 Slog.i(TAG, "No default found");
1412 return;
satok5b927c432012-05-01 20:09:34 +09001413 }
Yohei Yukawac2393ac2016-02-18 00:30:45 -08001414 final InputMethodInfo defIm = suitableImes.get(0);
Yohei Yukawad0332832017-02-01 13:59:43 -08001415 if (DEBUG) {
1416 Slog.i(TAG, "Default found, using " + defIm.getId());
1417 }
Yohei Yukawac2393ac2016-02-18 00:30:45 -08001418 setSelectedInputMethodAndSubtypeLocked(defIm, NOT_A_SUBTYPE_ID, false);
satok5b927c432012-05-01 20:09:34 +09001419 }
1420
Andreas Gampea36dc622018-02-05 17:19:22 -08001421 @GuardedBy("mMethodMap")
Satoshi Kataoka5ade83b2012-09-26 22:59:41 +09001422 private void switchUserLocked(int newUserId) {
Yohei Yukawa90bf7082015-06-03 23:50:27 -07001423 if (DEBUG) Slog.d(TAG, "Switching user stage 1/3. newUserId=" + newUserId
1424 + " currentUserId=" + mSettings.getCurrentUserId());
1425
Yohei Yukawa81482972015-06-04 00:58:59 -07001426 // ContentObserver should be registered again when the user is changed
1427 mSettingsObserver.registerContentObserverLocked(newUserId);
Yohei Yukawa68645a62016-02-17 07:54:20 -08001428
Yohei Yukawaed4952a2016-02-17 07:57:25 -08001429 // If the system is not ready or the device is not yed unlocked by the user, then we use
1430 // copy-on-write settings.
1431 final boolean useCopyOnWriteSettings =
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001432 !mSystemReady || !mUserManager.isUserUnlockingOrUnlocked(newUserId);
Yohei Yukawa68645a62016-02-17 07:54:20 -08001433 mSettings.switchCurrentUser(newUserId, useCopyOnWriteSettings);
Kenny Guy2a764942014-04-02 13:29:20 +01001434 updateCurrentProfileIds();
Satoshi Kataoka5ade83b2012-09-26 22:59:41 +09001435 // InputMethodFileManager should be reset when the user is changed
1436 mFileManager = new InputMethodFileManager(mMethodMap, newUserId);
Satoshi Kataoka7f7535f2013-02-18 12:54:16 +09001437 final String defaultImiId = mSettings.getSelectedInputMethod();
Yohei Yukawa90bf7082015-06-03 23:50:27 -07001438
1439 if (DEBUG) Slog.d(TAG, "Switching user stage 2/3. newUserId=" + newUserId
1440 + " defaultImiId=" + defaultImiId);
1441
Satoshi Kataoka7c4a2a12013-02-25 15:25:43 +09001442 // For secondary users, the list of enabled IMEs may not have been updated since the
1443 // callbacks to PackageMonitor are ignored for the secondary user. Here, defaultImiId may
1444 // not be empty even if the IME has been uninstalled by the primary user.
1445 // Even in such cases, IMMS works fine because it will find the most applicable
1446 // IME for that user.
Satoshi Kataokaed1cdb22013-04-17 16:41:58 +09001447 final boolean initialUserSwitch = TextUtils.isEmpty(defaultImiId);
Yohei Yukawa0d7aff82017-02-10 00:40:51 -08001448 mLastSystemLocales = mRes.getConfiguration().getLocales();
1449
1450 // TODO: Is it really possible that switchUserLocked() happens before system ready?
1451 if (mSystemReady) {
1452 hideCurrentInputLocked(0, null);
1453 resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_SWITCH_USER);
1454 buildInputMethodListLocked(initialUserSwitch);
1455 if (TextUtils.isEmpty(mSettings.getSelectedInputMethod())) {
1456 // This is the first time of the user switch and
1457 // set the current ime to the proper one.
1458 resetDefaultImeLocked(mContext);
1459 }
1460 updateFromSettingsLocked(true);
1461 try {
1462 startInputInnerLocked();
1463 } catch (RuntimeException e) {
1464 Slog.w(TAG, "Unexpected exception", e);
1465 }
1466 }
1467
Satoshi Kataokaed1cdb22013-04-17 16:41:58 +09001468 if (initialUserSwitch) {
Yohei Yukawa094c71f2015-06-20 00:41:31 -07001469 InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
1470 mSettings.getEnabledInputMethodListLocked(), newUserId,
1471 mContext.getBasePackageName());
Satoshi Kataokaed1cdb22013-04-17 16:41:58 +09001472 }
Yohei Yukawa90bf7082015-06-03 23:50:27 -07001473
1474 if (DEBUG) Slog.d(TAG, "Switching user stage 3/3. newUserId=" + newUserId
1475 + " selectedIme=" + mSettings.getSelectedInputMethod());
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001476 }
1477
Kenny Guy2a764942014-04-02 13:29:20 +01001478 void updateCurrentProfileIds() {
Fyodor Kupolov7f98aa42016-04-07 14:56:25 -07001479 mSettings.setCurrentProfileIds(
1480 mUserManager.getProfileIdsWithDisabled(mSettings.getCurrentUserId()));
Amith Yamasani734983f2014-03-04 16:48:05 -08001481 }
1482
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001483 @Override
1484 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
1485 throws RemoteException {
1486 try {
1487 return super.onTransact(code, data, reply, flags);
1488 } catch (RuntimeException e) {
1489 // The input method manager only throws security exceptions, so let's
1490 // log all others.
1491 if (!(e instanceof SecurityException)) {
Dianne Hackborn164371f2013-10-01 19:10:13 -07001492 Slog.wtf(TAG, "Input Method Manager Crash", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001493 }
1494 throw e;
1495 }
1496 }
1497
Svetoslav Ganova0027152013-06-25 14:59:53 -07001498 public void systemRunning(StatusBarManagerService statusBar) {
Dianne Hackborna34f1ad2009-09-02 13:26:28 -07001499 synchronized (mMethodMap) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001500 if (DEBUG) {
1501 Slog.d(TAG, "--- systemReady");
1502 }
Dianne Hackborna34f1ad2009-09-02 13:26:28 -07001503 if (!mSystemReady) {
1504 mSystemReady = true;
Yohei Yukawa79247822017-01-23 15:26:15 -08001505 mLastSystemLocales = mRes.getConfiguration().getLocales();
Yohei Yukawa68645a62016-02-17 07:54:20 -08001506 final int currentUserId = mSettings.getCurrentUserId();
Yohei Yukawaed4952a2016-02-17 07:57:25 -08001507 mSettings.switchCurrentUser(currentUserId,
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001508 !mUserManager.isUserUnlockingOrUnlocked(currentUserId));
Yohei Yukawad34e1482016-02-11 08:03:52 -08001509 mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
1510 mNotificationManager = mContext.getSystemService(NotificationManager.class);
Dianne Hackborn661cd522011-08-22 00:26:20 -07001511 mStatusBar = statusBar;
Griff Hazen6090c262016-03-25 08:11:24 -07001512 if (mStatusBar != null) {
1513 mStatusBar.setIconVisibility(mSlotIme, false);
1514 }
Seigo Nonakad9eb9112015-05-26 20:54:43 +09001515 updateSystemUiLocked(mCurToken, mImeWindowVis, mBackDisposition);
satokb858c732011-07-22 19:54:34 +09001516 mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
1517 com.android.internal.R.bool.show_ongoing_ime_switcher);
satok01038492012-04-09 21:08:27 +09001518 if (mShowOngoingImeSwitcherForPhones) {
Seigo Nonaka7309b122015-08-17 18:34:13 -07001519 mWindowManagerInternal.setOnHardKeyboardStatusChangeListener(
satok01038492012-04-09 21:08:27 +09001520 mHardKeyboardListener);
1521 }
Yohei Yukawa79247822017-01-23 15:26:15 -08001522
1523 mMyPackageMonitor.register(mContext, null, UserHandle.ALL, true);
1524 mSettingsObserver.registerContentObserverLocked(currentUserId);
1525
1526 final IntentFilter broadcastFilter = new IntentFilter();
1527 broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
1528 broadcastFilter.addAction(Intent.ACTION_USER_ADDED);
1529 broadcastFilter.addAction(Intent.ACTION_USER_REMOVED);
Yohei Yukawa79247822017-01-23 15:26:15 -08001530 broadcastFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
Tadashi G. Takaoka01065a52017-07-19 14:10:24 +09001531 broadcastFilter.addAction(ACTION_SHOW_INPUT_METHOD_PICKER);
Yohei Yukawa79247822017-01-23 15:26:15 -08001532 mContext.registerReceiver(new ImmsBroadcastReceiver(), broadcastFilter);
1533
Yohei Yukawa1f9a3cb2017-10-26 15:00:59 -07001534 final String defaultImiId = mSettings.getSelectedInputMethod();
1535 final boolean imeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId);
1536 buildInputMethodListLocked(!imeSelectedOnBoot /* resetDefaultEnabledIme */);
Yohei Yukawa79247822017-01-23 15:26:15 -08001537 resetDefaultImeLocked(mContext);
1538 updateFromSettingsLocked(true);
1539 InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
1540 mSettings.getEnabledInputMethodListLocked(), currentUserId,
1541 mContext.getBasePackageName());
1542
Dianne Hackborncc278702009-09-02 23:07:23 -07001543 try {
1544 startInputInnerLocked();
1545 } catch (RuntimeException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001546 Slog.w(TAG, "Unexpected exception", e);
Dianne Hackborncc278702009-09-02 23:07:23 -07001547 }
Dianne Hackborna34f1ad2009-09-02 13:26:28 -07001548 }
1549 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001550 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001551
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001552 // ---------------------------------------------------------------------------------------
1553 // Check whether or not this is a valid IPC. Assumes an IPC is valid when either
1554 // 1) it comes from the system process
1555 // 2) the calling process' user id is identical to the current user id IMMS thinks.
1556 private boolean calledFromValidUser() {
1557 final int uid = Binder.getCallingUid();
1558 final int userId = UserHandle.getUserId(uid);
1559 if (DEBUG) {
1560 Slog.d(TAG, "--- calledFromForegroundUserOrSystemProcess ? "
1561 + "calling uid = " + uid + " system uid = " + Process.SYSTEM_UID
1562 + " calling userId = " + userId + ", foreground user id = "
Satoshi Kataoka87c29142013-07-31 23:11:54 +09001563 + mSettings.getCurrentUserId() + ", calling pid = " + Binder.getCallingPid()
1564 + InputMethodUtils.getApiCallStack());
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001565 }
Kenny Guy2a764942014-04-02 13:29:20 +01001566 if (uid == Process.SYSTEM_UID || mSettings.isCurrentProfile(userId)) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001567 return true;
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001568 }
Satoshi Kataoka135e5fb2012-09-28 18:25:06 +09001569
1570 // Caveat: A process which has INTERACT_ACROSS_USERS_FULL gets results for the
1571 // foreground user, not for the user of that process. Accordingly InputMethodManagerService
1572 // must not manage background users' states in any functions.
1573 // Note that privacy-sensitive IPCs, such as setInputMethod, are still securely guarded
1574 // by a token.
1575 if (mContext.checkCallingOrSelfPermission(
1576 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
1577 == PackageManager.PERMISSION_GRANTED) {
1578 if (DEBUG) {
1579 Slog.d(TAG, "--- Access granted because the calling process has "
1580 + "the INTERACT_ACROSS_USERS_FULL permission");
1581 }
1582 return true;
1583 }
Yohei Yukawad0332832017-02-01 13:59:43 -08001584 // TODO(b/34886274): The semantics of this verification is actually not well-defined.
Seigo Nonakae27dc2b2015-08-14 18:21:27 -07001585 Slog.w(TAG, "--- IPC called from background users. Ignore. callers="
1586 + Debug.getCallers(10));
Satoshi Kataoka135e5fb2012-09-28 18:25:06 +09001587 return false;
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001588 }
1589
Yohei Yukawa22c97be2014-06-04 19:43:36 +09001590
1591 /**
1592 * Returns true iff the caller is identified to be the current input method with the token.
1593 * @param token The window token given to the input method when it was started.
1594 * @return true if and only if non-null valid token is specified.
1595 */
Yohei Yukawad0332832017-02-01 13:59:43 -08001596 private boolean calledWithValidToken(@Nullable IBinder token) {
1597 if (token == null && Binder.getCallingPid() == Process.myPid()) {
1598 if (DEBUG) {
1599 // TODO(b/34851776): Basically it's the caller's fault if we reach here.
1600 Slog.d(TAG, "Bug 34851776 is detected callers=" + Debug.getCallers(10));
1601 }
1602 return false;
1603 }
1604 if (token == null || token != mCurToken) {
1605 // TODO(b/34886274): The semantics of this verification is actually not well-defined.
1606 Slog.e(TAG, "Ignoring " + Debug.getCaller() + " due to an invalid token."
1607 + " uid:" + Binder.getCallingUid() + " token:" + token);
Yohei Yukawa22c97be2014-06-04 19:43:36 +09001608 return false;
1609 }
1610 return true;
1611 }
1612
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001613 private boolean bindCurrentInputMethodService(
1614 Intent service, ServiceConnection conn, int flags) {
1615 if (service == null || conn == null) {
1616 Slog.e(TAG, "--- bind failed: service = " + service + ", conn = " + conn);
1617 return false;
1618 }
Amith Yamasani27b89e62013-01-16 12:30:11 -08001619 return mContext.bindServiceAsUser(service, conn, flags,
1620 new UserHandle(mSettings.getCurrentUserId()));
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001621 }
1622
satoke7c6998e2011-06-03 17:57:59 +09001623 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001624 public List<InputMethodInfo> getInputMethodList() {
Tarandeep Singh89a6c482017-11-21 14:26:11 -08001625 return getInputMethodList(false /* isVrOnly */);
1626 }
1627
1628 public List<InputMethodInfo> getVrInputMethodList() {
1629 return getInputMethodList(true /* isVrOnly */);
1630 }
1631
1632 private List<InputMethodInfo> getInputMethodList(final boolean isVrOnly) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001633 // TODO: Make this work even for non-current users?
1634 if (!calledFromValidUser()) {
1635 return Collections.emptyList();
1636 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001637 synchronized (mMethodMap) {
Tarandeep Singh89a6c482017-11-21 14:26:11 -08001638 ArrayList<InputMethodInfo> methodList = new ArrayList<>();
1639 for (InputMethodInfo info : mMethodList) {
1640
1641 if (info.isVrOnly() == isVrOnly) {
1642 methodList.add(info);
1643 }
1644 }
1645 return methodList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001646 }
1647 }
1648
satoke7c6998e2011-06-03 17:57:59 +09001649 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001650 public List<InputMethodInfo> getEnabledInputMethodList() {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001651 // TODO: Make this work even for non-current users?
1652 if (!calledFromValidUser()) {
1653 return Collections.emptyList();
1654 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001655 synchronized (mMethodMap) {
satokd87c2592010-09-29 11:52:06 +09001656 return mSettings.getEnabledInputMethodListLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001657 }
1658 }
1659
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09001660 /**
Satoshi Kataokab3c21ac2013-08-07 15:43:29 +09001661 * @param imiId if null, returns enabled subtypes for the current imi
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09001662 * @return enabled subtypes of the specified imi
1663 */
satoke7c6998e2011-06-03 17:57:59 +09001664 @Override
Satoshi Kataokab3c21ac2013-08-07 15:43:29 +09001665 public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
satok16331c82010-12-20 23:48:46 +09001666 boolean allowsImplicitlySelectedSubtypes) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001667 // TODO: Make this work even for non-current users?
1668 if (!calledFromValidUser()) {
Yohei Yukawab0377bb2015-08-10 21:06:30 -07001669 return Collections.emptyList();
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001670 }
satok67ddf9c2010-11-17 09:45:54 +09001671 synchronized (mMethodMap) {
Satoshi Kataokab3c21ac2013-08-07 15:43:29 +09001672 final InputMethodInfo imi;
1673 if (imiId == null && mCurMethodId != null) {
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09001674 imi = mMethodMap.get(mCurMethodId);
Satoshi Kataokab3c21ac2013-08-07 15:43:29 +09001675 } else {
1676 imi = mMethodMap.get(imiId);
1677 }
1678 if (imi == null) {
Yohei Yukawab0377bb2015-08-10 21:06:30 -07001679 return Collections.emptyList();
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09001680 }
1681 return mSettings.getEnabledInputMethodSubtypeListLocked(
1682 mContext, imi, allowsImplicitlySelectedSubtypes);
satok67ddf9c2010-11-17 09:45:54 +09001683 }
1684 }
1685
satoke7c6998e2011-06-03 17:57:59 +09001686 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001687 public void addClient(IInputMethodClient client,
1688 IInputContext inputContext, int uid, int pid) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001689 if (!calledFromValidUser()) {
1690 return;
1691 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001692 synchronized (mMethodMap) {
1693 mClients.put(client.asBinder(), new ClientState(client,
1694 inputContext, uid, pid));
1695 }
1696 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001697
satoke7c6998e2011-06-03 17:57:59 +09001698 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001699 public void removeClient(IInputMethodClient client) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001700 if (!calledFromValidUser()) {
1701 return;
1702 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001703 synchronized (mMethodMap) {
Jeff Brownc28867a2013-03-26 15:42:39 -07001704 ClientState cs = mClients.remove(client.asBinder());
1705 if (cs != null) {
1706 clearClientSessionLocked(cs);
Yohei Yukawa072b1b52015-11-18 15:54:34 -08001707 if (mCurClient == cs) {
Tarandeep Singh93c00cea2018-02-16 14:31:17 -08001708 if (mBoundToMethod) {
1709 mBoundToMethod = false;
1710 if (mCurMethod != null) {
1711 executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
1712 MSG_UNBIND_INPUT, mCurMethod));
1713 }
1714 }
Yohei Yukawa072b1b52015-11-18 15:54:34 -08001715 mCurClient = null;
1716 }
Yohei Yukawae39d4ed2015-11-19 03:38:49 -08001717 if (mCurFocusedWindowClient == cs) {
1718 mCurFocusedWindowClient = null;
1719 }
Jeff Brownc28867a2013-03-26 15:42:39 -07001720 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001721 }
1722 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001723
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001724 void executeOrSendMessage(IInterface target, Message msg) {
1725 if (target.asBinder() instanceof Binder) {
1726 mCaller.sendMessage(msg);
1727 } else {
1728 handleMessage(msg);
1729 msg.recycle();
1730 }
1731 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001732
Yohei Yukawa33e81792015-11-17 21:14:42 -08001733 void unbindCurrentClientLocked(
1734 /* @InputMethodClient.UnbindReason */ final int unbindClientReason) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001735 if (mCurClient != null) {
Yohei Yukawa35d3f372015-11-25 11:07:19 -08001736 if (DEBUG) Slog.v(TAG, "unbindCurrentInputLocked: client="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001737 + mCurClient.client.asBinder());
1738 if (mBoundToMethod) {
1739 mBoundToMethod = false;
1740 if (mCurMethod != null) {
1741 executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
1742 MSG_UNBIND_INPUT, mCurMethod));
1743 }
1744 }
Dianne Hackborna6e41342012-05-22 16:30:34 -07001745
Yohei Yukawa2bc66172017-02-08 11:13:25 -08001746 executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO(
1747 MSG_SET_ACTIVE, 0, 0, mCurClient));
Yohei Yukawa33e81792015-11-17 21:14:42 -08001748 executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO(
1749 MSG_UNBIND_CLIENT, mCurSeq, unbindClientReason, mCurClient.client));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001750 mCurClient.sessionRequested = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001751 mCurClient = null;
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001752
The Android Open Source Project10592532009-03-18 17:39:46 -07001753 hideInputMethodMenuLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001754 }
1755 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001756
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001757 private int getImeShowFlags() {
1758 int flags = 0;
1759 if (mShowForced) {
1760 flags |= InputMethod.SHOW_FORCED
1761 | InputMethod.SHOW_EXPLICIT;
1762 } else if (mShowExplicitlyRequested) {
1763 flags |= InputMethod.SHOW_EXPLICIT;
1764 }
1765 return flags;
1766 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001767
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001768 private int getAppShowFlags() {
1769 int flags = 0;
1770 if (mShowForced) {
1771 flags |= InputMethodManager.SHOW_FORCED;
1772 } else if (!mShowExplicitlyRequested) {
1773 flags |= InputMethodManager.SHOW_IMPLICIT;
1774 }
1775 return flags;
1776 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001777
Andreas Gampea36dc622018-02-05 17:19:22 -08001778 @GuardedBy("mMethodMap")
Yohei Yukawa2553e482017-12-15 15:47:33 -08001779 @NonNull
Yohei Yukawa87ca8402017-02-07 00:13:14 -08001780 InputBindResult attachNewInputLocked(
1781 /* @InputMethodClient.StartInputReason */ final int startInputReason, boolean initial) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001782 if (!mBoundToMethod) {
1783 executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
1784 MSG_BIND_INPUT, mCurMethod, mCurClient.binding));
1785 mBoundToMethod = true;
1786 }
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -08001787
1788 final Binder startInputToken = new Binder();
Yohei Yukawa357b2f62017-02-14 09:40:03 -08001789 final StartInputInfo info = new StartInputInfo(mCurToken, mCurId, startInputReason,
1790 !initial, mCurFocusedWindow, mCurAttribute, mCurFocusedWindowSoftInputMode,
1791 mCurSeq);
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -08001792 mStartInputMap.put(startInputToken, info);
Yohei Yukawa357b2f62017-02-14 09:40:03 -08001793 mStartInputHistory.addEntry(info);
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -08001794
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001795 final SessionState session = mCurClient.curSession;
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -08001796 executeOrSendMessage(session.method, mCaller.obtainMessageIIOOOO(
Yohei Yukawaf7526b52017-02-11 20:57:10 -08001797 MSG_START_INPUT, mCurInputContextMissingMethods, initial ? 0 : 1 /* restarting */,
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -08001798 startInputToken, session, mCurInputContext, mCurAttribute));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001799 if (mShowRequested) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08001800 if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
The Android Open Source Project4df24232009-03-05 14:34:35 -08001801 showCurrentInputLocked(getAppShowFlags(), null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001802 }
Yohei Yukawa2553e482017-12-15 15:47:33 -08001803 return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
1804 session.session, (session.channel != null ? session.channel.dup() : null),
Yohei Yukawa3d1e8122014-06-06 19:12:47 +09001805 mCurId, mCurSeq, mCurUserActionNotificationSequenceNumber);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001806 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001807
Andreas Gampea36dc622018-02-05 17:19:22 -08001808 @GuardedBy("mMethodMap")
Yohei Yukawa2553e482017-12-15 15:47:33 -08001809 @NonNull
Yohei Yukawa35d3f372015-11-25 11:07:19 -08001810 InputBindResult startInputLocked(
1811 /* @InputMethodClient.StartInputReason */ final int startInputReason,
Yohei Yukawa19a80a12016-03-14 22:57:37 -07001812 IInputMethodClient client, IInputContext inputContext,
1813 /* @InputConnectionInspector.missingMethods */ final int missingMethods,
Yohei Yukawa74750f22016-03-22 12:54:22 -07001814 @Nullable EditorInfo attribute, int controlFlags) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001815 // If no method is currently selected, do nothing.
1816 if (mCurMethodId == null) {
Yohei Yukawa2553e482017-12-15 15:47:33 -08001817 return InputBindResult.NO_IME;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001818 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001819
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001820 ClientState cs = mClients.get(client.asBinder());
1821 if (cs == null) {
1822 throw new IllegalArgumentException("unknown client "
1823 + client.asBinder());
1824 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001825
Yohei Yukawa74750f22016-03-22 12:54:22 -07001826 if (attribute == null) {
1827 Slog.w(TAG, "Ignoring startInput with null EditorInfo."
1828 + " uid=" + cs.uid + " pid=" + cs.pid);
Yohei Yukawa2553e482017-12-15 15:47:33 -08001829 return InputBindResult.NULL_EDITOR_INFO;
Yohei Yukawa74750f22016-03-22 12:54:22 -07001830 }
1831
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001832 try {
1833 if (!mIWindowManager.inputMethodClientHasFocus(cs.client)) {
1834 // Check with the window manager to make sure this client actually
1835 // has a window with focus. If not, reject. This is thread safe
1836 // because if the focus changes some time before or after, the
1837 // next client receiving focus that has any interest in input will
1838 // be calling through here after that change happens.
Yohei Yukawad0332832017-02-01 13:59:43 -08001839 if (DEBUG) {
1840 Slog.w(TAG, "Starting input on non-focused client " + cs.client
1841 + " (uid=" + cs.uid + " pid=" + cs.pid + ")");
1842 }
Yohei Yukawa2553e482017-12-15 15:47:33 -08001843 return InputBindResult.NOT_IME_TARGET_WINDOW;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001844 }
1845 } catch (RemoteException e) {
1846 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001847
Yohei Yukawa19a80a12016-03-14 22:57:37 -07001848 return startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
Yohei Yukawa87ca8402017-02-07 00:13:14 -08001849 controlFlags, startInputReason);
Dianne Hackborn7663d802012-02-24 13:08:49 -08001850 }
1851
Andreas Gampea36dc622018-02-05 17:19:22 -08001852 @GuardedBy("mMethodMap")
Yohei Yukawa2553e482017-12-15 15:47:33 -08001853 @NonNull
Yohei Yukawa35d3f372015-11-25 11:07:19 -08001854 InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
Yohei Yukawa19a80a12016-03-14 22:57:37 -07001855 /* @InputConnectionInspector.missingMethods */ final int missingMethods,
Yohei Yukawa87ca8402017-02-07 00:13:14 -08001856 @NonNull EditorInfo attribute, int controlFlags,
1857 /* @InputMethodClient.StartInputReason */ final int startInputReason) {
Dianne Hackborn7663d802012-02-24 13:08:49 -08001858 // If no method is currently selected, do nothing.
1859 if (mCurMethodId == null) {
Yohei Yukawa2553e482017-12-15 15:47:33 -08001860 return InputBindResult.NO_IME;
Dianne Hackborn7663d802012-02-24 13:08:49 -08001861 }
1862
Yohei Yukawad57ba672015-06-08 16:39:46 -07001863 if (!InputMethodUtils.checkIfPackageBelongsToUid(mAppOpsManager, cs.uid,
1864 attribute.packageName)) {
1865 Slog.e(TAG, "Rejecting this client as it reported an invalid package name."
1866 + " uid=" + cs.uid + " package=" + attribute.packageName);
Yohei Yukawa2553e482017-12-15 15:47:33 -08001867 return InputBindResult.INVALID_PACKAGE_NAME;
Yohei Yukawa0f3ad12015-04-06 16:48:24 -07001868 }
1869
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001870 if (mCurClient != cs) {
John Spurlocke0980502013-10-25 11:59:29 -04001871 // Was the keyguard locked when switching over to the new client?
1872 mCurClientInKeyguard = isKeyguardLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001873 // If the client is changing, we need to switch over to the new
1874 // one.
Yohei Yukawa33e81792015-11-17 21:14:42 -08001875 unbindCurrentClientLocked(InputMethodClient.UNBIND_REASON_SWITCH_CLIENT);
Yohei Yukawa35d3f372015-11-25 11:07:19 -08001876 if (DEBUG) Slog.v(TAG, "switching to client: client="
John Spurlocke0980502013-10-25 11:59:29 -04001877 + cs.client.asBinder() + " keyguard=" + mCurClientInKeyguard);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001878
1879 // If the screen is on, inform the new client it is active
Yohei Yukawafa6e0a82015-07-23 15:08:59 -07001880 if (mIsInteractive) {
Dianne Hackborna6e41342012-05-22 16:30:34 -07001881 executeOrSendMessage(cs.client, mCaller.obtainMessageIO(
Yohei Yukawafa6e0a82015-07-23 15:08:59 -07001882 MSG_SET_ACTIVE, mIsInteractive ? 1 : 0, cs));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001883 }
1884 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001885
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001886 // Bump up the sequence for this client and attach it.
1887 mCurSeq++;
1888 if (mCurSeq <= 0) mCurSeq = 1;
1889 mCurClient = cs;
1890 mCurInputContext = inputContext;
Yohei Yukawa19a80a12016-03-14 22:57:37 -07001891 mCurInputContextMissingMethods = missingMethods;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001892 mCurAttribute = attribute;
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001893
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001894 // Check if the input method is changing.
1895 if (mCurId != null && mCurId.equals(mCurMethodId)) {
1896 if (cs.curSession != null) {
1897 // Fast case: if we are already connected to the input method,
1898 // then just return it.
Yohei Yukawa87ca8402017-02-07 00:13:14 -08001899 return attachNewInputLocked(startInputReason,
Dianne Hackborn7663d802012-02-24 13:08:49 -08001900 (controlFlags&InputMethodManager.CONTROL_START_INITIAL) != 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001901 }
1902 if (mHaveConnection) {
1903 if (mCurMethod != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001904 // Return to client, and we will get back with it when
1905 // we have had a session made for it.
Jeff Brownc28867a2013-03-26 15:42:39 -07001906 requestClientSessionLocked(cs);
Yohei Yukawa2553e482017-12-15 15:47:33 -08001907 return new InputBindResult(
1908 InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
1909 null, null, mCurId, mCurSeq,
Yohei Yukawa3d1e8122014-06-06 19:12:47 +09001910 mCurUserActionNotificationSequenceNumber);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001911 } else if (SystemClock.uptimeMillis()
1912 < (mLastBindTime+TIME_TO_RECONNECT)) {
1913 // In this case we have connected to the service, but
1914 // don't yet have its interface. If it hasn't been too
1915 // long since we did the connection, we'll return to
1916 // the client and wait to get the service interface so
1917 // we can report back. If it has been too long, we want
1918 // to fall through so we can try a disconnect/reconnect
1919 // to see if we can get back in touch with the service.
Yohei Yukawa2553e482017-12-15 15:47:33 -08001920 return new InputBindResult(
1921 InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
1922 null, null, mCurId, mCurSeq,
Yohei Yukawa3d1e8122014-06-06 19:12:47 +09001923 mCurUserActionNotificationSequenceNumber);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001924 } else {
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001925 EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
1926 mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001927 }
1928 }
1929 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001930
Dianne Hackborna34f1ad2009-09-02 13:26:28 -07001931 return startInputInnerLocked();
1932 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001933
Dianne Hackborna34f1ad2009-09-02 13:26:28 -07001934 InputBindResult startInputInnerLocked() {
1935 if (mCurMethodId == null) {
Yohei Yukawa2553e482017-12-15 15:47:33 -08001936 return InputBindResult.NO_IME;
Dianne Hackborna34f1ad2009-09-02 13:26:28 -07001937 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001938
Dianne Hackborna34f1ad2009-09-02 13:26:28 -07001939 if (!mSystemReady) {
1940 // If the system is not yet ready, we shouldn't be running third
1941 // party code.
Yohei Yukawa2553e482017-12-15 15:47:33 -08001942 return new InputBindResult(
1943 InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
1944 null, null, mCurMethodId, mCurSeq,
Yohei Yukawa3d1e8122014-06-06 19:12:47 +09001945 mCurUserActionNotificationSequenceNumber);
Dianne Hackborna34f1ad2009-09-02 13:26:28 -07001946 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001947
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001948 InputMethodInfo info = mMethodMap.get(mCurMethodId);
1949 if (info == null) {
1950 throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
1951 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001952
Yohei Yukawabc7b5262015-11-17 17:38:41 -08001953 unbindCurrentMethodLocked(true);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001954
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001955 mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE);
1956 mCurIntent.setComponent(info.getComponent());
Dianne Hackborndd9b82c2009-09-03 00:18:47 -07001957 mCurIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
1958 com.android.internal.R.string.input_method_binding_label);
1959 mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
1960 mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));
Yohei Yukawaa67a4592017-03-30 15:57:02 -07001961 if (bindCurrentInputMethodService(mCurIntent, this, IME_CONNECTION_BIND_FLAGS)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001962 mLastBindTime = SystemClock.uptimeMillis();
1963 mHaveConnection = true;
1964 mCurId = info.getId();
1965 mCurToken = new Binder();
1966 try {
Yohei Yukawad0332832017-02-01 13:59:43 -08001967 if (DEBUG) Slog.v(TAG, "Adding window token: " + mCurToken);
Wale Ogunwaleac2561e2016-11-01 15:43:46 -07001968 mIWindowManager.addWindowToken(mCurToken, TYPE_INPUT_METHOD, DEFAULT_DISPLAY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001969 } catch (RemoteException e) {
1970 }
Yohei Yukawa2553e482017-12-15 15:47:33 -08001971 return new InputBindResult(
1972 InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
1973 null, null, mCurId, mCurSeq,
Yohei Yukawa3d1e8122014-06-06 19:12:47 +09001974 mCurUserActionNotificationSequenceNumber);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001975 }
Yohei Yukawa2553e482017-12-15 15:47:33 -08001976 mCurIntent = null;
1977 Slog.w(TAG, "Failure connecting to input method service: " + mCurIntent);
1978 return InputBindResult.IME_NOT_CONNECTED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001979 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08001980
Yohei Yukawa2553e482017-12-15 15:47:33 -08001981 @NonNull
Yohei Yukawa05c25f82016-02-22 12:41:17 -08001982 private InputBindResult startInput(
Yohei Yukawa35d3f372015-11-25 11:07:19 -08001983 /* @InputMethodClient.StartInputReason */ final int startInputReason,
Yohei Yukawa19a80a12016-03-14 22:57:37 -07001984 IInputMethodClient client, IInputContext inputContext,
1985 /* @InputConnectionInspector.missingMethods */ final int missingMethods,
Yohei Yukawa74750f22016-03-22 12:54:22 -07001986 @Nullable EditorInfo attribute, int controlFlags) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001987 if (!calledFromValidUser()) {
Yohei Yukawa2553e482017-12-15 15:47:33 -08001988 return InputBindResult.INVALID_USER;
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09001989 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001990 synchronized (mMethodMap) {
Yohei Yukawa35d3f372015-11-25 11:07:19 -08001991 if (DEBUG) {
1992 Slog.v(TAG, "startInput: reason="
1993 + InputMethodClient.getStartInputReason(startInputReason)
1994 + " client = " + client.asBinder()
1995 + " inputContext=" + inputContext
Yohei Yukawa19a80a12016-03-14 22:57:37 -07001996 + " missingMethods="
1997 + InputConnectionInspector.getMissingMethodFlagsAsString(missingMethods)
Yohei Yukawa35d3f372015-11-25 11:07:19 -08001998 + " attribute=" + attribute
1999 + " controlFlags=#" + Integer.toHexString(controlFlags));
2000 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002001 final long ident = Binder.clearCallingIdentity();
2002 try {
Yohei Yukawa19a80a12016-03-14 22:57:37 -07002003 return startInputLocked(startInputReason, client, inputContext, missingMethods,
2004 attribute, controlFlags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002005 } finally {
2006 Binder.restoreCallingIdentity(ident);
2007 }
2008 }
2009 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002010
satoke7c6998e2011-06-03 17:57:59 +09002011 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002012 public void finishInput(IInputMethodClient client) {
2013 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002014
satoke7c6998e2011-06-03 17:57:59 +09002015 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002016 public void onServiceConnected(ComponentName name, IBinder service) {
2017 synchronized (mMethodMap) {
2018 if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
2019 mCurMethod = IInputMethod.Stub.asInterface(service);
Dianne Hackborncc278702009-09-02 23:07:23 -07002020 if (mCurToken == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002021 Slog.w(TAG, "Service connected without a token!");
Yohei Yukawabc7b5262015-11-17 17:38:41 -08002022 unbindCurrentMethodLocked(false);
Dianne Hackborncc278702009-09-02 23:07:23 -07002023 return;
2024 }
Craig Mautnere4bbb1c2013-03-15 11:38:44 -07002025 if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
Dianne Hackborncc278702009-09-02 23:07:23 -07002026 executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
2027 MSG_ATTACH_TOKEN, mCurMethod, mCurToken));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002028 if (mCurClient != null) {
Jeff Brownc28867a2013-03-26 15:42:39 -07002029 clearClientSessionLocked(mCurClient);
2030 requestClientSessionLocked(mCurClient);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002031 }
2032 }
2033 }
2034 }
2035
Jeff Brownc28867a2013-03-26 15:42:39 -07002036 void onSessionCreated(IInputMethod method, IInputMethodSession session,
2037 InputChannel channel) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002038 synchronized (mMethodMap) {
2039 if (mCurMethod != null && method != null
2040 && mCurMethod.asBinder() == method.asBinder()) {
2041 if (mCurClient != null) {
Jeff Brownc28867a2013-03-26 15:42:39 -07002042 clearClientSessionLocked(mCurClient);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002043 mCurClient.curSession = new SessionState(mCurClient,
Jeff Brownc28867a2013-03-26 15:42:39 -07002044 method, session, channel);
Yohei Yukawa87ca8402017-02-07 00:13:14 -08002045 InputBindResult res = attachNewInputLocked(
2046 InputMethodClient.START_INPUT_REASON_SESSION_CREATED_BY_IME, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002047 if (res.method != null) {
2048 executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
Yohei Yukawa33e81792015-11-17 21:14:42 -08002049 MSG_BIND_CLIENT, mCurClient.client, res));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002050 }
Jeff Brownc28867a2013-03-26 15:42:39 -07002051 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002052 }
2053 }
2054 }
Jeff Brownc28867a2013-03-26 15:42:39 -07002055
2056 // Session abandoned. Close its associated input channel.
2057 channel.dispose();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002058 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002059
Yohei Yukawabc7b5262015-11-17 17:38:41 -08002060 void unbindCurrentMethodLocked(boolean savePosition) {
Dianne Hackborn2c84cfc2011-10-31 15:39:59 -07002061 if (mVisibleBound) {
2062 mContext.unbindService(mVisibleConnection);
2063 mVisibleBound = false;
2064 }
2065
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07002066 if (mHaveConnection) {
2067 mContext.unbindService(this);
2068 mHaveConnection = false;
2069 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002070
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07002071 if (mCurToken != null) {
2072 try {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002073 if (DEBUG) Slog.v(TAG, "Removing window token: " + mCurToken);
Dianne Hackborn2ea9bae2012-11-02 18:43:48 -07002074 if ((mImeWindowVis & InputMethodService.IME_ACTIVE) != 0 && savePosition) {
satoke0a99412012-05-10 02:22:58 +09002075 // The current IME is shown. Hence an IME switch (transition) is happening.
Seigo Nonaka7309b122015-08-17 18:34:13 -07002076 mWindowManagerInternal.saveLastInputMethodWindowForTransition();
satoke0a99412012-05-10 02:22:58 +09002077 }
Wale Ogunwaleac2561e2016-11-01 15:43:46 -07002078 mIWindowManager.removeWindowToken(mCurToken, DEFAULT_DISPLAY);
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07002079 } catch (RemoteException e) {
2080 }
2081 mCurToken = null;
2082 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002083
The Android Open Source Project10592532009-03-18 17:39:46 -07002084 mCurId = null;
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07002085 clearCurMethodLocked();
Yohei Yukawabc7b5262015-11-17 17:38:41 -08002086 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002087
Yohei Yukawa33e81792015-11-17 21:14:42 -08002088 void resetCurrentMethodAndClient(
2089 /* @InputMethodClient.UnbindReason */ final int unbindClientReason) {
Yohei Yukawabc7b5262015-11-17 17:38:41 -08002090 mCurMethodId = null;
2091 unbindCurrentMethodLocked(false);
Yohei Yukawa33e81792015-11-17 21:14:42 -08002092 unbindCurrentClientLocked(unbindClientReason);
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07002093 }
Jeff Brownc28867a2013-03-26 15:42:39 -07002094
2095 void requestClientSessionLocked(ClientState cs) {
2096 if (!cs.sessionRequested) {
2097 if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs);
2098 InputChannel[] channels = InputChannel.openInputChannelPair(cs.toString());
2099 cs.sessionRequested = true;
2100 executeOrSendMessage(mCurMethod, mCaller.obtainMessageOOO(
2101 MSG_CREATE_SESSION, mCurMethod, channels[1],
2102 new MethodCallback(this, mCurMethod, channels[0])));
2103 }
2104 }
2105
2106 void clearClientSessionLocked(ClientState cs) {
2107 finishSessionLocked(cs.curSession);
2108 cs.curSession = null;
2109 cs.sessionRequested = false;
2110 }
2111
2112 private void finishSessionLocked(SessionState sessionState) {
2113 if (sessionState != null) {
2114 if (sessionState.session != null) {
2115 try {
2116 sessionState.session.finishSession();
2117 } catch (RemoteException e) {
2118 Slog.w(TAG, "Session failed to close due to remote exception", e);
Seigo Nonakad9eb9112015-05-26 20:54:43 +09002119 updateSystemUiLocked(mCurToken, 0 /* vis */, mBackDisposition);
Jeff Brownc28867a2013-03-26 15:42:39 -07002120 }
2121 sessionState.session = null;
2122 }
2123 if (sessionState.channel != null) {
2124 sessionState.channel.dispose();
2125 sessionState.channel = null;
Devin Taylor0c33ed22010-02-23 13:26:46 -06002126 }
2127 }
2128 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002129
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07002130 void clearCurMethodLocked() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002131 if (mCurMethod != null) {
2132 for (ClientState cs : mClients.values()) {
Jeff Brownc28867a2013-03-26 15:42:39 -07002133 clearClientSessionLocked(cs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002134 }
Devin Taylor0c33ed22010-02-23 13:26:46 -06002135
Jeff Brownc28867a2013-03-26 15:42:39 -07002136 finishSessionLocked(mEnabledSession);
Devin Taylor0c33ed22010-02-23 13:26:46 -06002137 mEnabledSession = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002138 mCurMethod = null;
2139 }
Dianne Hackborn661cd522011-08-22 00:26:20 -07002140 if (mStatusBar != null) {
Jason Monk3e189872016-01-12 09:10:34 -05002141 mStatusBar.setIconVisibility(mSlotIme, false);
Dianne Hackborn661cd522011-08-22 00:26:20 -07002142 }
Yohei Yukawa2bc66172017-02-08 11:13:25 -08002143 mInFullscreenMode = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002144 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002145
satoke7c6998e2011-06-03 17:57:59 +09002146 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002147 public void onServiceDisconnected(ComponentName name) {
Yohei Yukawa817d5f72017-01-04 20:15:02 -08002148 // Note that mContext.unbindService(this) does not trigger this. Hence if we are here the
2149 // disconnection is not intended by IMMS (e.g. triggered because the current IMS crashed),
2150 // which is irregular but can eventually happen for everyone just by continuing using the
2151 // device. Thus it is important to make sure that all the internal states are properly
2152 // refreshed when this method is called back. Running
2153 // adb install -r <APK that implements the current IME>
2154 // would be a good way to trigger such a situation.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002155 synchronized (mMethodMap) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002156 if (DEBUG) Slog.v(TAG, "Service disconnected: " + name
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002157 + " mCurIntent=" + mCurIntent);
2158 if (mCurMethod != null && mCurIntent != null
2159 && name.equals(mCurIntent.getComponent())) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07002160 clearCurMethodLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002161 // We consider this to be a new bind attempt, since the system
2162 // should now try to restart the service for us.
2163 mLastBindTime = SystemClock.uptimeMillis();
2164 mShowRequested = mInputShown;
2165 mInputShown = false;
Yohei Yukawa817d5f72017-01-04 20:15:02 -08002166 unbindCurrentClientLocked(InputMethodClient.UNBIND_REASON_DISCONNECT_IME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002167 }
2168 }
2169 }
2170
satokf9f01002011-05-19 21:31:50 +09002171 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002172 public void updateStatusIcon(IBinder token, String packageName, int iconId) {
Yohei Yukawa59377ca2017-01-31 21:32:26 -08002173 synchronized (mMethodMap) {
2174 if (!calledWithValidToken(token)) {
Yohei Yukawa59377ca2017-01-31 21:32:26 -08002175 return;
2176 }
2177 final long ident = Binder.clearCallingIdentity();
2178 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002179 if (iconId == 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002180 if (DEBUG) Slog.d(TAG, "hide the small icon for the input method");
Dianne Hackborn661cd522011-08-22 00:26:20 -07002181 if (mStatusBar != null) {
Jason Monk3e189872016-01-12 09:10:34 -05002182 mStatusBar.setIconVisibility(mSlotIme, false);
Dianne Hackborn661cd522011-08-22 00:26:20 -07002183 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002184 } else if (packageName != null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002185 if (DEBUG) Slog.d(TAG, "show a small icon for the input method");
Svetoslav Ganov6179ea32011-06-28 01:12:41 -07002186 CharSequence contentDescription = null;
2187 try {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09002188 // Use PackageManager to load label
2189 final PackageManager packageManager = mContext.getPackageManager();
Svetoslav Ganov6179ea32011-06-28 01:12:41 -07002190 contentDescription = packageManager.getApplicationLabel(
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09002191 mIPackageManager.getApplicationInfo(packageName, 0,
2192 mSettings.getCurrentUserId()));
2193 } catch (RemoteException e) {
Svetoslav Ganov6179ea32011-06-28 01:12:41 -07002194 /* ignore */
2195 }
Dianne Hackborn661cd522011-08-22 00:26:20 -07002196 if (mStatusBar != null) {
Jason Monk3e189872016-01-12 09:10:34 -05002197 mStatusBar.setIcon(mSlotIme, packageName, iconId, 0,
Dianne Hackborn661cd522011-08-22 00:26:20 -07002198 contentDescription != null
2199 ? contentDescription.toString() : null);
Jason Monk3e189872016-01-12 09:10:34 -05002200 mStatusBar.setIconVisibility(mSlotIme, true);
Dianne Hackborn661cd522011-08-22 00:26:20 -07002201 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002202 }
Yohei Yukawa59377ca2017-01-31 21:32:26 -08002203 } finally {
2204 Binder.restoreCallingIdentity(ident);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002205 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002206 }
2207 }
2208
Seigo Nonakad9eb9112015-05-26 20:54:43 +09002209 private boolean shouldShowImeSwitcherLocked(int visibility) {
satok7cfc0ed2011-06-20 21:29:36 +09002210 if (!mShowOngoingImeSwitcherForPhones) return false;
Jason Monk807ef762014-05-08 15:47:46 -04002211 if (mSwitchingDialog != null) return false;
Yohei Yukawad2bc3092017-07-31 15:37:14 -07002212 if (mWindowManagerInternal.isKeyguardShowingAndNotOccluded()
2213 && mKeyguardManager != null && mKeyguardManager.isKeyguardSecure()) return false;
Seigo Nonakad9eb9112015-05-26 20:54:43 +09002214 if ((visibility & InputMethodService.IME_ACTIVE) == 0) return false;
Seigo Nonaka7309b122015-08-17 18:34:13 -07002215 if (mWindowManagerInternal.isHardKeyboardAvailable()) {
Yohei Yukawafa0e47e2016-04-05 09:55:56 -07002216 if (mHardKeyboardBehavior == HardKeyboardBehavior.WIRELESS_AFFORDANCE) {
2217 // When physical keyboard is attached, we show the ime switcher (or notification if
2218 // NavBar is not available) because SHOW_IME_WITH_HARD_KEYBOARD settings currently
2219 // exists in the IME switcher dialog. Might be OK to remove this condition once
2220 // SHOW_IME_WITH_HARD_KEYBOARD settings finds a good place to live.
2221 return true;
2222 }
Yohei Yukawa89398382016-03-29 11:37:04 -07002223 } else if ((visibility & InputMethodService.IME_VISIBLE) == 0) {
2224 return false;
Yohei Yukawac6c7cd22015-05-12 15:00:33 -07002225 }
Yohei Yukawac6c7cd22015-05-12 15:00:33 -07002226
2227 List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
2228 final int N = imis.size();
2229 if (N > 2) return true;
2230 if (N < 1) return false;
2231 int nonAuxCount = 0;
2232 int auxCount = 0;
2233 InputMethodSubtype nonAuxSubtype = null;
2234 InputMethodSubtype auxSubtype = null;
2235 for(int i = 0; i < N; ++i) {
2236 final InputMethodInfo imi = imis.get(i);
2237 final List<InputMethodSubtype> subtypes =
2238 mSettings.getEnabledInputMethodSubtypeListLocked(mContext, imi, true);
2239 final int subtypeCount = subtypes.size();
2240 if (subtypeCount == 0) {
2241 ++nonAuxCount;
2242 } else {
2243 for (int j = 0; j < subtypeCount; ++j) {
2244 final InputMethodSubtype subtype = subtypes.get(j);
2245 if (!subtype.isAuxiliary()) {
2246 ++nonAuxCount;
2247 nonAuxSubtype = subtype;
2248 } else {
2249 ++auxCount;
2250 auxSubtype = subtype;
satok7cfc0ed2011-06-20 21:29:36 +09002251 }
2252 }
satok7cfc0ed2011-06-20 21:29:36 +09002253 }
2254 }
Yohei Yukawac6c7cd22015-05-12 15:00:33 -07002255 if (nonAuxCount > 1 || auxCount > 1) {
2256 return true;
2257 } else if (nonAuxCount == 1 && auxCount == 1) {
2258 if (nonAuxSubtype != null && auxSubtype != null
2259 && (nonAuxSubtype.getLocale().equals(auxSubtype.getLocale())
2260 || auxSubtype.overridesImplicitlyEnabledSubtype()
2261 || nonAuxSubtype.overridesImplicitlyEnabledSubtype())
2262 && nonAuxSubtype.containsExtraValueKey(TAG_TRY_SUPPRESSING_IME_SWITCHER)) {
2263 return false;
2264 }
2265 return true;
2266 }
2267 return false;
satok7cfc0ed2011-06-20 21:29:36 +09002268 }
2269
John Spurlocke0980502013-10-25 11:59:29 -04002270 private boolean isKeyguardLocked() {
2271 return mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
2272 }
2273
Yohei Yukawad6475a62017-04-17 10:35:27 -07002274 @BinderThread
satokdbf29502011-08-25 15:28:23 +09002275 @SuppressWarnings("deprecation")
satokf9f01002011-05-19 21:31:50 +09002276 @Override
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -08002277 public void setImeWindowStatus(IBinder token, IBinder startInputToken, int vis,
2278 int backDisposition) {
Seigo Nonakad9eb9112015-05-26 20:54:43 +09002279 if (!calledWithValidToken(token)) {
Seigo Nonakad9eb9112015-05-26 20:54:43 +09002280 return;
2281 }
2282
Yohei Yukawa69e68022017-02-13 12:04:50 -08002283 final StartInputInfo info;
Seigo Nonakad9eb9112015-05-26 20:54:43 +09002284 synchronized (mMethodMap) {
Yohei Yukawa69e68022017-02-13 12:04:50 -08002285 info = mStartInputMap.get(startInputToken);
Seigo Nonakad9eb9112015-05-26 20:54:43 +09002286 mImeWindowVis = vis;
2287 mBackDisposition = backDisposition;
2288 updateSystemUiLocked(token, vis, backDisposition);
2289 }
Yohei Yukawad6475a62017-04-17 10:35:27 -07002290
2291 final boolean dismissImeOnBackKeyPressed;
2292 switch (backDisposition) {
2293 case InputMethodService.BACK_DISPOSITION_WILL_DISMISS:
2294 dismissImeOnBackKeyPressed = true;
2295 break;
2296 case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS:
2297 dismissImeOnBackKeyPressed = false;
2298 break;
2299 default:
2300 case InputMethodService.BACK_DISPOSITION_DEFAULT:
2301 dismissImeOnBackKeyPressed = ((vis & InputMethodService.IME_VISIBLE) != 0);
2302 break;
2303 }
Yohei Yukawaee2a7ed2017-02-15 21:38:57 -08002304 mWindowManagerInternal.updateInputMethodWindowStatus(token,
2305 (vis & InputMethodService.IME_VISIBLE) != 0,
Yohei Yukawad6475a62017-04-17 10:35:27 -07002306 dismissImeOnBackKeyPressed, info != null ? info.mTargetWindow : null);
Seigo Nonakad9eb9112015-05-26 20:54:43 +09002307 }
2308
2309 private void updateSystemUi(IBinder token, int vis, int backDisposition) {
2310 synchronized (mMethodMap) {
2311 updateSystemUiLocked(token, vis, backDisposition);
2312 }
2313 }
2314
2315 // Caution! This method is called in this class. Handle multi-user carefully
2316 private void updateSystemUiLocked(IBinder token, int vis, int backDisposition) {
2317 if (!calledWithValidToken(token)) {
Seigo Nonakad9eb9112015-05-26 20:54:43 +09002318 return;
2319 }
2320
2321 // TODO: Move this clearing calling identity block to setImeWindowStatus after making sure
2322 // all updateSystemUi happens on system previlege.
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09002323 final long ident = Binder.clearCallingIdentity();
satok06487a52010-10-29 11:37:18 +09002324 try {
Seigo Nonakad9eb9112015-05-26 20:54:43 +09002325 // apply policy for binder calls
2326 if (vis != 0 && isKeyguardLocked() && !mCurClientInKeyguard) {
2327 vis = 0;
satok06487a52010-10-29 11:37:18 +09002328 }
Seigo Nonakad9eb9112015-05-26 20:54:43 +09002329 // mImeWindowVis should be updated before calling shouldShowImeSwitcherLocked().
2330 final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis);
2331 if (mStatusBar != null) {
2332 mStatusBar.setImeWindowStatus(token, vis, backDisposition,
2333 needsToShowImeSwitcher);
2334 }
2335 final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
2336 if (imi != null && needsToShowImeSwitcher) {
2337 // Used to load label
2338 final CharSequence title = mRes.getText(
2339 com.android.internal.R.string.select_input_method);
2340 final CharSequence summary = InputMethodUtils.getImeAndSubtypeDisplayName(
2341 mContext, imi, mCurrentSubtype);
Chris Wren1ce4b6d2015-06-11 10:19:43 -04002342 mImeSwitcherNotification.setContentTitle(title)
2343 .setContentText(summary)
2344 .setContentIntent(mImeSwitchPendingIntent);
Seigo Nonaka7309b122015-08-17 18:34:13 -07002345 try {
2346 if ((mNotificationManager != null)
2347 && !mIWindowManager.hasNavigationBar()) {
2348 if (DEBUG) {
2349 Slog.d(TAG, "--- show notification: label = " + summary);
2350 }
2351 mNotificationManager.notifyAsUser(null,
Chris Wren282cfef2017-03-27 15:01:44 -04002352 SystemMessage.NOTE_SELECT_INPUT_METHOD,
Seigo Nonaka7309b122015-08-17 18:34:13 -07002353 mImeSwitcherNotification.build(), UserHandle.ALL);
2354 mNotificationShown = true;
Dianne Hackborn661cd522011-08-22 00:26:20 -07002355 }
Seigo Nonaka7309b122015-08-17 18:34:13 -07002356 } catch (RemoteException e) {
Seigo Nonakad9eb9112015-05-26 20:54:43 +09002357 }
2358 } else {
2359 if (mNotificationShown && mNotificationManager != null) {
2360 if (DEBUG) {
2361 Slog.d(TAG, "--- hide notification");
satok7cfc0ed2011-06-20 21:29:36 +09002362 }
Seigo Nonakad9eb9112015-05-26 20:54:43 +09002363 mNotificationManager.cancelAsUser(null,
Chris Wren282cfef2017-03-27 15:01:44 -04002364 SystemMessage.NOTE_SELECT_INPUT_METHOD, UserHandle.ALL);
Seigo Nonakad9eb9112015-05-26 20:54:43 +09002365 mNotificationShown = false;
satok7cfc0ed2011-06-20 21:29:36 +09002366 }
satok06487a52010-10-29 11:37:18 +09002367 }
2368 } finally {
2369 Binder.restoreCallingIdentity(ident);
2370 }
2371 }
2372
satoke7c6998e2011-06-03 17:57:59 +09002373 @Override
satokf9f01002011-05-19 21:31:50 +09002374 public void registerSuggestionSpansForNotification(SuggestionSpan[] spans) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09002375 if (!calledFromValidUser()) {
2376 return;
2377 }
satokf9f01002011-05-19 21:31:50 +09002378 synchronized (mMethodMap) {
2379 final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId);
2380 for (int i = 0; i < spans.length; ++i) {
2381 SuggestionSpan ss = spans[i];
satok42c5a162011-05-26 16:46:14 +09002382 if (!TextUtils.isEmpty(ss.getNotificationTargetClassName())) {
satokf9f01002011-05-19 21:31:50 +09002383 mSecureSuggestionSpans.put(ss, currentImi);
2384 }
2385 }
2386 }
2387 }
2388
satoke7c6998e2011-06-03 17:57:59 +09002389 @Override
satokf9f01002011-05-19 21:31:50 +09002390 public boolean notifySuggestionPicked(SuggestionSpan span, String originalString, int index) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09002391 if (!calledFromValidUser()) {
2392 return false;
2393 }
satokf9f01002011-05-19 21:31:50 +09002394 synchronized (mMethodMap) {
2395 final InputMethodInfo targetImi = mSecureSuggestionSpans.get(span);
2396 // TODO: Do not send the intent if the process of the targetImi is already dead.
2397 if (targetImi != null) {
2398 final String[] suggestions = span.getSuggestions();
2399 if (index < 0 || index >= suggestions.length) return false;
satok42c5a162011-05-26 16:46:14 +09002400 final String className = span.getNotificationTargetClassName();
satokf9f01002011-05-19 21:31:50 +09002401 final Intent intent = new Intent();
2402 // Ensures that only a class in the original IME package will receive the
2403 // notification.
satok42c5a162011-05-26 16:46:14 +09002404 intent.setClassName(targetImi.getPackageName(), className);
satokf9f01002011-05-19 21:31:50 +09002405 intent.setAction(SuggestionSpan.ACTION_SUGGESTION_PICKED);
2406 intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_BEFORE, originalString);
2407 intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_AFTER, suggestions[index]);
2408 intent.putExtra(SuggestionSpan.SUGGESTION_SPAN_PICKED_HASHCODE, span.hashCode());
Amith Yamasanif043de92012-10-24 06:42:40 -07002409 final long ident = Binder.clearCallingIdentity();
2410 try {
2411 mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
2412 } finally {
2413 Binder.restoreCallingIdentity(ident);
2414 }
satokf9f01002011-05-19 21:31:50 +09002415 return true;
2416 }
2417 }
2418 return false;
2419 }
2420
Dianne Hackbornfd7aded2013-01-22 17:10:23 -08002421 void updateFromSettingsLocked(boolean enabledMayChange) {
Michael Wright7b5a96b2014-08-09 19:28:42 -07002422 updateInputMethodsFromSettingsLocked(enabledMayChange);
2423 updateKeyboardFromSettingsLocked();
2424 }
2425
2426 void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) {
Dianne Hackbornfd7aded2013-01-22 17:10:23 -08002427 if (enabledMayChange) {
2428 List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked();
2429 for (int i=0; i<enabled.size(); i++) {
2430 // We allow the user to select "disabled until used" apps, so if they
2431 // are enabling one of those here we now need to make it enabled.
2432 InputMethodInfo imm = enabled.get(i);
2433 try {
2434 ApplicationInfo ai = mIPackageManager.getApplicationInfo(imm.getPackageName(),
2435 PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
2436 mSettings.getCurrentUserId());
Satoshi Kataoka7987a312013-04-17 18:59:33 +09002437 if (ai != null && ai.enabledSetting
Dianne Hackbornfd7aded2013-01-22 17:10:23 -08002438 == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
Satoshi Kataokaed1cdb22013-04-17 16:41:58 +09002439 if (DEBUG) {
2440 Slog.d(TAG, "Update state(" + imm.getId()
2441 + "): DISABLED_UNTIL_USED -> DEFAULT");
2442 }
Dianne Hackbornfd7aded2013-01-22 17:10:23 -08002443 mIPackageManager.setApplicationEnabledSetting(imm.getPackageName(),
2444 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
Dianne Hackborn3fa3c28a2013-03-26 16:15:41 -07002445 PackageManager.DONT_KILL_APP, mSettings.getCurrentUserId(),
2446 mContext.getBasePackageName());
Dianne Hackbornfd7aded2013-01-22 17:10:23 -08002447 }
2448 } catch (RemoteException e) {
2449 }
2450 }
2451 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07002452 // We are assuming that whoever is changing DEFAULT_INPUT_METHOD and
2453 // ENABLED_INPUT_METHODS is taking care of keeping them correctly in
2454 // sync, so we will never have a DEFAULT_INPUT_METHOD that is not
2455 // enabled.
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09002456 String id = mSettings.getSelectedInputMethod();
satok03eb319a2010-11-11 18:17:42 +09002457 // There is no input method selected, try to choose new applicable input method.
2458 if (TextUtils.isEmpty(id) && chooseNewDefaultIMELocked()) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09002459 id = mSettings.getSelectedInputMethod();
satok03eb319a2010-11-11 18:17:42 +09002460 }
2461 if (!TextUtils.isEmpty(id)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002462 try {
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09002463 setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002464 } catch (IllegalArgumentException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002465 Slog.w(TAG, "Unknown input method from prefs: " + id, e);
Yohei Yukawa33e81792015-11-17 21:14:42 -08002466 resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_SWITCH_IME_FAILED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002467 }
satokf3db1af2010-11-23 13:34:33 +09002468 mShortcutInputMethodsAndSubtypes.clear();
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07002469 } else {
2470 // There is no longer an input method set, so stop any current one.
Yohei Yukawa33e81792015-11-17 21:14:42 -08002471 resetCurrentMethodAndClient(InputMethodClient.UNBIND_REASON_NO_IME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002472 }
Yohei Yukawa3d46bab2014-05-30 18:10:18 +09002473 // Here is not the perfect place to reset the switching controller. Ideally
2474 // mSwitchingController and mSettings should be able to share the same state.
2475 // TODO: Make sure that mSwitchingController and mSettings are sharing the
2476 // the same enabled IMEs list.
2477 mSwitchingController.resetCircularListLocked(mContext);
Michael Wright7b5a96b2014-08-09 19:28:42 -07002478
2479 }
2480
2481 public void updateKeyboardFromSettingsLocked() {
2482 mShowImeWithHardKeyboard = mSettings.isShowImeWithHardKeyboardEnabled();
2483 if (mSwitchingDialog != null
2484 && mSwitchingDialogTitleView != null
2485 && mSwitchingDialog.isShowing()) {
2486 final Switch hardKeySwitch = (Switch)mSwitchingDialogTitleView.findViewById(
2487 com.android.internal.R.id.hard_keyboard_switch);
2488 hardKeySwitch.setChecked(mShowImeWithHardKeyboard);
2489 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002490 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002491
satokab751aa2010-09-14 19:17:36 +09002492 /* package */ void setInputMethodLocked(String id, int subtypeId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002493 InputMethodInfo info = mMethodMap.get(id);
2494 if (info == null) {
satok913a8922010-08-26 21:53:41 +09002495 throw new IllegalArgumentException("Unknown id: " + id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002496 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002497
satokd81e9502012-05-21 12:58:45 +09002498 // See if we need to notify a subtype change within the same IME.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002499 if (id.equals(mCurMethodId)) {
satokd81e9502012-05-21 12:58:45 +09002500 final int subtypeCount = info.getSubtypeCount();
2501 if (subtypeCount <= 0) {
2502 return;
satokcd7cd292010-11-20 15:46:23 +09002503 }
satokd81e9502012-05-21 12:58:45 +09002504 final InputMethodSubtype oldSubtype = mCurrentSubtype;
2505 final InputMethodSubtype newSubtype;
2506 if (subtypeId >= 0 && subtypeId < subtypeCount) {
2507 newSubtype = info.getSubtypeAt(subtypeId);
2508 } else {
2509 // If subtype is null, try to find the most applicable one from
2510 // getCurrentInputMethodSubtype.
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09002511 newSubtype = getCurrentInputMethodSubtypeLocked();
satokd81e9502012-05-21 12:58:45 +09002512 }
2513 if (newSubtype == null || oldSubtype == null) {
2514 Slog.w(TAG, "Illegal subtype state: old subtype = " + oldSubtype
2515 + ", new subtype = " + newSubtype);
2516 return;
2517 }
2518 if (newSubtype != oldSubtype) {
2519 setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true);
2520 if (mCurMethod != null) {
2521 try {
Seigo Nonakad9eb9112015-05-26 20:54:43 +09002522 updateSystemUiLocked(mCurToken, mImeWindowVis, mBackDisposition);
satokd81e9502012-05-21 12:58:45 +09002523 mCurMethod.changeInputMethodSubtype(newSubtype);
2524 } catch (RemoteException e) {
2525 Slog.w(TAG, "Failed to call changeInputMethodSubtype");
satokab751aa2010-09-14 19:17:36 +09002526 }
2527 }
2528 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002529 return;
2530 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002531
satokd81e9502012-05-21 12:58:45 +09002532 // Changing to a different IME.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002533 final long ident = Binder.clearCallingIdentity();
2534 try {
satokab751aa2010-09-14 19:17:36 +09002535 // Set a subtype to this input method.
2536 // subtypeId the name of a subtype which will be set.
satok723a27e2010-11-11 14:58:11 +09002537 setSelectedInputMethodAndSubtypeLocked(info, subtypeId, false);
2538 // mCurMethodId should be updated after setSelectedInputMethodAndSubtypeLocked()
2539 // because mCurMethodId is stored as a history in
2540 // setSelectedInputMethodAndSubtypeLocked().
2541 mCurMethodId = id;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002542
Sudheer Shankafc46e9b2016-10-21 17:55:27 -07002543 if (LocalServices.getService(ActivityManagerInternal.class).isSystemReady()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002544 Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -08002545 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002546 intent.putExtra("input_method_id", id);
Amith Yamasanicd757062012-10-19 18:23:52 -07002547 mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002548 }
Yohei Yukawa33e81792015-11-17 21:14:42 -08002549 unbindCurrentClientLocked(InputMethodClient.UNBIND_REASON_SWITCH_IME);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002550 } finally {
2551 Binder.restoreCallingIdentity(ident);
2552 }
2553 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002554
satok42c5a162011-05-26 16:46:14 +09002555 @Override
The Android Open Source Project4df24232009-03-05 14:34:35 -08002556 public boolean showSoftInput(IInputMethodClient client, int flags,
2557 ResultReceiver resultReceiver) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09002558 if (!calledFromValidUser()) {
2559 return false;
2560 }
Dianne Hackborncef65ee2010-09-30 18:27:22 -07002561 int uid = Binder.getCallingUid();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002562 long ident = Binder.clearCallingIdentity();
2563 try {
2564 synchronized (mMethodMap) {
2565 if (mCurClient == null || client == null
2566 || mCurClient.client.asBinder() != client.asBinder()) {
2567 try {
2568 // We need to check if this is the current client with
2569 // focus in the window manager, to allow this call to
2570 // be made before input is started in it.
2571 if (!mIWindowManager.inputMethodClientHasFocus(client)) {
Dianne Hackborncef65ee2010-09-30 18:27:22 -07002572 Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client);
The Android Open Source Project4df24232009-03-05 14:34:35 -08002573 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002574 }
2575 } catch (RemoteException e) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08002576 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002577 }
2578 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002579
Craig Mautnere4bbb1c2013-03-15 11:38:44 -07002580 if (DEBUG) Slog.v(TAG, "Client requesting input be shown");
The Android Open Source Project4df24232009-03-05 14:34:35 -08002581 return showCurrentInputLocked(flags, resultReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002582 }
2583 } finally {
2584 Binder.restoreCallingIdentity(ident);
2585 }
2586 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002587
The Android Open Source Project4df24232009-03-05 14:34:35 -08002588 boolean showCurrentInputLocked(int flags, ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002589 mShowRequested = true;
Anna Galusza9b278112016-01-04 11:37:37 -08002590 if (mAccessibilityRequestingNoSoftKeyboard) {
2591 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002592 }
Anna Galusza9b278112016-01-04 11:37:37 -08002593
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002594 if ((flags&InputMethodManager.SHOW_FORCED) != 0) {
2595 mShowExplicitlyRequested = true;
2596 mShowForced = true;
Anna Galusza9b278112016-01-04 11:37:37 -08002597 } else if ((flags&InputMethodManager.SHOW_IMPLICIT) == 0) {
2598 mShowExplicitlyRequested = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002599 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002600
Dianne Hackborncc278702009-09-02 23:07:23 -07002601 if (!mSystemReady) {
2602 return false;
2603 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002604
The Android Open Source Project4df24232009-03-05 14:34:35 -08002605 boolean res = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002606 if (mCurMethod != null) {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -07002607 if (DEBUG) Slog.d(TAG, "showCurrentInputLocked: mCurToken=" + mCurToken);
The Android Open Source Project4df24232009-03-05 14:34:35 -08002608 executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(
2609 MSG_SHOW_SOFT_INPUT, getImeShowFlags(), mCurMethod,
2610 resultReceiver));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002611 mInputShown = true;
Dianne Hackborn2c84cfc2011-10-31 15:39:59 -07002612 if (mHaveConnection && !mVisibleBound) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09002613 bindCurrentInputMethodService(
Yohei Yukawaa67a4592017-03-30 15:57:02 -07002614 mCurIntent, mVisibleConnection, IME_VISIBLE_BIND_FLAGS);
Dianne Hackborn2c84cfc2011-10-31 15:39:59 -07002615 mVisibleBound = true;
2616 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08002617 res = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002618 } else if (mHaveConnection && SystemClock.uptimeMillis()
satok59b424c2011-09-30 17:21:46 +09002619 >= (mLastBindTime+TIME_TO_RECONNECT)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002620 // The client has asked to have the input method shown, but
2621 // we have been sitting here too long with a connection to the
2622 // service and no interface received, so let's disconnect/connect
2623 // to try to prod things along.
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002624 EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, mCurMethodId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002625 SystemClock.uptimeMillis()-mLastBindTime,1);
satok59b424c2011-09-30 17:21:46 +09002626 Slog.w(TAG, "Force disconnect/connect to the IME in showCurrentInputLocked()");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002627 mContext.unbindService(this);
Yohei Yukawaa67a4592017-03-30 15:57:02 -07002628 bindCurrentInputMethodService(mCurIntent, this, IME_CONNECTION_BIND_FLAGS);
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09002629 } else {
2630 if (DEBUG) {
2631 Slog.d(TAG, "Can't show input: connection = " + mHaveConnection + ", time = "
2632 + ((mLastBindTime+TIME_TO_RECONNECT) - SystemClock.uptimeMillis()));
2633 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002634 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002635
The Android Open Source Project4df24232009-03-05 14:34:35 -08002636 return res;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002637 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002638
satok42c5a162011-05-26 16:46:14 +09002639 @Override
The Android Open Source Project4df24232009-03-05 14:34:35 -08002640 public boolean hideSoftInput(IInputMethodClient client, int flags,
2641 ResultReceiver resultReceiver) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09002642 if (!calledFromValidUser()) {
2643 return false;
2644 }
Dianne Hackborncef65ee2010-09-30 18:27:22 -07002645 int uid = Binder.getCallingUid();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002646 long ident = Binder.clearCallingIdentity();
2647 try {
2648 synchronized (mMethodMap) {
2649 if (mCurClient == null || client == null
2650 || mCurClient.client.asBinder() != client.asBinder()) {
2651 try {
2652 // We need to check if this is the current client with
2653 // focus in the window manager, to allow this call to
2654 // be made before input is started in it.
2655 if (!mIWindowManager.inputMethodClientHasFocus(client)) {
Dianne Hackborncef65ee2010-09-30 18:27:22 -07002656 if (DEBUG) Slog.w(TAG, "Ignoring hideSoftInput of uid "
2657 + uid + ": " + client);
The Android Open Source Project4df24232009-03-05 14:34:35 -08002658 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002659 }
2660 } catch (RemoteException e) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08002661 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002662 }
2663 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002664
Joe Onorato8a9b2202010-02-26 18:56:32 -08002665 if (DEBUG) Slog.v(TAG, "Client requesting input be hidden");
The Android Open Source Project4df24232009-03-05 14:34:35 -08002666 return hideCurrentInputLocked(flags, resultReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002667 }
2668 } finally {
2669 Binder.restoreCallingIdentity(ident);
2670 }
2671 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002672
The Android Open Source Project4df24232009-03-05 14:34:35 -08002673 boolean hideCurrentInputLocked(int flags, ResultReceiver resultReceiver) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002674 if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
2675 && (mShowExplicitlyRequested || mShowForced)) {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -07002676 if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide");
The Android Open Source Project4df24232009-03-05 14:34:35 -08002677 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002678 }
2679 if (mShowForced && (flags&InputMethodManager.HIDE_NOT_ALWAYS) != 0) {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -07002680 if (DEBUG) Slog.v(TAG, "Not hiding: forced show not cancelled by not-always hide");
The Android Open Source Project4df24232009-03-05 14:34:35 -08002681 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002682 }
Seigo Nonakaec928652015-06-10 15:31:20 +09002683
2684 // There is a chance that IMM#hideSoftInput() is called in a transient state where
2685 // IMMS#InputShown is already updated to be true whereas IMMS#mImeWindowVis is still waiting
2686 // to be updated with the new value sent from IME process. Even in such a transient state
2687 // historically we have accepted an incoming call of IMM#hideSoftInput() from the
2688 // application process as a valid request, and have even promised such a behavior with CTS
2689 // since Android Eclair. That's why we need to accept IMM#hideSoftInput() even when only
2690 // IMMS#InputShown indicates that the software keyboard is shown.
2691 // TODO: Clean up, IMMS#mInputShown, IMMS#mImeWindowVis and mShowRequested.
2692 final boolean shouldHideSoftInput = (mCurMethod != null) && (mInputShown ||
2693 (mImeWindowVis & InputMethodService.IME_ACTIVE) != 0);
The Android Open Source Project4df24232009-03-05 14:34:35 -08002694 boolean res;
Seigo Nonakaec928652015-06-10 15:31:20 +09002695 if (shouldHideSoftInput) {
2696 // The IME will report its visible state again after the following message finally
2697 // delivered to the IME process as an IPC. Hence the inconsistency between
2698 // IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in
2699 // the final state.
The Android Open Source Project4df24232009-03-05 14:34:35 -08002700 executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
2701 MSG_HIDE_SOFT_INPUT, mCurMethod, resultReceiver));
2702 res = true;
2703 } else {
2704 res = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002705 }
Dianne Hackborn2c84cfc2011-10-31 15:39:59 -07002706 if (mHaveConnection && mVisibleBound) {
2707 mContext.unbindService(mVisibleConnection);
2708 mVisibleBound = false;
2709 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002710 mInputShown = false;
2711 mShowRequested = false;
2712 mShowExplicitlyRequested = false;
2713 mShowForced = false;
The Android Open Source Project4df24232009-03-05 14:34:35 -08002714 return res;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002715 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002716
Yohei Yukawa2553e482017-12-15 15:47:33 -08002717 @NonNull
satok42c5a162011-05-26 16:46:14 +09002718 @Override
Yohei Yukawa05c25f82016-02-22 12:41:17 -08002719 public InputBindResult startInputOrWindowGainedFocus(
2720 /* @InputMethodClient.StartInputReason */ final int startInputReason,
2721 IInputMethodClient client, IBinder windowToken, int controlFlags, int softInputMode,
Yohei Yukawa74750f22016-03-22 12:54:22 -07002722 int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
Yohei Yukawacf68d522017-12-12 09:33:26 -08002723 /* @InputConnectionInspector.missingMethods */ final int missingMethods,
2724 int unverifiedTargetSdkVersion) {
Yohei Yukawa2553e482017-12-15 15:47:33 -08002725 final InputBindResult result;
Yohei Yukawa05c25f82016-02-22 12:41:17 -08002726 if (windowToken != null) {
Yohei Yukawa2553e482017-12-15 15:47:33 -08002727 result = windowGainedFocus(startInputReason, client, windowToken, controlFlags,
Yohei Yukawacf68d522017-12-12 09:33:26 -08002728 softInputMode, windowFlags, attribute, inputContext, missingMethods,
2729 unverifiedTargetSdkVersion);
Yohei Yukawa05c25f82016-02-22 12:41:17 -08002730 } else {
Yohei Yukawa2553e482017-12-15 15:47:33 -08002731 result = startInput(startInputReason, client, inputContext, missingMethods, attribute,
Yohei Yukawa19a80a12016-03-14 22:57:37 -07002732 controlFlags);
Yohei Yukawa05c25f82016-02-22 12:41:17 -08002733 }
Yohei Yukawa2553e482017-12-15 15:47:33 -08002734 if (result == null) {
2735 // This must never happen, but just in case.
2736 Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason="
2737 + InputMethodClient.getStartInputReason(startInputReason)
2738 + " windowFlags=#" + Integer.toHexString(windowFlags)
2739 + " editorInfo=" + attribute);
2740 return InputBindResult.NULL;
2741 }
2742 return result;
Yohei Yukawa05c25f82016-02-22 12:41:17 -08002743 }
2744
Yohei Yukawa2553e482017-12-15 15:47:33 -08002745 @NonNull
Yohei Yukawa05c25f82016-02-22 12:41:17 -08002746 private InputBindResult windowGainedFocus(
Yohei Yukawa35d3f372015-11-25 11:07:19 -08002747 /* @InputMethodClient.StartInputReason */ final int startInputReason,
Yohei Yukawa22dac1c2017-02-12 16:54:16 -08002748 IInputMethodClient client, IBinder windowToken, int controlFlags,
2749 /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
Yohei Yukawa19a80a12016-03-14 22:57:37 -07002750 int windowFlags, EditorInfo attribute, IInputContext inputContext,
Yohei Yukawacf68d522017-12-12 09:33:26 -08002751 /* @InputConnectionInspector.missingMethods */ final int missingMethods,
2752 int unverifiedTargetSdkVersion) {
Satoshi Kataoka8d033052012-11-19 17:30:40 +09002753 // Needs to check the validity before clearing calling identity
2754 final boolean calledFromValidUser = calledFromValidUser();
Dianne Hackborn7663d802012-02-24 13:08:49 -08002755 InputBindResult res = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002756 long ident = Binder.clearCallingIdentity();
2757 try {
2758 synchronized (mMethodMap) {
Yohei Yukawa35d3f372015-11-25 11:07:19 -08002759 if (DEBUG) Slog.v(TAG, "windowGainedFocus: reason="
2760 + InputMethodClient.getStartInputReason(startInputReason)
2761 + " client=" + client.asBinder()
2762 + " inputContext=" + inputContext
Yohei Yukawa19a80a12016-03-14 22:57:37 -07002763 + " missingMethods="
2764 + InputConnectionInspector.getMissingMethodFlagsAsString(missingMethods)
Yohei Yukawa35d3f372015-11-25 11:07:19 -08002765 + " attribute=" + attribute
Dianne Hackborn7663d802012-02-24 13:08:49 -08002766 + " controlFlags=#" + Integer.toHexString(controlFlags)
Yohei Yukawa22a89232017-02-12 16:38:59 -08002767 + " softInputMode=" + InputMethodClient.softInputModeToString(softInputMode)
Yohei Yukawacf68d522017-12-12 09:33:26 -08002768 + " windowFlags=#" + Integer.toHexString(windowFlags)
2769 + " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002770
Dianne Hackborn7663d802012-02-24 13:08:49 -08002771 ClientState cs = mClients.get(client.asBinder());
2772 if (cs == null) {
2773 throw new IllegalArgumentException("unknown client "
2774 + client.asBinder());
2775 }
2776
2777 try {
2778 if (!mIWindowManager.inputMethodClientHasFocus(cs.client)) {
2779 // Check with the window manager to make sure this client actually
2780 // has a window with focus. If not, reject. This is thread safe
2781 // because if the focus changes some time before or after, the
2782 // next client receiving focus that has any interest in input will
2783 // be calling through here after that change happens.
Yohei Yukawad0332832017-02-01 13:59:43 -08002784 if (DEBUG) {
2785 Slog.w(TAG, "Focus gain on non-focused client " + cs.client
2786 + " (uid=" + cs.uid + " pid=" + cs.pid + ")");
2787 }
Yohei Yukawa2553e482017-12-15 15:47:33 -08002788 return InputBindResult.NOT_IME_TARGET_WINDOW;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002789 }
Dianne Hackborn7663d802012-02-24 13:08:49 -08002790 } catch (RemoteException e) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002791 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002792
Satoshi Kataoka8d033052012-11-19 17:30:40 +09002793 if (!calledFromValidUser) {
2794 Slog.w(TAG, "A background user is requesting window. Hiding IME.");
2795 Slog.w(TAG, "If you want to interect with IME, you need "
2796 + "android.permission.INTERACT_ACROSS_USERS_FULL");
2797 hideCurrentInputLocked(0, null);
Yohei Yukawa2553e482017-12-15 15:47:33 -08002798 return InputBindResult.INVALID_USER;
Satoshi Kataoka8d033052012-11-19 17:30:40 +09002799 }
2800
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07002801 if (mCurFocusedWindow == windowToken) {
Yohei Yukawad0332832017-02-01 13:59:43 -08002802 if (DEBUG) {
2803 Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client
2804 + " attribute=" + attribute + ", token = " + windowToken);
2805 }
Dianne Hackborn7663d802012-02-24 13:08:49 -08002806 if (attribute != null) {
Yohei Yukawa19a80a12016-03-14 22:57:37 -07002807 return startInputUncheckedLocked(cs, inputContext, missingMethods,
Yohei Yukawa87ca8402017-02-07 00:13:14 -08002808 attribute, controlFlags, startInputReason);
Dianne Hackborn7663d802012-02-24 13:08:49 -08002809 }
Yohei Yukawa2553e482017-12-15 15:47:33 -08002810 return new InputBindResult(
2811 InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
2812 null, null, null, -1, -1);
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07002813 }
2814 mCurFocusedWindow = windowToken;
Yohei Yukawa22a89232017-02-12 16:38:59 -08002815 mCurFocusedWindowSoftInputMode = softInputMode;
Yohei Yukawae39d4ed2015-11-19 03:38:49 -08002816 mCurFocusedWindowClient = cs;
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002817
Dianne Hackborn7d3a5bc2010-11-29 22:52:12 -08002818 // Should we auto-show the IME even if the caller has not
2819 // specified what should be done with it?
2820 // We only do this automatically if the window can resize
2821 // to accommodate the IME (so what the user sees will give
2822 // them good context without input information being obscured
2823 // by the IME) or if running on a large screen where there
2824 // is more room for the target window + IME.
2825 final boolean doAutoShow =
2826 (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
2827 == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
2828 || mRes.getConfiguration().isLayoutSizeAtLeast(
2829 Configuration.SCREENLAYOUT_SIZE_LARGE);
Dianne Hackborn7663d802012-02-24 13:08:49 -08002830 final boolean isTextEditor =
2831 (controlFlags&InputMethodManager.CONTROL_WINDOW_IS_TEXT_EDITOR) != 0;
2832
2833 // We want to start input before showing the IME, but after closing
2834 // it. We want to do this after closing it to help the IME disappear
2835 // more quickly (not get stuck behind it initializing itself for the
2836 // new focused input, even if its window wants to hide the IME).
2837 boolean didStart = false;
Yohei Yukawa0f3ad12015-04-06 16:48:24 -07002838
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002839 switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) {
2840 case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
Dianne Hackborn7d3a5bc2010-11-29 22:52:12 -08002841 if (!isTextEditor || !doAutoShow) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002842 if (WindowManager.LayoutParams.mayUseInputMethod(windowFlags)) {
2843 // There is no focus view, and this window will
2844 // be behind any soft input window, so hide the
2845 // soft input window if it is shown.
Joe Onorato8a9b2202010-02-26 18:56:32 -08002846 if (DEBUG) Slog.v(TAG, "Unspecified window will hide input");
The Android Open Source Project4df24232009-03-05 14:34:35 -08002847 hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002848 }
Dianne Hackborn7d3a5bc2010-11-29 22:52:12 -08002849 } else if (isTextEditor && doAutoShow && (softInputMode &
2850 WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002851 // There is a focus view, and we are navigating forward
2852 // into the window, so show the input window for the user.
Dianne Hackborn7663d802012-02-24 13:08:49 -08002853 // We only do this automatically if the window can resize
2854 // to accommodate the IME (so what the user sees will give
Dianne Hackborn7d3a5bc2010-11-29 22:52:12 -08002855 // them good context without input information being obscured
2856 // by the IME) or if running on a large screen where there
2857 // is more room for the target window + IME.
Joe Onorato8a9b2202010-02-26 18:56:32 -08002858 if (DEBUG) Slog.v(TAG, "Unspecified window will show input");
Dianne Hackborn7663d802012-02-24 13:08:49 -08002859 if (attribute != null) {
Yohei Yukawa19a80a12016-03-14 22:57:37 -07002860 res = startInputUncheckedLocked(cs, inputContext,
Yohei Yukawa87ca8402017-02-07 00:13:14 -08002861 missingMethods, attribute, controlFlags, startInputReason);
Dianne Hackborn7663d802012-02-24 13:08:49 -08002862 didStart = true;
2863 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08002864 showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002865 }
2866 break;
2867 case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
2868 // Do nothing.
2869 break;
2870 case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN:
2871 if ((softInputMode &
2872 WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002873 if (DEBUG) Slog.v(TAG, "Window asks to hide input going forward");
The Android Open Source Project4df24232009-03-05 14:34:35 -08002874 hideCurrentInputLocked(0, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002875 }
2876 break;
2877 case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
Joe Onorato8a9b2202010-02-26 18:56:32 -08002878 if (DEBUG) Slog.v(TAG, "Window asks to hide input");
The Android Open Source Project4df24232009-03-05 14:34:35 -08002879 hideCurrentInputLocked(0, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002880 break;
2881 case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE:
2882 if ((softInputMode &
2883 WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08002884 if (DEBUG) Slog.v(TAG, "Window asks to show input going forward");
Yohei Yukawacf68d522017-12-12 09:33:26 -08002885 if (InputMethodUtils.isSoftInputModeStateVisibleAllowed(
2886 unverifiedTargetSdkVersion, controlFlags)) {
2887 if (attribute != null) {
2888 res = startInputUncheckedLocked(cs, inputContext,
2889 missingMethods, attribute, controlFlags,
2890 startInputReason);
2891 didStart = true;
2892 }
2893 showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
2894 } else {
2895 Slog.e(TAG, "SOFT_INPUT_STATE_VISIBLE is ignored because"
2896 + " there is no focused view that also returns true from"
2897 + " View#onCheckIsTextEditor()");
Dianne Hackborn7663d802012-02-24 13:08:49 -08002898 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002899 }
2900 break;
2901 case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
Joe Onorato8a9b2202010-02-26 18:56:32 -08002902 if (DEBUG) Slog.v(TAG, "Window asks to always show input");
Yohei Yukawacf68d522017-12-12 09:33:26 -08002903 if (InputMethodUtils.isSoftInputModeStateVisibleAllowed(
2904 unverifiedTargetSdkVersion, controlFlags)) {
2905 if (attribute != null) {
2906 res = startInputUncheckedLocked(cs, inputContext, missingMethods,
2907 attribute, controlFlags, startInputReason);
2908 didStart = true;
2909 }
2910 showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
2911 } else {
2912 Slog.e(TAG, "SOFT_INPUT_STATE_ALWAYS_VISIBLE is ignored because"
2913 + " there is no focused view that also returns true from"
2914 + " View#onCheckIsTextEditor()");
Dianne Hackborn7663d802012-02-24 13:08:49 -08002915 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002916 break;
2917 }
Dianne Hackborn7663d802012-02-24 13:08:49 -08002918
Tarandeep Singh2f731c552018-02-05 13:55:06 -08002919 if (!didStart) {
2920 if (attribute != null) {
2921 if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value()
2922 || (controlFlags
2923 & InputMethodManager.CONTROL_WINDOW_IS_TEXT_EDITOR) != 0) {
2924 res = startInputUncheckedLocked(cs, inputContext, missingMethods,
2925 attribute,
2926 controlFlags, startInputReason);
2927 } else {
2928 res = InputBindResult.NO_EDITOR;
2929 }
2930 } else {
2931 res = InputBindResult.NULL_EDITOR_INFO;
Tarandeep Singh75a92392018-01-12 14:58:59 -08002932 }
Dianne Hackborn7663d802012-02-24 13:08:49 -08002933 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002934 }
2935 } finally {
2936 Binder.restoreCallingIdentity(ident);
2937 }
Dianne Hackborn7663d802012-02-24 13:08:49 -08002938
2939 return res;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002940 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08002941
Guliz Tuncay6908c152017-06-02 16:06:10 -07002942 private boolean canShowInputMethodPickerLocked(IInputMethodClient client) {
2943 final int uid = Binder.getCallingUid();
2944 if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) {
2945 return true;
Tarandeep Singheb570612018-01-29 16:20:32 -08002946 } else if (mCurFocusedWindowClient != null && client != null
2947 && mCurFocusedWindowClient.client.asBinder() == client.asBinder()) {
Guliz Tuncay6908c152017-06-02 16:06:10 -07002948 return true;
2949 } else if (mCurIntent != null && InputMethodUtils.checkIfPackageBelongsToUid(
2950 mAppOpsManager,
2951 uid,
2952 mCurIntent.getComponent().getPackageName())) {
2953 return true;
2954 } else if (mContext.checkCallingPermission(
2955 android.Manifest.permission.WRITE_SECURE_SETTINGS)
2956 == PackageManager.PERMISSION_GRANTED) {
2957 return true;
2958 }
2959
2960 return false;
2961 }
2962
satok42c5a162011-05-26 16:46:14 +09002963 @Override
Seigo Nonaka14e13912015-05-06 21:04:13 -07002964 public void showInputMethodPickerFromClient(
2965 IInputMethodClient client, int auxiliarySubtypeMode) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09002966 if (!calledFromValidUser()) {
2967 return;
2968 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002969 synchronized (mMethodMap) {
Guliz Tuncay6908c152017-06-02 16:06:10 -07002970 if(!canShowInputMethodPickerLocked(client)) {
satok47a44912010-10-06 16:03:58 +09002971 Slog.w(TAG, "Ignoring showInputMethodPickerFromClient of uid "
Dianne Hackborncef65ee2010-09-30 18:27:22 -07002972 + Binder.getCallingUid() + ": " + client);
Guliz Tuncay6908c152017-06-02 16:06:10 -07002973 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002974 }
2975
satok440aab52010-11-25 09:43:11 +09002976 // Always call subtype picker, because subtype picker is a superset of input method
2977 // picker.
Seigo Nonaka14e13912015-05-06 21:04:13 -07002978 mHandler.sendMessage(mCaller.obtainMessageI(
2979 MSG_SHOW_IM_SUBTYPE_PICKER, auxiliarySubtypeMode));
satokab751aa2010-09-14 19:17:36 +09002980 }
2981 }
2982
Tarandeep Singheb570612018-01-29 16:20:32 -08002983 public boolean isInputMethodPickerShownForTest() {
2984 synchronized(mMethodMap) {
2985 if (mSwitchingDialog == null) {
2986 return false;
2987 }
2988 return mSwitchingDialog.isShowing();
2989 }
2990 }
2991
satok42c5a162011-05-26 16:46:14 +09002992 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002993 public void setInputMethod(IBinder token, String id) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09002994 if (!calledFromValidUser()) {
2995 return;
2996 }
satok28203512010-11-24 11:06:49 +09002997 setInputMethodWithSubtypeId(token, id, NOT_A_SUBTYPE_ID);
2998 }
2999
satok42c5a162011-05-26 16:46:14 +09003000 @Override
satok28203512010-11-24 11:06:49 +09003001 public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09003002 if (!calledFromValidUser()) {
3003 return;
3004 }
satok28203512010-11-24 11:06:49 +09003005 synchronized (mMethodMap) {
3006 if (subtype != null) {
Yohei Yukawa4e02bc62014-06-04 18:37:20 +09003007 setInputMethodWithSubtypeIdLocked(token, id,
3008 InputMethodUtils.getSubtypeIdFromHashCode(mMethodMap.get(id),
3009 subtype.hashCode()));
satok28203512010-11-24 11:06:49 +09003010 } else {
3011 setInputMethod(token, id);
3012 }
3013 }
satokab751aa2010-09-14 19:17:36 +09003014 }
3015
satok42c5a162011-05-26 16:46:14 +09003016 @Override
satokb416a712010-11-25 20:42:14 +09003017 public void showInputMethodAndSubtypeEnablerFromClient(
satok217f5482010-12-15 05:19:19 +09003018 IInputMethodClient client, String inputMethodId) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09003019 if (!calledFromValidUser()) {
3020 return;
3021 }
satokb416a712010-11-25 20:42:14 +09003022 synchronized (mMethodMap) {
satok7fee71f2010-12-17 18:54:26 +09003023 executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
3024 MSG_SHOW_IM_SUBTYPE_ENABLER, inputMethodId));
satokb416a712010-11-25 20:42:14 +09003025 }
3026 }
3027
satok4fc87d62011-05-20 16:13:43 +09003028 @Override
Tarandeep Singh164cfba2018-02-28 14:17:43 -08003029 public boolean switchToPreviousInputMethod(IBinder token) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09003030 if (!calledFromValidUser()) {
3031 return false;
3032 }
satok735cf382010-11-11 20:40:09 +09003033 synchronized (mMethodMap) {
satokc445bcd2011-01-25 18:57:24 +09003034 final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked();
satok4fc87d62011-05-20 16:13:43 +09003035 final InputMethodInfo lastImi;
satok208d5632011-05-20 22:13:38 +09003036 if (lastIme != null) {
satok4fc87d62011-05-20 16:13:43 +09003037 lastImi = mMethodMap.get(lastIme.first);
3038 } else {
3039 lastImi = null;
satok735cf382010-11-11 20:40:09 +09003040 }
satok4fc87d62011-05-20 16:13:43 +09003041 String targetLastImiId = null;
3042 int subtypeId = NOT_A_SUBTYPE_ID;
3043 if (lastIme != null && lastImi != null) {
3044 final boolean imiIdIsSame = lastImi.getId().equals(mCurMethodId);
Narayan Kamatha09b4d22016-04-15 18:32:45 +01003045 final int lastSubtypeHash = Integer.parseInt(lastIme.second);
satok4fc87d62011-05-20 16:13:43 +09003046 final int currentSubtypeHash = mCurrentSubtype == null ? NOT_A_SUBTYPE_ID
3047 : mCurrentSubtype.hashCode();
3048 // If the last IME is the same as the current IME and the last subtype is not
3049 // defined, there is no need to switch to the last IME.
3050 if (!imiIdIsSame || lastSubtypeHash != currentSubtypeHash) {
3051 targetLastImiId = lastIme.first;
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09003052 subtypeId = InputMethodUtils.getSubtypeIdFromHashCode(lastImi, lastSubtypeHash);
satok4fc87d62011-05-20 16:13:43 +09003053 }
3054 }
3055
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09003056 if (TextUtils.isEmpty(targetLastImiId)
3057 && !InputMethodUtils.canAddToLastInputMethod(mCurrentSubtype)) {
satok4fc87d62011-05-20 16:13:43 +09003058 // This is a safety net. If the currentSubtype can't be added to the history
3059 // and the framework couldn't find the last ime, we will make the last ime be
3060 // the most applicable enabled keyboard subtype of the system imes.
3061 final List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked();
3062 if (enabled != null) {
3063 final int N = enabled.size();
3064 final String locale = mCurrentSubtype == null
3065 ? mRes.getConfiguration().locale.toString()
3066 : mCurrentSubtype.getLocale();
3067 for (int i = 0; i < N; ++i) {
3068 final InputMethodInfo imi = enabled.get(i);
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09003069 if (imi.getSubtypeCount() > 0 && InputMethodUtils.isSystemIme(imi)) {
satok4fc87d62011-05-20 16:13:43 +09003070 InputMethodSubtype keyboardSubtype =
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09003071 InputMethodUtils.findLastResortApplicableSubtypeLocked(mRes,
3072 InputMethodUtils.getSubtypes(imi),
3073 InputMethodUtils.SUBTYPE_MODE_KEYBOARD, locale, true);
satok4fc87d62011-05-20 16:13:43 +09003074 if (keyboardSubtype != null) {
3075 targetLastImiId = imi.getId();
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09003076 subtypeId = InputMethodUtils.getSubtypeIdFromHashCode(
satok4fc87d62011-05-20 16:13:43 +09003077 imi, keyboardSubtype.hashCode());
3078 if(keyboardSubtype.getLocale().equals(locale)) {
3079 break;
3080 }
3081 }
3082 }
3083 }
3084 }
3085 }
3086
3087 if (!TextUtils.isEmpty(targetLastImiId)) {
3088 if (DEBUG) {
3089 Slog.d(TAG, "Switch to: " + lastImi.getId() + ", " + lastIme.second
3090 + ", from: " + mCurMethodId + ", " + subtypeId);
3091 }
Yohei Yukawa4e02bc62014-06-04 18:37:20 +09003092 setInputMethodWithSubtypeIdLocked(token, targetLastImiId, subtypeId);
satok4fc87d62011-05-20 16:13:43 +09003093 return true;
3094 } else {
3095 return false;
3096 }
satok735cf382010-11-11 20:40:09 +09003097 }
3098 }
3099
satoke7c6998e2011-06-03 17:57:59 +09003100 @Override
satok688bd472012-02-09 20:09:17 +09003101 public boolean switchToNextInputMethod(IBinder token, boolean onlyCurrentIme) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09003102 if (!calledFromValidUser()) {
3103 return false;
3104 }
satok688bd472012-02-09 20:09:17 +09003105 synchronized (mMethodMap) {
Yohei Yukawaa0755742014-06-04 20:28:18 +09003106 if (!calledWithValidToken(token)) {
Yohei Yukawaa0755742014-06-04 20:28:18 +09003107 return false;
3108 }
Yohei Yukawa5a647b692014-05-22 12:49:00 +09003109 final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
Yohei Yukawad39ae852016-04-10 20:28:40 -07003110 onlyCurrentIme, mMethodMap.get(mCurMethodId), mCurrentSubtype,
3111 true /* forward */);
satok688bd472012-02-09 20:09:17 +09003112 if (nextSubtype == null) {
3113 return false;
3114 }
Yohei Yukawa4e02bc62014-06-04 18:37:20 +09003115 setInputMethodWithSubtypeIdLocked(token, nextSubtype.mImi.getId(),
3116 nextSubtype.mSubtypeId);
satok688bd472012-02-09 20:09:17 +09003117 return true;
3118 }
3119 }
3120
3121 @Override
Satoshi Kataoka2b10b522013-08-21 20:39:12 +09003122 public boolean shouldOfferSwitchingToNextInputMethod(IBinder token) {
3123 if (!calledFromValidUser()) {
3124 return false;
3125 }
3126 synchronized (mMethodMap) {
Yohei Yukawaa0755742014-06-04 20:28:18 +09003127 if (!calledWithValidToken(token)) {
Yohei Yukawaa0755742014-06-04 20:28:18 +09003128 return false;
3129 }
Yohei Yukawa5a647b692014-05-22 12:49:00 +09003130 final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
Yohei Yukawad39ae852016-04-10 20:28:40 -07003131 false /* onlyCurrentIme */, mMethodMap.get(mCurMethodId), mCurrentSubtype,
3132 true /* forward */);
Satoshi Kataoka2b10b522013-08-21 20:39:12 +09003133 if (nextSubtype == null) {
3134 return false;
3135 }
3136 return true;
3137 }
3138 }
3139
3140 @Override
satok68f1b782011-04-11 14:26:04 +09003141 public InputMethodSubtype getLastInputMethodSubtype() {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09003142 if (!calledFromValidUser()) {
3143 return null;
3144 }
satok68f1b782011-04-11 14:26:04 +09003145 synchronized (mMethodMap) {
3146 final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked();
3147 // TODO: Handle the case of the last IME with no subtypes
3148 if (lastIme == null || TextUtils.isEmpty(lastIme.first)
3149 || TextUtils.isEmpty(lastIme.second)) return null;
3150 final InputMethodInfo lastImi = mMethodMap.get(lastIme.first);
3151 if (lastImi == null) return null;
3152 try {
Narayan Kamatha09b4d22016-04-15 18:32:45 +01003153 final int lastSubtypeHash = Integer.parseInt(lastIme.second);
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09003154 final int lastSubtypeId =
3155 InputMethodUtils.getSubtypeIdFromHashCode(lastImi, lastSubtypeHash);
satok0e7d7d62011-07-05 13:28:06 +09003156 if (lastSubtypeId < 0 || lastSubtypeId >= lastImi.getSubtypeCount()) {
3157 return null;
3158 }
3159 return lastImi.getSubtypeAt(lastSubtypeId);
satok68f1b782011-04-11 14:26:04 +09003160 } catch (NumberFormatException e) {
3161 return null;
3162 }
3163 }
3164 }
3165
satoke7c6998e2011-06-03 17:57:59 +09003166 @Override
satokee5e77c2011-09-02 18:50:15 +09003167 public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09003168 if (!calledFromValidUser()) {
3169 return;
3170 }
satok91e88122011-07-18 11:11:42 +09003171 // By this IPC call, only a process which shares the same uid with the IME can add
3172 // additional input method subtypes to the IME.
Yohei Yukawa70f5c482016-01-04 19:42:36 -08003173 if (TextUtils.isEmpty(imiId) || subtypes == null) return;
satoke7c6998e2011-06-03 17:57:59 +09003174 synchronized (mMethodMap) {
Yohei Yukawa79247822017-01-23 15:26:15 -08003175 if (!mSystemReady) {
3176 return;
3177 }
satok91e88122011-07-18 11:11:42 +09003178 final InputMethodInfo imi = mMethodMap.get(imiId);
satokee5e77c2011-09-02 18:50:15 +09003179 if (imi == null) return;
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09003180 final String[] packageInfos;
3181 try {
3182 packageInfos = mIPackageManager.getPackagesForUid(Binder.getCallingUid());
3183 } catch (RemoteException e) {
3184 Slog.e(TAG, "Failed to get package infos");
3185 return;
3186 }
satok91e88122011-07-18 11:11:42 +09003187 if (packageInfos != null) {
3188 final int packageNum = packageInfos.length;
3189 for (int i = 0; i < packageNum; ++i) {
3190 if (packageInfos[i].equals(imi.getPackageName())) {
3191 mFileManager.addInputMethodSubtypes(imi, subtypes);
satokc5933802011-08-31 21:26:04 +09003192 final long ident = Binder.clearCallingIdentity();
3193 try {
Yohei Yukawa94e33302016-02-12 19:37:03 -08003194 buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
satokc5933802011-08-31 21:26:04 +09003195 } finally {
3196 Binder.restoreCallingIdentity(ident);
3197 }
satokee5e77c2011-09-02 18:50:15 +09003198 return;
satok91e88122011-07-18 11:11:42 +09003199 }
3200 }
3201 }
satoke7c6998e2011-06-03 17:57:59 +09003202 }
satokee5e77c2011-09-02 18:50:15 +09003203 return;
satoke7c6998e2011-06-03 17:57:59 +09003204 }
3205
Satoshi Kataoka658c7b82013-10-10 17:03:51 +09003206 @Override
3207 public int getInputMethodWindowVisibleHeight() {
Seigo Nonaka7309b122015-08-17 18:34:13 -07003208 return mWindowManagerInternal.getInputMethodWindowVisibleHeight();
Satoshi Kataoka658c7b82013-10-10 17:03:51 +09003209 }
3210
Satoshi Kataokad7443c82013-10-15 17:45:43 +09003211 @Override
Yohei Yukawa833bdce2016-05-15 20:05:56 -07003212 public void clearLastInputMethodWindowForTransition(IBinder token) {
3213 if (!calledFromValidUser()) {
3214 return;
3215 }
Yohei Yukawafa49c002017-01-31 19:15:00 -08003216 synchronized (mMethodMap) {
3217 if (!calledWithValidToken(token)) {
Yohei Yukawafa49c002017-01-31 19:15:00 -08003218 return;
Yohei Yukawa833bdce2016-05-15 20:05:56 -07003219 }
Yohei Yukawa833bdce2016-05-15 20:05:56 -07003220 }
Yohei Yukawafa49c002017-01-31 19:15:00 -08003221 mWindowManagerInternal.clearLastInputMethodWindowForTransition();
Yohei Yukawa833bdce2016-05-15 20:05:56 -07003222 }
3223
3224 @Override
Yohei Yukawa3d1e8122014-06-06 19:12:47 +09003225 public void notifyUserAction(int sequenceNumber) {
Satoshi Kataokad7443c82013-10-15 17:45:43 +09003226 if (DEBUG) {
Yohei Yukawa3d1e8122014-06-06 19:12:47 +09003227 Slog.d(TAG, "Got the notification of a user action. sequenceNumber:" + sequenceNumber);
Satoshi Kataokad7443c82013-10-15 17:45:43 +09003228 }
Yohei Yukawa5a647b692014-05-22 12:49:00 +09003229 synchronized (mMethodMap) {
Yohei Yukawa3d1e8122014-06-06 19:12:47 +09003230 if (mCurUserActionNotificationSequenceNumber != sequenceNumber) {
3231 if (DEBUG) {
3232 Slog.d(TAG, "Ignoring the user action notification due to the sequence number "
3233 + "mismatch. expected:" + mCurUserActionNotificationSequenceNumber
3234 + " actual: " + sequenceNumber);
3235 }
3236 return;
3237 }
Yohei Yukawa5a647b692014-05-22 12:49:00 +09003238 final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
3239 if (imi != null) {
Yohei Yukawa02970512014-06-05 16:16:18 +09003240 mSwitchingController.onUserActionLocked(imi, mCurrentSubtype);
Yohei Yukawa5a647b692014-05-22 12:49:00 +09003241 }
Satoshi Kataokad7443c82013-10-15 17:45:43 +09003242 }
3243 }
3244
satok28203512010-11-24 11:06:49 +09003245 private void setInputMethodWithSubtypeId(IBinder token, String id, int subtypeId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003246 synchronized (mMethodMap) {
Yohei Yukawa4e02bc62014-06-04 18:37:20 +09003247 setInputMethodWithSubtypeIdLocked(token, id, subtypeId);
3248 }
3249 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003250
Yohei Yukawa4e02bc62014-06-04 18:37:20 +09003251 private void setInputMethodWithSubtypeIdLocked(IBinder token, String id, int subtypeId) {
3252 if (token == null) {
3253 if (mContext.checkCallingOrSelfPermission(
3254 android.Manifest.permission.WRITE_SECURE_SETTINGS)
3255 != PackageManager.PERMISSION_GRANTED) {
3256 throw new SecurityException(
3257 "Using null token requires permission "
3258 + android.Manifest.permission.WRITE_SECURE_SETTINGS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003259 }
Yohei Yukawa4e02bc62014-06-04 18:37:20 +09003260 } else if (mCurToken != token) {
3261 Slog.w(TAG, "Ignoring setInputMethod of uid " + Binder.getCallingUid()
3262 + " token: " + token);
3263 return;
3264 }
3265
3266 final long ident = Binder.clearCallingIdentity();
3267 try {
3268 setInputMethodLocked(id, subtypeId);
3269 } finally {
3270 Binder.restoreCallingIdentity(ident);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003271 }
3272 }
3273
satok42c5a162011-05-26 16:46:14 +09003274 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003275 public void hideMySoftInput(IBinder token, int flags) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09003276 if (!calledFromValidUser()) {
3277 return;
3278 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003279 synchronized (mMethodMap) {
Yohei Yukawa22c97be2014-06-04 19:43:36 +09003280 if (!calledWithValidToken(token)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003281 return;
3282 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003283 long ident = Binder.clearCallingIdentity();
3284 try {
The Android Open Source Project4df24232009-03-05 14:34:35 -08003285 hideCurrentInputLocked(flags, null);
3286 } finally {
3287 Binder.restoreCallingIdentity(ident);
3288 }
3289 }
3290 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003291
satok42c5a162011-05-26 16:46:14 +09003292 @Override
The Android Open Source Project4df24232009-03-05 14:34:35 -08003293 public void showMySoftInput(IBinder token, int flags) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09003294 if (!calledFromValidUser()) {
3295 return;
3296 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08003297 synchronized (mMethodMap) {
Yohei Yukawa22c97be2014-06-04 19:43:36 +09003298 if (!calledWithValidToken(token)) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08003299 return;
3300 }
3301 long ident = Binder.clearCallingIdentity();
3302 try {
3303 showCurrentInputLocked(flags, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003304 } finally {
3305 Binder.restoreCallingIdentity(ident);
3306 }
3307 }
3308 }
3309
3310 void setEnabledSessionInMainThread(SessionState session) {
3311 if (mEnabledSession != session) {
Yohei Yukawa9d91b432014-05-19 16:03:24 +09003312 if (mEnabledSession != null && mEnabledSession.session != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003313 try {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003314 if (DEBUG) Slog.v(TAG, "Disabling: " + mEnabledSession);
Yohei Yukawa9d91b432014-05-19 16:03:24 +09003315 mEnabledSession.method.setSessionEnabled(mEnabledSession.session, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003316 } catch (RemoteException e) {
3317 }
3318 }
3319 mEnabledSession = session;
Yohei Yukawa9d91b432014-05-19 16:03:24 +09003320 if (mEnabledSession != null && mEnabledSession.session != null) {
3321 try {
3322 if (DEBUG) Slog.v(TAG, "Enabling: " + mEnabledSession);
3323 mEnabledSession.method.setSessionEnabled(mEnabledSession.session, true);
3324 } catch (RemoteException e) {
3325 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003326 }
3327 }
3328 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003329
Yohei Yukawa930328c2017-10-18 20:19:53 -07003330 @MainThread
satok42c5a162011-05-26 16:46:14 +09003331 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003332 public boolean handleMessage(Message msg) {
Svetoslav Ganov758143e2012-08-06 16:40:27 -07003333 SomeArgs args;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003334 switch (msg.what) {
satokab751aa2010-09-14 19:17:36 +09003335 case MSG_SHOW_IM_SUBTYPE_PICKER:
Seigo Nonaka14e13912015-05-06 21:04:13 -07003336 final boolean showAuxSubtypes;
3337 switch (msg.arg1) {
3338 case InputMethodManager.SHOW_IM_PICKER_MODE_AUTO:
3339 // This is undocumented so far, but IMM#showInputMethodPicker() has been
3340 // implemented so that auxiliary subtypes will be excluded when the soft
3341 // keyboard is invisible.
3342 showAuxSubtypes = mInputShown;
3343 break;
3344 case InputMethodManager.SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES:
3345 showAuxSubtypes = true;
3346 break;
3347 case InputMethodManager.SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES:
3348 showAuxSubtypes = false;
3349 break;
3350 default:
3351 Slog.e(TAG, "Unknown subtype picker mode = " + msg.arg1);
3352 return false;
3353 }
3354 showInputMethodMenu(showAuxSubtypes);
satokab751aa2010-09-14 19:17:36 +09003355 return true;
3356
satok47a44912010-10-06 16:03:58 +09003357 case MSG_SHOW_IM_SUBTYPE_ENABLER:
Yohei Yukawa41f34272015-12-14 15:41:52 -08003358 showInputMethodAndSubtypeEnabler((String)msg.obj);
satok217f5482010-12-15 05:19:19 +09003359 return true;
3360
3361 case MSG_SHOW_IM_CONFIG:
3362 showConfigureInputMethods();
satok47a44912010-10-06 16:03:58 +09003363 return true;
3364
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003365 // ---------------------------------------------------------
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003366
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003367 case MSG_UNBIND_INPUT:
3368 try {
3369 ((IInputMethod)msg.obj).unbindInput();
3370 } catch (RemoteException e) {
3371 // There is nothing interesting about the method dying.
3372 }
3373 return true;
3374 case MSG_BIND_INPUT:
Svetoslav Ganov758143e2012-08-06 16:40:27 -07003375 args = (SomeArgs)msg.obj;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003376 try {
3377 ((IInputMethod)args.arg1).bindInput((InputBinding)args.arg2);
3378 } catch (RemoteException e) {
3379 }
Svetoslav Ganov758143e2012-08-06 16:40:27 -07003380 args.recycle();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003381 return true;
3382 case MSG_SHOW_SOFT_INPUT:
Svetoslav Ganov758143e2012-08-06 16:40:27 -07003383 args = (SomeArgs)msg.obj;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003384 try {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -07003385 if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".showSoftInput("
Craig Mautner6efb4c72013-03-13 10:17:41 -07003386 + msg.arg1 + ", " + args.arg2 + ")");
Craig Mautnerca0ac712013-03-14 09:43:02 -07003387 ((IInputMethod)args.arg1).showSoftInput(msg.arg1, (ResultReceiver)args.arg2);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003388 } catch (RemoteException e) {
3389 }
Svetoslav Ganov758143e2012-08-06 16:40:27 -07003390 args.recycle();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003391 return true;
3392 case MSG_HIDE_SOFT_INPUT:
Svetoslav Ganov758143e2012-08-06 16:40:27 -07003393 args = (SomeArgs)msg.obj;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003394 try {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -07003395 if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".hideSoftInput(0, "
Craig Mautner6efb4c72013-03-13 10:17:41 -07003396 + args.arg2 + ")");
Craig Mautnerca0ac712013-03-14 09:43:02 -07003397 ((IInputMethod)args.arg1).hideSoftInput(0, (ResultReceiver)args.arg2);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003398 } catch (RemoteException e) {
3399 }
Svetoslav Ganov758143e2012-08-06 16:40:27 -07003400 args.recycle();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003401 return true;
Jorim Jaggi3c5d0f12016-05-24 19:04:30 -07003402 case MSG_HIDE_CURRENT_INPUT_METHOD:
3403 synchronized (mMethodMap) {
3404 hideCurrentInputLocked(0, null);
3405 }
3406 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003407 case MSG_ATTACH_TOKEN:
Svetoslav Ganov758143e2012-08-06 16:40:27 -07003408 args = (SomeArgs)msg.obj;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003409 try {
Craig Mautnere4bbb1c2013-03-15 11:38:44 -07003410 if (DEBUG) Slog.v(TAG, "Sending attach of token: " + args.arg2);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003411 ((IInputMethod)args.arg1).attachToken((IBinder)args.arg2);
3412 } catch (RemoteException e) {
3413 }
Svetoslav Ganov758143e2012-08-06 16:40:27 -07003414 args.recycle();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003415 return true;
Jeff Brownc28867a2013-03-26 15:42:39 -07003416 case MSG_CREATE_SESSION: {
Svetoslav Ganov758143e2012-08-06 16:40:27 -07003417 args = (SomeArgs)msg.obj;
Jeff Brown1951ce82013-04-04 22:45:12 -07003418 IInputMethod method = (IInputMethod)args.arg1;
Jeff Brownc28867a2013-03-26 15:42:39 -07003419 InputChannel channel = (InputChannel)args.arg2;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003420 try {
Jeff Brown1951ce82013-04-04 22:45:12 -07003421 method.createSession(channel, (IInputSessionCallback)args.arg3);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003422 } catch (RemoteException e) {
Jeff Brownc28867a2013-03-26 15:42:39 -07003423 } finally {
Jeff Brown1951ce82013-04-04 22:45:12 -07003424 // Dispose the channel if the input method is not local to this process
3425 // because the remote proxy will get its own copy when unparceled.
3426 if (channel != null && Binder.isProxy(method)) {
Jeff Brownc28867a2013-03-26 15:42:39 -07003427 channel.dispose();
3428 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003429 }
Svetoslav Ganov758143e2012-08-06 16:40:27 -07003430 args.recycle();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003431 return true;
Jeff Brownc28867a2013-03-26 15:42:39 -07003432 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003433 // ---------------------------------------------------------
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003434
Yohei Yukawa19a80a12016-03-14 22:57:37 -07003435 case MSG_START_INPUT: {
Yohei Yukawaf7526b52017-02-11 20:57:10 -08003436 final int missingMethods = msg.arg1;
3437 final boolean restarting = msg.arg2 != 0;
Yohei Yukawa19a80a12016-03-14 22:57:37 -07003438 args = (SomeArgs) msg.obj;
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -08003439 final IBinder startInputToken = (IBinder) args.arg1;
3440 final SessionState session = (SessionState) args.arg2;
3441 final IInputContext inputContext = (IInputContext) args.arg3;
3442 final EditorInfo editorInfo = (EditorInfo) args.arg4;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003443 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003444 setEnabledSessionInMainThread(session);
Yohei Yukawa6db3bfe2017-02-13 12:04:41 -08003445 session.method.startInput(startInputToken, inputContext, missingMethods,
3446 editorInfo, restarting);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003447 } catch (RemoteException e) {
3448 }
Svetoslav Ganov758143e2012-08-06 16:40:27 -07003449 args.recycle();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003450 return true;
Yohei Yukawa19a80a12016-03-14 22:57:37 -07003451 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003452
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003453 // ---------------------------------------------------------
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003454
Yohei Yukawa33e81792015-11-17 21:14:42 -08003455 case MSG_UNBIND_CLIENT:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003456 try {
Yohei Yukawa33e81792015-11-17 21:14:42 -08003457 ((IInputMethodClient)msg.obj).onUnbindMethod(msg.arg1, msg.arg2);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003458 } catch (RemoteException e) {
3459 // There is nothing interesting about the last client dying.
3460 }
3461 return true;
Yohei Yukawa33e81792015-11-17 21:14:42 -08003462 case MSG_BIND_CLIENT: {
Svetoslav Ganov758143e2012-08-06 16:40:27 -07003463 args = (SomeArgs)msg.obj;
Jeff Brown1951ce82013-04-04 22:45:12 -07003464 IInputMethodClient client = (IInputMethodClient)args.arg1;
3465 InputBindResult res = (InputBindResult)args.arg2;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003466 try {
Jeff Brown1951ce82013-04-04 22:45:12 -07003467 client.onBindMethod(res);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003468 } catch (RemoteException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003469 Slog.w(TAG, "Client died receiving input method " + args.arg2);
Jeff Brown1951ce82013-04-04 22:45:12 -07003470 } finally {
3471 // Dispose the channel if the input method is not local to this process
3472 // because the remote proxy will get its own copy when unparceled.
3473 if (res.channel != null && Binder.isProxy(client)) {
3474 res.channel.dispose();
3475 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003476 }
Svetoslav Ganov758143e2012-08-06 16:40:27 -07003477 args.recycle();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003478 return true;
Jeff Brown1951ce82013-04-04 22:45:12 -07003479 }
Dianne Hackborna6e41342012-05-22 16:30:34 -07003480 case MSG_SET_ACTIVE:
3481 try {
Yohei Yukawa2bc66172017-02-08 11:13:25 -08003482 ((ClientState)msg.obj).client.setActive(msg.arg1 != 0, msg.arg2 != 0);
Dianne Hackborna6e41342012-05-22 16:30:34 -07003483 } catch (RemoteException e) {
3484 Slog.w(TAG, "Got RemoteException sending setActive(false) notification to pid "
3485 + ((ClientState)msg.obj).pid + " uid "
3486 + ((ClientState)msg.obj).uid);
3487 }
3488 return true;
Yohei Yukawafa6e0a82015-07-23 15:08:59 -07003489 case MSG_SET_INTERACTIVE:
3490 handleSetInteractive(msg.arg1 != 0);
3491 return true;
Tarandeep Singh89a6c482017-11-21 14:26:11 -08003492 case MSG_START_VR_INPUT:
3493 startVrInputMethodNoCheck((ComponentName) msg.obj);
3494 return true;
Yohei Yukawaae61f712015-12-09 13:00:10 -08003495 case MSG_SWITCH_IME:
3496 handleSwitchInputMethod(msg.arg1 != 0);
3497 return true;
Yohei Yukawa3d1e8122014-06-06 19:12:47 +09003498 case MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER: {
3499 final int sequenceNumber = msg.arg1;
Yohei Yukawa080fa342014-08-31 16:10:05 -07003500 final ClientState clientState = (ClientState)msg.obj;
Yohei Yukawa3d1e8122014-06-06 19:12:47 +09003501 try {
Yohei Yukawa080fa342014-08-31 16:10:05 -07003502 clientState.client.setUserActionNotificationSequenceNumber(sequenceNumber);
Yohei Yukawa3d1e8122014-06-06 19:12:47 +09003503 } catch (RemoteException e) {
3504 Slog.w(TAG, "Got RemoteException sending "
3505 + "setUserActionNotificationSequenceNumber("
3506 + sequenceNumber + ") notification to pid "
Yohei Yukawa080fa342014-08-31 16:10:05 -07003507 + clientState.pid + " uid "
3508 + clientState.uid);
Yohei Yukawa3d1e8122014-06-06 19:12:47 +09003509 }
3510 return true;
3511 }
Yohei Yukawa2bc66172017-02-08 11:13:25 -08003512 case MSG_REPORT_FULLSCREEN_MODE: {
3513 final boolean fullscreen = msg.arg1 != 0;
3514 final ClientState clientState = (ClientState)msg.obj;
3515 try {
3516 clientState.client.reportFullscreenMode(fullscreen);
3517 } catch (RemoteException e) {
3518 Slog.w(TAG, "Got RemoteException sending "
3519 + "reportFullscreen(" + fullscreen + ") notification to pid="
3520 + clientState.pid + " uid=" + clientState.uid);
3521 }
3522 return true;
3523 }
satok01038492012-04-09 21:08:27 +09003524
3525 // --------------------------------------------------------------
3526 case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
Michael Wright7b5a96b2014-08-09 19:28:42 -07003527 mHardKeyboardListener.handleHardKeyboardStatusChange(msg.arg1 == 1);
satok01038492012-04-09 21:08:27 +09003528 return true;
Fyodor Kupolov7877b8a2016-06-29 14:39:19 -07003529 case MSG_SYSTEM_UNLOCK_USER:
3530 final int userId = msg.arg1;
3531 onUnlockUser(userId);
3532 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003533 }
3534 return false;
3535 }
3536
Yohei Yukawafa6e0a82015-07-23 15:08:59 -07003537 private void handleSetInteractive(final boolean interactive) {
3538 synchronized (mMethodMap) {
3539 mIsInteractive = interactive;
3540 updateSystemUiLocked(mCurToken, interactive ? mImeWindowVis : 0, mBackDisposition);
3541
3542 // Inform the current client of the change in active status
3543 if (mCurClient != null && mCurClient.client != null) {
Yohei Yukawa2bc66172017-02-08 11:13:25 -08003544 executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO(
3545 MSG_SET_ACTIVE, mIsInteractive ? 1 : 0, mInFullscreenMode ? 1 : 0,
3546 mCurClient));
Yohei Yukawafa6e0a82015-07-23 15:08:59 -07003547 }
3548 }
3549 }
3550
Yohei Yukawaae61f712015-12-09 13:00:10 -08003551 private void handleSwitchInputMethod(final boolean forwardDirection) {
3552 synchronized (mMethodMap) {
Yohei Yukawaae61f712015-12-09 13:00:10 -08003553 final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
Yohei Yukawad39ae852016-04-10 20:28:40 -07003554 false, mMethodMap.get(mCurMethodId), mCurrentSubtype, forwardDirection);
Yohei Yukawaae61f712015-12-09 13:00:10 -08003555 if (nextSubtype == null) {
3556 return;
3557 }
3558 setInputMethodLocked(nextSubtype.mImi.getId(), nextSubtype.mSubtypeId);
Yohei Yukawaebda7d72016-04-02 17:39:23 -07003559 final InputMethodInfo newInputMethodInfo = mMethodMap.get(mCurMethodId);
3560 if (newInputMethodInfo == null) {
3561 return;
3562 }
3563 final CharSequence toastText = InputMethodUtils.getImeAndSubtypeDisplayName(mContext,
3564 newInputMethodInfo, mCurrentSubtype);
3565 if (!TextUtils.isEmpty(toastText)) {
Yohei Yukawab2f901a2016-04-12 01:19:31 -07003566 if (mSubtypeSwitchedByShortCutToast == null) {
3567 mSubtypeSwitchedByShortCutToast = Toast.makeText(mContext, toastText,
3568 Toast.LENGTH_SHORT);
3569 } else {
3570 mSubtypeSwitchedByShortCutToast.setText(toastText);
3571 }
Yohei Yukawaebda7d72016-04-02 17:39:23 -07003572 mSubtypeSwitchedByShortCutToast.show();
3573 }
Yohei Yukawaae61f712015-12-09 13:00:10 -08003574 }
3575 }
3576
satokdc9ddae2011-10-06 12:22:36 +09003577 private boolean chooseNewDefaultIMELocked() {
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09003578 final InputMethodInfo imi = InputMethodUtils.getMostApplicableDefaultIME(
3579 mSettings.getEnabledInputMethodListLocked());
satokdc9ddae2011-10-06 12:22:36 +09003580 if (imi != null) {
satok03eb319a2010-11-11 18:17:42 +09003581 if (DEBUG) {
3582 Slog.d(TAG, "New default IME was selected: " + imi.getId());
3583 }
satok723a27e2010-11-11 14:58:11 +09003584 resetSelectedInputMethodAndSubtypeLocked(imi.getId());
Brandon Ballinger6da35a02009-10-21 00:38:13 -07003585 return true;
3586 }
3587
3588 return false;
3589 }
3590
Andreas Gampea36dc622018-02-05 17:19:22 -08003591 @GuardedBy("mMethodMap")
Yohei Yukawa94e33302016-02-12 19:37:03 -08003592 void buildInputMethodListLocked(boolean resetDefaultEnabledIme) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09003593 if (DEBUG) {
Satoshi Kataokaf1367b72013-01-25 17:20:12 +09003594 Slog.d(TAG, "--- re-buildInputMethodList reset = " + resetDefaultEnabledIme
Seigo Nonakae27dc2b2015-08-14 18:21:27 -07003595 + " \n ------ caller=" + Debug.getCallers(10));
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09003596 }
Yohei Yukawa79247822017-01-23 15:26:15 -08003597 if (!mSystemReady) {
3598 Slog.e(TAG, "buildInputMethodListLocked is not allowed until system is ready");
3599 return;
3600 }
Yohei Yukawa94e33302016-02-12 19:37:03 -08003601 mMethodList.clear();
3602 mMethodMap.clear();
Yohei Yukawae0733062017-02-09 22:49:35 -08003603 mMethodMapUpdateCount++;
Yohei Yukawa5e3e8a52017-02-14 20:05:17 -08003604 mMyPackageMonitor.clearKnownImePackageNamesLocked();
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003605
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09003606 // Use for queryIntentServicesAsUser
3607 final PackageManager pm = mContext.getPackageManager();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003608
Yohei Yukawaed4952a2016-02-17 07:57:25 -08003609 // Note: We do not specify PackageManager.MATCH_ENCRYPTION_* flags here because the default
3610 // behavior of PackageManager is exactly what we want. It by default picks up appropriate
3611 // services depending on the unlock state for the specified user.
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09003612 final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003613 new Intent(InputMethod.SERVICE_INTERFACE),
Dianne Hackbornfd7aded2013-01-22 17:10:23 -08003614 PackageManager.GET_META_DATA | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
3615 mSettings.getCurrentUserId());
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003616
Yohei Yukawaddad4b92017-02-02 01:46:13 -08003617 final HashMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
satoke7c6998e2011-06-03 17:57:59 +09003618 mFileManager.getAllAdditionalInputMethodSubtypes();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003619 for (int i = 0; i < services.size(); ++i) {
3620 ResolveInfo ri = services.get(i);
3621 ServiceInfo si = ri.serviceInfo;
Yohei Yukawaddad4b92017-02-02 01:46:13 -08003622 final String imeId = InputMethodInfo.computeId(ri);
3623 if (!android.Manifest.permission.BIND_INPUT_METHOD.equals(si.permission)) {
3624 Slog.w(TAG, "Skipping input method " + imeId
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003625 + ": it does not require the permission "
3626 + android.Manifest.permission.BIND_INPUT_METHOD);
3627 continue;
3628 }
3629
Yohei Yukawaddad4b92017-02-02 01:46:13 -08003630 if (DEBUG) Slog.d(TAG, "Checking " + imeId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003631
Yohei Yukawaddad4b92017-02-02 01:46:13 -08003632 final List<InputMethodSubtype> additionalSubtypes = additionalSubtypeMap.get(imeId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003633 try {
satoke7c6998e2011-06-03 17:57:59 +09003634 InputMethodInfo p = new InputMethodInfo(mContext, ri, additionalSubtypes);
Yohei Yukawa94e33302016-02-12 19:37:03 -08003635 mMethodList.add(p);
Amith Yamasanie861ec12010-03-24 21:39:27 -07003636 final String id = p.getId();
Yohei Yukawa94e33302016-02-12 19:37:03 -08003637 mMethodMap.put(id, p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003638
3639 if (DEBUG) {
Satoshi Kataokaf1367b72013-01-25 17:20:12 +09003640 Slog.d(TAG, "Found an input method " + p);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003641 }
Tadashi G. Takaoka3c23d5b2016-09-16 11:41:07 +09003642 } catch (Exception e) {
Yohei Yukawaddad4b92017-02-02 01:46:13 -08003643 Slog.wtf(TAG, "Unable to load input method " + imeId, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003644 }
3645 }
Brandon Ballinger6da35a02009-10-21 00:38:13 -07003646
Yohei Yukawac4e44912017-02-09 19:30:22 -08003647 // Construct the set of possible IME packages for onPackageChanged() to avoid false
3648 // negatives when the package state remains to be the same but only the component state is
3649 // changed.
3650 {
3651 // Here we intentionally use PackageManager.MATCH_DISABLED_COMPONENTS since the purpose
3652 // of this query is to avoid false negatives. PackageManager.MATCH_ALL could be more
3653 // conservative, but it seems we cannot use it for now (Issue 35176630).
3654 final List<ResolveInfo> allInputMethodServices = pm.queryIntentServicesAsUser(
3655 new Intent(InputMethod.SERVICE_INTERFACE),
3656 PackageManager.MATCH_DISABLED_COMPONENTS, mSettings.getCurrentUserId());
3657 final int N = allInputMethodServices.size();
3658 for (int i = 0; i < N; ++i) {
3659 final ServiceInfo si = allInputMethodServices.get(i).serviceInfo;
Yohei Yukawa5e3e8a52017-02-14 20:05:17 -08003660 if (android.Manifest.permission.BIND_INPUT_METHOD.equals(si.permission)) {
3661 mMyPackageMonitor.addKnownImePackageNameLocked(si.packageName);
Yohei Yukawac4e44912017-02-09 19:30:22 -08003662 }
Yohei Yukawac4e44912017-02-09 19:30:22 -08003663 }
3664 }
3665
Yohei Yukawa9c372192018-03-20 22:54:56 -07003666 boolean reenableMinimumNonAuxSystemImes = false;
Yohei Yukawa859df052016-02-17 07:56:46 -08003667 // TODO: The following code should find better place to live.
3668 if (!resetDefaultEnabledIme) {
3669 boolean enabledImeFound = false;
Yohei Yukawa9c372192018-03-20 22:54:56 -07003670 boolean enabledNonAuxImeFound = false;
Yohei Yukawa859df052016-02-17 07:56:46 -08003671 final List<InputMethodInfo> enabledImes = mSettings.getEnabledInputMethodListLocked();
3672 final int N = enabledImes.size();
3673 for (int i = 0; i < N; ++i) {
3674 final InputMethodInfo imi = enabledImes.get(i);
3675 if (mMethodList.contains(imi)) {
3676 enabledImeFound = true;
Yohei Yukawa9c372192018-03-20 22:54:56 -07003677 if (!imi.isAuxiliaryIme()) {
3678 enabledNonAuxImeFound = true;
3679 break;
3680 }
Yohei Yukawa859df052016-02-17 07:56:46 -08003681 }
3682 }
3683 if (!enabledImeFound) {
Yohei Yukawad0332832017-02-01 13:59:43 -08003684 if (DEBUG) {
3685 Slog.i(TAG, "All the enabled IMEs are gone. Reset default enabled IMEs.");
3686 }
Yohei Yukawa859df052016-02-17 07:56:46 -08003687 resetDefaultEnabledIme = true;
3688 resetSelectedInputMethodAndSubtypeLocked("");
Yohei Yukawa9c372192018-03-20 22:54:56 -07003689 } else if (!enabledNonAuxImeFound) {
3690 if (DEBUG) {
3691 Slog.i(TAG, "All the enabled non-Aux IMEs are gone. Do partial reset.");
3692 }
3693 reenableMinimumNonAuxSystemImes = true;
Yohei Yukawa859df052016-02-17 07:56:46 -08003694 }
3695 }
3696
Yohei Yukawa9c372192018-03-20 22:54:56 -07003697 if (resetDefaultEnabledIme || reenableMinimumNonAuxSystemImes) {
Satoshi Kataokaf1367b72013-01-25 17:20:12 +09003698 final ArrayList<InputMethodInfo> defaultEnabledIme =
Yohei Yukawa9c372192018-03-20 22:54:56 -07003699 InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList,
3700 reenableMinimumNonAuxSystemImes);
Yohei Yukawa68645a62016-02-17 07:54:20 -08003701 final int N = defaultEnabledIme.size();
3702 for (int i = 0; i < N; ++i) {
Satoshi Kataokaf1367b72013-01-25 17:20:12 +09003703 final InputMethodInfo imi = defaultEnabledIme.get(i);
3704 if (DEBUG) {
3705 Slog.d(TAG, "--- enable ime = " + imi);
3706 }
3707 setInputMethodEnabledLocked(imi.getId(), true);
3708 }
3709 }
3710
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09003711 final String defaultImiId = mSettings.getSelectedInputMethod();
satok0a1bcf42012-05-16 19:26:31 +09003712 if (!TextUtils.isEmpty(defaultImiId)) {
Yohei Yukawa94e33302016-02-12 19:37:03 -08003713 if (!mMethodMap.containsKey(defaultImiId)) {
satok0a1bcf42012-05-16 19:26:31 +09003714 Slog.w(TAG, "Default IME is uninstalled. Choose new default IME.");
3715 if (chooseNewDefaultIMELocked()) {
Michael Wright7b5a96b2014-08-09 19:28:42 -07003716 updateInputMethodsFromSettingsLocked(true);
satok0a1bcf42012-05-16 19:26:31 +09003717 }
3718 } else {
3719 // Double check that the default IME is certainly enabled.
3720 setInputMethodEnabledLocked(defaultImiId, true);
Brandon Ballinger6da35a02009-10-21 00:38:13 -07003721 }
3722 }
Yohei Yukawa3d46bab2014-05-30 18:10:18 +09003723 // Here is not the perfect place to reset the switching controller. Ideally
3724 // mSwitchingController and mSettings should be able to share the same state.
3725 // TODO: Make sure that mSwitchingController and mSettings are sharing the
3726 // the same enabled IMEs list.
Yohei Yukawac834a252014-05-21 22:42:32 +09003727 mSwitchingController.resetCircularListLocked(mContext);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003728 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003729
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003730 // ----------------------------------------------------------------------
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003731
satok217f5482010-12-15 05:19:19 +09003732 private void showInputMethodAndSubtypeEnabler(String inputMethodId) {
Tadashi G. Takaokaf49688f2011-01-20 17:56:13 +09003733 Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS);
satok47a44912010-10-06 16:03:58 +09003734 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
satok86417ea2010-10-27 14:11:03 +09003735 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
3736 | Intent.FLAG_ACTIVITY_CLEAR_TOP);
satok7fee71f2010-12-17 18:54:26 +09003737 if (!TextUtils.isEmpty(inputMethodId)) {
Tadashi G. Takaoka25480202011-01-20 23:13:02 +09003738 intent.putExtra(Settings.EXTRA_INPUT_METHOD_ID, inputMethodId);
satok7fee71f2010-12-17 18:54:26 +09003739 }
Yohei Yukawa41f34272015-12-14 15:41:52 -08003740 final int userId;
3741 synchronized (mMethodMap) {
3742 userId = mSettings.getCurrentUserId();
3743 }
3744 mContext.startActivityAsUser(intent, null, UserHandle.of(userId));
satok217f5482010-12-15 05:19:19 +09003745 }
3746
3747 private void showConfigureInputMethods() {
3748 Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS);
3749 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
3750 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
3751 | Intent.FLAG_ACTIVITY_CLEAR_TOP);
Satoshi Kataoka3ba439d2012-10-05 18:30:13 +09003752 mContext.startActivityAsUser(intent, null, UserHandle.CURRENT);
satok47a44912010-10-06 16:03:58 +09003753 }
3754
satok2c93efc2012-04-02 19:33:47 +09003755 private boolean isScreenLocked() {
3756 return mKeyguardManager != null
3757 && mKeyguardManager.isKeyguardLocked() && mKeyguardManager.isKeyguardSecure();
3758 }
Tadashi G. Takaokad130b802014-08-01 14:05:47 +09003759
Seigo Nonaka14e13912015-05-06 21:04:13 -07003760 private void showInputMethodMenu(boolean showAuxSubtypes) {
3761 if (DEBUG) Slog.v(TAG, "Show switching menu. showAuxSubtypes=" + showAuxSubtypes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003762
satok2c93efc2012-04-02 19:33:47 +09003763 final boolean isScreenLocked = isScreenLocked();
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003764
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09003765 final String lastInputMethodId = mSettings.getSelectedInputMethod();
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09003766 int lastInputMethodSubtypeId = mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId);
Joe Onorato8a9b2202010-02-26 18:56:32 -08003767 if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003768
Dianne Hackborn8cf1bcd2010-03-16 13:06:10 -07003769 synchronized (mMethodMap) {
satokbb4aa062011-01-19 21:40:27 +09003770 final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis =
Satoshi Kataokad787f692013-10-26 04:44:21 +09003771 mSettings.getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked(
3772 mContext);
satok7f35c8c2010-10-07 21:13:11 +09003773 if (immis == null || immis.size() == 0) {
3774 return;
3775 }
3776
Dianne Hackborn8cf1bcd2010-03-16 13:06:10 -07003777 hideInputMethodMenuLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003778
satok688bd472012-02-09 20:09:17 +09003779 final List<ImeSubtypeListItem> imList =
Yohei Yukawa5a647b692014-05-22 12:49:00 +09003780 mSwitchingController.getSortedInputMethodAndSubtypeListLocked(
Yohei Yukawa5f8e7312015-12-10 00:58:55 -08003781 showAuxSubtypes, isScreenLocked);
satok913a8922010-08-26 21:53:41 +09003782
satokc3690562012-01-10 20:14:43 +09003783 if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09003784 final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtypeLocked();
satokc3690562012-01-10 20:14:43 +09003785 if (currentSubtype != null) {
3786 final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId);
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09003787 lastInputMethodSubtypeId = InputMethodUtils.getSubtypeIdFromHashCode(
3788 currentImi, currentSubtype.hashCode());
satokc3690562012-01-10 20:14:43 +09003789 }
3790 }
3791
Ken Wakasa761eb372011-03-04 19:06:18 +09003792 final int N = imList.size();
satokab751aa2010-09-14 19:17:36 +09003793 mIms = new InputMethodInfo[N];
3794 mSubtypeIds = new int[N];
Dianne Hackborn8cf1bcd2010-03-16 13:06:10 -07003795 int checkedItem = 0;
3796 for (int i = 0; i < N; ++i) {
Ken Wakasa05dbb652011-08-22 15:22:43 +09003797 final ImeSubtypeListItem item = imList.get(i);
3798 mIms[i] = item.mImi;
3799 mSubtypeIds[i] = item.mSubtypeId;
Dianne Hackborn8cf1bcd2010-03-16 13:06:10 -07003800 if (mIms[i].getId().equals(lastInputMethodId)) {
satokab751aa2010-09-14 19:17:36 +09003801 int subtypeId = mSubtypeIds[i];
3802 if ((subtypeId == NOT_A_SUBTYPE_ID)
3803 || (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID && subtypeId == 0)
3804 || (subtypeId == lastInputMethodSubtypeId)) {
3805 checkedItem = i;
3806 }
Dianne Hackborn8cf1bcd2010-03-16 13:06:10 -07003807 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003808 }
Alan Viverette505e3ab2014-11-24 15:22:11 -08003809
Andrew Sapperstein8a3b4cb2017-04-28 14:35:31 -07003810 final Context settingsContext = new ContextThemeWrapper(
3811 ActivityThread.currentActivityThread().getSystemUiContext(),
Alan Viverette505e3ab2014-11-24 15:22:11 -08003812 com.android.internal.R.style.Theme_DeviceDefault_Settings);
3813
3814 mDialogBuilder = new AlertDialog.Builder(settingsContext);
Tadashi G. Takaokad130b802014-08-01 14:05:47 +09003815 mDialogBuilder.setOnCancelListener(new OnCancelListener() {
3816 @Override
3817 public void onCancel(DialogInterface dialog) {
3818 hideInputMethodMenu();
3819 }
3820 });
Alan Viverette505e3ab2014-11-24 15:22:11 -08003821
3822 final Context dialogContext = mDialogBuilder.getContext();
3823 final TypedArray a = dialogContext.obtainStyledAttributes(null,
3824 com.android.internal.R.styleable.DialogPreference,
3825 com.android.internal.R.attr.alertDialogStyle, 0);
3826 final Drawable dialogIcon = a.getDrawable(
3827 com.android.internal.R.styleable.DialogPreference_dialogIcon);
3828 a.recycle();
3829
3830 mDialogBuilder.setIcon(dialogIcon);
3831
Yohei Yukawad34e1482016-02-11 08:03:52 -08003832 final LayoutInflater inflater = dialogContext.getSystemService(LayoutInflater.class);
satok01038492012-04-09 21:08:27 +09003833 final View tv = inflater.inflate(
3834 com.android.internal.R.layout.input_method_switch_dialog_title, null);
3835 mDialogBuilder.setCustomTitle(tv);
3836
3837 // Setup layout for a toggle switch of the hardware keyboard
3838 mSwitchingDialogTitleView = tv;
Tadashi G. Takaokad130b802014-08-01 14:05:47 +09003839 mSwitchingDialogTitleView
3840 .findViewById(com.android.internal.R.id.hard_keyboard_section)
Seigo Nonaka7309b122015-08-17 18:34:13 -07003841 .setVisibility(mWindowManagerInternal.isHardKeyboardAvailable()
Tadashi G. Takaokad130b802014-08-01 14:05:47 +09003842 ? View.VISIBLE : View.GONE);
Alan Viverette505e3ab2014-11-24 15:22:11 -08003843 final Switch hardKeySwitch = (Switch) mSwitchingDialogTitleView.findViewById(
Tadashi G. Takaokad130b802014-08-01 14:05:47 +09003844 com.android.internal.R.id.hard_keyboard_switch);
Michael Wright7b5a96b2014-08-09 19:28:42 -07003845 hardKeySwitch.setChecked(mShowImeWithHardKeyboard);
Tadashi G. Takaokad130b802014-08-01 14:05:47 +09003846 hardKeySwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
3847 @Override
3848 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Michael Wright7b5a96b2014-08-09 19:28:42 -07003849 mSettings.setShowImeWithHardKeyboard(isChecked);
Tadashi G. Takaokad130b802014-08-01 14:05:47 +09003850 // Ensure that the input method dialog is dismissed when changing
3851 // the hardware keyboard state.
3852 hideInputMethodMenu();
3853 }
3854 });
3855
Alan Viverette505e3ab2014-11-24 15:22:11 -08003856 final ImeSubtypeListAdapter adapter = new ImeSubtypeListAdapter(dialogContext,
Tadashi G. Takaokad130b802014-08-01 14:05:47 +09003857 com.android.internal.R.layout.input_method_switch_item, imList, checkedItem);
3858 final OnClickListener choiceListener = new OnClickListener() {
3859 @Override
3860 public void onClick(final DialogInterface dialog, final int which) {
3861 synchronized (mMethodMap) {
3862 if (mIms == null || mIms.length <= which || mSubtypeIds == null
3863 || mSubtypeIds.length <= which) {
3864 return;
satok01038492012-04-09 21:08:27 +09003865 }
Tadashi G. Takaokad130b802014-08-01 14:05:47 +09003866 final InputMethodInfo im = mIms[which];
3867 int subtypeId = mSubtypeIds[which];
3868 adapter.mCheckedItem = which;
3869 adapter.notifyDataSetChanged();
3870 hideInputMethodMenu();
3871 if (im != null) {
3872 if (subtypeId < 0 || subtypeId >= im.getSubtypeCount()) {
3873 subtypeId = NOT_A_SUBTYPE_ID;
Dianne Hackborn20cb56e2010-03-04 00:58:29 -08003874 }
Tadashi G. Takaokad130b802014-08-01 14:05:47 +09003875 setInputMethodLocked(im.getId(), subtypeId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003876 }
Tadashi G. Takaokad130b802014-08-01 14:05:47 +09003877 }
3878 }
3879 };
3880 mDialogBuilder.setSingleChoiceItems(adapter, checkedItem, choiceListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003881
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003882 mSwitchingDialog = mDialogBuilder.create();
Dianne Hackborne3a7f622011-03-03 21:48:24 -08003883 mSwitchingDialog.setCanceledOnTouchOutside(true);
Wale Ogunwale3a931692016-11-02 16:49:48 -07003884 final Window w = mSwitchingDialog.getWindow();
3885 final WindowManager.LayoutParams attrs = w.getAttributes();
3886 w.setType(TYPE_INPUT_METHOD_DIALOG);
3887 // Use an alternate token for the dialog for that window manager can group the token
3888 // with other IME windows based on type vs. grouping based on whichever token happens
3889 // to get selected by the system later on.
3890 attrs.token = mSwitchingDialogToken;
3891 attrs.privateFlags |= PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
3892 attrs.setTitle("Select input method");
3893 w.setAttributes(attrs);
Seigo Nonakad9eb9112015-05-26 20:54:43 +09003894 updateSystemUi(mCurToken, mImeWindowVis, mBackDisposition);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003895 mSwitchingDialog.show();
3896 }
3897 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003898
Ken Wakasa05dbb652011-08-22 15:22:43 +09003899 private static class ImeSubtypeListAdapter extends ArrayAdapter<ImeSubtypeListItem> {
3900 private final LayoutInflater mInflater;
3901 private final int mTextViewResourceId;
3902 private final List<ImeSubtypeListItem> mItemsList;
Satoshi Kataokad2142962012-11-12 18:43:06 +09003903 public int mCheckedItem;
Ken Wakasa05dbb652011-08-22 15:22:43 +09003904 public ImeSubtypeListAdapter(Context context, int textViewResourceId,
3905 List<ImeSubtypeListItem> itemsList, int checkedItem) {
3906 super(context, textViewResourceId, itemsList);
Alan Viverette505e3ab2014-11-24 15:22:11 -08003907
Ken Wakasa05dbb652011-08-22 15:22:43 +09003908 mTextViewResourceId = textViewResourceId;
3909 mItemsList = itemsList;
3910 mCheckedItem = checkedItem;
Yohei Yukawad34e1482016-02-11 08:03:52 -08003911 mInflater = context.getSystemService(LayoutInflater.class);
Ken Wakasa05dbb652011-08-22 15:22:43 +09003912 }
3913
3914 @Override
3915 public View getView(int position, View convertView, ViewGroup parent) {
3916 final View view = convertView != null ? convertView
3917 : mInflater.inflate(mTextViewResourceId, null);
3918 if (position < 0 || position >= mItemsList.size()) return view;
3919 final ImeSubtypeListItem item = mItemsList.get(position);
3920 final CharSequence imeName = item.mImeName;
3921 final CharSequence subtypeName = item.mSubtypeName;
3922 final TextView firstTextView = (TextView)view.findViewById(android.R.id.text1);
3923 final TextView secondTextView = (TextView)view.findViewById(android.R.id.text2);
3924 if (TextUtils.isEmpty(subtypeName)) {
3925 firstTextView.setText(imeName);
3926 secondTextView.setVisibility(View.GONE);
3927 } else {
3928 firstTextView.setText(subtypeName);
3929 secondTextView.setText(imeName);
3930 secondTextView.setVisibility(View.VISIBLE);
3931 }
3932 final RadioButton radioButton =
3933 (RadioButton)view.findViewById(com.android.internal.R.id.radio);
3934 radioButton.setChecked(position == mCheckedItem);
3935 return view;
3936 }
3937 }
3938
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003939 void hideInputMethodMenu() {
The Android Open Source Project10592532009-03-18 17:39:46 -07003940 synchronized (mMethodMap) {
3941 hideInputMethodMenuLocked();
3942 }
3943 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003944
The Android Open Source Project10592532009-03-18 17:39:46 -07003945 void hideInputMethodMenuLocked() {
Joe Onorato8a9b2202010-02-26 18:56:32 -08003946 if (DEBUG) Slog.v(TAG, "Hide switching menu");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003947
The Android Open Source Project10592532009-03-18 17:39:46 -07003948 if (mSwitchingDialog != null) {
3949 mSwitchingDialog.dismiss();
3950 mSwitchingDialog = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003951 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003952
Seigo Nonakad9eb9112015-05-26 20:54:43 +09003953 updateSystemUiLocked(mCurToken, mImeWindowVis, mBackDisposition);
The Android Open Source Project10592532009-03-18 17:39:46 -07003954 mDialogBuilder = null;
The Android Open Source Project10592532009-03-18 17:39:46 -07003955 mIms = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003956 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003957
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003958 // ----------------------------------------------------------------------
Doug Zongkerab5c49c2009-12-04 10:31:43 -08003959
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08003960 boolean setInputMethodEnabledLocked(String id, boolean enabled) {
3961 // Make sure this is a valid input method.
3962 InputMethodInfo imm = mMethodMap.get(id);
3963 if (imm == null) {
satokd87c2592010-09-29 11:52:06 +09003964 throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08003965 }
3966
satokd87c2592010-09-29 11:52:06 +09003967 List<Pair<String, ArrayList<String>>> enabledInputMethodsList = mSettings
3968 .getEnabledInputMethodsAndSubtypeListLocked();
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08003969
satokd87c2592010-09-29 11:52:06 +09003970 if (enabled) {
3971 for (Pair<String, ArrayList<String>> pair: enabledInputMethodsList) {
3972 if (pair.first.equals(id)) {
3973 // We are enabling this input method, but it is already enabled.
3974 // Nothing to do. The previous state was enabled.
3975 return true;
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08003976 }
3977 }
satokd87c2592010-09-29 11:52:06 +09003978 mSettings.appendAndPutEnabledInputMethodLocked(id, false);
3979 // Previous state was disabled.
3980 return false;
3981 } else {
3982 StringBuilder builder = new StringBuilder();
3983 if (mSettings.buildAndPutEnabledInputMethodsStrRemovingIdLocked(
3984 builder, enabledInputMethodsList, id)) {
3985 // Disabled input method is currently selected, switch to another one.
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09003986 final String selId = mSettings.getSelectedInputMethod();
satok03eb319a2010-11-11 18:17:42 +09003987 if (id.equals(selId) && !chooseNewDefaultIMELocked()) {
3988 Slog.i(TAG, "Can't find new IME, unsetting the current input method.");
3989 resetSelectedInputMethodAndSubtypeLocked("");
satokd87c2592010-09-29 11:52:06 +09003990 }
3991 // Previous state was enabled.
3992 return true;
3993 } else {
3994 // We are disabling the input method but it is already disabled.
3995 // Nothing to do. The previous state was disabled.
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08003996 return false;
3997 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08003998 }
Dianne Hackborn21f1bd12010-02-19 17:02:21 -08003999 }
The Android Open Source Project4df24232009-03-05 14:34:35 -08004000
satok723a27e2010-11-11 14:58:11 +09004001 private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId,
4002 boolean setSubtypeOnly) {
Tarandeep Singh89a6c482017-11-21 14:26:11 -08004003 // Updates to InputMethod are transient in VR mode. Its not included in history.
4004 final boolean isVrInput = imi != null && imi.isVrOnly();
4005 if (!isVrInput) {
4006 // Update the history of InputMethod and Subtype
4007 mSettings.saveCurrentInputMethodAndSubtypeToHistory(mCurMethodId, mCurrentSubtype);
4008 }
satok723a27e2010-11-11 14:58:11 +09004009
Yohei Yukawa3d1e8122014-06-06 19:12:47 +09004010 mCurUserActionNotificationSequenceNumber =
4011 Math.max(mCurUserActionNotificationSequenceNumber + 1, 1);
4012 if (DEBUG) {
4013 Slog.d(TAG, "Bump mCurUserActionNotificationSequenceNumber:"
4014 + mCurUserActionNotificationSequenceNumber);
4015 }
4016
4017 if (mCurClient != null && mCurClient.client != null) {
4018 executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
4019 MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER,
Yohei Yukawa080fa342014-08-31 16:10:05 -07004020 mCurUserActionNotificationSequenceNumber, mCurClient));
Yohei Yukawa3d1e8122014-06-06 19:12:47 +09004021 }
4022
Tarandeep Singh89a6c482017-11-21 14:26:11 -08004023 if (isVrInput) {
4024 // Updates to InputMethod are transient in VR mode. Any changes to Settings are skipped.
4025 return;
4026 }
4027
satok723a27e2010-11-11 14:58:11 +09004028 // Set Subtype here
4029 if (imi == null || subtypeId < 0) {
4030 mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
Tadashi G. Takaoka0ba75bb2010-11-09 12:19:32 -08004031 mCurrentSubtype = null;
satok723a27e2010-11-11 14:58:11 +09004032 } else {
Ken Wakasa586f0512011-01-20 22:31:01 +09004033 if (subtypeId < imi.getSubtypeCount()) {
4034 InputMethodSubtype subtype = imi.getSubtypeAt(subtypeId);
4035 mSettings.putSelectedSubtype(subtype.hashCode());
4036 mCurrentSubtype = subtype;
satok723a27e2010-11-11 14:58:11 +09004037 } else {
4038 mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
satokd81e9502012-05-21 12:58:45 +09004039 // If the subtype is not specified, choose the most applicable one
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09004040 mCurrentSubtype = getCurrentInputMethodSubtypeLocked();
satok723a27e2010-11-11 14:58:11 +09004041 }
satokab751aa2010-09-14 19:17:36 +09004042 }
satok723a27e2010-11-11 14:58:11 +09004043
Yohei Yukawa68645a62016-02-17 07:54:20 -08004044 if (!setSubtypeOnly) {
satok723a27e2010-11-11 14:58:11 +09004045 // Set InputMethod here
4046 mSettings.putSelectedInputMethod(imi != null ? imi.getId() : "");
4047 }
4048 }
4049
4050 private void resetSelectedInputMethodAndSubtypeLocked(String newDefaultIme) {
4051 InputMethodInfo imi = mMethodMap.get(newDefaultIme);
4052 int lastSubtypeId = NOT_A_SUBTYPE_ID;
4053 // newDefaultIme is empty when there is no candidate for the selected IME.
4054 if (imi != null && !TextUtils.isEmpty(newDefaultIme)) {
4055 String subtypeHashCode = mSettings.getLastSubtypeForInputMethodLocked(newDefaultIme);
4056 if (subtypeHashCode != null) {
4057 try {
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09004058 lastSubtypeId = InputMethodUtils.getSubtypeIdFromHashCode(
Narayan Kamatha09b4d22016-04-15 18:32:45 +01004059 imi, Integer.parseInt(subtypeHashCode));
satok723a27e2010-11-11 14:58:11 +09004060 } catch (NumberFormatException e) {
4061 Slog.w(TAG, "HashCode for subtype looks broken: " + subtypeHashCode, e);
4062 }
4063 }
4064 }
4065 setSelectedInputMethodAndSubtypeLocked(imi, lastSubtypeId, false);
satokab751aa2010-09-14 19:17:36 +09004066 }
4067
satok4e4569d2010-11-19 18:45:53 +09004068 // If there are no selected shortcuts, tries finding the most applicable ones.
4069 private Pair<InputMethodInfo, InputMethodSubtype>
4070 findLastResortApplicableShortcutInputMethodAndSubtypeLocked(String mode) {
4071 List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
4072 InputMethodInfo mostApplicableIMI = null;
satokcd7cd292010-11-20 15:46:23 +09004073 InputMethodSubtype mostApplicableSubtype = null;
satok4e4569d2010-11-19 18:45:53 +09004074 boolean foundInSystemIME = false;
4075
4076 // Search applicable subtype for each InputMethodInfo
4077 for (InputMethodInfo imi: imis) {
satok7599a7f2010-12-22 13:45:23 +09004078 final String imiId = imi.getId();
4079 if (foundInSystemIME && !imiId.equals(mCurMethodId)) {
4080 continue;
4081 }
satokcd7cd292010-11-20 15:46:23 +09004082 InputMethodSubtype subtype = null;
satokdf31ae62011-01-15 06:19:44 +09004083 final List<InputMethodSubtype> enabledSubtypes =
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09004084 mSettings.getEnabledInputMethodSubtypeListLocked(mContext, imi, true);
satokdf31ae62011-01-15 06:19:44 +09004085 // 1. Search by the current subtype's locale from enabledSubtypes.
satok4e4569d2010-11-19 18:45:53 +09004086 if (mCurrentSubtype != null) {
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09004087 subtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
satokdf31ae62011-01-15 06:19:44 +09004088 mRes, enabledSubtypes, mode, mCurrentSubtype.getLocale(), false);
satok4e4569d2010-11-19 18:45:53 +09004089 }
satokdf31ae62011-01-15 06:19:44 +09004090 // 2. Search by the system locale from enabledSubtypes.
4091 // 3. Search the first enabled subtype matched with mode from enabledSubtypes.
satokcd7cd292010-11-20 15:46:23 +09004092 if (subtype == null) {
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09004093 subtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
satokdf31ae62011-01-15 06:19:44 +09004094 mRes, enabledSubtypes, mode, null, true);
satok4e4569d2010-11-19 18:45:53 +09004095 }
satoka86f5e42011-09-02 17:12:42 +09004096 final ArrayList<InputMethodSubtype> overridingImplicitlyEnabledSubtypes =
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09004097 InputMethodUtils.getOverridingImplicitlyEnabledSubtypes(imi, mode);
satoka86f5e42011-09-02 17:12:42 +09004098 final ArrayList<InputMethodSubtype> subtypesForSearch =
4099 overridingImplicitlyEnabledSubtypes.isEmpty()
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09004100 ? InputMethodUtils.getSubtypes(imi)
4101 : overridingImplicitlyEnabledSubtypes;
satok7599a7f2010-12-22 13:45:23 +09004102 // 4. Search by the current subtype's locale from all subtypes.
4103 if (subtype == null && mCurrentSubtype != null) {
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09004104 subtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
satoka86f5e42011-09-02 17:12:42 +09004105 mRes, subtypesForSearch, mode, mCurrentSubtype.getLocale(), false);
satok7599a7f2010-12-22 13:45:23 +09004106 }
4107 // 5. Search by the system locale from all subtypes.
4108 // 6. Search the first enabled subtype matched with mode from all subtypes.
satokcd7cd292010-11-20 15:46:23 +09004109 if (subtype == null) {
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09004110 subtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
satoka86f5e42011-09-02 17:12:42 +09004111 mRes, subtypesForSearch, mode, null, true);
satok4e4569d2010-11-19 18:45:53 +09004112 }
satokcd7cd292010-11-20 15:46:23 +09004113 if (subtype != null) {
satok7599a7f2010-12-22 13:45:23 +09004114 if (imiId.equals(mCurMethodId)) {
satok4e4569d2010-11-19 18:45:53 +09004115 // The current input method is the most applicable IME.
4116 mostApplicableIMI = imi;
satokcd7cd292010-11-20 15:46:23 +09004117 mostApplicableSubtype = subtype;
satok4e4569d2010-11-19 18:45:53 +09004118 break;
satok7599a7f2010-12-22 13:45:23 +09004119 } else if (!foundInSystemIME) {
satok4e4569d2010-11-19 18:45:53 +09004120 // The system input method is 2nd applicable IME.
4121 mostApplicableIMI = imi;
satokcd7cd292010-11-20 15:46:23 +09004122 mostApplicableSubtype = subtype;
satok7599a7f2010-12-22 13:45:23 +09004123 if ((imi.getServiceInfo().applicationInfo.flags
4124 & ApplicationInfo.FLAG_SYSTEM) != 0) {
4125 foundInSystemIME = true;
4126 }
satok4e4569d2010-11-19 18:45:53 +09004127 }
4128 }
4129 }
4130 if (DEBUG) {
satokcd7cd292010-11-20 15:46:23 +09004131 if (mostApplicableIMI != null) {
4132 Slog.w(TAG, "Most applicable shortcut input method was:"
4133 + mostApplicableIMI.getId());
4134 if (mostApplicableSubtype != null) {
4135 Slog.w(TAG, "Most applicable shortcut input method subtype was:"
4136 + "," + mostApplicableSubtype.getMode() + ","
4137 + mostApplicableSubtype.getLocale());
4138 }
4139 }
satok4e4569d2010-11-19 18:45:53 +09004140 }
satokcd7cd292010-11-20 15:46:23 +09004141 if (mostApplicableIMI != null) {
Yohei Yukawab0377bb2015-08-10 21:06:30 -07004142 return new Pair<> (mostApplicableIMI, mostApplicableSubtype);
satok4e4569d2010-11-19 18:45:53 +09004143 } else {
4144 return null;
4145 }
4146 }
4147
satokab751aa2010-09-14 19:17:36 +09004148 /**
4149 * @return Return the current subtype of this input method.
4150 */
satok42c5a162011-05-26 16:46:14 +09004151 @Override
satokab751aa2010-09-14 19:17:36 +09004152 public InputMethodSubtype getCurrentInputMethodSubtype() {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09004153 // TODO: Make this work even for non-current users?
4154 if (!calledFromValidUser()) {
4155 return null;
4156 }
4157 synchronized (mMethodMap) {
4158 return getCurrentInputMethodSubtypeLocked();
4159 }
4160 }
4161
4162 private InputMethodSubtype getCurrentInputMethodSubtypeLocked() {
satokfdf419e2012-05-08 16:52:08 +09004163 if (mCurMethodId == null) {
4164 return null;
4165 }
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09004166 final boolean subtypeIsSelected = mSettings.isSubtypeSelected();
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09004167 final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
4168 if (imi == null || imi.getSubtypeCount() == 0) {
4169 return null;
satok4e4569d2010-11-19 18:45:53 +09004170 }
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09004171 if (!subtypeIsSelected || mCurrentSubtype == null
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09004172 || !InputMethodUtils.isValidSubtypeId(imi, mCurrentSubtype.hashCode())) {
4173 int subtypeId = mSettings.getSelectedInputMethodSubtypeId(mCurMethodId);
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09004174 if (subtypeId == NOT_A_SUBTYPE_ID) {
4175 // If there are no selected subtypes, the framework will try to find
4176 // the most applicable subtype from explicitly or implicitly enabled
4177 // subtypes.
4178 List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypes =
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09004179 mSettings.getEnabledInputMethodSubtypeListLocked(mContext, imi, true);
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09004180 // If there is only one explicitly or implicitly enabled subtype,
4181 // just returns it.
4182 if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) {
4183 mCurrentSubtype = explicitlyOrImplicitlyEnabledSubtypes.get(0);
4184 } else if (explicitlyOrImplicitlyEnabledSubtypes.size() > 1) {
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09004185 mCurrentSubtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09004186 mRes, explicitlyOrImplicitlyEnabledSubtypes,
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09004187 InputMethodUtils.SUBTYPE_MODE_KEYBOARD, null, true);
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09004188 if (mCurrentSubtype == null) {
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09004189 mCurrentSubtype = InputMethodUtils.findLastResortApplicableSubtypeLocked(
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09004190 mRes, explicitlyOrImplicitlyEnabledSubtypes, null, null,
4191 true);
satok4e4569d2010-11-19 18:45:53 +09004192 }
satok3ef8b292010-11-23 06:06:29 +09004193 }
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09004194 } else {
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09004195 mCurrentSubtype = InputMethodUtils.getSubtypes(imi).get(subtypeId);
satok8fbb1e82010-11-02 23:15:58 +09004196 }
4197 }
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09004198 return mCurrentSubtype;
satokab751aa2010-09-14 19:17:36 +09004199 }
4200
satok4e4569d2010-11-19 18:45:53 +09004201 // TODO: We should change the return type from List to List<Parcelable>
satokdbf29502011-08-25 15:28:23 +09004202 @SuppressWarnings("rawtypes")
satoke7c6998e2011-06-03 17:57:59 +09004203 @Override
satok4e4569d2010-11-19 18:45:53 +09004204 public List getShortcutInputMethodsAndSubtypes() {
4205 synchronized (mMethodMap) {
Yohei Yukawab0377bb2015-08-10 21:06:30 -07004206 ArrayList<Object> ret = new ArrayList<>();
satokf3db1af2010-11-23 13:34:33 +09004207 if (mShortcutInputMethodsAndSubtypes.size() == 0) {
satok4e4569d2010-11-19 18:45:53 +09004208 // If there are no selected shortcut subtypes, the framework will try to find
4209 // the most applicable subtype from all subtypes whose mode is
4210 // SUBTYPE_MODE_VOICE. This is an exceptional case, so we will hardcode the mode.
satokf3db1af2010-11-23 13:34:33 +09004211 Pair<InputMethodInfo, InputMethodSubtype> info =
4212 findLastResortApplicableShortcutInputMethodAndSubtypeLocked(
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09004213 InputMethodUtils.SUBTYPE_MODE_VOICE);
satok7599a7f2010-12-22 13:45:23 +09004214 if (info != null) {
satok3da92232011-01-11 22:46:30 +09004215 ret.add(info.first);
4216 ret.add(info.second);
satok7599a7f2010-12-22 13:45:23 +09004217 }
satok3da92232011-01-11 22:46:30 +09004218 return ret;
satokf3db1af2010-11-23 13:34:33 +09004219 }
satokf3db1af2010-11-23 13:34:33 +09004220 for (InputMethodInfo imi: mShortcutInputMethodsAndSubtypes.keySet()) {
4221 ret.add(imi);
4222 for (InputMethodSubtype subtype: mShortcutInputMethodsAndSubtypes.get(imi)) {
4223 ret.add(subtype);
satok4e4569d2010-11-19 18:45:53 +09004224 }
4225 }
satokf3db1af2010-11-23 13:34:33 +09004226 return ret;
satok4e4569d2010-11-19 18:45:53 +09004227 }
4228 }
4229
satok42c5a162011-05-26 16:46:14 +09004230 @Override
satokb66d2872010-11-10 01:04:04 +09004231 public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
Satoshi Kataoka4e1ab152012-09-13 21:34:20 +09004232 // TODO: Make this work even for non-current users?
4233 if (!calledFromValidUser()) {
4234 return false;
4235 }
satokb66d2872010-11-10 01:04:04 +09004236 synchronized (mMethodMap) {
4237 if (subtype != null && mCurMethodId != null) {
4238 InputMethodInfo imi = mMethodMap.get(mCurMethodId);
Satoshi Kataoka8e303cc2013-01-11 15:55:28 +09004239 int subtypeId = InputMethodUtils.getSubtypeIdFromHashCode(imi, subtype.hashCode());
satokb66d2872010-11-10 01:04:04 +09004240 if (subtypeId != NOT_A_SUBTYPE_ID) {
4241 setInputMethodLocked(mCurMethodId, subtypeId);
4242 return true;
4243 }
4244 }
4245 return false;
4246 }
4247 }
4248
Satoshi Kataoka5ade83b2012-09-26 22:59:41 +09004249 // TODO: Cache the state for each user and reset when the cached user is removed.
satoke7c6998e2011-06-03 17:57:59 +09004250 private static class InputMethodFileManager {
4251 private static final String SYSTEM_PATH = "system";
4252 private static final String INPUT_METHOD_PATH = "inputmethod";
4253 private static final String ADDITIONAL_SUBTYPES_FILE_NAME = "subtypes.xml";
4254 private static final String NODE_SUBTYPES = "subtypes";
4255 private static final String NODE_SUBTYPE = "subtype";
4256 private static final String NODE_IMI = "imi";
4257 private static final String ATTR_ID = "id";
4258 private static final String ATTR_LABEL = "label";
4259 private static final String ATTR_ICON = "icon";
Yohei Yukawa66baf692016-04-11 02:29:35 -07004260 private static final String ATTR_IME_SUBTYPE_ID = "subtypeId";
satoke7c6998e2011-06-03 17:57:59 +09004261 private static final String ATTR_IME_SUBTYPE_LOCALE = "imeSubtypeLocale";
Yohei Yukawa868d19b2015-12-07 15:58:57 -08004262 private static final String ATTR_IME_SUBTYPE_LANGUAGE_TAG = "languageTag";
satoke7c6998e2011-06-03 17:57:59 +09004263 private static final String ATTR_IME_SUBTYPE_MODE = "imeSubtypeMode";
4264 private static final String ATTR_IME_SUBTYPE_EXTRA_VALUE = "imeSubtypeExtraValue";
4265 private static final String ATTR_IS_AUXILIARY = "isAuxiliary";
Yohei Yukawa1e1a4472016-03-10 23:46:07 -08004266 private static final String ATTR_IS_ASCII_CAPABLE = "isAsciiCapable";
satoke7c6998e2011-06-03 17:57:59 +09004267 private final AtomicFile mAdditionalInputMethodSubtypeFile;
4268 private final HashMap<String, InputMethodInfo> mMethodMap;
Satoshi Kataokae62e6d82012-07-02 18:45:43 +09004269 private final HashMap<String, List<InputMethodSubtype>> mAdditionalSubtypesMap =
Yohei Yukawab0377bb2015-08-10 21:06:30 -07004270 new HashMap<>();
Satoshi Kataoka5ade83b2012-09-26 22:59:41 +09004271 public InputMethodFileManager(HashMap<String, InputMethodInfo> methodMap, int userId) {
satoke7c6998e2011-06-03 17:57:59 +09004272 if (methodMap == null) {
4273 throw new NullPointerException("methodMap is null");
4274 }
4275 mMethodMap = methodMap;
Xiaohui Chen7c696362015-09-16 09:56:14 -07004276 final File systemDir = userId == UserHandle.USER_SYSTEM
Satoshi Kataoka5ade83b2012-09-26 22:59:41 +09004277 ? new File(Environment.getDataDirectory(), SYSTEM_PATH)
4278 : Environment.getUserSystemDirectory(userId);
satoke7c6998e2011-06-03 17:57:59 +09004279 final File inputMethodDir = new File(systemDir, INPUT_METHOD_PATH);
Yohei Yukawadf5af482015-08-04 22:11:11 -07004280 if (!inputMethodDir.exists() && !inputMethodDir.mkdirs()) {
satoke7c6998e2011-06-03 17:57:59 +09004281 Slog.w(TAG, "Couldn't create dir.: " + inputMethodDir.getAbsolutePath());
4282 }
4283 final File subtypeFile = new File(inputMethodDir, ADDITIONAL_SUBTYPES_FILE_NAME);
Dianne Hackborne17b4452018-01-10 13:15:40 -08004284 mAdditionalInputMethodSubtypeFile = new AtomicFile(subtypeFile, "input-subtypes");
satoke7c6998e2011-06-03 17:57:59 +09004285 if (!subtypeFile.exists()) {
4286 // If "subtypes.xml" doesn't exist, create a blank file.
Satoshi Kataokae62e6d82012-07-02 18:45:43 +09004287 writeAdditionalInputMethodSubtypes(
4288 mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, methodMap);
satoke7c6998e2011-06-03 17:57:59 +09004289 } else {
Satoshi Kataokae62e6d82012-07-02 18:45:43 +09004290 readAdditionalInputMethodSubtypes(
4291 mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile);
satoke7c6998e2011-06-03 17:57:59 +09004292 }
4293 }
4294
4295 private void deleteAllInputMethodSubtypes(String imiId) {
4296 synchronized (mMethodMap) {
Satoshi Kataokae62e6d82012-07-02 18:45:43 +09004297 mAdditionalSubtypesMap.remove(imiId);
4298 writeAdditionalInputMethodSubtypes(
4299 mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, mMethodMap);
satoke7c6998e2011-06-03 17:57:59 +09004300 }
4301 }
4302
4303 public void addInputMethodSubtypes(
satok4a28bde2011-06-29 21:03:40 +09004304 InputMethodInfo imi, InputMethodSubtype[] additionalSubtypes) {
satoke7c6998e2011-06-03 17:57:59 +09004305 synchronized (mMethodMap) {
Yohei Yukawab0377bb2015-08-10 21:06:30 -07004306 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
satoke7c6998e2011-06-03 17:57:59 +09004307 final int N = additionalSubtypes.length;
4308 for (int i = 0; i < N; ++i) {
4309 final InputMethodSubtype subtype = additionalSubtypes[i];
satoked2b24e2011-08-31 18:03:21 +09004310 if (!subtypes.contains(subtype)) {
satoke7c6998e2011-06-03 17:57:59 +09004311 subtypes.add(subtype);
Satoshi Kataokae62e6d82012-07-02 18:45:43 +09004312 } else {
4313 Slog.w(TAG, "Duplicated subtype definition found: "
4314 + subtype.getLocale() + ", " + subtype.getMode());
satoke7c6998e2011-06-03 17:57:59 +09004315 }
4316 }
Satoshi Kataokae62e6d82012-07-02 18:45:43 +09004317 mAdditionalSubtypesMap.put(imi.getId(), subtypes);
4318 writeAdditionalInputMethodSubtypes(
4319 mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, mMethodMap);
satoke7c6998e2011-06-03 17:57:59 +09004320 }
4321 }
4322
4323 public HashMap<String, List<InputMethodSubtype>> getAllAdditionalInputMethodSubtypes() {
4324 synchronized (mMethodMap) {
Satoshi Kataokae62e6d82012-07-02 18:45:43 +09004325 return mAdditionalSubtypesMap;
satoke7c6998e2011-06-03 17:57:59 +09004326 }
4327 }
4328
4329 private static void writeAdditionalInputMethodSubtypes(
4330 HashMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile,
4331 HashMap<String, InputMethodInfo> methodMap) {
4332 // Safety net for the case that this function is called before methodMap is set.
4333 final boolean isSetMethodMap = methodMap != null && methodMap.size() > 0;
4334 FileOutputStream fos = null;
4335 try {
4336 fos = subtypesFile.startWrite();
4337 final XmlSerializer out = new FastXmlSerializer();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +01004338 out.setOutput(fos, StandardCharsets.UTF_8.name());
satoke7c6998e2011-06-03 17:57:59 +09004339 out.startDocument(null, true);
4340 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
4341 out.startTag(null, NODE_SUBTYPES);
4342 for (String imiId : allSubtypes.keySet()) {
4343 if (isSetMethodMap && !methodMap.containsKey(imiId)) {
4344 Slog.w(TAG, "IME uninstalled or not valid.: " + imiId);
4345 continue;
4346 }
4347 out.startTag(null, NODE_IMI);
4348 out.attribute(null, ATTR_ID, imiId);
4349 final List<InputMethodSubtype> subtypesList = allSubtypes.get(imiId);
4350 final int N = subtypesList.size();
4351 for (int i = 0; i < N; ++i) {
4352 final InputMethodSubtype subtype = subtypesList.get(i);
4353 out.startTag(null, NODE_SUBTYPE);
Yohei Yukawa66baf692016-04-11 02:29:35 -07004354 if (subtype.hasSubtypeId()) {
4355 out.attribute(null, ATTR_IME_SUBTYPE_ID,
4356 String.valueOf(subtype.getSubtypeId()));
4357 }
satoke7c6998e2011-06-03 17:57:59 +09004358 out.attribute(null, ATTR_ICON, String.valueOf(subtype.getIconResId()));
4359 out.attribute(null, ATTR_LABEL, String.valueOf(subtype.getNameResId()));
4360 out.attribute(null, ATTR_IME_SUBTYPE_LOCALE, subtype.getLocale());
Yohei Yukawa868d19b2015-12-07 15:58:57 -08004361 out.attribute(null, ATTR_IME_SUBTYPE_LANGUAGE_TAG,
4362 subtype.getLanguageTag());
satoke7c6998e2011-06-03 17:57:59 +09004363 out.attribute(null, ATTR_IME_SUBTYPE_MODE, subtype.getMode());
4364 out.attribute(null, ATTR_IME_SUBTYPE_EXTRA_VALUE, subtype.getExtraValue());
4365 out.attribute(null, ATTR_IS_AUXILIARY,
4366 String.valueOf(subtype.isAuxiliary() ? 1 : 0));
Yohei Yukawa1e1a4472016-03-10 23:46:07 -08004367 out.attribute(null, ATTR_IS_ASCII_CAPABLE,
4368 String.valueOf(subtype.isAsciiCapable() ? 1 : 0));
satoke7c6998e2011-06-03 17:57:59 +09004369 out.endTag(null, NODE_SUBTYPE);
4370 }
4371 out.endTag(null, NODE_IMI);
4372 }
4373 out.endTag(null, NODE_SUBTYPES);
4374 out.endDocument();
4375 subtypesFile.finishWrite(fos);
4376 } catch (java.io.IOException e) {
4377 Slog.w(TAG, "Error writing subtypes", e);
4378 if (fos != null) {
4379 subtypesFile.failWrite(fos);
4380 }
4381 }
4382 }
4383
4384 private static void readAdditionalInputMethodSubtypes(
4385 HashMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile) {
4386 if (allSubtypes == null || subtypesFile == null) return;
4387 allSubtypes.clear();
Yohei Yukawa5894b432015-08-11 13:29:24 -07004388 try (final FileInputStream fis = subtypesFile.openRead()) {
satoke7c6998e2011-06-03 17:57:59 +09004389 final XmlPullParser parser = Xml.newPullParser();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +01004390 parser.setInput(fis, StandardCharsets.UTF_8.name());
satoke7c6998e2011-06-03 17:57:59 +09004391 int type = parser.getEventType();
4392 // Skip parsing until START_TAG
4393 while ((type = parser.next()) != XmlPullParser.START_TAG
4394 && type != XmlPullParser.END_DOCUMENT) {}
4395 String firstNodeName = parser.getName();
4396 if (!NODE_SUBTYPES.equals(firstNodeName)) {
4397 throw new XmlPullParserException("Xml doesn't start with subtypes");
4398 }
4399 final int depth =parser.getDepth();
4400 String currentImiId = null;
4401 ArrayList<InputMethodSubtype> tempSubtypesArray = null;
4402 while (((type = parser.next()) != XmlPullParser.END_TAG
4403 || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
4404 if (type != XmlPullParser.START_TAG)
4405 continue;
4406 final String nodeName = parser.getName();
4407 if (NODE_IMI.equals(nodeName)) {
4408 currentImiId = parser.getAttributeValue(null, ATTR_ID);
4409 if (TextUtils.isEmpty(currentImiId)) {
4410 Slog.w(TAG, "Invalid imi id found in subtypes.xml");
4411 continue;
4412 }
Yohei Yukawab0377bb2015-08-10 21:06:30 -07004413 tempSubtypesArray = new ArrayList<>();
satoke7c6998e2011-06-03 17:57:59 +09004414 allSubtypes.put(currentImiId, tempSubtypesArray);
4415 } else if (NODE_SUBTYPE.equals(nodeName)) {
4416 if (TextUtils.isEmpty(currentImiId) || tempSubtypesArray == null) {
4417 Slog.w(TAG, "IME uninstalled or not valid.: " + currentImiId);
4418 continue;
4419 }
Narayan Kamatha09b4d22016-04-15 18:32:45 +01004420 final int icon = Integer.parseInt(
satoke7c6998e2011-06-03 17:57:59 +09004421 parser.getAttributeValue(null, ATTR_ICON));
Narayan Kamatha09b4d22016-04-15 18:32:45 +01004422 final int label = Integer.parseInt(
satoke7c6998e2011-06-03 17:57:59 +09004423 parser.getAttributeValue(null, ATTR_LABEL));
4424 final String imeSubtypeLocale =
4425 parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LOCALE);
Yohei Yukawa868d19b2015-12-07 15:58:57 -08004426 final String languageTag =
4427 parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LANGUAGE_TAG);
satoke7c6998e2011-06-03 17:57:59 +09004428 final String imeSubtypeMode =
4429 parser.getAttributeValue(null, ATTR_IME_SUBTYPE_MODE);
4430 final String imeSubtypeExtraValue =
4431 parser.getAttributeValue(null, ATTR_IME_SUBTYPE_EXTRA_VALUE);
satok4a28bde2011-06-29 21:03:40 +09004432 final boolean isAuxiliary = "1".equals(String.valueOf(
4433 parser.getAttributeValue(null, ATTR_IS_AUXILIARY)));
Yohei Yukawa1e1a4472016-03-10 23:46:07 -08004434 final boolean isAsciiCapable = "1".equals(String.valueOf(
4435 parser.getAttributeValue(null, ATTR_IS_ASCII_CAPABLE)));
Yohei Yukawa66baf692016-04-11 02:29:35 -07004436 final InputMethodSubtypeBuilder builder = new InputMethodSubtypeBuilder()
Yohei Yukawa443c2ba2014-09-10 14:10:30 +09004437 .setSubtypeNameResId(label)
4438 .setSubtypeIconResId(icon)
4439 .setSubtypeLocale(imeSubtypeLocale)
Yohei Yukawa868d19b2015-12-07 15:58:57 -08004440 .setLanguageTag(languageTag)
Yohei Yukawa443c2ba2014-09-10 14:10:30 +09004441 .setSubtypeMode(imeSubtypeMode)
4442 .setSubtypeExtraValue(imeSubtypeExtraValue)
4443 .setIsAuxiliary(isAuxiliary)
Yohei Yukawa66baf692016-04-11 02:29:35 -07004444 .setIsAsciiCapable(isAsciiCapable);
4445 final String subtypeIdString =
4446 parser.getAttributeValue(null, ATTR_IME_SUBTYPE_ID);
4447 if (subtypeIdString != null) {
Narayan Kamatha09b4d22016-04-15 18:32:45 +01004448 builder.setSubtypeId(Integer.parseInt(subtypeIdString));
Yohei Yukawa66baf692016-04-11 02:29:35 -07004449 }
4450 tempSubtypesArray.add(builder.build());
satoke7c6998e2011-06-03 17:57:59 +09004451 }
4452 }
Yohei Yukawa5894b432015-08-11 13:29:24 -07004453 } catch (XmlPullParserException | IOException | NumberFormatException e) {
4454 Slog.w(TAG, "Error reading subtypes", e);
satoke7c6998e2011-06-03 17:57:59 +09004455 return;
satoke7c6998e2011-06-03 17:57:59 +09004456 }
4457 }
4458 }
4459
Yohei Yukawafa6e0a82015-07-23 15:08:59 -07004460 private static final class LocalServiceImpl implements InputMethodManagerInternal {
4461 @NonNull
4462 private final Handler mHandler;
4463
4464 LocalServiceImpl(@NonNull final Handler handler) {
4465 mHandler = handler;
4466 }
4467
4468 @Override
4469 public void setInteractive(boolean interactive) {
4470 // Do everything in handler so as not to block the caller.
4471 mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_INTERACTIVE,
4472 interactive ? 1 : 0, 0));
4473 }
Yohei Yukawaae61f712015-12-09 13:00:10 -08004474
4475 @Override
4476 public void switchInputMethod(boolean forwardDirection) {
4477 // Do everything in handler so as not to block the caller.
4478 mHandler.sendMessage(mHandler.obtainMessage(MSG_SWITCH_IME,
4479 forwardDirection ? 1 : 0, 0));
4480 }
Jorim Jaggi3c5d0f12016-05-24 19:04:30 -07004481
4482 @Override
4483 public void hideCurrentInputMethod() {
4484 mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD);
4485 mHandler.sendEmptyMessage(MSG_HIDE_CURRENT_INPUT_METHOD);
4486 }
Tarandeep Singh89a6c482017-11-21 14:26:11 -08004487
4488 @Override
4489 public void startVrInputMethodNoCheck(@Nullable ComponentName componentName) {
4490 mHandler.sendMessage(mHandler.obtainMessage(MSG_START_VR_INPUT, componentName));
4491 }
Yohei Yukawafa6e0a82015-07-23 15:08:59 -07004492 }
4493
Yohei Yukawaebda7d72016-04-02 17:39:23 -07004494 private static String imeWindowStatusToString(final int imeWindowVis) {
4495 final StringBuilder sb = new StringBuilder();
4496 boolean first = true;
4497 if ((imeWindowVis & InputMethodService.IME_ACTIVE) != 0) {
4498 sb.append("Active");
4499 first = false;
4500 }
4501 if ((imeWindowVis & InputMethodService.IME_VISIBLE) != 0) {
4502 if (!first) {
4503 sb.append("|");
4504 }
4505 sb.append("Visible");
4506 }
4507 return sb.toString();
4508 }
4509
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004510 @Override
Yohei Yukawa25e08132016-06-22 16:31:41 -07004511 public IInputContentUriToken createInputContentUriToken(@Nullable IBinder token,
4512 @Nullable Uri contentUri, @Nullable String packageName) {
4513 if (!calledFromValidUser()) {
4514 return null;
4515 }
4516
4517 if (token == null) {
4518 throw new NullPointerException("token");
4519 }
4520 if (packageName == null) {
4521 throw new NullPointerException("packageName");
4522 }
4523 if (contentUri == null) {
4524 throw new NullPointerException("contentUri");
4525 }
4526 final String contentUriScheme = contentUri.getScheme();
4527 if (!"content".equals(contentUriScheme)) {
4528 throw new InvalidParameterException("contentUri must have content scheme");
4529 }
4530
4531 synchronized (mMethodMap) {
4532 final int uid = Binder.getCallingUid();
4533 if (mCurMethodId == null) {
4534 return null;
4535 }
4536 if (mCurToken != token) {
4537 Slog.e(TAG, "Ignoring createInputContentUriToken mCurToken=" + mCurToken
4538 + " token=" + token);
4539 return null;
4540 }
4541 // We cannot simply distinguish a bad IME that reports an arbitrary package name from
4542 // an unfortunate IME whose internal state is already obsolete due to the asynchronous
4543 // nature of our system. Let's compare it with our internal record.
4544 if (!TextUtils.equals(mCurAttribute.packageName, packageName)) {
4545 Slog.e(TAG, "Ignoring createInputContentUriToken mCurAttribute.packageName="
4546 + mCurAttribute.packageName + " packageName=" + packageName);
4547 return null;
4548 }
Yohei Yukawa3933a6e2016-11-10 00:47:48 -08004549 // This user ID can never bee spoofed.
Yohei Yukawa25e08132016-06-22 16:31:41 -07004550 final int imeUserId = UserHandle.getUserId(uid);
Yohei Yukawa3933a6e2016-11-10 00:47:48 -08004551 // This user ID can never bee spoofed.
Yohei Yukawa25e08132016-06-22 16:31:41 -07004552 final int appUserId = UserHandle.getUserId(mCurClient.uid);
Yohei Yukawa3933a6e2016-11-10 00:47:48 -08004553 // This user ID may be invalid if "contentUri" embedded an invalid user ID.
4554 final int contentUriOwnerUserId = ContentProvider.getUserIdFromUri(contentUri,
4555 imeUserId);
4556 final Uri contentUriWithoutUserId = ContentProvider.getUriWithoutUserId(contentUri);
4557 // Note: InputContentUriTokenHandler.take() checks whether the IME (specified by "uid")
4558 // actually has the right to grant a read permission for "contentUriWithoutUserId" that
4559 // is claimed to belong to "contentUriOwnerUserId". For example, specifying random
4560 // content URI and/or contentUriOwnerUserId just results in a SecurityException thrown
4561 // from InputContentUriTokenHandler.take() and can never be allowed beyond what is
4562 // actually allowed to "uid", which is guaranteed to be the IME's one.
4563 return new InputContentUriTokenHandler(contentUriWithoutUserId, uid,
4564 packageName, contentUriOwnerUserId, appUserId);
Yohei Yukawa25e08132016-06-22 16:31:41 -07004565 }
4566 }
4567
4568 @Override
Yohei Yukawa2bc66172017-02-08 11:13:25 -08004569 public void reportFullscreenMode(IBinder token, boolean fullscreen) {
4570 if (!calledFromValidUser()) {
4571 return;
4572 }
4573 synchronized (mMethodMap) {
4574 if (!calledWithValidToken(token)) {
4575 return;
4576 }
4577 if (mCurClient != null && mCurClient.client != null) {
4578 mInFullscreenMode = fullscreen;
4579 executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
4580 MSG_REPORT_FULLSCREEN_MODE, fullscreen ? 1 : 0, mCurClient));
4581 }
4582 }
4583 }
4584
4585 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004586 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -06004587 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004588
4589 IInputMethod method;
4590 ClientState client;
Yohei Yukawae39d4ed2015-11-19 03:38:49 -08004591 ClientState focusedWindowClient;
Doug Zongkerab5c49c2009-12-04 10:31:43 -08004592
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004593 final Printer p = new PrintWriterPrinter(pw);
Doug Zongkerab5c49c2009-12-04 10:31:43 -08004594
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004595 synchronized (mMethodMap) {
4596 p.println("Current Input Method Manager state:");
4597 int N = mMethodList.size();
Yohei Yukawae0733062017-02-09 22:49:35 -08004598 p.println(" Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004599 for (int i=0; i<N; i++) {
4600 InputMethodInfo info = mMethodList.get(i);
4601 p.println(" InputMethod #" + i + ":");
4602 info.dump(p, " ");
4603 }
4604 p.println(" Clients:");
4605 for (ClientState ci : mClients.values()) {
4606 p.println(" Client " + ci + ":");
4607 p.println(" client=" + ci.client);
4608 p.println(" inputContext=" + ci.inputContext);
4609 p.println(" sessionRequested=" + ci.sessionRequested);
4610 p.println(" curSession=" + ci.curSession);
4611 }
The Android Open Source Project10592532009-03-18 17:39:46 -07004612 p.println(" mCurMethodId=" + mCurMethodId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004613 client = mCurClient;
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07004614 p.println(" mCurClient=" + client + " mCurSeq=" + mCurSeq);
Yohei Yukawa22a89232017-02-12 16:38:59 -08004615 p.println(" mCurFocusedWindow=" + mCurFocusedWindow
4616 + " softInputMode=" +
4617 InputMethodClient.softInputModeToString(mCurFocusedWindowSoftInputMode)
4618 + " client=" + mCurFocusedWindowClient);
Yohei Yukawae39d4ed2015-11-19 03:38:49 -08004619 focusedWindowClient = mCurFocusedWindowClient;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004620 p.println(" mCurId=" + mCurId + " mHaveConnect=" + mHaveConnection
4621 + " mBoundToMethod=" + mBoundToMethod);
4622 p.println(" mCurToken=" + mCurToken);
4623 p.println(" mCurIntent=" + mCurIntent);
4624 method = mCurMethod;
4625 p.println(" mCurMethod=" + mCurMethod);
4626 p.println(" mEnabledSession=" + mEnabledSession);
Yohei Yukawaebda7d72016-04-02 17:39:23 -07004627 p.println(" mImeWindowVis=" + imeWindowStatusToString(mImeWindowVis));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004628 p.println(" mShowRequested=" + mShowRequested
4629 + " mShowExplicitlyRequested=" + mShowExplicitlyRequested
4630 + " mShowForced=" + mShowForced
4631 + " mInputShown=" + mInputShown);
Yohei Yukawa2bc66172017-02-08 11:13:25 -08004632 p.println(" mInFullscreenMode=" + mInFullscreenMode);
Yohei Yukawa3d1e8122014-06-06 19:12:47 +09004633 p.println(" mCurUserActionNotificationSequenceNumber="
4634 + mCurUserActionNotificationSequenceNumber);
Yohei Yukawafa6e0a82015-07-23 15:08:59 -07004635 p.println(" mSystemReady=" + mSystemReady + " mInteractive=" + mIsInteractive);
Yohei Yukawa81482972015-06-04 00:58:59 -07004636 p.println(" mSettingsObserver=" + mSettingsObserver);
Yohei Yukawad7248862015-06-03 23:56:12 -07004637 p.println(" mSwitchingController:");
4638 mSwitchingController.dump(p);
Yohei Yukawa68645a62016-02-17 07:54:20 -08004639 p.println(" mSettings:");
4640 mSettings.dumpLocked(p, " ");
Yohei Yukawa357b2f62017-02-14 09:40:03 -08004641
4642 p.println(" mStartInputHistory:");
4643 mStartInputHistory.dump(pw, " ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004644 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08004645
Jeff Brownb88102f2010-09-08 11:49:43 -07004646 p.println(" ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004647 if (client != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004648 pw.flush();
4649 try {
Jeff Sharkey850c83e2016-11-09 12:25:44 -07004650 TransferPipe.dumpAsync(client.client.asBinder(), fd, args);
4651 } catch (IOException | RemoteException e) {
4652 p.println("Failed to dump input method client: " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004653 }
Jeff Brownb88102f2010-09-08 11:49:43 -07004654 } else {
4655 p.println("No input method client.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004656 }
Doug Zongkerab5c49c2009-12-04 10:31:43 -08004657
Yohei Yukawae39d4ed2015-11-19 03:38:49 -08004658 if (focusedWindowClient != null && client != focusedWindowClient) {
4659 p.println(" ");
4660 p.println("Warning: Current input method client doesn't match the last focused. "
4661 + "window.");
4662 p.println("Dumping input method client in the last focused window just in case.");
4663 p.println(" ");
4664 pw.flush();
4665 try {
Jeff Sharkey850c83e2016-11-09 12:25:44 -07004666 TransferPipe.dumpAsync(focusedWindowClient.client.asBinder(), fd, args);
4667 } catch (IOException | RemoteException e) {
4668 p.println("Failed to dump input method client in focused window: " + e);
Yohei Yukawae39d4ed2015-11-19 03:38:49 -08004669 }
4670 }
4671
Jeff Brownb88102f2010-09-08 11:49:43 -07004672 p.println(" ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004673 if (method != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004674 pw.flush();
4675 try {
Jeff Sharkey850c83e2016-11-09 12:25:44 -07004676 TransferPipe.dumpAsync(method.asBinder(), fd, args);
4677 } catch (IOException | RemoteException e) {
4678 p.println("Failed to dump input method service: " + e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004679 }
Jeff Brownb88102f2010-09-08 11:49:43 -07004680 } else {
4681 p.println("No input method service.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004682 }
4683 }
Yohei Yukawa926488d2017-12-11 17:24:55 -08004684
4685 @BinderThread
4686 @Override
4687 public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
4688 @Nullable FileDescriptor err,
4689 @NonNull String[] args, @Nullable ShellCallback callback,
4690 @NonNull ResultReceiver resultReceiver) throws RemoteException {
4691 new ShellCommandImpl(this).exec(
4692 this, in, out, err, args, callback, resultReceiver);
4693 }
4694
4695 private static final class ShellCommandImpl extends ShellCommand {
4696 @NonNull
4697 final InputMethodManagerService mService;
4698
4699 ShellCommandImpl(InputMethodManagerService service) {
4700 mService = service;
4701 }
4702
4703 @BinderThread
4704 @ShellCommandResult
4705 @Override
4706 public int onCommand(@Nullable String cmd) {
Yohei Yukawadfdab732018-02-21 07:16:30 +09004707 if ("refresh_debug_properties".equals(cmd)) {
4708 return refreshDebugProperties();
4709 }
4710
Yohei Yukawacac97722017-12-15 16:52:05 -08004711 // For existing "adb shell ime <command>".
4712 if ("ime".equals(cmd)) {
4713 final String imeCommand = getNextArg();
4714 if (imeCommand == null || "help".equals(imeCommand) || "-h".equals(imeCommand)) {
4715 onImeCommandHelp();
4716 return ShellCommandResult.SUCCESS;
4717 }
4718 switch (imeCommand) {
4719 case "list":
4720 return mService.handleShellCommandListInputMethods(this);
4721 case "enable":
4722 return mService.handleShellCommandEnableDisableInputMethod(this, true);
4723 case "disable":
4724 return mService.handleShellCommandEnableDisableInputMethod(this, false);
4725 case "set":
4726 return mService.handleShellCommandSetInputMethod(this);
4727 case "reset":
4728 return mService.handleShellCommandResetInputMethod(this);
4729 default:
4730 getOutPrintWriter().println("Unknown command: " + imeCommand);
4731 return ShellCommandResult.FAILURE;
4732 }
Yohei Yukawa926488d2017-12-11 17:24:55 -08004733 }
Yohei Yukawacac97722017-12-15 16:52:05 -08004734
4735 return handleDefaultCommands(cmd);
Yohei Yukawa926488d2017-12-11 17:24:55 -08004736 }
4737
4738 @BinderThread
Tarandeep Singh75a92392018-01-12 14:58:59 -08004739 @ShellCommandResult
4740 private int refreshDebugProperties() {
4741 DebugFlags.FLAG_OPTIMIZE_START_INPUT.refresh();
4742 return ShellCommandResult.SUCCESS;
4743 }
4744
4745 @BinderThread
Yohei Yukawa926488d2017-12-11 17:24:55 -08004746 @Override
4747 public void onHelp() {
4748 try (PrintWriter pw = getOutPrintWriter()) {
4749 pw.println("InputMethodManagerService commands:");
4750 pw.println(" help");
4751 pw.println(" Prints this help text.");
4752 pw.println(" dump [options]");
4753 pw.println(" Synonym of dumpsys.");
Yohei Yukawacac97722017-12-15 16:52:05 -08004754 pw.println(" ime <command> [options]");
4755 pw.println(" Manipulate IMEs. Run \"ime help\" for details.");
4756 }
4757 }
4758
4759 private void onImeCommandHelp() {
4760 try (IndentingPrintWriter pw =
4761 new IndentingPrintWriter(getOutPrintWriter(), " ", 100)) {
4762 pw.println("ime <command>:");
4763 pw.increaseIndent();
4764
4765 pw.println("list [-a] [-s]");
4766 pw.increaseIndent();
4767 pw.println("prints all enabled input methods.");
4768 pw.increaseIndent();
4769 pw.println("-a: see all input methods");
4770 pw.println("-s: only a single summary line of each");
4771 pw.decreaseIndent();
4772 pw.decreaseIndent();
4773
4774 pw.println("enable <ID>");
4775 pw.increaseIndent();
4776 pw.println("allows the given input method ID to be used.");
4777 pw.decreaseIndent();
4778
4779 pw.println("disable <ID>");
4780 pw.increaseIndent();
4781 pw.println("disallows the given input method ID to be used.");
4782 pw.decreaseIndent();
4783
4784 pw.println("set <ID>");
4785 pw.increaseIndent();
4786 pw.println("switches to the given input method ID.");
4787 pw.decreaseIndent();
4788
4789 pw.println("reset");
4790 pw.increaseIndent();
4791 pw.println("reset currently selected/enabled IMEs to the default ones as if "
4792 + "the device is initially booted with the current locale.");
4793 pw.decreaseIndent();
4794
4795 pw.decreaseIndent();
Yohei Yukawa926488d2017-12-11 17:24:55 -08004796 }
4797 }
4798 }
4799
4800 // ----------------------------------------------------------------------
4801 // Shell command handlers:
4802
4803 /**
4804 * Handles {@code adb shell ime list}.
4805 * @param shellCommand {@link ShellCommand} object that is handling this command.
4806 * @return Exit code of the command.
4807 */
4808 @BinderThread
4809 @ShellCommandResult
4810 private int handleShellCommandListInputMethods(@NonNull ShellCommand shellCommand) {
4811 boolean all = false;
4812 boolean brief = false;
4813 while (true) {
4814 final String nextOption = shellCommand.getNextOption();
4815 if (nextOption == null) {
4816 break;
4817 }
4818 switch (nextOption) {
4819 case "-a":
4820 all = true;
4821 break;
4822 case "-s":
4823 brief = true;
4824 break;
4825 }
4826 }
4827 final List<InputMethodInfo> methods = all ?
4828 getInputMethodList() : getEnabledInputMethodList();
4829 final PrintWriter pr = shellCommand.getOutPrintWriter();
4830 final Printer printer = x -> pr.println(x);
4831 final int N = methods.size();
4832 for (int i = 0; i < N; ++i) {
4833 if (brief) {
4834 pr.println(methods.get(i).getId());
4835 } else {
4836 pr.print(methods.get(i).getId()); pr.println(":");
4837 methods.get(i).dump(printer, " ");
4838 }
4839 }
4840 return ShellCommandResult.SUCCESS;
4841 }
4842
4843 /**
4844 * Handles {@code adb shell ime enable} and {@code adb shell ime disable}.
4845 * @param shellCommand {@link ShellCommand} object that is handling this command.
4846 * @param enabled {@code true} if the command was {@code adb shell ime enable}.
4847 * @return Exit code of the command.
4848 */
4849 @BinderThread
4850 @ShellCommandResult
4851 @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
4852 private int handleShellCommandEnableDisableInputMethod(
4853 @NonNull ShellCommand shellCommand, boolean enabled) {
4854 if (!calledFromValidUser()) {
4855 shellCommand.getErrPrintWriter().print(
4856 "Must be called from the foreground user or with INTERACT_ACROSS_USERS_FULL");
4857 return ShellCommandResult.FAILURE;
4858 }
4859 final String id = shellCommand.getNextArgRequired();
4860
4861 final boolean previouslyEnabled;
4862 synchronized (mMethodMap) {
4863 if (mContext.checkCallingOrSelfPermission(
4864 android.Manifest.permission.WRITE_SECURE_SETTINGS)
4865 != PackageManager.PERMISSION_GRANTED) {
4866 shellCommand.getErrPrintWriter().print(
4867 "Caller must have WRITE_SECURE_SETTINGS permission");
4868 throw new SecurityException(
4869 "Requires permission "
4870 + android.Manifest.permission.WRITE_SECURE_SETTINGS);
4871 }
4872
4873 final long ident = Binder.clearCallingIdentity();
4874 try {
4875 previouslyEnabled = setInputMethodEnabledLocked(id, enabled);
4876 } finally {
4877 Binder.restoreCallingIdentity(ident);
4878 }
4879 }
4880 final PrintWriter pr = shellCommand.getOutPrintWriter();
4881 pr.print("Input method ");
4882 pr.print(id);
4883 pr.print(": ");
4884 pr.print((enabled == previouslyEnabled) ? "already " : "now ");
4885 pr.println(enabled ? "enabled" : "disabled");
4886 return ShellCommandResult.SUCCESS;
4887 }
4888
4889 /**
4890 * Handles {@code adb shell ime set}.
4891 * @param shellCommand {@link ShellCommand} object that is handling this command.
4892 * @return Exit code of the command.
4893 */
4894 @BinderThread
4895 @ShellCommandResult
4896 private int handleShellCommandSetInputMethod(@NonNull ShellCommand shellCommand) {
4897 final String id = shellCommand.getNextArgRequired();
4898 setInputMethod(null, id);
4899 final PrintWriter pr = shellCommand.getOutPrintWriter();
4900 pr.print("Input method ");
4901 pr.print(id);
4902 pr.println(" selected");
4903 return ShellCommandResult.SUCCESS;
4904 }
Yohei Yukawacc97ebd2017-12-11 17:54:43 -08004905
4906 /**
4907 * Handles {@code adb shell ime reset-ime}.
4908 * @param shellCommand {@link ShellCommand} object that is handling this command.
4909 * @return Exit code of the command.
4910 */
4911 @BinderThread
4912 @ShellCommandResult
4913 @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
4914 private int handleShellCommandResetInputMethod(@NonNull ShellCommand shellCommand) {
4915 if (!calledFromValidUser()) {
4916 shellCommand.getErrPrintWriter().print(
4917 "Must be called from the foreground user or with INTERACT_ACROSS_USERS_FULL");
4918 return ShellCommandResult.FAILURE;
4919 }
4920 synchronized (mMethodMap) {
4921 if (mContext.checkCallingOrSelfPermission(
4922 android.Manifest.permission.WRITE_SECURE_SETTINGS)
4923 != PackageManager.PERMISSION_GRANTED) {
4924 shellCommand.getErrPrintWriter().print(
4925 "Caller must have WRITE_SECURE_SETTINGS permission");
4926 throw new SecurityException(
4927 "Requires permission "
4928 + android.Manifest.permission.WRITE_SECURE_SETTINGS);
4929 }
4930 final String nextIme;
4931 final List<InputMethodInfo> nextEnabledImes;
4932 final long ident = Binder.clearCallingIdentity();
4933 try {
4934 synchronized (mMethodMap) {
4935 hideCurrentInputLocked(0, null);
4936 unbindCurrentMethodLocked(false);
4937 // Reset the current IME
4938 resetSelectedInputMethodAndSubtypeLocked(null);
4939 // Also reset the settings of the current IME
4940 mSettings.putSelectedInputMethod(null);
4941 // Disable all enabled IMEs.
4942 {
4943 final ArrayList<InputMethodInfo> enabledImes =
4944 mSettings.getEnabledInputMethodListLocked();
4945 final int N = enabledImes.size();
4946 for (int i = 0; i < N; ++i) {
4947 setInputMethodEnabledLocked(enabledImes.get(i).getId(), false);
4948 }
4949 }
4950 // Re-enable with default enabled IMEs.
4951 {
4952 final ArrayList<InputMethodInfo> defaultEnabledIme =
4953 InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList);
4954 final int N = defaultEnabledIme.size();
4955 for (int i = 0; i < N; ++i) {
4956 setInputMethodEnabledLocked(defaultEnabledIme.get(i).getId(), true);
4957 }
4958 }
4959 updateInputMethodsFromSettingsLocked(true /* enabledMayChange */);
4960 InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
4961 mSettings.getEnabledInputMethodListLocked(),
4962 mSettings.getCurrentUserId(),
4963 mContext.getBasePackageName());
4964 nextIme = mSettings.getSelectedInputMethod();
4965 nextEnabledImes = getEnabledInputMethodList();
4966 }
4967 } finally {
4968 Binder.restoreCallingIdentity(ident);
4969 }
4970 final PrintWriter pr = shellCommand.getOutPrintWriter();
4971 pr.println("Reset current and enabled IMEs");
4972 pr.println("Newly selected IME:");
4973 pr.print(" "); pr.println(nextIme);
4974 pr.println("Newly enabled IMEs:");
4975 {
4976 final int N = nextEnabledImes.size();
4977 for (int i = 0; i < N; ++i) {
4978 pr.print(" ");
4979 pr.println(nextEnabledImes.get(i).getId());
4980 }
4981 }
4982 return ShellCommandResult.SUCCESS;
4983 }
4984 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004985}