Allow IMEs to start/stop receiving onUpdateCursor callback

This CL introduces an API which allows IMEs to start/stop
receiving onUpdateCursor callback upon their request.

BUG: 13388665
Change-Id: I987326872def181dda5d9d701b762f088e0d9c39
diff --git a/api/current.txt b/api/current.txt
index 759b8d0..597e4e7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12469,6 +12469,7 @@
     method public void setBackDisposition(int);
     method public void setCandidatesView(android.view.View);
     method public void setCandidatesViewShown(boolean);
+    method public void setCursorAnchorMonitorMode(int);
     method public void setExtractView(android.view.View);
     method public void setExtractViewShown(boolean);
     method public void setInputView(android.view.View);
@@ -12480,6 +12481,8 @@
     field public static final int BACK_DISPOSITION_DEFAULT = 0; // 0x0
     field public static final int BACK_DISPOSITION_WILL_DISMISS = 2; // 0x2
     field public static final int BACK_DISPOSITION_WILL_NOT_DISMISS = 1; // 0x1
+    field public static final int CURSOR_ANCHOR_MONITOR_MODE_CURSOR_RECT = 1; // 0x1
+    field public static final int CURSOR_ANCHOR_MONITOR_MODE_NONE = 0; // 0x0
   }
 
   public class InputMethodService.InputMethodImpl extends android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodImpl {
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index a355d1e..c51d1a7 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -249,6 +249,16 @@
      */
     public static final int IME_VISIBLE = 0x2;
 
+    /**
+     * The IME does not require cursor/anchor position.
+     */
+    public static final int CURSOR_ANCHOR_MONITOR_MODE_NONE = 0x0;
+
+    /**
+     * The IME expects that {@link #onUpdateCursor(Rect)} is called back.
+     */
+    public static final int CURSOR_ANCHOR_MONITOR_MODE_CURSOR_RECT = 0x1;
+
     InputMethodManager mImm;
     
     int mTheme = 0;
@@ -1702,6 +1712,13 @@
     }
 
     /**
+     * Update the cursor/anthor monitor mode.
+     */
+    public void setCursorAnchorMonitorMode(int monitorMode) {
+        mImm.setCursorAnchorMonitorMode(mToken, monitorMode);
+    }
+
+    /**
      * Close this input method's soft input area, removing it from the display.
      * The input method will continue running, but the user can no longer use
      * it to generate input by touching the screen.
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index f2ce113..e3bd9fd 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -26,6 +26,7 @@
 
 import android.content.Context;
 import android.graphics.Rect;
+import android.inputmethodservice.InputMethodService;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -41,13 +42,13 @@
 import android.util.Pools.SimplePool;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
+import android.util.SparseArray;
 import android.view.InputChannel;
 import android.view.InputEvent;
 import android.view.InputEventSender;
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewRootImpl;
-import android.util.SparseArray;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -334,6 +335,11 @@
     InputChannel mCurChannel;
     ImeInputEventSender mCurSender;
 
+    /**
+     * The current cursor/anchor monitor mode.
+     */
+    int mCursorAnchorMonitorMode = InputMethodService.CURSOR_ANCHOR_MONITOR_MODE_NONE;
+
     final Pool<PendingEvent> mPendingEventPool = new SimplePool<PendingEvent>(20);
     final SparseArray<PendingEvent> mPendingEvents = new SparseArray<PendingEvent>(20);
 
@@ -346,6 +352,7 @@
     static final int MSG_SEND_INPUT_EVENT = 5;
     static final int MSG_TIMEOUT_INPUT_EVENT = 6;
     static final int MSG_FLUSH_INPUT_EVENT = 7;
+    static final int SET_CURSOR_ANCHOR_MONITOR_MODE = 8;
 
     class H extends Handler {
         H(Looper looper) {
@@ -476,6 +483,12 @@
                     finishedInputEvent(msg.arg1, false, false);
                     return;
                 }
+                case SET_CURSOR_ANCHOR_MONITOR_MODE: {
+                    synchronized (mH) {
+                        mCursorAnchorMonitorMode = msg.arg1;
+                    }
+                    return;
+                }
             }
         }
     }
