Operating System Support for Inter-Application Monitoring in Android

Page created by Oscar Watts
 
CONTINUE READING
Operating System Support for Inter-Application
            Monitoring in Android
                      Daniel M. Jackowitz

                          Spring 2013

                 Submitted in partial fulfillment
                   of the requirements of the
            Master of Science in Software Engineering
Abstract
        In the stock Android mobile operating system there exists no mechanism by which an

application is able to observe state changes within other applications. While for most typical

applications this functionality is not necessary, there are particular instances in which it proves

incredibly useful. One such scenario presented in this paper is an app to monitor the behavioral patterns

of users through their device interactions. The addition of this feature also opens up many new

possibilities for more adaptive applications that respond to how the device is being used. This thesis

presents a mechanism by which apps can register to be notified of lifecycle transitions within other

applications, extends the Android Open Source Project to include this mechanism, and ensures that the

security of the device is not compromised and that performance is impacted as little as possible.

                                                                                                        i
Table of Contents
Abstract .......................................................................................................................................................... i

1      Introduction .......................................................................................................................................... 1

    1.1        Motivation..................................................................................................................................... 1

    1.2        System Goals ................................................................................................................................. 1

    1.3        Extending the Potential Uses ........................................................................................................ 2

    1.4        Related Work ................................................................................................................................ 3

2      Android Application Structure .............................................................................................................. 3

3      What to Intercept ................................................................................................................................. 6

4      The Communication Protocol ............................................................................................................... 7

    4.1        General Mechanism ...................................................................................................................... 7

    4.2        Broadcast Actions ......................................................................................................................... 8

    4.3        Intent Extras .................................................................................................................................. 9

5      Permissions ......................................................................................................................................... 10

6      Fragments ........................................................................................................................................... 11

7      Implementation .................................................................................................................................. 15

8      Using the System: Developer Samples................................................................................................ 21

9      Limitations........................................................................................................................................... 25

    9.1        Falsified Broadcasts..................................................................................................................... 25

    9.2        Android Support Library.............................................................................................................. 25

10         Conclusion ....................................................................................................................................... 27

Appendix A: Building Android from Source ................................................................................................ 28

Glossary ....................................................................................................................................................... 30

References .................................................................................................................................................. 31

                                                                                                                                                                   ii
1          Introduction
1.1        Motivation
           The original idea for this project was born out of a very specialized goal – to be able to record

data about application usage on smart devices and send that data to a remote server where a pattern

analysis could be performed and abnormalities in user behavior detected. The intent was to implement

this mechanism in the form of an iOS application and distribute it for the Apple family of mobile devices.

It quickly became apparent, however, that this monitoring of other applications would not be possible

under the strict iOS security model. As a result, Google’s Android mobile operating system emerged as

the new target platform. Like iOS, however, Android provides a security model to protect users from

malicious applications intruding on other apps. This protection therefore also eliminates the possibility

of a simple monitoring app on the Android platform as well.

           Fortunately, the open nature of the Android operating system lends itself to an alternative

approach. Whereas much of the internals of iOS are proprietary to Apple and hidden from developers,

the entirety1 of the Android OS has been open-sourced in the form of the Android Open Source Project

(AOSP) [1]. All code in the AOSP repository, which completely encompasses core Android functionality,

is freely accessible and modifiable by anyone who is interested. Therefore, the goal of the project

migrated away from creating a monitoring app running on a mobile operating system to instead creating

a custom, monitoring-enabled version of the Android OS itself.

1.2        System Goals
           With the need for modification to the operating system itself identified, a high-level list of the

necessary features of the system was outlined. These initial requirements involved augmenting the

Android operating system to:

           Intercept application events

1
    With the exception of some low-level hardware drivers for specific devices.

                                                                                                           1
Log each event to a repository on the device

        Periodically upload the contents of the repository to a web service and then reset the repository

        on success

While this solution will work to accomplish the task at hand, it is a suboptimal design. The major issue is

that some of the responsibilities being moved into the operating system would be more appropriately

handled by an application. Android provides apps with easy ways to both write to local storage [2] and

make HTTP requests [3], so these existing solutions should be leveraged whenever possible. The only

feature of the system truly beyond the capabilities of a standard app is the actual interception of events.

Therefore, the requirements are revised to include:

        An operating system modification to intercept application events

        An application to log, process and upload the events

        A communication protocol between the operating system and the application

This updated solution is much more elegant as it maintains a clear separation between the tasks of the

operating system and the tasks of the application.

1.3     Extending the Potential Uses
        With the processing logic moved out of the operating system and communication being

performed over a defined protocol, it becomes possible to extend the system to cover a much larger set

of potential uses. In order to accomplish the original goal of behavioral monitoring through device

interaction, the monitoring application is particularly interested in other applications’ launch and close

events. The app must also receive timing data regarding both when and for how long the application

was used. Any additional details, such as what URL is opened by the browser app, should also be made

available to the monitor if possible.

        Considering this core set of data, the “What?”, “When?” and “How?” of user interaction, it is not

difficult to imagine other scenarios beyond behavioral monitoring where it may also prove useful. One

                                                                                                         2
simple such case would be providing the ability to define macros of applications. For example, a mobile

device user notices that he or she consistently checks for new e-mail immediately upon waking in the

morning. Utilizing the proposed system, it would be possible for a software developer to create an

application that allows a user to define a macro describing the relationships between the executions of

other applications – in this example, the e-mail app immediately following the alarm clock app. This

macro app then listens for the alarm clock application close event and reacts by automatically launching

the e-mail app. The next logical extension of this idea takes the automation even further – based on

patterns in past events, the app learns what the proper macros should be.

1.4     Related Work
        LogCat is the Android logging system mechanism for collecting and viewing system debug

output [4]. It is most often used as a debugging tool during application development but frequently

logging statements are left in deployed applications either by accident or for the purpose of error

reporting. In addition to providing command line tools, LogCat can also be executed from within an

Android application allowing for programmatic parsing of the logs. Although Android 4.1 added the

restriction that applications are now able to read only the log entries which they themselves have

written, in prior versions of the operating system it is possible for developers to glean some information

about other applications by analyzing the LogCat logs. Parsing the logs relies heavily on heuristics and

