• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.inputmethod;
18 
19 import android.annotation.AnyThread;
20 import android.annotation.Nullable;
21 import android.os.IBinder;
22 import android.os.RemoteException;
23 import android.util.Log;
24 import android.view.InputChannel;
25 
26 import com.android.internal.annotations.GuardedBy;
27 import com.android.internal.view.IInputMethodSession;
28 
29 /**
30  * A utility class to take care of boilerplate code around IPCs.
31  *
32  * <p>Note: All public methods are designed to be thread-safe.</p>
33  */
34 public class MultiClientInputMethodPrivilegedOperations {
35     private static final String TAG = "MultiClientInputMethodPrivilegedOperations";
36 
37     private static final class OpsHolder {
38         @Nullable
39         @GuardedBy("this")
40         private IMultiClientInputMethodPrivilegedOperations mPrivOps;
41 
42         /**
43          * Sets {@link IMultiClientInputMethodPrivilegedOperations}.
44          *
45          * <p>This method can be called only once.</p>
46          *
47          * @param privOps Binder interface to be set.
48          */
49         @AnyThread
set(IMultiClientInputMethodPrivilegedOperations privOps)50         public synchronized void set(IMultiClientInputMethodPrivilegedOperations privOps) {
51             if (mPrivOps != null) {
52                 throw new IllegalStateException(
53                         "IMultiClientInputMethodPrivilegedOperations must be set at most once."
54                                 + " privOps=" + privOps);
55             }
56             mPrivOps = privOps;
57         }
58 
59         /**
60          * A simplified version of {@link android.os.Debug#getCaller()}.
61          *
62          * @return method name of the caller.
63          */
64         @AnyThread
getCallerMethodName()65         private static String getCallerMethodName() {
66             final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
67             if (callStack.length <= 4) {
68                 return "<bottom of call stack>";
69             }
70             return callStack[4].getMethodName();
71         }
72 
73         @AnyThread
dispose()74         public synchronized void dispose() {
75             mPrivOps = null;
76         }
77 
78         @AnyThread
79         @Nullable
getAndWarnIfNull()80         public synchronized IMultiClientInputMethodPrivilegedOperations getAndWarnIfNull() {
81             if (mPrivOps == null) {
82                 Log.e(TAG, getCallerMethodName() + " is ignored."
83                         + " Call it within attachToken() and InputMethodService.onDestroy()");
84             }
85             return mPrivOps;
86         }
87     }
88     private final OpsHolder mOps = new OpsHolder();
89 
90     /**
91      * Sets {@link IMultiClientInputMethodPrivilegedOperations}.
92      *
93      * <p>This method can be called only once.</p>
94      *
95      * @param privOps Binder interface to be set.
96      */
97     @AnyThread
set(IMultiClientInputMethodPrivilegedOperations privOps)98     public void set(IMultiClientInputMethodPrivilegedOperations privOps) {
99         mOps.set(privOps);
100     }
101 
102     /**
103      * Disposes internal Binder proxy so that the real Binder object can be garbage collected.
104      */
105     @AnyThread
dispose()106     public void dispose() {
107         mOps.dispose();
108     }
109 
110     /**
111 
112      * Calls {@link IMultiClientInputMethodPrivilegedOperations#createInputMethodWindowToken(int)}.
113      *
114      * @param displayId display ID on which the IME window will be shown.
115      * @return Window token to be specified to the IME window.
116      */
117     @AnyThread
createInputMethodWindowToken(int displayId)118     public IBinder createInputMethodWindowToken(int displayId) {
119         IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
120         if (ops == null) {
121             return null;
122         }
123         try {
124             return ops.createInputMethodWindowToken(displayId);
125         } catch (RemoteException e) {
126             throw e.rethrowFromSystemServer();
127         }
128     }
129 
130     /**
131      * Calls {@link
132      * IMultiClientInputMethodPrivilegedOperations#deleteInputMethodWindowToken(IBinder)}.
133      *
134      * @param token Window token that is to be deleted.
135      */
136     @AnyThread
deleteInputMethodWindowToken(IBinder token)137     public void deleteInputMethodWindowToken(IBinder token) {
138         IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
139         if (ops == null) {
140             return;
141         }
142         try {
143             ops.deleteInputMethodWindowToken(token);
144         } catch (RemoteException e) {
145             throw e.rethrowFromSystemServer();
146         }
147     }
148 
149     /**
150      * Calls {@link IMultiClientInputMethodPrivilegedOperations#acceptClient(int,
151      * IInputMethodSession, IMultiClientInputMethodSession, InputChannel)}.
152      *
153      * @param clientId client ID to be accepted.
154      * @param session {@link IInputMethodSession} that is also used for traditional IME protocol.
155      * @param multiClientSession {@link IMultiClientInputMethodSession} that defines new callbacks
156      *                           for multi-client scenarios.
157      * @param writeChannel {@link InputChannel} that is also used for traditional IME protocol.
158      */
159     @AnyThread
acceptClient(int clientId, IInputMethodSession session, IMultiClientInputMethodSession multiClientSession, InputChannel writeChannel)160     public void acceptClient(int clientId, IInputMethodSession session,
161             IMultiClientInputMethodSession multiClientSession, InputChannel writeChannel) {
162         final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
163         if (ops == null) {
164             return;
165         }
166         try {
167             ops.acceptClient(clientId, session, multiClientSession, writeChannel);
168         } catch (RemoteException e) {
169             throw e.rethrowFromSystemServer();
170         }
171     }
172 
173     /**
174      * Calls {@link IMultiClientInputMethodPrivilegedOperations#reportImeWindowTarget(int, int,
175      * IBinder)}.
176      *
177      * @param clientId client ID about which new IME target window is reported.
178      * @param targetWindowHandle integer handle of the target window.
179      * @param imeWindowToken {@link IBinder} window token of the IME window.
180      */
181     @AnyThread
reportImeWindowTarget(int clientId, int targetWindowHandle, IBinder imeWindowToken)182     public void reportImeWindowTarget(int clientId, int targetWindowHandle,
183             IBinder imeWindowToken) {
184         final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
185         if (ops == null) {
186             return;
187         }
188         try {
189             ops.reportImeWindowTarget(clientId, targetWindowHandle, imeWindowToken);
190         } catch (RemoteException e) {
191             throw e.rethrowFromSystemServer();
192         }
193     }
194 
195     /**
196      * Calls {@link IMultiClientInputMethodPrivilegedOperations#isUidAllowedOnDisplay(int, int)}.
197      *
198      * @param displayId display ID to be verified.
199      * @param uid UID to be verified.
200      * @return {@code true} when {@code uid} is allowed to access to {@code displayId}.
201      */
202     @AnyThread
isUidAllowedOnDisplay(int displayId, int uid)203     public boolean isUidAllowedOnDisplay(int displayId, int uid) {
204         final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
205         if (ops == null) {
206             return false;
207         }
208         try {
209             return ops.isUidAllowedOnDisplay(displayId, uid);
210         } catch (RemoteException e) {
211             throw e.rethrowFromSystemServer();
212         }
213     }
214 
215     /**
216      * Calls {@link IMultiClientInputMethodPrivilegedOperations#setActive(int, boolean)}.
217      * @param clientId client ID to be set active/inactive
218      * @param active {@code true} set set active.
219      */
220     @AnyThread
setActive(int clientId, boolean active)221     public void setActive(int clientId, boolean active) {
222         final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
223         if (ops == null) {
224             return;
225         }
226         try {
227             ops.setActive(clientId, active);
228         } catch (RemoteException e) {
229             throw e.rethrowFromSystemServer();
230         }
231     }
232 }
233