1/* 2 * Copyright (C) 2015 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 17package com.android.server.accessibility; 18 19import android.os.Handler; 20import android.os.Message; 21import android.os.SystemClock; 22import android.util.Pools; 23import android.util.Slog; 24import android.view.KeyEvent; 25 26import com.android.server.policy.WindowManagerPolicy; 27 28/** 29 * Intercepts key events and forwards them to accessibility manager service. 30 */ 31public class KeyboardInterceptor extends BaseEventStreamTransformation implements Handler.Callback { 32 private static final int MESSAGE_PROCESS_QUEUED_EVENTS = 1; 33 private static final String LOG_TAG = "KeyboardInterceptor"; 34 35 private final AccessibilityManagerService mAms; 36 private final WindowManagerPolicy mPolicy; 37 private final Handler mHandler; 38 39 private KeyEventHolder mEventQueueStart; 40 private KeyEventHolder mEventQueueEnd; 41 42 /** 43 * @param service The service to notify of key events 44 * @param policy The policy to check for keys that may affect a11y 45 */ 46 public KeyboardInterceptor(AccessibilityManagerService service, WindowManagerPolicy policy) { 47 mAms = service; 48 mPolicy = policy; 49 mHandler = new Handler(this); 50 } 51 52 /** 53 * @param service The service to notify of key events 54 * @param policy The policy to check for keys that may affect a11y 55 * @param handler The handler to use. Only used for testing. 56 */ 57 public KeyboardInterceptor(AccessibilityManagerService service, WindowManagerPolicy policy, 58 Handler handler) { 59 // Can't combine the constructors without making at least mHandler non-final. 60 mAms = service; 61 mPolicy = policy; 62 mHandler = handler; 63 } 64 65 @Override 66 public void onKeyEvent(KeyEvent event, int policyFlags) { 67 /* 68 * Certain keys have system-level behavior that affects accessibility services. 69 * Let that behavior settle before handling the keys 70 */ 71 long eventDelay = getEventDelay(event, policyFlags); 72 if (eventDelay < 0) { 73 return; 74 } 75 if ((eventDelay > 0) || (mEventQueueStart != null)) { 76 addEventToQueue(event, policyFlags, eventDelay); 77 return; 78 } 79 80 mAms.notifyKeyEvent(event, policyFlags); 81 } 82 83 @Override 84 public boolean handleMessage(Message msg) { 85 if (msg.what != MESSAGE_PROCESS_QUEUED_EVENTS) { 86 Slog.e(LOG_TAG, "Unexpected message type"); 87 return false; 88 } 89 processQueuedEvents(); 90 if (mEventQueueStart != null) { 91 scheduleProcessQueuedEvents(); 92 } 93 return true; 94 } 95 96 private void addEventToQueue(KeyEvent event, int policyFlags, long delay) { 97 long dispatchTime = SystemClock.uptimeMillis() + delay; 98 if (mEventQueueStart == null) { 99 mEventQueueEnd = mEventQueueStart = 100 KeyEventHolder.obtain(event, policyFlags, dispatchTime); 101 scheduleProcessQueuedEvents(); 102 return; 103 } 104 final KeyEventHolder holder = KeyEventHolder.obtain(event, policyFlags, dispatchTime); 105 holder.next = mEventQueueStart; 106 mEventQueueStart.previous = holder; 107 mEventQueueStart = holder; 108 } 109 110 private void scheduleProcessQueuedEvents() { 111 if (!mHandler.sendEmptyMessageAtTime( 112 MESSAGE_PROCESS_QUEUED_EVENTS, mEventQueueEnd.dispatchTime)) { 113 Slog.e(LOG_TAG, "Failed to schedule key event"); 114 }; 115 } 116 117 private void processQueuedEvents() { 118 final long currentTime = SystemClock.uptimeMillis(); 119 while ((mEventQueueEnd != null) && (mEventQueueEnd.dispatchTime <= currentTime)) { 120 final long eventDelay = getEventDelay(mEventQueueEnd.event, mEventQueueEnd.policyFlags); 121 if (eventDelay > 0) { 122 // Reschedule the event 123 mEventQueueEnd.dispatchTime = currentTime + eventDelay; 124 return; 125 } 126 // We'll either send or drop the event 127 if (eventDelay == 0) { 128 mAms.notifyKeyEvent(mEventQueueEnd.event, mEventQueueEnd.policyFlags); 129 } 130 final KeyEventHolder eventToBeRecycled = mEventQueueEnd; 131 mEventQueueEnd = mEventQueueEnd.previous; 132 if (mEventQueueEnd != null) { 133 mEventQueueEnd.next = null; 134 } 135 eventToBeRecycled.recycle(); 136 if (mEventQueueEnd == null) { 137 mEventQueueStart = null; 138 } 139 } 140 } 141 142 private long getEventDelay(KeyEvent event, int policyFlags) { 143 int keyCode = event.getKeyCode(); 144 if ((keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) || (keyCode == KeyEvent.KEYCODE_VOLUME_UP)) { 145 return mPolicy.interceptKeyBeforeDispatching(null, event, policyFlags); 146 } 147 return 0; 148 } 149 150 private static class KeyEventHolder { 151 private static final int MAX_POOL_SIZE = 32; 152 private static final Pools.SimplePool<KeyEventHolder> sPool = 153 new Pools.SimplePool<>(MAX_POOL_SIZE); 154 155 public int policyFlags; 156 public long dispatchTime; 157 public KeyEvent event; 158 public KeyEventHolder next; 159 public KeyEventHolder previous; 160 161 public static KeyEventHolder obtain(KeyEvent event, int policyFlags, long dispatchTime) { 162 KeyEventHolder holder = sPool.acquire(); 163 if (holder == null) { 164 holder = new KeyEventHolder(); 165 } 166 holder.event = KeyEvent.obtain(event); 167 holder.policyFlags = policyFlags; 168 holder.dispatchTime = dispatchTime; 169 return holder; 170 } 171 172 public void recycle() { 173 event.recycle(); 174 event = null; 175 policyFlags = 0; 176 dispatchTime = 0; 177 next = null; 178 previous = null; 179 sPool.release(this); 180 } 181 } 182} 183