Add InputMethodService#exposeContent()

This is a follow up CL to my previous CLs [1][2] that introduced
InputConnection#commitContent(InputContentInfo, Bundle) API to enable
IMEs to send a content to the target application.

With this CL, IME developers are able to temporarily expose
InputContentInfo object to the target package without permanently
granting URI permission.  Although calling IMS#exposeContent() is
allowed only for the IME that is currently selected, the client is able
to request a temporary read-only access even after the current IME is
switched to any other IME as long as the client keeps InputContentInfo
object.

Here is a sample code snippet about how to use this mechanism.

  [IME]
  InputContentInfo contentInfo = new InputContentInfo(
          contentUri,
          new ClipDescription(description, new String[]{mimeType}),
          linkUrl);
  exposeContent(contentInfo, getCurrentInputEditorInfo());
  getCurrentInputConnection().commitContent(inputContentInfo, null);

  [App]
  try {
      contentInfo.requestPermission();
      // Load inputContentInfo.getContentUri() here.
  } finally {
      contentInfo.releasePermission();
  }

 [1]: Iaadf934a997ffcd6000a516cc3c1873db56e60ad
      152944f4909c47917473293b258d266435c6ab35
 [2]: Ica1ba3154795c1bf44e140dfe639b299f83cd8af
      adebb52588b098a1af678d4e33a234ef1ce783b2

Bug: 29450031
Change-Id: I2772889ca01f2ecb2cdeed4e04a9319bdf7bc5a6
diff --git a/Android.mk b/Android.mk
index 50bed92..320fe2f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -318,6 +318,7 @@
 	core/java/com/android/internal/appwidget/IAppWidgetHost.aidl \
 	core/java/com/android/internal/backup/IBackupTransport.aidl \
 	core/java/com/android/internal/backup/IObbBackupService.aidl \
+	core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl \
 	core/java/com/android/internal/policy/IKeyguardDrawnCallback.aidl \
 	core/java/com/android/internal/policy/IKeyguardExitCallback.aidl \
 	core/java/com/android/internal/policy/IKeyguardService.aidl \
diff --git a/api/current.txt b/api/current.txt
index a69f503..a54d055 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -18862,6 +18862,7 @@
   public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService {
     ctor public InputMethodService();
     method public deprecated boolean enableHardwareAcceleration();
+    method public final boolean exposeContent(android.view.inputmethod.InputContentInfo, android.view.inputmethod.EditorInfo);
     method public int getBackDisposition();
     method public int getCandidatesHiddenVisibility();
     method public android.view.inputmethod.InputBinding getCurrentInputBinding();
@@ -44921,13 +44922,15 @@
     method public void setTarget(android.view.inputmethod.InputConnection);
   }
 
-  public class InputContentInfo implements android.os.Parcelable {
+  public final class InputContentInfo implements android.os.Parcelable {
     ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription);
     ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription, android.net.Uri);
     method public int describeContents();
     method public android.net.Uri getContentUri();
     method public android.content.ClipDescription getDescription();
     method public android.net.Uri getLinkUri();
+    method public void releasePermission();
+    method public void requestPermission();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.inputmethod.InputContentInfo> CREATOR;
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index e30c247..fc50e01 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -20062,6 +20062,7 @@
   public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService {
     ctor public InputMethodService();
     method public deprecated boolean enableHardwareAcceleration();
+    method public final boolean exposeContent(android.view.inputmethod.InputContentInfo, android.view.inputmethod.EditorInfo);
     method public int getBackDisposition();
     method public int getCandidatesHiddenVisibility();
     method public android.view.inputmethod.InputBinding getCurrentInputBinding();
@@ -48028,13 +48029,15 @@
     method public void setTarget(android.view.inputmethod.InputConnection);
   }
 
-  public class InputContentInfo implements android.os.Parcelable {
+  public final class InputContentInfo implements android.os.Parcelable {
     ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription);
     ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription, android.net.Uri);
     method public int describeContents();
     method public android.net.Uri getContentUri();
     method public android.content.ClipDescription getDescription();
     method public android.net.Uri getLinkUri();
