IInputMethodWrapper.java revision d24b8183b93e781080b2c16c487e60d51c12da31
1package android.inputmethodservice; 2 3import com.android.internal.os.HandlerCaller; 4import com.android.internal.view.IInputContext; 5import com.android.internal.view.IInputMethod; 6import com.android.internal.view.IInputMethodCallback; 7import com.android.internal.view.IInputMethodSession; 8import com.android.internal.view.InputConnectionWrapper; 9 10import android.content.Context; 11import android.content.pm.PackageManager; 12import android.os.Binder; 13import android.os.IBinder; 14import android.os.Message; 15import android.os.RemoteException; 16import android.util.Log; 17import android.view.inputmethod.EditorInfo; 18import android.view.inputmethod.InputBinding; 19import android.view.inputmethod.InputConnection; 20import android.view.inputmethod.InputMethod; 21import android.view.inputmethod.InputMethodSession; 22 23import java.io.FileDescriptor; 24import java.io.PrintWriter; 25import java.util.concurrent.CountDownLatch; 26import java.util.concurrent.TimeUnit; 27 28/** 29 * Implements the internal IInputMethod interface to convert incoming calls 30 * on to it back to calls on the public InputMethod interface, scheduling 31 * them on the main thread of the process. 32 */ 33class IInputMethodWrapper extends IInputMethod.Stub 34 implements HandlerCaller.Callback { 35 private static final String TAG = "InputMethodWrapper"; 36 private static final boolean DEBUG = false; 37 38 private static final int DO_DUMP = 1; 39 private static final int DO_ATTACH_TOKEN = 10; 40 private static final int DO_SET_INPUT_CONTEXT = 20; 41 private static final int DO_UNSET_INPUT_CONTEXT = 30; 42 private static final int DO_START_INPUT = 32; 43 private static final int DO_RESTART_INPUT = 34; 44 private static final int DO_CREATE_SESSION = 40; 45 private static final int DO_SET_SESSION_ENABLED = 45; 46 private static final int DO_REVOKE_SESSION = 50; 47 private static final int DO_SHOW_SOFT_INPUT = 60; 48 private static final int DO_HIDE_SOFT_INPUT = 70; 49 50 final AbstractInputMethodService mTarget; 51 final HandlerCaller mCaller; 52 final InputMethod mInputMethod; 53 54 static class Notifier { 55 boolean notified; 56 } 57 58 // NOTE: we should have a cache of these. 59 static class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback { 60 final Context mContext; 61 final IInputMethodCallback mCb; 62 InputMethodSessionCallbackWrapper(Context context, IInputMethodCallback cb) { 63 mContext = context; 64 mCb = cb; 65 } 66 public void sessionCreated(InputMethodSession session) { 67 try { 68 if (session != null) { 69 IInputMethodSessionWrapper wrap = 70 new IInputMethodSessionWrapper(mContext, session); 71 mCb.sessionCreated(wrap); 72 } else { 73 mCb.sessionCreated(null); 74 } 75 } catch (RemoteException e) { 76 } 77 } 78 } 79 80 public IInputMethodWrapper(AbstractInputMethodService context, 81 InputMethod inputMethod) { 82 mTarget = context; 83 mCaller = new HandlerCaller(context, this); 84 mInputMethod = inputMethod; 85 } 86 87 public InputMethod getInternalInputMethod() { 88 return mInputMethod; 89 } 90 91 public void executeMessage(Message msg) { 92 switch (msg.what) { 93 case DO_DUMP: { 94 HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj; 95 try { 96 mTarget.dump((FileDescriptor)args.arg1, 97 (PrintWriter)args.arg2, (String[])args.arg3); 98 } catch (RuntimeException e) { 99 ((PrintWriter)args.arg2).println("Exception: " + e); 100 } 101 synchronized (args.arg4) { 102 ((CountDownLatch)args.arg4).countDown(); 103 } 104 return; 105 } 106 107 case DO_ATTACH_TOKEN: { 108 mInputMethod.attachToken((IBinder)msg.obj); 109 return; 110 } 111 case DO_SET_INPUT_CONTEXT: { 112 mInputMethod.bindInput((InputBinding)msg.obj); 113 return; 114 } 115 case DO_UNSET_INPUT_CONTEXT: 116 mInputMethod.unbindInput(); 117 return; 118 case DO_START_INPUT: { 119 HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj; 120 IInputContext inputContext = (IInputContext)args.arg1; 121 InputConnection ic = inputContext != null 122 ? new InputConnectionWrapper(inputContext) : null; 123 mInputMethod.startInput(ic, (EditorInfo)args.arg2); 124 return; 125 } 126 case DO_RESTART_INPUT: { 127 HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj; 128 IInputContext inputContext = (IInputContext)args.arg1; 129 InputConnection ic = inputContext != null 130 ? new InputConnectionWrapper(inputContext) : null; 131 mInputMethod.restartInput(ic, (EditorInfo)args.arg2); 132 return; 133 } 134 case DO_CREATE_SESSION: { 135 mInputMethod.createSession(new InputMethodSessionCallbackWrapper( 136 mCaller.mContext, (IInputMethodCallback)msg.obj)); 137 return; 138 } 139 case DO_SET_SESSION_ENABLED: 140 mInputMethod.setSessionEnabled((InputMethodSession)msg.obj, 141 msg.arg1 != 0); 142 return; 143 case DO_REVOKE_SESSION: 144 mInputMethod.revokeSession((InputMethodSession)msg.obj); 145 return; 146 case DO_SHOW_SOFT_INPUT: 147 mInputMethod.showSoftInput(msg.arg1); 148 return; 149 case DO_HIDE_SOFT_INPUT: 150 mInputMethod.hideSoftInput(); 151 return; 152 } 153 Log.w(TAG, "Unhandled message code: " + msg.what); 154 } 155 156 @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { 157 if (mTarget.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 158 != PackageManager.PERMISSION_GRANTED) { 159 160 fout.println("Permission Denial: can't dump InputMethodManager from from pid=" 161 + Binder.getCallingPid() 162 + ", uid=" + Binder.getCallingUid()); 163 return; 164 } 165 166 CountDownLatch latch = new CountDownLatch(1); 167 mCaller.executeOrSendMessage(mCaller.obtainMessageOOOO(DO_DUMP, 168 fd, fout, args, latch)); 169 try { 170 if (!latch.await(5, TimeUnit.SECONDS)) { 171 fout.println("Timeout waiting for dump"); 172 } 173 } catch (InterruptedException e) { 174 fout.println("Interrupted waiting for dump"); 175 } 176 } 177 178 public void attachToken(IBinder token) { 179 mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_ATTACH_TOKEN, token)); 180 } 181 182 public void bindInput(InputBinding binding) { 183 InputConnection ic = new InputConnectionWrapper( 184 IInputContext.Stub.asInterface(binding.getConnectionToken())); 185 InputBinding nu = new InputBinding(ic, binding); 186 mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu)); 187 } 188 189 public void unbindInput() { 190 mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_UNSET_INPUT_CONTEXT)); 191 } 192 193 public void startInput(IInputContext inputContext, EditorInfo attribute) { 194 mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_START_INPUT, 195 inputContext, attribute)); 196 } 197 198 public void restartInput(IInputContext inputContext, EditorInfo attribute) { 199 mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_RESTART_INPUT, 200 inputContext, attribute)); 201 } 202 203 public void createSession(IInputMethodCallback callback) { 204 mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CREATE_SESSION, callback)); 205 } 206 207 public void setSessionEnabled(IInputMethodSession session, boolean enabled) { 208 try { 209 InputMethodSession ls = ((IInputMethodSessionWrapper) 210 session).getInternalInputMethodSession(); 211 mCaller.executeOrSendMessage(mCaller.obtainMessageIO( 212 DO_SET_SESSION_ENABLED, enabled ? 1 : 0, ls)); 213 } catch (ClassCastException e) { 214 Log.w(TAG, "Incoming session not of correct type: " + session, e); 215 } 216 } 217 218 public void revokeSession(IInputMethodSession session) { 219 try { 220 InputMethodSession ls = ((IInputMethodSessionWrapper) 221 session).getInternalInputMethodSession(); 222 mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_REVOKE_SESSION, ls)); 223 } catch (ClassCastException e) { 224 Log.w(TAG, "Incoming session not of correct type: " + session, e); 225 } 226 } 227 228 public void showSoftInput(int flags) { 229 mCaller.executeOrSendMessage(mCaller.obtainMessageI(DO_SHOW_SOFT_INPUT, 230 flags)); 231 } 232 233 public void hideSoftInput() { 234 mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_HIDE_SOFT_INPUT)); 235 } 236} 237