guesswork, however, as not all applications log the same information or even any information at all. As a

result, monitoring applications developed in this fashion typically behave quite inconsistently.

                                                                                                        3
2       Android Application Structure
        In order to determine how to best capture Android application events, the structure of the

applications must first be understood. The major component of an Android app is the activity. An

activity is a single, focused thing that the user can do and, typically, corresponds to a single screen in the

user interface [4]. Only a single activity can be active at any given time and there is always an active

activity when the device is in use. This activity is defined as being in the running or foreground state.

        Activities communicate with each other via intents. An intent is an abstract description of an

operation to be performed as well as a container for data related to that operation [5]. An intent falls

into one of two categories – explicit or implicit. An explicit intent specifies the exact target of the intent

by means of its class and package names. Explicit intents are typically used to transition between

activities within a single application. An implicit intent does not specify a target, but rather provides a

set of criteria, in the form of actions and categories, used by the system to find an appropriate target.

Implicit intents are often used to start an activity which is part of a separate application. For example,

when the user clicks on a phone number within the Browser app, it will launch an implicit intent

requesting the system to start an activity which has registered itself as being capable of handling a

phone number. In this case, the action will be android.intent.action.CALL and the phone

number will be passed as a key-value pair in an intent extra. There are many other nuances related to

the use of intents, but the above can be considered as the representative cases.

        At any point in time, an Android activity is in one of four states. As mentioned earlier, the

current activity the user is interacting with is in the running state. An activity becomes paused when it

has lost focus, but is still visible. The standard example of a paused activity is one with a dialog box

partially covering it. A stopped activity is completely obscured by another activity. When an activity

issues an intent to start another activity, it becomes briefly paused, and then stopped, after the newly

started activity enters the running state. In both the paused and stopped states the activity is kept in

                                                                                                            4
memory unless the space is truly needed elsewhere. If the memory used by an activity must be

reclaimed, the activity will then enter the destroyed state. An activity can also request for itself to be

destroyed when it is no longer needed.

        Although the states of activities are managed by the operating system, activities are informed of

transitions through a series of lifecycle callbacks. There are 6 main lifecycle callbacks, each representing

a transition between a pair of the above states as described in Figure 1.

Figure 1: The activity lifecycle callbacks.

        All Android activities must extend the Activity                class, which provides the basic

implementations necessary for interacting with the Android framework classes. Developers then add

functionality to their activities by overriding Activity methods to include additional

implementations. It is very important to note that for many of the activity methods, and particularly for

all of the lifecycle callbacks, it is required that all overriding implementations first call through to the

superclass implementation to ensure that all framework interaction is handled properly. This

requirement is essential to the system as it provides a well-defined set of guaranteed checkpoints at

which activities can be examined. Since all activity implementations must call through to the superclass,

by altering the callback methods in the Activity class itself it becomes possible to intercept the

callbacks for the activities of all applications without requiring any cooperation, or even awareness, on

the part of the developers of said applications.

                                                                                                          5
3        What to Intercept
         After identifying where this interception of activity transitions should occur, the focus is shifted

to defining exactly what data should be extracted from the activities. The goal throughout this process is

to provide enough data to completely represent the most basic components of the state of the activity

at the time of the callback. Firstly, the set of interest for lifecycle callbacks is identified as the six

callbacks in Figure 1 - onCreate(), onStart(), onResume(), onPause(), onStop() and

onDestroy().

         Next, extracting appropriate data fields begins with the obvious – the method of identifying the

activity itself. In Android, activities are identified by a component name. Encapsulated in a

ComponentName object, it is the combination of the package name of the application and the fully

qualified name of the Java class of the activity [6]. Android guarantees that application package names

are unique and Java guarantees that class names are unique within a package, so the component name

serves    this   purpose    perfectly.    The   component       name     of   an    activity   is   returned    by

getComponentName().

         The system must also provide a means of identifying which of the lifecycle callbacks was

invoked, and when. How the former will be conveyed depends largely on the communication protocol

chosen, while the latter is accomplished with a simple timestamp via getSystemTimeMillis().

         The final field is the current intent for the activity. More specifically, this is the result of calling

getIntent() on the activity at the time the callback is invoked. When an activity is started, this field

is initialized to the intent used to start it, but it can be altered by the activity during its lifetime by calling

setIntent(). In general, this intent will contain any extra information passed to the activity for

processing. For example, an intent used to start a browser activity may contain the URL of a page to

load.

                                                                                                                 6
4       The Communication Protocol

4.1     General Mechanism
        Next, a communication protocol is defined to marshal the intercepted activity data back to the

monitor applications. The initial approach designated a particular file within the application space as a

repository. The interception component would then write to this file and the applications would read

from it. The approach has some serious drawbacks, however, as it requires guarding against two

processes accessing, and potentially modifying, the file concurrently. It is also overly restrictive in the

fact that the path of the file is statically restricted to within a single application’s space. Supporting

multiple monitor apps would then require either writing to multiple files or, despite security

recommendations, making the repository file accessible to all apps. Additionally, the responsibility of

managing these files is not clearly defined. This can result in either excessive accumulation or premature

deletion of the event data.

        A much better technique makes use of a broadcast-receive protocol, where an app can register

itself as desiring to be notified when a lifecycle callback occurs. Then, when an event is intercepted,

instead of writing to a file or some other repository the system sends a broadcast containing the data it

has gathered to each of the registered receivers. The important advantage here is that each of the

receivers can process this data independently and in whatever way it deems appropriate. In the example

applications described earlier the behavioral monitor can log all events while the macro app will only

listen for particular events and then respond to them immediately.

        Fortunately, the Android framework provides such a broadcast mechanism in the form of the

BroadcastReceiver [7]. When a broadcast receiver is registered, either statically within an

application manifest or dynamically by a running application, it is associated with one or more actions,

encapsulated in an implicit intent as described earlier. When an activity (or other component) wants to

send   a   broadcast,    it   creates   an   intent   with    the    desired   action   and   then    calls

                                                                                                         7
sendBroadcast(Intent intent) with that intent as the argument. The operating system then

handles delivering the intent to all broadcast receivers registered for that particular action.

4.2     Broadcast Actions
        When defining the protocol, the categorization of actions is an important consideration. At one

