1 /* 2 * Copyright (C) 2017 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.systemui.shared.system; 18 19 import static android.view.Display.DEFAULT_DISPLAY; 20 import static android.view.WindowManager.INPUT_CONSUMER_PIP; 21 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; 22 23 import android.os.Binder; 24 import android.os.IBinder; 25 import android.os.Looper; 26 import android.os.RemoteException; 27 import android.util.Log; 28 import android.view.BatchedInputEventReceiver; 29 import android.view.Choreographer; 30 import android.view.IWindowManager; 31 import android.view.InputChannel; 32 import android.view.InputEvent; 33 import android.view.WindowManagerGlobal; 34 35 import java.io.PrintWriter; 36 37 /** 38 * Manages the input consumer that allows the SystemUI to directly receive input. 39 */ 40 public class InputConsumerController { 41 42 private static final String TAG = InputConsumerController.class.getSimpleName(); 43 44 /** 45 * Listener interface for callers to subscribe to input events. 46 */ 47 public interface InputListener { 48 /** Handles any input event. */ onInputEvent(InputEvent ev)49 boolean onInputEvent(InputEvent ev); 50 } 51 52 /** 53 * Listener interface for callers to learn when this class is registered or unregistered with 54 * window manager 55 */ 56 public interface RegistrationListener { onRegistrationChanged(boolean isRegistered)57 void onRegistrationChanged(boolean isRegistered); 58 } 59 60 /** 61 * Input handler used for the input consumer. Input events are batched and consumed with the 62 * SurfaceFlinger vsync. 63 */ 64 private final class InputEventReceiver extends BatchedInputEventReceiver { 65 InputEventReceiver(InputChannel inputChannel, Looper looper, Choreographer choreographer)66 InputEventReceiver(InputChannel inputChannel, Looper looper, 67 Choreographer choreographer) { 68 super(inputChannel, looper, choreographer); 69 } 70 71 @Override onInputEvent(InputEvent event)72 public void onInputEvent(InputEvent event) { 73 boolean handled = true; 74 try { 75 if (mListener != null) { 76 handled = mListener.onInputEvent(event); 77 } 78 } finally { 79 finishInputEvent(event, handled); 80 } 81 } 82 } 83 84 private final IWindowManager mWindowManager; 85 private final IBinder mToken; 86 private final String mName; 87 88 private InputEventReceiver mInputEventReceiver; 89 private InputListener mListener; 90 private RegistrationListener mRegistrationListener; 91 92 /** 93 * @param name the name corresponding to the input consumer that is defined in the system. 94 */ InputConsumerController(IWindowManager windowManager, String name)95 public InputConsumerController(IWindowManager windowManager, String name) { 96 mWindowManager = windowManager; 97 mToken = new Binder(); 98 mName = name; 99 } 100 101 /** 102 * @return A controller for the pip input consumer. 103 */ getPipInputConsumer()104 public static InputConsumerController getPipInputConsumer() { 105 return new InputConsumerController(WindowManagerGlobal.getWindowManagerService(), 106 INPUT_CONSUMER_PIP); 107 } 108 109 /** 110 * @return A controller for the recents animation input consumer. 111 */ getRecentsAnimationInputConsumer()112 public static InputConsumerController getRecentsAnimationInputConsumer() { 113 return new InputConsumerController(WindowManagerGlobal.getWindowManagerService(), 114 INPUT_CONSUMER_RECENTS_ANIMATION); 115 } 116 117 /** 118 * Sets the input listener. 119 */ setInputListener(InputListener listener)120 public void setInputListener(InputListener listener) { 121 mListener = listener; 122 } 123 124 /** 125 * Sets the registration listener. 126 */ setRegistrationListener(RegistrationListener listener)127 public void setRegistrationListener(RegistrationListener listener) { 128 mRegistrationListener = listener; 129 if (mRegistrationListener != null) { 130 mRegistrationListener.onRegistrationChanged(mInputEventReceiver != null); 131 } 132 } 133 134 /** 135 * Check if the InputConsumer is currently registered with WindowManager 136 * 137 * @return {@code true} if registered, {@code false} if not. 138 */ isRegistered()139 public boolean isRegistered() { 140 return mInputEventReceiver != null; 141 } 142 143 /** 144 * Registers the input consumer. 145 */ registerInputConsumer()146 public void registerInputConsumer() { 147 registerInputConsumer(false); 148 } 149 150 /** 151 * Registers the input consumer. 152 * @param withSfVsync the flag set using sf vsync signal or no 153 */ registerInputConsumer(boolean withSfVsync)154 public void registerInputConsumer(boolean withSfVsync) { 155 if (mInputEventReceiver == null) { 156 final InputChannel inputChannel = new InputChannel(); 157 try { 158 // TODO(b/113087003): Support Picture-in-picture in multi-display. 159 mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY); 160 mWindowManager.createInputConsumer(mToken, mName, DEFAULT_DISPLAY, inputChannel); 161 } catch (RemoteException e) { 162 Log.e(TAG, "Failed to create input consumer", e); 163 } 164 mInputEventReceiver = new InputEventReceiver(inputChannel, Looper.myLooper(), 165 withSfVsync ? Choreographer.getSfInstance() : Choreographer.getInstance()); 166 if (mRegistrationListener != null) { 167 mRegistrationListener.onRegistrationChanged(true /* isRegistered */); 168 } 169 } 170 } 171 172 /** 173 * Unregisters the input consumer. 174 */ unregisterInputConsumer()175 public void unregisterInputConsumer() { 176 if (mInputEventReceiver != null) { 177 try { 178 // TODO(b/113087003): Support Picture-in-picture in multi-display. 179 mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY); 180 } catch (RemoteException e) { 181 Log.e(TAG, "Failed to destroy input consumer", e); 182 } 183 mInputEventReceiver.dispose(); 184 mInputEventReceiver = null; 185 if (mRegistrationListener != null) { 186 mRegistrationListener.onRegistrationChanged(false /* isRegistered */); 187 } 188 } 189 } 190 dump(PrintWriter pw, String prefix)191 public void dump(PrintWriter pw, String prefix) { 192 final String innerPrefix = prefix + " "; 193 pw.println(prefix + TAG); 194 pw.println(innerPrefix + "registered=" + (mInputEventReceiver != null)); 195 } 196 } 197