FileDocCategorySizeDatePackage
InputMethodManagerService.javaAPI DocAndroid 1.5 API66023Wed May 06 22:42:00 BST 2009com.android.server

InputMethodManagerService

public class InputMethodManagerService extends IInputMethodManager.Stub implements android.content.ServiceConnection, Handler.Callback
This class provides a system service that manages input methods.

Fields Summary
static final boolean
DEBUG
static final String
TAG
static final int
MSG_SHOW_IM_PICKER
static final int
MSG_UNBIND_INPUT
static final int
MSG_BIND_INPUT
static final int
MSG_SHOW_SOFT_INPUT
static final int
MSG_HIDE_SOFT_INPUT
static final int
MSG_ATTACH_TOKEN
static final int
MSG_CREATE_SESSION
static final int
MSG_START_INPUT
static final int
MSG_RESTART_INPUT
static final int
MSG_UNBIND_METHOD
static final int
MSG_BIND_METHOD
static final long
TIME_TO_RECONNECT
static final int
LOG_IMF_FORCE_RECONNECT_IME
final android.content.Context
mContext
final android.os.Handler
mHandler
final SettingsObserver
mSettingsObserver
final com.android.server.status.StatusBarService
mStatusBar
final android.os.IBinder
mInputMethodIcon
final com.android.server.status.IconData
mInputMethodData
final android.view.IWindowManager
mIWindowManager
final com.android.internal.os.HandlerCaller
mCaller
final com.android.internal.view.InputBindResult
mNoBinding
final ArrayList
mMethodList
final HashMap
mMethodMap
final TextUtils.SimpleStringSplitter
mStringColonSplitter
final HashMap
mClients
String
mCurMethodId
Id of the currently selected input method.
int
mCurSeq
The current binding sequence number, incremented every time there is a new bind performed.
ClientState
mCurClient
The client that is currently bound to an input method.
android.os.IBinder
mCurFocusedWindow
The last window token that gained focus.
com.android.internal.view.IInputContext
mCurInputContext
The input context last provided by the current client.
android.view.inputmethod.EditorInfo
mCurAttribute
The attributes last provided by the current client.
String
mCurId
The input method ID of the input method service that we are currently connected to or in the process of connecting to.
boolean
mHaveConnection
Set to true if our ServiceConnection is currently actively bound to a service (whether or not we have gotten its IBinder back yet).
boolean
mShowRequested
Set if the client has asked for the input method to be shown.
boolean
mShowExplicitlyRequested
Set if we were explicitly told to show the input method.
boolean
mShowForced
Set if we were forced to be shown.
boolean
mInputShown
Set if we last told the input method to show itself.
android.content.Intent
mCurIntent
The Intent used to connect to the current input method.
android.os.IBinder
mCurToken
The token we have made for the currently active input method, to identify it in the future.
com.android.internal.view.IInputMethod
mCurMethod
If non-null, this is the input method service we are currently connected to.
long
mLastBindTime
Time that we last initiated a bind to the input method, to determine if we should try to disconnect and reconnect to it.
boolean
mBoundToMethod
Have we called mCurMethod.bindInput()?
SessionState
mEnabledSession
Currently enabled session. Only touched by service thread, not protected by a lock.
boolean
mScreenOn
True if the screen is on. The value is true initially.
AlertDialog.Builder
mDialogBuilder
android.app.AlertDialog
mSwitchingDialog
android.view.inputmethod.InputMethodInfo[]
mIms
CharSequence[]
mItems
Constructors Summary
public InputMethodManagerService(android.content.Context context, com.android.server.status.StatusBarService statusBar)

        mContext = context;
        mHandler = new Handler(this);
        mIWindowManager = IWindowManager.Stub.asInterface(
                ServiceManager.getService(Context.WINDOW_SERVICE));
        mCaller = new HandlerCaller(context, new HandlerCaller.Callback() {
            public void executeMessage(Message msg) {
                handleMessage(msg);
            }
        });
        
        IntentFilter packageFilt = new IntentFilter();
        packageFilt.addAction(Intent.ACTION_PACKAGE_ADDED);
        packageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED);
        packageFilt.addAction(Intent.ACTION_PACKAGE_REMOVED);
        packageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED);
        packageFilt.addDataScheme("package");
        mContext.registerReceiver(new PackageReceiver(), packageFilt);
        
        IntentFilter screenOnOffFilt = new IntentFilter();
        screenOnOffFilt.addAction(Intent.ACTION_SCREEN_ON);
        screenOnOffFilt.addAction(Intent.ACTION_SCREEN_OFF);
        screenOnOffFilt.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
        mContext.registerReceiver(new ScreenOnOffReceiver(), screenOnOffFilt);
        
        buildInputMethodListLocked(mMethodList, mMethodMap);

        final String enabledStr = Settings.Secure.getString(
                mContext.getContentResolver(),
                Settings.Secure.ENABLED_INPUT_METHODS);
        Log.i(TAG, "Enabled input methods: " + enabledStr);
        if (enabledStr == null) {
            Log.i(TAG, "Enabled input methods has not been set, enabling all");
            InputMethodInfo defIm = null;
            StringBuilder sb = new StringBuilder(256);
            final int N = mMethodList.size();
            for (int i=0; i<N; i++) {
                InputMethodInfo imi = mMethodList.get(i);
                Log.i(TAG, "Adding: " + imi.getId());
                if (i > 0) sb.append(':");
                sb.append(imi.getId());
                if (defIm == null && imi.getIsDefaultResourceId() != 0) {
                    try {
                        Resources res = mContext.createPackageContext(
                                imi.getPackageName(), 0).getResources();
                        if (res.getBoolean(imi.getIsDefaultResourceId())) {
                            defIm = imi;
                            Log.i(TAG, "Selected default: " + imi.getId());
                        }
                    } catch (PackageManager.NameNotFoundException ex) {
                    } catch (Resources.NotFoundException ex) {
                    }
                }
            }
            if (defIm == null && N > 0) {
                defIm = mMethodList.get(0);
                Log.i(TAG, "No default found, using " + defIm.getId());
            }
            Settings.Secure.putString(mContext.getContentResolver(),
                    Settings.Secure.ENABLED_INPUT_METHODS, sb.toString());
            if (defIm != null) {
                Settings.Secure.putString(mContext.getContentResolver(),
                        Settings.Secure.DEFAULT_INPUT_METHOD, defIm.getId());
            }
        }
        
        mStatusBar = statusBar;
        mInputMethodData = IconData.makeIcon("ime", null, 0, 0, 0);
        mInputMethodIcon = statusBar.addIcon(mInputMethodData, null);
        statusBar.setIconVisibility(mInputMethodIcon, false);
        
        mSettingsObserver = new SettingsObserver(mHandler);
        updateFromSettingsLocked();
    
Methods Summary
public voidaddClient(com.android.internal.view.IInputMethodClient client, com.android.internal.view.IInputContext inputContext, int uid, int pid)

        synchronized (mMethodMap) {
            mClients.put(client.asBinder(), new ClientState(client,
                    inputContext, uid, pid));
        }
    
com.android.internal.view.InputBindResultattachNewInputLocked(boolean initial, boolean needResult)

        if (!mBoundToMethod) {
            executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
                    MSG_BIND_INPUT, mCurMethod, mCurClient.binding));
            mBoundToMethod = true;
        }
        final SessionState session = mCurClient.curSession;
        if (initial) {
            executeOrSendMessage(session.method, mCaller.obtainMessageOOO(
                    MSG_START_INPUT, session, mCurInputContext, mCurAttribute));
        } else {
            executeOrSendMessage(session.method, mCaller.obtainMessageOOO(
                    MSG_RESTART_INPUT, session, mCurInputContext, mCurAttribute));
        }
        if (mShowRequested) {
            if (DEBUG) Log.v(TAG, "Attach new input asks to show input");
            showCurrentInputLocked(getAppShowFlags(), null);
        }
        return needResult
                ? new InputBindResult(session.session, mCurId, mCurSeq)
                : null;
    
