Developing Android* Applications using Intel® Common Connectivity Framework (Intel® CCF)

The constant growth in popularity of wireless systems has prompted developers to think more seriously about the usage of wireless apps. Some issues being experienced are that wireless applications can be difficult but “roll your own” applications are expensive. There are high degrees of fragmentation in wireless apps as well as scaling and reach issues. Transport management is also complex.

These problems can be solved with Intel® CCF:

  • User friendly, simple connection APIs
  • Plugs into standard OS radios and drivers
  • Works across devices -> avoids walled gardens
  • Security and safety are included
  • Handles all of the transport services

This article demonstrates how to develop Android* applications using Intel® CCF.

What is the Intel® Common Connectivity Framework (Intel® CCF)?

CCF is a type of middleware. It runs on top of the OS and provides a simple and secure channel between devices for use by other applications. Intel CCF allows sharing between different types of devices. CCF unlocks new usage scenarios across devices including connection middleware for peer-to-peer (P2P) apps. The Intel CCF has the following features:

  • Delivers flexible connectivity regardless of Internet access or presence
  • Allows apps to utilize the power of "proximity"
  • Enables true social interactions; find, share, collaborate, play...
  • Build using "Identity" based connections instead of traditional "Device" or "Address" mechanisms
  • Available to developers via an SDK

Enabling Intel CCF with Apps

Out of process service:

  • Runs as a common service
  • Uses a common identity for all apps
  • Automatic execution of app on incoming invitation

Out of process platform:

  • Runs as a library bound to each app
  • Each app has an instance of Intel CCF
  • The application must be running to use Intel CCF
  • Identity is handled on a per-app basis

Developing Android Apps Using Intel CCF

Intel CCF supports multiple OS; Microsoft Windows, Google Android, Apple iOS.

When using Android OS to develop an app for CCF, we cannot rely on an Activity to handle a stable network connection, since the OS may close on any suspension (user answers a phone call or replies an SMS). We need to use a Service to hold and manage connections and connection events (disconnection, new invite, etc.) Our service will 'consume' the CCF services through the CCF Background Service (aka STCLib) which would provide looking for nearby users, inviting or receiving invitations from nearby users, and initiating connections with nearby users.

So now our app has Activities and a Service. In order to simplify the integration between them, which requires calling the Android Bind() methods every now and then, we add an Abstract Activity (parent class) from which our Activities will inherit some auto-binding features. It will simplify and clean our Activity code.

Our app is ready to initiate connection sessions between devices. A basic Connection Flow will look like this:

App activity starts -> StartingMyService -> Notifying CCF.

1) User taps "search" -> activity calls MyService method -> MyService invokes CCF's "scan".

2) CCF will invoke MyService with its results -> MyService will notify the activity with results, or will raise Notification alert if app is in suspension

3) User will interact with results in the same pattern of activity -> MyService -> CCF -> (Invite a nearby user to a session, accept an Invitation to a session, etc.)

Development Environment and Creating a New Project

Install JDK.

Download and install the latest Android SDK.

Eclipse IDE is used to develop and build the sample applications. The CCF In Proc Library is intended to simplify integrating an application with CCF. The first step in developing a CCF application is to import the inproc library to the project. In Eclipse:

File –>Import –>Android –>Existing Android Code Into Workspace –>Browse to and select the Samples/inproc_lib project

Click Finish

The inproc_lib project will be added to your workspace

Open Properties of inproc_lib visible as starterActivity.

Go to Android and verify the "Project Build Target" is API Level 15 or greater. Also, make sure the checkbox "Is Library" is checked.

Go to Java Build Path and remove stclib.jar from Libraries, if any. Click "Add External JARs", browse and select the location of stclibcc.jar.

Include external jar "android-support-v4.jar. If this JAR is missing, open the Android SDK Manager and download it under Extras -> Android Support Library

The second step in developing a CCF application is to create a new project. Run Eclipse and create a new project by clicking file -> New -> Project -> Android Application Project. Add Application name, Project name and Package name and click on Finish. Add a reference to CCF libraries by right clicking on the project and click on Add Reference and select Inproc_lib (StarterActivity). Configure Java build path and add missing external jars by clicking "Add external jars". The Inproc_Lib (StarterActivity) is referenced to your project automatically. Project is now created.

