Skip to content

Commit

Permalink
Allow bypassing stuck proximity sensor via multiple power button presses
Browse files Browse the repository at this point in the history
If proximity sensor gets stuck to "covered" state due to dirt/fault
it is possible that the whole phone is practically unusable.

While the sensor still needs to be cleaned or possibly repaired for fully
normal behavior, make it possible for users to recover from this state by
by forcing the proximity sensor to "uncovered" state when the power key
is repeatedly pressed.

By default this requires 3 power key presses within 333 ms from
each other. Customization can be done via mcetool options

      --set-powerkey-ps-override-count=<press-count>
      --set-powerkey-ps-override-timeout=<ms>

[mce] Allow bypassing stuck proximity sensor via multiple power button presses
  • Loading branch information
spiiroin committed Sep 8, 2014
1 parent c16cd9f commit d6a7d31
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 5 deletions.
2 changes: 2 additions & 0 deletions .depend
Expand Up @@ -5,6 +5,7 @@ builtin-gconf.o:\
mce-io.h\
mce-log.h\
modules/memnotify.h\
powerkey.h\

builtin-gconf.pic.o:\
builtin-gconf.c\
Expand All @@ -13,6 +14,7 @@ builtin-gconf.pic.o:\
mce-io.h\
mce-log.h\
modules/memnotify.h\
powerkey.h\

datapipe.o:\
datapipe.c\
Expand Down
18 changes: 14 additions & 4 deletions builtin-gconf.c
Expand Up @@ -22,6 +22,8 @@
#include "mce-io.h"
#include "mce-dbus.h"

#include "powerkey.h"

#include "modules/memnotify.h"

#include <stdlib.h>
Expand Down Expand Up @@ -1461,17 +1463,25 @@ static const setting_t gconf_defaults[] =
.def = "2",
},
{
// MCE_GCONF_POWERKEY_MODE @ powerkey.h
.key = "/system/osso/dsm/powerkey/mode",
.key = MCE_GCONF_POWERKEY_MODE,
.type = "i",
.def = "1",
},
{
// MCE_GCONF_POWERKEY_BLANKING_MODE @ powerkey.h
.key = "/system/osso/dsm/powerkey/blanking_mode",
.key = MCE_GCONF_POWERKEY_BLANKING_MODE,
.type = "i",
.def = "0", // = PWRKEY_BLANK_TO_OFF
},
{
.key = MCE_GCONF_POWERKEY_PS_OVERRIDE_COUNT,
.type = "i",
.def = "3",
},
{
.key = MCE_GCONF_POWERKEY_PS_OVERRIDE_TIMEOUT,
.type = "i",
.def = "333",
},
{
.key = MCE_GCONF_MEMNOTIFY_WARNING_USED,
.type = "i",
Expand Down
144 changes: 143 additions & 1 deletion powerkey.c
Expand Up @@ -127,6 +127,18 @@ static gint powerkey_blanking_mode = PWRKEY_BLANK_TO_OFF;
/** GConf callback ID for powerkey_blanking_mode */
static guint powerkey_blanking_mode_cb_id = 0;

/** Power key press count for proximity sensor override */
static gint powerkey_ps_override_count = 3;

/** GConf callback ID for powerkey_ps_override_count */
static guint powerkey_ps_override_count_cb_id = 0;

/** Maximum time between power key presses for proximity sensor override */
static gint powerkey_ps_override_timeout = 333;

/** GConf callback ID for powerkey_ps_override_timeout */
static guint powerkey_ps_override_timeout_cb_id = 0;

/** GConf callback for powerkey related settings
*
* @param gcc (not used)
Expand Down Expand Up @@ -161,6 +173,18 @@ static void powerkey_gconf_cb(GConfClient *const gcc, const guint id,
mce_log(LL_NOTICE, "powerkey_blanking_mode: %d -> %d",
old, powerkey_blanking_mode);
}
else if( id == powerkey_ps_override_count_cb_id ) {
gint old = powerkey_ps_override_count;
powerkey_ps_override_count = gconf_value_get_int(gcv);
mce_log(LL_NOTICE, "powerkey_ps_override_count: %d -> %d",
old, powerkey_ps_override_count);
}
else if( id == powerkey_ps_override_timeout_cb_id ) {
gint old = powerkey_ps_override_timeout;
powerkey_ps_override_timeout = gconf_value_get_int(gcv);
mce_log(LL_NOTICE, "powerkey_ps_override_timeout: %d -> %d",
old, powerkey_ps_override_timeout);
}
else {
mce_log(LL_WARN, "Spurious GConf value received; confused!");
}
Expand Down Expand Up @@ -190,6 +214,24 @@ static void powerkey_gconf_init(void)
mce_gconf_get_int(MCE_GCONF_POWERKEY_BLANKING_MODE,
&powerkey_blanking_mode);

/* Power key press count for proximity sensor override */
mce_gconf_notifier_add(MCE_GCONF_POWERKEY_PATH,
MCE_GCONF_POWERKEY_PS_OVERRIDE_COUNT,
powerkey_gconf_cb,
&powerkey_ps_override_count_cb_id);

mce_gconf_get_int(MCE_GCONF_POWERKEY_PS_OVERRIDE_COUNT,
&powerkey_ps_override_count);

/* Maximum time between power key presses for ps override */
mce_gconf_notifier_add(MCE_GCONF_POWERKEY_PATH,
MCE_GCONF_POWERKEY_PS_OVERRIDE_TIMEOUT,
powerkey_gconf_cb,
&powerkey_ps_override_timeout_cb_id);

mce_gconf_get_int(MCE_GCONF_POWERKEY_PS_OVERRIDE_TIMEOUT,
&powerkey_ps_override_timeout);

}