extreme is a single action for all lifecycle callback broadcasts, providing an all-or-nothing approach. The

simplicity of this design in both implementation and use is balanced by the inability of apps to register

for only a subset of lifecycle callback broadcasts. This can potentially waste resources both in sending

excessive broadcasts and in performing filtering in the receiving app. The other extreme assigns each

lifecycle callback a unique action, allowing apps to be very selective about what broadcasts they want to

receive at the expense of some complexity in development.

        After some analysis, a compromise between the above approaches was achieved by identifying

a subset of the lifecycle callbacks as foreground lifecycle callbacks. The callbacks in this group are those

which are invoked when the activity transitions either into or out of the running state – specifically,

onResume() and onPause(). As it is anticipated that members of this set will be the only callbacks

many applications using the system are interested in, it is reasonable to separate them logically.

        This distinction of foreground callbacks allows for partitioning of the lifecycle callbacks into two

classes. Each class is then assigned its own broadcast action, shown in Table 1. Note that the sets of

lifecycle callbacks in the two classes are mutually exclusive. One important consequence of this

arrangement is that in order to receive the complete set of lifecycle callbacks an application must

register to receive broadcasts for both actions.

                                                         Broadcast Action
  Lifecycle Callback
                                                      (Activity Class Constant)
    onCreate()
     onStart()                      “android.activity.action.LIFECYCLE_CALLBACK”
      onStop()                               (ACTION_LIFECYCLE_CALLBACK)
   onDestroy()

                                                                                                          8
onResume()            “android.activity.action.FOREGROUND_LIFECYCLE_CALLBACK”
       onPause()                     (ACTION_FOREGROUND_LIFECYCLE_CALLBACK)

Table 1: The activity lifecycle callbacks and their corresponding broadcast actions.

4.3     Intent Extras
        In the Android broadcast-receive mechanism, data payload is passed from broadcaster to

receiver by means of intent extras attached to the broadcast intent. As described earlier, an intent extra

is a key-value pair where the key is a Java String and the value is a Java object that must be either

Parcelable or Serializable. With the payload data already identified, the task here becomes

mapping the data to appropriate keys. This mapping is shown in Table 2.

                                           Key
      Value                                                                            Type
                                (Activity Class Constant)

                   “android.activity.extra.COMPONENT_NAME”                        Parcelable
Activity Name               (EXTRA_COMPONENT_NAME)                             (ComponentName)

                        “android.activity.extra.TIMESTAMP”
  Timestamp                                                                            long
                                 (EXTRA_TIMESTAMP)

                        “android.activity.extra.CALLBACK”
   Callback                                                                            int
                                 (EXTRA_CALLBACK)

                         “android.activity.extra.INTENT”
Current Intent                                                              Parcelable (Intent)
                                  (EXTRA_INTENT)

Table 2: Intent extras for the ACTION_LIFECYCLE_CALLBACK and
ACTION_FOREGROUND_LIFECYCLE_CALLBACK activity lifecycle callback broadcast actions.

        Of particular interest is the data type of the Callback             Constant             Value
                                                                           ON_CREATE               0
value. For consistency and ease of use, integer constants were              ON_START               1
                                                                           ON_RESUME               2
defined within the Activity class to correspond to each of the              ON_PAUSE               3
                                                                             ON_STOP               4
lifecycle callbacks. These values are shown in Table 3. Developers        ON_DESTROY               5
                                                           Table 3: Activity lifecycle callback
are recommended to always use the constant rather than the constants.

integer value itself.

                                                                                                         9
5       Permissions
        The Android security model is a complex structure stretching across all layers of the system. For

the purposes of this project, however, it is sufficient to consider enforcement only at the permission

level. Permissions in Android are a way to ensure that protected features of the device are not accessed

without the user’s consent [8]. Examples of protected features include both hardware components, such

as turning on the Bluetooth radio, and software components, such as reading the user’s list of contacts.

Most simply, a protected feature is any feature that can be easily exploited in a malicious way. From this

definition, it is clear that intercepting events from another application qualifies as a protected feature

and therefore must be accessible only with permission. The remaining question is the appropriate

coarseness of the access control.

        With broadcasts being sent for two separate actions, the options regarding permissions are

effectively narrowed down to either a separate permission for each action, or a single permission

encompassing both actions. Considering the fact that the permission model is intended largely as a

benefit to the end user, a distinct permission for each broadcast action is chosen in order to make it

more explicit to the user to what extent the requesting app is able to monitor other applications. The

two permissions, along with the broadcast actions they protect, are shown in Table 4.

                        Permission                                         Broadcast Action

      “android.permission.MONITOR_LIFECYCLE”                             LIFECYCLE_CALLBACK

“android.permission.MONITOR_FOREGROUND_LIFECYCLE”                 FOREGROUND_LIFECYCLE_CALLBACK

Table 4: Permissions and the activity lifecycle callback broadcast actions they protect.

                                                                                                       10
6       Fragments
        Until now, all design considerations have been working under the simplifying assumption that all

foreground components with which users interact are activities. In early versions of Android this

assumption held true but with Android 3.0 came the concept of fragments. In the context of this project

it is easiest to think of a fragment as a sub-activity. Like an activity, each fragment represents something

that the user can do. A fragment also has its own lifecycle events. The key difference is that a fragment

is a lightweight component - it must always be embedded within an activity [9]. Fragments can be

combined in ways that allow for complex multi-pane layouts across devices of vastly different screen

dimensions and can be reused across activities. Consider the typical use of fragments illustrated in

Figure 2.

Figure 2: Fragments displayed serially on a phone and side-by-side on a tablet.

        Fragments are also dynamic. They can be added to and removed from an activity while the

activity is running. For this reason, in order to get a true picture of the state of the application the

system must monitor not only activity lifecycle callbacks but also fragment lifecycle callbacks. From the

perspective of developers, implementing a fragment very closely parallels implementing an activity. All

fragment implementations extend the Fragment class. Functionality is added by overriding methods

of this class, including the lifecycle callbacks. As with activities, when overriding these methods

                                                                                                         11
fragments must call through to the superclass implementations to ensure proper interaction with the

framework. Although fragments have some additional lifecycle callbacks not present for activities, these

