copy license file to /usr/share/license
[platform/core/system/power-manager.git] / pm_key_filter.c
1 /*
2  * power-manager
3  * Copyright (c) 2012 Samsung Electronics Co., Ltd.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16 */
17
18
19 #include <glib.h>
20 #include <stdio.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <stdbool.h>
24
25 #include <vconf.h>
26 #include <sysman.h>
27
28 #include "util.h"
29 #include "pm_core.h"
30 #include "pm_poll.h"
31
32 #include <linux/input.h>
33 #ifndef KEY_SCREENLOCK
34 #define KEY_SCREENLOCK          0x98
35 #endif
36
37 #define PREDEF_PWROFF_POPUP     "pwroff-popup"
38 #define PREDEF_LEAVESLEEP       "leavesleep"
39 #define PREDEF_POWEROFF         "poweroff"
40
41 #define USEC_PER_SEC                    1000000
42 #define LONG_PRESS_INTERVAL             1000000 /* 1000 ms */
43 #define COMBINATION_INTERVAL            300000  /* 300 ms */
44 #define POWER_KEY_PRESS_IGNORE_TIME     700000  /* 700 ms */
45
46 #define KEY_RELEASED            0
47 #define KEY_PRESSED             1
48 #define KEY_BEING_PRESSED       2
49
50 #define KEY_COMBINATION_STOP            0
51 #define KEY_COMBINATION_START           1
52 #define KEY_COMBINATION_SCREENCAPTURE   2
53
54 static struct timeval pressed_time;
55 static guint longkey_timeout_id = 0;
56 static guint combination_timeout_id = 0;
57 static int cancel_lcdoff;
58 static int key_combination = KEY_COMBINATION_STOP;
59 static int powerkey_ignored = false;
60
61 void unlock()
62 {
63         vconf_set_int(VCONFKEY_IDLE_LOCK_STATE, VCONFKEY_IDLE_UNLOCK);
64 }
65
66 static inline int current_state_in_on()
67 {
68         return (cur_state == S_LCDDIM || cur_state == S_NORMAL);
69 }
70
71 static void longkey_pressed()
72 {
73         int rc = -1, val = 0;
74         LOGINFO("Power key long pressed!");
75         cancel_lcdoff = 1;
76
77         rc = vconf_get_int(VCONFKEY_TESTMODE_POWER_OFF_POPUP, &val);
78
79         if (rc < 0 || val != 1) {
80                 if (sysman_call_predef_action(PREDEF_PWROFF_POPUP, 0) <
81                                 0)
82                         LOGERR("poweroff popup exec failed");
83         } else {
84                 if (sysman_call_predef_action(PREDEF_POWEROFF, 0) < 0) {
85                         LOGERR("poweroff exec failed");
86                         system("poweroff");
87                 }
88
89         }
90         (*g_pm_callback) (INPUT_POLL_EVENT, NULL);
91 }
92
93 static gboolean longkey_pressed_cb(gpointer data)
94 {
95         longkey_pressed();
96         longkey_timeout_id = 0;
97
98         return FALSE;
99 }
100
101 static gboolean combination_failed_cb(gpointer data)
102 {
103         key_combination = KEY_COMBINATION_STOP;
104         combination_timeout_id = 0;
105
106         return FALSE;
107 }
108
109 static unsigned long timediff_usec(struct timeval t1, struct timeval t2)
110 {
111         unsigned long udiff;
112
113         udiff = (t2.tv_sec - t1.tv_sec) * USEC_PER_SEC;
114         udiff += (t2.tv_usec - t1.tv_usec);
115
116         return udiff;
117 }
118
119 static void stop_key_combination()
120 {
121         key_combination = KEY_COMBINATION_STOP;
122         if (combination_timeout_id > 0) {
123                 g_source_remove(combination_timeout_id);
124                 combination_timeout_id = 0;
125         }
126 }
127
128 static int process_power_key(struct input_event *pinput)
129 {
130         int ignore = true;
131
132         switch (pinput->value) {
133         case KEY_RELEASED:
134                 if (current_state_in_on() && !cancel_lcdoff &&
135                         !(key_combination == KEY_COMBINATION_SCREENCAPTURE)) {
136                         check_processes(S_LCDOFF);
137                         check_processes(S_LCDDIM);
138
139                         if (!check_holdkey_block(S_LCDOFF) &&
140                                 !check_holdkey_block(S_LCDDIM)) {
141                                 delete_condition(S_LCDOFF);
142                                 delete_condition(S_LCDDIM);
143                                 /* LCD off forcly */
144                                 recv_data.pid = -1;
145                                 recv_data.cond = 0x400;
146                                 (*g_pm_callback)(PM_CONTROL_EVENT, &recv_data);
147                         }
148                 } else {
149                         if (!powerkey_ignored)
150                                 ignore = false;
151                 }
152
153                 stop_key_combination();
154                 cancel_lcdoff = 0;
155                 if (longkey_timeout_id > 0) {
156                         g_source_remove(longkey_timeout_id);
157                         longkey_timeout_id = 0;
158                 }
159                 break;
160         case KEY_PRESSED:
161                 if (timediff_usec(pressed_time, pinput->time) <
162                     POWER_KEY_PRESS_IGNORE_TIME) {
163                         LOGINFO("power key double pressed ignored");
164                         powerkey_ignored = true;
165                         break;
166                 } else {
167                         powerkey_ignored = false;
168                 }
169                 LOGINFO("power key pressed");
170                 pressed_time.tv_sec = (pinput->time).tv_sec;
171                 pressed_time.tv_usec = (pinput->time).tv_usec;
172                 if (key_combination == KEY_COMBINATION_STOP) {
173                         /* add long key timer */
174                         longkey_timeout_id = g_timeout_add_full(
175                                         G_PRIORITY_DEFAULT,
176                                         LONG_PRESS_INTERVAL / 1000,
177                                         (GSourceFunc)longkey_pressed_cb,
178                                         NULL, NULL);
179                         key_combination = KEY_COMBINATION_START;
180                         combination_timeout_id = g_timeout_add_full(
181                                         G_PRIORITY_DEFAULT,
182                                         COMBINATION_INTERVAL / 1000,
183                                         (GSourceFunc)combination_failed_cb,
184                                         NULL, NULL);
185                 } else if (key_combination == KEY_COMBINATION_START) {
186                         if (combination_timeout_id > 0) {
187                                 g_source_remove(combination_timeout_id);
188                                 combination_timeout_id = 0;
189                         }
190                         LOGINFO("capture mode");
191                         key_combination = KEY_COMBINATION_SCREENCAPTURE;
192                         ignore = false;
193                 }
194                 break;
195         case KEY_BEING_PRESSED:
196                 if (timediff_usec(pressed_time, pinput->time) >
197                         LONG_PRESS_INTERVAL)
198                         longkey_pressed();
199                 break;
200         }
201
202         return ignore;
203 }
204
205 static int process_volumedown_key(struct input_event *pinput)
206 {
207         int ignore = true;
208
209         if (pinput->value == KEY_PRESSED) {
210                 if (key_combination == KEY_COMBINATION_STOP) {
211                         key_combination = KEY_COMBINATION_START;
212                         combination_timeout_id = g_timeout_add_full(
213                                 G_PRIORITY_DEFAULT,
214                                 COMBINATION_INTERVAL / 1000,
215                                 (GSourceFunc)combination_failed_cb,
216                                 NULL, NULL);
217                 } else if (key_combination == KEY_COMBINATION_START) {
218                         if (combination_timeout_id > 0) {
219                                 g_source_remove(combination_timeout_id);
220                                 combination_timeout_id = 0;
221                         }
222                         LOGINFO("capture mode");
223                         key_combination = KEY_COMBINATION_SCREENCAPTURE;
224                         ignore = false;
225                 }
226         } else if (pinput->value == KEY_RELEASED) {
227                 if (key_combination != KEY_COMBINATION_SCREENCAPTURE) {
228                         stop_key_combination();
229                         if (current_state_in_on())
230                                 ignore = false;
231                 }
232         }
233
234         return ignore;
235 }
236
237 static int check_key(struct input_event *pinput)
238 {
239         int ignore = true;
240
241         switch (pinput->code) {
242         case KEY_POWER:
243                 ignore = process_power_key(pinput);
244                 break;
245         case KEY_VOLUMEDOWN:
246                 ignore = process_volumedown_key(pinput);
247                 break;
248         case KEY_VOLUMEUP:
249         case KEY_CAMERA:
250         case KEY_EXIT:
251         case KEY_PHONE:
252         case KEY_CONFIG:
253         case KEY_SEARCH:
254                 stop_key_combination();
255                 if (current_state_in_on())
256                         ignore = false;
257                 break;
258         case KEY_SCREENLOCK:
259         case 0x1DB:
260         case 0x1DC:
261         case 0x1DD:
262         case 0x1DE:
263                 stop_key_combination();
264                 break;
265         default:
266                 stop_key_combination();
267                 ignore = false;
268         }
269
270         return ignore;
271 }
272
273 int check_key_filter(int length, char buf[])
274 {
275         struct input_event *pinput;
276         int ignore = true;
277         int idx = 0;
278
279         do {
280                 pinput = (struct input_event *)&buf[idx];
281                 switch (pinput->type) {
282                 case EV_KEY:
283                         ignore = check_key(pinput);
284                         break;
285                 case EV_REL:
286                         ignore = false;
287                         break;
288                 case EV_ABS:
289                         if (current_state_in_on())
290                                 ignore = false;
291                         break;
292                 }
293
294                 idx += sizeof(struct input_event);
295                 if (ignore == true && length <= idx)
296                         return 1;
297         } while (length > idx);
298
299         return 0;
300 }
301