voidbuildInputMethodListLocked(java.util.ArrayList list, java.util.HashMap map)

        list.clear();
        map.clear();
        
        PackageManager pm = mContext.getPackageManager();

        List<ResolveInfo> services = pm.queryIntentServices(
                new Intent(InputMethod.SERVICE_INTERFACE),
                PackageManager.GET_META_DATA);
        
        for (int i = 0; i < services.size(); ++i) {
            ResolveInfo ri = services.get(i);
            ServiceInfo si = ri.serviceInfo;
            ComponentName compName = new ComponentName(si.packageName, si.name);
            if (!android.Manifest.permission.BIND_INPUT_METHOD.equals(
                    si.permission)) {
                Log.w(TAG, "Skipping input method " + compName
                        + ": it does not require the permission "
                        + android.Manifest.permission.BIND_INPUT_METHOD);
                continue;
            }

            if (DEBUG) Log.d(TAG, "Checking " + compName);

            try {
                InputMethodInfo p = new InputMethodInfo(mContext, ri);
                list.add(p);
                map.put(p.getId(), p);

                if (DEBUG) {
                    Log.d(TAG, "Found a third-party input method " + p);
                }
                
            } catch (XmlPullParserException e) {
                Log.w(TAG, "Unable to load input method " + compName, e);
            } catch (IOException e) {
                Log.w(TAG, "Unable to load input method " + compName, e);
            }
        }
    
voidclearCurMethodLocked()

        if (mCurMethod != null) {
            for (ClientState cs : mClients.values()) {
                cs.sessionRequested = false;
                cs.curSession = null;
            }
            mCurMethod = null;
        }
        mStatusBar.setIconVisibility(mInputMethodIcon, false);
    
