Use the Accessory SDK in Android

1. Declaring the Android Permission

To use the Accessory SDK, you must declare the following permission.

<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" />

If the permission is not added in the AndroidManifest.xml file, the initialization fails with SecurityException:

  • In Samsung devices:

    • Android 4.4.2 (KitKat) and above: SecurityException is thrown and your application does not work.

    • Prior to Android 4.4.2 (KitKat): No exception is thrown and the application works properly.

  • In other companies’ devices:

    • No exception is thrown and the application works properly.

The following permission has to be specified in the AndroidManifest.xml file to use the Samsung Accessory Service:

<uses-permission android:name="com.samsung.accessory.permission.ACCESSORY_FRAMEWORK" />

2. Declaring the Broadcast Receiver

If the broadcast receiver is not added in the AndroidManifest.xml file, no intents handled by the Samsung Accessory Service Framework are delivered to your application.

<application>
	<receiver android:name="com.samsung.android.sdk.accessory.RegisterUponInstallReceiver">
		<intent-filter>
			<action android:name="com.samsung.accessory.action.REGISTER_AGENT" />
		</intent-filter>
	</receiver>
	<receiver android:name="com.samsung.android.sdk.accessory.ServiceConnectionIndicationBroadcastReceiver">
		<intent-filter>
			<action android:name="com.samsung.accessory.action.SERVICE_CONNECTION_REQUESTED" />
		</intent-filter>
	</receiver>
</application>
Note

The action names are changed from 2.3.0. For backward compatibility, old actions are supported for a while.

Communicating with a remote Peer Agent requires the service declaration in the AndroidManifest.xml file. This ensures that the application is derived from the SAAgent class.

<application>

						<service android:name="com.example.myapplication.ProviderService" />

					</application>
Note

The SAAgent class extends the Android service and handles asynchronous Accessory-related intents. Its implementation executes all of its activities in a worker thread, which means that it does not overload your application main thread.

3. Defining the Accessory Service Profile

Communicating with a remote Peer Agent requires the declaration of descriptions about the Accessory Service Profile. This is declared in a separate file in the /res/xml folder in your application project. The path of the actual XML file can be added in the application’s AndroidManifest.xml file.

For example, /res/xml/<profileName>.xml:

<application>

	<meta-data
			android:name="AccessoryServicesLocation"
			android:value="/res/xml/accessoryservices.xml" />

</application>

An example of the Accessory Service Profile XML file:

<?xml version="1.0" encoding="utf-8"?>
<resources>
	<application name="MyApplication">
		<serviceProfile
				id="/org/example/myapp/my_message"
				name="myapplication"
				role="provider"
				serviceImpl="com.example.myapplication.ProviderService"
				version="1.0"
				serviceLimit="ANY"
				serviceTimeout="10">
			<supportedTransports>
				<transport type="TRANSPORT_BT" />
				<transport type="TRANSPORT_WIFI" />
			</supportedTransports>
			<serviceChannel
					id="110"
					dataRate="low"
					priority="low"
					reliability= "enable"/>
		</serviceProfile>
	</application>
</resources>

When the application is installed, the Samsung Accessory Service Framework automatically registers its Accessory Peer Agents using the information specified in your service profile XML file. Similarly, the Accessory Peer Agents are deregistered when the application is uninstalled. An error log is dumped if the registration process fails to register the Accessory Service Profile implementation. To define the Accessory Service Profile, see A.1 Validating Accessory Service Profile XML in this.

4.Setting up the Service Connection

If the application wants to establish a service connection with only one Accessory Peer Agent, check the first callback. You can also check the identity or properties of the discovered Accessory Peer Agents by calling the functions provided by the SAPeerAgent class to decide which Accessory Peer Agent you want to form a service connection with. The application can initiate a service connection with an Accessory Peer Agent by calling the SAAgent.requestServiceConnection() function.

This function is called from a worker thread. If you need to do any heavy lifting or long latency work in the callback, spawn a separate thread.

If a service provider connects only with a specific service consumer, or a service consumer with a specific service provider, the service provider and consumer are called ”companion applications”. When you only want to connect to a companion service provider or service consumer, call the functions provided by the SAPeerAgent class for specific information, such as model number or vendor information, before calling the SAAgent.requestServiceConnection() function. For example, when a photo printer service provider on an accessory device from a company only wants to connect to a photo printer service consumer on a smart device from the same company, they are companion applications.

The remote Accessory Peer Agent either accepts or rejects your service connection request. Your application is notified with the SAAgent.onServiceConnectionResponse() callback. The request can be accepted (and a service connection established) or rejected, or it can fail to establish the service connection for other reasons.

When a service connection is successfully established, the requesting Accessory Peer Agent gets an instance of the SASocket object, which is used to handle service connection events and send or receive data to and from Accessory Peer Agents.

