| 1 |
/* |
| 2 |
* This file is part of hildon-desktop |
| 3 |
* |
| 4 |
* Copyright (C) 2008 Nokia Corporation. |
| 5 |
* |
| 6 |
* Author: Marc Ordinas i Llopis <marc.ordinasillopis@collabora.co.uk> |
| 7 |
* |
| 8 |
* This library is free software; you can redistribute it and/or |
| 9 |
* modify it under the terms of the GNU Lesser General Public License |
| 10 |
* version 2.1 as published by the Free Software Foundation. |
| 11 |
* |
| 12 |
* This library is distributed in the hope that it will be useful, but |
| 13 |
* WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 |
* Lesser General Public License for more details. |
| 16 |
* |
| 17 |
* You should have received a copy of the GNU Lesser General Public |
| 18 |
* License along with this library; if not, write to the Free Software |
| 19 |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
| 20 |
* 02110-1301 USA |
| 21 |
* |
| 22 |
*/ |
| 23 |
|
| 24 |
#ifdef HAVE_CONFIG_H |
| 25 |
#include "config.h" |
| 26 |
#endif |
| 27 |
|
| 28 |
#include "hildon-desktop.h" |
| 29 |
#include "hd-app-mgr.h" |
| 30 |
#include "hd-app-mgr-glue.h" |
| 31 |
#include <errno.h> |
| 32 |
#include <fcntl.h> |
| 33 |
#include <signal.h> |
| 34 |
#include <string.h> |
| 35 |
#include <stdio.h> |
| 36 |
#include <unistd.h> |
| 37 |
#include <sys/resource.h> |
| 38 |
#include <sys/types.h> |
| 39 |
|
| 40 |
#include <dbus/dbus.h> |
| 41 |
#include <dbus/dbus-glib.h> |
| 42 |
#include <dbus/dbus-glib-bindings.h> |
| 43 |
#include <dbus/dbus-glib-lowlevel.h> |
| 44 |
#include <gconf/gconf-client.h> |
| 45 |
#include <mce/dbus-names.h> |
| 46 |
#include <mce/mode-names.h> |
| 47 |
#include "hd-launcher.h" |
| 48 |
#include "hd-launcher-tree.h" |
| 49 |
#include "home/hd-render-manager.h" |
| 50 |
#include "hd-transition.h" |
| 51 |
#include "hd-wm.h" |
| 52 |
|
| 53 |
#undef G_LOG_DOMAIN |
| 54 |
#define G_LOG_DOMAIN "hd-app-mgr" |
| 55 |
|
| 56 |
#define I_(str) (g_intern_static_string ((str))) |
| 57 |
|
| 58 |
typedef enum |
| 59 |
{ |
| 60 |
QUEUE_PRESTARTABLE, |
| 61 |
QUEUE_PRESTARTED, |
| 62 |
QUEUE_HIBERNATABLE, |
| 63 |
QUEUE_HIBERNATED, |
| 64 |
|
| 65 |
NUM_QUEUES |
| 66 |
} HdAppMgrQueue; |
| 67 |
|
| 68 |
/* Prestarting depends on the env var HILDON_DESKTOP_APPS_PRESTART and the |
| 69 |
* amount of /proc/sys/vm/lowmem_free_pages up to |
| 70 |
* /proc/sys/vm/lowmem_notify_low_pages. |
| 71 |
* not set|false|no - Never prestart apps. |
| 72 |
* yes|auto|0 - Prestart if there are more free pages than stated in |
| 73 |
* /proc/sys/vm/lowmem_notify_low_pages. |
| 74 |
* number - Prestart if there are more than this number of free pages. |
| 75 |
*/ |
| 76 |
typedef enum |
| 77 |
{ |
| 78 |
PRESTART_NEVER, |
| 79 |
PRESTART_AUTO, |
| 80 |
PRESTART_ALWAYS /* Used in scratchbox where we don't have memory limits. */ |
| 81 |
} HdAppMgrPrestartMode; |
| 82 |
|
| 83 |
/* Trying to launch an app can have different results. */ |
| 84 |
typedef enum |
| 85 |
{ |
| 86 |
LAUNCH_OK, |
| 87 |
LAUNCH_FAILED, |
| 88 |
LAUNCH_NO_MEM |
| 89 |
} HdAppMgrLaunchResult; |
| 90 |
|
| 91 |
struct _HdAppMgrPrivate |
| 92 |
{ |
| 93 |
HdLauncherTree *tree; |
| 94 |
|
| 95 |
DBusGProxy *dbus_proxy; |
| 96 |
|
| 97 |
/* All the running apps we know about. */ |
| 98 |
GList *running_apps; |
| 99 |
|
| 100 |
/* Each one of these lists contain different HdRunningApps. */ |
| 101 |
GQueue *queues[NUM_QUEUES]; |
| 102 |
|
| 103 |
/* Is the state check already looping? */ |
| 104 |
gboolean state_check_looping; |
| 105 |
|
| 106 |
/* Memory limits. */ |
| 107 |
HdAppMgrPrestartMode prestart_mode; |
| 108 |
size_t prestart_required_pages; |
| 109 |
size_t launch_required_pages; |
| 110 |
size_t notify_low_pages; |
| 111 |
size_t notify_high_pages; |
| 112 |
size_t nr_decay_pages; |
| 113 |
|
| 114 |
/* Memory status and prestarting flags.*/ |
| 115 |
gboolean bg_killing:1; |
| 116 |
gboolean lowmem:1; |
| 117 |
gboolean init_done:1; |
| 118 |
gboolean prestarting_stopped:1; |
| 119 |
gboolean prestarting; |
| 120 |
|
| 121 |
/* Flags for showing CallUI. */ |
| 122 |
GConfClient *gconf_client; |
| 123 |
gboolean portrait; |
| 124 |
gboolean unlocked; |
| 125 |
gboolean slide_closed; |
| 126 |
gboolean disable_callui; |
| 127 |
gboolean accel_enabled; |
| 128 |
}; |
| 129 |
|
| 130 |
#define HD_APP_MGR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ |
| 131 |
HD_TYPE_APP_MGR, HdAppMgrPrivate)) |
| 132 |
|
| 133 |
/* Signals */ |
| 134 |
enum |
| 135 |
{ |
| 136 |
APP_LAUNCHED, |
| 137 |
APP_RELAUNCHED, |
| 138 |
APP_SHOWN, |
| 139 |
APP_LOADING_FAIL, |
| 140 |
APP_CRASHED, |
| 141 |
NOT_ENOUGH_MEMORY, /* The boolean argument tells if it was waking up. */ |
| 142 |
|
| 143 |
LAST_SIGNAL |
| 144 |
}; |
| 145 |
static guint app_mgr_signals[LAST_SIGNAL] = {0, }; |
| 146 |
|
| 147 |
G_DEFINE_TYPE (HdAppMgr, hd_app_mgr, G_TYPE_OBJECT); |
| 148 |
|
| 149 |
/* Memory usage */ |
| 150 |
#define LOWMEM_PROC_ALLOWED "/proc/sys/vm/lowmem_allowed_pages" |
| 151 |
#define LOWMEM_PROC_USED "/proc/sys/vm/lowmem_used_pages" |
| 152 |
#define LOWMEM_PROC_FREE "/proc/sys/vm/lowmem_free_pages" |
| 153 |
#define LOWMEM_PROC_NOTIFY_LOW "/proc/sys/vm/lowmem_notify_low_pages" |
| 154 |
#define LOWMEM_PROC_NOTIFY_HIGH "/proc/sys/vm/lowmem_notify_high_pages" |
| 155 |
#define LOWMEM_PROC_NR_DECAY "/proc/sys/vm/lowmem_nr_decay_pages" |
| 156 |
|
| 157 |
#define LOADAVG_MAX (1.0) |
| 158 |
#define STATE_CHECK_INTERVAL (1) |
| 159 |
#define LOADING_TIMEOUT (10) |
| 160 |
#define INIT_DONE_TIMEOUT (5) |
| 161 |
|
| 162 |
#define PRESTART_ENV_VAR "HILDON_DESKTOP_APPS_PRESTART" |
| 163 |
#define NSIZE ((size_t)(-1)) |
| 164 |
#define PRESTART_ENV_AUTO ((size_t)(-2)) |
| 165 |
#define PRESTART_ENV_NEVER ((size_t)(-3)) |
| 166 |
/* This is used in scratchbox. */ |
| 167 |
#define PRESTART_ENV_ALWAYS ((size_t)(-4)) |
| 168 |
|
| 169 |
/* DBus names */ |
| 170 |
#define OSSO_BUS_ROOT "com.nokia" |
| 171 |
#define OSSO_BUS_TOP "top_application" |
| 172 |
#define PATH_NAME_LEN 255 |
| 173 |
#define DBUS_NAMEOWNERCHANGED_SIGNAL_NAME "NameOwnerChanged" |
| 174 |
#define HD_APP_MGR_DBUS_PATH "/com/nokia/HildonDesktop/AppMgr" |
| 175 |
#define HD_APP_MGR_DBUS_NAME "com.nokia.HildonDesktop.AppMgr" |
| 176 |
|
| 177 |
#define LOWMEM_ON_SIGNAL_INTERFACE "com.nokia.ke_recv.lowmem_on" |
| 178 |
#define LOWMEM_ON_SIGNAL_PATH "/com/nokia/ke_recv/lowmem_on" |
| 179 |
#define LOWMEM_ON_SIGNAL_NAME "lowmem_on" |
| 180 |
|
| 181 |
#define LOWMEM_OFF_SIGNAL_INTERFACE "com.nokia.ke_recv.lowmem_off" |
| 182 |
#define LOWMEM_OFF_SIGNAL_PATH "/com/nokia/ke_recv/lowmem_off" |
| 183 |
#define LOWMEM_OFF_SIGNAL_NAME "lowmem_off" |
| 184 |
|
| 185 |
#define BGKILL_ON_SIGNAL_INTERFACE "com.nokia.ke_recv.bgkill_on" |
| 186 |
#define BGKILL_ON_SIGNAL_PATH "/com/nokia/ke_recv/bgkill_on" |
| 187 |
#define BGKILL_ON_SIGNAL_NAME "bgkill_on" |
| 188 |
|
| 189 |
#define BGKILL_OFF_SIGNAL_INTERFACE "com.nokia.ke_recv.bgkill_off" |
| 190 |
#define BGKILL_OFF_SIGNAL_PATH "/com/nokia/ke_recv/bgkill_off" |
| 191 |
#define BGKILL_OFF_SIGNAL_NAME "bgkill_off" |
| 192 |
|
| 193 |
#define INIT_DONE_SIGNAL_INTERFACE "com.nokia.startup.signal" |
| 194 |
#define INIT_DONE_SIGNAL_PATH "/com/nokia/startup/signal" |
| 195 |
#define INIT_DONE_SIGNAL_NAME "init_done" |
| 196 |
|
| 197 |
#define MAEMO_LAUNCHER_IFACE "org.maemo.launcher" |
| 198 |
#define MAEMO_LAUNCHER_PATH "/org/maemo/launcher" |
| 199 |
#define MAEMO_LAUNCHER_APP_DIED_SIGNAL_NAME "ApplicationDied" |
| 200 |
|
| 201 |
/* Signals for showing CallUI. */ |
| 202 |
#define CALLUI_INTERFACE "com.nokia.CallUI" |
| 203 |
#define CALLUI_PORTRAIT_TIMEOUT 1 |
| 204 |
#define GCONF_SLIDE_OPEN_DIR "/system/osso/af" |
| 205 |
#define GCONF_SLIDE_OPEN_KEY "/system/osso/af/slide-open" |
| 206 |
#define GCONF_DISABLE_CALLUI_DIR "/apps/osso/hildon-desktop" |
| 207 |
#define GCONF_DISABLE_CALLUI_KEY "/apps/osso/hildon-desktop/disable_phone_gesture" |
| 208 |
|
| 209 |
/* Forward declarations */ |
| 210 |
static void hd_app_mgr_dispose (GObject *gobject); |
| 211 |
|
| 212 |
static void hd_app_mgr_populate_tree_finished (HdLauncherTree *tree, |
| 213 |
gpointer data); |
| 214 |
|
| 215 |
HdAppMgrLaunchResult hd_app_mgr_start (HdRunningApp *app); |
| 216 |
HdAppMgrLaunchResult hd_app_mgr_relaunch (HdRunningApp *app); |
| 217 |
HdAppMgrLaunchResult hd_app_mgr_wakeup (HdRunningApp *app); |
| 218 |
gboolean hd_app_mgr_prestart (HdRunningApp *app); |
| 219 |
gboolean hd_app_mgr_hibernate (HdRunningApp *app); |
| 220 |
|
| 221 |
static gboolean hd_app_mgr_service_top (const gchar *service, |
| 222 |
const gchar *param); |
| 223 |
|
| 224 |
void hd_app_mgr_prestartable (HdRunningApp *app, gboolean prestartable); |
| 225 |
|
| 226 |
static void hd_app_mgr_add_to_queue (HdAppMgrQueue queue, |
| 227 |
HdRunningApp *app); |
| 228 |
static void hd_app_mgr_remove_from_queue (HdAppMgrQueue queue, |
| 229 |
HdRunningApp *app); |
| 230 |
static void hd_app_mgr_move_queue (HdAppMgrQueue queue_from, |
| 231 |
HdAppMgrQueue queue_to, |
| 232 |
HdRunningApp *app); |
| 233 |
|
| 234 |
static size_t hd_app_mgr_read_lowmem (const gchar *filename); |
| 235 |
static HdAppMgrPrestartMode |
| 236 |
hd_app_mgr_setup_prestart (size_t low_pages, |
| 237 |
size_t nr_decay_pages, |
| 238 |
size_t *prestart_required_pages); |
| 239 |
static void |
| 240 |
hd_app_mgr_setup_launch (size_t high_pages, |
| 241 |
size_t nr_decay_pages, |
| 242 |
size_t *launch_required_pages); |
| 243 |
static gboolean hd_app_mgr_can_launch (HdLauncherApp *launcher); |
| 244 |
static gboolean hd_app_mgr_can_prestart (HdLauncherApp *launcher); |
| 245 |
static void hd_app_mgr_hdrm_state_change (gpointer hdrm, |
| 246 |
GParamSpec *pspec, |
| 247 |
HdAppMgrPrivate *priv); |
| 248 |
static void hd_app_mgr_state_check (void); |
| 249 |
static gboolean hd_app_mgr_state_check_loop (gpointer data); |
| 250 |
|
| 251 |
static void hd_app_mgr_dbus_name_owner_changed (DBusGProxy *proxy, |
| 252 |
const char *name, |
| 253 |
const char *old_owner, |
| 254 |
const char *new_owner, |
| 255 |
gpointer data); |
| 256 |
static DBusHandlerResult hd_app_mgr_dbus_app_died (DBusConnection *conn, |
| 257 |
DBusMessage *msg, |
| 258 |
void *data); |
| 259 |
static DBusHandlerResult hd_app_mgr_dbus_signal_handler (DBusConnection *conn, |
| 260 |
DBusMessage *msg, |
| 261 |
void *data); |
| 262 |
static void hd_app_mgr_gconf_value_changed (GConfClient *client, |
| 263 |
guint cnxn_id, |
| 264 |
GConfEntry *entry, |
| 265 |
gpointer user_data); |
| 266 |
static gboolean hd_app_mgr_show_callui_cb (gpointer data); |
| 267 |
|
| 268 |
static void hd_app_mgr_request_app_pid (HdRunningApp *app); |
| 269 |
static gboolean hd_app_mgr_loading_timeout (HdRunningApp *app); |
| 270 |
static gboolean hd_app_mgr_init_done_timeout (HdAppMgr *self); |
| 271 |
|
| 272 |
static void hd_app_mgr_kill_all_prestarted (void); |
| 273 |
|
| 274 |
/* The HdLauncher singleton */ |
| 275 |
static HdAppMgr *the_app_mgr = NULL; |
| 276 |
|
| 277 |
HdAppMgr * |
| 278 |
hd_app_mgr_get (void) |
| 279 |
{ |
| 280 |
if (G_UNLIKELY (!the_app_mgr)) |
| 281 |
{ /* "Protect" against reentrancy. */ |
| 282 |
static gboolean under_construction; |
| 283 |
|
| 284 |
g_assert(!under_construction); |
| 285 |
under_construction = TRUE; |
| 286 |
the_app_mgr = g_object_new (HD_TYPE_APP_MGR, NULL); |
| 287 |
} |
| 288 |
return the_app_mgr; |
| 289 |
} |
| 290 |
|
| 291 |
static void |
| 292 |
hd_app_mgr_class_init (HdAppMgrClass *klass) |
| 293 |
{ |
| 294 |
GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
| 295 |
|
| 296 |
g_type_class_add_private (klass, sizeof (HdAppMgrPrivate)); |
| 297 |
|
| 298 |
gobject_class->dispose = hd_app_mgr_dispose; |
| 299 |
|
| 300 |
app_mgr_signals[APP_LAUNCHED] = |
| 301 |
g_signal_new (I_("application-launched"), |
| 302 |
HD_TYPE_APP_MGR, |
| 303 |
G_SIGNAL_RUN_FIRST, |
| 304 |
0, NULL, NULL, |
| 305 |
g_cclosure_marshal_VOID__OBJECT, |
| 306 |
G_TYPE_NONE, 1, HD_TYPE_LAUNCHER_APP); |
| 307 |
app_mgr_signals[APP_RELAUNCHED] = |
| 308 |
g_signal_new (I_("application-relaunched"), |
| 309 |
HD_TYPE_APP_MGR, |
| 310 |
G_SIGNAL_RUN_FIRST, |
| 311 |
0, NULL, NULL, |
| 312 |
g_cclosure_marshal_VOID__OBJECT, |
| 313 |
G_TYPE_NONE, 1, HD_TYPE_LAUNCHER_APP); |
| 314 |
app_mgr_signals[APP_SHOWN] = |
| 315 |
g_signal_new (I_("application-appeared"), |
| 316 |
HD_TYPE_APP_MGR, |
| 317 |
G_SIGNAL_RUN_FIRST, |
| 318 |
0, NULL, NULL, |
| 319 |
g_cclosure_marshal_VOID__OBJECT, |
| 320 |
G_TYPE_NONE, 1, HD_TYPE_LAUNCHER_APP); |
| 321 |
app_mgr_signals[APP_LOADING_FAIL] = |
| 322 |
g_signal_new (I_("application-loading-fail"), |
| 323 |
HD_TYPE_APP_MGR, |
| 324 |
G_SIGNAL_RUN_FIRST, |
| 325 |
0, NULL, NULL, |
| 326 |
g_cclosure_marshal_VOID__OBJECT, |
| 327 |
G_TYPE_NONE, 1, HD_TYPE_LAUNCHER_APP); |
| 328 |
app_mgr_signals[APP_CRASHED] = |
| 329 |
g_signal_new (I_("application-crashed"), |
| 330 |
HD_TYPE_APP_MGR, |
| 331 |
G_SIGNAL_RUN_FIRST, |
| 332 |
0, NULL, NULL, |
| 333 |
g_cclosure_marshal_VOID__OBJECT, |
| 334 |
G_TYPE_NONE, 1, HD_TYPE_LAUNCHER_APP); |
| 335 |
app_mgr_signals[NOT_ENOUGH_MEMORY] = |
| 336 |
g_signal_new (I_("not-enough-memory"), |
| 337 |
HD_TYPE_APP_MGR, |
| 338 |
G_SIGNAL_RUN_FIRST, |
| 339 |
0, NULL, NULL, |
| 340 |
g_cclosure_marshal_VOID__BOOLEAN, |
| 341 |
G_TYPE_NONE, 1, G_TYPE_BOOLEAN); |
| 342 |
|
| 343 |
/* Bind D-Bus info. */ |
| 344 |
dbus_g_object_type_install_info (HD_TYPE_APP_MGR, |
| 345 |
&dbus_glib_hd_app_mgr_object_info); |
| 346 |
} |
| 347 |
|
| 348 |
static gchar * |
| 349 |
_hd_app_mgr_build_match (const gchar *interface, |
| 350 |
const gchar *member) |
| 351 |
{ |
| 352 |
gchar *arg; |
| 353 |
if (member) |
| 354 |
arg = g_strdup_printf("type='signal', interface='%s', member='%s'", |
| 355 |
interface, member); |
| 356 |
else |
| 357 |
arg = g_strdup_printf("type='signal', interface='%s'", interface); |
| 358 |
|
| 359 |
return arg; |
| 360 |
} |
| 361 |
|
| 362 |
/* TODO: Extend to use, in addition to an interface, a path and a signal |
| 363 |
* name. |
| 364 |
*/ |
| 365 |
static void |
| 366 |
hd_app_mgr_dbus_add_signal_match (DBusConnection *conn, |
| 367 |
const gchar *interface, |
| 368 |
const gchar *member) |
| 369 |
{ |
| 370 |
gchar *arg = _hd_app_mgr_build_match (interface, member); |
| 371 |
dbus_bus_add_match (conn, arg, NULL); |
| 372 |
g_free (arg); |
| 373 |
} |
| 374 |
|
| 375 |
static void |
| 376 |
hd_app_mgr_dbus_remove_signal_match (DBusConnection *conn, |
| 377 |
const gchar *interface, |
| 378 |
const gchar *member) |
| 379 |
{ |
| 380 |
gchar *arg = _hd_app_mgr_build_match (interface, member); |
| 381 |
dbus_bus_remove_match (conn, arg, NULL); |
| 382 |
g_free (arg); |
| 383 |
} |
| 384 |
|
| 385 |
static void |
| 386 |
hd_app_mgr_init (HdAppMgr *self) |
| 387 |
{ |
| 388 |
HdAppMgrPrivate *priv; |
| 389 |
|
| 390 |
self->priv = priv = HD_APP_MGR_GET_PRIVATE (self); |
| 391 |
|
| 392 |
/* Initialize the queues. */ |
| 393 |
for (int i = 0; i < NUM_QUEUES; i++) |
| 394 |
priv->queues[i] = g_queue_new (); |
| 395 |
|
| 396 |
priv->tree = hd_launcher_tree_new (); |
| 397 |
hd_launcher_tree_ensure_user_menu (); |
| 398 |
g_signal_connect (priv->tree, "finished", |
| 399 |
G_CALLBACK (hd_app_mgr_populate_tree_finished), |
| 400 |
self); |
| 401 |
hd_launcher_tree_populate (priv->tree); |
| 402 |
|
| 403 |
/* NOTE: Can we assume this when we start up? */ |
| 404 |
priv->unlocked = TRUE; |
| 405 |
priv->gconf_client = gconf_client_get_default (); |
| 406 |
if (priv->gconf_client) |
| 407 |
{ |
| 408 |
priv->slide_closed = !gconf_client_get_bool (priv->gconf_client, |
| 409 |
GCONF_SLIDE_OPEN_KEY, |
| 410 |
NULL); |
| 411 |
gconf_client_add_dir (priv->gconf_client, GCONF_SLIDE_OPEN_DIR, |
| 412 |
GCONF_CLIENT_PRELOAD_NONE, NULL); |
| 413 |
gconf_client_notify_add (priv->gconf_client, GCONF_SLIDE_OPEN_KEY, |
| 414 |
hd_app_mgr_gconf_value_changed, |
| 415 |
(gpointer) self, |
| 416 |
NULL, NULL); |
| 417 |
priv->disable_callui = gconf_client_get_bool (priv->gconf_client, |
| 418 |
GCONF_DISABLE_CALLUI_KEY, |
| 419 |
NULL); |
| 420 |
/* We don't call |
| 421 |
hd_app_mgr_mce_activate_accel_if_needed (); |
| 422 |
here because hdrm is not ready yet. */ |
| 423 |
gconf_client_add_dir (priv->gconf_client, GCONF_DISABLE_CALLUI_DIR, |
| 424 |
GCONF_CLIENT_PRELOAD_NONE, NULL); |
| 425 |
gconf_client_notify_add (priv->gconf_client, GCONF_DISABLE_CALLUI_KEY, |
| 426 |
hd_app_mgr_gconf_value_changed, |
| 427 |
(gpointer) self, |
| 428 |
NULL, NULL); |
| 429 |
} |
| 430 |
|
| 431 |
/* Start memory limits. */ |
| 432 |
priv->notify_low_pages = hd_app_mgr_read_lowmem (LOWMEM_PROC_NOTIFY_LOW); |
| 433 |
priv->notify_high_pages = hd_app_mgr_read_lowmem (LOWMEM_PROC_NOTIFY_HIGH); |
| 434 |
priv->nr_decay_pages = hd_app_mgr_read_lowmem (LOWMEM_PROC_NR_DECAY); |
| 435 |
priv->prestart_mode = hd_app_mgr_setup_prestart (priv->notify_low_pages, |
| 436 |
priv->nr_decay_pages, |
| 437 |
&priv->prestart_required_pages); |
| 438 |
hd_app_mgr_setup_launch (priv->notify_high_pages, |
| 439 |
priv->nr_decay_pages, |
| 440 |
&priv->launch_required_pages); |
| 441 |
|
| 442 |
/* Start dbus signal tracking. */ |
| 443 |
DBusGConnection *connection; |
| 444 |
connection = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); |
| 445 |
if (connection) |
| 446 |
{ |
| 447 |
/* Connect to NameOwnerChanged to track closing applications. */ |
| 448 |
priv->dbus_proxy = dbus_g_proxy_new_for_name (connection, |
| 449 |
DBUS_SERVICE_DBUS, |
| 450 |
DBUS_PATH_DBUS, |
| 451 |
DBUS_INTERFACE_DBUS); |
| 452 |
if (priv->dbus_proxy) |
| 453 |
{ |
| 454 |
dbus_g_proxy_add_signal (priv->dbus_proxy, |
| 455 |
DBUS_NAMEOWNERCHANGED_SIGNAL_NAME, |
| 456 |
G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID); |
| 457 |
dbus_g_proxy_connect_signal (priv->dbus_proxy, |
| 458 |
DBUS_NAMEOWNERCHANGED_SIGNAL_NAME, |
| 459 |
(GCallback)hd_app_mgr_dbus_name_owner_changed, |
| 460 |
NULL, NULL); |
| 461 |
|
| 462 |
/* Serve the AppMgr interface. */ |
| 463 |
guint result; |
| 464 |
if (!org_freedesktop_DBus_request_name (priv->dbus_proxy, |
| 465 |
HD_APP_MGR_DBUS_NAME, |
| 466 |
DBUS_NAME_FLAG_DO_NOT_QUEUE, |
| 467 |
&result, |
| 468 |
NULL)) |
| 469 |
{ |
| 470 |
g_warning ("%s: Could not register name.\n", __FUNCTION__); |
| 471 |
} |
| 472 |
else |
| 473 |
{ |
| 474 |
dbus_g_connection_register_g_object (connection, |
| 475 |
HD_APP_MGR_DBUS_PATH, |
| 476 |
G_OBJECT (self)); |
| 477 |
} |
| 478 |
} |
| 479 |
else |
| 480 |
g_warning ("%s: Failed to proxy session dbus.", __FUNCTION__); |
| 481 |
|
| 482 |
/* Connect to the maemo launcher dbus interface. */ |
| 483 |
hd_app_mgr_dbus_add_signal_match ( |
| 484 |
dbus_g_connection_get_connection (connection), |
| 485 |
MAEMO_LAUNCHER_IFACE, |
| 486 |
MAEMO_LAUNCHER_APP_DIED_SIGNAL_NAME); |
| 487 |
dbus_connection_add_filter (dbus_g_connection_get_connection (connection), |
| 488 |
hd_app_mgr_dbus_app_died, |
| 489 |
self, NULL); |
| 490 |
} |
| 491 |
else |
| 492 |
g_warning ("%s: Failed to connect to session dbus.", __FUNCTION__); |
| 493 |
|
| 494 |
connection = NULL; |
| 495 |
|
| 496 |
/* Connect to the memory management signals. */ |
| 497 |
DBusConnection *sys_conn = dbus_bus_get (DBUS_BUS_SYSTEM, NULL); |
| 498 |
if (sys_conn) |
| 499 |
{ |
| 500 |
hd_app_mgr_dbus_add_signal_match (sys_conn, |
| 501 |
LOWMEM_ON_SIGNAL_INTERFACE, |
| 502 |
LOWMEM_ON_SIGNAL_NAME); |
| 503 |
hd_app_mgr_dbus_add_signal_match (sys_conn, |
| 504 |
LOWMEM_OFF_SIGNAL_INTERFACE, |
| 505 |
LOWMEM_OFF_SIGNAL_NAME); |
| 506 |
hd_app_mgr_dbus_add_signal_match (sys_conn, |
| 507 |
BGKILL_ON_SIGNAL_INTERFACE, |
| 508 |
BGKILL_ON_SIGNAL_NAME); |
| 509 |
hd_app_mgr_dbus_add_signal_match (sys_conn, |
| 510 |
BGKILL_OFF_SIGNAL_INTERFACE, |
| 511 |
BGKILL_OFF_SIGNAL_NAME); |
| 512 |
hd_app_mgr_dbus_add_signal_match (sys_conn, |
| 513 |
INIT_DONE_SIGNAL_INTERFACE, |
| 514 |
INIT_DONE_SIGNAL_NAME); |
| 515 |
dbus_connection_add_filter (sys_conn, |
| 516 |
hd_app_mgr_dbus_signal_handler, |
| 517 |
self, NULL); |
| 518 |
} |
| 519 |
else |
| 520 |
g_warning ("%s: Failed to connect to system dbus.\n", __FUNCTION__); |
| 521 |
|
| 522 |
/* Add a timeout in case init_done is never received. That can happen |
| 523 |
* when restarting, for example. |
| 524 |
*/ |
| 525 |
g_timeout_add_seconds (INIT_DONE_TIMEOUT, |
| 526 |
(GSourceFunc)hd_app_mgr_init_done_timeout, |
| 527 |
self); |
| 528 |
} |
| 529 |
|
| 530 |
void |
| 531 |
hd_app_mgr_set_render_manager (GObject *rendermgr) |
| 532 |
{ |
| 533 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (hd_app_mgr_get ()); |
| 534 |
|
| 535 |
/* Connect to state changes. */ |
| 536 |
g_signal_connect (rendermgr, "notify::state", |
| 537 |
G_CALLBACK (hd_app_mgr_hdrm_state_change), |
| 538 |
priv); |
| 539 |
} |
| 540 |
|
| 541 |
static void |
| 542 |
_hd_app_mgr_kill_prestarted (HdRunningApp *app, gpointer user_data) |
| 543 |
{ |
| 544 |
hd_app_mgr_kill (app); |
| 545 |
} |
| 546 |
|
| 547 |
static void |
| 548 |
hd_app_mgr_kill_all_prestarted () |
| 549 |
{ |
| 550 |
GQueue *prestarted; |
| 551 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (hd_app_mgr_get ()); |
| 552 |
|
| 553 |
prestarted = g_queue_copy (priv->queues[QUEUE_PRESTARTED]); |
| 554 |
g_queue_foreach (prestarted, |
| 555 |
(GFunc)_hd_app_mgr_kill_prestarted, |
| 556 |
NULL); |
| 557 |
g_queue_free (prestarted); |
| 558 |
} |
| 559 |
|
| 560 |
/* Called when exiting main() to close all prestarted apps. */ |
| 561 |
void |
| 562 |
hd_app_mgr_stop () |
| 563 |
{ |
| 564 |
if (!the_app_mgr) |
| 565 |
return; |
| 566 |
|
| 567 |
hd_app_mgr_kill_all_prestarted (); |
| 568 |
} |
| 569 |
|
| 570 |
static void |
| 571 |
hd_app_mgr_dispose (GObject *gobject) |
| 572 |
{ |
| 573 |
HdAppMgr *self = HD_APP_MGR (gobject); |
| 574 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (self); |
| 575 |
|
| 576 |
if (priv->dbus_proxy) |
| 577 |
{ |
| 578 |
g_object_unref (priv->dbus_proxy); |
| 579 |
priv->dbus_proxy = NULL; |
| 580 |
} |
| 581 |
|
| 582 |
if (priv->tree) |
| 583 |
{ |
| 584 |
g_object_unref (priv->tree); |
| 585 |
priv->tree = NULL; |
| 586 |
} |
| 587 |
|
| 588 |
if (priv->running_apps) |
| 589 |
{ |
| 590 |
g_list_foreach (priv->running_apps, (GFunc)g_object_unref, NULL); |
| 591 |
g_list_free (priv->running_apps); |
| 592 |
priv->running_apps = NULL; |
| 593 |
} |
| 594 |
|
| 595 |
for (int i = 0; i < NUM_QUEUES; i++) |
| 596 |
{ |
| 597 |
if (priv->queues[i]) |
| 598 |
{ |
| 599 |
g_queue_foreach (priv->queues[i], (GFunc)g_object_unref, NULL); |
| 600 |
g_queue_free (priv->queues[i]); |
| 601 |
priv->queues[i] = NULL; |
| 602 |
} |
| 603 |
} |
| 604 |
|
| 605 |
if (priv->gconf_client) |
| 606 |
{ |
| 607 |
gconf_client_remove_dir (priv->gconf_client, GCONF_SLIDE_OPEN_DIR, NULL); |
| 608 |
g_object_unref (G_OBJECT (priv->gconf_client)); |
| 609 |
priv->gconf_client = NULL; |
| 610 |
} |
| 611 |
|
| 612 |
G_OBJECT_CLASS (hd_app_mgr_parent_class)->dispose (gobject); |
| 613 |
} |
| 614 |
|
| 615 |
static gint |
| 616 |
_hd_app_mgr_compare_app_priority (gconstpointer a, |
| 617 |
gconstpointer b, |
| 618 |
gpointer user_data) |
| 619 |
{ |
| 620 |
HdRunningApp *a_rapp = HD_RUNNING_APP (a); |
| 621 |
HdRunningApp *b_rapp = HD_RUNNING_APP (b); |
| 622 |
HdLauncherApp *a_launcher = hd_running_app_get_launcher_app (a_rapp); |
| 623 |
HdLauncherApp *b_launcher = hd_running_app_get_launcher_app (b_rapp); |
| 624 |
|
| 625 |
if (!a_launcher && !b_launcher) |
| 626 |
return 0; |
| 627 |
if (!a_launcher) |
| 628 |
return -1; |
| 629 |
if (!b_launcher) |
| 630 |
return 1; |
| 631 |
|
| 632 |
gint a_priority = hd_launcher_app_get_priority (a_launcher); |
| 633 |
gint b_priority = hd_launcher_app_get_priority (b_launcher); |
| 634 |
|
| 635 |
return b_priority - a_priority; |
| 636 |
} |
| 637 |
|
| 638 |
static void |
| 639 |
hd_app_mgr_add_to_queue (HdAppMgrQueue queue, HdRunningApp *app) |
| 640 |
{ |
| 641 |
if (!app) |
| 642 |
return; |
| 643 |
|
| 644 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (hd_app_mgr_get ()); |
| 645 |
|
| 646 |
/* Check if app's already there. */ |
| 647 |
GList *link = g_queue_find (priv->queues[queue], app); |
| 648 |
if (link) |
| 649 |
return; |
| 650 |
|
| 651 |
g_queue_insert_sorted (priv->queues[queue], |
| 652 |
g_object_ref (app), |
| 653 |
_hd_app_mgr_compare_app_priority, |
| 654 |
NULL); |
| 655 |
} |
| 656 |
|
| 657 |
static void |
| 658 |
hd_app_mgr_remove_from_queue (HdAppMgrQueue queue, HdRunningApp *app) |
| 659 |
{ |
| 660 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (hd_app_mgr_get ()); |
| 661 |
GList *link = g_queue_find (priv->queues[queue], app); |
| 662 |
|
| 663 |
if (link) |
| 664 |
{ |
| 665 |
g_object_unref (app); |
| 666 |
g_queue_delete_link (priv->queues[queue], link); |
| 667 |
} |
| 668 |
} |
| 669 |
|
| 670 |
static void |
| 671 |
hd_app_mgr_move_queue (HdAppMgrQueue queue_from, |
| 672 |
HdAppMgrQueue queue_to, |
| 673 |
HdRunningApp *app) |
| 674 |
{ |
| 675 |
if (!app) |
| 676 |
return; |
| 677 |
|
| 678 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (hd_app_mgr_get ()); |
| 679 |
GList *link = g_queue_find (priv->queues[queue_from], app); |
| 680 |
|
| 681 |
if (link) |
| 682 |
{ |
| 683 |
g_queue_delete_link (priv->queues[queue_from], link); |
| 684 |
g_queue_insert_sorted (priv->queues[queue_to], |
| 685 |
app, |
| 686 |
_hd_app_mgr_compare_app_priority, |
| 687 |
NULL); |
| 688 |
|
| 689 |
} |
| 690 |
else |
| 691 |
hd_app_mgr_add_to_queue (queue_to, app); |
| 692 |
} |
| 693 |
|
| 694 |
void hd_app_mgr_prestartable (HdRunningApp *app, gboolean prestartable) |
| 695 |
{ |
| 696 |
if (prestartable) |
| 697 |
hd_app_mgr_add_to_queue (QUEUE_PRESTARTABLE, app); |
| 698 |
else |
| 699 |
hd_app_mgr_remove_from_queue (QUEUE_PRESTARTABLE, app); |
| 700 |
} |
| 701 |
|
| 702 |
void hd_app_mgr_hibernatable (HdRunningApp *app, gboolean hibernatable) |
| 703 |
{ |
| 704 |
/* We can only hibernate apps that have a dbus service. |
| 705 |
*/ |
| 706 |
HdLauncherApp *launcher = hd_running_app_get_launcher_app (app); |
| 707 |
if (!launcher || !hd_launcher_app_get_service (launcher)) |
| 708 |
return; |
| 709 |
|
| 710 |
g_debug ("%s: Making app %s %s hibernatable", __FUNCTION__, |
| 711 |
hd_launcher_app_get_service (launcher), |
| 712 |
hibernatable ? "really" : "not"); |
| 713 |
|
| 714 |
if (hibernatable) |
| 715 |
hd_app_mgr_add_to_queue (QUEUE_HIBERNATABLE, app); |
| 716 |
else |
| 717 |
hd_app_mgr_remove_from_queue (QUEUE_HIBERNATABLE, app); |
| 718 |
|
| 719 |
/* Go to state check, as marking an app hibernatable could mean we |
| 720 |
* have to bgkill it immediately. |
| 721 |
*/ |
| 722 |
hd_app_mgr_state_check (); |
| 723 |
} |
| 724 |
|
| 725 |
/* Application management */ |
| 726 |
|
| 727 |
static gint |
| 728 |
_hd_app_mgr_compare_app_launcher (HdRunningApp *a, |
| 729 |
HdLauncherApp *b) |
| 730 |
{ |
| 731 |
HdLauncherApp *a_launcher = hd_running_app_get_launcher_app (a); |
| 732 |
|
| 733 |
if (a_launcher == b) |
| 734 |
return 0; |
| 735 |
|
| 736 |
return -1; |
| 737 |
} |
| 738 |
|
| 739 |
/* This function either: |
| 740 |
* - Relaunches an app if already running. |
| 741 |
* - Wakes up an app if it's hibernating. |
| 742 |
* - Starts the app if not running. |
| 743 |
*/ |
| 744 |
gboolean |
| 745 |
hd_app_mgr_launch (HdLauncherApp *launcher) |
| 746 |
{ |
| 747 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (hd_app_mgr_get ()); |
| 748 |
gboolean result = FALSE; |
| 749 |
HdRunningApp *app = NULL; |
| 750 |
GList *link = NULL; |
| 751 |
|
| 752 |
/* Find if we already have a running app for this launcher. */ |
| 753 |
link = g_list_find_custom (priv->running_apps, launcher, |
| 754 |
(GCompareFunc)_hd_app_mgr_compare_app_launcher); |
| 755 |
if (link) |
| 756 |
/* We already have a running app for this one. */ |
| 757 |
app = HD_RUNNING_APP (link->data); |
| 758 |
else |
| 759 |
{ |
| 760 |
/* We need to create a new running app for it. */ |
| 761 |
app = hd_running_app_new (launcher); |
| 762 |
} |
| 763 |
|
| 764 |
result = hd_app_mgr_activate (app); |
| 765 |
|
| 766 |
if (!link) |
| 767 |
{ |
| 768 |
/* We just created this running app, so add to list or get rid of it. */ |
| 769 |
if (result) |
| 770 |
priv->running_apps = g_list_prepend (priv->running_apps, app); |
| 771 |
else |
| 772 |
g_object_unref (app); |
| 773 |
} |
| 774 |
|
| 775 |
return result; |
| 776 |
} |
| 777 |
|
| 778 |
static gdouble |
| 779 |
hd_app_mgr_timeout_backoff_factor () |
| 780 |
{ |
| 781 |
return hd_transition_get_double("loading_timeout", |
| 782 |
"load_average_factor", |
| 783 |
0.0); |
| 784 |
} |
| 785 |
|
| 786 |
/* |
| 787 |
* Returns the current system load average. |
| 788 |
* Returns a negative value iff the load average |
| 789 |
* cannot be found. |
| 790 |
*/ |
| 791 |
static gdouble |
| 792 |
hd_app_mgr_system_load_average (void) |
| 793 |
{ |
| 794 |
int fd = open ("/proc/loadavg", O_RDONLY); |
| 795 |
|
| 796 |
if (fd >= 0) |
| 797 |
{ |
| 798 |
char buffer[32]; |
| 799 |
int size = read (fd, buffer, sizeof(buffer) -1); |
| 800 |
|
| 801 |
close (fd); |
| 802 |
if (size > 0) |
| 803 |
{ |
| 804 |
gdouble load; |
| 805 |
buffer[size] = 0; |
| 806 |
load = g_ascii_strtod(buffer, NULL); |
| 807 |
|
| 808 |
return load; |
| 809 |
} |
| 810 |
} |
| 811 |
|
| 812 |
return -1.0; |
| 813 |
} |
| 814 |
|
| 815 |
/* This function either: |
| 816 |
* - Relaunches an app if already running. |
| 817 |
* - Wakes up an app if it's hibernating. |
| 818 |
* - Starts the app if not running. |
| 819 |
*/ |
| 820 |
gboolean |
| 821 |
hd_app_mgr_activate (HdRunningApp *app) |
| 822 |
{ |
| 823 |
HdAppMgrLaunchResult result = LAUNCH_FAILED; |
| 824 |
gboolean timer = FALSE; |
| 825 |
HdRunningAppState state; |
| 826 |
|
| 827 |
state = hd_running_app_get_state (app); |
| 828 |
switch (state) |
| 829 |
{ |
| 830 |
case HD_APP_STATE_INACTIVE: |
| 831 |
case HD_APP_STATE_PRESTARTED: |
| 832 |
result = hd_app_mgr_start (app); |
| 833 |
timer = TRUE; |
| 834 |
break; |
| 835 |
case HD_APP_STATE_SHOWN: |
| 836 |
result = !STATE_IS_APP (hd_render_manager_get_state ()) || app != hd_comp_mgr_client_get_app (hd_comp_mgr_get_current_client (hd_comp_mgr_get ())) |
| 837 |
? hd_app_mgr_relaunch (app) : LAUNCH_OK; |
| 838 |
break; |
| 839 |
case HD_APP_STATE_HIBERNATED: |
| 840 |
result = hd_app_mgr_wakeup (app); |
| 841 |
timer = TRUE; |
| 842 |
break; |
| 843 |
case HD_APP_STATE_LOADING: |
| 844 |
case HD_APP_STATE_WAKING: |
| 845 |
/* Send the launched signal so the loading screen will be shown again. */ |
| 846 |
g_signal_emit (hd_app_mgr_get (), app_mgr_signals[APP_LAUNCHED], |
| 847 |
0, hd_running_app_get_launcher_app (app), NULL); |
| 848 |
result = LAUNCH_OK; |
| 849 |
break; |
| 850 |
default: |
| 851 |
result = LAUNCH_FAILED; |
| 852 |
} |
| 853 |
|
| 854 |
switch (result) |
| 855 |
{ |
| 856 |
case LAUNCH_OK: |
| 857 |
if (timer) |
| 858 |
{ |
| 859 |
/* Start a loading timer. */ |
| 860 |
time_t now; |
| 861 |
gint timeout = (gint) (hd_app_mgr_timeout_backoff_factor () * |
| 862 |
hd_app_mgr_system_load_average ()); |
| 863 |
|
| 864 |
if (timeout < LOADING_TIMEOUT) |
| 865 |
{ |
| 866 |
timeout = LOADING_TIMEOUT; |
| 867 |
} |
| 868 |
|
| 869 |
time (&now); |
| 870 |
hd_running_app_set_last_launch (app, now); |
| 871 |
g_timeout_add_seconds (timeout, |
| 872 |
(GSourceFunc)hd_app_mgr_loading_timeout, |
| 873 |
g_object_ref (app)); |
| 874 |
} |
| 875 |
break; |
| 876 |
case LAUNCH_FAILED: |
| 877 |
g_signal_emit (hd_app_mgr_get (), app_mgr_signals[APP_LOADING_FAIL], |
| 878 |
0, hd_running_app_get_launcher_app (app), NULL); |
| 879 |
break; |
| 880 |
case LAUNCH_NO_MEM: |
| 881 |
g_signal_emit (hd_app_mgr_get (), app_mgr_signals[NOT_ENOUGH_MEMORY], 0, |
| 882 |
(state == HD_APP_STATE_HIBERNATED)); |
| 883 |
break; |
| 884 |
} |
| 885 |
|
| 886 |
return (result == LAUNCH_OK)? TRUE : FALSE; |
| 887 |
} |
| 888 |
|
| 889 |
static void |
| 890 |
_hd_app_mgr_child_exit (GPid pid, gint status, HdRunningApp *app) |
| 891 |
{ |
| 892 |
g_spawn_close_pid (pid); |
| 893 |
hd_app_mgr_app_closed (app); |
| 894 |
g_object_unref (app); |
| 895 |
} |
| 896 |
|
| 897 |
HdAppMgrLaunchResult |
| 898 |
hd_app_mgr_start (HdRunningApp *app) |
| 899 |
{ |
| 900 |
gboolean result = FALSE; |
| 901 |
HdLauncherApp *launcher = hd_running_app_get_launcher_app (app); |
| 902 |
if (!launcher) |
| 903 |
return LAUNCH_FAILED; |
| 904 |
|
| 905 |
const gchar *service = hd_launcher_app_get_service (launcher); |
| 906 |
const gchar *exec; |
| 907 |
|
| 908 |
if (!hd_app_mgr_can_launch (launcher)) |
| 909 |
return LAUNCH_NO_MEM; |
| 910 |
|
| 911 |
if (service) |
| 912 |
{ |
| 913 |
result = hd_app_mgr_service_top (service, NULL); |
| 914 |
if (result) |
| 915 |
{ |
| 916 |
/* As the app has been manually launched, stop considering it |
| 917 |
* for prestarting. |
| 918 |
*/ |
| 919 |
hd_app_mgr_prestartable (app, FALSE); |
| 920 |
} |
| 921 |
} |
| 922 |
else |
| 923 |
{ |
| 924 |
exec = hd_launcher_app_get_exec (launcher); |
| 925 |
if (exec) |
| 926 |
{ |
| 927 |
GPid pid = 0; |
| 928 |
result = hd_app_mgr_execute (exec, &pid, FALSE); |
| 929 |
if (result) |
| 930 |
{ |
| 931 |
hd_running_app_set_pid (app, pid); |
| 932 |
/* Watch the child. */ |
| 933 |
g_child_watch_add (pid, |
| 934 |
(GChildWatchFunc)_hd_app_mgr_child_exit, |
| 935 |
g_object_ref (app)); |
| 936 |
} |
| 937 |
} |
| 938 |
} |
| 939 |
|
| 940 |
if (result) |
| 941 |
{ |
| 942 |
hd_running_app_set_state (app, HD_APP_STATE_LOADING); |
| 943 |
g_signal_emit (hd_app_mgr_get (), app_mgr_signals[APP_LAUNCHED], |
| 944 |
0, launcher, NULL); |
| 945 |
} |
| 946 |
return result ? LAUNCH_OK : LAUNCH_FAILED; |
| 947 |
} |
| 948 |
|
| 949 |
HdAppMgrLaunchResult |
| 950 |
hd_app_mgr_relaunch (HdRunningApp *app) |
| 951 |
{ |
| 952 |
/* we have to call hd_app_mgr_service_top when we relaunch an app, |
| 953 |
* but we can't do it straight away because the change in focus pulls |
| 954 |
* us out of our transitions. Instead, we get HdSwitcher to call |
| 955 |
* hd_app_mgr_relaunch_set_top after the task navigator has zoomed in |
| 956 |
* on the application. */ |
| 957 |
|
| 958 |
HdLauncherApp *launcher = hd_running_app_get_launcher_app (app); |
| 959 |
if (launcher) |
| 960 |
g_signal_emit (hd_app_mgr_get (), app_mgr_signals[APP_RELAUNCHED], |
| 961 |
0, launcher, NULL); |
| 962 |
|
| 963 |
return LAUNCH_OK; |
| 964 |
} |
| 965 |
|
| 966 |
static gboolean |
| 967 |
hd_app_mgr_loading_timeout (HdRunningApp *app) |
| 968 |
{ |
| 969 |
time_t now; |
| 970 |
time (&now); |
| 971 |
HdRunningAppState state = hd_running_app_get_state (app); |
| 972 |
if ((state == HD_APP_STATE_LOADING || state == HD_APP_STATE_WAKING) && |
| 973 |
difftime (now, hd_running_app_get_last_launch (app)) >= LOADING_TIMEOUT) |
| 974 |
{ |
| 975 |
HdLauncherApp *launcher = hd_running_app_get_launcher_app (app); |
| 976 |
|
| 977 |
if (state == HD_APP_STATE_LOADING) |
| 978 |
/* If application hasn't appeared after this long, consider it |
| 979 |
* closed. */ |
| 980 |
hd_app_mgr_app_closed (app); |
| 981 |
/* But not if hibernating, as we may need the pid later. */ |
| 982 |
else |
| 983 |
hd_running_app_set_state (app, HD_APP_STATE_HIBERNATED); |
| 984 |
|
| 985 |
/* Tell the world, so something can be shown to the user. |
| 986 |
* Do this only if the app has a known service, because |
| 987 |
* it could be a command line. */ |
| 988 |
if (hd_running_app_get_service(app) != NULL) |
| 989 |
{ |
| 990 |
g_signal_emit (hd_app_mgr_get (), app_mgr_signals[APP_LOADING_FAIL], |
| 991 |
0, launcher, NULL); |
| 992 |
} |
| 993 |
} |
| 994 |
|
| 995 |
g_object_unref (app); |
| 996 |
return FALSE; |
| 997 |
} |
| 998 |
|
| 999 |
gboolean |
| 1000 |
hd_app_mgr_relaunch_set_top (HdLauncherApp *app) |
| 1001 |
{ |
| 1002 |
gboolean result = TRUE; |
| 1003 |
const gchar *service = hd_launcher_app_get_service (app); |
| 1004 |
|
| 1005 |
if (service) |
| 1006 |
{ |
| 1007 |
result = hd_app_mgr_service_top (service, NULL); |
| 1008 |
} |
| 1009 |
|
| 1010 |
/* If it's a plain old app, nothing to do. */ |
| 1011 |
return result; |
| 1012 |
} |
| 1013 |
|
| 1014 |
gboolean |
| 1015 |
hd_app_mgr_kill (HdRunningApp *app) |
| 1016 |
{ |
| 1017 |
GPid pid = hd_running_app_get_pid (app); |
| 1018 |
|
| 1019 |
if (!hd_running_app_is_executing (app)) |
| 1020 |
return FALSE; |
| 1021 |
|
| 1022 |
if (pid <= 0) |
| 1023 |
{ |
| 1024 |
g_warning ("%s: No pid for app %s\n", __FUNCTION__, |
| 1025 |
hd_launcher_item_get_id (HD_LAUNCHER_ITEM (app))); |
| 1026 |
return FALSE; |
| 1027 |
} |
| 1028 |
|
| 1029 |
if (kill (pid, SIGTERM) != 0) |
| 1030 |
return FALSE; |
| 1031 |
|
| 1032 |
return TRUE; |
| 1033 |
} |
| 1034 |
|
| 1035 |
void |
| 1036 |
hd_app_mgr_kill_all (void) |
| 1037 |
{ |
| 1038 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (hd_app_mgr_get ()); |
| 1039 |
/* We need to make a copy because the list is going to be changed. */ |
| 1040 |
GList *apps = g_list_copy(priv->running_apps); |
| 1041 |
while (apps) |
| 1042 |
{ |
| 1043 |
/* We only kill the shown apps. */ |
| 1044 |
if (hd_running_app_get_state (apps->data) == HD_APP_STATE_SHOWN) |
| 1045 |
hd_app_mgr_kill (apps->data); |
| 1046 |
apps = apps->next; |
| 1047 |
} |
| 1048 |
g_list_free(apps); |
| 1049 |
} |
| 1050 |
|
| 1051 |
void hd_app_mgr_app_opened (HdRunningApp *app) |
| 1052 |
{ |
| 1053 |
HdLauncherApp *launcher = hd_running_app_get_launcher_app (app); |
| 1054 |
hd_running_app_set_state (app, HD_APP_STATE_SHOWN); |
| 1055 |
|
| 1056 |
/* Signal that the app has appeared. |
| 1057 |
*/ |
| 1058 |
if (launcher) |
| 1059 |
g_signal_emit (hd_app_mgr_get (), app_mgr_signals[APP_SHOWN], |
| 1060 |
0, launcher, NULL); |
| 1061 |
|
| 1062 |
/* Remove it from prestarting lists, just in case it has been launched |
| 1063 |
* from somewhere else. |
| 1064 |
*/ |
| 1065 |
hd_app_mgr_remove_from_queue (QUEUE_HIBERNATED, app); |
| 1066 |
hd_app_mgr_remove_from_queue (QUEUE_PRESTARTED, app); |
| 1067 |
hd_app_mgr_remove_from_queue (QUEUE_PRESTARTABLE, app); |
| 1068 |
|
| 1069 |
} |
| 1070 |
|
| 1071 |
void |
| 1072 |
hd_app_mgr_app_closed (HdRunningApp *app) |
| 1073 |
{ |
| 1074 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (hd_app_mgr_get ()); |
| 1075 |
HdLauncherApp *launcher = hd_running_app_get_launcher_app (app); |
| 1076 |
HdRunningAppState state = hd_running_app_get_state (app); |
| 1077 |
|
| 1078 |
if (state == HD_APP_STATE_HIBERNATED) |
| 1079 |
{ |
| 1080 |
/* Do nothing with hibernating apps, as we need to keep them around. */ |
| 1081 |
return; |
| 1082 |
} |
| 1083 |
if (state == HD_APP_STATE_WAKING) |
| 1084 |
{ |
| 1085 |
/* If the user switches really fast, h-d can try to wake up an app |
| 1086 |
* that hasn't closed completely yet. As a dirty fix, try to |
| 1087 |
* wake it up again. */ |
| 1088 |
hd_running_app_set_state (app, HD_APP_STATE_HIBERNATED); |
| 1089 |
hd_app_mgr_activate (app); |
| 1090 |
return; |
| 1091 |
} |
| 1092 |
|
| 1093 |
/* Remove from anywhere we keep executing apps. */ |
| 1094 |
hd_app_mgr_remove_from_queue (QUEUE_PRESTARTED, app); |
| 1095 |
hd_app_mgr_remove_from_queue (QUEUE_HIBERNATED, app); |
| 1096 |
hd_app_mgr_remove_from_queue (QUEUE_HIBERNATABLE, app); |
| 1097 |
|
| 1098 |
hd_running_app_set_pid (app, 0); |
| 1099 |
hd_running_app_set_state (app, HD_APP_STATE_INACTIVE); |
| 1100 |
|
| 1101 |
if (launcher && |
| 1102 |
hd_launcher_app_get_prestart_mode (launcher) == HD_APP_PRESTART_ALWAYS) |
| 1103 |
{ |
| 1104 |
/* Add it to prestartable so it will be prestarted again. */ |
| 1105 |
hd_app_mgr_add_to_queue (QUEUE_PRESTARTABLE, app); |
| 1106 |
hd_app_mgr_state_check (); |
| 1107 |
} |
| 1108 |
else |
| 1109 |
{ |
| 1110 |
/* Take it out of the list of running apps. */ |
| 1111 |
GList *link = g_list_find (priv->running_apps, app); |
| 1112 |
if (link) |
| 1113 |
{ |
| 1114 |
g_object_unref (app); |
| 1115 |
priv->running_apps = g_list_delete_link (priv->running_apps, link); |
| 1116 |
} |
| 1117 |
} |
| 1118 |
} |
| 1119 |
|
| 1120 |
/* Called when an hibernating app is closed in the switcher. */ |
| 1121 |
void |
| 1122 |
hd_app_mgr_app_stop_hibernation (HdRunningApp *app) |
| 1123 |
{ |
| 1124 |
hd_running_app_set_state (app, HD_APP_STATE_INACTIVE); |
| 1125 |
hd_app_mgr_app_closed (app); |
| 1126 |
} |
| 1127 |
|
| 1128 |
static void |
| 1129 |
hd_app_mgr_populate_tree_finished (HdLauncherTree *tree, gpointer data) |
| 1130 |
{ |
| 1131 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (HD_APP_MGR (data)); |
| 1132 |
/* We need to copy thess lists because we'll be modifying them. */ |
| 1133 |
GList *apps = g_list_copy (priv->running_apps); |
| 1134 |
GList *items = g_list_copy (hd_launcher_tree_get_items (tree)); |
| 1135 |
GList *apps_to_free = apps; |
| 1136 |
GList *items_to_free = items; |
| 1137 |
|
| 1138 |
/* First, traverse the already running apps to see if their HdLauncherApp |
| 1139 |
* info has changed. |
| 1140 |
*/ |
| 1141 |
for (; apps; apps = apps->next) |
| 1142 |
{ |
| 1143 |
HdRunningApp *app = apps->data; |
| 1144 |
HdLauncherApp *old, *new; |
| 1145 |
old = hd_running_app_get_launcher_app (app); |
| 1146 |
if (!old) |
| 1147 |
/* TODO? Try to recognize newly installed but already running apps? */ |
| 1148 |
continue; |
| 1149 |
|
| 1150 |
new = HD_LAUNCHER_APP (hd_launcher_tree_find_item (tree, |
| 1151 |
hd_running_app_get_id (app))); |
| 1152 |
hd_running_app_set_launcher_app (app, new); |
| 1153 |
if (old && !new) |
| 1154 |
{ |
| 1155 |
/* The .desktop file no longer exists, but the app could be running. */ |
| 1156 |
HdRunningAppState state = hd_running_app_get_state (app); |
| 1157 |
if (state == HD_APP_STATE_PRESTARTED) |
| 1158 |
/* Kill it, as it shouldn't be prestarted. */ |
| 1159 |
hd_app_mgr_kill (app); |
| 1160 |
else if (state == HD_APP_STATE_INACTIVE) |
| 1161 |
/* What's it doing here? */ |
| 1162 |
hd_app_mgr_app_closed (app); |
| 1163 |
} |
| 1164 |
if (old && new) |
| 1165 |
{ |
| 1166 |
/* If the old was prestarted and the new one isn't, kill it. */ |
| 1167 |
if (hd_running_app_get_state (app) == HD_APP_STATE_PRESTARTED && |
| 1168 |
hd_launcher_app_get_prestart_mode (new) == HD_APP_PRESTART_NONE) |
| 1169 |
hd_app_mgr_kill (app); |
| 1170 |
} |
| 1171 |
} |
| 1172 |
|
| 1173 |
g_list_free (apps_to_free); |
| 1174 |
|
| 1175 |
/* Now we need to look if we have new prestarted apps. */ |
| 1176 |
for (; items; items = items->next) |
| 1177 |
{ |
| 1178 |
HdLauncherApp *launcher; |
| 1179 |
GList *link = NULL; |
| 1180 |
|
| 1181 |
if (hd_launcher_item_get_item_type (HD_LAUNCHER_ITEM (items->data)) != |
| 1182 |
HD_APPLICATION_LAUNCHER) |
| 1183 |
continue; |
| 1184 |
|
| 1185 |
launcher = HD_LAUNCHER_APP (items->data); |
| 1186 |
if (priv->prestart_mode == PRESTART_NEVER || |
| 1187 |
hd_launcher_app_get_prestart_mode(launcher) != HD_APP_PRESTART_ALWAYS) |
| 1188 |
continue; |
| 1189 |
|
| 1190 |
/* Look if we already have a running app for it. */ |
| 1191 |
link = g_list_find_custom (priv->running_apps, launcher, |
| 1192 |
(GCompareFunc)_hd_app_mgr_compare_app_launcher); |
| 1193 |
if (link) |
| 1194 |
/* We dealt with it before. */ |
| 1195 |
continue; |
| 1196 |
|
| 1197 |
/* Create a new running app for it. */ |
| 1198 |
HdRunningApp *app = hd_running_app_new (launcher); |
| 1199 |
priv->running_apps = g_list_prepend (priv->running_apps, app); |
| 1200 |
hd_app_mgr_prestartable (app, TRUE); |
| 1201 |
} |
| 1202 |
|
| 1203 |
g_list_free (items_to_free); |
| 1204 |
hd_app_mgr_state_check (); |
| 1205 |
} |
| 1206 |
|
| 1207 |
static void |
| 1208 |
_hd_app_mgr_prestart_cb (DBusGProxy *proxy, guint result, |
| 1209 |
GError *error, gpointer data) |
| 1210 |
{ |
| 1211 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (hd_app_mgr_get ()); |
| 1212 |
HdRunningApp *app = HD_RUNNING_APP (data); |
| 1213 |
|
| 1214 |
/* Prestarting can go ahead now. */ |
| 1215 |
priv->prestarting = FALSE; |
| 1216 |
|
| 1217 |
if (error || !result) |
| 1218 |
{ |
| 1219 |
g_warning ("%s: Couldn't prestart service %s, error: %s\n", |
| 1220 |
__FUNCTION__, hd_running_app_get_service (app), |
| 1221 |
error ? error->message : "no result"); |
| 1222 |
/* TODO: Check number of times this has been tried and stop after |
| 1223 |
* a while. |
| 1224 |
*/ |
| 1225 |
} |
| 1226 |
else |
| 1227 |
{ |
| 1228 |
g_debug ("%s: %s prestarted\n", __FUNCTION__, |
| 1229 |
hd_running_app_get_service (app)); |
| 1230 |
hd_running_app_set_state (app, HD_APP_STATE_PRESTARTED); |
| 1231 |
hd_app_mgr_add_to_queue (QUEUE_PRESTARTED, app); |
| 1232 |
if (!hd_running_app_get_pid (app)) |
| 1233 |
hd_app_mgr_request_app_pid (app); |
| 1234 |
} |
| 1235 |
} |
| 1236 |
|
| 1237 |
gboolean |
| 1238 |
hd_app_mgr_prestart (HdRunningApp *app) |
| 1239 |
{ |
| 1240 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (hd_app_mgr_get ()); |
| 1241 |
const gchar *service = hd_running_app_get_service (app); |
| 1242 |
|
| 1243 |
if (hd_running_app_is_executing (app)) |
| 1244 |
return TRUE; |
| 1245 |
|
| 1246 |
if (!service) |
| 1247 |
{ |
| 1248 |
g_warning ("%s: Can't prestart an app without service.\n", __FUNCTION__); |
| 1249 |
return FALSE; |
| 1250 |
} |
| 1251 |
|
| 1252 |
hd_app_mgr_remove_from_queue (QUEUE_PRESTARTABLE, app); |
| 1253 |
|
| 1254 |
g_debug ("%s: Starting to prestart %s\n", __FUNCTION__, |
| 1255 |
service); |
| 1256 |
priv->prestarting = TRUE; |
| 1257 |
org_freedesktop_DBus_start_service_by_name_async (priv->dbus_proxy, |
| 1258 |
service, 0, |
| 1259 |
_hd_app_mgr_prestart_cb, (gpointer)app); |
| 1260 |
|
| 1261 |
/* We always return true because we don't know the result at this point. */ |
| 1262 |
return TRUE; |
| 1263 |
} |
| 1264 |
|
| 1265 |
gboolean |
| 1266 |
hd_app_mgr_hibernate (HdRunningApp *app) |
| 1267 |
{ |
| 1268 |
const gchar *service = hd_running_app_get_service (app); |
| 1269 |
|
| 1270 |
if (!service) |
| 1271 |
/* Can't hibernate a non-dbus app. */ |
| 1272 |
return FALSE; |
| 1273 |
|
| 1274 |
if (hd_app_mgr_kill (app)) |
| 1275 |
{ |
| 1276 |
hd_running_app_set_state (app, HD_APP_STATE_HIBERNATED); |
| 1277 |
hd_app_mgr_move_queue (QUEUE_HIBERNATABLE, QUEUE_HIBERNATED, app); |
| 1278 |
hd_running_app_set_pid (app, 0); |
| 1279 |
hd_app_mgr_remove_from_queue (QUEUE_PRESTARTABLE, app); |
| 1280 |
} |
| 1281 |
else |
| 1282 |
{ |
| 1283 |
/* We couldn't kill it, so just take it out of the hibernatable queue. */ |
| 1284 |
hd_app_mgr_hibernatable (app, FALSE); |
| 1285 |
return FALSE; |
| 1286 |
} |
| 1287 |
|
| 1288 |
return TRUE; |
| 1289 |
} |
| 1290 |
|
| 1291 |
HdAppMgrLaunchResult |
| 1292 |
hd_app_mgr_wakeup (HdRunningApp *app) |
| 1293 |
{ |
| 1294 |
g_return_val_if_fail (app, FALSE); |
| 1295 |
|
| 1296 |
gboolean res = FALSE; |
| 1297 |
const gchar *service = hd_running_app_get_service (app); |
| 1298 |
|
| 1299 |
/* If the app is not hibernating, do nothing. */ |
| 1300 |
if (hd_running_app_get_state (app) != HD_APP_STATE_HIBERNATED) |
| 1301 |
return LAUNCH_OK; |
| 1302 |
|
| 1303 |
if (!service) |
| 1304 |
{ |
| 1305 |
g_warning ("%s: Can't wake up an app without service.\n", __FUNCTION__); |
| 1306 |
return LAUNCH_FAILED; |
| 1307 |
} |
| 1308 |
|
| 1309 |
if (!hd_app_mgr_can_launch (hd_running_app_get_launcher_app (app))) |
| 1310 |
{ |
| 1311 |
return LAUNCH_NO_MEM; |
| 1312 |
} |
| 1313 |
|
| 1314 |
res = hd_app_mgr_service_top (service, "RESTORE"); |
| 1315 |
if (res) { |
| 1316 |
hd_running_app_set_state (app, HD_APP_STATE_WAKING); |
| 1317 |
} |
| 1318 |
|
| 1319 |
return res ? LAUNCH_OK : LAUNCH_FAILED; |
| 1320 |
} |
| 1321 |
|
| 1322 |
#define OOM_DISABLE "0" |
| 1323 |
|
| 1324 |
static void |
| 1325 |
_hd_app_mgr_child_setup(gpointer user_data) |
| 1326 |
{ |
| 1327 |
int priority; |
| 1328 |
int fd; |
| 1329 |
|
| 1330 |
/* If the child process inherited desktop's high priority, |
| 1331 |
* give child default priority */ |
| 1332 |
priority = getpriority (PRIO_PROCESS, 0); |
| 1333 |
|
| 1334 |
if (!errno && priority < 0) |
| 1335 |
{ |
| 1336 |
setpriority (PRIO_PROCESS, 0, 0); |
| 1337 |
} |
| 1338 |
|
| 1339 |
/* Unprotect from OOM */ |
| 1340 |
fd = open ("/proc/self/oom_adj", O_WRONLY); |
| 1341 |
if (fd >= 0) |
| 1342 |
{ |
| 1343 |
write (fd, OOM_DISABLE, sizeof (OOM_DISABLE)); |
| 1344 |
close (fd); |
| 1345 |
} |
| 1346 |
} |
| 1347 |
|
| 1348 |
gboolean |
| 1349 |
hd_app_mgr_execute (const gchar *exec, GPid *pid, gboolean auto_reap) |
| 1350 |
{ |
| 1351 |
gboolean res = FALSE; |
| 1352 |
gchar *space = strchr (exec, ' '); |
| 1353 |
gchar *exec_cmd; |
| 1354 |
gint argc; |
| 1355 |
gchar **argv = NULL; |
| 1356 |
|
| 1357 |
if (space) |
| 1358 |
{ |
| 1359 |
gchar *cmd = g_strndup (exec, space - exec); |
| 1360 |
gchar *exc = g_find_program_in_path (cmd); |
| 1361 |
|
| 1362 |
exec_cmd = g_strconcat (exc, space, NULL); |
| 1363 |
|
| 1364 |
g_free (cmd); |
| 1365 |
g_free (exc); |
| 1366 |
} |
| 1367 |
else |
| 1368 |
exec_cmd = g_find_program_in_path (exec); |
| 1369 |
|
| 1370 |
if (!exec_cmd || |
| 1371 |
!g_shell_parse_argv (exec_cmd, &argc, &argv, NULL)) |
| 1372 |
{ |
| 1373 |
g_free (exec_cmd); |
| 1374 |
if (argv) |
| 1375 |
g_strfreev (argv); |
| 1376 |
|
| 1377 |
return FALSE; |
| 1378 |
} |
| 1379 |
|
| 1380 |
res = g_spawn_async (NULL, |
| 1381 |
argv, NULL, |
| 1382 |
auto_reap ? 0 : G_SPAWN_DO_NOT_REAP_CHILD, |
| 1383 |
_hd_app_mgr_child_setup, NULL, |
| 1384 |
pid, |
| 1385 |
NULL); |
| 1386 |
g_free (exec_cmd); |
| 1387 |
|
| 1388 |
if (argv) |
| 1389 |
g_strfreev (argv); |
| 1390 |
|
| 1391 |
return res; |
| 1392 |
} |
| 1393 |
|
| 1394 |
static gboolean |
| 1395 |
hd_app_mgr_service_top (const gchar *service, const gchar *param) |
| 1396 |
{ |
| 1397 |
gchar path[PATH_NAME_LEN]; |
| 1398 |
DBusMessage *msg = NULL; |
| 1399 |
DBusError derror; |
| 1400 |
DBusConnection *conn; |
| 1401 |
|
| 1402 |
gchar *tmp = g_strdelimit(g_strdup (service), ".", '/'); |
| 1403 |
g_snprintf (path, PATH_NAME_LEN, "/%s", tmp); |
| 1404 |
g_free (tmp); |
| 1405 |
|
| 1406 |
dbus_error_init (&derror); |
| 1407 |
conn = dbus_bus_get (DBUS_BUS_SESSION, &derror); |
| 1408 |
if (dbus_error_is_set (&derror)) |
| 1409 |
{ |
| 1410 |
g_warning ("could not start: %s: %s", service, derror.message); |
| 1411 |
dbus_error_free (&derror); |
| 1412 |
return FALSE; |
| 1413 |
} |
| 1414 |
|
| 1415 |
msg = dbus_message_new_method_call (service, path, service, OSSO_BUS_TOP); |
| 1416 |
if (msg == NULL) |
| 1417 |
{ |
| 1418 |
g_warning ("failed to create message"); |
| 1419 |
return FALSE; |
| 1420 |
} |
| 1421 |
|
| 1422 |
dbus_message_set_auto_start (msg, TRUE); |
| 1423 |
dbus_message_set_no_reply (msg, TRUE); |
| 1424 |
|
| 1425 |
if (param) |
| 1426 |
dbus_message_append_args (msg, DBUS_TYPE_STRING, ¶m, |
| 1427 |
DBUS_TYPE_INVALID); |
| 1428 |
|
| 1429 |
if (!dbus_connection_send (conn, msg, NULL)) |
| 1430 |
{ |
| 1431 |
dbus_message_unref (msg); |
| 1432 |
g_warning ("dbus_connection_send failed"); |
| 1433 |
return FALSE; |
| 1434 |
} |
| 1435 |
|
| 1436 |
g_debug ("%s: service: %s, param: %s", __FUNCTION__, |
| 1437 |
service, param ? param : "null"); |
| 1438 |
dbus_message_unref (msg); |
| 1439 |
return TRUE; |
| 1440 |
} |
| 1441 |
|
| 1442 |
/* Memory management. */ |
| 1443 |
static size_t |
| 1444 |
hd_app_mgr_read_lowmem (const gchar *filename) |
| 1445 |
{ |
| 1446 |
int fd = open (filename, O_RDONLY); |
| 1447 |
|
| 1448 |
if (fd >= 0) |
| 1449 |
{ |
| 1450 |
char buffer[32]; |
| 1451 |
size_t size = read (fd, buffer, sizeof(buffer) -1); |
| 1452 |
|
| 1453 |
close (fd); |
| 1454 |
if (size > 0) |
| 1455 |
{ |
| 1456 |
buffer[size] = 0; |
| 1457 |
return (size_t)strtol(buffer, NULL, 10); |
| 1458 |
} |
| 1459 |
} |
| 1460 |
|
| 1461 |
return NSIZE; |
| 1462 |
} |
| 1463 |
|
| 1464 |
/* |
| 1465 |
* Returns whether the load average is too high |
| 1466 |
* to preload applications. |
| 1467 |
*/ |
| 1468 |
static gboolean |
| 1469 |
hd_app_mgr_check_loadavg (void) |
| 1470 |
{ |
| 1471 |
gdouble load = hd_app_mgr_system_load_average (); |
| 1472 |
|
| 1473 |
return (load >= 0.0 && |
| 1474 |
load <= LOADAVG_MAX); |
| 1475 |
} |
| 1476 |
|
| 1477 |
static HdAppMgrPrestartMode |
| 1478 |
hd_app_mgr_setup_prestart (size_t low_pages, |
| 1479 |
size_t nr_decay_pages, |
| 1480 |
size_t *prestart_required_pages) |
| 1481 |
{ |
| 1482 |
gchar *prestart_env = NULL; |
| 1483 |
HdAppMgrPrestartMode result = PRESTART_NEVER; |
| 1484 |
*prestart_required_pages = NSIZE; |
| 1485 |
|
| 1486 |
prestart_env = getenv (PRESTART_ENV_VAR); |
| 1487 |
|
| 1488 |
if (!prestart_env || |
| 1489 |
!*prestart_env || |
| 1490 |
!g_strcmp0 (prestart_env, "no") || |
| 1491 |
!g_strcmp0 (prestart_env, "false")) |
| 1492 |
{ |
| 1493 |
result = PRESTART_NEVER; |
| 1494 |
} |
| 1495 |
else if (low_pages == NSIZE || nr_decay_pages == NSIZE) |
| 1496 |
{ |
| 1497 |
*prestart_required_pages = NSIZE; |
| 1498 |
result = PRESTART_ALWAYS; |
| 1499 |
} |
| 1500 |
else |
| 1501 |
{ |
| 1502 |
size_t reserved = (size_t)strtol (prestart_env, NULL, 10); |
| 1503 |
if (reserved == 0) |
| 1504 |
*prestart_required_pages = |
| 1505 |
low_pages + hd_app_mgr_read_lowmem (LOWMEM_PROC_NR_DECAY); |
| 1506 |
else |
| 1507 |
*prestart_required_pages = low_pages + reserved; |
| 1508 |
result = PRESTART_AUTO; |
| 1509 |
} |
| 1510 |
|
| 1511 |
return result; |
| 1512 |
} |
| 1513 |
|
| 1514 |
static void |
| 1515 |
hd_app_mgr_setup_launch (size_t high_pages, |
| 1516 |
size_t nr_decay_pages, |
| 1517 |
size_t *launch_required_pages) |
| 1518 |
{ |
| 1519 |
if (high_pages == NSIZE || nr_decay_pages == NSIZE) |
| 1520 |
{ |
| 1521 |
g_debug ("%s: No memory limits, assuming scratchbox.\n", __FUNCTION__); |
| 1522 |
*launch_required_pages = NSIZE; |
| 1523 |
return; |
| 1524 |
} |
| 1525 |
|
| 1526 |
*launch_required_pages = high_pages + nr_decay_pages; |
| 1527 |
} |
| 1528 |
|
| 1529 |
static gboolean |
| 1530 |
hd_app_mgr_can_launch (HdLauncherApp *launcher) |
| 1531 |
{ |
| 1532 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (hd_app_mgr_get ()); |
| 1533 |
|
| 1534 |
if (launcher && hd_launcher_app_get_ignore_lowmem (launcher)) |
| 1535 |
return TRUE; |
| 1536 |
|
| 1537 |
return !priv->lowmem; |
| 1538 |
} |
| 1539 |
|
| 1540 |
static gboolean hd_app_mgr_can_prestart (HdLauncherApp *launcher) |
| 1541 |
{ |
| 1542 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (hd_app_mgr_get ()); |
| 1543 |
if (priv->prestart_mode == PRESTART_ALWAYS) |
| 1544 |
return TRUE; |
| 1545 |
else if (priv->prestart_mode == PRESTART_NEVER) |
| 1546 |
return FALSE; |
| 1547 |
|
| 1548 |
if (hd_launcher_app_get_ignore_load (launcher)) |
| 1549 |
return TRUE; |
| 1550 |
|
| 1551 |
if (!hd_app_mgr_check_loadavg ()) |
| 1552 |
return FALSE; |
| 1553 |
|
| 1554 |
size_t free_pages = hd_app_mgr_read_lowmem (LOWMEM_PROC_FREE); |
| 1555 |
if (free_pages == NSIZE) |
| 1556 |
return TRUE; |
| 1557 |
|
| 1558 |
return free_pages >= priv->prestart_required_pages; |
| 1559 |
} |
| 1560 |
|
| 1561 |
static void |
| 1562 |
hd_app_mgr_hdrm_state_change (gpointer hdrm, |
| 1563 |
GParamSpec *pspec, |
| 1564 |
HdAppMgrPrivate *priv) |
| 1565 |
{ |
| 1566 |
gboolean launcher = hd_render_manager_get_state () == HDRM_STATE_LAUNCHER; |
| 1567 |
if (launcher != priv->prestarting_stopped) |
| 1568 |
{ |
| 1569 |
priv->prestarting_stopped = launcher; |
| 1570 |
hd_app_mgr_state_check (); |
| 1571 |
} |
| 1572 |
|
| 1573 |
/* Also check if we should enable the accelerometer. */ |
| 1574 |
hd_app_mgr_mce_activate_accel_if_needed (TRUE); |
| 1575 |
} |
| 1576 |
|
| 1577 |
static void |
| 1578 |
hd_app_mgr_state_check (void) |
| 1579 |
{ |
| 1580 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (hd_app_mgr_get ()); |
| 1581 |
|
| 1582 |
/* If it's already looping, it'll get there, so do nothing. */ |
| 1583 |
if (priv->state_check_looping) |
| 1584 |
return; |
| 1585 |
|
| 1586 |
/* If not, start looping. */ |
| 1587 |
priv->state_check_looping = TRUE; |
| 1588 |
g_timeout_add_seconds (STATE_CHECK_INTERVAL, |
| 1589 |
hd_app_mgr_state_check_loop, |
| 1590 |
NULL); |
| 1591 |
} |
| 1592 |
|
| 1593 |
/* |
| 1594 |
* This function runs in a loop or whenever there's a change in memory |
| 1595 |
* conditions. Depending on those conditions, it |
| 1596 |
* - Kills prestarted apps. |
| 1597 |
* - Hibernates apps. |
| 1598 |
* - Prestarts apps. |
| 1599 |
* It continues to loop if |
| 1600 |
* - There are still apps to be prestarted. |
| 1601 |
* - If memory is not low enough. |
| 1602 |
*/ |
| 1603 |
static gboolean |
| 1604 |
hd_app_mgr_state_check_loop (gpointer data) |
| 1605 |
{ |
| 1606 |
gboolean loop = FALSE; |
| 1607 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (hd_app_mgr_get ()); |
| 1608 |
|
| 1609 |
/* First check if we are really low on memory. */ |
| 1610 |
if (priv->lowmem) |
| 1611 |
{ |
| 1612 |
/* If there are prestarted apps, kill one of them. */ |
| 1613 |
if (!g_queue_is_empty (priv->queues[QUEUE_PRESTARTED])) |
| 1614 |
{ |
| 1615 |
HdRunningApp *app = g_queue_peek_tail (priv->queues[QUEUE_PRESTARTED]); |
| 1616 |
hd_app_mgr_kill (app); |
| 1617 |
if (!g_queue_is_empty (priv->queues[QUEUE_PRESTARTED])) |
| 1618 |
loop = TRUE; |
| 1619 |
} |
| 1620 |
} |
| 1621 |
|
| 1622 |
/* If we're running low, hibernate an app. */ |
| 1623 |
else if (priv->bg_killing) |
| 1624 |
{ |
| 1625 |
/* TODO: Hibernate an app and loop. */ |
| 1626 |
if (!g_queue_is_empty (priv->queues[QUEUE_HIBERNATABLE])) |
| 1627 |
{ |
| 1628 |
HdRunningApp *app = g_queue_peek_tail (priv->queues[QUEUE_HIBERNATABLE]); |
| 1629 |
hd_app_mgr_hibernate (app); |
| 1630 |
if (!g_queue_is_empty (priv->queues[QUEUE_HIBERNATABLE])) |
| 1631 |
loop = TRUE; |
| 1632 |
} |
| 1633 |
} |
| 1634 |
/* If there's enough memory and hibernated apps, try to awake them. |
| 1635 |
* TODO: Add some way to avoid waking-up apps from being shown immediately |
| 1636 |
* to the user as if launched anew. |
| 1637 |
else if (!g_queue_is_empty (priv->queues[QUEUE_HIBERNATED]) && |
| 1638 |
hd_app_mgr_can_launch (NULL)) |
| 1639 |
{ |
| 1640 |
HdLauncherApp *app = g_queue_peek_head (priv->queues[QUEUE_HIBERNATED]); |
| 1641 |
hd_app_mgr_wakeup (app); |
| 1642 |
if (!g_queue_is_empty (priv->queues[QUEUE_HIBERNATED])) |
| 1643 |
loop = TRUE; |
| 1644 |
} |
| 1645 |
*/ |
| 1646 |
/* If we have enough memory and there are apps waiting to be prestarted, |
| 1647 |
* do that. |
| 1648 |
*/ |
| 1649 |
else if (priv->init_done && |
| 1650 |
priv->prestart_mode != PRESTART_NEVER && |
| 1651 |
!priv->prestarting_stopped && |
| 1652 |
!g_queue_is_empty (priv->queues[QUEUE_PRESTARTABLE]) |
| 1653 |
) |
| 1654 |
{ |
| 1655 |
/* We make this tests here to loop even if we can't prestart right now.*/ |
| 1656 |
if (!priv->prestarting) |
| 1657 |
{ |
| 1658 |
HdRunningApp *app = g_queue_peek_head (priv->queues[QUEUE_PRESTARTABLE]); |
| 1659 |
HdLauncherApp *launcher = hd_running_app_get_launcher_app (app); |
| 1660 |
if (launcher && hd_app_mgr_can_prestart (launcher)) |
| 1661 |
hd_app_mgr_prestart (app); |
| 1662 |
} |
| 1663 |
if (!g_queue_is_empty (priv->queues[QUEUE_PRESTARTABLE])) |
| 1664 |
loop = TRUE; |
| 1665 |
} |
| 1666 |
|
| 1667 |
/* Now the tricky part. This function is called by a timeout or by |
| 1668 |
* changes in memory conditions. If we're already looping, return if we |
| 1669 |
* need to loop. If not, and we need to loop, start the loop. |
| 1670 |
*/ |
| 1671 |
priv->state_check_looping = loop; |
| 1672 |
|
| 1673 |
return loop; |
| 1674 |
} |
| 1675 |
|
| 1676 |
static void |
| 1677 |
hd_app_mgr_dbus_name_owner_changed (DBusGProxy *proxy, |
| 1678 |
const char *name, |
| 1679 |
const char *old_owner, |
| 1680 |
const char *new_owner, |
| 1681 |
gpointer data) |
| 1682 |
{ |
| 1683 |
GList *apps; |
| 1684 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (hd_app_mgr_get ()); |
| 1685 |
|
| 1686 |
/* Check only connections and disconnections. */ |
| 1687 |
if (!old_owner[0] == !new_owner[0]) |
| 1688 |
return; |
| 1689 |
|
| 1690 |
/* Check if the service is one we want always on. */ |
| 1691 |
apps = priv->running_apps; |
| 1692 |
while (apps) |
| 1693 |
{ |
| 1694 |
HdRunningApp *app = HD_RUNNING_APP (apps->data); |
| 1695 |
|
| 1696 |
if (hd_running_app_is_inactive (app)) |
| 1697 |
goto next; |
| 1698 |
|
| 1699 |
if (!g_strcmp0 (name, hd_running_app_get_service (app))) |
| 1700 |
{ |
| 1701 |
if (!new_owner[0]) |
| 1702 |
{ /* Disconnection */ |
| 1703 |
g_debug ("%s: App %s has fallen\n", __FUNCTION__, |
| 1704 |
hd_running_app_get_id (app)); |
| 1705 |
|
| 1706 |
/* We have the correct app, deal accordingly. */ |
| 1707 |
hd_app_mgr_app_closed (app); |
| 1708 |
} |
| 1709 |
else |
| 1710 |
{ /* Connection */ |
| 1711 |
if (!hd_running_app_get_pid (app)) |
| 1712 |
hd_app_mgr_request_app_pid (app); |
| 1713 |
} |
| 1714 |
break; |
| 1715 |
} |
| 1716 |
|
| 1717 |
next: |
| 1718 |
apps = g_list_next (apps); |
| 1719 |
} |
| 1720 |
} |
| 1721 |
|
| 1722 |
static gint |
| 1723 |
_hd_app_mgr_compare_launcher_exec (HdLauncherItem *item, |
| 1724 |
gchar *filename) |
| 1725 |
{ |
| 1726 |
HdLauncherApp *launcher; |
| 1727 |
|
| 1728 |
if (hd_launcher_item_get_item_type (item) != HD_APPLICATION_LAUNCHER) |
| 1729 |
return -1; |
| 1730 |
|
| 1731 |
launcher = HD_LAUNCHER_APP (item); |
| 1732 |
return g_strcmp0 (hd_launcher_app_get_exec (launcher), filename); |
| 1733 |
} |
| 1734 |
|
| 1735 |
static DBusHandlerResult hd_app_mgr_dbus_app_died (DBusConnection *conn, |
| 1736 |
DBusMessage *msg, |
| 1737 |
void *data) |
| 1738 |
{ |
| 1739 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (hd_app_mgr_get ()); |
| 1740 |
HdLauncherApp *launcher = NULL; |
| 1741 |
GList *link; |
| 1742 |
gchar *filename; |
| 1743 |
GPid pid; |
| 1744 |
gint status; |
| 1745 |
DBusError err; |
| 1746 |
|
| 1747 |
if (!dbus_message_is_signal (msg, |
| 1748 |
MAEMO_LAUNCHER_IFACE, |
| 1749 |
MAEMO_LAUNCHER_APP_DIED_SIGNAL_NAME)) |
| 1750 |
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| 1751 |
|
| 1752 |
dbus_error_init(&err); |
| 1753 |
|
| 1754 |
dbus_message_get_args(msg, &err, |
| 1755 |
DBUS_TYPE_STRING, &filename, |
| 1756 |
DBUS_TYPE_INT32, &pid, |
| 1757 |
DBUS_TYPE_INT32, &status, |
| 1758 |
DBUS_TYPE_INVALID); |
| 1759 |
|
| 1760 |
if (dbus_error_is_set(&err)) |
| 1761 |
{ |
| 1762 |
g_warning ("%s: Error getting message args: %s\n", |
| 1763 |
__FUNCTION__, err.message); |
| 1764 |
dbus_error_free (&err); |
| 1765 |
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| 1766 |
} |
| 1767 |
|
| 1768 |
/* Find which app died. */ |
| 1769 |
link = g_list_find_custom (hd_launcher_tree_get_items (priv->tree), |
| 1770 |
(gconstpointer)filename, |
| 1771 |
(GCompareFunc)_hd_app_mgr_compare_launcher_exec); |
| 1772 |
|
| 1773 |
/* NOTE: Should we report crashes of app we don't know about? */ |
| 1774 |
g_debug ("%s: app: %s, filename: %s", __FUNCTION__, |
| 1775 |
link ? hd_launcher_item_get_id (HD_LAUNCHER_ITEM (link->data)) : "<unknown>", |
| 1776 |
filename); |
| 1777 |
|
| 1778 |
if (link) |
| 1779 |
{ |
| 1780 |
launcher = link->data; |
| 1781 |
g_signal_emit (hd_app_mgr_get (), app_mgr_signals[APP_CRASHED], |
| 1782 |
0, launcher, NULL); |
| 1783 |
} |
| 1784 |
|
| 1785 |
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| 1786 |
} |
| 1787 |
|
| 1788 |
gboolean |
| 1789 |
hd_app_mgr_dbus_launch_app (HdAppMgr *self, const gchar *id) |
| 1790 |
{ |
| 1791 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (self); |
| 1792 |
HdLauncherItem *item; |
| 1793 |
HdLauncherApp *app; |
| 1794 |
|
| 1795 |
if (!id || !strlen(id)) |
| 1796 |
{ /* If no ID was specified, all we want to do is trigger the |
| 1797 |
app start animation with a blank window, not do anything fancy. |
| 1798 |
We must check first that we haven't had a window mapped recently, |
| 1799 |
because it could be that the dbus message got to us after the window |
| 1800 |
appeared. */ |
| 1801 |
if (hd_render_manager_allow_dbus_launch_transition()) |
| 1802 |
hd_launcher_transition_app_start(NULL); |
| 1803 |
return TRUE; |
| 1804 |
} |
| 1805 |
|
| 1806 |
item = hd_launcher_tree_find_item (priv->tree, id); |
| 1807 |
if (!item || hd_launcher_item_get_item_type (item) != HD_APPLICATION_LAUNCHER) |
| 1808 |
app = hd_launcher_tree_find_app_by_service (priv->tree, id); |
| 1809 |
else |
| 1810 |
app = HD_LAUNCHER_APP (item); |
| 1811 |
return app ? hd_app_mgr_launch (app) : FALSE; |
| 1812 |
} |
| 1813 |
|
| 1814 |
gboolean |
| 1815 |
hd_app_mgr_dbus_prestart (HdAppMgr *self, const gboolean enable) |
| 1816 |
{ |
| 1817 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (self); |
| 1818 |
|
| 1819 |
if (enable) |
| 1820 |
{ |
| 1821 |
priv->prestarting_stopped = FALSE; |
| 1822 |
hd_app_mgr_state_check (); |
| 1823 |
} |
| 1824 |
else |
| 1825 |
{ |
| 1826 |
priv->prestarting_stopped = TRUE; |
| 1827 |
hd_app_mgr_kill_all_prestarted (); |
| 1828 |
} |
| 1829 |
|
| 1830 |
return TRUE; |
| 1831 |
} |
| 1832 |
|
| 1833 |
static gboolean |
| 1834 |
_hd_app_mgr_should_show_callui () |
| 1835 |
{ |
| 1836 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (hd_app_mgr_get ()); |
| 1837 |
extern MBWindowManager *hd_mb_wm; |
| 1838 |
extern gboolean hd_dbus_display_is_off; |
| 1839 |
|
| 1840 |
if (STATE_SHOW_CALLUI (hd_render_manager_get_state ()) && |
| 1841 |
priv->accel_enabled && |
| 1842 |
priv->portrait && |
| 1843 |
priv->unlocked && |
| 1844 |
!hd_dbus_display_is_off && |
| 1845 |
priv->slide_closed && |
| 1846 |
!priv->disable_callui && |
| 1847 |
!hd_wm_has_modal_blockers (hd_mb_wm)) |
| 1848 |
{ |
| 1849 |
return TRUE; |
| 1850 |
} |
| 1851 |
|
| 1852 |
return FALSE; |
| 1853 |
} |
| 1854 |
|
| 1855 |
/* Callback for showing the CALL UI */ |
| 1856 |
static gboolean |
| 1857 |
hd_app_mgr_show_callui_cb (gpointer data) |
| 1858 |
{ |
| 1859 |
if (_hd_app_mgr_should_show_callui()) |
| 1860 |
hd_app_mgr_service_top (CALLUI_INTERFACE, NULL); |
| 1861 |
|
| 1862 |
return FALSE; |
| 1863 |
} |
| 1864 |
|
| 1865 |
/* Show CallUI if |
| 1866 |
* - Showing the desktop. |
| 1867 |
* - In portrait mode. |
| 1868 |
* - Unlocked. |
| 1869 |
* - Display on. |
| 1870 |
* - Slide closed. |
| 1871 |
*/ |
| 1872 |
void |
| 1873 |
hd_app_mgr_check_show_callui (void) |
| 1874 |
{ |
| 1875 |
if (_hd_app_mgr_should_show_callui ()) |
| 1876 |
{ |
| 1877 |
g_timeout_add_seconds(CALLUI_PORTRAIT_TIMEOUT, |
| 1878 |
(GSourceFunc) hd_app_mgr_show_callui_cb, |
| 1879 |
NULL); |
| 1880 |
} |
| 1881 |
} |
| 1882 |
|
| 1883 |
static gboolean |
| 1884 |
hd_app_mgr_init_done_timeout (HdAppMgr *self) |
| 1885 |
{ |
| 1886 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (self); |
| 1887 |
|
| 1888 |
if (!priv->init_done) |
| 1889 |
{ |
| 1890 |
g_debug ("%s", __FUNCTION__); |
| 1891 |
priv->init_done = TRUE; |
| 1892 |
hd_app_mgr_state_check (); |
| 1893 |
} |
| 1894 |
|
| 1895 |
return FALSE; |
| 1896 |
} |
| 1897 |
|
| 1898 |
static gboolean |
| 1899 |
_hd_app_mgr_dbus_check_value (DBusMessage *msg, |
| 1900 |
const gchar *value) |
| 1901 |
{ |
| 1902 |
DBusError err; |
| 1903 |
gchar *arg; |
| 1904 |
|
| 1905 |
dbus_error_init (&err); |
| 1906 |
dbus_message_get_args (msg, &err, |
| 1907 |
DBUS_TYPE_STRING, &arg, |
| 1908 |
DBUS_TYPE_INVALID); |
| 1909 |
|
| 1910 |
if (dbus_error_is_set(&err)) |
| 1911 |
{ |
| 1912 |
g_warning ("%s: Error getting message args: %s\n", |
| 1913 |
__FUNCTION__, err.message); |
| 1914 |
dbus_error_free (&err); |
| 1915 |
return FALSE; |
| 1916 |
} |
| 1917 |
|
| 1918 |
if (!g_strcmp0 (arg, value)) |
| 1919 |
return TRUE; |
| 1920 |
|
| 1921 |
return FALSE; |
| 1922 |
} |
| 1923 |
|
| 1924 |
static void |
| 1925 |
hd_app_mgr_update_portraitness(HdAppMgr *self) |
| 1926 |
{ |
| 1927 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (self); |
| 1928 |
HdCompMgr *hmgr = hd_comp_mgr_get (); |
| 1929 |
// what about priv->unlocked/priv->display_on ? |
| 1930 |
|
| 1931 |
if (!hmgr) |
| 1932 |
/* We're in some initialization state. */ |
| 1933 |
return; |
| 1934 |
hd_comp_mgr_set_pip_flags (hmgr, |
| 1935 |
priv->accel_enabled, |
| 1936 |
priv->portrait && priv->slide_closed); |
| 1937 |
hd_comp_mgr_portrait_or_not_portrait (MB_WM_COMP_MGR (hmgr), NULL); |
| 1938 |
} |
| 1939 |
|
| 1940 |
static DBusHandlerResult |
| 1941 |
hd_app_mgr_dbus_signal_handler (DBusConnection *conn, |
| 1942 |
DBusMessage *msg, |
| 1943 |
void *data) |
| 1944 |
{ |
| 1945 |
gboolean changed = TRUE; |
| 1946 |
HdAppMgr *self = HD_APP_MGR (data); |
| 1947 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (self); |
| 1948 |
|
| 1949 |
if (dbus_message_is_signal (msg, |
| 1950 |
LOWMEM_ON_SIGNAL_INTERFACE, |
| 1951 |
LOWMEM_ON_SIGNAL_NAME)) |
| 1952 |
priv->lowmem = TRUE; |
| 1953 |
else if (dbus_message_is_signal (msg, |
| 1954 |
LOWMEM_OFF_SIGNAL_INTERFACE, |
| 1955 |
LOWMEM_OFF_SIGNAL_NAME)) |
| 1956 |
priv->lowmem = FALSE; |
| 1957 |
else if (dbus_message_is_signal (msg, |
| 1958 |
BGKILL_ON_SIGNAL_INTERFACE, |
| 1959 |
BGKILL_ON_SIGNAL_NAME)) |
| 1960 |
priv->bg_killing = TRUE; |
| 1961 |
else if (dbus_message_is_signal (msg, |
| 1962 |
BGKILL_OFF_SIGNAL_INTERFACE, |
| 1963 |
BGKILL_OFF_SIGNAL_NAME)) |
| 1964 |
priv->bg_killing = FALSE; |
| 1965 |
else if (dbus_message_is_signal (msg, |
| 1966 |
INIT_DONE_SIGNAL_INTERFACE, |
| 1967 |
INIT_DONE_SIGNAL_NAME)) |
| 1968 |
priv->init_done = TRUE; |
| 1969 |
else |
| 1970 |
{ |
| 1971 |
/* Check for showing CallUI flags. */ |
| 1972 |
changed = FALSE; |
| 1973 |
if (dbus_message_is_signal (msg, |
| 1974 |
MCE_SIGNAL_IF, |
| 1975 |
MCE_TKLOCK_MODE_SIG)) |
| 1976 |
{ |
| 1977 |
priv->unlocked = _hd_app_mgr_dbus_check_value (msg, |
| 1978 |
MCE_DEVICE_UNLOCKED); |
| 1979 |
} |
| 1980 |
else if (dbus_message_is_signal (msg, |
| 1981 |
MCE_SIGNAL_IF, |
| 1982 |
MCE_DEVICE_ORIENTATION_SIG)) |
| 1983 |
{ |
| 1984 |
priv->portrait = _hd_app_mgr_dbus_check_value (msg, |
| 1985 |
MCE_ORIENTATION_PORTRAIT); |
| 1986 |
if (priv->portrait) |
| 1987 |
hd_app_mgr_check_show_callui (); |
| 1988 |
|
| 1989 |
hd_app_mgr_update_portraitness(self); |
| 1990 |
} |
| 1991 |
} |
| 1992 |
|
| 1993 |
if (changed) |
| 1994 |
hd_app_mgr_state_check (); |
| 1995 |
|
| 1996 |
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| 1997 |
} |
| 1998 |
|
| 1999 |
/* Activate the accelerometer when |
| 2000 |
* - The user has activated rotate-to-callui. |
| 2001 |
* - HDRM is in a state that shows callui. |
| 2002 |
* - We are showing an app, and all visible windows support portrait mode |
| 2003 |
*/ |
| 2004 |
void |
| 2005 |
hd_app_mgr_mce_activate_accel_if_needed (gboolean update_portraitness) |
| 2006 |
{ |
| 2007 |
extern gboolean hd_dbus_tklock_on; |
| 2008 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (the_app_mgr); |
| 2009 |
DBusConnection *conn = NULL; |
| 2010 |
DBusMessage *msg = NULL; |
| 2011 |
gboolean activate = !hd_dbus_tklock_on; |
| 2012 |
|
| 2013 |
if (activate) |
| 2014 |
activate = (!priv->disable_callui |
| 2015 |
&& STATE_SHOW_CALLUI (hd_render_manager_get_state ())) |
| 2016 |
|| (STATE_IS_APP(hd_render_manager_get_state ()) |
| 2017 |
&& hd_comp_mgr_can_be_portrait(hd_comp_mgr_get())); |
| 2018 |
if (priv->accel_enabled == activate) |
| 2019 |
return; |
| 2020 |
|
| 2021 |
conn = dbus_bus_get (DBUS_BUS_SYSTEM, NULL); |
| 2022 |
if (!conn) |
| 2023 |
{ |
| 2024 |
g_warning ("%s: Couldn't connect to session bus.", __FUNCTION__); |
| 2025 |
return; |
| 2026 |
} |
| 2027 |
|
| 2028 |
/* We're only interested in these signals if we're going to rotate. */ |
| 2029 |
if (activate) |
| 2030 |
{ |
| 2031 |
hd_app_mgr_dbus_add_signal_match (conn, MCE_SIGNAL_IF, |
| 2032 |
MCE_TKLOCK_MODE_SIG); |
| 2033 |
hd_app_mgr_dbus_add_signal_match (conn, MCE_SIGNAL_IF, |
| 2034 |
MCE_DEVICE_ORIENTATION_SIG); |
| 2035 |
} |
| 2036 |
else |
| 2037 |
{ |
| 2038 |
hd_app_mgr_dbus_remove_signal_match (conn, MCE_SIGNAL_IF, |
| 2039 |
MCE_TKLOCK_MODE_SIG); |
| 2040 |
hd_app_mgr_dbus_remove_signal_match (conn, MCE_SIGNAL_IF, |
| 2041 |
MCE_DEVICE_ORIENTATION_SIG); |
| 2042 |
} |
| 2043 |
|
| 2044 |
msg = dbus_message_new_method_call ( |
| 2045 |
MCE_SERVICE, |
| 2046 |
MCE_REQUEST_PATH, |
| 2047 |
MCE_REQUEST_IF, |
| 2048 |
activate? |
| 2049 |
MCE_ACCELEROMETER_ENABLE_REQ : |
| 2050 |
MCE_ACCELEROMETER_DISABLE_REQ); |
| 2051 |
if (!msg) |
| 2052 |
{ |
| 2053 |
g_warning ("%s: Couldn't create message.", __FUNCTION__); |
| 2054 |
return; |
| 2055 |
} |
| 2056 |
|
| 2057 |
dbus_message_set_auto_start (msg, TRUE); |
| 2058 |
if (activate) |
| 2059 |
{ |
| 2060 |
DBusMessage *reply; |
| 2061 |
|
| 2062 |
/* @reply will contain the current orientation */ |
| 2063 |
if ((reply = dbus_connection_send_with_reply_and_block ( |
| 2064 |
conn, msg, -1, NULL)) != NULL) |
| 2065 |
{ |
| 2066 |
priv->portrait = _hd_app_mgr_dbus_check_value (reply, |
| 2067 |
MCE_ORIENTATION_PORTRAIT); |
| 2068 |
dbus_message_unref (reply); |
| 2069 |
} |
| 2070 |
else |
| 2071 |
g_warning ("%s: Couldn't send message.", __FUNCTION__); |
| 2072 |
dbus_message_unref (msg); |
| 2073 |
} |
| 2074 |
else |
| 2075 |
{ /* Deactivate, expect no reply. We can only deactivate in lscape. */ |
| 2076 |
dbus_message_set_no_reply (msg, TRUE); |
| 2077 |
if (!dbus_connection_send (conn, msg, NULL)) |
| 2078 |
g_warning ("%s: Couldn't send message.", __FUNCTION__); |
| 2079 |
dbus_message_unref (msg); |
| 2080 |
priv->portrait = FALSE; |
| 2081 |
} |
| 2082 |
|
| 2083 |
g_debug ("%s: %s", __FUNCTION__, activate ? "enabled" : "disabled"); |
| 2084 |
priv->accel_enabled = activate; |
| 2085 |
|
| 2086 |
if (update_portraitness) |
| 2087 |
hd_app_mgr_update_portraitness(the_app_mgr); |
| 2088 |
else |
| 2089 |
hd_comp_mgr_set_pip_flags (hd_comp_mgr_get (), priv->accel_enabled, |
| 2090 |
priv->portrait && priv->slide_closed); |
| 2091 |
} |
| 2092 |
|
| 2093 |
static void |
| 2094 |
hd_app_mgr_gconf_value_changed (GConfClient *client, |
| 2095 |
guint cnxn_id, |
| 2096 |
GConfEntry *entry, |
| 2097 |
gpointer user_data) |
| 2098 |
{ |
| 2099 |
HdAppMgr *self = HD_APP_MGR (user_data); |
| 2100 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (self); |
| 2101 |
GConfValue *gvalue; |
| 2102 |
gboolean value = FALSE; |
| 2103 |
|
| 2104 |
if (!entry) |
| 2105 |
return; |
| 2106 |
|
| 2107 |
gvalue = gconf_entry_get_value (entry); |
| 2108 |
if (gvalue->type == GCONF_VALUE_BOOL) |
| 2109 |
value = gconf_value_get_bool (gvalue); |
| 2110 |
|
| 2111 |
if (!g_strcmp0 (gconf_entry_get_key (entry), |
| 2112 |
GCONF_SLIDE_OPEN_KEY)) |
| 2113 |
{ |
| 2114 |
priv->slide_closed = !value; |
| 2115 |
|
| 2116 |
hd_app_mgr_update_portraitness(self); |
| 2117 |
} |
| 2118 |
|
| 2119 |
if (!g_strcmp0 (gconf_entry_get_key (entry), |
| 2120 |
GCONF_DISABLE_CALLUI_KEY)) |
| 2121 |
{ |
| 2122 |
priv->disable_callui = value; |
| 2123 |
|
| 2124 |
/* Check if h-d needs to track the orientation. */ |
| 2125 |
hd_app_mgr_mce_activate_accel_if_needed (TRUE); |
| 2126 |
} |
| 2127 |
|
| 2128 |
return; |
| 2129 |
} |
| 2130 |
|
| 2131 |
static void |
| 2132 |
_hd_app_mgr_request_app_pid_cb (DBusGProxy *proxy, guint pid, |
| 2133 |
GError *error, gpointer data) |
| 2134 |
{ |
| 2135 |
HdRunningApp *app = HD_RUNNING_APP (data); |
| 2136 |
|
| 2137 |
if (error) |
| 2138 |
{ |
| 2139 |
g_warning ("%s: Couldn't get pid for service %s because %s\n", |
| 2140 |
__FUNCTION__, hd_running_app_get_service (app), |
| 2141 |
error->message); |
| 2142 |
return; |
| 2143 |
} |
| 2144 |
|
| 2145 |
g_debug ("%s: Got pid %d for %s\n", __FUNCTION__, |
| 2146 |
pid, hd_running_app_get_service (app)); |
| 2147 |
hd_running_app_set_pid (app, pid); |
| 2148 |
} |
| 2149 |
|
| 2150 |
static void |
| 2151 |
hd_app_mgr_request_app_pid (HdRunningApp *app) |
| 2152 |
{ |
| 2153 |
DBusGProxy *proxy = (HD_APP_MGR_GET_PRIVATE (hd_app_mgr_get()))->dbus_proxy; |
| 2154 |
const gchar *service = hd_running_app_get_service (app); |
| 2155 |
|
| 2156 |
if (!service) |
| 2157 |
{ |
| 2158 |
g_warning ("%s: Can't get the pid for a non-dbus app.\n", __FUNCTION__); |
| 2159 |
hd_running_app_set_pid (app, 0); |
| 2160 |
} |
| 2161 |
|
| 2162 |
org_freedesktop_DBus_get_connection_unix_process_id_async (proxy, |
| 2163 |
service, |
| 2164 |
_hd_app_mgr_request_app_pid_cb, (gpointer)app); |
| 2165 |
} |
| 2166 |
|
| 2167 |
HdRunningApp * |
| 2168 |
hd_app_mgr_match_window (const char *res_name, |
| 2169 |
const char *res_class, |
| 2170 |
GPid pid) |
| 2171 |
{ |
| 2172 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (hd_app_mgr_get ()); |
| 2173 |
HdRunningApp *app = NULL; |
| 2174 |
HdLauncherApp *launcher = NULL; |
| 2175 |
GList *link = NULL; |
| 2176 |
|
| 2177 |
/* First we need to look if there's already a running app for this. */ |
| 2178 |
link = priv->running_apps; |
| 2179 |
while (link) |
| 2180 |
{ |
| 2181 |
app = HD_RUNNING_APP (link->data); |
| 2182 |
launcher = hd_running_app_get_launcher_app (app); |
| 2183 |
|
| 2184 |
/* If we know the running app's pid and it's the same, we found it. */ |
| 2185 |
GPid app_pid = hd_running_app_get_pid (app); |
| 2186 |
if (app_pid && (app_pid == pid)) |
| 2187 |
return app; |
| 2188 |
|
| 2189 |
/* Now we look if the app's launcher matches the window. */ |
| 2190 |
if (launcher) |
| 2191 |
{ |
| 2192 |
if (hd_launcher_app_match_window (launcher, res_name, res_class)) |
| 2193 |
{ |
| 2194 |
/* Now we have a good pid. */ |
| 2195 |
if (!app_pid) |
| 2196 |
hd_running_app_set_pid (app, pid); |
| 2197 |
return app; |
| 2198 |
} |
| 2199 |
} |
| 2200 |
|
| 2201 |
/* Next. */ |
| 2202 |
link = link->next; |
| 2203 |
} |
| 2204 |
|
| 2205 |
/* Well, there wasn't any already running app, so we'll have to look for |
| 2206 |
* a launcher that matches. |
| 2207 |
*/ |
| 2208 |
GList *launchers = hd_launcher_tree_get_items (priv->tree); |
| 2209 |
app = NULL; |
| 2210 |
|
| 2211 |
if (res_name || res_class) |
| 2212 |
{ |
| 2213 |
while (launchers) |
| 2214 |
{ |
| 2215 |
/* Filter non-applications. */ |
| 2216 |
if (hd_launcher_item_get_item_type (HD_LAUNCHER_ITEM (launchers->data)) != |
| 2217 |
HD_APPLICATION_LAUNCHER) |
| 2218 |
goto next; |
| 2219 |
|
| 2220 |
launcher = HD_LAUNCHER_APP (launchers->data); |
| 2221 |
if (hd_launcher_app_match_window (launcher, res_name, res_class)) |
| 2222 |
{ |
| 2223 |
/* Let's make a new running app for it. */ |
| 2224 |
app = hd_running_app_new (launcher); |
| 2225 |
hd_running_app_set_pid (app, pid); |
| 2226 |
priv->running_apps = g_list_prepend (priv->running_apps, app); |
| 2227 |
return app; |
| 2228 |
} |
| 2229 |
|
| 2230 |
next: |
| 2231 |
launchers = g_list_next (launchers); |
| 2232 |
} |
| 2233 |
} |
| 2234 |
|
| 2235 |
/* |
| 2236 |
* We didn't find any perfectly matching app, so try to see if we have |
| 2237 |
* any loading one. |
| 2238 |
* TODO: Review the wiseness of this move. |
| 2239 |
*/ |
| 2240 |
link = priv->running_apps; |
| 2241 |
while (link) |
| 2242 |
{ |
| 2243 |
app = HD_RUNNING_APP (link->data); |
| 2244 |
if (hd_running_app_get_state (app) == HD_APP_STATE_LOADING) |
| 2245 |
{ |
| 2246 |
if (!hd_running_app_get_pid (app)) |
| 2247 |
hd_running_app_set_pid (app, pid); |
| 2248 |
return app; |
| 2249 |
} |
| 2250 |
|
| 2251 |
link = link->next; |
| 2252 |
} |
| 2253 |
|
| 2254 |
/* What? We haven't found any yet? |
| 2255 |
* Well, let's just make one for this one and keep the pid in case we |
| 2256 |
* have to kill it |
| 2257 |
*/ |
| 2258 |
app = hd_running_app_new (NULL); |
| 2259 |
hd_running_app_set_pid (app, pid); |
| 2260 |
priv->running_apps = g_list_prepend (priv->running_apps, app); |
| 2261 |
|
| 2262 |
return app; |
| 2263 |
} |
| 2264 |
|
| 2265 |
HdLauncherTree * |
| 2266 |
hd_app_mgr_get_tree () |
| 2267 |
{ |
| 2268 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (hd_app_mgr_get ()); |
| 2269 |
return priv->tree; |
| 2270 |
} |
| 2271 |
|
| 2272 |
#ifndef G_DEBUG_DISABLE |
| 2273 |
void |
| 2274 |
hd_app_mgr_dump_app_list (gboolean only_running) |
| 2275 |
{ |
| 2276 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (hd_app_mgr_get ()); |
| 2277 |
GList *apps = priv->running_apps; |
| 2278 |
|
| 2279 |
g_debug ("%s:\n", __FUNCTION__); |
| 2280 |
for (; apps; apps = apps->next) |
| 2281 |
{ |
| 2282 |
HdRunningApp *app = HD_RUNNING_APP (apps->data); |
| 2283 |
|
| 2284 |
if (!only_running || hd_running_app_get_state (app) == HD_APP_STATE_SHOWN) |
| 2285 |
{ |
| 2286 |
g_debug("\tapp=%p, id=%s, pid=%d, state=%d\n", |
| 2287 |
app, |
| 2288 |
hd_running_app_get_id (app), |
| 2289 |
hd_running_app_get_pid (app), |
| 2290 |
hd_running_app_get_state (app)); |
| 2291 |
} |
| 2292 |
} |
| 2293 |
} |
| 2294 |
|
| 2295 |
void |
| 2296 |
hd_app_mgr_dump_tree () |
| 2297 |
{ |
| 2298 |
HdAppMgrPrivate *priv = HD_APP_MGR_GET_PRIVATE (hd_app_mgr_get ()); |
| 2299 |
GList *items = hd_launcher_tree_get_items (priv->tree); |
| 2300 |
|
| 2301 |
g_debug ("%s:\n", __FUNCTION__); |
| 2302 |
for (; items; items = items->next) |
| 2303 |
{ |
| 2304 |
HdLauncherItem *item = HD_LAUNCHER_ITEM (items->data); |
| 2305 |
|
| 2306 |
g_debug("\titem=%p, id=%s, category=%s\n", |
| 2307 |
item, |
| 2308 |
hd_launcher_item_get_id (item), |
| 2309 |
hd_launcher_item_get_category (item)); |
| 2310 |
} |
| 2311 |
} |
| 2312 |
|
| 2313 |
#endif /* G_DEBUG_DISABLE */ |