protected voiddump(java.io.FileDescriptor fd, java.io.PrintWriter pw, java.lang.String[] args)

        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                != PackageManager.PERMISSION_GRANTED) {
            
            pw.println("Permission Denial: can't dump InputMethodManager from from pid="
                    + Binder.getCallingPid()
                    + ", uid=" + Binder.getCallingUid());
            return;
        }

        IInputMethod method;
        ClientState client;
        
        final Printer p = new PrintWriterPrinter(pw);
        
        synchronized (mMethodMap) {
            p.println("Current Input Method Manager state:");
            int N = mMethodList.size();
            p.println("  Input Methods:");
            for (int i=0; i<N; i++) {
                InputMethodInfo info = mMethodList.get(i);
                p.println("  InputMethod #" + i + ":");
                info.dump(p, "    ");
            }
            p.println("  Clients:");
            for (ClientState ci : mClients.values()) {
                p.println("  Client " + ci + ":");
                p.println("    client=" + ci.client);
                p.println("    inputContext=" + ci.inputContext);
                p.println("    sessionRequested=" + ci.sessionRequested);
                p.println("    curSession=" + ci.curSession);
            }
            p.println("  mInputMethodIcon=" + mInputMethodIcon);
            p.println("  mInputMethodData=" + mInputMethodData);
            p.println("  mCurMethodId=" + mCurMethodId);
            client = mCurClient;
            p.println("  mCurClient=" + client + " mCurSeq=" + mCurSeq);
            p.println("  mCurFocusedWindow=" + mCurFocusedWindow);
            p.println("  mCurId=" + mCurId + " mHaveConnect=" + mHaveConnection
                    + " mBoundToMethod=" + mBoundToMethod);
            p.println("  mCurToken=" + mCurToken);
            p.println("  mCurIntent=" + mCurIntent);
            method = mCurMethod;
            p.println("  mCurMethod=" + mCurMethod);
            p.println("  mEnabledSession=" + mEnabledSession);
            p.println("  mShowRequested=" + mShowRequested
                    + " mShowExplicitlyRequested=" + mShowExplicitlyRequested
                    + " mShowForced=" + mShowForced
                    + " mInputShown=" + mInputShown);
            p.println("  mScreenOn=" + mScreenOn);
        }
        
        if (client != null) {
            p.println(" ");
            pw.flush();
            try {
                client.client.asBinder().dump(fd, args);
            } catch (RemoteException e) {
                p.println("Input method client dead: " + e);
            }
        }
        
        if (method != null) {
            p.println(" ");
            pw.flush();
            try {
                method.asBinder().dump(fd, args);
            } catch (RemoteException e) {
                p.println("Input method service dead: " + e);
            }
        }
    
voidexecuteOrSendMessage(android.os.IInterface target, android.os.Message msg)

         if (target.asBinder() instanceof Binder) {
             mCaller.sendMessage(msg);
         } else {
             handleMessage(msg);
             msg.recycle();
         }
    
public voidfinishInput(com.android.internal.view.IInputMethodClient client)

    
private intgetAppShowFlags()

        int flags = 0;
        if (mShowForced) {
            flags |= InputMethodManager.SHOW_FORCED;
        } else if (!mShowExplicitlyRequested) {
            flags |= InputMethodManager.SHOW_IMPLICIT;
        }
        return flags;
    
public java.util.ListgetEnabledInputMethodList()

        synchronized (mMethodMap) {
            return getEnabledInputMethodListLocked();
        }
    
java.util.ListgetEnabledInputMethodListLocked()

        final ArrayList<InputMethodInfo> res = new ArrayList<InputMethodInfo>();
        
        final String enabledStr = Settings.Secure.getString(
                mContext.getContentResolver(),
                Settings.Secure.ENABLED_INPUT_METHODS);
        if (enabledStr != null) {
            final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
            splitter.setString(enabledStr);
            
            while (splitter.hasNext()) {
                InputMethodInfo info = mMethodMap.get(splitter.next());
                if (info != null) {
                    res.add(info);
                }
            }
        }
        
        return res;
    
private intgetImeShowFlags()

        int flags = 0;
        if (mShowForced) {
            flags |= InputMethod.SHOW_FORCED
                    | InputMethod.SHOW_EXPLICIT;
        } else if (mShowExplicitlyRequested) {
            flags |= InputMethod.SHOW_EXPLICIT;
        }
        return flags;
    
public java.util.ListgetInputMethodList()

        synchronized (mMethodMap) {
            return new ArrayList<InputMethodInfo>(mMethodList);
        }
    
public booleanhandleMessage(android.os.Message msg)

        HandlerCaller.SomeArgs args;
        switch (msg.what) {
            case MSG_SHOW_IM_PICKER:
                showInputMethodMenu();
                return true;
            
            // ---------------------------------------------------------
                
            case MSG_UNBIND_INPUT:
                try {
                    ((IInputMethod)msg.obj).unbindInput();
                } catch (RemoteException e) {
                    // There is nothing interesting about the method dying.
                }
                return true;
            case MSG_BIND_INPUT:
                args = (HandlerCaller.SomeArgs)msg.obj;
                try {
                    ((IInputMethod)args.arg1).bindInput((InputBinding)args.arg2);
                } catch (RemoteException e) {
                }
                return true;
            case MSG_SHOW_SOFT_INPUT:
                args = (HandlerCaller.SomeArgs)msg.obj;
                try {
                    ((IInputMethod)args.arg1).showSoftInput(msg.arg1,
                            (ResultReceiver)args.arg2);
                } catch (RemoteException e) {
                }
                return true;
            case MSG_HIDE_SOFT_INPUT:
                args = (HandlerCaller.SomeArgs)msg.obj;
                try {
                    ((IInputMethod)args.arg1).hideSoftInput(0,
                            (ResultReceiver)args.arg2);
                } catch (RemoteException e) {
                }
                return true;
            case MSG_ATTACH_TOKEN:
                args = (HandlerCaller.SomeArgs)msg.obj;
                try {
                    if (DEBUG) Log.v(TAG, "Sending attach of token: " + args.arg2);
                    ((IInputMethod)args.arg1).attachToken((IBinder)args.arg2);
                } catch (RemoteException e) {
                }
                return true;
            case MSG_CREATE_SESSION:
                args = (HandlerCaller.SomeArgs)msg.obj;
                try {
                    ((IInputMethod)args.arg1).createSession(
                            (IInputMethodCallback)args.arg2);
                } catch (RemoteException e) {
                }
                return true;
            // ---------------------------------------------------------
                
            case MSG_START_INPUT:
                args = (HandlerCaller.SomeArgs)msg.obj;
                try {
                    SessionState session = (SessionState)args.arg1;
                    setEnabledSessionInMainThread(session);
                    session.method.startInput((IInputContext)args.arg2,
                            (EditorInfo)args.arg3);
                } catch (RemoteException e) {
                }
                return true;
            case MSG_RESTART_INPUT:
                args = (HandlerCaller.SomeArgs)msg.obj;
                try {
                    SessionState session = (SessionState)args.arg1;
                    setEnabledSessionInMainThread(session);
                    session.method.restartInput((IInputContext)args.arg2,
                            (EditorInfo)args.arg3);
                } catch (RemoteException e) {
                }
                return true;
                
            // ---------------------------------------------------------
                
            case MSG_UNBIND_METHOD:
                try {
                    ((IInputMethodClient)msg.obj).onUnbindMethod(msg.arg1);
                } catch (RemoteException e) {
                    // There is nothing interesting about the last client dying.
                }
                return true;
            case MSG_BIND_METHOD:
                args = (HandlerCaller.SomeArgs)msg.obj;
                try {
                    ((IInputMethodClient)args.arg1).onBindMethod(
                            (InputBindResult)args.arg2);
                } catch (RemoteException e) {
                    Log.w(TAG, "Client died receiving input method " + args.arg2);
                }
                return true;
        }
        return false;
    
