| /* |
| * 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.Nullable; |
| import android.os.IBinder; |
| import android.os.RemoteException; |
| import android.util.Log; |
| import android.view.InputChannel; |
| |
| import com.android.internal.annotations.GuardedBy; |
| import com.android.internal.view.IInputMethodSession; |
| |
| /** |
| * A utility class to take care of boilerplate code around IPCs. |
| * |
| * <p>Note: All public methods are designed to be thread-safe.</p> |
| */ |
| public class MultiClientInputMethodPrivilegedOperations { |
| private static final String TAG = "MultiClientInputMethodPrivilegedOperations"; |
| |
| private static final class OpsHolder { |
| @Nullable |
| @GuardedBy("this") |
| private IMultiClientInputMethodPrivilegedOperations mPrivOps; |
| |
| /** |
| * Sets {@link IMultiClientInputMethodPrivilegedOperations}. |
| * |
| * <p>This method can be called only once.</p> |
| * |
| * @param privOps Binder interface to be set. |
| */ |
| @AnyThread |
| public synchronized void set(IMultiClientInputMethodPrivilegedOperations privOps) { |
| if (mPrivOps != null) { |
| throw new IllegalStateException( |
| "IMultiClientInputMethodPrivilegedOperations must be set at most once." |
| + " privOps=" + privOps); |
| } |
| mPrivOps = privOps; |
| } |
| |
| /** |
| * A simplified version of {@link android.os.Debug#getCaller()}. |
| * |
| * @return method name of the caller. |
| */ |
| @AnyThread |
| private static String getCallerMethodName() { |
| final StackTraceElement[] callStack = Thread.currentThread().getStackTrace(); |
| if (callStack.length <= 4) { |
| return "<bottom of call stack>"; |
| } |
| return callStack[4].getMethodName(); |
| } |
| |
| @AnyThread |
| public synchronized void dispose() { |
| mPrivOps = null; |
| } |
| |
| @AnyThread |
| @Nullable |
| public synchronized IMultiClientInputMethodPrivilegedOperations getAndWarnIfNull() { |
| if (mPrivOps == null) { |
| Log.e(TAG, getCallerMethodName() + " is ignored." |
| + " Call it within attachToken() and InputMethodService.onDestroy()"); |
| } |
| return mPrivOps; |
| } |
| } |
| private final OpsHolder mOps = new OpsHolder(); |
| |
| /** |
| * Sets {@link IMultiClientInputMethodPrivilegedOperations}. |
| * |
| * <p>This method can be called only once.</p> |
| * |
| * @param privOps Binder interface to be set. |
| */ |
| @AnyThread |
| public void set(IMultiClientInputMethodPrivilegedOperations privOps) { |
| mOps.set(privOps); |
| } |
| |
| /** |
| * Disposes internal Binder proxy so that the real Binder object can be garbage collected. |
| */ |
| @AnyThread |
| public void dispose() { |
| mOps.dispose(); |
| } |
| |
| /** |
| |
| * Calls {@link IMultiClientInputMethodPrivilegedOperations#createInputMethodWindowToken(int)}. |
| * |
| * @param displayId display ID on which the IME window will be shown. |
| * @return Window token to be specified to the IME window. |
| */ |
| @AnyThread |
| public IBinder createInputMethodWindowToken(int displayId) { |
| IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); |
| if (ops == null) { |
| return null; |
| } |
| try { |
| return ops.createInputMethodWindowToken(displayId); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Calls {@link |
| * IMultiClientInputMethodPrivilegedOperations#deleteInputMethodWindowToken(IBinder)}. |
| * |
| * @param token Window token that is to be deleted. |
| */ |
| @AnyThread |
| public void deleteInputMethodWindowToken(IBinder token) { |
| IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); |
| if (ops == null) { |
| return; |
| } |
| try { |
| ops.deleteInputMethodWindowToken(token); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Calls {@link IMultiClientInputMethodPrivilegedOperations#acceptClient(int, |
| * IInputMethodSession, IMultiClientInputMethodSession, InputChannel)}. |
| * |
| * @param clientId client ID to be accepted. |
| * @param session {@link IInputMethodSession} that is also used for traditional IME protocol. |
| * @param multiClientSession {@link IMultiClientInputMethodSession} that defines new callbacks |
| * for multi-client scenarios. |
| * @param writeChannel {@link InputChannel} that is also used for traditional IME protocol. |
| */ |
| @AnyThread |
| public void acceptClient(int clientId, IInputMethodSession session, |
| IMultiClientInputMethodSession multiClientSession, InputChannel writeChannel) { |
| final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); |
| if (ops == null) { |
| return; |
| } |
| try { |
| ops.acceptClient(clientId, session, multiClientSession, writeChannel); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Calls {@link IMultiClientInputMethodPrivilegedOperations#reportImeWindowTarget(int, int, |
| * IBinder)}. |
| * |
| * @param clientId client ID about which new IME target window is reported. |
| * @param targetWindowHandle integer handle of the target window. |
| * @param imeWindowToken {@link IBinder} window token of the IME window. |
| */ |
| @AnyThread |
| public void reportImeWindowTarget(int clientId, int targetWindowHandle, |
| IBinder imeWindowToken) { |
| final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); |
| if (ops == null) { |
| return; |
| } |
| try { |
| ops.reportImeWindowTarget(clientId, targetWindowHandle, imeWindowToken); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Calls {@link IMultiClientInputMethodPrivilegedOperations#isUidAllowedOnDisplay(int, int)}. |
| * |
| * @param displayId display ID to be verified. |
| * @param uid UID to be verified. |
| * @return {@code true} when {@code uid} is allowed to access to {@code displayId}. |
| */ |
| @AnyThread |
| public boolean isUidAllowedOnDisplay(int displayId, int uid) { |
| final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); |
| if (ops == null) { |
| return false; |
| } |
| try { |
| return ops.isUidAllowedOnDisplay(displayId, uid); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| } |