List of mandatory permissions required to use CCF framework and Inproc service:

Open the Android manifest file and append the below Android permissions:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_PROFILE" />
<!-- ***********INPROC ADDITIONS*********** -->
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> 
<uses-permission android:name="android.permission.READ_LOGS" />
<!-- ***********INPROC ADDITIONS*********** -->

Registering With The CCF Framework

Applications using CCF have to register with the CCF Framework.

The following XML must be added to the Android manifest file:

<service
android:name=".MyAppRegister"
android:exported="true"
android:permission="android.permission.INTERNET">
	<intent-filter>
		<action android:name=".REGISTER_APP"/>
	</intent-filter>
</service>

".MyAppRegister" is the name of the class implementing AppRegisterService. The class extending AppRegisterService should override the getGadgetList() method, as shown below:

public MyAppRegister extends AppRegisterService {
	public static final String LAUNCH_INTENT = ""; // package name
	public static final String APP_UUID = ""; // app id
	private static final String clientId = ""; // client id
	private static final String clientSecret = ""; // client secret
	private static final Boolean allowCloudTransport = false;
	static final StcApplicationId id = new StcApplicationId(appId, clientId, clientSecret, allowCloudTransport);
	@Override
	protected List<GadgetRegistration> getGadgetList(Context context) {
		String appName = context.getString(R.string.app_name);
		String appDescription = context.getString(R.string.app_description);
		ArrayList<GadgetRegistration> list = new ArrayList<GadgetRegistration>();
		list.add(new GadgetRegistration(appName, R.drawable.ic_launcher, APP_UUID, appDescription, LAUNCH_INTENT, 2, R.string.invite_text, R.string.timeout_text, 0, context));
		return list;
	}
}

The class extending AppRegisterService must implement getGadgetList() . The getGadgetList() method returns a list of GadgetRegistration objects, encapsulating the metadata that the CCF Framework needs to know about the application. This metadata includes the application's name, icon, UUID, description, invitation messages, and the Activity to call when the application is launched.

Registering Inproc service with the Application

Register the Inproc service in Android manifest file:

<service
android:name="com.intel.inproclib.InProcService"
android:permission="android.permission.INTERNET" >
	<intent-filter>
		<action android:name=".IN_PROC_SERVICE" />
	</intent-filter>
</service>

Initializing and Starting Stclib Platform

Create Service extends to StcServiceInet. This Service is responsible for containing and saving your StcLib across multiple Activities and allowing it to run in the background. It will also start the CCF platform or an inproc platform. It is declared in your manifest like a normal Android service. This service class helps to establish connections with CCF users.

public class Service extends StcServiceInet{
	@Override
	public StcApplicationId getAppId() {
		return SimpleChatRegisterApp.id;
	}
	@Override
	public StcConnectionListener getConnListener() {
		return null;
	}
	@Override
	protected void stcLibPrepared(StcLib lib) {
	}
	@Override
	protected void stcPlatformMissing() {
	}
}

Register Service in Android manifest file:

<service
android:name=".Service"
android:exported="true" >
	<intent-filter>
		<action android:name.Service" />
	</intent-filter>
</service>

Starting Service to initialize Stclib Platform

Create an Activity called SelectSessionActivity extending to the Android Activity and implement the interface com.intel.stc.slib.IStcServInetClient. This interface will help the Inproc library to carry out the unboxing process by getting a callback from the Service via method requestStartActivityForResult (Intent intent).

public class SelectSessionActivity extends Activity implements IStcServInetClient {
	//AndroidHandler to update the UI contents.
	Handler myHandler = new Handler();
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}
	/* IStcServInetClient starts*/
	@Override
	public void libPrepared(StcLib lib) {
		myHandler.post(new Runnable() {
			public void run()
			{
				Toast.makeText(this, "Platform Prepared", Toast.LENGTH_SHORT).show();
			}
		});
	}
	@Override
	public void platformError() {
	}
	@Override
	public void platformMissing() {
	}
	@Override
	public void requestStartActivityForResult(Intent intent) {
	}
	/* IStcServInetClient ends*/
}

To start the Service call the method doStartService() in onCreate(), append the below code in SelectSessionActivity:

