Running, Stopped, Killed? A user shouldn’t care

To put it straight: A user should be able to invoke an arbitrary number of applications and experience neither a slow-down nor ever have to wonder whether an app is already running. It is just there whenever it is invoked and the system takes care of the rest.

When we started working on Ubuntu Touch roughly a year ago, our primary focus was the enablement of central HW components such that Ubuntu would be able to run on common mobile form factors. However, after having reached the goal of being able to leverage HW acceleration for UI, media decoding and accessing the on-board sensors, we started thinking about our application model that we wanted to deeply integrate with the OS. From a user’s and a developer’s perspective, our primary goals are:

  • Provide a consistent application model that spans installation, execution and de-installation of apps.
  • Ensure security at all stages and account for the fact that apps have to be considered harmful.
  • Ensure a seamless multi-tasking experience that is transparent to the user and does not require to think in terms of running/not running.
  • Make the application model as easy to develop with as possible.

From the system’s point of view, our objectives are:

  • Integrate a well-defined confinement model deep within the system.
  • Enable the system to aggressively control the resource consumption of apps.
  • Enable a seamless transition to the converged world.

Every single objective listed before is a challenge on its own. On top, they are interdependent and even conflicting at times. However, one of the most fundamental building blocks of the overall application model is the application lifecycle and this blog post  is dedicated to explaining both our lifecycle model and policies. From a user’s perspective

A mobile device is an environment offering a limited set of computing resources, i.e., CPU cycles, main memory, GPU cycles, graphics memory and power. Running applications are competing for these resources and we have to assume that applications are greedy, trying to use as much of the available resources as possible. The user expects the system to ensure a fluent user experience while providing the longest possible battery life at the same time. On top of this, the user should not be required to carry out any sort of process management tasks or to build a mental model of the different run states an app can be in. Ultimately, the application lifecycle should be completely transparent to the user and multi-tasking should be seamless.

A solution to the problem needs to satisfy the following additional constraints:

  1. The application lifecycle model should be easy to develop with. That is, the changes to the well-known process state machine should be as small as possible, providing sensible and robust fallback behavior.
  2. As Ubuntu is working towards a converged world, the lifecycle model needs to be adaptable to a range of different scenarios: From mobile phones, over tablets to classic desktop environments. The differences should be transparent to the developer and both applications and the overall system need to be able to transition seamlessly from one use-case to the other. This is especially important when thinking about the Ubuntu Edge, with the phone being a full-featured desktop/laptop replacement when docked or connected to a large screen.

The Application Lifecycle Model

As noted earlier, one of our goals is the ability to define different lifecycle policies and swap them out dynamically at runtime to account for different usage scenarios. We want to minimize the impact on developers when moving to a converged world and make lifecycle policy changes and decisions transparent to a user and a developer alike. To this end, we clearly separate the application lifecycle model and the policies that the system executes on top of it. Our current model we are putting in place extends on the well-known process state machine as presented in the following diagram:

Draft- Application Model (1)

The states are defined as:

  • Focused: The application is visible to the user and guaranteed to be running and provided with all necessary resources.
  • Unfocused: The application is not guaranteed to be running, i.e., it might not be granted CPU or GPU cycles and the policy is free to trigger a state transition to any of the not-running states. In the phone scenario, the app is not visible to the user.
  • Killed: The app’s process image has been removed from main memory.
  • Stopped: The app’s process is sigstop’d.
  • Stateless: The app’s process has been sigkill’d without prior state preservation. Serves as a way to reset an app’s state.

A “transparent” application lifecycle then translates to: Ensure that applications are able to preserve (and subsequently recreate) their state before being transitioned to the “not-running” meta state. This is indicated by dashed state transitions in the diagram. All of these transitions are preceeded by a notification to the app that it is about to be stopped or killed, handing over an archive file that the app can serialize its state to during a grace period. After that, the app is actually transitioned to “not running”. When the app is resurrected, the system provides the archive back to the app and the app recreates its previous state. In the diagram, an interesting aspect becomes visible: As we want to enable lifecycle policies to kill a stopped app, an application needs to preserve its state even if only being sig-stop’ed.

Application Lifecycle Policies

Based on the application lifecycle model presented before we can now start defining policies triggering the state transitions. Classical desktop behavior can be easily expressed in this model, too. The current desktop lifecycle policy never automatically triggers a state transition from the running to the “not-running” state and does not limit resources granted to an app when unfocused.