booleanhideCurrentInputLocked(int flags, android.os.ResultReceiver resultReceiver)

        if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
                && (mShowExplicitlyRequested || mShowForced)) {
            if (DEBUG) Log.v(TAG,
                    "Not hiding: explicit show not cancelled by non-explicit hide");
            return false;
        }
        if (mShowForced && (flags&InputMethodManager.HIDE_NOT_ALWAYS) != 0) {
            if (DEBUG) Log.v(TAG,
                    "Not hiding: forced show not cancelled by not-always hide");
            return false;
        }
        boolean res;
        if (mInputShown && mCurMethod != null) {
            executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
                    MSG_HIDE_SOFT_INPUT, mCurMethod, resultReceiver));
            res = true;
        } else {
            res = false;
        }
        mInputShown = false;
        mShowRequested = false;
        mShowExplicitlyRequested = false;
        mShowForced = false;
        return res;
    
voidhideInputMethodMenu()

        synchronized (mMethodMap) {
            hideInputMethodMenuLocked();
        }
    
voidhideInputMethodMenuLocked()

        if (DEBUG) Log.v(TAG, "Hide switching menu");

        if (mSwitchingDialog != null) {
            mSwitchingDialog.dismiss();
            mSwitchingDialog = null;
        }
        
        mDialogBuilder = null;
        mItems = null;
        mIms = null;
    
public voidhideMySoftInput(android.os.IBinder token, int flags)

        synchronized (mMethodMap) {
            if (token == null || mCurToken != token) {
                Log.w(TAG, "Ignoring hideInputMethod of token: " + token);
                return;
            }
            long ident = Binder.clearCallingIdentity();
            try {
                hideCurrentInputLocked(flags, null);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
    
public booleanhideSoftInput(com.android.internal.view.IInputMethodClient client, int flags, android.os.ResultReceiver resultReceiver)

        long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mMethodMap) {
                if (mCurClient == null || client == null
                        || mCurClient.client.asBinder() != client.asBinder()) {
                    try {
                        // We need to check if this is the current client with
                        // focus in the window manager, to allow this call to
                        // be made before input is started in it.
                        if (!mIWindowManager.inputMethodClientHasFocus(client)) {
                            Log.w(TAG, "Ignoring hideSoftInput of: " + client);
                            return false;
                        }
                    } catch (RemoteException e) {
                        return false;
                    }
                }
    
                if (DEBUG) Log.v(TAG, "Client requesting input be hidden");
                return hideCurrentInputLocked(flags, resultReceiver);
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    
public voidonServiceConnected(android.content.ComponentName name, android.os.IBinder service)

        synchronized (mMethodMap) {
            if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
                mCurMethod = IInputMethod.Stub.asInterface(service);
                if (mCurClient != null) {
                    if (DEBUG) Log.v(TAG, "Initiating attach with token: " + mCurToken);
                    executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
                            MSG_ATTACH_TOKEN, mCurMethod, mCurToken));
                    if (mCurClient != null) {
                        if (DEBUG) Log.v(TAG, "Creating first session while with client "
                                + mCurClient);
                        executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
                                MSG_CREATE_SESSION, mCurMethod,
                                new MethodCallback(mCurMethod)));
                    }
                }
            }
        }
    
public voidonServiceDisconnected(android.content.ComponentName name)

        synchronized (mMethodMap) {
            if (DEBUG) Log.v(TAG, "Service disconnected: " + name
                    + " mCurIntent=" + mCurIntent);
            if (mCurMethod != null && mCurIntent != null
                    && name.equals(mCurIntent.getComponent())) {
                clearCurMethodLocked();
                // We consider this to be a new bind attempt, since the system
                // should now try to restart the service for us.
                mLastBindTime = SystemClock.uptimeMillis();
                mShowRequested = mInputShown;
                mInputShown = false;
                if (mCurClient != null) {
                    executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
                            MSG_UNBIND_METHOD, mCurSeq, mCurClient.client));
                }
            }
        }
    
