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