callbacks either overlap with those of the parent activity, such as onActivityCreated(), or are

directly related to another fragment callback, such as onAttach() immediately preceding

onCreate(). Therefore the domain is restricted to only those callbacks present for both activity and

fragment components. Following the model used for activities, broadcast actions are assigned to the

fragment lifecycle callbacks as shown in Table 5.

                                                          Broadcast Action
  Lifecycle Callback
                                                     (Fragment Class Constant)
    onCreate()
     onStart()                      “android.fragment.action.LIFECYCLE_CALLBACK”
      onStop()                               (ACTION_LIFECYCLE_CALLBACK)
   onDestroy()
    onResume()              “android.fragment.action.FOREGROUND_LIFECYCLE_CALLBACK”
     onPause()                       (ACTION_FOREGROUND_LIFECYCLE_CALLBACK)

Table 5: The fragment lifecycle callbacks and their corresponding broadcast actions.

        Overall, the intent extras containing the data payload for fragments are also very similar to

those used for the activity lifecycle callback broadcast actions, with two key differences. Since fragments

can be reused across activities, the intent includes an additional field to identify the current parent

activity of the fragment at the time of the callback. It is also possible to remove the extra relating to the

current intent as this is a concept specific to activities. The intent extras for the fragment lifecycle

callback actions are outlined in Table 6.

                                            Key
  Value                                                                                       Type
                                  (Fragment Class Constant)

Fragment               “android.fragment.extra.COMPONENT_NAME”                           Parcelable
  Name                          (EXTRA_COMPONENT_NAME)                                (ComponentName)

 Parent
                “android.fragment.extra.PARENT_COMPONENT_NAME”                           Parcelable
 Activity               (EXTRA_PARENT_COMPONENT_NAME)                                 (ComponentName)
  Name

                                                                                                          12
“android.fragment.extra.TIMESTAMP”
Timestamp                                                                                      long
                                  (EXTRA_TIMESTAMP)

                         “android.fragment.extra.CALLBACK”
 Callback                                                                                      int
                                  (EXTRA_CALLBACK)

Table 6: Intent extras for the ACTION_LIFECYCLE_CALLBACK and ACTION_LIFECYCLE_CALLBACK
fragment lifecycle callback broadcast actions.

        As was the case with for the activity broadcast actions,               Constant               Value
                                                                              ON_CREATE                0
the possible values for the Callback intent extra were enumerated              ON_START                1
                                                                              ON_RESUME                2
in the form of integer constants in the Fragment class. These                  ON_PAUSE                3
                                                                                ON_STOP                4
constants are listed in Table 7.                                             ON_DESTROY                5
                                                           Table 7: Fragment lifecycle callback
        Rather than defining a new set of permissions, the constants.

permissions defined earlier to protect the activity broadcasts are reused for protecting the fragment

lifecycle callback broadcasts. This is possible because to the end user there is no practical difference

between activities and fragments. In fact, introducing a separate set of permissions would likely cause

additional confusion for a user. For this reason, the wording shown to the end user regarding

permissions sacrifices accuracy for understandability by describing everything in terms of “applications”

rather than “activities” or “fragments”.

        Although it would have been possible to group the fragment lifecycle callback broadcasts with

those of activity, this was not done for two reasons. Firstly, using distinct broadcast actions for activities

and fragments allows applications to selectively register to receive only activity callbacks, only fragment

callbacks, or both. This proves quite valuable in reducing the number of unnecessary broadcasts sent as

well as the amount of filtering that must be done on the receiving end if the application is only

interested in a subset of these events.

        Secondly, by keeping the two separate the system is more easily adaptable to future changes.

Consider the case where a new, interesting lifecycle callback is added to activities but not to fragments.

With this implementation it is simple to make the necessary changes to one component without

                                                                                                           13
impacting the other. Combined, the above advantages were determined to be worth the trade-off of a

small amount of code duplication.

                                                                                               14
7         Implementation
          With the design outlined clearly above, the implementation is fairly straightforward. First, the

framework manifest file is edited to include the definition of the new permissions.

frameworks/base/core/res/AndroidManifest.xml

    ...

    ...

          Here two new permissions are added to the SYSTEM_TOOLS permission group. Although an

additional permission group could have been defined specifically for this purpose, a review of the

existing members of SYSTEM_TOOLS revealed it to be an appropriate category for the monitoring

permissions. For example, SYSTEM_TOOLS also contains the GET_TASKS permission, which allows an

app to retrieve the list of currently running applications. The protection level for both new permissions

is declared as “dangerous”, which means that the user will always be asked to explicitly consent

whenever an app requesting the permission is installed. The label and description attributes

define exactly what will be displayed to the user at this time and are encapsulated in string resources.

Adding these resources is the next step.

frameworks/base/core/res/res/strings.xml

    ...

    monitor foreground applications
    Allows the app to receive broadcasts
        when Activities in other applications enter or leave the foreground.

    monitor application lifecycle events

                                                                                                       15
Allows the app to receive broadcasts
        when Activities in other applications make Activity lifecycle transitions.

    ...

          The remaining modifications are made within the Activity and Fragment Java classes.

These additions can be divided into two categories – constant definitions and broadcasts. Constant

definitions occur at the beginning of the class definition and encompass the broadcast actions and intent

extras described by the design. The broadcast components are a little more complex. Within each

relevant lifecycle callback method code is added to create an intent with the proper broadcast action,

collect the necessary data from the activity and package it within the intent extras, and request that the

system send a broadcast with the intent using the appropriate permission.

frameworks/base/core/java/android/app/Activity.java

public class Activity {
    ...

    private static final String MONITOR_FOREGROUND_LIFECYCLE_PERM =
            android.Manifest.permission.MONITOR_FOREGROUND_LIFECYCLE;
    private static final String MONITOR_LIFECYCLE_PERM =
            android.Manifest.permission.MONITOR_LIFECYCLE;

    public static final String ACTION_FOREGROUND_LIFECYCLE_CALLBACK =
            "android.activity.action.FOREGROUND_LIFECYCLE_CALLBACK";
    public static final String ACTION_LIFECYCLE_CALLBACK =
            "android.activity.action.LIFECYCLE_CALLBACK";