For version 1 on the phone (only considering the non-converged, standalone phone scenario) we are defining a very strict lifecycle policy. All non-focused apps are not guaranteed to stay in the “running” state and are transitioned to the “not running” state at the policies discretion to aggressively save resources. Today, we are already sigstop’ing app processes whenever they are unfocused and we will go even further and kill unfocused apps when we detect memory pressure (even before the OOM potentially kicks in).

Why are we so strict? We as a platform take on the responsibility to manage the scarce resources of a mobile device as efficiently as possible. We don’t want to leave it up to reviewers or users to identify and capture resource hogging apps. However, this is only version 1 and we consciously decided for a very conservative approach that we can open up gradually going forward as opposed to starting without a clear policy and taking away functionality over time.

Implications of a Strict Lifecycle Policy

Both internally and externally, a lot of discussions have been triggered by the strict lifecycle policy for version 1. We have discussed a multitude of different use-cases and almost all of them are solvable by means of separating apps into an engine and into a UI part. First, separating application logic from the presentation layer is good practice in software design. Second, relying on an engine/background service allows apps to escape the lifecycle “trap” easily by dispatching to an entity that exceeds an apps lifetime as dictated by our lifecycle policy. A nice side-effect is easily testable code.

How does work for version 1 in Ubuntu Touch? In summary, the system will provide a set of system services that cover the most prominent examples and use-cases identified from both external & internal discussions, e.g.:

  • Music playback in the background
  • Downloads happening in the background
  • Alarms/appointments

In this 1st version, apps will not be able to install their own background services/engines in the default setup. However, going forward in time, we will provide a mechanism for apps to hand their engine to the system and have it executing in the background (with resource constrains in place, though).

For readers interested in more details:


3 thoughts on “Running, Stopped, Killed? A user shouldn’t care

  1. Chad

    I’m always concerned when I hear comments like these because I hate all of the current implementations from Apple, Google, and Microsoft. The first mistake that Apple started with, and everyone copied, was that apps don’t have an easy way to be closed. Sure, the OS should handle it gracefully if I don’t close apps, but I also know when I don’t want one open any more and I should have an easy way to close it.

    For my usage, web browsers are the biggest pain because if I load up even one new app and then switch back to the browser it will frequently have to reload the page. And this behavior is consistent across every phone I have tried regardless of OS. As result there are times I won’t switch to another app because I don’t want to lose my place reading an article when I come back.

    Background processing does need to be controlled for power reasons (i.e. flash ads in a tab I can’t see anyway), but terminating an app and removing the user’s ability to prioritize is not an effective solution. Hopefully Ubuntu can figure out a better way.

  2. ThomasVo5 Post author

    For your browser example: That’s less a question of lifecycle management but more a question of correct state preservation in the browser. I don’t think we should solve the latter one by just saying: Do whatever you want.

    For having a way to close an app: Not sure if you have been using Ubuntu Touch recently, but apps can easily be killed/removed from the list of recent apps from the apps lens. Obviously, users should be able to care about app state in this respect.

    As pointed out in the post: We are perfectly aware that we are starting over with a very strict policy. Being smart and coming up with a more elaborate strategy is obviously our goal. But: Right now, we take the path of not guaranteeing anything that we cannot stick with over time.

  3. ianjo

    I have to say I completely agree with Chad.

    My Nokia N900 had a classic process model, so I knew that I could switch apps at any time, and would not loose what I was doing, and could do things like open 10 browser windows and chat for a bit knowing that when I switched back they would have finished loading. Same with opening an e-mail that needed fetching from the server, reading through any site-specific app.

    On Android, you become almost afraid to switch, as apps just stop what they are doing instead of finishing in the background. Site slow? Well, if you decide to chat in the meanwhile, the browser will stop its work. Received a call during a long boss battle? Back to the last save point with you (Final Fantasy for android actually does this).
    I think this is mainly why facebook added the awful chat bubbles, at least your current app is still active and so you solve the usecase of talking for a bit in facebook while waiting for something else, but do not solve the generic “most apps don’t bother to save state decently, or recognize the cases where they must not stop working when they are put in background”.

    Please be very careful about this. I have to say that this is what still makes me hate android “””multi-tasking””” so much.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s