voidonSessionCreated(com.android.internal.view.IInputMethod method, com.android.internal.view.IInputMethodSession session)

        synchronized (mMethodMap) {
            if (mCurMethod != null && method != null
                    && mCurMethod.asBinder() == method.asBinder()) {
                if (mCurClient != null) {
                    mCurClient.curSession = new SessionState(mCurClient,
                            method, session);
                    mCurClient.sessionRequested = false;
                    InputBindResult res = attachNewInputLocked(true, true);
                    if (res.method != null) {
                        executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
                                MSG_BIND_METHOD, mCurClient.client, res));
                    }
                }
            }
        }
    
public booleanonTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)

        try {
            return super.onTransact(code, data, reply, flags);
        } catch (RuntimeException e) {
            // The input method manager only throws security exceptions, so let's
            // log all others.
            if (!(e instanceof SecurityException)) {
                Log.e(TAG, "Input Method Manager Crash", e);
            }
            throw e;
        }
    
public voidremoveClient(com.android.internal.view.IInputMethodClient client)

        synchronized (mMethodMap) {
            mClients.remove(client.asBinder());
        }
    
voidsetEnabledSessionInMainThread(com.android.server.InputMethodManagerService$SessionState session)

        if (mEnabledSession != session) {
            if (mEnabledSession != null) {
                try {
                    if (DEBUG) Log.v(TAG, "Disabling: " + mEnabledSession);
                    mEnabledSession.method.setSessionEnabled(
                            mEnabledSession.session, false);
                } catch (RemoteException e) {
                }
            }
            mEnabledSession = session;
            try {
                if (DEBUG) Log.v(TAG, "Enabling: " + mEnabledSession);
                session.method.setSessionEnabled(
                        session.session, true);
            } catch (RemoteException e) {
            }
        }
    
