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 17 package android.view; 18 19 import android.compat.annotation.UnsupportedAppUsage; 20 import android.os.Handler; 21 import android.os.Looper; 22 import android.os.Trace; 23 24 /** 25 * Similar to {@link InputEventReceiver}, but batches events to vsync boundaries when possible. 26 * @hide 27 */ 28 public class BatchedInputEventReceiver extends InputEventReceiver { 29 private Choreographer mChoreographer; 30 private boolean mBatchingEnabled; 31 private boolean mBatchedInputScheduled; 32 private final String mTag; 33 private final Handler mHandler; 34 private final Runnable mConsumeBatchedInputEvents = new Runnable() { 35 @Override 36 public void run() { 37 consumeBatchedInputEvents(-1); 38 } 39 }; 40 41 @UnsupportedAppUsage BatchedInputEventReceiver( InputChannel inputChannel, Looper looper, Choreographer choreographer)42 public BatchedInputEventReceiver( 43 InputChannel inputChannel, Looper looper, Choreographer choreographer) { 44 super(inputChannel, looper); 45 mChoreographer = choreographer; 46 mBatchingEnabled = true; 47 mTag = inputChannel.getName(); 48 traceBoolVariable("mBatchingEnabled", mBatchingEnabled); 49 traceBoolVariable("mBatchedInputScheduled", mBatchedInputScheduled); 50 mHandler = new Handler(looper); 51 } 52 53 @Override onBatchedInputEventPending(int source)54 public void onBatchedInputEventPending(int source) { 55 if (mBatchingEnabled) { 56 scheduleBatchedInput(); 57 } else { 58 consumeBatchedInputEvents(-1); 59 } 60 } 61 62 @Override dispose()63 public void dispose() { 64 unscheduleBatchedInput(); 65 consumeBatchedInputEvents(-1); 66 super.dispose(); 67 } 68 69 /** 70 * Sets whether to enable batching on this input event receiver. 71 * @hide 72 */ setBatchingEnabled(boolean batchingEnabled)73 public void setBatchingEnabled(boolean batchingEnabled) { 74 if (mBatchingEnabled == batchingEnabled) { 75 return; 76 } 77 78 mBatchingEnabled = batchingEnabled; 79 traceBoolVariable("mBatchingEnabled", mBatchingEnabled); 80 mHandler.removeCallbacks(mConsumeBatchedInputEvents); 81 if (!batchingEnabled) { 82 unscheduleBatchedInput(); 83 mHandler.post(mConsumeBatchedInputEvents); 84 } 85 } 86 doConsumeBatchedInput(long frameTimeNanos)87 protected void doConsumeBatchedInput(long frameTimeNanos) { 88 if (mBatchedInputScheduled) { 89 mBatchedInputScheduled = false; 90 traceBoolVariable("mBatchedInputScheduled", mBatchedInputScheduled); 91 if (consumeBatchedInputEvents(frameTimeNanos) && frameTimeNanos != -1) { 92 // If we consumed a batch here, we want to go ahead and schedule the 93 // consumption of batched input events on the next frame. Otherwise, we would 94 // wait until we have more input events pending and might get starved by other 95 // things occurring in the process. If the frame time is -1, however, then 96 // we're in a non-batching mode, so there's no need to schedule this. 97 scheduleBatchedInput(); 98 } 99 } 100 } 101 scheduleBatchedInput()102 private void scheduleBatchedInput() { 103 if (!mBatchedInputScheduled) { 104 mBatchedInputScheduled = true; 105 traceBoolVariable("mBatchedInputScheduled", mBatchedInputScheduled); 106 mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, mBatchedInputRunnable, null); 107 } 108 } 109 unscheduleBatchedInput()110 private void unscheduleBatchedInput() { 111 if (mBatchedInputScheduled) { 112 mBatchedInputScheduled = false; 113 traceBoolVariable("mBatchedInputScheduled", mBatchedInputScheduled); 114 mChoreographer.removeCallbacks( 115 Choreographer.CALLBACK_INPUT, mBatchedInputRunnable, null); 116 } 117 } 118 119 // @TODO(b/311142655): Delete this temporary tracing. It's only used here to debug a very 120 // specific issue. traceBoolVariable(String name, boolean value)121 private void traceBoolVariable(String name, boolean value) { 122 Trace.traceCounter(Trace.TRACE_TAG_INPUT, name, value ? 1 : 0); 123 } 124 125 private final class BatchedInputRunnable implements Runnable { 126 @Override run()127 public void run() { 128 try { 129 Trace.traceBegin(Trace.TRACE_TAG_INPUT, mTag); 130 doConsumeBatchedInput(mChoreographer.getFrameTimeNanos()); 131 } finally { 132 Trace.traceEnd(Trace.TRACE_TAG_INPUT); 133 } 134 } 135 } 136 private final BatchedInputRunnable mBatchedInputRunnable = new BatchedInputRunnable(); 137 138 /** 139 * A {@link BatchedInputEventReceiver} that reports events to an {@link InputEventListener}. 140 * @hide 141 */ 142 public static class SimpleBatchedInputEventReceiver extends BatchedInputEventReceiver { 143 144 /** @hide */ 145 public interface InputEventListener { 146 /** 147 * Process the input event. 148 * @return handled 149 */ onInputEvent(InputEvent event)150 boolean onInputEvent(InputEvent event); 151 } 152 153 protected InputEventListener mListener; 154 SimpleBatchedInputEventReceiver(InputChannel inputChannel, Looper looper, Choreographer choreographer, InputEventListener listener)155 public SimpleBatchedInputEventReceiver(InputChannel inputChannel, Looper looper, 156 Choreographer choreographer, InputEventListener listener) { 157 super(inputChannel, looper, choreographer); 158 mListener = listener; 159 } 160 161 @Override onInputEvent(InputEvent event)162 public void onInputEvent(InputEvent event) { 163 boolean handled = false; 164 try { 165 handled = mListener.onInputEvent(event); 166 } finally { 167 finishInputEvent(event, handled); 168 } 169 } 170 } 171 } 172