Loading services/core/java/com/android/server/inputmethod/ClientController.java +16 −1 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.inputmethod; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.PackageManagerInternal; import android.os.IBinder; import android.os.RemoteException; Loading @@ -29,6 +30,7 @@ import com.android.internal.inputmethod.IRemoteInputConnection; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; /** * Store and manage {@link InputMethodManagerService} clients. This class was designed to be a Loading Loading @@ -62,7 +64,7 @@ final class ClientController { // TODO(b/314150112): Make this field private when breaking the cycle with IMMS. @GuardedBy("ImfLock.class") final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>(); private final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>(); @GuardedBy("ImfLock.class") private final List<ClientControllerCallback> mCallbacks = new ArrayList<>(); Loading Loading @@ -144,6 +146,19 @@ final class ClientController { mCallbacks.add(callback); } @GuardedBy("ImfLock.class") @Nullable ClientState getClient(IBinder binder) { return mClients.get(binder); } @GuardedBy("ImfLock.class") void forAllClients(Consumer<ClientState> consumer) { for (int i = 0; i < mClients.size(); i++) { consumer.accept(mClients.valueAt(i)); } } @GuardedBy("ImfLock.class") boolean verifyClientAndPackageMatch( @NonNull IInputMethodClient client, @NonNull String packageName) { Loading services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +86 −70 Original line number Diff line number Diff line Loading @@ -205,6 +205,7 @@ import java.util.WeakHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.IntConsumer; /** Loading Loading @@ -270,7 +271,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @NonNull private final String[] mNonPreemptibleInputMethods; // TODO(b/314150112): Move this to ClientController. @UserIdInt private int mLastSwitchUserId; Loading Loading @@ -1819,10 +1819,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } mLastSwitchUserId = newUserId; if (mIsInteractive && clientToBeReset != null) { final ClientState cs = mClientController.mClients.get(clientToBeReset.asBinder()); final ClientState cs = mClientController.getClient(clientToBeReset.asBinder()); if (cs == null) { // The client is already gone. return; Loading Loading @@ -2165,8 +2163,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub /** * Hide the IME if the removed user is the current user. */ @GuardedBy("ImfLock.class") private void onClientRemoved(ClientState client) { synchronized (ImfLock.class) { clearClientSessionLocked(client); clearClientSessionForAccessibilityLocked(client); if (mCurClient == client) { Loading @@ -2184,7 +2182,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } mBoundToAccessibility = false; mCurClient = null; } if (mCurFocusedWindowClient == client) { mCurFocusedWindowClient = null; mCurFocusedWindowEditorInfo = null; Loading @@ -2192,7 +2189,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } } // TODO(b/314150112): Move this to ClientController. @GuardedBy("ImfLock.class") void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) { if (mCurClient != null) { Loading Loading @@ -2883,11 +2879,16 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @GuardedBy("ImfLock.class") void clearClientSessionsLocked() { if (getCurMethodLocked() != null) { final int numClients = mClientController.mClients.size(); for (int i = 0; i < numClients; ++i) { clearClientSessionLocked(mClientController.mClients.valueAt(i)); clearClientSessionForAccessibilityLocked(mClientController.mClients.valueAt(i)); // TODO(b/322816970): Replace this with lambda. mClientController.forAllClients(new Consumer<ClientState>() { @GuardedBy("ImfLock.class") @Override public void accept(ClientState c) { clearClientSessionLocked(c); clearClientSessionForAccessibilityLocked(c); } }); finishSessionLocked(mEnabledSession); for (int i = 0; i < mEnabledAccessibilitySessions.size(); i++) { Loading Loading @@ -3732,9 +3733,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub return InputBindResult.INVALID_USER; } final ClientState cs = mClientController.mClients.get(client.asBinder()); final ClientState cs = mClientController.getClient(client.asBinder()); if (cs == null) { throw new IllegalArgumentException("unknown client " + client.asBinder()); throw new IllegalArgumentException("Unknown client " + client.asBinder()); } final int imeClientFocus = mWindowManagerInternal.hasInputMethodClientFocus( Loading Loading @@ -3906,8 +3907,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // 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. final ClientState cs = mClientController.mClients.get(client.asBinder()); final ClientState cs = mClientController.getClient(client.asBinder()); if (cs == null) { ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_SERVER_CLIENT_KNOWN); throw new IllegalArgumentException("unknown client " + client.asBinder()); Loading Loading @@ -4518,16 +4518,17 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @Override public void startImeTrace() { super.startImeTrace_enforcePermission(); ImeTracing.getInstance().startTrace(null /* printwriter */); ArrayMap<IBinder, ClientState> clients; synchronized (ImfLock.class) { clients = new ArrayMap<>(mClientController.mClients); } for (ClientState state : clients.values()) { if (state != null) { state.mClient.setImeTraceEnabled(true /* enabled */); // TODO(b/322816970): Replace this with lambda. mClientController.forAllClients(new Consumer<ClientState>() { @GuardedBy("ImfLock.class") @Override public void accept(ClientState c) { c.mClient.setImeTraceEnabled(true /* enabled */); } }); } } Loading @@ -4538,14 +4539,16 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub super.stopImeTrace_enforcePermission(); ImeTracing.getInstance().stopTrace(null /* printwriter */); ArrayMap<IBinder, ClientState> clients; synchronized (ImfLock.class) { clients = new ArrayMap<>(mClientController.mClients); } for (ClientState state : clients.values()) { if (state != null) { state.mClient.setImeTraceEnabled(false /* enabled */); // TODO(b/322816970): Replace this with lambda. mClientController.forAllClients(new Consumer<ClientState>() { @GuardedBy("ImfLock.class") @Override public void accept(ClientState c) { c.mClient.setImeTraceEnabled(false /* enabled */); } }); } } Loading Loading @@ -5779,11 +5782,15 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // We only have sessions when we bound to an input method. Remove this session // from all clients. if (getCurMethodLocked() != null) { final int numClients = mClientController.mClients.size(); for (int i = 0; i < numClients; ++i) { clearClientSessionForAccessibilityLocked( mClientController.mClients.valueAt(i), accessibilityConnectionId); // TODO(b/322816970): Replace this with lambda. mClientController.forAllClients(new Consumer<ClientState>() { @GuardedBy("ImfLock.class") @Override public void accept(ClientState c) { clearClientSessionForAccessibilityLocked(c, accessibilityConnectionId); } }); AccessibilitySessionState session = mEnabledAccessibilitySessions.get( accessibilityConnectionId); if (session != null) { Loading Loading @@ -5967,19 +5974,26 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub p.println(" InputMethod #" + i + ":"); info.dump(p, " "); } // Dump ClientController#mClients p.println(" ClientStates:"); // TODO(b/314150112): move client related dump info to ClientController#dump final int numClients = mClientController.mClients.size(); for (int i = 0; i < numClients; ++i) { final ClientState ci = mClientController.mClients.valueAt(i); p.println(" " + ci + ":"); p.println(" client=" + ci.mClient); p.println(" fallbackInputConnection=" + ci.mFallbackInputConnection); p.println(" sessionRequested=" + ci.mSessionRequested); p.println(" sessionRequestedForAccessibility=" + ci.mSessionRequestedForAccessibility); p.println(" curSession=" + ci.mCurSession); // TODO(b/322816970): Replace this with lambda. mClientController.forAllClients(new Consumer<ClientState>() { @GuardedBy("ImfLock.class") @Override public void accept(ClientState c) { p.println(" " + c + ":"); p.println(" client=" + c.mClient); p.println(" fallbackInputConnection=" + c.mFallbackInputConnection); p.println(" sessionRequested=" + c.mSessionRequested); p.println( " sessionRequestedForAccessibility=" + c.mSessionRequestedForAccessibility); p.println(" curSession=" + c.mCurSession); } }); p.println(" mCurMethodId=" + getSelectedMethodIdLocked()); client = mCurClient; p.println(" mCurClient=" + client + " mCurSeq=" + getSequenceNumberLocked()); Loading Loading @@ -6583,14 +6597,16 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } } boolean isImeTraceEnabled = ImeTracing.getInstance().isEnabled(); ArrayMap<IBinder, ClientState> clients; synchronized (ImfLock.class) { clients = new ArrayMap<>(mClientController.mClients); } for (ClientState state : clients.values()) { if (state != null) { state.mClient.setImeTraceEnabled(isImeTraceEnabled); // TODO(b/322816970): Replace this with lambda. mClientController.forAllClients(new Consumer<ClientState>() { @GuardedBy("ImfLock.class") @Override public void accept(ClientState c) { c.mClient.setImeTraceEnabled(isImeTraceEnabled); } }); } return ShellCommandResult.SUCCESS; } Loading services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java +2 −2 Original line number Diff line number Diff line Loading @@ -116,7 +116,7 @@ public final class ClientControllerTest { ANY_CALLER_PID); verify(invoker.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0)); assertThat(mController.mClients).containsEntry(invoker.asBinder(), added); assertThat(mController.getClient(invoker.asBinder())).isSameInstanceAs(added); } } Loading @@ -133,7 +133,7 @@ public final class ClientControllerTest { var invoker = IInputMethodClientInvoker.create(mClient, mHandler); added = mController.addClient(invoker, mConnection, ANY_DISPLAY_ID, ANY_CALLER_UID, ANY_CALLER_PID); assertThat(mController.mClients).containsEntry(invoker.asBinder(), added); assertThat(mController.getClient(invoker.asBinder())).isSameInstanceAs(added); assertThat(mController.removeClient(mClient)).isTrue(); } Loading Loading
services/core/java/com/android/server/inputmethod/ClientController.java +16 −1 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server.inputmethod; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.PackageManagerInternal; import android.os.IBinder; import android.os.RemoteException; Loading @@ -29,6 +30,7 @@ import com.android.internal.inputmethod.IRemoteInputConnection; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; /** * Store and manage {@link InputMethodManagerService} clients. This class was designed to be a Loading Loading @@ -62,7 +64,7 @@ final class ClientController { // TODO(b/314150112): Make this field private when breaking the cycle with IMMS. @GuardedBy("ImfLock.class") final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>(); private final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>(); @GuardedBy("ImfLock.class") private final List<ClientControllerCallback> mCallbacks = new ArrayList<>(); Loading Loading @@ -144,6 +146,19 @@ final class ClientController { mCallbacks.add(callback); } @GuardedBy("ImfLock.class") @Nullable ClientState getClient(IBinder binder) { return mClients.get(binder); } @GuardedBy("ImfLock.class") void forAllClients(Consumer<ClientState> consumer) { for (int i = 0; i < mClients.size(); i++) { consumer.accept(mClients.valueAt(i)); } } @GuardedBy("ImfLock.class") boolean verifyClientAndPackageMatch( @NonNull IInputMethodClient client, @NonNull String packageName) { Loading
services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +86 −70 Original line number Diff line number Diff line Loading @@ -205,6 +205,7 @@ import java.util.WeakHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.IntConsumer; /** Loading Loading @@ -270,7 +271,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @NonNull private final String[] mNonPreemptibleInputMethods; // TODO(b/314150112): Move this to ClientController. @UserIdInt private int mLastSwitchUserId; Loading Loading @@ -1819,10 +1819,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } mLastSwitchUserId = newUserId; if (mIsInteractive && clientToBeReset != null) { final ClientState cs = mClientController.mClients.get(clientToBeReset.asBinder()); final ClientState cs = mClientController.getClient(clientToBeReset.asBinder()); if (cs == null) { // The client is already gone. return; Loading Loading @@ -2165,8 +2163,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub /** * Hide the IME if the removed user is the current user. */ @GuardedBy("ImfLock.class") private void onClientRemoved(ClientState client) { synchronized (ImfLock.class) { clearClientSessionLocked(client); clearClientSessionForAccessibilityLocked(client); if (mCurClient == client) { Loading @@ -2184,7 +2182,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } mBoundToAccessibility = false; mCurClient = null; } if (mCurFocusedWindowClient == client) { mCurFocusedWindowClient = null; mCurFocusedWindowEditorInfo = null; Loading @@ -2192,7 +2189,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } } // TODO(b/314150112): Move this to ClientController. @GuardedBy("ImfLock.class") void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) { if (mCurClient != null) { Loading Loading @@ -2883,11 +2879,16 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @GuardedBy("ImfLock.class") void clearClientSessionsLocked() { if (getCurMethodLocked() != null) { final int numClients = mClientController.mClients.size(); for (int i = 0; i < numClients; ++i) { clearClientSessionLocked(mClientController.mClients.valueAt(i)); clearClientSessionForAccessibilityLocked(mClientController.mClients.valueAt(i)); // TODO(b/322816970): Replace this with lambda. mClientController.forAllClients(new Consumer<ClientState>() { @GuardedBy("ImfLock.class") @Override public void accept(ClientState c) { clearClientSessionLocked(c); clearClientSessionForAccessibilityLocked(c); } }); finishSessionLocked(mEnabledSession); for (int i = 0; i < mEnabledAccessibilitySessions.size(); i++) { Loading Loading @@ -3732,9 +3733,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub return InputBindResult.INVALID_USER; } final ClientState cs = mClientController.mClients.get(client.asBinder()); final ClientState cs = mClientController.getClient(client.asBinder()); if (cs == null) { throw new IllegalArgumentException("unknown client " + client.asBinder()); throw new IllegalArgumentException("Unknown client " + client.asBinder()); } final int imeClientFocus = mWindowManagerInternal.hasInputMethodClientFocus( Loading Loading @@ -3906,8 +3907,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // 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. final ClientState cs = mClientController.mClients.get(client.asBinder()); final ClientState cs = mClientController.getClient(client.asBinder()); if (cs == null) { ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_SERVER_CLIENT_KNOWN); throw new IllegalArgumentException("unknown client " + client.asBinder()); Loading Loading @@ -4518,16 +4518,17 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @Override public void startImeTrace() { super.startImeTrace_enforcePermission(); ImeTracing.getInstance().startTrace(null /* printwriter */); ArrayMap<IBinder, ClientState> clients; synchronized (ImfLock.class) { clients = new ArrayMap<>(mClientController.mClients); } for (ClientState state : clients.values()) { if (state != null) { state.mClient.setImeTraceEnabled(true /* enabled */); // TODO(b/322816970): Replace this with lambda. mClientController.forAllClients(new Consumer<ClientState>() { @GuardedBy("ImfLock.class") @Override public void accept(ClientState c) { c.mClient.setImeTraceEnabled(true /* enabled */); } }); } } Loading @@ -4538,14 +4539,16 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub super.stopImeTrace_enforcePermission(); ImeTracing.getInstance().stopTrace(null /* printwriter */); ArrayMap<IBinder, ClientState> clients; synchronized (ImfLock.class) { clients = new ArrayMap<>(mClientController.mClients); } for (ClientState state : clients.values()) { if (state != null) { state.mClient.setImeTraceEnabled(false /* enabled */); // TODO(b/322816970): Replace this with lambda. mClientController.forAllClients(new Consumer<ClientState>() { @GuardedBy("ImfLock.class") @Override public void accept(ClientState c) { c.mClient.setImeTraceEnabled(false /* enabled */); } }); } } Loading Loading @@ -5779,11 +5782,15 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // We only have sessions when we bound to an input method. Remove this session // from all clients. if (getCurMethodLocked() != null) { final int numClients = mClientController.mClients.size(); for (int i = 0; i < numClients; ++i) { clearClientSessionForAccessibilityLocked( mClientController.mClients.valueAt(i), accessibilityConnectionId); // TODO(b/322816970): Replace this with lambda. mClientController.forAllClients(new Consumer<ClientState>() { @GuardedBy("ImfLock.class") @Override public void accept(ClientState c) { clearClientSessionForAccessibilityLocked(c, accessibilityConnectionId); } }); AccessibilitySessionState session = mEnabledAccessibilitySessions.get( accessibilityConnectionId); if (session != null) { Loading Loading @@ -5967,19 +5974,26 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub p.println(" InputMethod #" + i + ":"); info.dump(p, " "); } // Dump ClientController#mClients p.println(" ClientStates:"); // TODO(b/314150112): move client related dump info to ClientController#dump final int numClients = mClientController.mClients.size(); for (int i = 0; i < numClients; ++i) { final ClientState ci = mClientController.mClients.valueAt(i); p.println(" " + ci + ":"); p.println(" client=" + ci.mClient); p.println(" fallbackInputConnection=" + ci.mFallbackInputConnection); p.println(" sessionRequested=" + ci.mSessionRequested); p.println(" sessionRequestedForAccessibility=" + ci.mSessionRequestedForAccessibility); p.println(" curSession=" + ci.mCurSession); // TODO(b/322816970): Replace this with lambda. mClientController.forAllClients(new Consumer<ClientState>() { @GuardedBy("ImfLock.class") @Override public void accept(ClientState c) { p.println(" " + c + ":"); p.println(" client=" + c.mClient); p.println(" fallbackInputConnection=" + c.mFallbackInputConnection); p.println(" sessionRequested=" + c.mSessionRequested); p.println( " sessionRequestedForAccessibility=" + c.mSessionRequestedForAccessibility); p.println(" curSession=" + c.mCurSession); } }); p.println(" mCurMethodId=" + getSelectedMethodIdLocked()); client = mCurClient; p.println(" mCurClient=" + client + " mCurSeq=" + getSequenceNumberLocked()); Loading Loading @@ -6583,14 +6597,16 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } } boolean isImeTraceEnabled = ImeTracing.getInstance().isEnabled(); ArrayMap<IBinder, ClientState> clients; synchronized (ImfLock.class) { clients = new ArrayMap<>(mClientController.mClients); } for (ClientState state : clients.values()) { if (state != null) { state.mClient.setImeTraceEnabled(isImeTraceEnabled); // TODO(b/322816970): Replace this with lambda. mClientController.forAllClients(new Consumer<ClientState>() { @GuardedBy("ImfLock.class") @Override public void accept(ClientState c) { c.mClient.setImeTraceEnabled(isImeTraceEnabled); } }); } return ShellCommandResult.SUCCESS; } Loading
services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java +2 −2 Original line number Diff line number Diff line Loading @@ -116,7 +116,7 @@ public final class ClientControllerTest { ANY_CALLER_PID); verify(invoker.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0)); assertThat(mController.mClients).containsEntry(invoker.asBinder(), added); assertThat(mController.getClient(invoker.asBinder())).isSameInstanceAs(added); } } Loading @@ -133,7 +133,7 @@ public final class ClientControllerTest { var invoker = IInputMethodClientInvoker.create(mClient, mHandler); added = mController.addClient(invoker, mConnection, ANY_DISPLAY_ID, ANY_CALLER_UID, ANY_CALLER_PID); assertThat(mController.mClients).containsEntry(invoker.asBinder(), added); assertThat(mController.getClient(invoker.asBinder())).isSameInstanceAs(added); assertThat(mController.removeClient(mClient)).isTrue(); } Loading