@@ -540,6 +553,11 @@
         public void setActive(boolean active) {
             mH.sendMessage(mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, 0));
         }
+
+        @Override
+        public void setCursorAnchorMonitorMode(int monitorMode) {
+            mH.sendMessage(mH.obtainMessage(SET_CURSOR_ANCHOR_MONITOR_MODE, monitorMode, 0));
+        }
     };
 
     final InputConnection mDummyInputConnection = new BaseInputConnection(this, false);
@@ -1465,9 +1483,30 @@
      * of the input editor's cursor in its window.
      */
     public boolean isWatchingCursor(View view) {
-        return false;
+        if (!isActive(view)) {
+            return false;
+        }
+        synchronized (mH) {
+            return mCursorAnchorMonitorMode ==
+                    InputMethodService.CURSOR_ANCHOR_MONITOR_MODE_CURSOR_RECT;
+        }
     }
-    
+
+    /**
+     * Set cursor/anchor monitor mode via {@link com.android.server.InputMethodManagerService}.
+     * This is an internal method for {@link android.inputmethodservice.InputMethodService} and
+     * should never be used from IMEs and applications.
+     *
+     * @hide
+     */
+    public void setCursorAnchorMonitorMode(IBinder imeToken, int monitorMode) {
+        try {
+            mService.setCursorAnchorMonitorMode(imeToken, monitorMode);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     /**
      * Report the current cursor location in its window.
      */
diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index ce4312d..9e8d12b 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -27,4 +27,5 @@
     void onBindMethod(in InputBindResult res);
     void onUnbindMethod(int sequence);
     void setActive(boolean active);
+    void setCursorAnchorMonitorMode(int monitorMode);
 }
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 45ca7fc..5336174 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -78,4 +78,5 @@
     void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
     int getInputMethodWindowVisibleHeight();
     oneway void notifyTextCommitted();
+    void setCursorAnchorMonitorMode(in IBinder token, int monitorMode);
 }
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index e69c9a4..6fc3e77 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -148,6 +148,7 @@
     static final int MSG_UNBIND_METHOD = 3000;
     static final int MSG_BIND_METHOD = 3010;
     static final int MSG_SET_ACTIVE = 3020;
+    static final int MSG_SET_CURSOR_ANCHOR_MONITOR_MODE = 3030;
 
     static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
 
@@ -2275,6 +2276,27 @@
         }
     }
 
+    @Override
+    public void setCursorAnchorMonitorMode(IBinder token, int monitorMode) {
+        if (DEBUG) {
+            Slog.d(TAG, "setCursorAnchorMonitorMode: monitorMode=" + monitorMode);
+        }
+        if (!calledFromValidUser()) {
+            return;
+        }
+        synchronized (mMethodMap) {
+            if (token == null || mCurToken != token) {
+                if (DEBUG) {
+                    Slog.w(TAG, "Ignoring setCursorAnchorMonitorMode from uid "
+                            + Binder.getCallingUid() + " token: " + token);
+                }
+                return;
+            }
+            executeOrSendMessage(mCurMethod, mCaller.obtainMessageIO(
+                    MSG_SET_CURSOR_ANCHOR_MONITOR_MODE, monitorMode, mCurClient));
+        }
+    }
+
     private void setInputMethodWithSubtypeId(IBinder token, String id, int subtypeId) {
         synchronized (mMethodMap) {
             if (token == null) {
@@ -2506,6 +2528,15 @@
                             + ((ClientState)msg.obj).uid);
                 }
                 return true;
+            case MSG_SET_CURSOR_ANCHOR_MONITOR_MODE:
+                try {
+                    ((ClientState)msg.obj).client.setCursorAnchorMonitorMode(msg.arg1);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Got RemoteException sending setCursorAnchorMonitorMode "
+                            + "notification to pid " + ((ClientState)msg.obj).pid
+                            + " uid " + ((ClientState)msg.obj).uid);
+                }
+                return true;
 
             // --------------------------------------------------------------
             case MSG_HARD_KEYBOARD_SWITCH_CHANGED: