1 /* 2 * Copyright (C) 2013 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.Handler; 22 import android.os.Looper; 23 import android.os.MessageQueue; 24 import android.util.Log; 25 26 import dalvik.system.CloseGuard; 27 28 import java.lang.ref.WeakReference; 29 import java.util.concurrent.Callable; 30 import java.util.concurrent.ExecutionException; 31 import java.util.concurrent.FutureTask; 32 import java.util.concurrent.RunnableFuture; 33 34 /** 35 * Provides a low-level mechanism for an application to send input events. 36 * @hide 37 */ 38 public abstract class InputEventSender { 39 private static final String TAG = "InputEventSender"; 40 41 private final CloseGuard mCloseGuard = CloseGuard.get(); 42 43 private long mSenderPtr; 44 45 // We keep references to the input channel and message queue objects (indirectly through 46 // Handler) here so that they are not GC'd while the native peer of the receiver is using them. 47 private InputChannel mInputChannel; 48 private Handler mHandler; 49 nativeInit(WeakReference<InputEventSender> sender, InputChannel inputChannel, MessageQueue messageQueue)50 private static native long nativeInit(WeakReference<InputEventSender> sender, 51 InputChannel inputChannel, MessageQueue messageQueue); nativeDispose(long senderPtr)52 private static native void nativeDispose(long senderPtr); nativeSendKeyEvent(long senderPtr, int seq, KeyEvent event)53 private static native boolean nativeSendKeyEvent(long senderPtr, int seq, KeyEvent event); nativeSendMotionEvent(long senderPtr, int seq, MotionEvent event)54 private static native boolean nativeSendMotionEvent(long senderPtr, int seq, MotionEvent event); 55 56 /** 57 * Creates an input event sender bound to the specified input channel. 58 * 59 * @param inputChannel The input channel. 60 * @param looper The looper to use when invoking callbacks. 61 */ InputEventSender(InputChannel inputChannel, Looper looper)62 public InputEventSender(InputChannel inputChannel, Looper looper) { 63 if (inputChannel == null) { 64 throw new IllegalArgumentException("inputChannel must not be null"); 65 } 66 if (looper == null) { 67 throw new IllegalArgumentException("looper must not be null"); 68 } 69 70 mInputChannel = inputChannel; 71 mHandler = new Handler(looper); 72 mSenderPtr = nativeInit(new WeakReference<InputEventSender>(this), 73 mInputChannel, looper.getQueue()); 74 75 mCloseGuard.open("InputEventSender.dispose"); 76 } 77 78 @Override finalize()79 protected void finalize() throws Throwable { 80 try { 81 dispose(true); 82 } finally { 83 super.finalize(); 84 } 85 } 86 87 /** 88 * Disposes the receiver. 89 */ dispose()90 public void dispose() { 91 dispose(false); 92 } 93 dispose(boolean finalized)94 private void dispose(boolean finalized) { 95 if (mCloseGuard != null) { 96 if (finalized) { 97 mCloseGuard.warnIfOpen(); 98 } 99 mCloseGuard.close(); 100 } 101 102 if (mSenderPtr != 0) { 103 nativeDispose(mSenderPtr); 104 mSenderPtr = 0; 105 } 106 mHandler = null; 107 mInputChannel = null; 108 } 109 110 /** 111 * Called when an input event is finished. 112 * 113 * @param seq The input event sequence number. 114 * @param handled True if the input event was handled. 115 */ onInputEventFinished(int seq, boolean handled)116 public void onInputEventFinished(int seq, boolean handled) { 117 } 118 119 /** 120 * Called when timeline is sent to the publisher. 121 * 122 * @param inputEventId The id of the input event that caused the frame being reported 123 * @param gpuCompletedTime The time when the frame left the app process 124 * @param presentTime The time when the frame was presented on screen 125 */ onTimelineReported(int inputEventId, long gpuCompletedTime, long presentTime)126 public void onTimelineReported(int inputEventId, long gpuCompletedTime, long presentTime) { 127 } 128 129 /** 130 * Sends an input event. Can be called from any thread. Do not call this if the looper thread 131 * is blocked! It would cause a deadlock. 132 * 133 * @param seq The input event sequence number. 134 * @param event The input event to send. 135 * @return True if the entire event was sent successfully. May return false 136 * if the input channel buffer filled before all samples were dispatched. 137 */ sendInputEvent(int seq, InputEvent event)138 public final boolean sendInputEvent(int seq, InputEvent event) { 139 if (event == null) { 140 throw new IllegalArgumentException("event must not be null"); 141 } 142 if (mSenderPtr == 0) { 143 Log.w(TAG, "Attempted to send an input event but the input event " 144 + "sender has already been disposed."); 145 return false; 146 } 147 148 if (mHandler.getLooper().isCurrentThread()) { 149 return sendInputEventInternal(seq, event); 150 } 151 // This is being called on another thread. Post a runnable to the looper thread 152 // with the event injection, and wait until it's processed. 153 final RunnableFuture<Boolean> task = new FutureTask<>(new Callable<Boolean>() { 154 @Override 155 public Boolean call() throws Exception { 156 return sendInputEventInternal(seq, event); 157 } 158 }); 159 mHandler.post(task); 160 try { 161 return task.get(); 162 } catch (InterruptedException exc) { 163 throw new IllegalStateException("Interrupted while sending " + event + ": " + exc); 164 } catch (ExecutionException exc) { 165 throw new IllegalStateException("Couldn't send " + event + ": " + exc); 166 } 167 } 168 sendInputEventInternal(int seq, InputEvent event)169 private boolean sendInputEventInternal(int seq, InputEvent event) { 170 if (event instanceof KeyEvent) { 171 return nativeSendKeyEvent(mSenderPtr, seq, (KeyEvent)event); 172 } else { 173 return nativeSendMotionEvent(mSenderPtr, seq, (MotionEvent)event); 174 } 175 } 176 177 // Called from native code. 178 @SuppressWarnings("unused") 179 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) dispatchInputEventFinished(int seq, boolean handled)180 private void dispatchInputEventFinished(int seq, boolean handled) { 181 onInputEventFinished(seq, handled); 182 } 183 184 // Called from native code. 185 @SuppressWarnings("unused") dispatchTimelineReported( int inputEventId, long gpuCompletedTime, long presentTime)186 private void dispatchTimelineReported( 187 int inputEventId, long gpuCompletedTime, long presentTime) { 188 onTimelineReported(inputEventId, gpuCompletedTime, presentTime); 189 } 190 } 191