1 /* 2 * Copyright (C) 2010 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.view; 18 19 import android.compat.annotation.UnsupportedAppUsage; 20 import android.os.Build; 21 import android.os.Looper; 22 import android.os.MessageQueue; 23 import android.util.LongSparseArray; 24 import android.util.Pools.Pool; 25 import android.util.Pools.SimplePool; 26 27 import dalvik.system.CloseGuard; 28 29 import java.lang.ref.WeakReference; 30 31 /** 32 * An input queue provides a mechanism for an application to receive incoming 33 * input events. Currently only usable from native code. 34 */ 35 public final class InputQueue { 36 private final LongSparseArray<ActiveInputEvent> mActiveEventArray = 37 new LongSparseArray<ActiveInputEvent>(20); 38 private final Pool<ActiveInputEvent> mActiveInputEventPool = 39 new SimplePool<ActiveInputEvent>(20); 40 41 private final CloseGuard mCloseGuard = CloseGuard.get(); 42 43 private long mPtr; 44 nativeInit(WeakReference<InputQueue> weakQueue, MessageQueue messageQueue)45 private static native long nativeInit(WeakReference<InputQueue> weakQueue, 46 MessageQueue messageQueue); nativeSendKeyEvent(long ptr, KeyEvent e, boolean preDispatch)47 private static native long nativeSendKeyEvent(long ptr, KeyEvent e, boolean preDispatch); nativeSendMotionEvent(long ptr, MotionEvent e)48 private static native long nativeSendMotionEvent(long ptr, MotionEvent e); nativeDispose(long ptr)49 private static native void nativeDispose(long ptr); 50 51 /** @hide */ InputQueue()52 public InputQueue() { 53 mPtr = nativeInit(new WeakReference<InputQueue>(this), Looper.myQueue()); 54 55 mCloseGuard.open("dispose"); 56 } 57 58 @Override finalize()59 protected void finalize() throws Throwable { 60 try { 61 dispose(true); 62 } finally { 63 super.finalize(); 64 } 65 } 66 67 /** @hide */ dispose()68 public void dispose() { 69 dispose(false); 70 } 71 72 /** @hide */ dispose(boolean finalized)73 public void dispose(boolean finalized) { 74 if (mCloseGuard != null) { 75 if (finalized) { 76 mCloseGuard.warnIfOpen(); 77 } 78 mCloseGuard.close(); 79 } 80 81 if (mPtr != 0) { 82 nativeDispose(mPtr); 83 mPtr = 0; 84 } 85 } 86 87 /** @hide */ getNativePtr()88 public long getNativePtr() { 89 return mPtr; 90 } 91 92 /** @hide */ sendInputEvent(InputEvent e, Object token, boolean predispatch, FinishedInputEventCallback callback)93 public void sendInputEvent(InputEvent e, Object token, boolean predispatch, 94 FinishedInputEventCallback callback) { 95 ActiveInputEvent event = obtainActiveInputEvent(token, callback); 96 long id; 97 if (e instanceof KeyEvent) { 98 id = nativeSendKeyEvent(mPtr, (KeyEvent) e, predispatch); 99 } else { 100 id = nativeSendMotionEvent(mPtr, (MotionEvent) e); 101 } 102 mActiveEventArray.put(id, event); 103 } 104 105 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) finishInputEvent(long id, boolean handled)106 private void finishInputEvent(long id, boolean handled) { 107 int index = mActiveEventArray.indexOfKey(id); 108 if (index >= 0) { 109 ActiveInputEvent e = mActiveEventArray.valueAt(index); 110 mActiveEventArray.removeAt(index); 111 e.mCallback.onFinishedInputEvent(e.mToken, handled); 112 recycleActiveInputEvent(e); 113 } 114 } 115 obtainActiveInputEvent(Object token, FinishedInputEventCallback callback)116 private ActiveInputEvent obtainActiveInputEvent(Object token, 117 FinishedInputEventCallback callback) { 118 ActiveInputEvent e = mActiveInputEventPool.acquire(); 119 if (e == null) { 120 e = new ActiveInputEvent(); 121 } 122 e.mToken = token; 123 e.mCallback = callback; 124 return e; 125 } 126 recycleActiveInputEvent(ActiveInputEvent e)127 private void recycleActiveInputEvent(ActiveInputEvent e) { 128 e.recycle(); 129 mActiveInputEventPool.release(e); 130 } 131 132 private final class ActiveInputEvent { 133 public Object mToken; 134 public FinishedInputEventCallback mCallback; 135 recycle()136 public void recycle() { 137 mToken = null; 138 mCallback = null; 139 } 140 } 141 142 /** 143 * Interface to receive notification of when an InputQueue is associated 144 * and dissociated with a thread. 145 */ 146 public static interface Callback { 147 /** 148 * Called when the given InputQueue is now associated with the 149 * thread making this call, so it can start receiving events from it. 150 */ onInputQueueCreated(InputQueue queue)151 void onInputQueueCreated(InputQueue queue); 152 153 /** 154 * Called when the given InputQueue is no longer associated with 155 * the thread and thus not dispatching events. 156 */ onInputQueueDestroyed(InputQueue queue)157 void onInputQueueDestroyed(InputQueue queue); 158 } 159 160 /** @hide */ 161 public static interface FinishedInputEventCallback { onFinishedInputEvent(Object token, boolean handled)162 void onFinishedInputEvent(Object token, boolean handled); 163 } 164 165 } 166