1 /*
2  * Copyright 2020 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.server.media;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.app.PendingIntent;
23 import android.content.Context;
24 import android.media.session.ISessionManager;
25 import android.media.session.MediaSession;
26 import android.os.Binder;
27 import android.view.KeyEvent;
28 import android.view.ViewConfiguration;
29 
30 import java.lang.annotation.Retention;
31 import java.lang.annotation.RetentionPolicy;
32 import java.util.HashMap;
33 import java.util.Map;
34 
35 /**
36  * Provides a way to customize behavior for media key events.
37  * <p>
38  * In order to override the implementation of the single/double/triple tap or long press,
39  * {@link #setOverriddenKeyEvents(int, int)} should be called for each key code with the
40  * overridden {@link KeyEventType} bit value set, and the corresponding method,
41  * {@link #onSingleTap(KeyEvent)}, {@link #onDoubleTap(KeyEvent)},
42  * {@link #onTripleTap(KeyEvent)}, {@link #onLongPress(KeyEvent)} should be implemented.
43  * <p>
44  * Note: When instantiating this class, {@link MediaSessionService} will only use the constructor
45  * without any parameters.
46  */
47 // TODO: Move this class to apex/media/
48 public abstract class MediaKeyDispatcher {
49     @IntDef(flag = true, value = {
50             KEY_EVENT_SINGLE_TAP,
51             KEY_EVENT_DOUBLE_TAP,
52             KEY_EVENT_TRIPLE_TAP,
53             KEY_EVENT_LONG_PRESS
54     })
55     @Retention(RetentionPolicy.SOURCE)
56     @interface KeyEventType {}
57     static final int KEY_EVENT_SINGLE_TAP = 1 << 0;
58     static final int KEY_EVENT_DOUBLE_TAP = 1 << 1;
59     static final int KEY_EVENT_TRIPLE_TAP = 1 << 2;
60     static final int KEY_EVENT_LONG_PRESS = 1 << 3;
61 
62     private Map<Integer, Integer> mOverriddenKeyEvents;
63 
MediaKeyDispatcher(Context context)64     public MediaKeyDispatcher(Context context) {
65         // Constructor used for reflection
66         mOverriddenKeyEvents = new HashMap<>();
67         mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_PLAY, 0);
68         mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_PAUSE, 0);
69         mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, 0);
70         mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MUTE, 0);
71         mOverriddenKeyEvents.put(KeyEvent.KEYCODE_HEADSETHOOK, 0);
72         mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_STOP, 0);
73         mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_NEXT, 0);
74         mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_PREVIOUS, 0);
75         mOverriddenKeyEvents.put(KeyEvent.KEYCODE_VOLUME_DOWN, 0);
76         mOverriddenKeyEvents.put(KeyEvent.KEYCODE_VOLUME_UP, 0);
77         mOverriddenKeyEvents.put(KeyEvent.KEYCODE_VOLUME_MUTE, 0);
78     }
79 
80     // TODO: Move this method into MediaSessionPolicyProvider.java for better readability.
81     /**
82      * Implement this to customize the logic for which MediaSession should consume which key event.
83      *
84      * Note: This session will have greater priority over the {@link PendingIntent} returned from
85      * {@link #getMediaButtonReceiver(KeyEvent, int, boolean)}.
86      *
87      * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons.
88      * @param uid the uid value retrieved by calling {@link Binder#getCallingUid()} from
89      *         {@link ISessionManager#dispatchMediaKeyEvent(String, boolean, KeyEvent, boolean)}
90      * @param asSystemService {@code true} if the event came from the system service via hardware
91      *         devices. {@code false} if the event came from the app process through key injection.
92      * @return a {@link MediaSession.Token} instance that should consume the given key event.
93      */
94     @Nullable
getMediaSession(@onNull KeyEvent keyEvent, int uid, boolean asSystemService)95     MediaSession.Token getMediaSession(@NonNull KeyEvent keyEvent, int uid,
96             boolean asSystemService) {
97         return null;
98     }
99 
100     /**
101      * Implement this to customize the logic for which MediaButtonReceiver should consume a
102      * dispatched key event.
103      * <p>
104      * This pending intent will have lower priority over the {@link MediaSession.Token}
105      * returned from {@link #getMediaSession(KeyEvent, int, boolean)}.
106      * <p>
107      * Use a pending intent with an explicit intent; setting a pending intent with an implicit
108      * intent that cannot be resolved to a certain component name will fail.
109      *
110      * @return a {@link PendingIntent} instance that should receive the dispatched key event.
111      */
112     @Nullable
getMediaButtonReceiver(@onNull KeyEvent keyEvent, int uid, boolean asSystemService)113     PendingIntent getMediaButtonReceiver(@NonNull KeyEvent keyEvent, int uid,
114             boolean asSystemService) {
115         return null;
116     }
117 
118     /**
119      * Gets the map of key code -> {@link KeyEventType} that have been overridden.
120      * <p>
121      * The list of valid key codes are the following:
122      * <ul>
123      * <li> {@link KeyEvent#KEYCODE_MEDIA_PLAY}
124      * <li> {@link KeyEvent#KEYCODE_MEDIA_PAUSE}
125      * <li> {@link KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE}
126      * <li> {@link KeyEvent#KEYCODE_MUTE}
127      * <li> {@link KeyEvent#KEYCODE_HEADSETHOOK}
128      * <li> {@link KeyEvent#KEYCODE_MEDIA_STOP}
129      * <li> {@link KeyEvent#KEYCODE_MEDIA_NEXT}
130      * <li> {@link KeyEvent#KEYCODE_MEDIA_PREVIOUS}
131      * <li> {@link KeyEvent#KEYCODE_VOLUME_UP}
132      * <li> {@link KeyEvent#KEYCODE_VOLUME_DOWN}
133      * <li> {@link KeyEvent#KEYCODE_VOLUME_MUTE}
134      * </ul>
135      * @see {@link KeyEvent#isMediaSessionKey(int)}
136      */
getOverriddenKeyEvents()137     @KeyEventType Map<Integer, Integer> getOverriddenKeyEvents() {
138         return mOverriddenKeyEvents;
139     }
140 
isSingleTapOverridden(@eyEventType int overriddenKeyEvents)141     static boolean isSingleTapOverridden(@KeyEventType int overriddenKeyEvents) {
142         return (overriddenKeyEvents & MediaKeyDispatcher.KEY_EVENT_SINGLE_TAP) != 0;
143     }
144 
isDoubleTapOverridden(@eyEventType int overriddenKeyEvents)145     static boolean isDoubleTapOverridden(@KeyEventType int overriddenKeyEvents) {
146         return (overriddenKeyEvents & MediaKeyDispatcher.KEY_EVENT_DOUBLE_TAP) != 0;
147     }
148 
isTripleTapOverridden(@eyEventType int overriddenKeyEvents)149     static boolean isTripleTapOverridden(@KeyEventType int overriddenKeyEvents) {
150         return (overriddenKeyEvents & MediaKeyDispatcher.KEY_EVENT_TRIPLE_TAP) != 0;
151     }
152 
isLongPressOverridden(@eyEventType int overriddenKeyEvents)153     static boolean isLongPressOverridden(@KeyEventType int overriddenKeyEvents) {
154         return (overriddenKeyEvents & MediaKeyDispatcher.KEY_EVENT_LONG_PRESS) != 0;
155     }
156 
157     /**
158      * Sets the value of the given key event type flagged with overridden {@link KeyEventType} to
159      * the given key code. If called multiple times for the same key code, will be overwritten to
160      * the most recently called {@link KeyEventType} value.
161      * <p>
162      * The list of valid key codes are the following:
163      * <ul>
164      * <li> {@link KeyEvent#KEYCODE_MEDIA_PLAY}
165      * <li> {@link KeyEvent#KEYCODE_MEDIA_PAUSE}
166      * <li> {@link KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE}
167      * <li> {@link KeyEvent#KEYCODE_MUTE}
168      * <li> {@link KeyEvent#KEYCODE_HEADSETHOOK}
169      * <li> {@link KeyEvent#KEYCODE_MEDIA_STOP}
170      * <li> {@link KeyEvent#KEYCODE_MEDIA_NEXT}
171      * <li> {@link KeyEvent#KEYCODE_MEDIA_PREVIOUS}
172      * <li> {@link KeyEvent#KEYCODE_VOLUME_DOWN}
173      * <li> {@link KeyEvent#KEYCODE_VOLUME_UP}
174      * <li> {@link KeyEvent#KEYCODE_VOLUME_MUTE}
175      * </ul>
176      * @see {@link KeyEvent#isMediaSessionKey(int)}
177      * @param keyCode
178      */
setOverriddenKeyEvents(int keyCode, @KeyEventType int keyEventType)179     void setOverriddenKeyEvents(int keyCode, @KeyEventType int keyEventType) {
180         mOverriddenKeyEvents.put(keyCode, keyEventType);
181     }
182 
183     /**
184      * Customized implementation for single tap event. Will be run if
185      * {@link #KEY_EVENT_SINGLE_TAP} flag is on for the corresponding key code from
186      * {@link #getOverriddenKeyEvents()}.
187      *
188      * It is considered a single tap if only one {@link KeyEvent} with the same
189      * {@link KeyEvent#getKeyCode()} is dispatched within
190      * {@link ViewConfiguration#getMultiPressTimeout()} milliseconds. Change the
191      * {@link android.provider.Settings.Secure#MULTI_PRESS_TIMEOUT} value to adjust the interval.
192      *
193      * Note: This will only be called once with the {@link KeyEvent#ACTION_UP} KeyEvent.
194      *
195      * @param keyEvent
196      */
onSingleTap(KeyEvent keyEvent)197     void onSingleTap(KeyEvent keyEvent) {
198     }
199 
200     /**
201      * Customized implementation for double tap event. Will be run if
202      * {@link #KEY_EVENT_DOUBLE_TAP} flag is on for the corresponding key code from
203      * {@link #getOverriddenKeyEvents()}.
204      *
205      * It is considered a double tap if two {@link KeyEvent}s with the same
206      * {@link KeyEvent#getKeyCode()} are dispatched within
207      * {@link ViewConfiguration#getMultiPressTimeout()} milliseconds of each other. Change the
208      * {@link android.provider.Settings.Secure#MULTI_PRESS_TIMEOUT} value to adjust the interval.
209      *
210      * Note: This will only be called once with the {@link KeyEvent#ACTION_UP} KeyEvent.
211      *
212      * @param keyEvent
213      */
onDoubleTap(KeyEvent keyEvent)214     void onDoubleTap(KeyEvent keyEvent) {
215     }
216 
217     /**
218      * Customized implementation for triple tap event. Will be run if
219      * {@link #KEY_EVENT_TRIPLE_TAP} flag is on for the corresponding key code from
220      * {@link #getOverriddenKeyEvents()}.
221      *
222      * It is considered a triple tap if three {@link KeyEvent}s with the same
223      * {@link KeyEvent#getKeyCode()} are dispatched within
224      * {@link ViewConfiguration#getMultiPressTimeout()} milliseconds of each other. Change the
225      * {@link android.provider.Settings.Secure#MULTI_PRESS_TIMEOUT} value to adjust the interval.
226      *
227      * Note: This will only be called once with the {@link KeyEvent#ACTION_UP} KeyEvent.
228      *
229      * @param keyEvent
230      */
onTripleTap(KeyEvent keyEvent)231     void onTripleTap(KeyEvent keyEvent) {
232     }
233 
234     /**
235      * Customized implementation for long press event. Will be run if
236      * {@link #KEY_EVENT_LONG_PRESS} flag is on for the corresponding key code from
237      * {@link #getOverriddenKeyEvents()}.
238      *
239      * It is considered a long press if an {@link KeyEvent#ACTION_DOWN} key event is followed by
240      * another {@link KeyEvent#ACTION_DOWN} key event with {@link KeyEvent#FLAG_LONG_PRESS}
241      * enabled, and an {@link KeyEvent#getRepeatCount()} that is equal to 1.
242      *
243      * Note: This will be called for the following key events:
244      * <ul>
245      *   <li>A {@link KeyEvent#ACTION_DOWN} KeyEvent with {@link KeyEvent#FLAG_LONG_PRESS} and
246      *   {@link KeyEvent#getRepeatCount()} equal to 1</li>
247      *   <li>Multiple {@link KeyEvent#ACTION_DOWN} KeyEvents with increasing
248      *   {@link KeyEvent#getRepeatCount()}</li>
249      *   <li>A {@link KeyEvent#ACTION_UP} KeyEvent</li>
250      * </ul>
251      *
252      * @param keyEvent
253      */
onLongPress(KeyEvent keyEvent)254     void onLongPress(KeyEvent keyEvent) {
255     }
256 }
257