/** Remove gconf change notifiers
Expand All @@ -203,6 +245,14 @@ static void powerkey_gconf_quit(void)
/* Power key press blanking mode */
mce_gconf_notifier_remove(powerkey_blanking_mode_cb_id),
powerkey_blanking_mode_cb_id = 0;

/* Power key press blanking mode */
mce_gconf_notifier_remove(powerkey_ps_override_count_cb_id),
powerkey_ps_override_count_cb_id = 0;

/* Power key press blanking mode */
mce_gconf_notifier_remove(powerkey_ps_override_timeout_cb_id),
powerkey_ps_override_timeout_cb_id = 0;
}

/** Helper for sending powerkey feedback dbus signal
Expand All @@ -217,6 +267,92 @@ static void powerkey_send_feedback_signal(const char *sig)
DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID);
}

/** Get CLOCK_BOOTTIME time stamp in milliseconds
*/
static int64_t powerkey_get_boot_tick(void)
{
int64_t res = 0;

struct timespec ts;

if( clock_gettime(CLOCK_BOOTTIME, &ts) == 0 ) {
res = ts.tv_sec;
res *= 1000;
res += ts.tv_nsec / 1000000;
}

return res;
}

/** Provide an emergency way out from stuck proximity sensor
*
* If the proximity sensor is dirty/faulty and stuck to "covered"
* state, it can leave the phone in a state where it is impossible
* to do anything about incoming call, ringing alarm.
*
* To offer somekind of remedy for the situation, this function
* allows user to force proximity sensor to "uncovered" state
* by rapidly pressing power button several times.
*/
static void powerkey_ps_override_evaluate(void)
{
static int64_t t_last = 0;
static gint count = 0;

/* If the feature is disabled, just reset the counter */
if( powerkey_ps_override_count <= 0 ||
powerkey_ps_override_timeout <= 0 ) {
t_last = 0, count = 0;
goto EXIT;
}

cover_state_t proximity_sensor_state =
datapipe_get_gint(proximity_sensor_pipe);

/* If the sensor is not covered, just reset the counter */
if( proximity_sensor_state != COVER_CLOSED ) {
t_last = 0, count = 0;
goto EXIT;
}

int64_t t_now = powerkey_get_boot_tick();

/* If the previous power key press was too far in
* the past, start counting from zero again */

if( t_now > t_last + powerkey_ps_override_timeout ) {
mce_log(LL_DEBUG, "ps override count restarted");
count = 0;
}

t_last = t_now;

/* If configured number of power key presses within the time
* limits has been reached, force proximity sensor state to
* "uncovered".
*
* This should allow touch input ungrabbing and turning
* display on during incoming call / alarm.
*
* If sensor gets unstuck and new proximity readings are
* received, this override will be automatically undone.
*/
if( ++count == powerkey_ps_override_count ) {
mce_log(LL_CRIT, "assuming stuck proximity sensor;"
" faking uncover event");
execute_datapipe(&proximity_sensor_pipe,
GINT_TO_POINTER(COVER_OPEN),
USE_INDATA, CACHE_INDATA);
t_last = 0, count = 0;
}
else
mce_log(LL_DEBUG, "ps override count = %d", count);

EXIT:

return;
}

/** Should power key action be ignored predicate
*/
static bool powerkey_ignore_action(void)
Expand Down Expand Up @@ -296,7 +432,6 @@ static bool powerkey_ignore_action(void)
}

EXIT:

return ignore_powerkey;
}

