1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.inputmethod; 18 19 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; 20 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; 21 22 import static java.lang.annotation.RetentionPolicy.SOURCE; 23 24 import android.annotation.AnyThread; 25 import android.annotation.BinderThread; 26 import android.annotation.IntDef; 27 import android.annotation.MainThread; 28 import android.annotation.NonNull; 29 import android.annotation.Nullable; 30 import android.annotation.UserIdInt; 31 import android.annotation.WorkerThread; 32 import android.app.AppOpsManager; 33 import android.app.Notification; 34 import android.app.NotificationManager; 35 import android.app.PendingIntent; 36 import android.content.BroadcastReceiver; 37 import android.content.ComponentName; 38 import android.content.Context; 39 import android.content.Intent; 40 import android.content.IntentFilter; 41 import android.content.ServiceConnection; 42 import android.content.pm.ApplicationInfo; 43 import android.content.pm.PackageManager; 44 import android.content.pm.ResolveInfo; 45 import android.content.pm.ServiceInfo; 46 import android.inputmethodservice.MultiClientInputMethodServiceDelegate; 47 import android.net.Uri; 48 import android.os.Binder; 49 import android.os.Build; 50 import android.os.Bundle; 51 import android.os.Debug; 52 import android.os.Handler; 53 import android.os.HandlerThread; 54 import android.os.IBinder; 55 import android.os.RemoteException; 56 import android.os.ResultReceiver; 57 import android.os.ShellCallback; 58 import android.os.UserHandle; 59 import android.provider.Settings; 60 import android.text.TextUtils; 61 import android.util.ArrayMap; 62 import android.util.ArraySet; 63 import android.util.Slog; 64 import android.util.SparseArray; 65 import android.view.InputChannel; 66 import android.view.WindowManager.LayoutParams.SoftInputModeFlags; 67 import android.view.inputmethod.EditorInfo; 68 import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags; 69 import android.view.inputmethod.InputMethodInfo; 70 import android.view.inputmethod.InputMethodSubtype; 71 72 import com.android.internal.R; 73 import com.android.internal.annotations.GuardedBy; 74 import com.android.internal.annotations.VisibleForTesting; 75 import com.android.internal.inputmethod.IMultiClientInputMethod; 76 import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations; 77 import com.android.internal.inputmethod.IMultiClientInputMethodSession; 78 import com.android.internal.inputmethod.SoftInputShowHideReason; 79 import com.android.internal.inputmethod.StartInputFlags; 80 import com.android.internal.inputmethod.StartInputReason; 81 import com.android.internal.inputmethod.UnbindReason; 82 import com.android.internal.messages.nano.SystemMessageProto; 83 import com.android.internal.notification.SystemNotificationChannels; 84 import com.android.internal.os.TransferPipe; 85 import com.android.internal.util.DumpUtils; 86 import com.android.internal.util.IndentingPrintWriter; 87 import com.android.internal.util.function.pooled.PooledLambda; 88 import com.android.internal.view.IInlineSuggestionsRequestCallback; 89 import com.android.internal.view.IInputContext; 90 import com.android.internal.view.IInputMethodClient; 91 import com.android.internal.view.IInputMethodManager; 92 import com.android.internal.view.IInputMethodSession; 93 import com.android.internal.view.InlineSuggestionsRequestInfo; 94 import com.android.internal.view.InputBindResult; 95 import com.android.server.LocalServices; 96 import com.android.server.SystemService; 97 import com.android.server.SystemService.TargetUser; 98 import com.android.server.wm.WindowManagerInternal; 99 100 import java.io.FileDescriptor; 101 import java.io.IOException; 102 import java.io.PrintWriter; 103 import java.lang.annotation.Retention; 104 import java.util.Collections; 105 import java.util.List; 106 import java.util.WeakHashMap; 107 108 /** 109 * Actual implementation of multi-client InputMethodManagerService. 110 * 111 * <p>This system service is intentionally compatible with {@link InputMethodManagerService} so that 112 * we can switch the implementation at the boot time.</p> 113 */ 114 public final class MultiClientInputMethodManagerService { 115 private static final String TAG = "MultiClientInputMethodManagerService"; 116 private static final boolean DEBUG = false; 117 118 private static final String PER_DISPLAY_FOCUS_DISABLED_WARNING_TITLE = 119 "config_perDisplayFocusEnabled is not true."; 120 121 private static final String PER_DISPLAY_FOCUS_DISABLED_WARNING_MSG = 122 "Consider rebuilding the system image after enabling config_perDisplayFocusEnabled to " 123 + "make IME focus compatible with multi-client IME mode."; 124 125 private static final long RECONNECT_DELAY_MSEC = 1000; 126 127 /** 128 * Unlike {@link InputMethodManagerService}, {@link MultiClientInputMethodManagerService} 129 * always binds to the IME with {@link Context#BIND_FOREGROUND_SERVICE} for now for simplicity. 130 */ 131 private static final int IME_CONNECTION_UNIFIED_BIND_FLAGS = 132 Context.BIND_AUTO_CREATE 133 | Context.BIND_NOT_VISIBLE 134 | Context.BIND_NOT_FOREGROUND 135 | Context.BIND_FOREGROUND_SERVICE; 136 137 private static final ComponentName sImeComponentName = 138 InputMethodSystemProperty.sMultiClientImeComponentName; 139 reportNotSupported()140 private static void reportNotSupported() { 141 if (DEBUG) { 142 Slog.d(TAG, "non-supported operation. callers=" + Debug.getCallers(3)); 143 } 144 } 145 146 /** 147 * {@link MultiClientInputMethodManagerService} is not intended to be instantiated. 148 */ MultiClientInputMethodManagerService()149 private MultiClientInputMethodManagerService() { 150 } 151 152 /** 153 * The implementation of {@link SystemService} for multi-client IME. 154 */ 155 public static final class Lifecycle extends SystemService { 156 private final ApiCallbacks mApiCallbacks; 157 private final OnWorkerThreadCallback mOnWorkerThreadCallback; 158 159 @MainThread Lifecycle(Context context)160 public Lifecycle(Context context) { 161 super(context); 162 163 final UserToInputMethodInfoMap userIdToInputMethodInfoMapper = 164 new UserToInputMethodInfoMap(); 165 final UserDataMap userDataMap = new UserDataMap(); 166 final HandlerThread workerThread = new HandlerThread(TAG); 167 workerThread.start(); 168 mApiCallbacks = new ApiCallbacks(context, userDataMap, userIdToInputMethodInfoMapper); 169 mOnWorkerThreadCallback = new OnWorkerThreadCallback( 170 context, userDataMap, userIdToInputMethodInfoMapper, 171 new Handler(workerThread.getLooper(), msg -> false, true)); 172 173 LocalServices.addService(InputMethodManagerInternal.class, 174 new InputMethodManagerInternal() { 175 @Override 176 public void setInteractive(boolean interactive) { 177 reportNotSupported(); 178 } 179 180 @Override 181 public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) { 182 reportNotSupported(); 183 } 184 185 @Override 186 public List<InputMethodInfo> getInputMethodListAsUser( 187 @UserIdInt int userId) { 188 return userIdToInputMethodInfoMapper.getAsList(userId); 189 } 190 191 @Override 192 public List<InputMethodInfo> getEnabledInputMethodListAsUser( 193 @UserIdInt int userId) { 194 return userIdToInputMethodInfoMapper.getAsList(userId); 195 } 196 197 @Override 198 public void onCreateInlineSuggestionsRequest(int userId, 199 InlineSuggestionsRequestInfo requestInfo, 200 IInlineSuggestionsRequestCallback cb) { 201 try { 202 cb.onInlineSuggestionsUnsupported(); 203 } catch (RemoteException e) { 204 Slog.w(TAG, "Failed to call onInlineSuggestionsUnsupported.", e); 205 } 206 } 207 208 @Override 209 public boolean switchToInputMethod(String imeId, @UserIdInt int userId) { 210 reportNotSupported(); 211 return false; 212 } 213 214 @Override 215 public void registerInputMethodListListener( 216 InputMethodListListener listener) { 217 reportNotSupported(); 218 } 219 220 @Override 221 public boolean transferTouchFocusToImeWindow( 222 @NonNull IBinder sourceInputToken, int displayId) { 223 reportNotSupported(); 224 return false; 225 } 226 227 @Override 228 public void reportImeControl(@Nullable IBinder windowToken, 229 boolean imeParentChanged) { 230 } 231 232 @Override 233 public void removeImeSurface() { 234 reportNotSupported(); 235 } 236 237 @Override 238 public void updateImeWindowStatus(boolean disableImeIcon) { 239 } 240 }); 241 } 242 243 @MainThread 244 @Override onBootPhase(int phase)245 public void onBootPhase(int phase) { 246 mOnWorkerThreadCallback.getHandler().sendMessage(PooledLambda.obtainMessage( 247 OnWorkerThreadCallback::onBootPhase, mOnWorkerThreadCallback, phase)); 248 } 249 250 @MainThread 251 @Override onStart()252 public void onStart() { 253 publishBinderService(Context.INPUT_METHOD_SERVICE, mApiCallbacks); 254 } 255 256 @MainThread 257 @Override onUserStarting(@onNull TargetUser user)258 public void onUserStarting(@NonNull TargetUser user) { 259 mOnWorkerThreadCallback.getHandler().sendMessage(PooledLambda.obtainMessage( 260 OnWorkerThreadCallback::onStartUser, mOnWorkerThreadCallback, 261 user.getUserIdentifier())); 262 } 263 264 @MainThread 265 @Override onUserUnlocking(@onNull TargetUser user)266 public void onUserUnlocking(@NonNull TargetUser user) { 267 mOnWorkerThreadCallback.getHandler().sendMessage(PooledLambda.obtainMessage( 268 OnWorkerThreadCallback::onUnlockUser, mOnWorkerThreadCallback, 269 user.getUserIdentifier())); 270 } 271 272 @MainThread 273 @Override onUserStopping(@onNull TargetUser user)274 public void onUserStopping(@NonNull TargetUser user) { 275 mOnWorkerThreadCallback.getHandler().sendMessage(PooledLambda.obtainMessage( 276 OnWorkerThreadCallback::onStopUser, mOnWorkerThreadCallback, 277 user.getUserIdentifier())); 278 } 279 } 280 281 private static final class OnWorkerThreadCallback { 282 private final Context mContext; 283 private final UserDataMap mUserDataMap; 284 private final UserToInputMethodInfoMap mInputMethodInfoMap; 285 private final Handler mHandler; 286 OnWorkerThreadCallback(Context context, UserDataMap userDataMap, UserToInputMethodInfoMap inputMethodInfoMap, Handler handler)287 OnWorkerThreadCallback(Context context, UserDataMap userDataMap, 288 UserToInputMethodInfoMap inputMethodInfoMap, Handler handler) { 289 mContext = context; 290 mUserDataMap = userDataMap; 291 mInputMethodInfoMap = inputMethodInfoMap; 292 mHandler = handler; 293 } 294 295 @AnyThread getHandler()296 Handler getHandler() { 297 return mHandler; 298 } 299 300 @WorkerThread tryBindInputMethodService(@serIdInt int userId)301 private void tryBindInputMethodService(@UserIdInt int userId) { 302 final PerUserData data = mUserDataMap.get(userId); 303 if (data == null) { 304 Slog.i(TAG, "tryBindInputMethodService is called for an unknown user=" + userId); 305 return; 306 } 307 308 final InputMethodInfo imi = queryInputMethod(mContext, userId, sImeComponentName); 309 if (imi == null) { 310 Slog.w(TAG, "Multi-client InputMethod is not found. component=" 311 + sImeComponentName); 312 synchronized (data.mLock) { 313 switch (data.mState) { 314 case PerUserState.USER_LOCKED: 315 case PerUserState.SERVICE_NOT_QUERIED: 316 case PerUserState.SERVICE_RECOGNIZED: 317 case PerUserState.UNBIND_CALLED: 318 // Safe to clean up. 319 mInputMethodInfoMap.remove(userId); 320 break; 321 } 322 } 323 return; 324 } 325 326 synchronized (data.mLock) { 327 switch (data.mState) { 328 case PerUserState.USER_LOCKED: 329 // If the user is still locked, we currently do not try to start IME. 330 return; 331 case PerUserState.SERVICE_NOT_QUERIED: 332 case PerUserState.SERVICE_RECOGNIZED: 333 case PerUserState.UNBIND_CALLED: 334 break; 335 case PerUserState.WAITING_SERVICE_CONNECTED: 336 case PerUserState.SERVICE_CONNECTED: 337 // OK, nothing to do. 338 return; 339 default: 340 Slog.wtf(TAG, "Unknown state=" + data.mState); 341 return; 342 } 343 data.mState = PerUserState.SERVICE_RECOGNIZED; 344 data.mCurrentInputMethodInfo = imi; 345 mInputMethodInfoMap.put(userId, imi); 346 final boolean bindResult = data.bindServiceLocked(mContext, userId); 347 if (!bindResult) { 348 Slog.e(TAG, "Failed to bind Multi-client InputMethod."); 349 return; 350 } 351 data.mState = PerUserState.WAITING_SERVICE_CONNECTED; 352 } 353 } 354 355 @WorkerThread onStartUser(@serIdInt int userId)356 void onStartUser(@UserIdInt int userId) { 357 if (DEBUG) { 358 Slog.v(TAG, "onStartUser userId=" + userId); 359 } 360 final PerUserData data = new PerUserData(userId, null, PerUserState.USER_LOCKED, this); 361 mUserDataMap.put(userId, data); 362 } 363 364 @WorkerThread onUnlockUser(@serIdInt int userId)365 void onUnlockUser(@UserIdInt int userId) { 366 if (DEBUG) { 367 Slog.v(TAG, "onUnlockUser() userId=" + userId); 368 } 369 final PerUserData data = mUserDataMap.get(userId); 370 if (data == null) { 371 Slog.i(TAG, "onUnlockUser is called for an unknown user=" + userId); 372 return; 373 } 374 synchronized (data.mLock) { 375 switch (data.mState) { 376 case PerUserState.USER_LOCKED: 377 data.mState = PerUserState.SERVICE_NOT_QUERIED; 378 tryBindInputMethodService(userId); 379 break; 380 default: 381 Slog.wtf(TAG, "Unknown state=" + data.mState); 382 break; 383 } 384 } 385 } 386 387 @WorkerThread onStopUser(@serIdInt int userId)388 void onStopUser(@UserIdInt int userId) { 389 if (DEBUG) { 390 Slog.v(TAG, "onStopUser() userId=" + userId); 391 } 392 mInputMethodInfoMap.remove(userId); 393 final PerUserData data = mUserDataMap.removeReturnOld(userId); 394 if (data == null) { 395 Slog.i(TAG, "onStopUser is called for an unknown user=" + userId); 396 return; 397 } 398 synchronized (data.mLock) { 399 switch (data.mState) { 400 case PerUserState.USER_LOCKED: 401 case PerUserState.SERVICE_RECOGNIZED: 402 case PerUserState.UNBIND_CALLED: 403 // OK, nothing to do. 404 return; 405 case PerUserState.SERVICE_CONNECTED: 406 case PerUserState.WAITING_SERVICE_CONNECTED: 407 break; 408 default: 409 Slog.wtf(TAG, "Unknown state=" + data.mState); 410 break; 411 } 412 data.unbindServiceLocked(mContext); 413 data.mState = PerUserState.UNBIND_CALLED; 414 data.mCurrentInputMethod = null; 415 416 // When a Service is explicitly unbound with Context.unbindService(), 417 // onServiceDisconnected() will not be triggered. Hence here we explicitly call 418 // onInputMethodDisconnectedLocked() as if the Service is already gone. 419 data.onInputMethodDisconnectedLocked(); 420 } 421 } 422 423 @WorkerThread onServiceConnected(PerUserData data, IMultiClientInputMethod service)424 void onServiceConnected(PerUserData data, IMultiClientInputMethod service) { 425 if (DEBUG) { 426 Slog.v(TAG, "onServiceConnected() data.mUserId=" + data.mUserId); 427 } 428 synchronized (data.mLock) { 429 switch (data.mState) { 430 case PerUserState.UNBIND_CALLED: 431 // We should ignore this callback. 432 return; 433 case PerUserState.WAITING_SERVICE_CONNECTED: 434 // OK. 435 data.mState = PerUserState.SERVICE_CONNECTED; 436 data.mCurrentInputMethod = service; 437 try { 438 data.mCurrentInputMethod.initialize(new ImeCallbacks(data)); 439 } catch (RemoteException e) { 440 } 441 data.onInputMethodConnectedLocked(); 442 break; 443 default: 444 Slog.wtf(TAG, "Unknown state=" + data.mState); 445 return; 446 } 447 } 448 } 449 450 @WorkerThread onServiceDisconnected(PerUserData data)451 void onServiceDisconnected(PerUserData data) { 452 if (DEBUG) { 453 Slog.v(TAG, "onServiceDisconnected() data.mUserId=" + data.mUserId); 454 } 455 final WindowManagerInternal windowManagerInternal = 456 LocalServices.getService(WindowManagerInternal.class); 457 synchronized (data.mLock) { 458 // We assume the number of tokens would not be that large (up to 10 or so) hence 459 // linear search should be acceptable. 460 final int numTokens = data.mDisplayIdToImeWindowTokenMap.size(); 461 for (int i = 0; i < numTokens; ++i) { 462 final TokenInfo info = data.mDisplayIdToImeWindowTokenMap.valueAt(i); 463 windowManagerInternal.removeWindowToken(info.mToken, false, info.mDisplayId); 464 } 465 data.mDisplayIdToImeWindowTokenMap.clear(); 466 switch (data.mState) { 467 case PerUserState.UNBIND_CALLED: 468 // We should ignore this callback. 469 return; 470 case PerUserState.WAITING_SERVICE_CONNECTED: 471 case PerUserState.SERVICE_CONNECTED: 472 // onServiceDisconnected() means the biding is still alive. 473 data.mState = PerUserState.WAITING_SERVICE_CONNECTED; 474 data.mCurrentInputMethod = null; 475 data.onInputMethodDisconnectedLocked(); 476 break; 477 default: 478 Slog.wtf(TAG, "Unknown state=" + data.mState); 479 return; 480 } 481 } 482 } 483 484 @WorkerThread onBindingDied(PerUserData data)485 void onBindingDied(PerUserData data) { 486 if (DEBUG) { 487 Slog.v(TAG, "onBindingDied() data.mUserId=" + data.mUserId); 488 } 489 final WindowManagerInternal windowManagerInternal = 490 LocalServices.getService(WindowManagerInternal.class); 491 synchronized (data.mLock) { 492 // We assume the number of tokens would not be that large (up to 10 or so) hence 493 // linear search should be acceptable. 494 final int numTokens = data.mDisplayIdToImeWindowTokenMap.size(); 495 for (int i = 0; i < numTokens; ++i) { 496 final TokenInfo info = data.mDisplayIdToImeWindowTokenMap.valueAt(i); 497 windowManagerInternal.removeWindowToken(info.mToken, false, info.mDisplayId); 498 } 499 data.mDisplayIdToImeWindowTokenMap.clear(); 500 switch (data.mState) { 501 case PerUserState.UNBIND_CALLED: 502 // We should ignore this callback. 503 return; 504 case PerUserState.WAITING_SERVICE_CONNECTED: 505 case PerUserState.SERVICE_CONNECTED: { 506 // onBindingDied() means the biding is dead. 507 data.mState = PerUserState.UNBIND_CALLED; 508 data.mCurrentInputMethod = null; 509 data.onInputMethodDisconnectedLocked(); 510 // Schedule a retry 511 mHandler.sendMessageDelayed(PooledLambda.obtainMessage( 512 OnWorkerThreadCallback::tryBindInputMethodService, 513 this, data.mUserId), RECONNECT_DELAY_MSEC); 514 break; 515 } 516 default: 517 Slog.wtf(TAG, "Unknown state=" + data.mState); 518 return; 519 } 520 } 521 } 522 523 @WorkerThread onBootPhase(int phase)524 void onBootPhase(int phase) { 525 if (DEBUG) { 526 Slog.v(TAG, "onBootPhase() phase=" + phase); 527 } 528 switch (phase) { 529 case SystemService.PHASE_ACTIVITY_MANAGER_READY: { 530 final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); 531 filter.addDataScheme("package"); 532 mContext.registerReceiver(new BroadcastReceiver() { 533 @Override 534 public void onReceive(Context context, Intent intent) { 535 onPackageAdded(intent); 536 } 537 }, filter, null, mHandler); 538 break; 539 } 540 case SystemService.PHASE_BOOT_COMPLETED: { 541 final boolean perDisplayFocusEnabled = mContext.getResources().getBoolean( 542 com.android.internal.R.bool.config_perDisplayFocusEnabled); 543 if (!perDisplayFocusEnabled) { 544 final Bundle extras = new Bundle(); 545 extras.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, true); 546 mContext.getSystemService(NotificationManager.class).notifyAsUser(TAG, 547 SystemMessageProto.SystemMessage.NOTE_SELECT_INPUT_METHOD, 548 new Notification.Builder(mContext, 549 SystemNotificationChannels.VIRTUAL_KEYBOARD) 550 .setContentTitle(PER_DISPLAY_FOCUS_DISABLED_WARNING_TITLE) 551 .setStyle(new Notification.BigTextStyle() 552 .bigText(PER_DISPLAY_FOCUS_DISABLED_WARNING_MSG)) 553 .setSmallIcon(R.drawable.ic_notification_ime_default) 554 .setWhen(0) 555 .setOngoing(true) 556 .setLocalOnly(true) 557 .addExtras(extras) 558 .setCategory(Notification.CATEGORY_SYSTEM) 559 .setColor(mContext.getColor( 560 R.color.system_notification_accent_color)) 561 .build(), UserHandle.ALL); 562 } 563 break; 564 } 565 } 566 } 567 568 @WorkerThread onPackageAdded(Intent intent)569 void onPackageAdded(Intent intent) { 570 if (DEBUG) { 571 Slog.v(TAG, "onPackageAdded() intent=" + intent); 572 } 573 final Uri uri = intent.getData(); 574 if (uri == null) { 575 return; 576 } 577 if (!intent.hasExtra(Intent.EXTRA_UID)) { 578 return; 579 } 580 final String packageName = uri.getSchemeSpecificPart(); 581 if (sImeComponentName == null 582 || packageName == null 583 || !TextUtils.equals(sImeComponentName.getPackageName(), packageName)) { 584 return; 585 } 586 final int userId = UserHandle.getUserId(intent.getIntExtra(Intent.EXTRA_UID, 0)); 587 tryBindInputMethodService(userId); 588 } 589 } 590 591 private static final class WindowInfo { 592 final IBinder mWindowToken; 593 final int mWindowHandle; 594 WindowInfo(IBinder windowToken, int windowCookie)595 WindowInfo(IBinder windowToken, int windowCookie) { 596 mWindowToken = windowToken; 597 mWindowHandle = windowCookie; 598 } 599 } 600 601 /** 602 * Describes the state of each IME client. 603 */ 604 @Retention(SOURCE) 605 @IntDef({InputMethodClientState.REGISTERED, 606 InputMethodClientState.WAITING_FOR_IME_SESSION, 607 InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT, 608 InputMethodClientState.ALREADY_SENT_BIND_RESULT, 609 InputMethodClientState.UNREGISTERED}) 610 private @interface InputMethodClientState { 611 /** 612 * {@link IInputMethodManager#addClient(IInputMethodClient, IInputContext, int)} is called 613 * and this client is now recognized by the system. When the system lost the connection to 614 * the current IME, all the clients need to be re-initialized from this state. 615 */ 616 int REGISTERED = 1; 617 /** 618 * This client is notified to the current IME with {@link 619 * IMultiClientInputMethod#addClient(int, int, int, int)} but the IME is not yet responded 620 * with {@link IMultiClientInputMethodPrivilegedOperations#acceptClient(int, 621 * IInputMethodSession, IMultiClientInputMethodSession, InputChannel)}. 622 */ 623 int WAITING_FOR_IME_SESSION = 2; 624 /** 625 * This client is already accepted by the IME but a valid {@link InputBindResult} has not 626 * been returned to the client yet. 627 */ 628 int READY_TO_SEND_FIRST_BIND_RESULT = 3; 629 /** 630 * This client has already received a valid {@link InputBindResult} at least once. This 631 * means that the client can directly call {@link IInputMethodSession} IPCs and key events 632 * via {@link InputChannel}. When the current IME is unbound, these client end points also 633 * need to be cleared. 634 */ 635 int ALREADY_SENT_BIND_RESULT = 4; 636 /** 637 * The client process is dying. 638 */ 639 int UNREGISTERED = 5; 640 } 641 642 private static final class InputMethodClientIdSource { 643 @GuardedBy("InputMethodClientIdSource.class") 644 private static int sNextValue = 0; 645 InputMethodClientIdSource()646 private InputMethodClientIdSource() { 647 } 648 getNext()649 static synchronized int getNext() { 650 final int result = sNextValue; 651 sNextValue++; 652 if (sNextValue < 0) { 653 sNextValue = 0; 654 } 655 return result; 656 } 657 } 658 659 private static final class WindowHandleSource { 660 @GuardedBy("WindowHandleSource.class") 661 private static int sNextValue = 0; 662 WindowHandleSource()663 private WindowHandleSource() { 664 } 665 getNext()666 static synchronized int getNext() { 667 final int result = sNextValue; 668 sNextValue++; 669 if (sNextValue < 0) { 670 sNextValue = 0; 671 } 672 return result; 673 } 674 } 675 676 private static final class InputMethodClientInfo { 677 final IInputMethodClient mClient; 678 final int mUid; 679 final int mPid; 680 final int mSelfReportedDisplayId; 681 final int mClientId; 682 683 @GuardedBy("PerUserData.mLock") 684 @InputMethodClientState 685 int mState; 686 @GuardedBy("PerUserData.mLock") 687 int mBindingSequence; 688 @GuardedBy("PerUserData.mLock") 689 InputChannel mWriteChannel; 690 @GuardedBy("PerUserData.mLock") 691 IInputMethodSession mInputMethodSession; 692 @GuardedBy("PerUserData.mLock") 693 IMultiClientInputMethodSession mMSInputMethodSession; 694 @GuardedBy("PerUserData.mLock") 695 final WeakHashMap<IBinder, WindowInfo> mWindowMap = new WeakHashMap<>(); 696 InputMethodClientInfo(IInputMethodClient client, int uid, int pid, int selfReportedDisplayId)697 InputMethodClientInfo(IInputMethodClient client, int uid, int pid, 698 int selfReportedDisplayId) { 699 mClient = client; 700 mUid = uid; 701 mPid = pid; 702 mSelfReportedDisplayId = selfReportedDisplayId; 703 mClientId = InputMethodClientIdSource.getNext(); 704 } 705 706 @GuardedBy("PerUserData.mLock") dumpLocked(FileDescriptor fd, IndentingPrintWriter ipw, String[] args)707 void dumpLocked(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) { 708 ipw.println("mState=" + mState + ",mBindingSequence=" + mBindingSequence 709 + ",mWriteChannel=" + mWriteChannel 710 + ",mInputMethodSession=" + mInputMethodSession 711 + ",mMSInputMethodSession=" + mMSInputMethodSession); 712 } 713 } 714 715 private static final class UserDataMap { 716 @GuardedBy("mMap") 717 private final SparseArray<PerUserData> mMap = new SparseArray<>(); 718 719 @AnyThread 720 @Nullable get(@serIdInt int userId)721 PerUserData get(@UserIdInt int userId) { 722 synchronized (mMap) { 723 return mMap.get(userId); 724 } 725 } 726 727 @AnyThread put(@serIdInt int userId, PerUserData data)728 void put(@UserIdInt int userId, PerUserData data) { 729 synchronized (mMap) { 730 mMap.put(userId, data); 731 } 732 } 733 734 @AnyThread 735 @Nullable removeReturnOld(@serIdInt int userId)736 PerUserData removeReturnOld(@UserIdInt int userId) { 737 synchronized (mMap) { 738 return mMap.removeReturnOld(userId); 739 } 740 } 741 742 @AnyThread dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args)743 void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) { 744 synchronized (mMap) { 745 for (int i = 0; i < mMap.size(); i++) { 746 int userId = mMap.keyAt(i); 747 PerUserData data = mMap.valueAt(i); 748 ipw.println("userId=" + userId + ", data="); 749 if (data != null) { 750 ipw.increaseIndent(); 751 data.dump(fd, ipw, args); 752 ipw.decreaseIndent(); 753 } 754 } 755 } 756 } 757 } 758 759 private static final class TokenInfo { 760 final Binder mToken; 761 final int mDisplayId; TokenInfo(Binder token, int displayId)762 TokenInfo(Binder token, int displayId) { 763 mToken = token; 764 mDisplayId = displayId; 765 } 766 } 767 768 @Retention(SOURCE) 769 @IntDef({ 770 PerUserState.USER_LOCKED, 771 PerUserState.SERVICE_NOT_QUERIED, 772 PerUserState.SERVICE_RECOGNIZED, 773 PerUserState.WAITING_SERVICE_CONNECTED, 774 PerUserState.SERVICE_CONNECTED, 775 PerUserState.UNBIND_CALLED}) 776 private @interface PerUserState { 777 /** 778 * The user is still locked. 779 */ 780 int USER_LOCKED = 1; 781 /** 782 * The system has not queried whether there is a multi-client IME or not. 783 */ 784 int SERVICE_NOT_QUERIED = 2; 785 /** 786 * A multi-client IME specified in {@link #PROP_DEBUG_MULTI_CLIENT_IME} is found in the 787 * system, but not bound yet. 788 */ 789 int SERVICE_RECOGNIZED = 3; 790 /** 791 * {@link Context#bindServiceAsUser(Intent, ServiceConnection, int, Handler, UserHandle)} is 792 * already called for the IME but 793 * {@link ServiceConnection#onServiceConnected(ComponentName, IBinder)} is not yet called 794 * back. This includes once the IME is bound but temporarily disconnected as notified with 795 * {@link ServiceConnection#onServiceDisconnected(ComponentName)}. 796 */ 797 int WAITING_SERVICE_CONNECTED = 4; 798 /** 799 * {@link ServiceConnection#onServiceConnected(ComponentName, IBinder)} is already called 800 * back. The IME is ready to be used. 801 */ 802 int SERVICE_CONNECTED = 5; 803 /** 804 * The binding is gone. Either {@link Context#unbindService(ServiceConnection)} is 805 * explicitly called or the system decided to destroy the binding as notified with 806 * {@link ServiceConnection#onBindingDied(ComponentName)}. 807 */ 808 int UNBIND_CALLED = 6; 809 } 810 811 /** 812 * Takes care of per-user state separation. 813 */ 814 private static final class PerUserData { 815 final Object mLock = new Object(); 816 817 /** 818 * User ID (not UID) that is associated with this data. 819 */ 820 @UserIdInt 821 private final int mUserId; 822 823 /** 824 * {@link IMultiClientInputMethod} of the currently connected multi-client IME. This 825 * must be non-{@code null} only while {@link #mState} is 826 * {@link PerUserState#SERVICE_CONNECTED}. 827 */ 828 @Nullable 829 @GuardedBy("mLock") 830 IMultiClientInputMethod mCurrentInputMethod; 831 832 /** 833 * {@link InputMethodInfo} of the currently selected multi-client IME. This must be 834 * non-{@code null} unless {@link #mState} is {@link PerUserState#SERVICE_NOT_QUERIED}. 835 */ 836 @GuardedBy("mLock") 837 @Nullable 838 InputMethodInfo mCurrentInputMethodInfo; 839 840 /** 841 * Describes the current service state. 842 */ 843 @GuardedBy("mLock") 844 @PerUserState 845 int mState; 846 847 /** 848 * A {@link SparseArray} that maps display ID to IME Window token that is already issued to 849 * the IME. 850 */ 851 @GuardedBy("mLock") 852 final ArraySet<TokenInfo> mDisplayIdToImeWindowTokenMap = new ArraySet<>(); 853 854 @GuardedBy("mLock") 855 private final ArrayMap<IBinder, InputMethodClientInfo> mClientMap = new ArrayMap<>(); 856 857 @GuardedBy("mLock") 858 private SparseArray<InputMethodClientInfo> mClientIdToClientMap = new SparseArray<>(); 859 860 private final OnWorkerThreadServiceConnection mOnWorkerThreadServiceConnection; 861 862 /** 863 * A {@link ServiceConnection} that is designed to run on a certain worker thread with 864 * which {@link OnWorkerThreadCallback} is associated. 865 * 866 * @see Context#bindServiceAsUser(Intent, ServiceConnection, int, Handler, UserHandle). 867 */ 868 private static final class OnWorkerThreadServiceConnection implements ServiceConnection { 869 private final PerUserData mData; 870 private final OnWorkerThreadCallback mCallback; 871 OnWorkerThreadServiceConnection(PerUserData data, OnWorkerThreadCallback callback)872 OnWorkerThreadServiceConnection(PerUserData data, OnWorkerThreadCallback callback) { 873 mData = data; 874 mCallback = callback; 875 } 876 877 @WorkerThread 878 @Override onServiceConnected(ComponentName name, IBinder service)879 public void onServiceConnected(ComponentName name, IBinder service) { 880 mCallback.onServiceConnected(mData, 881 IMultiClientInputMethod.Stub.asInterface(service)); 882 } 883 884 @WorkerThread 885 @Override onServiceDisconnected(ComponentName name)886 public void onServiceDisconnected(ComponentName name) { 887 mCallback.onServiceDisconnected(mData); 888 } 889 890 @WorkerThread 891 @Override onBindingDied(ComponentName name)892 public void onBindingDied(ComponentName name) { 893 mCallback.onBindingDied(mData); 894 } 895 getHandler()896 Handler getHandler() { 897 return mCallback.getHandler(); 898 } 899 } 900 PerUserData(@serIdInt int userId, @Nullable InputMethodInfo inputMethodInfo, @PerUserState int initialState, OnWorkerThreadCallback callback)901 PerUserData(@UserIdInt int userId, @Nullable InputMethodInfo inputMethodInfo, 902 @PerUserState int initialState, OnWorkerThreadCallback callback) { 903 mUserId = userId; 904 mCurrentInputMethodInfo = inputMethodInfo; 905 mState = initialState; 906 mOnWorkerThreadServiceConnection = 907 new OnWorkerThreadServiceConnection(this, callback); 908 } 909 910 @GuardedBy("mLock") bindServiceLocked(Context context, @UserIdInt int userId)911 boolean bindServiceLocked(Context context, @UserIdInt int userId) { 912 final Intent intent = 913 new Intent(MultiClientInputMethodServiceDelegate.SERVICE_INTERFACE) 914 .setComponent(mCurrentInputMethodInfo.getComponent()) 915 .putExtra(Intent.EXTRA_CLIENT_LABEL, 916 com.android.internal.R.string.input_method_binding_label) 917 .putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( 918 context, 0, 919 new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 920 PendingIntent.FLAG_IMMUTABLE)); 921 922 // Note: Instead of re-dispatching callback from the main thread to the worker thread 923 // where OnWorkerThreadCallback is running, we pass the Handler object here so that 924 // the callbacks will be directly dispatched to the worker thread. 925 return context.bindServiceAsUser(intent, mOnWorkerThreadServiceConnection, 926 IME_CONNECTION_UNIFIED_BIND_FLAGS, 927 mOnWorkerThreadServiceConnection.getHandler(), UserHandle.of(userId)); 928 } 929 930 @GuardedBy("mLock") unbindServiceLocked(Context context)931 void unbindServiceLocked(Context context) { 932 context.unbindService(mOnWorkerThreadServiceConnection); 933 } 934 935 @GuardedBy("mLock") 936 @Nullable getClientLocked(IInputMethodClient client)937 InputMethodClientInfo getClientLocked(IInputMethodClient client) { 938 return mClientMap.get(client.asBinder()); 939 } 940 941 @GuardedBy("mLock") 942 @Nullable getClientFromIdLocked(int clientId)943 InputMethodClientInfo getClientFromIdLocked(int clientId) { 944 return mClientIdToClientMap.get(clientId); 945 } 946 947 @GuardedBy("mLock") 948 @Nullable removeClientLocked(IInputMethodClient client)949 InputMethodClientInfo removeClientLocked(IInputMethodClient client) { 950 final InputMethodClientInfo info = mClientMap.remove(client.asBinder()); 951 if (info != null) { 952 mClientIdToClientMap.remove(info.mClientId); 953 } 954 return info; 955 } 956 957 @GuardedBy("mLock") addClientLocked(int uid, int pid, IInputMethodClient client, int selfReportedDisplayId)958 void addClientLocked(int uid, int pid, IInputMethodClient client, 959 int selfReportedDisplayId) { 960 if (getClientLocked(client) != null) { 961 Slog.wtf(TAG, "The same client is added multiple times"); 962 return; 963 } 964 final ClientDeathRecipient deathRecipient = new ClientDeathRecipient(this, client); 965 try { 966 client.asBinder().linkToDeath(deathRecipient, 0); 967 } catch (RemoteException e) { 968 throw new IllegalStateException(e); 969 } 970 final InputMethodClientInfo clientInfo = 971 new InputMethodClientInfo(client, uid, pid, selfReportedDisplayId); 972 clientInfo.mState = InputMethodClientState.REGISTERED; 973 mClientMap.put(client.asBinder(), clientInfo); 974 mClientIdToClientMap.put(clientInfo.mClientId, clientInfo); 975 switch (mState) { 976 case PerUserState.SERVICE_CONNECTED: 977 try { 978 mCurrentInputMethod.addClient( 979 clientInfo.mClientId, clientInfo.mPid, clientInfo.mUid, 980 clientInfo.mSelfReportedDisplayId); 981 clientInfo.mState = InputMethodClientState.WAITING_FOR_IME_SESSION; 982 } catch (RemoteException e) { 983 // TODO(yukawa): Need logging and expected behavior 984 } 985 break; 986 } 987 } 988 989 @GuardedBy("mLock") onInputMethodConnectedLocked()990 void onInputMethodConnectedLocked() { 991 final int numClients = mClientMap.size(); 992 for (int i = 0; i < numClients; ++i) { 993 final InputMethodClientInfo clientInfo = mClientMap.valueAt(i); 994 switch (clientInfo.mState) { 995 case InputMethodClientState.REGISTERED: 996 // OK 997 break; 998 default: 999 Slog.e(TAG, "Unexpected state=" + clientInfo.mState); 1000 return; 1001 } 1002 try { 1003 mCurrentInputMethod.addClient( 1004 clientInfo.mClientId, clientInfo.mUid, clientInfo.mPid, 1005 clientInfo.mSelfReportedDisplayId); 1006 clientInfo.mState = InputMethodClientState.WAITING_FOR_IME_SESSION; 1007 } catch (RemoteException e) { 1008 } 1009 } 1010 } 1011 1012 @GuardedBy("mLock") onInputMethodDisconnectedLocked()1013 void onInputMethodDisconnectedLocked() { 1014 final int numClients = mClientMap.size(); 1015 for (int i = 0; i < numClients; ++i) { 1016 final InputMethodClientInfo clientInfo = mClientMap.valueAt(i); 1017 switch (clientInfo.mState) { 1018 case InputMethodClientState.REGISTERED: 1019 // Disconnected before onInputMethodConnectedLocked(). 1020 break; 1021 case InputMethodClientState.WAITING_FOR_IME_SESSION: 1022 // Disconnected between addClient() and acceptClient(). 1023 clientInfo.mState = InputMethodClientState.REGISTERED; 1024 break; 1025 case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT: 1026 clientInfo.mState = InputMethodClientState.REGISTERED; 1027 clientInfo.mInputMethodSession = null; 1028 clientInfo.mMSInputMethodSession = null; 1029 if (clientInfo.mWriteChannel != null) { 1030 clientInfo.mWriteChannel.dispose(); 1031 clientInfo.mWriteChannel = null; 1032 } 1033 break; 1034 case InputMethodClientState.ALREADY_SENT_BIND_RESULT: 1035 try { 1036 clientInfo.mClient.onUnbindMethod(clientInfo.mBindingSequence, 1037 UnbindReason.DISCONNECT_IME); 1038 } catch (RemoteException e) { 1039 } 1040 clientInfo.mState = InputMethodClientState.REGISTERED; 1041 clientInfo.mInputMethodSession = null; 1042 clientInfo.mMSInputMethodSession = null; 1043 if (clientInfo.mWriteChannel != null) { 1044 clientInfo.mWriteChannel.dispose(); 1045 clientInfo.mWriteChannel = null; 1046 } 1047 break; 1048 } 1049 } 1050 } 1051 1052 @AnyThread dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args)1053 void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) { 1054 synchronized (mLock) { 1055 ipw.println("mState=" + mState 1056 + ",mCurrentInputMethod=" + mCurrentInputMethod 1057 + ",mCurrentInputMethodInfo=" + mCurrentInputMethodInfo); 1058 1059 if (mCurrentInputMethod != null) { 1060 // indentation will not be kept. So add visual separator here. 1061 ipw.println(">>Dump CurrentInputMethod>>"); 1062 ipw.flush(); 1063 try { 1064 TransferPipe.dumpAsync(mCurrentInputMethod.asBinder(), fd, args); 1065 } catch (IOException | RemoteException e) { 1066 ipw.println("Failed to dump input method service: " + e); 1067 } 1068 ipw.println("<<Dump CurrentInputMethod<<"); 1069 } 1070 1071 ipw.println("mDisplayIdToImeWindowTokenMap="); 1072 for (TokenInfo info : mDisplayIdToImeWindowTokenMap) { 1073 ipw.println(" display=" + info.mDisplayId + ",token=" 1074 + info.mToken); 1075 } 1076 ipw.println("mClientMap="); 1077 ipw.increaseIndent(); 1078 for (int i = 0; i < mClientMap.size(); i++) { 1079 1080 ipw.println("binder=" + mClientMap.keyAt(i)); 1081 ipw.println(" InputMethodClientInfo="); 1082 InputMethodClientInfo info = mClientMap.valueAt(i); 1083 if (info != null) { 1084 ipw.increaseIndent(); 1085 info.dumpLocked(fd, ipw, args); 1086 ipw.decreaseIndent(); 1087 } 1088 } 1089 ipw.decreaseIndent(); 1090 ipw.println("mClientIdToClientMap="); 1091 ipw.increaseIndent(); 1092 for (int i = 0; i < mClientIdToClientMap.size(); i++) { 1093 ipw.println("clientId=" + mClientIdToClientMap.keyAt(i)); 1094 ipw.println(" InputMethodClientInfo="); 1095 InputMethodClientInfo info = mClientIdToClientMap.valueAt(i); 1096 if (info != null) { 1097 ipw.increaseIndent(); 1098 info.dumpLocked(fd, ipw, args); 1099 ipw.decreaseIndent(); 1100 } 1101 if (info.mClient != null) { 1102 // indentation will not be kept. So add visual separator here. 1103 ipw.println(">>DumpClientStart>>"); 1104 ipw.flush(); // all writes should be flushed to guarantee order. 1105 try { 1106 TransferPipe.dumpAsync(info.mClient.asBinder(), fd, args); 1107 } catch (IOException | RemoteException e) { 1108 ipw.println(" Failed to dump client:" + e); 1109 } 1110 ipw.println("<<DumpClientEnd<<"); 1111 } 1112 } 1113 ipw.decreaseIndent(); 1114 } 1115 } 1116 1117 private static final class ClientDeathRecipient implements IBinder.DeathRecipient { 1118 private final PerUserData mPerUserData; 1119 private final IInputMethodClient mClient; 1120 ClientDeathRecipient(PerUserData perUserData, IInputMethodClient client)1121 ClientDeathRecipient(PerUserData perUserData, IInputMethodClient client) { 1122 mPerUserData = perUserData; 1123 mClient = client; 1124 } 1125 1126 @BinderThread 1127 @Override binderDied()1128 public void binderDied() { 1129 synchronized (mPerUserData.mLock) { 1130 mClient.asBinder().unlinkToDeath(this, 0); 1131 1132 final InputMethodClientInfo clientInfo = 1133 mPerUserData.removeClientLocked(mClient); 1134 if (clientInfo == null) { 1135 return; 1136 } 1137 1138 if (clientInfo.mWriteChannel != null) { 1139 clientInfo.mWriteChannel.dispose(); 1140 clientInfo.mWriteChannel = null; 1141 } 1142 if (clientInfo.mInputMethodSession != null) { 1143 try { 1144 clientInfo.mInputMethodSession.finishSession(); 1145 } catch (RemoteException e) { 1146 } 1147 clientInfo.mInputMethodSession = null; 1148 } 1149 clientInfo.mMSInputMethodSession = null; 1150 clientInfo.mState = InputMethodClientState.UNREGISTERED; 1151 switch (mPerUserData.mState) { 1152 case PerUserState.SERVICE_CONNECTED: 1153 try { 1154 mPerUserData.mCurrentInputMethod.removeClient(clientInfo.mClientId); 1155 } catch (RemoteException e) { 1156 // TODO(yukawa): Need logging and expected behavior 1157 } 1158 break; 1159 } 1160 } 1161 } 1162 } 1163 } 1164 1165 /** 1166 * Queries for multi-client IME specified with {@code componentName}. 1167 * 1168 * @param context {@link Context} to be used to query component. 1169 * @param userId User ID for which the multi-client IME is queried. 1170 * @param componentName {@link ComponentName} to be queried. 1171 * @return {@link InputMethodInfo} when multi-client IME is found. Otherwise {@code null}. 1172 */ 1173 @Nullable queryInputMethod(Context context, @UserIdInt int userId, @Nullable ComponentName componentName)1174 private static InputMethodInfo queryInputMethod(Context context, @UserIdInt int userId, 1175 @Nullable ComponentName componentName) { 1176 if (componentName == null) { 1177 Slog.w(TAG, "queryInputMethod invoked with null componentName"); 1178 return null; 1179 } 1180 1181 // Use for queryIntentServicesAsUser 1182 final PackageManager pm = context.getPackageManager(); 1183 final List<ResolveInfo> services = pm.queryIntentServicesAsUser( 1184 new Intent(MultiClientInputMethodServiceDelegate.SERVICE_INTERFACE) 1185 .setComponent(componentName), 1186 PackageManager.GET_META_DATA, userId); 1187 try { 1188 return new InputMethodInfo(context, resolveMultiClientImeService(services)); 1189 } catch (Exception e) { 1190 Slog.wtf(TAG, "Unable to load input method from services (" + services + ")", e); 1191 } 1192 return null; 1193 } 1194 1195 /** 1196 * Determines the multi-client IME from the specified {@link List<ResolveInfo>}. 1197 * 1198 * @return {@link ResolveInfo} when an appropriate multi-client IME is found. 1199 * Otherwise {@code null}. 1200 */ 1201 @Nullable 1202 @VisibleForTesting resolveMultiClientImeService(@onNull List<ResolveInfo> services)1203 static ResolveInfo resolveMultiClientImeService(@NonNull List<ResolveInfo> services) { 1204 if (services.isEmpty()) { 1205 Slog.e(TAG, "No IME found"); 1206 return null; 1207 } 1208 if (services.size() > 1) { 1209 Slog.e(TAG, "Only one IME service is supported."); 1210 return null; 1211 } 1212 final ResolveInfo ri = services.get(0); 1213 ServiceInfo si = ri.serviceInfo; 1214 final String imeId = InputMethodInfo.computeId(ri); 1215 if (!android.Manifest.permission.BIND_INPUT_METHOD.equals(si.permission)) { 1216 Slog.e(TAG, imeId + " must have required" 1217 + android.Manifest.permission.BIND_INPUT_METHOD); 1218 return null; 1219 } 1220 if (!Build.IS_DEBUGGABLE && (si.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { 1221 Slog.e(TAG, imeId + " must be pre-installed when Build.IS_DEBUGGABLE is false"); 1222 return null; 1223 } 1224 return ri; 1225 } 1226 1227 /** 1228 * Manages the mapping rule from user ID to {@link InputMethodInfo}. 1229 */ 1230 private static final class UserToInputMethodInfoMap { 1231 @GuardedBy("mArray") 1232 private final SparseArray<InputMethodInfo> mArray = new SparseArray<>(); 1233 1234 @AnyThread put(@serIdInt int userId, InputMethodInfo imi)1235 void put(@UserIdInt int userId, InputMethodInfo imi) { 1236 synchronized (mArray) { 1237 mArray.put(userId, imi); 1238 } 1239 } 1240 1241 @AnyThread remove(@serIdInt int userId)1242 void remove(@UserIdInt int userId) { 1243 synchronized (mArray) { 1244 mArray.remove(userId); 1245 } 1246 } 1247 1248 @AnyThread 1249 @Nullable get(@serIdInt int userId)1250 InputMethodInfo get(@UserIdInt int userId) { 1251 synchronized (mArray) { 1252 return mArray.get(userId); 1253 } 1254 } 1255 1256 @AnyThread getAsList(@serIdInt int userId)1257 List<InputMethodInfo> getAsList(@UserIdInt int userId) { 1258 final InputMethodInfo info = get(userId); 1259 if (info == null) { 1260 return Collections.emptyList(); 1261 } 1262 return Collections.singletonList(info); 1263 } 1264 1265 @AnyThread dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args)1266 void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) { 1267 synchronized (mArray) { 1268 for (int i = 0; i < mArray.size(); i++) { 1269 ipw.println("userId=" + mArray.keyAt(i)); 1270 ipw.println(" InputMethodInfo=" + mArray.valueAt(i)); 1271 } 1272 } 1273 } 1274 } 1275 1276 /** 1277 * Takes care of IPCs exposed to the multi-client IME. 1278 */ 1279 private static final class ImeCallbacks 1280 extends IMultiClientInputMethodPrivilegedOperations.Stub { 1281 private final PerUserData mPerUserData; 1282 private final WindowManagerInternal mIWindowManagerInternal; 1283 ImeCallbacks(PerUserData perUserData)1284 ImeCallbacks(PerUserData perUserData) { 1285 mPerUserData = perUserData; 1286 mIWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); 1287 } 1288 1289 @BinderThread 1290 @Override createInputMethodWindowToken(int displayId)1291 public IBinder createInputMethodWindowToken(int displayId) { 1292 synchronized (mPerUserData.mLock) { 1293 // We assume the number of tokens would not be that large (up to 10 or so) hence 1294 // linear search should be acceptable. 1295 final int numTokens = mPerUserData.mDisplayIdToImeWindowTokenMap.size(); 1296 for (int i = 0; i < numTokens; ++i) { 1297 final TokenInfo tokenInfo = 1298 mPerUserData.mDisplayIdToImeWindowTokenMap.valueAt(i); 1299 // Currently we issue up to one window token per display. 1300 if (tokenInfo.mDisplayId == displayId) { 1301 return tokenInfo.mToken; 1302 } 1303 } 1304 1305 final Binder token = new Binder(); 1306 Binder.withCleanCallingIdentity( 1307 PooledLambda.obtainRunnable(WindowManagerInternal::addWindowToken, 1308 mIWindowManagerInternal, token, TYPE_INPUT_METHOD, displayId, 1309 null /* options */)); 1310 mPerUserData.mDisplayIdToImeWindowTokenMap.add(new TokenInfo(token, displayId)); 1311 return token; 1312 } 1313 } 1314 1315 @BinderThread 1316 @Override deleteInputMethodWindowToken(IBinder token)1317 public void deleteInputMethodWindowToken(IBinder token) { 1318 synchronized (mPerUserData.mLock) { 1319 // We assume the number of tokens would not be that large (up to 10 or so) hence 1320 // linear search should be acceptable. 1321 final int numTokens = mPerUserData.mDisplayIdToImeWindowTokenMap.size(); 1322 for (int i = 0; i < numTokens; ++i) { 1323 final TokenInfo tokenInfo = 1324 mPerUserData.mDisplayIdToImeWindowTokenMap.valueAt(i); 1325 if (tokenInfo.mToken == token) { 1326 mPerUserData.mDisplayIdToImeWindowTokenMap.remove(tokenInfo); 1327 break; 1328 } 1329 } 1330 } 1331 } 1332 1333 @BinderThread 1334 @Override acceptClient(int clientId, IInputMethodSession inputMethodSession, IMultiClientInputMethodSession multiSessionInputMethodSession, InputChannel writeChannel)1335 public void acceptClient(int clientId, IInputMethodSession inputMethodSession, 1336 IMultiClientInputMethodSession multiSessionInputMethodSession, 1337 InputChannel writeChannel) { 1338 synchronized (mPerUserData.mLock) { 1339 final InputMethodClientInfo clientInfo = 1340 mPerUserData.getClientFromIdLocked(clientId); 1341 if (clientInfo == null) { 1342 Slog.e(TAG, "Unknown clientId=" + clientId); 1343 return; 1344 } 1345 switch (clientInfo.mState) { 1346 case InputMethodClientState.WAITING_FOR_IME_SESSION: 1347 try { 1348 clientInfo.mClient.setActive(true, false, false); 1349 } catch (RemoteException e) { 1350 // TODO(yukawa): Remove this client. 1351 return; 1352 } 1353 clientInfo.mState = InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT; 1354 clientInfo.mWriteChannel = writeChannel; 1355 clientInfo.mInputMethodSession = inputMethodSession; 1356 clientInfo.mMSInputMethodSession = multiSessionInputMethodSession; 1357 break; 1358 default: 1359 Slog.e(TAG, "Unexpected state=" + clientInfo.mState); 1360 break; 1361 } 1362 } 1363 } 1364 1365 @BinderThread 1366 @Override reportImeWindowTarget(int clientId, int targetWindowHandle, IBinder imeWindowToken)1367 public void reportImeWindowTarget(int clientId, int targetWindowHandle, 1368 IBinder imeWindowToken) { 1369 synchronized (mPerUserData.mLock) { 1370 final InputMethodClientInfo clientInfo = 1371 mPerUserData.getClientFromIdLocked(clientId); 1372 if (clientInfo == null) { 1373 Slog.e(TAG, "Unknown clientId=" + clientId); 1374 return; 1375 } 1376 for (WindowInfo windowInfo : clientInfo.mWindowMap.values()) { 1377 if (windowInfo.mWindowHandle == targetWindowHandle) { 1378 final IBinder targetWindowToken = windowInfo.mWindowToken; 1379 if (DEBUG) { 1380 Slog.v(TAG, "reportImeWindowTarget" 1381 + " clientId=" + clientId 1382 + " imeWindowToken=" + imeWindowToken 1383 + " targetWindowToken=" + targetWindowToken); 1384 } 1385 mIWindowManagerInternal.updateInputMethodTargetWindow( 1386 imeWindowToken, targetWindowToken); 1387 } 1388 } 1389 // not found. 1390 } 1391 } 1392 1393 @BinderThread 1394 @Override isUidAllowedOnDisplay(int displayId, int uid)1395 public boolean isUidAllowedOnDisplay(int displayId, int uid) { 1396 return mIWindowManagerInternal.isUidAllowedOnDisplay(displayId, uid); 1397 } 1398 1399 @BinderThread 1400 @Override setActive(int clientId, boolean active)1401 public void setActive(int clientId, boolean active) { 1402 synchronized (mPerUserData.mLock) { 1403 final InputMethodClientInfo clientInfo = 1404 mPerUserData.getClientFromIdLocked(clientId); 1405 if (clientInfo == null) { 1406 Slog.e(TAG, "Unknown clientId=" + clientId); 1407 return; 1408 } 1409 try { 1410 clientInfo.mClient.setActive(active, false /* fullscreen */, false); 1411 } catch (RemoteException e) { 1412 return; 1413 } 1414 } 1415 } 1416 } 1417 1418 /** 1419 * Takes care of IPCs exposed to the IME client. 1420 */ 1421 private static final class ApiCallbacks extends IInputMethodManager.Stub { 1422 private final Context mContext; 1423 private final UserDataMap mUserDataMap; 1424 private final UserToInputMethodInfoMap mInputMethodInfoMap; 1425 private final AppOpsManager mAppOpsManager; 1426 private final WindowManagerInternal mWindowManagerInternal; 1427 ApiCallbacks(Context context, UserDataMap userDataMap, UserToInputMethodInfoMap inputMethodInfoMap)1428 ApiCallbacks(Context context, UserDataMap userDataMap, 1429 UserToInputMethodInfoMap inputMethodInfoMap) { 1430 mContext = context; 1431 mUserDataMap = userDataMap; 1432 mInputMethodInfoMap = inputMethodInfoMap; 1433 mAppOpsManager = context.getSystemService(AppOpsManager.class); 1434 mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); 1435 } 1436 1437 @AnyThread checkFocus(int uid, int pid, int displayId)1438 private boolean checkFocus(int uid, int pid, int displayId) { 1439 return mWindowManagerInternal.isInputMethodClientFocus(uid, pid, displayId); 1440 } 1441 1442 @BinderThread 1443 @Override addClient(IInputMethodClient client, IInputContext inputContext, int selfReportedDisplayId)1444 public void addClient(IInputMethodClient client, IInputContext inputContext, 1445 int selfReportedDisplayId) { 1446 final int callingUid = Binder.getCallingUid(); 1447 final int callingPid = Binder.getCallingPid(); 1448 final int userId = UserHandle.getUserId(callingUid); 1449 final PerUserData data = mUserDataMap.get(userId); 1450 if (data == null) { 1451 Slog.e(TAG, "addClient() from unknown userId=" + userId 1452 + " uid=" + callingUid + " pid=" + callingPid); 1453 return; 1454 } 1455 synchronized (data.mLock) { 1456 data.addClientLocked(callingUid, callingPid, client, selfReportedDisplayId); 1457 } 1458 } 1459 1460 @BinderThread 1461 @Override getInputMethodList(@serIdInt int userId)1462 public List<InputMethodInfo> getInputMethodList(@UserIdInt int userId) { 1463 if (UserHandle.getCallingUserId() != userId) { 1464 mContext.enforceCallingPermission(INTERACT_ACROSS_USERS_FULL, null); 1465 } 1466 return mInputMethodInfoMap.getAsList(userId); 1467 } 1468 1469 @BinderThread 1470 @Override getEnabledInputMethodList(@serIdInt int userId)1471 public List<InputMethodInfo> getEnabledInputMethodList(@UserIdInt int userId) { 1472 if (UserHandle.getCallingUserId() != userId) { 1473 mContext.enforceCallingPermission(INTERACT_ACROSS_USERS_FULL, null); 1474 } 1475 return mInputMethodInfoMap.getAsList(userId); 1476 } 1477 1478 @BinderThread 1479 @Override getEnabledInputMethodSubtypeList(String imiId, boolean allowsImplicitlySelectedSubtypes)1480 public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId, 1481 boolean allowsImplicitlySelectedSubtypes) { 1482 reportNotSupported(); 1483 return Collections.emptyList(); 1484 } 1485 1486 @BinderThread 1487 @Override getLastInputMethodSubtype()1488 public InputMethodSubtype getLastInputMethodSubtype() { 1489 reportNotSupported(); 1490 return null; 1491 } 1492 1493 @BinderThread 1494 @Override removeImeSurface()1495 public void removeImeSurface() { 1496 reportNotSupported(); 1497 } 1498 1499 @BinderThread 1500 @Override removeImeSurfaceFromWindowAsync(IBinder windowToken)1501 public void removeImeSurfaceFromWindowAsync(IBinder windowToken) { 1502 reportNotSupported(); 1503 } 1504 1505 @BinderThread 1506 @Override showSoftInput( IInputMethodClient client, IBinder token, int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason)1507 public boolean showSoftInput( 1508 IInputMethodClient client, IBinder token, int flags, ResultReceiver resultReceiver, 1509 @SoftInputShowHideReason int reason) { 1510 return showSoftInputInternal(client, token, flags, resultReceiver); 1511 } 1512 1513 @BinderThread showSoftInputInternal( IInputMethodClient client, IBinder token, int flags, ResultReceiver resultReceiver)1514 private boolean showSoftInputInternal( 1515 IInputMethodClient client, IBinder token, int flags, 1516 ResultReceiver resultReceiver) { 1517 final int callingUid = Binder.getCallingUid(); 1518 final int callingPid = Binder.getCallingPid(); 1519 final int userId = UserHandle.getUserId(callingUid); 1520 final PerUserData data = mUserDataMap.get(userId); 1521 if (data == null) { 1522 Slog.e(TAG, "showSoftInput() from unknown userId=" + userId 1523 + " uid=" + callingUid + " pid=" + callingPid); 1524 return false; 1525 } 1526 synchronized (data.mLock) { 1527 final InputMethodClientInfo clientInfo = data.getClientLocked(client); 1528 if (clientInfo == null) { 1529 Slog.e(TAG, "showSoftInput. client not found. ignoring."); 1530 return false; 1531 } 1532 if (clientInfo.mUid != callingUid) { 1533 Slog.e(TAG, "Expected calling UID=" + clientInfo.mUid 1534 + " actual=" + callingUid); 1535 return false; 1536 } 1537 switch (clientInfo.mState) { 1538 case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT: 1539 case InputMethodClientState.ALREADY_SENT_BIND_RESULT: 1540 try { 1541 clientInfo.mMSInputMethodSession.showSoftInput(flags, resultReceiver); 1542 1543 // Forcing WM to show IME on imeTargetWindow 1544 mWindowManagerInternal.showImePostLayout(token); 1545 } catch (RemoteException e) { 1546 } 1547 break; 1548 default: 1549 if (DEBUG) { 1550 Slog.e(TAG, "Ignoring showSoftInput(). clientState=" 1551 + clientInfo.mState); 1552 } 1553 break; 1554 } 1555 return true; 1556 } 1557 } 1558 1559 @BinderThread 1560 @Override hideSoftInput( IInputMethodClient client, IBinder windowToken, int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason)1561 public boolean hideSoftInput( 1562 IInputMethodClient client, IBinder windowToken, int flags, 1563 ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { 1564 return hideSoftInputInternal(client, windowToken, flags, resultReceiver); 1565 } 1566 1567 @BinderThread hideSoftInputInternal( IInputMethodClient client, IBinder windowToken, int flags, ResultReceiver resultReceiver)1568 private boolean hideSoftInputInternal( 1569 IInputMethodClient client, IBinder windowToken, int flags, 1570 ResultReceiver resultReceiver) { 1571 final int callingUid = Binder.getCallingUid(); 1572 final int callingPid = Binder.getCallingPid(); 1573 final int userId = UserHandle.getUserId(callingUid); 1574 final PerUserData data = mUserDataMap.get(userId); 1575 if (data == null) { 1576 Slog.e(TAG, "hideSoftInput() from unknown userId=" + userId 1577 + " uid=" + callingUid + " pid=" + callingPid); 1578 return false; 1579 } 1580 synchronized (data.mLock) { 1581 final InputMethodClientInfo clientInfo = data.getClientLocked(client); 1582 if (clientInfo == null) { 1583 return false; 1584 } 1585 if (clientInfo.mUid != callingUid) { 1586 Slog.e(TAG, "Expected calling UID=" + clientInfo.mUid 1587 + " actual=" + callingUid); 1588 return false; 1589 } 1590 switch (clientInfo.mState) { 1591 case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT: 1592 case InputMethodClientState.ALREADY_SENT_BIND_RESULT: 1593 try { 1594 clientInfo.mMSInputMethodSession.hideSoftInput(flags, resultReceiver); 1595 } catch (RemoteException e) { 1596 } 1597 break; 1598 default: 1599 if (DEBUG) { 1600 Slog.e(TAG, "Ignoring hideSoftInput(). clientState=" 1601 + clientInfo.mState); 1602 } 1603 break; 1604 } 1605 return true; 1606 } 1607 } 1608 1609 @BinderThread 1610 @Override startInputOrWindowGainedFocus( @tartInputReason int startInputReason, @Nullable IInputMethodClient client, @Nullable IBinder windowToken, @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode, int windowFlags, @Nullable EditorInfo editorInfo, @Nullable IInputContext inputContext, @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion)1611 public InputBindResult startInputOrWindowGainedFocus( 1612 @StartInputReason int startInputReason, 1613 @Nullable IInputMethodClient client, 1614 @Nullable IBinder windowToken, 1615 @StartInputFlags int startInputFlags, 1616 @SoftInputModeFlags int softInputMode, 1617 int windowFlags, 1618 @Nullable EditorInfo editorInfo, 1619 @Nullable IInputContext inputContext, 1620 @MissingMethodFlags int missingMethods, 1621 int unverifiedTargetSdkVersion) { 1622 return startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken, 1623 startInputFlags, softInputMode, windowFlags, editorInfo, inputContext, 1624 missingMethods, unverifiedTargetSdkVersion); 1625 } 1626 1627 @BinderThread startInputOrWindowGainedFocusInternal( @tartInputReason int startInputReason, @Nullable IInputMethodClient client, @Nullable IBinder windowToken, @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode, int windowFlags, @Nullable EditorInfo editorInfo, @Nullable IInputContext inputContext, @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion)1628 private InputBindResult startInputOrWindowGainedFocusInternal( 1629 @StartInputReason int startInputReason, 1630 @Nullable IInputMethodClient client, 1631 @Nullable IBinder windowToken, 1632 @StartInputFlags int startInputFlags, 1633 @SoftInputModeFlags int softInputMode, 1634 int windowFlags, 1635 @Nullable EditorInfo editorInfo, 1636 @Nullable IInputContext inputContext, 1637 @MissingMethodFlags int missingMethods, 1638 int unverifiedTargetSdkVersion) { 1639 final int callingUid = Binder.getCallingUid(); 1640 final int callingPid = Binder.getCallingPid(); 1641 final int userId = UserHandle.getUserId(callingUid); 1642 1643 if (client == null) { 1644 return InputBindResult.INVALID_CLIENT; 1645 } 1646 1647 final boolean packageNameVerified = 1648 editorInfo != null && InputMethodUtils.checkIfPackageBelongsToUid( 1649 mAppOpsManager, callingUid, editorInfo.packageName); 1650 if (editorInfo != null && !packageNameVerified) { 1651 Slog.e(TAG, "Rejecting this client as it reported an invalid package name." 1652 + " uid=" + callingUid + " package=" + editorInfo.packageName); 1653 return InputBindResult.INVALID_PACKAGE_NAME; 1654 } 1655 1656 final PerUserData data = mUserDataMap.get(userId); 1657 if (data == null) { 1658 Slog.e(TAG, "startInputOrWindowGainedFocus() from unknown userId=" + userId 1659 + " uid=" + callingUid + " pid=" + callingPid); 1660 return InputBindResult.INVALID_USER; 1661 } 1662 1663 synchronized (data.mLock) { 1664 final InputMethodClientInfo clientInfo = data.getClientLocked(client); 1665 if (clientInfo == null) { 1666 return InputBindResult.INVALID_CLIENT; 1667 } 1668 if (clientInfo.mUid != callingUid) { 1669 Slog.e(TAG, "Expected calling UID=" + clientInfo.mUid 1670 + " actual=" + callingUid); 1671 return InputBindResult.INVALID_CLIENT; 1672 } 1673 1674 switch (data.mState) { 1675 case PerUserState.USER_LOCKED: 1676 case PerUserState.SERVICE_NOT_QUERIED: 1677 case PerUserState.SERVICE_RECOGNIZED: 1678 case PerUserState.WAITING_SERVICE_CONNECTED: 1679 case PerUserState.UNBIND_CALLED: 1680 return InputBindResult.IME_NOT_CONNECTED; 1681 case PerUserState.SERVICE_CONNECTED: 1682 // OK 1683 break; 1684 default: 1685 Slog.wtf(TAG, "Unexpected state=" + data.mState); 1686 return InputBindResult.IME_NOT_CONNECTED; 1687 } 1688 1689 WindowInfo windowInfo = null; 1690 if (windowToken != null) { 1691 windowInfo = clientInfo.mWindowMap.get(windowToken); 1692 if (windowInfo == null) { 1693 windowInfo = new WindowInfo(windowToken, WindowHandleSource.getNext()); 1694 clientInfo.mWindowMap.put(windowToken, windowInfo); 1695 } 1696 } 1697 1698 if (!checkFocus(clientInfo.mUid, clientInfo.mPid, 1699 clientInfo.mSelfReportedDisplayId)) { 1700 return InputBindResult.NOT_IME_TARGET_WINDOW; 1701 } 1702 1703 if (editorInfo == null) { 1704 // So-called fallback InputConnection scenario. For app compatibility, we still 1705 // notify this to the IME. 1706 switch (clientInfo.mState) { 1707 case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT: 1708 case InputMethodClientState.ALREADY_SENT_BIND_RESULT: 1709 final int windowHandle = windowInfo != null 1710 ? windowInfo.mWindowHandle 1711 : MultiClientInputMethodServiceDelegate.INVALID_WINDOW_HANDLE; 1712 try { 1713 clientInfo.mMSInputMethodSession.startInputOrWindowGainedFocus( 1714 inputContext, missingMethods, editorInfo, startInputFlags, 1715 softInputMode, windowHandle); 1716 } catch (RemoteException ignored) { } 1717 break; 1718 } 1719 return InputBindResult.NULL_EDITOR_INFO; 1720 } 1721 1722 switch (clientInfo.mState) { 1723 case InputMethodClientState.REGISTERED: 1724 case InputMethodClientState.WAITING_FOR_IME_SESSION: 1725 clientInfo.mBindingSequence++; 1726 if (clientInfo.mBindingSequence < 0) { 1727 clientInfo.mBindingSequence = 0; 1728 } 1729 return new InputBindResult( 1730 InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION, 1731 null, null, data.mCurrentInputMethodInfo.getId(), 1732 clientInfo.mBindingSequence, false); 1733 case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT: 1734 case InputMethodClientState.ALREADY_SENT_BIND_RESULT: 1735 clientInfo.mBindingSequence++; 1736 if (clientInfo.mBindingSequence < 0) { 1737 clientInfo.mBindingSequence = 0; 1738 } 1739 // Successful start input. 1740 final int windowHandle = windowInfo != null 1741 ? windowInfo.mWindowHandle 1742 : MultiClientInputMethodServiceDelegate.INVALID_WINDOW_HANDLE; 1743 try { 1744 clientInfo.mMSInputMethodSession.startInputOrWindowGainedFocus( 1745 inputContext, missingMethods, editorInfo, startInputFlags, 1746 softInputMode, windowHandle); 1747 } catch (RemoteException ignored) { } 1748 clientInfo.mState = InputMethodClientState.ALREADY_SENT_BIND_RESULT; 1749 return new InputBindResult( 1750 InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION, 1751 clientInfo.mInputMethodSession, 1752 clientInfo.mWriteChannel.dup(), 1753 data.mCurrentInputMethodInfo.getId(), 1754 clientInfo.mBindingSequence, false); 1755 case InputMethodClientState.UNREGISTERED: 1756 Slog.e(TAG, "The client is already unregistered."); 1757 return InputBindResult.INVALID_CLIENT; 1758 } 1759 } 1760 return null; 1761 } 1762 1763 @BinderThread 1764 @Override showInputMethodPickerFromClient(IInputMethodClient client, int auxiliarySubtypeMode)1765 public void showInputMethodPickerFromClient(IInputMethodClient client, 1766 int auxiliarySubtypeMode) { 1767 reportNotSupported(); 1768 } 1769 1770 @BinderThread 1771 @Override showInputMethodPickerFromSystem(IInputMethodClient client, int auxiliarySubtypeMode, int displayId)1772 public void showInputMethodPickerFromSystem(IInputMethodClient client, 1773 int auxiliarySubtypeMode, int displayId) { 1774 reportNotSupported(); 1775 } 1776 1777 @BinderThread 1778 @Override showInputMethodAndSubtypeEnablerFromClient(IInputMethodClient client, String inputMethodId)1779 public void showInputMethodAndSubtypeEnablerFromClient(IInputMethodClient client, 1780 String inputMethodId) { 1781 reportNotSupported(); 1782 } 1783 1784 @BinderThread 1785 @Override isInputMethodPickerShownForTest()1786 public boolean isInputMethodPickerShownForTest() { 1787 reportNotSupported(); 1788 return false; 1789 } 1790 1791 @BinderThread 1792 @Override getCurrentInputMethodSubtype()1793 public InputMethodSubtype getCurrentInputMethodSubtype() { 1794 reportNotSupported(); 1795 return null; 1796 } 1797 1798 @BinderThread 1799 @Override setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes)1800 public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) { 1801 reportNotSupported(); 1802 } 1803 1804 @BinderThread 1805 @Override getInputMethodWindowVisibleHeight()1806 public int getInputMethodWindowVisibleHeight() { 1807 reportNotSupported(); 1808 return 0; 1809 } 1810 1811 @BinderThread 1812 @Override reportPerceptibleAsync(IBinder windowClient, boolean perceptible)1813 public void reportPerceptibleAsync(IBinder windowClient, boolean perceptible) { 1814 reportNotSupported(); 1815 } 1816 1817 @BinderThread 1818 @Override onShellCommand(@ullable FileDescriptor in, @Nullable FileDescriptor out, @Nullable FileDescriptor err, String[] args, @Nullable ShellCallback callback, ResultReceiver resultReceiver)1819 public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out, 1820 @Nullable FileDescriptor err, String[] args, @Nullable ShellCallback callback, 1821 ResultReceiver resultReceiver) { 1822 } 1823 1824 @BinderThread 1825 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)1826 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1827 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; 1828 final String prefixChild = " "; 1829 pw.println("Current Multi Client Input Method Manager state:"); 1830 IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); 1831 ipw.println("mUserDataMap="); 1832 if (mUserDataMap != null) { 1833 ipw.increaseIndent(); 1834 mUserDataMap.dump(fd, ipw, args); 1835 } 1836 } 1837 1838 @BinderThread 1839 @Override startProtoDump(byte[] clientProtoDump, int source, String where)1840 public void startProtoDump(byte[] clientProtoDump, int source, String where) { 1841 } 1842 1843 @BinderThread 1844 @Override isImeTraceEnabled()1845 public boolean isImeTraceEnabled() { 1846 return false; 1847 } 1848 1849 @BinderThread 1850 @Override startImeTrace()1851 public void startImeTrace() { 1852 } 1853 1854 @BinderThread 1855 @Override stopImeTrace()1856 public void stopImeTrace() { 1857 } 1858 } 1859 } 1860