LayerCake: Application Embedding for Android

Disclaimer: Use the LayerCake research prototype and demo applications
for academic purposes only and at your own risk. View the license here.

Sample Applications

We provide several sample applications that take advantage of LayerCake's embedding functionality. You can download the source from GitHub, or you can download the pre-built APK files here.

The rest of this page describes how to create applications for LayerCake. We will use the example of a parent application (EmbedDemo) embedding an advertisement (AdMobWrapper) throughout our explanations.


LayerCake Android SDK

Because LayerCake modifies the Android SDK, you must use the modified SDK to build applications that take advantage of LayerCake's features. You can download the LayerCake version of the SDK here (Mac and Linux) (or you can build it from the LayerCake source by using the sdk-eng target).

If you use Eclipse to build Android applications, you must point it to the modified SDK (Eclipse > Preferences > Android > Browse for SDK directory).


Programming for LayerCake

To create an application that embeds an Activity from another application, you must take the following steps. Note that both applications must be installed.

Step 1: Embed Child Activity in Parent's Layout

First, you must include the child Activity in the parent Activity's user interface. Do this by including an EmbeddedActivityView. For example, EmbedDemo includes the advertisement provided by AdMobWrapper in the following way:
<EmbeddedActivityView
 android:id="@+id/embeddedAd"
 android:layout_width="fill_parent"
 android:layout_height="70dp"
 android:layout_gravity="center_horizontal"
 android:activityName="com.layercake.admobwrapper.EmbeddedAd"
 android:packageName="com.layercake.admobwrapper" 
 android:setupBinder="setupBinderAd" />
Please note that the following attributes are all required: android:id, android:setupBinder, android:activityName, and android:packageName..

Step 2: Setup API between Child and Parent

The parent and child applications sometimes need to communicate. For example, EmbedDemo may wish to communicate ad keywords to AdMobWrapper to target ads to users. Similarly, the AdMobDemo may wish to send a notification to EmbedDemo whenever an ad has been loaded.

In order to support parent-child communication, the child Activity must define two two AIDL interfaces, one that it (the child) will implement, and one that the parent application must implement.

For example, the AdMobWrapper application defines the following interfaces:

/* Child interface (AdMobWrapper must implement this) */

package com.layercake.admobwrapper;

import android.location.Location;

interface IRemoteEmbeddedAd {

	/* Create AdView */
	void createAdView(String adSize, String adDevId);
	
	/* Create and load adRequest. Leave testDevice as null if not testing. */
	void loadAdRequest(String gender, String birthdate, 
		in java.util.List keywords, in Location location, String testDevice);

}
/* Parent interface (EmbedDemo must implement this) */

package com.layercake.admobwrapper;

import android.os.IBinder;

interface IEmbeddedAdContainer {

    /* Functions for AdListener */
	void onReceiveAd();
	void onFailedToReceiveAd(String errorCode);
	void onPresentScreen();
	void onDismissScreen();
	void onLeaveApplication();
	
	/* Register the child's interface so that parent can call back. */
	void registerChildInterface(IBinder childInterface);

}
In order to embed AdMobWrapper's Activity, EmbedDemo must
(1) copy these AIDL interfaces into its own project,
(2) must implement the parent interface, and
(3) must return this interface from the function specified in the android:setupBinder attribute in the layout definition (above).

For example:
public IBinder setupBinderAd(View v) {
    return new IEmbeddedAdContainer.Stub() {

        @Override
        public void onReceiveAd() throws RemoteException {
            Log.i(TAG, "Received ad!");
        }
    
        /* Other functions omitted for example */

        @Override
        public void registerChildInterface(IBinder childInterface) 
            throws RemoteException {
            
            childAdInterface = IRemoteEmbeddedAd.Stub.asInterface(childInterface);
				
            // Go ahead and actually load the ad
            try {
                childAdInterface.createAdView("BANNER", DEVICE_ID);
                childAdInterface.loadAdRequest("FEMALE", null, null, 
                    null, "TEST_EMULATOR");
            } catch (RemoteException e) {
                Log.e(TAG, "Error loading ad: " + e.getMessage());
            }
        }
    };
}