• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.server.input;
18 
19 import android.content.Context;
20 import android.os.Handler;
21 import android.os.Looper;
22 import android.os.Message;
23 import android.util.ArrayMap;
24 import android.util.FeatureFlagUtils;
25 
26 import com.android.internal.annotations.GuardedBy;
27 
28 import java.util.Map;
29 
30 /**
31  * A component of {@link InputManagerService} responsible for managing key remappings.
32  *
33  * @hide
34  */
35 final class KeyRemapper {
36 
37     private static final int MSG_UPDATE_EXISTING_KEY_REMAPPING = 1;
38     private static final int MSG_REMAP_KEY = 2;
39     private static final int MSG_CLEAR_ALL_REMAPPING = 3;
40 
41     private final Context mContext;
42     private final NativeInputManagerService mNative;
43     // The PersistentDataStore should be locked before use.
44     @GuardedBy("mDataStore")
45     private final PersistentDataStore mDataStore;
46     private final Handler mHandler;
47 
KeyRemapper(Context context, NativeInputManagerService nativeService, PersistentDataStore dataStore, Looper looper)48     KeyRemapper(Context context, NativeInputManagerService nativeService,
49                 PersistentDataStore dataStore, Looper looper) {
50         mContext = context;
51         mNative = nativeService;
52         mDataStore = dataStore;
53         mHandler = new Handler(looper, this::handleMessage);
54     }
55 
systemRunning()56     public void systemRunning() {
57         Message.obtain(mHandler, MSG_UPDATE_EXISTING_KEY_REMAPPING).sendToTarget();
58     }
59 
remapKey(int fromKey, int toKey)60     public void remapKey(int fromKey, int toKey) {
61         if (!supportRemapping()) {
62             return;
63         }
64         Message msg = Message.obtain(mHandler, MSG_REMAP_KEY, fromKey, toKey);
65         mHandler.sendMessage(msg);
66     }
67 
clearAllKeyRemappings()68     public void clearAllKeyRemappings() {
69         if (!supportRemapping()) {
70             return;
71         }
72         Message msg = Message.obtain(mHandler, MSG_CLEAR_ALL_REMAPPING);
73         mHandler.sendMessage(msg);
74     }
75 
getKeyRemapping()76     public Map<Integer, Integer> getKeyRemapping() {
77         if (!supportRemapping()) {
78             return new ArrayMap<>();
79         }
80         synchronized (mDataStore) {
81             return mDataStore.getKeyRemapping();
82         }
83     }
84 
setKeyRemapping(Map<Integer, Integer> keyRemapping)85     private void setKeyRemapping(Map<Integer, Integer> keyRemapping) {
86         int index = 0;
87         int[] fromKeycodesArr = new int[keyRemapping.size()];
88         int[] toKeycodesArr = new int[keyRemapping.size()];
89         for (Map.Entry<Integer, Integer> entry : keyRemapping.entrySet()) {
90             fromKeycodesArr[index] = entry.getKey();
91             toKeycodesArr[index] = entry.getValue();
92             index++;
93         }
94         mNative.setKeyRemapping(fromKeycodesArr, toKeycodesArr);
95     }
96 
remapKeyInternal(int fromKey, int toKey)97     private void remapKeyInternal(int fromKey, int toKey) {
98         synchronized (mDataStore) {
99             try {
100                 if (fromKey == toKey) {
101                     mDataStore.clearMappedKey(fromKey);
102                 } else {
103                     mDataStore.remapKey(fromKey, toKey);
104                 }
105             } finally {
106                 mDataStore.saveIfNeeded();
107             }
108             setKeyRemapping(mDataStore.getKeyRemapping());
109         }
110     }
111 
clearAllRemappingsInternal()112     private void clearAllRemappingsInternal() {
113         synchronized (mDataStore) {
114             try {
115                 Map<Integer, Integer> keyRemapping = mDataStore.getKeyRemapping();
116                 for (int fromKey : keyRemapping.keySet()) {
117                     mDataStore.clearMappedKey(fromKey);
118                 }
119             } finally {
120                 mDataStore.saveIfNeeded();
121             }
122             setKeyRemapping(mDataStore.getKeyRemapping());
123         }
124     }
125 
updateExistingKeyMapping()126     public void updateExistingKeyMapping() {
127         if (!supportRemapping()) {
128             return;
129         }
130         setKeyRemapping(getKeyRemapping());
131     }
132 
handleMessage(Message msg)133     private boolean handleMessage(Message msg) {
134         switch (msg.what) {
135             case MSG_UPDATE_EXISTING_KEY_REMAPPING:
136                 updateExistingKeyMapping();
137                 return true;
138             case MSG_REMAP_KEY:
139                 remapKeyInternal(msg.arg1, msg.arg2);
140                 return true;
141             case MSG_CLEAR_ALL_REMAPPING:
142                 clearAllRemappingsInternal();
143                 return true;
144         }
145         return false;
146     }
147 
supportRemapping()148     private boolean supportRemapping() {
149         return FeatureFlagUtils.isEnabled(mContext,
150                 FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_MODIFIER_KEY);
151     }
152 }
153