    public static final String EXTRA_COMPONENT_NAME =
            "android.activity.extra.COMPONENT_NAME";

    public static final String EXTRA_TIMESTAMP =
            "android.activity.extra.TIMESTAMP";

    public static final String EXTRA_CALLBACK =
            "android.activity.extra.CALLBACK";

    public   static   final   int   ON_CREATE = 0;
    public   static   final   int   ON_START = 1;
    public   static   final   int   ON_RESUME = 2;
    public   static   final   int   ON_PAUSE = 3;
    public   static   final   int   ON_STOP = 4;
    public   static   final   int   ON_DESTROY = 5;

    public static final String EXTRA_INTENT =
            "android.activity.extra.INTENT";

    ...

    protected void onCreate(Bundle savedInstanceState) {
        if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState);
        if (mLastNonConfigurationInstances != null) {
            mAllLoaderManagers = mLastNonConfigurationInstances.loaders;
        }
        if (mActivityInfo.parentActivityName != null) {
            if (mActionBar == null) {

                                                                                                       16
mEnableDefaultActionBarUp = true;
         } else {
             mActionBar.setDefaultDisplayHomeAsUpEnabled(true);
         }
      }
      if (savedInstanceState != null) {
          Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
          mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
                  ? mLastNonConfigurationInstances.fragments : null);
      }
      Intent broadcast = new Intent(ACTION_LIFECYCLE_CALLBACK);
      broadcast.putExtra(EXTRA_COMPONENT_NAME, getComponentName());
      broadcast.putExtra(EXTRA_TIMESTAMP, System.currentTimeMillis());
      broadcast.putExtra(EXTRA_CALLBACK, ON_CREATE);
      broadcast.putExtra(EXTRA_INTENT, getIntent());
      sendBroadcast(broadcast, MONITOR_LIFECYCLE_PERM);
      mFragments.dispatchCreate();
      getApplication().dispatchActivityCreated(this, savedInstanceState);
      mCalled = true;
}

...

protected void onStart() {
    if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStart " + this);
    mCalled = true;

      if (!mLoadersStarted) {
          mLoadersStarted = true;
          if (mLoaderManager != null) {
              mLoaderManager.doStart();
          } else if (!mCheckedForLoaderManager) {
              mLoaderManager = getLoaderManager(-1, mLoadersStarted, false);
          }
          mCheckedForLoaderManager = true;
      }

      Intent broadcast = new Intent(ACTION_LIFECYCLE_CALLBACK);
      broadcast.putExtra(EXTRA_COMPONENT_NAME, getComponentName());
      broadcast.putExtra(EXTRA_TIMESTAMP, System.currentTimeMillis());
      broadcast.putExtra(EXTRA_CALLBACK, ON_START);
      broadcast.putExtra(EXTRA_INTENT, getIntent());
      sendBroadcast(broadcast, MONITOR_LIFECYCLE_PERM);
      getApplication().dispatchActivityStarted(this);
}

...

protected void onResume() {
    if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this);
    Intent broadcast = new Intent(ACTION_FOREGROUND_LIFECYCLE_CALLBACK);
    broadcast.putExtra(EXTRA_COMPONENT_NAME, getComponentName());
    broadcast.putExtra(EXTRA_TIMESTAMP, System.currentTimeMillis());
    broadcast.putExtra(EXTRA_CALLBACK, ON_RESUME);
    broadcast.putExtra(EXTRA_INTENT, getIntent());
    sendBroadcast(broadcast, MONITOR_FOREGROUND_LIFECYCLE_PERM);
    getApplication().dispatchActivityResumed(this);
    mCalled = true;
}

...

protected void onPause() {
    if (DEBUG_LIFECYCLE) Slog.v(TAG, "onPause " + this);
    Intent broadcast = new Intent(ACTION_FOREGROUND_LIFECYCLE_CALLBACK);
    broadcast.putExtra(EXTRA_COMPONENT_NAME, getComponentName());
    broadcast.putExtra(EXTRA_TIMESTAMP, System.currentTimeMillis());
    broadcast.putExtra(EXTRA_CALLBACK, ON_PAUSE);
    broadcast.putExtra(EXTRA_INTENT, getIntent());
    sendBroadcast(broadcast, MONITOR_FOREGROUND_LIFECYCLE_PERM);
    getApplication().dispatchActivityPaused(this);

                                                                                 17
mCalled = true;
    }

    ...

    protected void onStop() {
        if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this);
        if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
        Intent broadcast = new Intent(ACTION_LIFECYCLE_CALLBACK);
        broadcast.putExtra(EXTRA_COMPONENT_NAME, getComponentName());
        broadcast.putExtra(EXTRA_TIMESTAMP, System.currentTimeMillis());
        broadcast.putExtra(EXTRA_CALLBACK, ON_STOP);
        broadcast.putExtra(EXTRA_INTENT, getIntent());
        sendBroadcast(broadcast, MONITOR_LIFECYCLE_PERM);
        getApplication().dispatchActivityStopped(this);
        mCalled = true;
    }

    ...

    protected void onDestroy() {
        if (DEBUG_LIFECYCLE) Slog.v(TAG, "onDestroy " + this);
        mCalled = true;

          // dismiss any dialogs we are managing.
          if (mManagedDialogs != null) {
              final int numDialogs = mManagedDialogs.size();
              for (int i = 0; i < numDialogs; i++) {
                  final ManagedDialog md = mManagedDialogs.valueAt(i);
                  if (md.mDialog.isShowing()) {
                      md.mDialog.dismiss();
                  }
              }
              mManagedDialogs = null;
          }

          // close any cursors we are managing.
          synchronized (mManagedCursors) {
              int numCursors = mManagedCursors.size();
              for (int i = 0; i < numCursors; i++) {
                  ManagedCursor c = mManagedCursors.get(i);
                  if (c != null) {
                      c.mCursor.close();
                  }
              }
              mManagedCursors.clear();
          }

          // Close any open search dialog
          if (mSearchManager != null) {
              mSearchManager.stopSearch();
          }

          Intent broadcast = new Intent(ACTION_LIFECYCLE_CALLBACK);
          broadcast.putExtra(EXTRA_COMPONENT_NAME, getComponentName());
          broadcast.putExtra(EXTRA_TIMESTAMP, System.currentTimeMillis());
          broadcast.putExtra(EXTRA_CALLBACK, ON_DESTROY);
          broadcast.putExtra(EXTRA_INTENT, getIntent());
          sendBroadcast(broadcast, MONITOR_LIFECYCLE_PERM);
          getApplication().dispatchActivityDestroyed(this);
    }
    ...
}