public class ProviderService extends SAAgent
{
	private static final int CHANNEL_ID = 110;

	private ProviderServiceSocket mProviderServiceSocket = null;

	public ProviderService()
	{
		super(TAG, ProviderServiceSocket.class);
	}

	@Override
	public void
	onCreate()
	{
		super.onCreate();
		SA mAccessory = new SA();
		try
		{
			mAccessory.initialize(this);
		}
		catch (SsdkUnsupportedException e)
		{
			// try to handle SsdkUnsupportedException
		}
		catch (Exception e1)
		{
			e1.printStackTrace();
			stopSelf();
		}
	}

	@Override
	protected void
	onServiceConnectionResponse(SAPeerAgent peerAgent, SASocket socket, int result)
	{
		if (result == SAAgent.CONNECTION_SUCCESS)
		{
			if (socket != null)
			{
				mProviderServiceSocket = (ProviderServiceSocket) socket;
				Toast.makeText(getBaseContext(), "Gear connection is established",
				Toast.LENGTH_SHORT).show();
			}
		}
		else if (result == SAAgent.CONNECTION_ALREADY_EXIST)
		{
			Toast.makeText(getBaseContext(), "Gear connection already exists",
			Toast.LENGTH_SHORT).show();
		}
	}

	public class ProviderServiceSocket extends SASocket
	{
	}
}

5. Handling the Setup Service Connection Request

The service provider or consumer application is notified with the

SAAgent.onServiceConnectionRequested() callback when a remote Accessory Peer Agent wants to create a service connection with it:

  • The Accessory Peer Agent implementation can accept or reject service connection requests by calling the acceptServiceConnectionRequest() or rejectServiceConnectionRequest() function.

  • The default implementation of the SAAgent.onServiceConnectionRequested() callback is to accept every incoming service connection request from any remote Accessory Peer Agent. Your Accessory Peer Agent implementation can override this function, usually to check the identity and properties of the requesting remote Accessory Peer Agent before accepting or rejecting incoming service connection requests.

  • The SAAgent.onServiceConnectionRequested() callback can check for Accessory Peer Agent-specific information before accepting service connection requests. You can use the SAPeerAgent object functions for checking specific information, such as application name or vendor ID.

If your application accepts the service connection request, your application is notified through the SAAgent.onServiceConnectionResponse() callback when the service connection is established or a failure occurs. On success, an SASocket object is passed with the callback. If you want to implement a service provider application that can serve multiple service consumer applications at the same time, keep a repository of the SASocket objects for all active service connections, and give an identifier for each SASocket object.

The SAAgent.onServiceConnectionResponse() callback is called from a worker thread. If you need to do any heavy lifting or long latency work in the callback, spawn a separate thread.

@Override
protected void
onServiceConnectionRequested(SAPeerAgent peerAgent)
{
	if (peerAgent != null)
	{
	acceptServiceConnectionRequest(peerAgent);
	}
	}

	@Override
	protected void
	onServiceConnectionResponse(SAPeerAgent peerAgent, SASocket socket,
	int result)
	{
		if (result == SAAgent.CONNECTION_SUCCESS)
		{
			if (socket != null)
			{
				mProviderServiceSocket = (ProviderServiceSocket) socket;
				Toast.makeText(getBaseContext(), "Gear connection is established",
				Toast.LENGTH_SHORT).show();
			}
		}
		else if (result == SAAgent.CONNECTION_ALREADY_EXIST)
		{
			Toast.makeText(getBaseContext(), "Gear connection already exists",
			Toast.LENGTH_SHORT).show();
		}
	}

6. Exchanging Data with the Accessory Peer Agent

Call the SASocket.send() function of the SASocket object (passed with the SAAgent.onServiceConnectionResponse() callback) to send data on the selected service channel inside an established service connection. The Samsung Accessory Service Framework provides a datagram service. Either all the data is sent or nothing is sent. The service connection encapsulates all service channels as defined by the Accessory Service Profile specification.

public void sendMessage(final String message)
{
	if (mProviderServiceSocket != null)
	{
		new Thread(new Runnable()
		{
			public void run()
			{
			try
			{
				mProviderServiceSocket.send(CHANNEL_ID, message.getBytes());
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}
			}
		}).start();
	}
}
Note

See attached programming guide pdf file for more information.

public class ProviderServiceSocket extends SASocket
{
	@Override
	public void onReceive(int channelId, byte[] data)
	{
		final String message = new String(data);
		if (mProviderServiceListener != null && !mProviderServiceListener.isActivityHidden())
		{
			mProviderServiceListener.onReceiveMessage(message);
		}
		else
		{
			Toast.makeText(getBaseContext(), message, Toast.LENGTH_SHORT).show();
			sendMessage("I'm sleeping");
		}
	}
}