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, &param,
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 */