public class SelectSessionActivity extends Activity implements IStcServInetClient{
	private ServiceConnection mConnection = new ServiceConnection();
	//AndroidHandler to update the UI contents.
	Handler myHandler = new Handler();
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		doStartService();
	}
	protected void doStartService() {
		Log.i(LOGC, "starting service");
		Intent servIntent = new Intent(ServiceIntent);
		startService(servIntent); 
	}
	@Override
	protected void onResume() {
		Log.i(LOGC, "resuming");
		doBindService();
		super.onResume();
	}
	private void doBindService() {
		if(!isBound)
		{
			Log.i(LOGC, "binding service");
			Intent servIntent = new Intent(ServiceIntent);
			isBound = bindService(servIntent, mConnection, 0);
			if( !isBound )
			Log.i(LOGC, "service did not bind.");
		}
	}
	@Override
	protected void onPause() {
		Log.i(LOGC, "pausing");
		doUnbindService();
		super.onPause();
	}
	private void doUnbindService() {
		if(isBound)
		{
			Log.i(LOGC, "unbinding service ");
			isBound = false;
			unbindService(mConnection);
		}
	}
	private void doStopService() {
		Log.i(LOGC, "shutting down");
		Intent servIntent = new Intent(ServiceIntent);
		doUnbindService(); 
		stopService(servIntent);
	}
	/* ServiceConnection implementation */
	public class SimpleChatServiceConnection implements ServiceConnection
	{
		boolean serviceStopped = false;
		public void onServiceConnected(ComponentName className, IBinder binder) 
		{
			synchronized(this) {
			Log.i(LOGC, "service connected.");
			chatService = (SimpleChatService)((SimpleChatService.StcServInetBinder)binder).getService();
			if(chatService!=null)
			chatService.setLibPreparedCallback(SelectSessionAcitivity.this, myHandler);
		}
	}
	public void onServiceDisconnected(ComponentName className) 
	{
		Log.i(LOGC, "service disconnected.");
		chatService = null;
	}
	};
}

This activity will start the background Service to complete the unboxing and authorization process and also to detect the CCF users. After starting the Service, we must implement doBindService() to bind the Service and doUnbindService() to unbind the Service, this method will be called when an Activity resumes or pauses. doBindService() internally calls Android method bindService() which will bind the Service with ServiceConnection object. ServiceConnection class is used to connect to the Service and also to pass data to the Service.

Discovery

Discovery in CCF for Android can be done by implementing com.intel.stc.interfaces.StcSessionUpdateListener in your Activity or Service. This will add the sessionUpdate() method that is triggered when there is a change in the session list. Once you are connected to the CCF framework and an StcLib has been created you can register your Activity or Service as a listener with com.intel.stc.lib.StcLib.setStcSessionListListener() by passing the reference of Activity or Service implementing com.intel.stc.interfaces.StcSessionUpdateListener. When you receive a sessionUpdateEvent you can request the newly updated session list by calling com.intel.stc.lib.StcLib.getSessionListWithAvatar(). This will return a list of all STCSessions.

1. Register the callback "setStcSessionListListener" to receive the sessionUpdated events:

public class Service extends StcServiceInet implements StcSessionUpdateListener{
	@Override
	public void sessionUpdated(StcSessionUpdateEvent event) {
		StcLib lib = getSTCLib();
		List<StcSession> allUsers = lib.getSessionListWithAvatar();
	}
	@Override
	protected void stcLibPrepared(StcLib slib) {
		if (slib!=null)
		slib.setStcSessionListListener(this);
	}
}

2. To update the Android UI:

