Merge "Allow IMM to forward API calls to IMS"

This commit is contained in:
TreeHugger Robot 2018-09-10 06:13:52 +00:00 committed by Android (Google) Code Review
commit d172e22a6a
5 changed files with 216 additions and 53 deletions

View File

@ -458,6 +458,7 @@ public class InputMethodService extends AbstractInputMethodService {
public final void initializeInternal(IBinder token,
IInputMethodPrivilegedOperations privilegedOperations) {
mPrivOps.set(privilegedOperations);
mImm.registerInputMethodPrivOps(token, mPrivOps);
attachToken(token);
}
@ -1000,6 +1001,11 @@ public class InputMethodService extends AbstractInputMethodService {
mSettingsObserver.unregister();
mSettingsObserver = null;
}
if (mToken != null) {
// This is completely optional, but allows us to show more explicit error messages
// when IME developers are doing something unsupported.
mImm.unregisterInputMethodPrivOps(mToken);
}
}
/**

View File

@ -55,6 +55,8 @@ import android.view.ViewRootImpl;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.autofill.AutofillManager;
import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import com.android.internal.os.SomeArgs;
import com.android.internal.view.IInputConnectionWrapper;
import com.android.internal.view.IInputContext;
@ -405,6 +407,9 @@ public final class InputMethodManager {
final Pool<PendingEvent> mPendingEventPool = new SimplePool<>(20);
final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20);
private final InputMethodPrivilegedOperationsRegistry mPrivOpsRegistry =
new InputMethodPrivilegedOperationsRegistry();
// -----------------------------------------------------------
static final int MSG_DUMP = 1;
@ -772,11 +777,7 @@ public final class InputMethodManager {
*/
@Deprecated
public void showStatusIcon(IBinder imeToken, String packageName, int iconId) {
try {
mService.updateStatusIcon(imeToken, packageName, iconId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
mPrivOpsRegistry.get(imeToken).updateStatusIcon(packageName, iconId);
}
/**
@ -786,11 +787,7 @@ public final class InputMethodManager {
*/
@Deprecated
public void hideStatusIcon(IBinder imeToken) {
try {
mService.updateStatusIcon(imeToken, null, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
mPrivOpsRegistry.get(imeToken).updateStatusIcon(null, 0);
}
/** @hide */
@ -1831,11 +1828,18 @@ public final class InputMethodManager {
*/
@Deprecated
public void setInputMethod(IBinder token, String id) {
try {
mService.setInputMethod(token, id);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
if (token == null) {
// Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
// Thus we cannot always rely on mPrivOpsRegistry unfortunately.
// TODO(Bug 114488811): Consider deprecating null token rule.
try {
mService.setInputMethod(token, id);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
return;
}
mPrivOpsRegistry.get(token).setInputMethod(id);
}
/**
@ -1853,11 +1857,18 @@ public final class InputMethodManager {
*/
@Deprecated
public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
try {
mService.setInputMethodAndSubtype(token, id, subtype);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
if (token == null) {
// Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
// Thus we cannot always rely on mPrivOpsRegistry unfortunately.
// TODO(Bug 114488811): Consider deprecating null token rule.
try {
mService.setInputMethodAndSubtype(token, id, subtype);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
return;
}
mPrivOpsRegistry.get(token).setInputMethodAndSubtype(id, subtype);
}
/**
@ -1877,11 +1888,7 @@ public final class InputMethodManager {
*/
@Deprecated
public void hideSoftInputFromInputMethod(IBinder token, int flags) {
try {
mService.hideMySoftInput(token, flags);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
mPrivOpsRegistry.get(token).hideMySoftInput(flags);
}
/**
@ -1902,11 +1909,7 @@ public final class InputMethodManager {
*/
@Deprecated
public void showSoftInputFromInputMethod(IBinder token, int flags) {
try {
mService.showMySoftInput(token, flags);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
mPrivOpsRegistry.get(token).showMySoftInput(flags);
}
/**
@ -2285,11 +2288,17 @@ public final class InputMethodManager {
*/
@Deprecated
public boolean switchToLastInputMethod(IBinder imeToken) {
try {
return mService.switchToPreviousInputMethod(imeToken);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
if (imeToken == null) {
// Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
// Thus we cannot always rely on mPrivOpsRegistry unfortunately.
// TODO(Bug 114488811): Consider deprecating null token rule.
try {
return mService.switchToPreviousInputMethod(imeToken);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return mPrivOpsRegistry.get(imeToken).switchToPreviousInputMethod();
}
/**
@ -2307,11 +2316,17 @@ public final class InputMethodManager {
*/
@Deprecated
public boolean switchToNextInputMethod(IBinder imeToken, boolean onlyCurrentIme) {
try {
return mService.switchToNextInputMethod(imeToken, onlyCurrentIme);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
if (imeToken == null) {
// Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
// Thus we cannot always rely on mPrivOpsRegistry unfortunately.
// TODO(Bug 114488811): Consider deprecating null token rule.
try {
return mService.switchToNextInputMethod(imeToken, onlyCurrentIme);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return mPrivOpsRegistry.get(imeToken).switchToNextInputMethod(onlyCurrentIme);
}
/**
@ -2330,11 +2345,7 @@ public final class InputMethodManager {
*/
@Deprecated
public boolean shouldOfferSwitchingToNextInputMethod(IBinder imeToken) {
try {
return mService.shouldOfferSwitchingToNextInputMethod(imeToken);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
return mPrivOpsRegistry.get(imeToken).shouldOfferSwitchingToNextInputMethod();
}
/**
@ -2474,4 +2485,34 @@ public final class InputMethodManager {
sb.append(",temporaryDetach=" + view.isTemporarilyDetached());
return sb.toString();
}
/**
* Called by {@link InputMethodService} so that API calls to deprecated ones defined in this
* class can be forwarded to {@link InputMethodPrivilegedOperations}.
*
* <p>Note: this method does not hold strong references to {@code token} and {@code ops}. The
* registry entry will be automatically cleared after {@code token} is garbage collected.</p>
*
* @param token IME token that is associated with {@code ops}
* @param ops {@link InputMethodPrivilegedOperations} that is associated with {@code token}
* @hide
*/
public void registerInputMethodPrivOps(IBinder token, InputMethodPrivilegedOperations ops) {
mPrivOpsRegistry.put(token, ops);
}
/**
* Called from {@link InputMethodService#onDestroy()} to make sure that deprecated IME APIs
* defined in this class can no longer access to {@link InputMethodPrivilegedOperations}.
*
* <p>Note: Calling this method is optional, but at least gives more explict error message in
* logcat when IME developers are doing something unsupported (e.g. trying to call IME APIs
* after {@link InputMethodService#onDestroy()}).</p>
*
* @param token IME token to be removed.
* @hide
*/
public void unregisterInputMethodPrivOps(IBinder token) {
mPrivOpsRegistry.remove(token);
}
}

View File

@ -0,0 +1,116 @@
/*
* Copyright (C) 2018 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.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
import com.android.internal.annotations.GuardedBy;
import java.lang.ref.WeakReference;
import java.util.WeakHashMap;
/**
* A weak-reference-based mapper from IME token to {@link InputMethodPrivilegedOperations} that is
* used only to support deprecated IME APIs in {@link android.view.inputmethod.InputMethodManager}.
*/
public final class InputMethodPrivilegedOperationsRegistry {
private final Object mLock = new Object();
@GuardedBy("mLock")
private final WeakHashMap<IBinder, WeakReference<InputMethodPrivilegedOperations>>
mRegistry = new WeakHashMap<>();
@Nullable
private static InputMethodPrivilegedOperations sNop;
@NonNull
@AnyThread
private static InputMethodPrivilegedOperations getNopOps() {
// Strict thread-safety is not necessary because temporarily creating multiple nop instance
// is basically harmless
if (sNop == null) {
sNop = new InputMethodPrivilegedOperations();
}
return sNop;
}
/**
* Put a new entry to the registry.
*
* <p>Note: {@link InputMethodPrivilegedOperationsRegistry} does not hold strong reference to
* {@code token} and {@code ops}. The caller must be responsible for holding strong references
* to those objects, that is until {@link android.inputmethodservice.InputMethodService} is
* destroyed.</p>
*
* @param token IME token
* @param ops {@link InputMethodPrivilegedOperations} to be associated with the given IME token
*/
@AnyThread
public void put(IBinder token, InputMethodPrivilegedOperations ops) {
synchronized (mLock) {
final WeakReference<InputMethodPrivilegedOperations> previousOps =
mRegistry.put(token, new WeakReference<>(ops));
if (previousOps != null) {
throw new IllegalStateException(previousOps.get() + " is already registered for "
+ " this token=" + token + " newOps=" + ops);
}
}
}
/**
* Get a {@link InputMethodPrivilegedOperations} from the given IME token. If it is not
* available, return a fake instance that does nothing except for showing warning messages.
*
* @param token IME token
* @return real {@link InputMethodPrivilegedOperations} object if {@code token} is still valid.
* Otherwise a fake instance of {@link InputMethodPrivilegedOperations} hat does nothing
* except for showing warning messages
*/
@NonNull
@AnyThread
public InputMethodPrivilegedOperations get(IBinder token) {
synchronized (mLock) {
final WeakReference<InputMethodPrivilegedOperations> wrapperRef = mRegistry.get(token);
if (wrapperRef == null) {
return getNopOps();
}
final InputMethodPrivilegedOperations wrapper = wrapperRef.get();
if (wrapper == null) {
return getNopOps();
}
return wrapper;
}
}
/**
* Explicitly removes the specified entry.
*
* <p>Note: Calling this method is optional. In general, {@link WeakHashMap} and
* {@link WeakReference} guarantee that the entry will be removed after specified binder proxies
* are garbage collected.</p>
*
* @param token IME token to be removed.
*/
@AnyThread
public void remove(IBinder token) {
synchronized (mLock) {
mRegistry.remove(token);
}
}
}

View File

@ -63,18 +63,18 @@ interface IInputMethodManager {
int auxiliarySubtypeMode);
void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId);
boolean isInputMethodPickerShownForTest();
// TODO(Bug 114488811): this can be removed once we deprecate special null token rule.
void setInputMethod(in IBinder token, String id);
// TODO(Bug 114488811): this can be removed once we deprecate special null token rule.
void setInputMethodAndSubtype(in IBinder token, String id, in InputMethodSubtype subtype);
void hideMySoftInput(in IBinder token, int flags);
void showMySoftInput(in IBinder token, int flags);
void updateStatusIcon(in IBinder token, String packageName, int iconId);
void registerSuggestionSpansForNotification(in SuggestionSpan[] spans);
boolean notifySuggestionPicked(in SuggestionSpan span, String originalString, int index);
InputMethodSubtype getCurrentInputMethodSubtype();
boolean setCurrentInputMethodSubtype(in InputMethodSubtype subtype);
// TODO(Bug 114488811): this can be removed once we deprecate special null token rule.
boolean switchToPreviousInputMethod(in IBinder token);
// TODO(Bug 114488811): this can be removed once we deprecate special null token rule.
boolean switchToNextInputMethod(in IBinder token, boolean onlyCurrentIme);
boolean shouldOfferSwitchingToNextInputMethod(in IBinder token);
void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
// This is kept due to @UnsupportedAppUsage.
// TODO(Bug 113914148): Consider removing this.

View File

@ -2113,8 +2113,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
@Override
public void updateStatusIcon(IBinder token, String packageName, int iconId) {
@BinderThread
private void updateStatusIcon(@NonNull IBinder token, String packageName, int iconId) {
synchronized (mMethodMap) {
if (!calledWithValidToken(token)) {
return;
@ -3060,8 +3060,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
@Override
public boolean shouldOfferSwitchingToNextInputMethod(IBinder token) {
@BinderThread
private boolean shouldOfferSwitchingToNextInputMethod(@NonNull IBinder token) {
if (!calledFromValidUser()) {
return false;
}
@ -3220,8 +3220,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
@Override
public void hideMySoftInput(IBinder token, int flags) {
@BinderThread
private void hideMySoftInput(@NonNull IBinder token, int flags) {
if (!calledFromValidUser()) {
return;
}
@ -3238,8 +3238,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
@Override
public void showMySoftInput(IBinder token, int flags) {
@BinderThread
private void showMySoftInput(@NonNull IBinder token, int flags) {
if (!calledFromValidUser()) {
return;
}