+    method public void releasePermission();
+    method public void requestPermission();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.inputmethod.InputContentInfo> CREATOR;
   }
diff --git a/api/test-current.txt b/api/test-current.txt
index 480d8dc..d5ea5e2 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -18876,6 +18876,7 @@
   public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService {
     ctor public InputMethodService();
     method public deprecated boolean enableHardwareAcceleration();
+    method public final boolean exposeContent(android.view.inputmethod.InputContentInfo, android.view.inputmethod.EditorInfo);
     method public int getBackDisposition();
     method public int getCandidatesHiddenVisibility();
     method public android.view.inputmethod.InputBinding getCurrentInputBinding();
@@ -45001,13 +45002,15 @@
     method public void setTarget(android.view.inputmethod.InputConnection);
   }
 
-  public class InputContentInfo implements android.os.Parcelable {
+  public final class InputContentInfo implements android.os.Parcelable {
     ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription);
     ctor public InputContentInfo(android.net.Uri, android.content.ClipDescription, android.net.Uri);
     method public int describeContents();
     method public android.net.Uri getContentUri();
     method public android.content.ClipDescription getDescription();
     method public android.net.Uri getLinkUri();
+    method public void releasePermission();
+    method public void requestPermission();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.inputmethod.InputContentInfo> CREATOR;
   }
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 4799773..8e0e0b0 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -23,6 +23,7 @@
 import android.annotation.DrawableRes;
 import android.annotation.IntDef;
 import android.annotation.MainThread;
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.Dialog;
 import android.content.Context;
@@ -65,6 +66,7 @@
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputBinding;
 import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputContentInfo;
 import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
@@ -2598,6 +2600,39 @@
     }
 
     /**
+     * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
+     * permission to the content.
+     *
+     * <p>Make sure that the content provider owning the Uri sets the
+     * {@link android.R.styleable#AndroidManifestProvider_grantUriPermissions
+     * grantUriPermissions} attribute in its manifest or included the
+     * {@link android.R.styleable#AndroidManifestGrantUriPermission
+     * &lt;grant-uri-permissions&gt;} tag. Otherwise {@link InputContentInfo#requestPermission()}
+     * can fail.</p>
+     *
+     * <p>Although calling this API is allowed only for the IME that is currently selected, the
+     * client is able to request a temporary read-only access even after the current IME is switched
+     * to any other IME as long as the client keeps {@link InputContentInfo} object.</p>
+     *
+     * @param inputContentInfo Content to be temporarily exposed from the input method to the
+     * application.
+     * This cannot be {@code null}.
+     * @param editorInfo The editor that receives {@link InputContentInfo}.
+     * @return {@code false} if we cannot allow a temporary access permission.
+     */
+    public final boolean exposeContent(@NonNull InputContentInfo inputContentInfo,
+            @NonNull EditorInfo editorInfo) {
+        if (inputContentInfo == null) {
+            throw new NullPointerException("inputContentInfo");
+        }
+        if (editorInfo == null) {
+            throw new NullPointerException("editorInfo");
+        }
+
+        return mImm.exposeContent(mToken, inputContentInfo, editorInfo);
+    }
+
+    /**
      * Performs a dump of the InputMethodService's internal state.  Override
      * to add your own information to the dump.
      */
diff --git a/core/java/android/view/inputmethod/InputContentInfo.java b/core/java/android/view/inputmethod/InputContentInfo.java
index e2ecfae..9579bbf 100644
--- a/core/java/android/view/inputmethod/InputContentInfo.java
+++ b/core/java/android/view/inputmethod/InputContentInfo.java
@@ -22,13 +22,16 @@
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.RemoteException;
+
+import com.android.internal.inputmethod.IInputContentUriToken;
 
 import java.security.InvalidParameterException;
 
 /**
  * A container object with which input methods can send content files to the target application.
  */
