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.WindowManager.INPUT_CONSUMER_PIP;
20 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
21 
22 import android.os.Binder;
23 import android.os.IBinder;
24 import android.os.Looper;
25 import android.os.RemoteException;
26 import android.util.Log;
27 import android.view.BatchedInputEventReceiver;
28 import android.view.Choreographer;
29 import android.view.InputChannel;
30 import android.view.InputEvent;
31 import android.view.IWindowManager;
32 import android.view.MotionEvent;
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 touch 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 touch events.
46      */
47     public interface TouchListener {
onTouchEvent(MotionEvent ev)48         boolean onTouchEvent(MotionEvent ev);
49     }
50 
51     /**
52      * Listener interface for callers to learn when this class is registered or unregistered with
53      * window manager
54      */
55     public interface RegistrationListener {
onRegistrationChanged(boolean isRegistered)56         void onRegistrationChanged(boolean isRegistered);
57     }
58 
59     /**
60      * Input handler used for the input consumer. Input events are batched and consumed with the
61      * SurfaceFlinger vsync.
62      */
63     private final class InputEventReceiver extends BatchedInputEventReceiver {
64 
InputEventReceiver(InputChannel inputChannel, Looper looper)65         public InputEventReceiver(InputChannel inputChannel, Looper looper) {
66             super(inputChannel, looper, Choreographer.getSfInstance());
67         }
68 
69         @Override
onInputEvent(InputEvent event, int displayId)70         public void onInputEvent(InputEvent event, int displayId) {
71             boolean handled = true;
72             try {
73                 if (mListener != null && event instanceof MotionEvent) {
74                     MotionEvent ev = (MotionEvent) event;
75                     handled = mListener.onTouchEvent(ev);
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 TouchListener 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 touch listener.
118      */
setTouchListener(TouchListener listener)119     public void setTouchListener(TouchListener 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                 mWindowManager.destroyInputConsumer(mName);
150                 mWindowManager.createInputConsumer(mToken, mName, inputChannel);
151             } catch (RemoteException e) {
152                 Log.e(TAG, "Failed to create input consumer", e);
153             }
154             mInputEventReceiver = new InputEventReceiver(inputChannel, Looper.myLooper());
155             if (mRegistrationListener != null) {
156                 mRegistrationListener.onRegistrationChanged(true /* isRegistered */);
157             }
158         }
159     }
160 
161     /**
162      * Unregisters the input consumer.
163      */
unregisterInputConsumer()164     public void unregisterInputConsumer() {
165         if (mInputEventReceiver != null) {
166             try {
167                 mWindowManager.destroyInputConsumer(mName);
168             } catch (RemoteException e) {
169                 Log.e(TAG, "Failed to destroy input consumer", e);
170             }
171             mInputEventReceiver.dispose();
172             mInputEventReceiver = null;
173             if (mRegistrationListener != null) {
174                 mRegistrationListener.onRegistrationChanged(false /* isRegistered */);
175             }
176         }
177     }
178 
dump(PrintWriter pw, String prefix)179     public void dump(PrintWriter pw, String prefix) {
180         final String innerPrefix = prefix + "  ";
181         pw.println(prefix + TAG);
182         pw.println(innerPrefix + "registered=" + (mInputEventReceiver != null));
183     }
184 }
185