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