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)66 public InputEventReceiver(InputChannel inputChannel, Looper looper) { 67 super(inputChannel, looper, Choreographer.getSfInstance()); 68 } 69 70 @Override onInputEvent(InputEvent event)71 public void onInputEvent(InputEvent event) { 72 boolean handled = true; 73 try { 74 if (mListener != null) { 75 handled = mListener.onInputEvent(event); 76 } 77 } finally { 78 finishInputEvent(event, handled); 79 } 80 } 81 } 82 83 private final IWindowManager mWindowManager; 84 private final IBinder mToken; 85 private final String mName; 86 87 private InputEventReceiver mInputEventReceiver; 88 private InputListener mListener; 89 private RegistrationListener mRegistrationListener; 90 91 /** 92 * @param name the name corresponding to the input consumer that is defined in the system. 93 */ InputConsumerController(IWindowManager windowManager, String name)94 public InputConsumerController(IWindowManager windowManager, String name) { 95 mWindowManager = windowManager; 96 mToken = new Binder(); 97 mName = name; 98 } 99 100 /** 101 * @return A controller for the pip input consumer. 102 */ getPipInputConsumer()103 public static InputConsumerController getPipInputConsumer() { 104 return new InputConsumerController(WindowManagerGlobal.getWindowManagerService(), 105 INPUT_CONSUMER_PIP); 106 } 107 108 /** 109 * @return A controller for the recents animation input consumer. 110 */ getRecentsAnimationInputConsumer()111 public static InputConsumerController getRecentsAnimationInputConsumer() { 112 return new InputConsumerController(WindowManagerGlobal.getWindowManagerService(), 113 INPUT_CONSUMER_RECENTS_ANIMATION); 114 } 115 116 /** 117 * Sets the input listener. 118 */ setInputListener(InputListener listener)119 public void setInputListener(InputListener listener) { 120 mListener = listener; 121 } 122 123 /** 124 * Sets the registration listener. 125 */ setRegistrationListener(RegistrationListener listener)126 public void setRegistrationListener(RegistrationListener listener) { 127 mRegistrationListener = listener; 128 if (mRegistrationListener != null) { 129 mRegistrationListener.onRegistrationChanged(mInputEventReceiver != null); 130 } 131 } 132 133 /** 134 * Check if the InputConsumer is currently registered with WindowManager 135 * 136 * @return {@code true} if registered, {@code false} if not. 137 */ isRegistered()138 public boolean isRegistered() { 139 return mInputEventReceiver != null; 140 } 141 142 /** 143 * Registers the input consumer. 144 */ registerInputConsumer()145 public void registerInputConsumer() { 146 if (mInputEventReceiver == null) { 147 final InputChannel inputChannel = new InputChannel(); 148 try { 149 // TODO(b/113087003): Support Picture-in-picture in multi-display. 150 mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY); 151 mWindowManager.createInputConsumer(mToken, mName, DEFAULT_DISPLAY, inputChannel); 152 } catch (RemoteException e) { 153 Log.e(TAG, "Failed to create input consumer", e); 154 } 155 mInputEventReceiver = new InputEventReceiver(inputChannel, Looper.myLooper()); 156 if (mRegistrationListener != null) { 157 mRegistrationListener.onRegistrationChanged(true /* isRegistered */); 158 } 159 } 160 } 161 162 /** 163 * Unregisters the input consumer. 164 */ unregisterInputConsumer()165 public void unregisterInputConsumer() { 166 if (mInputEventReceiver != null) { 167 try { 168 // TODO(b/113087003): Support Picture-in-picture in multi-display. 169 mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY); 170 } catch (RemoteException e) { 171 Log.e(TAG, "Failed to destroy input consumer", e); 172 } 173 mInputEventReceiver.dispose(); 174 mInputEventReceiver = null; 175 if (mRegistrationListener != null) { 176 mRegistrationListener.onRegistrationChanged(false /* isRegistered */); 177 } 178 } 179 } 180 dump(PrintWriter pw, String prefix)181 public void dump(PrintWriter pw, String prefix) { 182 final String innerPrefix = prefix + " "; 183 pw.println(prefix + TAG); 184 pw.println(innerPrefix + "registered=" + (mInputEventReceiver != null)); 185 } 186 } 187