1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.inputmethodservice; 18 19 import android.annotation.BinderThread; 20 import android.annotation.DurationMillisLong; 21 import android.annotation.MainThread; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.compat.annotation.UnsupportedAppUsage; 25 import android.content.Context; 26 import android.content.pm.PackageManager; 27 import android.os.Binder; 28 import android.os.IBinder; 29 import android.os.Message; 30 import android.os.RemoteException; 31 import android.os.ResultReceiver; 32 import android.util.Log; 33 import android.view.InputChannel; 34 import android.view.MotionEvent; 35 import android.view.inputmethod.CursorAnchorInfo; 36 import android.view.inputmethod.ImeTracker; 37 import android.view.inputmethod.InputBinding; 38 import android.view.inputmethod.InputConnection; 39 import android.view.inputmethod.InputMethod; 40 import android.view.inputmethod.InputMethodSession; 41 import android.view.inputmethod.InputMethodSubtype; 42 43 import com.android.internal.inputmethod.CancellationGroup; 44 import com.android.internal.inputmethod.IConnectionlessHandwritingCallback; 45 import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback; 46 import com.android.internal.inputmethod.IInputMethod; 47 import com.android.internal.inputmethod.IInputMethodSession; 48 import com.android.internal.inputmethod.IInputMethodSessionCallback; 49 import com.android.internal.inputmethod.IRemoteInputConnection; 50 import com.android.internal.inputmethod.InlineSuggestionsRequestInfo; 51 import com.android.internal.inputmethod.InputMethodNavButtonFlags; 52 import com.android.internal.os.HandlerCaller; 53 import com.android.internal.os.SomeArgs; 54 55 import java.io.FileDescriptor; 56 import java.io.PrintWriter; 57 import java.lang.ref.WeakReference; 58 import java.util.List; 59 import java.util.concurrent.CountDownLatch; 60 import java.util.concurrent.TimeUnit; 61 62 /** 63 * Implements the internal IInputMethod interface to convert incoming calls 64 * on to it back to calls on the public InputMethod interface, scheduling 65 * them on the main thread of the process. 66 */ 67 class IInputMethodWrapper extends IInputMethod.Stub 68 implements HandlerCaller.Callback { 69 private static final String TAG = "InputMethodWrapper"; 70 71 private static final int DO_DUMP = 1; 72 private static final int DO_INITIALIZE_INTERNAL = 10; 73 private static final int DO_SET_INPUT_CONTEXT = 20; 74 private static final int DO_UNSET_INPUT_CONTEXT = 30; 75 private static final int DO_START_INPUT = 32; 76 private static final int DO_ON_NAV_BUTTON_FLAGS_CHANGED = 35; 77 private static final int DO_CREATE_SESSION = 40; 78 private static final int DO_SET_SESSION_ENABLED = 45; 79 private static final int DO_SHOW_SOFT_INPUT = 60; 80 private static final int DO_HIDE_SOFT_INPUT = 70; 81 private static final int DO_CHANGE_INPUTMETHOD_SUBTYPE = 80; 82 private static final int DO_CREATE_INLINE_SUGGESTIONS_REQUEST = 90; 83 private static final int DO_CAN_START_STYLUS_HANDWRITING = 100; 84 private static final int DO_START_STYLUS_HANDWRITING = 110; 85 private static final int DO_INIT_INK_WINDOW = 120; 86 private static final int DO_FINISH_STYLUS_HANDWRITING = 130; 87 private static final int DO_UPDATE_TOOL_TYPE = 140; 88 private static final int DO_REMOVE_STYLUS_HANDWRITING_WINDOW = 150; 89 private static final int DO_SET_STYLUS_WINDOW_IDLE_TIMEOUT = 160; 90 private static final int DO_COMMIT_HANDWRITING_DELEGATION_TEXT_IF_AVAILABLE = 170; 91 private static final int DO_DISCARD_HANDWRITING_DELEGATION_TEXT = 180; 92 93 final WeakReference<InputMethodServiceInternal> mTarget; 94 final Context mContext; 95 @UnsupportedAppUsage 96 final HandlerCaller mCaller; 97 final WeakReference<InputMethod> mInputMethod; 98 final int mTargetSdkVersion; 99 100 /** 101 * This is not {@code null} only between {@link #bindInput(InputBinding)} and 102 * {@link #unbindInput()} so that {@link RemoteInputConnection} can query if 103 * {@link #unbindInput()} has already been called or not, mainly to avoid unnecessary 104 * blocking operations. 105 * 106 * <p>This field must be set and cleared only from the binder thread(s), where the system 107 * guarantees that {@link #bindInput(InputBinding)}, 108 * {@link #startInput(IInputMethod.StartInputParams)}, and {@link #unbindInput()} are called 109 * with the same order as the original calls in 110 * {@link com.android.server.inputmethod.InputMethodManagerService}. 111 * See {@link IBinder#FLAG_ONEWAY} for detailed semantics.</p> 112 */ 113 @Nullable 114 CancellationGroup mCancellationGroup = null; 115 116 // NOTE: we should have a cache of these. 117 static final class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback { 118 final Context mContext; 119 final InputChannel mChannel; 120 final IInputMethodSessionCallback mCb; 121 InputMethodSessionCallbackWrapper(Context context, InputChannel channel, IInputMethodSessionCallback cb)122 InputMethodSessionCallbackWrapper(Context context, InputChannel channel, 123 IInputMethodSessionCallback cb) { 124 mContext = context; 125 mChannel = channel; 126 mCb = cb; 127 } 128 129 @Override sessionCreated(InputMethodSession session)130 public void sessionCreated(InputMethodSession session) { 131 try { 132 if (session != null) { 133 IInputMethodSessionWrapper wrap = 134 new IInputMethodSessionWrapper(mContext, session, mChannel); 135 mCb.sessionCreated(wrap); 136 } else { 137 if (mChannel != null) { 138 mChannel.dispose(); 139 } 140 mCb.sessionCreated(null); 141 } 142 } catch (RemoteException e) { 143 } 144 } 145 } 146 IInputMethodWrapper(InputMethodServiceInternal imsInternal, InputMethod inputMethod)147 IInputMethodWrapper(InputMethodServiceInternal imsInternal, InputMethod inputMethod) { 148 mTarget = new WeakReference<>(imsInternal); 149 mContext = imsInternal.getContext().getApplicationContext(); 150 mCaller = new HandlerCaller(mContext, null, this, true /*asyncHandler*/); 151 mInputMethod = new WeakReference<>(inputMethod); 152 mTargetSdkVersion = imsInternal.getContext().getApplicationInfo().targetSdkVersion; 153 } 154 155 @MainThread 156 @Override executeMessage(Message msg)157 public void executeMessage(Message msg) { 158 final InputMethod inputMethod = mInputMethod.get(); 159 final InputMethodServiceInternal target = mTarget.get(); 160 switch (msg.what) { 161 case DO_DUMP: { 162 SomeArgs args = (SomeArgs) msg.obj; 163 if (isValid(inputMethod, target, "DO_DUMP")) { 164 final FileDescriptor fd = (FileDescriptor) args.arg1; 165 final PrintWriter fout = (PrintWriter) args.arg2; 166 final String[] dumpArgs = (String[]) args.arg3; 167 final CountDownLatch latch = (CountDownLatch) args.arg4; 168 try { 169 target.dump(fd, fout, dumpArgs); 170 } catch (RuntimeException e) { 171 fout.println("Exception: " + e); 172 } finally { 173 latch.countDown(); 174 } 175 } 176 args.recycle(); 177 return; 178 } 179 case DO_INITIALIZE_INTERNAL: 180 if (isValid(inputMethod, target, "DO_INITIALIZE_INTERNAL")) { 181 inputMethod.initializeInternal((IInputMethod.InitParams) msg.obj); 182 } 183 return; 184 case DO_SET_INPUT_CONTEXT: { 185 if (isValid(inputMethod, target, "DO_SET_INPUT_CONTEXT")) { 186 inputMethod.bindInput((InputBinding) msg.obj); 187 } 188 return; 189 } 190 case DO_UNSET_INPUT_CONTEXT: 191 if (isValid(inputMethod, target, "DO_UNSET_INPUT_CONTEXT")) { 192 inputMethod.unbindInput(); 193 } 194 return; 195 case DO_START_INPUT: { 196 final SomeArgs args = (SomeArgs) msg.obj; 197 if (isValid(inputMethod, target, "DO_START_INPUT")) { 198 final InputConnection inputConnection = (InputConnection) args.arg1; 199 final IInputMethod.StartInputParams params = 200 (IInputMethod.StartInputParams) args.arg2; 201 inputMethod.dispatchStartInput(inputConnection, params); 202 } 203 args.recycle(); 204 return; 205 } 206 case DO_ON_NAV_BUTTON_FLAGS_CHANGED: 207 if (isValid(inputMethod, target, "DO_ON_NAV_BUTTON_FLAGS_CHANGED")) { 208 inputMethod.onNavButtonFlagsChanged(msg.arg1); 209 } 210 return; 211 case DO_CREATE_SESSION: { 212 SomeArgs args = (SomeArgs) msg.obj; 213 if (isValid(inputMethod, target, "DO_CREATE_SESSION")) { 214 inputMethod.createSession(new InputMethodSessionCallbackWrapper( 215 mContext, (InputChannel) args.arg1, 216 (IInputMethodSessionCallback) args.arg2)); 217 } 218 args.recycle(); 219 return; 220 } 221 case DO_SET_SESSION_ENABLED: 222 if (isValid(inputMethod, target, "DO_SET_SESSION_ENABLED")) { 223 inputMethod.setSessionEnabled((InputMethodSession) msg.obj, msg.arg1 != 0); 224 } 225 return; 226 case DO_SHOW_SOFT_INPUT: { 227 final SomeArgs args = (SomeArgs) msg.obj; 228 final ImeTracker.Token statsToken = (ImeTracker.Token) args.arg3; 229 if (isValid(inputMethod, target, "DO_SHOW_SOFT_INPUT")) { 230 ImeTracker.forLogging().onProgress( 231 statsToken, ImeTracker.PHASE_IME_WRAPPER_DISPATCH); 232 inputMethod.showSoftInputWithToken( 233 msg.arg1, (ResultReceiver) args.arg2, (IBinder) args.arg1, statsToken); 234 } else { 235 ImeTracker.forLogging().onFailed( 236 statsToken, ImeTracker.PHASE_IME_WRAPPER_DISPATCH); 237 } 238 args.recycle(); 239 return; 240 } 241 case DO_HIDE_SOFT_INPUT: { 242 final SomeArgs args = (SomeArgs) msg.obj; 243 final ImeTracker.Token statsToken = (ImeTracker.Token) args.arg3; 244 if (isValid(inputMethod, target, "DO_HIDE_SOFT_INPUT")) { 245 ImeTracker.forLogging().onProgress( 246 statsToken, ImeTracker.PHASE_IME_WRAPPER_DISPATCH); 247 inputMethod.hideSoftInputWithToken(msg.arg1, (ResultReceiver) args.arg2, 248 (IBinder) args.arg1, statsToken); 249 } else { 250 ImeTracker.forLogging().onFailed( 251 statsToken, ImeTracker.PHASE_IME_WRAPPER_DISPATCH); 252 } 253 args.recycle(); 254 return; 255 } 256 case DO_CHANGE_INPUTMETHOD_SUBTYPE: 257 if (isValid(inputMethod, target, "DO_CHANGE_INPUTMETHOD_SUBTYPE")) { 258 inputMethod.changeInputMethodSubtype((InputMethodSubtype) msg.obj); 259 } 260 return; 261 case DO_CREATE_INLINE_SUGGESTIONS_REQUEST: { 262 final SomeArgs args = (SomeArgs) msg.obj; 263 if (isValid(inputMethod, target, "DO_CREATE_INLINE_SUGGESTIONS_REQUEST")) { 264 inputMethod.onCreateInlineSuggestionsRequest( 265 (InlineSuggestionsRequestInfo) args.arg1, 266 (IInlineSuggestionsRequestCallback) args.arg2); 267 } 268 args.recycle(); 269 return; 270 } 271 case DO_CAN_START_STYLUS_HANDWRITING: { 272 final SomeArgs args = (SomeArgs) msg.obj; 273 if (isValid(inputMethod, target, "DO_CAN_START_STYLUS_HANDWRITING")) { 274 inputMethod.canStartStylusHandwriting(msg.arg1, 275 (IConnectionlessHandwritingCallback) args.arg1, 276 (CursorAnchorInfo) args.arg2, msg.arg2 != 0); 277 } 278 args.recycle(); 279 return; 280 } 281 case DO_UPDATE_TOOL_TYPE: { 282 if (isValid(inputMethod, target, "DO_UPDATE_TOOL_TYPE")) { 283 inputMethod.updateEditorToolType(msg.arg1); 284 } 285 return; 286 } 287 case DO_START_STYLUS_HANDWRITING: { 288 final SomeArgs args = (SomeArgs) msg.obj; 289 if (isValid(inputMethod, target, "DO_START_STYLUS_HANDWRITING")) { 290 inputMethod.startStylusHandwriting(msg.arg1, (InputChannel) args.arg1, 291 (List<MotionEvent>) args.arg2); 292 } 293 args.recycle(); 294 return; 295 } 296 case DO_INIT_INK_WINDOW: { 297 if (isValid(inputMethod, target, "DO_INIT_INK_WINDOW")) { 298 inputMethod.initInkWindow(); 299 } 300 return; 301 } 302 case DO_FINISH_STYLUS_HANDWRITING: { 303 if (isValid(inputMethod, target, "DO_FINISH_STYLUS_HANDWRITING")) { 304 inputMethod.finishStylusHandwriting(); 305 } 306 return; 307 } 308 case DO_REMOVE_STYLUS_HANDWRITING_WINDOW: { 309 if (isValid(inputMethod, target, "DO_REMOVE_STYLUS_HANDWRITING_WINDOW")) { 310 inputMethod.removeStylusHandwritingWindow(); 311 } 312 return; 313 } 314 case DO_SET_STYLUS_WINDOW_IDLE_TIMEOUT: { 315 if (isValid(inputMethod, target, "DO_SET_STYLUS_WINDOW_IDLE_TIMEOUT")) { 316 inputMethod.setStylusWindowIdleTimeoutForTest((long) msg.obj); 317 } 318 return; 319 } 320 case DO_COMMIT_HANDWRITING_DELEGATION_TEXT_IF_AVAILABLE: { 321 if (isValid(inputMethod, target, 322 "DO_COMMIT_HANDWRITING_DELEGATION_TEXT_IF_AVAILABLE")) { 323 inputMethod.commitHandwritingDelegationTextIfAvailable(); 324 } 325 return; 326 } 327 case DO_DISCARD_HANDWRITING_DELEGATION_TEXT: { 328 if (isValid(inputMethod, target, "DO_DISCARD_HANDWRITING_DELEGATION_TEXT")) { 329 inputMethod.discardHandwritingDelegationText(); 330 } 331 return; 332 } 333 } 334 Log.w(TAG, "Unhandled message code: " + msg.what); 335 } 336 337 @BinderThread 338 @Override dump(FileDescriptor fd, PrintWriter fout, String[] args)339 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 340 InputMethodServiceInternal target = mTarget.get(); 341 if (target == null) { 342 return; 343 } 344 if (target.getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 345 != PackageManager.PERMISSION_GRANTED) { 346 347 fout.println("Permission Denial: can't dump InputMethodManager from from pid=" 348 + Binder.getCallingPid() 349 + ", uid=" + Binder.getCallingUid()); 350 return; 351 } 352 353 CountDownLatch latch = new CountDownLatch(1); 354 mCaller.getHandler().sendMessageAtFrontOfQueue(mCaller.obtainMessageOOOO(DO_DUMP, 355 fd, fout, args, latch)); 356 try { 357 if (!latch.await(5, TimeUnit.SECONDS)) { 358 fout.println("Timeout waiting for dump"); 359 } 360 } catch (InterruptedException e) { 361 fout.println("Interrupted waiting for dump"); 362 } 363 } 364 365 @BinderThread 366 @Override initializeInternal(@onNull IInputMethod.InitParams params)367 public void initializeInternal(@NonNull IInputMethod.InitParams params) { 368 mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_INITIALIZE_INTERNAL, params)); 369 } 370 371 @BinderThread 372 @Override onCreateInlineSuggestionsRequest(InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb)373 public void onCreateInlineSuggestionsRequest(InlineSuggestionsRequestInfo requestInfo, 374 IInlineSuggestionsRequestCallback cb) { 375 mCaller.executeOrSendMessage( 376 mCaller.obtainMessageOO(DO_CREATE_INLINE_SUGGESTIONS_REQUEST, requestInfo, cb)); 377 } 378 379 @BinderThread 380 @Override bindInput(InputBinding binding)381 public void bindInput(InputBinding binding) { 382 if (mCancellationGroup != null) { 383 Log.e(TAG, "bindInput must be paired with unbindInput."); 384 } 385 mCancellationGroup = new CancellationGroup(); 386 InputConnection ic = new RemoteInputConnection(mTarget, 387 IRemoteInputConnection.Stub.asInterface(binding.getConnectionToken()), 388 mCancellationGroup); 389 InputBinding nu = new InputBinding(ic, binding); 390 mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu)); 391 } 392 393 @BinderThread 394 @Override unbindInput()395 public void unbindInput() { 396 if (mCancellationGroup != null) { 397 // Signal the flag then forget it. 398 mCancellationGroup.cancelAll(); 399 mCancellationGroup = null; 400 } else { 401 Log.e(TAG, "unbindInput must be paired with bindInput."); 402 } 403 mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_UNSET_INPUT_CONTEXT)); 404 } 405 406 @BinderThread 407 @Override startInput(@onNull IInputMethod.StartInputParams params)408 public void startInput(@NonNull IInputMethod.StartInputParams params) { 409 if (mCancellationGroup == null) { 410 Log.e(TAG, "startInput must be called after bindInput."); 411 mCancellationGroup = new CancellationGroup(); 412 } 413 414 params.editorInfo.makeCompatible(mTargetSdkVersion); 415 416 final InputConnection ic = params.remoteInputConnection == null ? null 417 : new RemoteInputConnection(mTarget, params.remoteInputConnection, 418 mCancellationGroup); 419 420 mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_START_INPUT, ic, params)); 421 } 422 423 @BinderThread 424 @Override onNavButtonFlagsChanged(@nputMethodNavButtonFlags int navButtonFlags)425 public void onNavButtonFlagsChanged(@InputMethodNavButtonFlags int navButtonFlags) { 426 mCaller.executeOrSendMessage( 427 mCaller.obtainMessageI(DO_ON_NAV_BUTTON_FLAGS_CHANGED, navButtonFlags)); 428 } 429 430 @BinderThread 431 @Override createSession(InputChannel channel, IInputMethodSessionCallback callback)432 public void createSession(InputChannel channel, IInputMethodSessionCallback callback) { 433 mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_SESSION, 434 channel, callback)); 435 } 436 437 @BinderThread 438 @Override setSessionEnabled(IInputMethodSession session, boolean enabled)439 public void setSessionEnabled(IInputMethodSession session, boolean enabled) { 440 try { 441 InputMethodSession ls = ((IInputMethodSessionWrapper) 442 session).getInternalInputMethodSession(); 443 if (ls == null) { 444 Log.w(TAG, "Session is already finished: " + session); 445 return; 446 } 447 mCaller.executeOrSendMessage(mCaller.obtainMessageIO( 448 DO_SET_SESSION_ENABLED, enabled ? 1 : 0, ls)); 449 } catch (ClassCastException e) { 450 Log.w(TAG, "Incoming session not of correct type: " + session, e); 451 } 452 } 453 454 @BinderThread 455 @Override showSoftInput(IBinder showInputToken, @NonNull ImeTracker.Token statsToken, @InputMethod.ShowFlags int flags, ResultReceiver resultReceiver)456 public void showSoftInput(IBinder showInputToken, @NonNull ImeTracker.Token statsToken, 457 @InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) { 458 ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_WRAPPER); 459 mCaller.executeOrSendMessage(mCaller.obtainMessageIOOO(DO_SHOW_SOFT_INPUT, 460 flags, showInputToken, resultReceiver, statsToken)); 461 } 462 463 @BinderThread 464 @Override hideSoftInput(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken, int flags, ResultReceiver resultReceiver)465 public void hideSoftInput(IBinder hideInputToken, @NonNull ImeTracker.Token statsToken, 466 int flags, ResultReceiver resultReceiver) { 467 ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_WRAPPER); 468 mCaller.executeOrSendMessage(mCaller.obtainMessageIOOO(DO_HIDE_SOFT_INPUT, 469 flags, hideInputToken, resultReceiver, statsToken)); 470 } 471 472 @BinderThread 473 @Override changeInputMethodSubtype(InputMethodSubtype subtype)474 public void changeInputMethodSubtype(InputMethodSubtype subtype) { 475 mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CHANGE_INPUTMETHOD_SUBTYPE, 476 subtype)); 477 } 478 479 @BinderThread 480 @Override canStartStylusHandwriting(int requestId, IConnectionlessHandwritingCallback connectionlessCallback, CursorAnchorInfo cursorAnchorInfo, boolean isConnectionlessForDelegation)481 public void canStartStylusHandwriting(int requestId, 482 IConnectionlessHandwritingCallback connectionlessCallback, 483 CursorAnchorInfo cursorAnchorInfo, boolean isConnectionlessForDelegation) 484 throws RemoteException { 485 mCaller.executeOrSendMessage(mCaller.obtainMessageIIOO(DO_CAN_START_STYLUS_HANDWRITING, 486 requestId, isConnectionlessForDelegation ? 1 : 0, connectionlessCallback, 487 cursorAnchorInfo)); 488 } 489 490 @BinderThread 491 @Override updateEditorToolType(int toolType)492 public void updateEditorToolType(int toolType) 493 throws RemoteException { 494 mCaller.executeOrSendMessage( 495 mCaller.obtainMessageI(DO_UPDATE_TOOL_TYPE, toolType)); 496 } 497 498 @BinderThread 499 @Override startStylusHandwriting(int requestId, @NonNull InputChannel channel, @Nullable List<MotionEvent> stylusEvents)500 public void startStylusHandwriting(int requestId, @NonNull InputChannel channel, 501 @Nullable List<MotionEvent> stylusEvents) 502 throws RemoteException { 503 mCaller.executeOrSendMessage( 504 mCaller.obtainMessageIOO(DO_START_STYLUS_HANDWRITING, requestId, channel, 505 stylusEvents)); 506 } 507 508 @BinderThread 509 @Override commitHandwritingDelegationTextIfAvailable()510 public void commitHandwritingDelegationTextIfAvailable() { 511 mCaller.executeOrSendMessage( 512 mCaller.obtainMessage(DO_COMMIT_HANDWRITING_DELEGATION_TEXT_IF_AVAILABLE)); 513 } 514 515 @BinderThread 516 @Override discardHandwritingDelegationText()517 public void discardHandwritingDelegationText() { 518 mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_DISCARD_HANDWRITING_DELEGATION_TEXT)); 519 } 520 521 @BinderThread 522 @Override initInkWindow()523 public void initInkWindow() { 524 mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_INIT_INK_WINDOW)); 525 } 526 527 @BinderThread 528 @Override finishStylusHandwriting()529 public void finishStylusHandwriting() { 530 mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_STYLUS_HANDWRITING)); 531 } 532 533 @BinderThread 534 @Override removeStylusHandwritingWindow()535 public void removeStylusHandwritingWindow() { 536 mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_STYLUS_HANDWRITING_WINDOW)); 537 } 538 539 @BinderThread 540 @Override setStylusWindowIdleTimeoutForTest(@urationMillisLong long timeout)541 public void setStylusWindowIdleTimeoutForTest(@DurationMillisLong long timeout) { 542 mCaller.executeOrSendMessage( 543 mCaller.obtainMessageO(DO_SET_STYLUS_WINDOW_IDLE_TIMEOUT, timeout)); 544 } 545 isValid(InputMethod inputMethod, InputMethodServiceInternal target, String msg)546 private static boolean isValid(InputMethod inputMethod, InputMethodServiceInternal target, 547 String msg) { 548 if (inputMethod != null && target != null && !target.isServiceDestroyed()) { 549 return true; 550 } else { 551 Log.w(TAG, "Ignoring " + msg + ", InputMethod:" + inputMethod 552 + ", InputMethodServiceInternal:" + target); 553 return false; 554 } 555 } 556 } 557