Home | History | Annotate | Download | only in inputmethodservice
      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 
     68     MultiClientInputMethodServiceDelegateImpl(Context context,
     69             MultiClientInputMethodServiceDelegate.ServiceCallback serviceCallback) {
     70         mInitializationPhase = InitializationPhase.INSTANTIATED;
     71         mContext = context;
     72         mServiceCallback = serviceCallback;
     73     }
     74 
     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 
     92         ServiceImpl(MultiClientInputMethodServiceDelegateImpl service) {
     93             mImpl = new WeakReference<>(service);
     94         }
     95 
     96         @Override
     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
    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
    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 
    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 
    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 
    165     IBinder createInputMethodWindowToken(int displayId) {
    166         return mPrivOps.createInputMethodWindowToken(displayId);
    167     }
    168 
    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 
    186     void reportImeWindowTarget(int clientId, int targetWindowHandle, IBinder imeWindowToken) {
    187         mPrivOps.reportImeWindowTarget(clientId, targetWindowHandle, imeWindowToken);
    188     }
    189 
    190     boolean isUidAllowedOnDisplay(int displayId, int uid) {
    191         return mPrivOps.isUidAllowedOnDisplay(displayId, uid);
    192     }
    193 
    194     void setActive(int clientId, boolean active) {
    195         mPrivOps.setActive(clientId, active);
    196     }
    197 }
    198