frameworks/base/core/java/android/app/Fragment.java

public class Fragment {
    ...

    private static final String MONITOR_FOREGROUND_LIFECYCLE_PERM =
            android.Manifest.permission.MONITOR_FOREGROUND_LIFECYCLE;

                                                                                 18
private static final String MONITOR_LIFECYCLE_PERM =
        android.Manifest.permission.MONITOR_LIFECYCLE;

public static final String ACTION_FOREGROUND_LIFECYCLE_CALLBACK =
        "android.fragment.action.FOREGROUND_LIFECYCLE_CALLBACK";
public static final String ACTION_LIFECYCLE_CALLBACK =
        "android.fragment.action.LIFECYCLE_CALLBACK";

public static final String EXTRA_COMPONENT_NAME =
        "android.fragment.extra.COMPONENT_NAME";

public static final String EXTRA_PARENT_COMPONENT_NAME =
        "android.fragment.extra.PARENT_COMPONENT_NAME";

public static final String EXTRA_TIMESTAMP =
        "android.fragment.extra.TIMESTAMP";

public static final String EXTRA_CALLBACK =
        "android.fragment.extra.CALLBACK";

public   static   final   int   ON_CREATE = 0;
public   static   final   int   ON_START = 1;
public   static   final   int   ON_RESUME = 2;
public   static   final   int   ON_PAUSE = 3;
public   static   final   int   ON_STOP = 4;
public   static   final   int   ON_DESTROY = 5;

...

public void onCreate(Bundle savedInstanceState) {
    Intent broadcast = new Intent(ACTION_LIFECYCLE_CALLBACK);
    ComponentName parentComponentName = getActivity().getComponentName();
    ComponentName componentName = new ComponentName(
            parentComponentName.getPackageName(), getClass().getName());
    broadcast.putExtra(EXTRA_COMPONENT_NAME, componentName);
    broadcast.putExtra(EXTRA_PARENT_COMPONENT_NAME, parentComponentName);
    broadcast.putExtra(EXTRA_TIMESTAMP, System.currentTimeMillis());
    broadcast.putExtra(EXTRA_CALLBACK, ON_CREATE);
    getActivity().sendBroadcast(broadcast, MONITOR_LIFECYCLE_PERM);
    mCalled = true;
}

...

public void onStart() {
    mCalled = true;

      if (!mLoadersStarted) {
          mLoadersStarted = true;
          if (!mCheckedForLoaderManager) {
              mCheckedForLoaderManager = true;
              mLoaderManager = mActivity.getLoaderManager(mIndex, mLoadersStarted, false);
          }
          if (mLoaderManager != null) {
              mLoaderManager.doStart();
          }
      }
      Intent broadcast = new Intent(ACTION_LIFECYCLE_CALLBACK);
      ComponentName parentComponentName = getActivity().getComponentName();
      ComponentName componentName = new ComponentName(
              parentComponentName.getPackageName(), getClass().getName());
      broadcast.putExtra(EXTRA_COMPONENT_NAME, componentName);
      broadcast.putExtra(EXTRA_PARENT_COMPONENT_NAME, parentComponentName);
      broadcast.putExtra(EXTRA_TIMESTAMP, System.currentTimeMillis());
      broadcast.putExtra(EXTRA_CALLBACK, ON_START);
      getActivity().sendBroadcast(broadcast, MONITOR_LIFECYCLE_PERM);
}

...

public void onResume() {

                                                                                             19
Intent broadcast = new Intent(ACTION_FOREGROUND_LIFECYCLE_CALLBACK);
          ComponentName parentComponentName = getActivity().getComponentName();
          ComponentName componentName = new ComponentName(
                  parentComponentName.getPackageName(), getClass().getName());
          broadcast.putExtra(EXTRA_COMPONENT_NAME, componentName);
          broadcast.putExtra(EXTRA_PARENT_COMPONENT_NAME, parentComponentName);
          broadcast.putExtra(EXTRA_TIMESTAMP, System.currentTimeMillis());
          broadcast.putExtra(EXTRA_CALLBACK, ON_RESUME);
          getActivity().sendBroadcast(broadcast, MONITOR_FOREGROUND_LIFECYCLE_PERM);
          mCalled = true;
    }

    ...

    public void onPause() {
        Intent broadcast = new Intent(ACTION_FOREGROUND_LIFECYCLE_CALLBACK);
        ComponentName parentComponentName = getActivity().getComponentName();
        ComponentName componentName = new ComponentName(
                parentComponentName.getPackageName(), getClass().getName());
        broadcast.putExtra(EXTRA_COMPONENT_NAME, componentName);
        broadcast.putExtra(EXTRA_PARENT_COMPONENT_NAME, parentComponentName);
        broadcast.putExtra(EXTRA_TIMESTAMP, System.currentTimeMillis());
        broadcast.putExtra(EXTRA_CALLBACK, ON_PAUSE);
        getActivity().sendBroadcast(broadcast, MONITOR_FOREGROUND_LIFECYCLE_PERM);
        mCalled = true;
    }

    ...

    public void onStop() {
        Intent broadcast = new Intent(ACTION_LIFECYCLE_CALLBACK);
        ComponentName parentComponentName = getActivity().getComponentName();
        ComponentName componentName = new ComponentName(
                parentComponentName.getPackageName(), getClass().getName());
        broadcast.putExtra(EXTRA_COMPONENT_NAME, componentName);
        broadcast.putExtra(EXTRA_PARENT_COMPONENT_NAME, parentComponentName);
        broadcast.putExtra(EXTRA_TIMESTAMP, System.currentTimeMillis());
        broadcast.putExtra(EXTRA_CALLBACK, ON_STOP);
        getActivity().sendBroadcast(broadcast, MONITOR_LIFECYCLE_PERM);
        mCalled = true;
    }

    ...