public interface UIListener{
	public void updateDiscoveryList(List<StcSession> list);
}
public class Service extends StcServiceInet implements StcSessionUpdateListener{
	UIListener listner;
	@Override
	public void sessionUpdated(StcSessionUpdateEvent event) {
		StcLib lib = getSTCLib();
		List<StcSession> allUsers = lib.getSessionListWithAvatar();
		//Passing the list of discovered users to the SelectSessionAcitivity with help of UIListener interface.
		listner.updateDiscoveryList(allUsers);
	}
	@Override
	protected void stcLibPrepared(StcLib slib) {
		\set the listener to receive a callabck at sessionUpdated() of StcSessionUpdateListener.
		if (slib!=null)
		slib.setStcSessionListListener(this);
	}
}
public class SelectSessionAcitivity extends Activity implements IStcServInetClient, UIListener{
	private ListView view;
	private ArrayAdapter<String> adapter;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		//Intializing the listView
		view = new ListView(this);
		//Intialize the adapter.
		adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1);
		//Setting adapter to listview
		view.setAdapter(adapter);
		setContentView(view);
	}
	@Override
	public void updateDiscoveryList(List<StcSession> list){
		for(StcSession session : list){
			//Add discovered sessions to the adapter
			adapter.add(session.getUserName ());
		}
		//Updating Adapter to display the discovered sessions.
		adapter.notifyDataSetChanged();
	}
}

Invitation or Establishing a Connection

The process of establishing a connection using CCF is called invitation. In Android SDK, the invitation involves implementing the com.intel.stc.interfaces.StcConnectionListener. There are 2 callbacks associated with invitations. The inviter uses the com.intel.stc.lib.StcLib.inviteSession() to initiate the invitation process. The invitee will get a com.intel.stc.interfaces.connectionRequest() callback. The invitee can accept the invite by calling com.intel.stc.lib.StcLib.acceptInvitation() and as a result, get a StcSocket handle. The inviter will receive the com.intel.stc.interfaces.connectionCompleted() event with StcSocket as the event argument which can be used to read, write and close the socket.

To enable StcConnectionListener we need to set a call back to StcLib platform by calling "setConnectionLisner()":

public class Service extends StcServiceInet implements StcConnectionListener{
	@Override
	protected void stcLibPrepared(StcLib slib) {
		if (slib!=null)
		slib.setConnectionListener(this);
	}
	@Override
	public void connectionCompleted(InviteResponseEvent event) {
	}
	@Override
	public void connectionRequest(InviteRequestEvent event) {
	}
}

How to Send Invitations

public class tService extends StcServiceInet implements StcSessionUpdateListener{
	@Override
	public void sessionUpdated(StcSessionUpdateEvent event) {
		StcLib lib = getSTCLib();
		List<StcSession> allUsers = lib.getSessionListWithAvatar();
		for(StcSession session : allUsers){
			if(session.isOnline())
			sendInvite(session);
		}
	}
	//To send an invitation:
	private void sendInvite(StcSession session) { 
		// seconds to wait before the invitation times out 
		final short TIMEOUT = 180; 
		try{ 
			StcLib lib = getSTCLib();
			if(lib!=null)
			lib.inviteSession(session.getSessionUuid(), TIMEOUT); 
		}catch(StcException e){}
	} 
}

CCF calls StcConnectionListener::connectionCompleted() with an InviteResponseEvent when an event related to the invitation occurs. The application calls InviteResponseEvent::getStatus() to get an InviteResponseEvent.InviteStatus indicating the status of the invitation. If the remote user accepts the invitation, the application can get a StcSocket from the InviteResponseEvent object passed to StcConnectionListener::connectionCompleted().

//Triggered when you get a response back from remote session you invited.
@Override
public void connectionCompleted(InviteResponseEvent event) { 
	if (event.getStatus() == InviteResponseEvent.InviteStatus.sqtAccepted){ 
		try{ 
			StcSocket socket = getSTCLib().getPreparedSocket(event.getSessionGuid(),event.getConnectionHandle()); 
		}catch(StcException e){} 
	}
}

How to Receive An Invitation

CCF calls StcConnectionListener::connectionRequest() when it receives an invitation from a remote user.

@Override
public void connectionRequest(InviteRequestEvent event) { 
	try{ 
		StcSocket socket = getSTCLib().acceptInvitation(event.getAppUuid(), event.getConnectionHandle()); 
	}catch(StcException e){}
}

About The Author

Denis Smirnov (denis.smirnov@intel.com) – Software Intern. He has been working for Intel as a Technical Intern for nine months. He is a studies fourth great student in the NN Technical University, specialized in Applied Mathematics. Denis is getting the bachelor degree this year and He is also planning to hold a master’s degree in Computer science.

Para obtener información más completa sobre las optimizaciones del compilador, consulte nuestro Aviso de optimización.