Expand Down Expand Up @@ -806,6 +941,13 @@ static void powerkey_trigger(gconstpointer const data)
} else if (ev->value == 0) {
mce_log(LL_DEVEL, "[powerkey] released");

/* Detect repeated power key pressing while
* proximity sensor is covered; assume it means
* the sensor is stuck and user wants to be able
* to turn on the display regardless of the sensor
* state */
powerkey_ps_override_evaluate();

/* Short key press */
if (powerkey_timeout_cb_id != 0) {
handle_shortpress();
Expand Down
6 changes: 6 additions & 0 deletions powerkey.h
Expand Up @@ -32,6 +32,12 @@
/** Path to the powerkey blanking mode GConf setting */
# define MCE_GCONF_POWERKEY_BLANKING_MODE MCE_GCONF_POWERKEY_PATH "/blanking_mode"

/** Powerkey press count for proximity sensor override */
# define MCE_GCONF_POWERKEY_PS_OVERRIDE_COUNT MCE_GCONF_POWERKEY_PATH "/ps_override_count"

/** Maximum delay between powerkey presses for ps override */
# define MCE_GCONF_POWERKEY_PS_OVERRIDE_TIMEOUT MCE_GCONF_POWERKEY_PATH "/ps_override_timeout"

/** Power key action enable modes */
typedef enum
{
Expand Down
80 changes: 80 additions & 0 deletions tools/mcetool.c
Expand Up @@ -2465,6 +2465,68 @@ static void xmce_get_powerkey_blanking(void)
printf("%-"PAD1"s %s \n", "Powerkey blanking mode:", txt ?: "unknown");
}

/** Set powerkey proximity override press count
*
* @param args string that can be parsed to number
*/
static bool xmce_set_ps_override_count(const char *args)
{
const char *key = MCE_GCONF_POWERKEY_PS_OVERRIDE_COUNT;
gint val = xmce_parse_integer(args);
mcetool_gconf_set_int(key, val);
return true;
}

/** Get current powerkey proximity override press count
*/
static void xmce_get_ps_override_count(void)
{
const char *tag = "Powerkey ps override count:";
const char *key = MCE_GCONF_POWERKEY_PS_OVERRIDE_COUNT;
gint val = 0;
char txt[64];

if( !mcetool_gconf_get_int(key, &val) )
snprintf(txt, sizeof txt, "unknown");
else if( val <= 0 )
snprintf(txt, sizeof txt, "disabled");
else
snprintf(txt, sizeof txt, "%d", val);

printf("%-"PAD1"s %s\n", tag, txt);
}

/** Set powerkey proximity override press timeout
*
* @param args string that can be parsed to number
*/
static bool xmce_set_ps_override_timeout(const char *args)
{
const char *key = MCE_GCONF_POWERKEY_PS_OVERRIDE_TIMEOUT;
gint val = xmce_parse_integer(args);
mcetool_gconf_set_int(key, val);
return true;
}

/** Get current powerkey proximity override press timeout
*/
static void xmce_get_ps_override_timeout(void)
{
const char *tag = "Powerkey ps override timeout:";
const char *key = MCE_GCONF_POWERKEY_PS_OVERRIDE_TIMEOUT;
gint val = 0;
char txt[64];

if( !mcetool_gconf_get_int(key, &val) )
snprintf(txt, sizeof txt, "unknown");
else if( val <= 0 )
snprintf(txt, sizeof txt, "disabled");
else
snprintf(txt, sizeof txt, "%d [ms]", val);

printf("%-"PAD1"s %s\n", tag, txt);
}

/* ------------------------------------------------------------------------- *
* display off request override
* ------------------------------------------------------------------------- */
Expand Down Expand Up @@ -3283,6 +3345,8 @@ static bool xmce_get_status(const char *args)
xmce_get_doubletap_wakeup();
xmce_get_powerkey_action();
xmce_get_powerkey_blanking();
xmce_get_ps_override_count();
xmce_get_ps_override_timeout();
xmce_get_display_off_override();
xmce_get_low_power_mode();
xmce_get_lpmui_triggering();
Expand Down Expand Up @@ -3623,6 +3687,22 @@ static const mce_opt_t options[] =
"set the doubletap blanking mode; valid modes are:\n"
"'off', 'lpm'\n"
},
{
.name = "set-powerkey-ps-override-count",
.with_arg = xmce_set_ps_override_count,
.values = "press-count",
.usage =
"set number of repeated power key presses needed to\n"
"override stuck proximity sensor; use 0 to disable\n"
},
{
.name = "set-powerkey-ps-override-timeout",
.with_arg = xmce_set_ps_override_timeout,
.values = "ms",
.usage =
"maximum delay between repeated power key presses that\n"
"can override stuck proximity sensor; use 0 to disable\n"
},
{
.name = "set-display-off-override",
.with_arg = xmce_set_display_off_override,
Expand Down

0 comments on commit d6a7d31

Please sign in to comment.