In the context of Android’s ICC, when an application component intends to communicate with other component(s), it sends an explicit intent by providing the name of the component (if known), otherwise, an implicit intent (without the target name) is sent. Explicit intent does not require any evaluation and are simple to understand, while implicit intent requires a number of tests done by the system before an intent is passed on to an appropriate component. The existing specifications of intents, intent filters and the mechanism used to match a particular message request by a source component to a target component is informal and can not be used within formal proofs. In this section, the structure of implicit intent, intent filter and the mechanism to find the most appropriate component to receive implicit intent are formalized in theorem prover Coq. Such a formal definition can be used to prove theorems about intents, intent filters and inter-component communication using the Coq proof facility and the proof script can be checked mechanically using the Coq proof checker. The Coq source code and proofs of theorems are available on-line at [24].
Syntax of intent and intent filter
The main components of Android ICC are the data structures intent, intent filter and matching functions. An intent represents the content and the requested operation while the filter models a component. The data structures intent and intent filters are formalized in this sub-section and the matching functions are formalized in the next sub-section.
An implicit intent is a simple data structure containing the address and type of the data to act upon, the action to perform on data and some additional information. An intent is inductively defined in Coq as shown in Listing 3. The inductive definition consists of only one constructor (int, line 2) to construct elements (intents) of type intent. The constructor take as arguments an action, list of categories, URI and MIME type of the data. All the names (such as actions, categories, schemes, hosts and MIME types) are represented using the type atom defined in the library Atom, edited from [5]. The third argument of intent of type uri represents the content URI and is defined in Listing 4. A URI include optional elements scheme, host, port and path, represented by the arguments on lines 3, 4, 5 and 6, respectively. All the URI elements have type atom except port (line 5) which is modelled as a natural.
To map an intent to a component, the elements of an intent are matched against the elements in the intent filter of a component defined in the manifest file of Android application. Intent filter filter is inductively defined in Listing 5. The only constructor filt of type filter gets four arguments: list of actions, list of categories, list of content URIs and MIME types (lines 3, 4, 5 and 6, respectively). The definitions of intent and intent filters are used to define Android application (line 1, Listing 6) at a high level modelled as a list of intents that it may invoke to integrate with other applications on the device. The mobile device environment (line 2, Listing 6) is the list of applications on the mobile device, represented by the list of filters in all the applications installed.
Encoding Android application and environment
To demonstrate the applicability of our formal developments, a simple Android application and mobile device environment is created using formal notations defined. For simplicity, we assume the application consists of a single intent and the device contains a single application with just one intent filter. To realize this scenario, the intent and intent filter from Listings 1 and 2 are encoded in the formal notations developed in “Syntax of intent and intent filter” section. The encoding is used in a formal proof in next section.
The intent ingredients including the action ACTION_VIEW, two categories CATEGORY_DEFAULT and CATEGORY_BROWSABLE, data type text_html and elements (scheme, host, port and path) of data URL are defined of type atom in Listing 7 (lines 5–7). The URL of the web page (line 1, Listing 1) is defined on lines 8–9 with scheme, host, port number and path. All these parameters just defined are used to define intent and filter from Listings 1 and 2, respectively. In other words, the Coq definitions exampleintent and examplefilter of intent and filter (Listing 8) are the formal representations of the corresponding Java definitions in Listings 1 and 2. The implicit intent exampleintent is defined with action ACTION_VIEW, list of categories including CATEGORY_DEFAULT and CATEGORY_BROWSABLE, data URL and MIME type text_html. Similarly, the filter (for the Browser activity) is defined with a list of actions that it can perform, data categories, data URLs and types.
Finally, the example intent is used to define the application exampleapp (representing Email) and the filter is used to define the device environment exampleenv. The exampleenv represents the only application Browser on the device with one filter examplefilter. The major advantage of such formal definitions is that they can be used to mathematically reason about Android applications as demonstrated in “Proofs” section.
Intent resolution
After Android system receives an intent, it starts the appropriate target component for the intent based on the result of three tests [24] for action, category and data, respectively, against the corresponding elements in the intent filter of the target component. For an intent to resolve to a component, it must pass all these three tests. If the intent can not be resolved to any component, the source application may crash [17]. Following are the formal definitions of these three tests.
The action test is simple: the action in intent is matched against actions in the filter. This is modelled by the function testaction defined using the keyword Definition in Listing 9. The function definition in turn is using another function find which searches the action of type atom in intent (first argument) in the list of actions in filter (second argument). The space holder _ is used to represent arguments whose values are not important in the function body. The category test compares all the categories in the intent with the categories in the filter. The recursive function testcategory (Listing 10) takes a list of categories (of type atom) and categories listed in the intent filter and checks if all the categories in intent exist in the filter.
The third test, defined as a recursive function testdata in Listing 11, checks if the URI and MIME type of the content in the intent exist in the list of URIs and MIME types in the intent filter. Based on whether or not the URI and/or MIME type exists in the intent and/or filter, there are five different results. The first four cases in the function body (lines 4–10) correspond to the four rules on Android developers’ website [19]. These (informal) rules are (formally) implemented using pattern matching on the four arguments namely, intent URI (iuri), intent type (itype), list of URIs in filter (filuris) and list of MIME types in filter (filtypes).
The first rule states that an intent with no URI and no MIME type passes the test if there are no URI and MIME types listed in the filter. This is represented on the line 4 where values of all the four arguments are None (option type) or nil (list). The second case (line 5) models rule b in the documentation, which states that an intent with a URI but no MIME type can be accepted only if its URI matches a URI pattern in the filter and the MIME type specification list is empty. The rule c is represented by the case 3 (line 7). An intent with only MIME type can pass the test if the same type exists in the list of types in the filter and there is no URI specification in the filter. The fourth rule is modelled by the case 4 (lines 8–10). An intent with URI and MIME type is accepted only if both, the URI and MIME type, matches with URI and MIME type in the filter. In all other cases, such as | None, None, cons u’ ul, cons t’ tl \(\Rightarrow\) false, implicitly included in the code using space holders on line 11, the test fails.
The function testdata is calling two other functions testtype and testuri (lines 6, 7 and 9, Listing 11). For the first rule of function testdata, there is no URI and no MIME type and the result is true. For the next three rules, however, at least one of the URI and MIME type exists and the corresponding test function(s) is/are called. The test function testtype (Listing 12) is simple: it searches intent type in the list of filter types. If there is no intent type and likewise the filter does not require one (list of types in filter is empty), the test is passed. The formalization include explicit types and does not model implicit types. In the later case, the type would be inferred from the URI.
The definition of testuri is shown in Listing 13. It gets the list of filter URIs and the intent URI as arguments and compares the intent URI to a URI specification in the filter by comparing it only to the parts of URI included in the filter. In the first case, no URI in the intent, test is passed. The next six cases (lines 4–25) models the text If a scheme is not specified \(\dots\) authority, and path pass the filter. in the official documentation [19]. The last case (lines 26–31) represents the tests for all other cases: the axillary function cmpoattr compares the optional filter URI attribute with an intent URI attribute. For the attribute test to pass, the intent must include the same attribute listed in the filter and for the URI test to pass, all the attributes must pass the attribute test.
Finally, all the tests are combined together in function resolve defined in Listing 14. This function gets an intent and a filter and returns true if all the tests, namely testaction, testcategory and testdata, are passed. In other words, given an intent and a filter, the formal developments enable one to check if an intent would resolve to (accepted by) a component.
Application crash-safety
After intent, intent filter and intent resolution are defined, Android applications’ safety against crashes due to intents is formally defined. The definition resolve is used to formally describe crash-safety property intent_crash_safety of an intent (Listing 15). This property is too strong: it defines an intent is crash safe if it must be accepted by the filter. To define crash-safety of an intent with respect to the entire device, a recursive function intent_crash_safetey_env is defined in Listing 16. An intent is crash-safe in the device if there exists at least an application (filter) that accepts the intent.
Finally, a crash-safe application is defined in Listing 17. An application (represented by the intents it may invoke) is crash-safe if every intent it may invoke resolves to an application component (represented by a filter) on the device. In the next section (“4” section), we formally prove that the example Android application with invoked intent exampleintent does not crash in the context of another application examplefilter on the device.