• 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 android.inputmethodservice;
18 
19 import static java.lang.annotation.RetentionPolicy.SOURCE;
20 
21 import android.annotation.IntDef;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.os.IBinder;
25 import android.os.Looper;
26 import android.util.Log;
27 import android.view.InputChannel;
28 import android.view.KeyEvent;
29 
30 import com.android.internal.annotations.GuardedBy;
31 import com.android.internal.inputmethod.IMultiClientInputMethod;
32 import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations;
33 import com.android.internal.inputmethod.MultiClientInputMethodPrivilegedOperations;
34 
35 import java.lang.annotation.Retention;
36 import java.lang.ref.WeakReference;
37 
38 final class MultiClientInputMethodServiceDelegateImpl {
39     private static final String TAG = "MultiClientInputMethodServiceDelegateImpl";
40 
41     private final Object mLock = new Object();
42 
43     @Retention(SOURCE)
44     @IntDef({InitializationPhase.INSTANTIATED,
45             InitializationPhase.ON_BIND_CALLED,
46             InitializationPhase.INITIALIZE_CALLED,
47             InitializationPhase.ON_UNBIND_CALLED,
48             InitializationPhase.ON_DESTROY_CALLED})
49     private @interface InitializationPhase {
50         int INSTANTIATED = 1;
51         int ON_BIND_CALLED = 2;
52         int INITIALIZE_CALLED = 3;
53         int ON_UNBIND_CALLED  = 4;
54         int ON_DESTROY_CALLED = 5;
55     }
56 
57     @GuardedBy("mLock")
58     @InitializationPhase
59     private int mInitializationPhase;
60 
61     private final MultiClientInputMethodPrivilegedOperations mPrivOps =
62             new MultiClientInputMethodPrivilegedOperations();
63 
64     private final MultiClientInputMethodServiceDelegate.ServiceCallback mServiceCallback;
65 
66     private final Context mContext;
67 
MultiClientInputMethodServiceDelegateImpl(Context context, MultiClientInputMethodServiceDelegate.ServiceCallback serviceCallback)68     MultiClientInputMethodServiceDelegateImpl(Context context,
69             MultiClientInputMethodServiceDelegate.ServiceCallback serviceCallback) {
70         mInitializationPhase = InitializationPhase.INSTANTIATED;
71         mContext = context;
72         mServiceCallback = serviceCallback;
73     }
74 
onDestroy()75     void onDestroy() {
76         synchronized (mLock) {
77             switch (mInitializationPhase) {
78                 case InitializationPhase.INSTANTIATED:
79                 case InitializationPhase.ON_UNBIND_CALLED:
80                     mInitializationPhase = InitializationPhase.ON_DESTROY_CALLED;
81                     break;
82                 default:
83                     Log.e(TAG, "unexpected state=" + mInitializationPhase);
84                     break;
85             }
86         }
87     }
88 
89     private static final class ServiceImpl extends IMultiClientInputMethod.Stub {
90         private final WeakReference<MultiClientInputMethodServiceDelegateImpl> mImpl;
91 
ServiceImpl(MultiClientInputMethodServiceDelegateImpl service)92         ServiceImpl(MultiClientInputMethodServiceDelegateImpl service) {
93             mImpl = new WeakReference<>(service);
94         }
95 
96         @Override
initialize(IMultiClientInputMethodPrivilegedOperations privOps)97         public void initialize(IMultiClientInputMethodPrivilegedOperations privOps) {
98             final MultiClientInputMethodServiceDelegateImpl service = mImpl.get();
99             if (service == null) {
100                 return;
101             }
102             synchronized (service.mLock) {
103                 switch (service.mInitializationPhase) {
104                     case InitializationPhase.ON_BIND_CALLED:
105                         service.mPrivOps.set(privOps);
106                         service.mInitializationPhase = InitializationPhase.INITIALIZE_CALLED;
107                         service.mServiceCallback.initialized();
108                         break;
109                     default:
110                         Log.e(TAG, "unexpected state=" + service.mInitializationPhase);
111                         break;
112                 }
113             }
114         }
115 
116         @Override
addClient(int clientId, int uid, int pid, int selfReportedDisplayId)117         public void addClient(int clientId, int uid, int pid, int selfReportedDisplayId) {
118             final MultiClientInputMethodServiceDelegateImpl service = mImpl.get();
119             if (service == null) {
120                 return;
121             }
122             service.mServiceCallback.addClient(clientId, uid, pid, selfReportedDisplayId);
123         }
124 
125         @Override
removeClient(int clientId)126         public void removeClient(int clientId) {
127             final MultiClientInputMethodServiceDelegateImpl service = mImpl.get();
128             if (service == null) {
129                 return;
130             }
131             service.mServiceCallback.removeClient(clientId);
132         }
133     }
134 
onBind(Intent intent)135     IBinder onBind(Intent intent) {
136         synchronized (mLock) {
137             switch (mInitializationPhase) {
138                 case InitializationPhase.INSTANTIATED:
139                     mInitializationPhase = InitializationPhase.ON_BIND_CALLED;
140                     return new ServiceImpl(this);
141                 default:
142                     Log.e(TAG, "unexpected state=" + mInitializationPhase);
143                     break;
144             }
145         }
146         return null;
147     }
148 
onUnbind(Intent intent)149     boolean onUnbind(Intent intent) {
150         synchronized (mLock) {
151             switch (mInitializationPhase) {
152                 case InitializationPhase.ON_BIND_CALLED:
153                 case InitializationPhase.INITIALIZE_CALLED:
154                     mInitializationPhase = InitializationPhase.ON_UNBIND_CALLED;
155                     mPrivOps.dispose();
156                     break;
157                 default:
158                     Log.e(TAG, "unexpected state=" + mInitializationPhase);
159                     break;
160             }
161         }
162         return false;
163     }
164 
createInputMethodWindowToken(int displayId)165     IBinder createInputMethodWindowToken(int displayId) {
166         return mPrivOps.createInputMethodWindowToken(displayId);
167     }
168 
acceptClient(int clientId, MultiClientInputMethodServiceDelegate.ClientCallback clientCallback, KeyEvent.DispatcherState dispatcherState, Looper looper)169     void acceptClient(int clientId,
170             MultiClientInputMethodServiceDelegate.ClientCallback clientCallback,
171             KeyEvent.DispatcherState dispatcherState, Looper looper) {
172         final InputChannel[] channels = InputChannel.openInputChannelPair("MSIMS-session");
173         final InputChannel writeChannel = channels[0];
174         final InputChannel readChannel = channels[1];
175         try {
176             final MultiClientInputMethodClientCallbackAdaptor callbackAdaptor =
177                     new MultiClientInputMethodClientCallbackAdaptor(clientCallback, looper,
178                             dispatcherState, readChannel);
179             mPrivOps.acceptClient(clientId, callbackAdaptor.createIInputMethodSession(),
180                     callbackAdaptor.createIMultiClientInputMethodSession(), writeChannel);
181         } finally {
182             writeChannel.dispose();
183         }
184     }
185 
reportImeWindowTarget(int clientId, int targetWindowHandle, IBinder imeWindowToken)186     void reportImeWindowTarget(int clientId, int targetWindowHandle, IBinder imeWindowToken) {
187         mPrivOps.reportImeWindowTarget(clientId, targetWindowHandle, imeWindowToken);
188     }
189 
isUidAllowedOnDisplay(int displayId, int uid)190     boolean isUidAllowedOnDisplay(int displayId, int uid) {
191         return mPrivOps.isUidAllowedOnDisplay(displayId, uid);
192     }
193 
setActive(int clientId, boolean active)194     void setActive(int clientId, boolean active) {
195         mPrivOps.setActive(clientId, active);
196     }
197 }
198