public voidsetInputMethod(android.os.IBinder token, java.lang.String id)

        synchronized (mMethodMap) {
            if (token == null) {
                if (mContext.checkCallingOrSelfPermission(
                        android.Manifest.permission.WRITE_SECURE_SETTINGS)
                        != PackageManager.PERMISSION_GRANTED) {
                    throw new SecurityException(
                            "Using null token requires permission "
                            + android.Manifest.permission.WRITE_SECURE_SETTINGS);
                }
            } else if (mCurToken != token) {
                Log.w(TAG, "Ignoring setInputMethod of token: " + token);
                return;
            }

            long ident = Binder.clearCallingIdentity();
            try {
                setInputMethodLocked(id);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
    
public booleansetInputMethodEnabled(java.lang.String id, boolean enabled)

        synchronized (mMethodMap) {
            if (mContext.checkCallingOrSelfPermission(
                    android.Manifest.permission.WRITE_SECURE_SETTINGS)
                    != PackageManager.PERMISSION_GRANTED) {
                throw new SecurityException(
                        "Requires permission "
                        + android.Manifest.permission.WRITE_SECURE_SETTINGS);
            }
            
            long ident = Binder.clearCallingIdentity();
            try {
                // Make sure this is a valid input method.
                InputMethodInfo imm = mMethodMap.get(id);
                if (imm == null) {
                    if (imm == null) {
                        throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
                    }
                }
                
                StringBuilder builder = new StringBuilder(256);
                
                boolean removed = false;
                String firstId = null;
                
                // Look through the currently enabled input methods.
                String enabledStr = Settings.Secure.getString(mContext.getContentResolver(),
                        Settings.Secure.ENABLED_INPUT_METHODS);
                if (enabledStr != null) {
                    final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
                    splitter.setString(enabledStr);
                    while (splitter.hasNext()) {
                        String curId = splitter.next();
                        if (curId.equals(id)) {
                            if (enabled) {
                                // We are enabling this input method, but it is
                                // already enabled.  Nothing to do.  The previous
                                // state was enabled.
                                return true;
                            }
                            // We are disabling this input method, and it is
                            // currently enabled.  Skip it to remove from the
                            // new list.
                            removed = true;
                        } else if (!enabled) {
                            // We are building a new list of input methods that
                            // doesn't contain the given one.
                            if (firstId == null) firstId = curId;
                            if (builder.length() > 0) builder.append(':");
                            builder.append(curId);
                        }
                    }
                }
                
                if (!enabled) {
                    if (!removed) {
                        // We are disabling the input method but it is already
                        // disabled.  Nothing to do.  The previous state was
                        // disabled.
                        return false;
                    }
                    // Update the setting with the new list of input methods.
                    Settings.Secure.putString(mContext.getContentResolver(),
                            Settings.Secure.ENABLED_INPUT_METHODS, builder.toString());
                    // We the disabled input method is currently selected, switch
                    // to another one.
                    String selId = Settings.Secure.getString(mContext.getContentResolver(),
                            Settings.Secure.DEFAULT_INPUT_METHOD);
                    if (id.equals(selId)) {
                        Settings.Secure.putString(mContext.getContentResolver(),
                                Settings.Secure.DEFAULT_INPUT_METHOD,
                                firstId != null ? firstId : "");
                    }
                    // Previous state was enabled.
                    return true;
                }
                
                // Add in the newly enabled input method.
                if (enabledStr == null || enabledStr.length() == 0) {
                    enabledStr = id;
                } else {
                    enabledStr = enabledStr + ':" + id;
                }
                
                Settings.Secure.putString(mContext.getContentResolver(),
                        Settings.Secure.ENABLED_INPUT_METHODS, enabledStr);
                
                // Previous state was disabled.
                return false;
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
    
voidsetInputMethodLocked(java.lang.String id)

        InputMethodInfo info = mMethodMap.get(id);
        if (info == null) {
            throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
        }
        
        if (id.equals(mCurMethodId)) {
            return;
        }
        
        final long ident = Binder.clearCallingIdentity();
        try {
            mCurMethodId = id;
            Settings.Secure.putString(mContext.getContentResolver(),
                Settings.Secure.DEFAULT_INPUT_METHOD, id);

            if (ActivityManagerNative.isSystemReady()) {
                Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED);
                intent.putExtra("input_method_id", id);
                mContext.sendBroadcast(intent);
            }
            unbindCurrentClientLocked();
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    
booleanshowCurrentInputLocked(int flags, android.os.ResultReceiver resultReceiver)

        mShowRequested = true;
        if ((flags&InputMethodManager.SHOW_IMPLICIT) == 0) {
            mShowExplicitlyRequested = true;
        }
        if ((flags&InputMethodManager.SHOW_FORCED) != 0) {
            mShowExplicitlyRequested = true;
            mShowForced = true;
        }
        boolean res = false;
        if (mCurMethod != null) {
            executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(
                    MSG_SHOW_SOFT_INPUT, getImeShowFlags(), mCurMethod,
                    resultReceiver));
            mInputShown = true;
            res = true;
        } else if (mHaveConnection && SystemClock.uptimeMillis()
                < (mLastBindTime+TIME_TO_RECONNECT)) {
            // The client has asked to have the input method shown, but
            // we have been sitting here too long with a connection to the
            // service and no interface received, so let's disconnect/connect
            // to try to prod things along.
            EventLog.writeEvent(LOG_IMF_FORCE_RECONNECT_IME, mCurMethodId,
                    SystemClock.uptimeMillis()-mLastBindTime,1);
            mContext.unbindService(this);
            mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE);
        }
        
        return res;
    
voidshowInputMethodMenu()

        if (DEBUG) Log.v(TAG, "Show switching menu");

        hideInputMethodMenu();
        
        final Context context = mContext;
        
        final PackageManager pm = context.getPackageManager();
        
        String lastInputMethodId = Settings.Secure.getString(context
                .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
        if (DEBUG) Log.v(TAG, "Current IME: " + lastInputMethodId);
        
        final List<InputMethodInfo> immis = getEnabledInputMethodList();
        
        int N = (immis == null ? 0 : immis.size());

        mItems = new CharSequence[N];
        mIms = new InputMethodInfo[N];

        for (int i = 0; i < N; ++i) {
            InputMethodInfo property = immis.get(i);
            mItems[i] = property.loadLabel(pm);
            mIms[i] = property;
        }

        int checkedItem = 0;
        for (int i = 0; i < N; ++i) {
            if (mIms[i].getId().equals(lastInputMethodId)) {
                checkedItem = i;
                break;
            }
        }

        AlertDialog.OnClickListener adocl = new AlertDialog.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                hideInputMethodMenu();
            }
        };
        
        TypedArray a = context.obtainStyledAttributes(null,
                com.android.internal.R.styleable.DialogPreference,
                com.android.internal.R.attr.alertDialogStyle, 0);
        mDialogBuilder = new AlertDialog.Builder(context)
                .setTitle(com.android.internal.R.string.select_input_method)
                .setOnCancelListener(new OnCancelListener() {
                    public void onCancel(DialogInterface dialog) {
                        hideInputMethodMenu();
                    }
                })
                .setIcon(a.getDrawable(
                        com.android.internal.R.styleable.DialogPreference_dialogTitle));
        a.recycle();
        
        mDialogBuilder.setSingleChoiceItems(mItems, checkedItem,
                new AlertDialog.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        synchronized (mMethodMap) {
                            InputMethodInfo im = mIms[which];
                            hideInputMethodMenu();
                            setInputMethodLocked(im.getId());
                        }
                    }
                });

        synchronized (mMethodMap) {
            mSwitchingDialog = mDialogBuilder.create();
            mSwitchingDialog.getWindow().setType(
                    WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG);
            mSwitchingDialog.show();
        }
    
public voidshowInputMethodPickerFromClient(com.android.internal.view.IInputMethodClient client)

        synchronized (mMethodMap) {
            if (mCurClient == null || client == null
                    || mCurClient.client.asBinder() != client.asBinder()) {
                Log.w(TAG, "Ignoring showInputMethodDialogFromClient of: " + client);
            }

            mHandler.sendEmptyMessage(MSG_SHOW_IM_PICKER);
        }
    