    public void onDestroy() {
        mCalled = true;
        //Log.v("foo", "onDestroy: mCheckedForLoaderManager=" + mCheckedForLoaderManager
        //        + " mLoaderManager=" + mLoaderManager);
        if (!mCheckedForLoaderManager) {
            mCheckedForLoaderManager = true;
            mLoaderManager = mActivity.getLoaderManager(mIndex, mLoadersStarted, false);
        }
        if (mLoaderManager != null) {
            mLoaderManager.doDestroy();
        }
        Intent broadcast = new Intent(ACTION_LIFECYCLE_CALLBACK);
        ComponentName parentComponentName = getActivity().getComponentName();
        ComponentName componentName = new ComponentName(
                parentComponentName.getPackageName(), getClass().getName());
        broadcast.putExtra(EXTRA_COMPONENT_NAME, componentName);
        broadcast.putExtra(EXTRA_PARENT_COMPONENT_NAME, parentComponentName);
        broadcast.putExtra(EXTRA_TIMESTAMP, System.currentTimeMillis());
        broadcast.putExtra(EXTRA_CALLBACK, ON_DESTROY);
        getActivity().sendBroadcast(broadcast, MONITOR_LIFECYCLE_PERM);
    }

    ...
}

                                                                                           20
8          Using the System: Developer Samples
           The following examples provide annotated sample code for interacting with the monitoring

system. The first example shows how to simply log all activity foreground transition events to a file.

After installing the custom platform version of the Android SDK2, create a new project and choose the

AOSP build as the minimum SDK version. Open AndroidManifest.xml and ensure that the

android:minSdkVersion attribute is set to “AOSP”. Next declare the application as using the

android.permission.MONITOR_FOREGROUND_LIFECYCLE permission. Finally, add a receiver

for the android.activity.action.FORGROUND_LIFECYCLE_CALLBACK action.

AndroidManifest.xml

           Next the implementation of the broadcast receiver defined in AndroidManifest.xml is

filled in. This is accomplished by overriding the onReceive() method to handle the intent broadcast

2
    Simply copy the platform/android-x.x.x directory to android-sdk/platforms in the SDK installation.

                                                                                                         21
by the system. For this example onReceive() simply extracts the intent extras for the timestamp, the

component name, and the callback type and formats them in a comma-delimited string. It then appends

this string to a file in the application space.

LifecycleReceiver.java

import java.io.IOException;
import java.io.OutputStreamWriter;

import   android.app.Activity;
import   android.content.BroadcastReceiver;
import   android.content.ComponentName;
import   android.content.Context;
import   android.content.Intent;

public class LifecycleReceiver extends BroadcastReceiver {
    private static final String TAG = "LifecycleReceiver";
    private static final String UNKNOWN = "Unknown";
    private static final String OPENED = "Opened";
    private static final String CLOSED = "Closed";