-public class InputContentInfo implements Parcelable {
+public final class InputContentInfo implements Parcelable {
 
     @NonNull
     private final Uri mContentUri;
@@ -36,6 +39,8 @@
     private final ClipDescription mDescription;
     @Nullable
     private final Uri mLinkUri;
+    @NonNull
+    private IInputContentUriToken mUriToken;
 
     /**
      * Constructs {@link InputContentInfo} object only with mandatory data.
@@ -110,7 +115,7 @@
             return false;
         }
         final String contentUriScheme = contentUri.getScheme();
-        if (contentUriScheme == null || !contentUriScheme.equalsIgnoreCase("content")) {
+        if (!"content".equals(contentUriScheme)) {
             if (throwException) {
                 throw new InvalidParameterException("contentUri must have content scheme");
             }
@@ -137,8 +142,9 @@
     public Uri getContentUri() { return mContentUri; }
 
     /**
-     * @return {@link ClipDescription} object that contains the metadata of {@code contentUri} such
-     * as MIME type(s). {@link ClipDescription#getLabel()} can be used for accessibility purpose.
+     * @return {@link ClipDescription} object that contains the metadata of {@code #getContentUri()}
+     * such as MIME type(s). {@link ClipDescription#getLabel()} can be used for accessibility
+     * purpose.
      */
     @NonNull
     public ClipDescription getDescription() { return mDescription; }
@@ -149,6 +155,47 @@
     @Nullable
     public Uri getLinkUri() { return mLinkUri; }
 
+    void setUriToken(IInputContentUriToken token) {
+        if (mUriToken != null) {
+            throw new IllegalStateException("URI token is already set");
+        }
+        mUriToken = token;
+    }
+
+    /**
+     * Requests a temporary read-only access permission for content URI associated with this object.
+     *
+     * <p>Does nothing if the temporary permission is already granted.</p>
+     */
+    public void requestPermission() {
+        if (mUriToken == null) {
+            return;
+        }
+        try {
+            mUriToken.take();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Releases a temporary read-only access permission for content URI associated with this object.
+     *
+     * <p>Does nothing if the temporary permission is not granted.</p>
+     */
+    public void releasePermission() {
+        if (mUriToken == null) {
+            return;
+        }
+        try {
+            mUriToken.release();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        } finally {
+            mUriToken = null;
+        }
+    }
+
     /**
      * Used to package this object into a {@link Parcel}.
      *
@@ -160,12 +207,23 @@
         Uri.writeToParcel(dest, mContentUri);
         mDescription.writeToParcel(dest, flags);
         Uri.writeToParcel(dest, mLinkUri);
+        if (mUriToken != null) {
+            dest.writeInt(1);
+            dest.writeStrongBinder(mUriToken.asBinder());
+        } else {
+            dest.writeInt(0);
+        }
     }
 
     private InputContentInfo(@NonNull Parcel source) {
         mContentUri = Uri.CREATOR.createFromParcel(source);
         mDescription = ClipDescription.CREATOR.createFromParcel(source);
         mLinkUri = Uri.CREATOR.createFromParcel(source);
+        if (source.readInt() == 1) {
+            mUriToken = IInputContentUriToken.Stub.asInterface(source.readStrongBinder());
+        } else {
+            mUriToken = null;
+        }
     }
 
     /**
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 4013b30..b35f5c3 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -16,6 +16,7 @@
 
 package android.view.inputmethod;
 
+import com.android.internal.inputmethod.IInputContentUriToken;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.view.IInputConnectionWrapper;
 import com.android.internal.view.IInputContext;
@@ -30,6 +31,7 @@
 import android.annotation.RequiresPermission;
 import android.content.Context;
 import android.graphics.Rect;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -56,6 +58,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.security.InvalidParameterException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -2288,6 +2291,41 @@
         }
     }
 
+    /**
+     * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
+     * permission to the content.
+     *
+     * <p>See {@link android.inputmethodservice.InputMethodService#exposeContent(InputContentInfo, EditorInfo)}
+     * for details.</p>
+     *
+     * @param token Supplies the identifying token given to an input method when it was started,
+     * which allows it to perform this operation on itself.
+     * @param inputContentInfo Content to be temporarily exposed from the input method to the
+     * application.
+     * This cannot be {@code null}.
+     * @param editorInfo The editor that receives {@link InputContentInfo}.
+     * @return {@code false} if we cannot allow a temporary access permission.
+     * @hide
+     */
+    public boolean exposeContent(@NonNull IBinder token, @NonNull InputContentInfo inputContentInfo,
+            @NonNull EditorInfo editorInfo) {
+        final IInputContentUriToken uriToken;
+        final Uri contentUri = inputContentInfo.getContentUri();
+        try {
+            uriToken = mService.createInputContentUriToken(token, contentUri,
+                    editorInfo.packageName);
+            if (uriToken == null) {
+                return false;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "createInputContentAccessToken failed. contentUri=" + contentUri.toString()
+                    + " packageName=" + editorInfo.packageName, e);
+            return false;
+        }
+        inputContentInfo.setUriToken(uriToken);
+        return true;
+    }
+
     void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
         final Printer p = new PrintWriterPrinter(fout);
         p.println("Input method client state for " + this + ":");
diff --git a/core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl b/core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl
new file mode 100644
index 0000000..8abc807
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl
@@ -0,0 +1,27 @@
+/*
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package com.android.internal.inputmethod;
+
+import android.os.IBinder;
+
+/**
+ * {@hide}
+ */
+interface IInputContentUriToken {
+    void take();
+    void release();
+}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index cb7c3bf..9e4b43b 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -16,11 +16,13 @@
 
 package com.android.internal.view;
 
+import android.net.Uri;
 import android.os.ResultReceiver;
 import android.text.style.SuggestionSpan;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype;
 import android.view.inputmethod.EditorInfo;
+import com.android.internal.inputmethod.IInputContentUriToken;
 import com.android.internal.view.InputBindResult;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
@@ -81,5 +83,8 @@
     int getInputMethodWindowVisibleHeight();
     void clearLastInputMethodWindowForTransition(in IBinder token);
 
+    IInputContentUriToken createInputContentUriToken(in IBinder token, in Uri contentUri,
+            in String packageName);
+
     oneway void notifyUserAction(int sequenceNumber);
 }
diff --git a/services/core/java/com/android/server/InputContentUriTokenHandler.java b/services/core/java/com/android/server/InputContentUriTokenHandler.java
new file mode 100644
index 0000000..3f4972b
--- /dev/null
+++ b/services/core/java/com/android/server/InputContentUriTokenHandler.java
@@ -0,0 +1,121 @@
+/*
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package com.android.server;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.ActivityManagerNative;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.IInputContentUriToken;
+
+final class InputContentUriTokenHandler extends IInputContentUriToken.Stub {
+
+    @NonNull
+    private final Uri mUri;
+    private final int mSourceUid;
+    @NonNull
+    private final String mTargetPackage;
+    @UserIdInt
+    private final int mSourceUserId;
+    @UserIdInt
+    private final int mTargetUserId;
+
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private IBinder mPermissionOwnerToken = null;
+
+    InputContentUriTokenHandler(@NonNull Uri contentUri, int sourceUid,
+            @NonNull String targetPackage, @UserIdInt int sourceUserId,
+            @UserIdInt int targetUserId) {
+        mUri = contentUri;
+        mSourceUid = sourceUid;
+        mTargetPackage = targetPackage;
+        mSourceUserId = sourceUserId;
+        mTargetUserId = targetUserId;
+    }
+
+    @Override
+    public void take() {
+        synchronized (mLock) {
+            if (mPermissionOwnerToken != null) {
+                // Permission is already granted.
+                return;
+            }
+
+            try {
+                mPermissionOwnerToken = ActivityManagerNative.getDefault()
+                        .newUriPermissionOwner("InputContentUriTokenHandler");
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+
+            doTakeLocked(mPermissionOwnerToken);
+        }
+    }
+
+    private void doTakeLocked(@NonNull IBinder permissionOwner) {
+        long origId = Binder.clearCallingIdentity();
+        try {
+            try {
+                ActivityManagerNative.getDefault().grantUriPermissionFromOwner(
+                        permissionOwner, mSourceUid, mTargetPackage, mUri,
+                        Intent.FLAG_GRANT_READ_URI_PERMISSION, mSourceUserId, mTargetUserId);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public void release() {
+        synchronized (mLock) {
+            if (mPermissionOwnerToken == null) {
+                return;
+            }
+            try {
+                ActivityManagerNative.getDefault().revokeUriPermissionFromOwner(
+                        mPermissionOwnerToken, mUri,
+                        Intent.FLAG_GRANT_READ_URI_PERMISSION, mSourceUserId);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            } finally {
+                mPermissionOwnerToken = null;
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            release();
+        } finally {
+            super.finalize();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 5d8fe7c..e0d89f2 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -18,6 +18,7 @@
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import com.android.internal.content.PackageMonitor;
+import com.android.internal.inputmethod.IInputContentUriToken;
 import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController;
 import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
 import com.android.internal.inputmethod.InputMethodUtils;
@@ -137,6 +138,7 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.nio.charset.StandardCharsets;
+import java.security.InvalidParameterException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -3911,6 +3913,52 @@
     }
 
     @Override
+    public IInputContentUriToken createInputContentUriToken(@Nullable IBinder token,
+            @Nullable Uri contentUri, @Nullable String packageName) {
+        if (!calledFromValidUser()) {
+            return null;
+        }
+
+        if (token == null) {
+            throw new NullPointerException("token");
+        }
+        if (packageName == null) {
+            throw new NullPointerException("packageName");
+        }
+        if (contentUri == null) {
+            throw new NullPointerException("contentUri");
+        }
+        final String contentUriScheme = contentUri.getScheme();
+        if (!"content".equals(contentUriScheme)) {
+            throw new InvalidParameterException("contentUri must have content scheme");
+        }
+
+        synchronized (mMethodMap) {
+            final int uid = Binder.getCallingUid();
+            if (mCurMethodId == null) {
+                return null;
+            }
+            if (mCurToken != token) {
+                Slog.e(TAG, "Ignoring createInputContentUriToken mCurToken=" + mCurToken
+                        + " token=" + token);
+                return null;
+            }
+            // We cannot simply distinguish a bad IME that reports an arbitrary package name from
+            // an unfortunate IME whose internal state is already obsolete due to the asynchronous
+            // nature of our system.  Let's compare it with our internal record.
+            if (!TextUtils.equals(mCurAttribute.packageName, packageName)) {
+                Slog.e(TAG, "Ignoring createInputContentUriToken mCurAttribute.packageName="
+                    + mCurAttribute.packageName + " packageName=" + packageName);
+                return null;
+            }
+            final int imeUserId = UserHandle.getUserId(uid);
+            final int appUserId = UserHandle.getUserId(mCurClient.uid);
+            return new InputContentUriTokenHandler(contentUri, uid, packageName, imeUserId,
+                    appUserId);
+        }
+    }
+
+    @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                 != PackageManager.PERMISSION_GRANTED) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
index 3f276c9..ab73a8b 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
@@ -16,11 +16,13 @@
 
 package com.android.layoutlib.bridge.android;
 
+import com.android.internal.inputmethod.IInputContentUriToken;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
 import com.android.internal.view.IInputMethodManager;
 import com.android.internal.view.InputBindResult;
 
+import android.net.Uri;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -239,4 +241,11 @@
         // TODO Auto-generated method stub
         return null;
     }
+
+    @Override
+    public IInputContentUriToken createInputContentUriToken(IBinder token, Uri contentUri,
+            String packageName) {
+        // TODO Auto-generated method stub
+        return null;
+    }
 }