public voidshowMySoftInput(android.os.IBinder token, int flags)

        synchronized (mMethodMap) {
            if (token == null || mCurToken != token) {
                Log.w(TAG, "Ignoring hideInputMethod of token: " + token);
                return;
            }
            long ident = Binder.clearCallingIdentity();
            try {
                showCurrentInputLocked(flags, null);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
    
public booleanshowSoftInput(com.android.internal.view.IInputMethodClient client, int flags, android.os.ResultReceiver resultReceiver)

        long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mMethodMap) {
                if (mCurClient == null || client == null
                        || mCurClient.client.asBinder() != client.asBinder()) {
                    try {
                        // We need to check if this is the current client with
                        // focus in the window manager, to allow this call to
                        // be made before input is started in it.
                        if (!mIWindowManager.inputMethodClientHasFocus(client)) {
                            Log.w(TAG, "Ignoring showSoftInput of: " + client);
                            return false;
                        }
                    } catch (RemoteException e) {
                        return false;
                    }
                }
    
                if (DEBUG) Log.v(TAG, "Client requesting input be shown");
                return showCurrentInputLocked(flags, resultReceiver);
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    
public com.android.internal.view.InputBindResultstartInput(com.android.internal.view.IInputMethodClient client, com.android.internal.view.IInputContext inputContext, android.view.inputmethod.EditorInfo attribute, boolean initial, boolean needResult)

        synchronized (mMethodMap) {
            final long ident = Binder.clearCallingIdentity();
            try {
                return startInputLocked(client, inputContext, attribute,
                        initial, needResult);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
    
com.android.internal.view.InputBindResultstartInputLocked(com.android.internal.view.IInputMethodClient client, com.android.internal.view.IInputContext inputContext, android.view.inputmethod.EditorInfo attribute, boolean initial, boolean needResult)

        // If no method is currently selected, do nothing.
        if (mCurMethodId == null) {
            return mNoBinding;
        }
        
        ClientState cs = mClients.get(client.asBinder());
        if (cs == null) {
            throw new IllegalArgumentException("unknown client "
                    + client.asBinder());
        }
        
        try {
            if (!mIWindowManager.inputMethodClientHasFocus(cs.client)) {
                // Check with the window manager to make sure this client actually
                // has a window with focus.  If not, reject.  This is thread safe
                // because if the focus changes some time before or after, the
                // next client receiving focus that has any interest in input will
                // be calling through here after that change happens.
                Log.w(TAG, "Starting input on non-focused client " + cs.client
                        + " (uid=" + cs.uid + " pid=" + cs.pid + ")");
                return null;
            }
        } catch (RemoteException e) {
        }
        
        if (mCurClient != cs) {
            // If the client is changing, we need to switch over to the new
            // one.
            unbindCurrentClientLocked();
            if (DEBUG) Log.v(TAG, "switching to client: client = "
                    + cs.client.asBinder());

            // If the screen is on, inform the new client it is active
            if (mScreenOn) {
                try {
                    cs.client.setActive(mScreenOn);
                } catch (RemoteException e) {
                    Log.w(TAG, "Got RemoteException sending setActive notification to pid "
                            + cs.pid + " uid " + cs.uid);
                }
            }
        }
        
        // Bump up the sequence for this client and attach it.
        mCurSeq++;
        if (mCurSeq <= 0) mCurSeq = 1;
        mCurClient = cs;
        mCurInputContext = inputContext;
        mCurAttribute = attribute;
        
        // Check if the input method is changing.
        if (mCurId != null && mCurId.equals(mCurMethodId)) {
            if (cs.curSession != null) {
                // Fast case: if we are already connected to the input method,
                // then just return it.
                return attachNewInputLocked(initial, needResult);
            }
            if (mHaveConnection) {
                if (mCurMethod != null) {
                    if (!cs.sessionRequested) {
                        cs.sessionRequested = true;
                        if (DEBUG) Log.v(TAG, "Creating new session for client " + cs);
                        executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
                                MSG_CREATE_SESSION, mCurMethod,
                                new MethodCallback(mCurMethod)));
                    }
                    // Return to client, and we will get back with it when
                    // we have had a session made for it.
                    return new InputBindResult(null, mCurId, mCurSeq);
                } else if (SystemClock.uptimeMillis()
                        < (mLastBindTime+TIME_TO_RECONNECT)) {
                    // In this case we have connected to the service, but
                    // don't yet have its interface.  If it hasn't been too
                    // long since we did the connection, we'll return to
                    // the client and wait to get the service interface so
                    // we can report back.  If it has been too long, we want
                    // to fall through so we can try a disconnect/reconnect
                    // to see if we can get back in touch with the service.
                    return new InputBindResult(null, mCurId, mCurSeq);
                } else {
                    EventLog.writeEvent(LOG_IMF_FORCE_RECONNECT_IME, mCurMethodId,
                            SystemClock.uptimeMillis()-mLastBindTime, 0);
                }
            }
        }
        
        InputMethodInfo info = mMethodMap.get(mCurMethodId);
        if (info == null) {
            throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
        }
        
        unbindCurrentMethodLocked(false);
        
        mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE);
        mCurIntent.setComponent(info.getComponent());
        if (mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE)) {
            mLastBindTime = SystemClock.uptimeMillis();
            mHaveConnection = true;
            mCurId = info.getId();
            mCurToken = new Binder();
            try {
                if (DEBUG) Log.v(TAG, "Adding window token: " + mCurToken);
                mIWindowManager.addWindowToken(mCurToken,
                        WindowManager.LayoutParams.TYPE_INPUT_METHOD);
            } catch (RemoteException e) {
            }
            return new InputBindResult(null, mCurId, mCurSeq);
        } else {
            mCurIntent = null;
            Log.w(TAG, "Failure connecting to input method service: "
                    + mCurIntent);
        }
        return null;
    
public voidsystemReady()

    
voidunbindCurrentClientLocked()

        if (mCurClient != null) {
            if (DEBUG) Log.v(TAG, "unbindCurrentInputLocked: client = "
                    + mCurClient.client.asBinder());
            if (mBoundToMethod) {
                mBoundToMethod = false;
                if (mCurMethod != null) {
                    executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
                            MSG_UNBIND_INPUT, mCurMethod));
                }
            }
            executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
                    MSG_UNBIND_METHOD, mCurSeq, mCurClient.client));
            mCurClient.sessionRequested = false;
            
            // Call setActive(false) on the old client
            try {
                mCurClient.client.setActive(false);
            } catch (RemoteException e) {
                Log.w(TAG, "Got RemoteException sending setActive(false) notification to pid "
                        + mCurClient.pid + " uid " + mCurClient.uid);
            }
            mCurClient = null;
            
            hideInputMethodMenuLocked();
        }
    
