• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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