    @Override
    public void onReceive(Context context, Intent intent) {
        long timestamp = intent.getLongExtra(Activity.EXTRA_TIMESTAMP, -1);
        String className = ((ComponentName) intent
                .getParcelableExtra(Activity.EXTRA_COMPONENT_NAME)).getClassName();
        int callback = intent.getIntExtra(Activity.EXTRA_CALLBACK, -1);
        String state = UNKNOWN;
        switch (callback) {
            case Activity.ON_PAUSE:
                state = CLOSED;
                break;
            case Activity.ON_RESUME:
                state = OPENED;
                break;
        }
        String csv = timestamp + "," + className + "," + state + "\n";

         OutputStreamWriter out = null;
         try {
             out = new OutputStreamWriter(context.openFileOutput(
                     "lifecycle.log", Context.MODE_APPEND));
             out.write(csv);
             out.flush();
         } catch (Exception e) {
             e.printStackTrace();
         } finally {
             try {
                 out.close();
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
    }
}

         Running the above application on a device for a short period of time will produce a log file

similar to the following:

                                                                                                  22
1363879283106,com.android.launcher2.Launcher,Opened
        1363879295661,com.android.launcher2.Launcher,Closed
        1363879295672,com.android.email.activity.EmailActivity,Opened
        1363879305036,com.android.email.activity.EmailActivity,Closed
        1363879305055,com.android.launcher2.Launcher,Opened
        1363879305062,com.android.launcher2.Launcher,Closed
        1363879305189,com.android.browser.BrowserActivity,Opened
        1363879319226,com.android.browser.BrowserActivity,Closed
        1363879319256,com.android.launcher2.Launcher,Opened
        1363879320468,com.android.launcher2.Launcher,Closed
        1363879353116,com.android.launcher2.Launcher,Opened
        1363879366429,com.android.launcher2.Launcher,Closed
        1363879366889,com.android.contacts.activities.PeopleActivity,Opened
        1363879369170,com.android.contacts.activities.PeopleActivity,Closed
        1363879369180,com.android.launcher2.Launcher,Opened
        1363879371055,com.android.launcher2.Launcher,Closed
        1363879371128,com.android.deskclock.DeskClock,Opened
        1363879372918,com.android.deskclock.DeskClock,Closed
        1363879372934,com.android.launcher2.Launcher,Opened
        1363879374060,com.android.launcher2.Launcher,Closed
        1363879374266,com.android.calendar.AllInOneActivity,Opened
        1363879376829,com.android.calendar.AllInOneActivity,Closed
        1363879376845,com.android.launcher2.Launcher,Opened
        1363879386740,com.android.launcher2.Launcher,Closed

        While much more sophisticated parsing and pattern analysis can be easily performed on data in

this, or similar, formats, for the purposes of this example it is sufficient to note the obvious. The log

begins with the launcher, or the user’s “home screen”. After that, many of the apps should be fairly

recognizable from the activity names. This record shows the e-mail, browser, contacts, clock and

calendar apps being used, in that order. The presence of the launcher activity between each app is

expected, as the user returns there when exiting an application. The launcher, or any other app for that

matter, being opened twice in a row is also not uncommon and indicates that the device screen has

been turned off and then back on, temporarily pausing the foreground activity.

        The second developer sample takes a more active approach to using the system. It is the

simplest version of the macro app described earlier in the document. This example requires no changes

to the AndroidManifest.xml used in the first example. All modifications are made in the

LifecycleReceiver.java file, as shown below.

LifecycleReceiver.java

                                                                                                      23
import   android.app.Activity;
import   android.content.BroadcastReceiver;
import   android.content.ComponentName;
import   android.content.Context;
import   android.content.Intent;
import   android.net.Uri;
import   android.util.Log;

public class LifecycleReceiver extends BroadcastReceiver {
    private static final String TAG = "LifecycleReceiver";
    private static final String TARGET_CLASS_NAME = "com.android.deskclock.AlarmAlert";

    @Override
    public void onReceive(Context context, Intent intent) {
        String className = ((ComponentName) intent
                .getParcelableExtra(Activity.EXTRA_COMPONENT_NAME))
                .getClassName();
        int callback = intent.getIntExtra(Activity.EXTRA_CALLBACK, -1);
        if (className.equals(TARGET_CLASS_NAME) && callback == Activity.ON_PAUSE) {
            Intent emailIntent = context.getPackageManager()
                    .getLaunchIntentForPackage(“com.android.email”);
            emailIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(emailIntent);
        }
    }
}

         There are two main responsibilities of this broadcast receiver. The first is to recognize the user

dismissing the alarm alert. This is done by retrieving the component name and lifecycle callback extras

from the broadcast and comparing them with the name of the alarm alert activity in the desk clock

application and the ON_PAUSE event, respectively. In the case where both items match, the second

responsibility of launching the e-mail client application is executed3. The end result is that every time the

user dismisses an alarm, the e-mail client is automatically opened.

3
 From an application design perspective, using an explicit intent to launch the e-mail app is not best practice. It is
used in this example for the sake of simplicity.

                                                                                                                     24
9       Limitations

        While the system very well suits the purpose for which it was developed, there do exist

limitations which may hinder its general applicability to other problems. These limitations are described

in the following sections.

9.1     Falsified Broadcasts
        Because the lifecycle callback broadcasts are sent (unwittingly) by the activities and fragments

that are being monitored, it is currently not possible to prevent an application from “spamming”

broadcasts and bombarding receivers with false data. The impact of this limitation is that a malicious

application can effectively corrupt the lifecycle callback data and potentially break the functionality of

any application which relies on it.

        Possible solutions to this problem all rely on completely revamping the design to move the

interception out of the Activity and Fragment classes and into a lower-level framework, or

possibly even kernel, component. If the changes introduced by this system were to be merged back into

the Android Open Source Project for use on a much larger scale this redesign would likely require

further consideration.

9.2     Android Support Library
        Although fragments were introduced in Android 3.0, they have been back-ported to Android 1.6

through the Android Support Library [10]. Developers using fragments in applications targeting lower

API levels can do so by packaging this library, which includes its own implementations of Fragment

and other related classes, with their application. In cases where the support library is present, the

implementations from the library will always be used rather than those from the operating system and,

since these implementations are entirely distinct from those which this project has modified, the

interception code for fragment lifecycle callbacks will not be available.

                                                                                                       25
At first this may seem like a problem, but the percentage of applications which actually fall into

this category is very small and only going to decrease as the older operating system versions become

obsolete. Additionally, only the fragment callbacks are missed – all activity callbacks are still intercepted

properly. For these reasons, there is no need to address this limitation any further than simply

documenting it.

                                                                                                          26
10      Conclusion
        This project concludes with a fully functional, modified version of the Android mobile operating

system extended to include support for inter-application monitoring. This version of the operating

system has been loaded onto multiple Google Nexus 7 tablets and is indistinguishable from the stock

operating system in terms of performance. In addition, several proof-of-concept apps utilizing the

monitoring capabilities have been developed by the author and all perform as expected and offer

functionality that would otherwise not be possible. Among these apps is the behavioral monitoring app

that originally motivated the project. The author will continue the development of this application in

particular as both a flagship for the operating system modifications and for use in a behavioral study.

        The author also has plans to contact members of the Android Open Source Project team at

Google to inquire whether the changes introduced to the system by this project would be appropriate

for merging back into the main branch of Android. There are existing formal requests for features similar

to the monitoring capabilities presented here, so the changes may very well be welcomed by the

community. If this is the case, the author has every intention of contributing.

                                                                                                          27
Appendix A: Building Android from Source
The following sections describe the steps taken to build the Android Open Source Project 4.1.1
operating system for the Google Nexus 7 on a fresh install of Ubuntu 11.10 (x64). At the time of writing,
these directions are accurate but they are both subject and likely to change in future versions of
Android.

Install Java [11]
$ sudo add-apt-repository "deb http://archive.canonical.com/ lucid partner"
$ sudo apt-get update
$ sudo apt-get install sun-java6-jdk

Install Dependencies
$ sudo apt-get install git-core gnupg flex bison gperf build-essential \
  zip curl zlib1g-dev libc6-dev lib32ncurses5-dev ia32-libs \
  x11proto-core-dev libx11-dev lib32readline5-dev lib32z-dev \
  libgl1-mesa-dev g++-multilib mingw32 tofrodos python-markdown \
  libxml2-utils xsltproc libx11-dev:i386

Configure USB Access

Create file /etc/udev/rules.d/51-android.rules and save the following to it (replacing
 with your username):
# adb protocol on grouper/tilapia (Nexus 7)
SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", ATTR{idProduct}=="4e42", MODE="0600", OWNER=""
# fastboot protocol on grouper/tilapia (Nexus 7)
SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", ATTR{idProduct}=="4e40", MODE="0600", OWNER=""

Download Source [12]

The following commands download the repo script, initialize a directory for the Android source and,
finally, download the source.
$   curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo > ~/bin/repo
$   chmod a+x ~/bin/repo
$   mkdir WORKING_DIRECTORY
$   cd WORKING_DIRECTORY
$   repo init -u https://android.googlesource.com/platform/manifest
$   repo sync

Extract Proprietary Blobs

Open https://developers.google.com/android/nexus/drivers in a browser. Locate and
download the archives for the appropriate hardware and software versions of the Nexus 7. Extract these
archives to WORKING_DIRECTORY and run the resulting shell scripts to extract the proprietary blobs for
the various device drivers.

Build [13]

                                                                                                      28
You can also read