Merge "Allow IMM to forward API calls to IMS"
This commit is contained in:
commit
d172e22a6a
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user