voidunbindCurrentMethodLocked(boolean reportToClient)

        if (mHaveConnection) {
            mContext.unbindService(this);
            mHaveConnection = false;
        }
        
        if (mCurToken != null) {
            try {
                if (DEBUG) Log.v(TAG, "Removing window token: " + mCurToken);
                mIWindowManager.removeWindowToken(mCurToken);
            } catch (RemoteException e) {
            }
            mCurToken = null;
        }
        
        mCurId = null;
        clearCurMethodLocked();
        
        if (reportToClient && mCurClient != null) {
            executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
                    MSG_UNBIND_METHOD, mCurSeq, mCurClient.client));
        }
    
voidupdateFromSettingsLocked()

        // We are assuming that whoever is changing DEFAULT_INPUT_METHOD and
        // ENABLED_INPUT_METHODS is taking care of keeping them correctly in
        // sync, so we will never have a DEFAULT_INPUT_METHOD that is not
        // enabled.
        String id = Settings.Secure.getString(mContext.getContentResolver(),
            Settings.Secure.DEFAULT_INPUT_METHOD);
        if (id != null && id.length() > 0) {
            try {
                setInputMethodLocked(id);
            } catch (IllegalArgumentException e) {
                Log.w(TAG, "Unknown input method from prefs: " + id, e);
                mCurMethodId = null;
                unbindCurrentMethodLocked(true);
            }
        } else {
            // There is no longer an input method set, so stop any current one.
            mCurMethodId = null;
            unbindCurrentMethodLocked(true);
        }
    
public voidupdateStatusIcon(android.os.IBinder token, java.lang.String packageName, int iconId)

        long ident = Binder.clearCallingIdentity();
        try {
            if (token == null || mCurToken != token) {
                Log.w(TAG, "Ignoring setInputMethod of token: " + token);
                return;
            }
            
            synchronized (mMethodMap) {
                if (iconId == 0) {
                    if (DEBUG) Log.d(TAG, "hide the small icon for the input method");
                    mStatusBar.setIconVisibility(mInputMethodIcon, false);
                } else if (packageName != null) {
                    if (DEBUG) Log.d(TAG, "show a small icon for the input method");
                    mInputMethodData.iconId = iconId;
                    mInputMethodData.iconPackage = packageName;
                    mStatusBar.updateIcon(mInputMethodIcon, mInputMethodData, null);
                    mStatusBar.setIconVisibility(mInputMethodIcon, true);
                }
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    
public voidwindowGainedFocus(com.android.internal.view.IInputMethodClient client, android.os.IBinder windowToken, boolean viewHasFocus, boolean isTextEditor, int softInputMode, boolean first, int windowFlags)

        long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mMethodMap) {
                if (DEBUG) Log.v(TAG, "windowGainedFocus: " + client.asBinder()
                        + " viewHasFocus=" + viewHasFocus
                        + " isTextEditor=" + isTextEditor
                        + " softInputMode=#" + Integer.toHexString(softInputMode)
                        + " first=" + first + " flags=#"
                        + Integer.toHexString(windowFlags));
                
                if (mCurClient == null || client == null
                        || mCurClient.client.asBinder() != client.asBinder()) {
                    try {
                        // We need to check if this is the current client with
                        // focus in the window manager, to allow this call to
                        // be made before input is started in it.
                        if (!mIWindowManager.inputMethodClientHasFocus(client)) {
                            Log.w(TAG, "Client not active, ignoring focus gain of: " + client);
                            return;
                        }
                    } catch (RemoteException e) {
                    }
                }
    
                if (mCurFocusedWindow == windowToken) {
                    Log.w(TAG, "Window already focused, ignoring focus gain of: " + client);
                    return;
                }
                mCurFocusedWindow = windowToken;
                
                switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) {
                    case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
                        if (!isTextEditor || (softInputMode &
                                WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
                                != WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) {
                            if (WindowManager.LayoutParams.mayUseInputMethod(windowFlags)) {
                                // There is no focus view, and this window will
                                // be behind any soft input window, so hide the
                                // soft input window if it is shown.
                                if (DEBUG) Log.v(TAG, "Unspecified window will hide input");
                                hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS, null);
                            }
                        } else if (isTextEditor && (softInputMode &
                                WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST)
                                == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
                                && (softInputMode &
                                        WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
                            // There is a focus view, and we are navigating forward
                            // into the window, so show the input window for the user.
                            if (DEBUG) Log.v(TAG, "Unspecified window will show input");
                            showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
                        }
                        break;
                    case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
                        // Do nothing.
                        break;
                    case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN:
                        if ((softInputMode &
                                WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
                            if (DEBUG) Log.v(TAG, "Window asks to hide input going forward");
                            hideCurrentInputLocked(0, null);
                        }
                        break;
                    case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
                        if (DEBUG) Log.v(TAG, "Window asks to hide input");
                        hideCurrentInputLocked(0, null);
                        break;
                    case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE:
                        if ((softInputMode &
                                WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
                            if (DEBUG) Log.v(TAG, "Window asks to show input going forward");
                            showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
                        }
                        break;
                    case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE:
                        if (DEBUG) Log.v(TAG, "Window asks to always show input");
                        showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT, null);
                        break;
                }
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }