From 10cd26af5eb6bbaa2ad276fac00ccd72b6575fb4 Mon Sep 17 00:00:00 2001 From: Matthew Simmons Date: Mon, 21 Feb 2011 19:49:43 -0500 Subject: [PATCH] Upgrade ANT support libraries Rework AntSensorManager (and subclasses) to use asynchronous ANT interfaces Encapsulate message byte arrays with per-message classes Add tests --- MyTracks/src/com/dsi/ant/AntDefine.java | 8 - MyTracks/src/com/dsi/ant/AntInterface.java | 1200 ++++++++++------- .../src/com/dsi/ant/AntInterfaceIntent.java | 7 +- MyTracks/src/com/dsi/ant/IAnt_6.aidl | 77 ++ MyTracks/src/com/dsi/ant/Version.java | 35 + .../ant/exception/AntInterfaceException.java | 20 + .../dsi/ant/exception/AntRemoteException.java | 23 + .../AntServiceNotConnectedException.java | 19 + .../sensors/AntDirectSensorManager.java | 102 +- .../services/sensors/AntSRMSensorManager.java | 32 +- .../services/sensors/AntSensorManager.java | 385 ++++-- .../services/sensors/SensorManager.java | 8 +- .../sensors/ant/AntChannelIdMessage.java | 56 + .../ant/AntChannelResponseMessage.java | 49 + .../services/sensors/ant/AntMessage.java | 32 + .../sensors/ant/AntStartupMessage.java | 35 + .../sensors/AntDirectSensorManagerTest.java | 36 +- .../sensors/AntSensorManagerTest.java | 85 ++ .../sensors/ant/AntChannelIdMessageTest.java | 35 + .../ant/AntChannelResponseMessageTest.java | 36 + .../services/sensors/ant/AntMessageTest.java | 30 + .../sensors/ant/AntStartupMessageTest.java | 29 + 22 files changed, 1603 insertions(+), 736 deletions(-) create mode 100644 MyTracks/src/com/dsi/ant/IAnt_6.aidl create mode 100644 MyTracks/src/com/dsi/ant/Version.java create mode 100644 MyTracks/src/com/dsi/ant/exception/AntInterfaceException.java create mode 100644 MyTracks/src/com/dsi/ant/exception/AntRemoteException.java create mode 100644 MyTracks/src/com/dsi/ant/exception/AntServiceNotConnectedException.java create mode 100644 MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntChannelIdMessage.java create mode 100644 MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntChannelResponseMessage.java create mode 100644 MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntMessage.java create mode 100644 MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntStartupMessage.java create mode 100644 MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/AntSensorManagerTest.java create mode 100644 MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/ant/AntChannelIdMessageTest.java create mode 100644 MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/ant/AntChannelResponseMessageTest.java create mode 100644 MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/ant/AntMessageTest.java create mode 100644 MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/ant/AntStartupMessageTest.java diff --git a/MyTracks/src/com/dsi/ant/AntDefine.java b/MyTracks/src/com/dsi/ant/AntDefine.java index 4aabb7eb2..bda615df9 100644 --- a/MyTracks/src/com/dsi/ant/AntDefine.java +++ b/MyTracks/src/com/dsi/ant/AntDefine.java @@ -28,14 +28,6 @@ package com.dsi.ant; */ public class AntDefine { - ////////////////////////////////////////////// - // Library Version Information - ////////////////////////////////////////////// - public static final int ANT_LIBRARY_VERSION_CODE = 5; - public static final int ANT_LIBRARY_VERSION_MAJOR = 1; - public static final int ANT_LIBRARY_VERSION_MINOR = 4; - public static final String ANT_LIBRARY_VERSION_NAME = String.valueOf(ANT_LIBRARY_VERSION_MAJOR) + "." + String.valueOf(ANT_LIBRARY_VERSION_MINOR); - ////////////////////////////////////////////// // Valid Configuration Values ////////////////////////////////////////////// diff --git a/MyTracks/src/com/dsi/ant/AntInterface.java b/MyTracks/src/com/dsi/ant/AntInterface.java index 20d30349a..f527c20f5 100644 --- a/MyTracks/src/com/dsi/ant/AntInterface.java +++ b/MyTracks/src/com/dsi/ant/AntInterface.java @@ -13,88 +13,101 @@ * License for the specific language governing permissions and limitations under * the License. */ - package com.dsi.ant; +package com.dsi.ant; +import java.util.Arrays; + +import android.app.Activity; +import android.app.AlertDialog; import android.content.ComponentName; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.PackageManager; import android.net.Uri; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; - -// TODO: Auto-generated Javadoc +import com.dsi.ant.exception.*; /** * Public API for controlling the Ant Service. AntInterface is a proxy * object for controlling the Ant Service via IPC. Creating a AntInterface * object will create a binding with the Ant service. - * + * * @hide */ public class AntInterface { /** The Log Tag. */ public static final String TAG = "ANTInterface"; - + /** Enable debug logging. */ - public static boolean DEBUG = false; + public static boolean DEBUG = true; /** Search string to find ANT Radio Proxy Service in the Android Marketplace */ private static final String MARKET_SEARCH_TEXT_DEFAULT = "ANT Radio Service Dynastream Innovations Inc"; - + + /** Name of the ANT Radio shared library */ + private static final String ANT_LIBRARY_NAME = "com.dsi.ant.antradio_library"; + /** Inter-process communication with the ANT Radio Proxy Service. */ - public static IAnt sAntReceiver = null; - public static IServiceSettings sServiceSettingsReceiver = null; - + private static IAnt_6 sAntReceiver = null; + /** Singleton instance of this class. */ private static AntInterface INSTANCE; - + /** Used when obtaining a reference to the singleton instance. */ private static Object INSTANCE_LOCK = new Object(); - + /** The context to use. */ private static Context sContext = null; - + /** Listens to changes to service connection status. */ private static ServiceListener sServiceListener; - + /** Is the ANT Radio Proxy Service connected. */ private static boolean sServiceConnected = false; - private static boolean sServiceSettingsConnected = false; + /** The version code (eg. 1) of ANTLib used by the ANT application service */ private static int mServiceLibraryVersionCode = 0; - + + /** Has support for ANT already been checked */ + private static boolean mCheckedAntSupported = false; + + /** Is ANT supported on this device */ + private static boolean mAntSupported = false; + /** * An interface for notifying AntInterface IPC clients when they have * been connected to the ANT service. * * @see ServiceEvent */ - public interface ServiceListener - { - /** - * Called to notify the client when this proxy object has been - * connected to the ANT service. Clients must wait for - * this callback before making IPC calls on the ANT - * service. - */ - public void onServiceConnected(); + public interface ServiceListener + { + /** + * Called to notify the client when this proxy object has been + * connected to the ANT service. Clients must wait for + * this callback before making IPC calls on the ANT + * service. + */ + public void onServiceConnected(); - /** - * Called to notify the client that this proxy object has been - * disconnected from the ANT service. Clients must not - * make IPC calls on the ANT service after this callback. - * This callback will currently only occur if the application hosting - * the BluetoothAg service, but may be called more often in future. - */ - public void onServiceDisconnected(); - } + /** + * Called to notify the client that this proxy object has been + * disconnected from the ANT service. Clients must not + * make IPC calls on the ANT service after this callback. + * This callback will currently only occur if the application hosting + * the BluetoothAg service, but may be called more often in future. + */ + public void onServiceDisconnected(); + } - - //Constructor + + //Constructor /** * Instantiates a new ant interface. * @@ -111,23 +124,30 @@ public class AntInterface { /** * Gets the single instance of AntInterface, creating it if it doesn't exist. + * Only the initial request for an instance will have context and listener set to the requested objects. * - * @param context the context. + * @param context the context used to bind to the remote service. * @param listener the listener to be notified of status changes. * @return the AntInterface instance. * @since 1.0 */ - public static AntInterface getInstance(Context context,ServiceListener listener) + public static AntInterface getInstance(Context context,ServiceListener listener) { if(DEBUG) Log.d(TAG, "getInstance"); - synchronized (INSTANCE_LOCK) + synchronized (INSTANCE_LOCK) { - if (INSTANCE == null) + if(!hasAntSupport(context)) + { + if(DEBUG) Log.d(TAG, "getInstance: ANT not supported"); + + return null; + } + + if (INSTANCE == null) { if(DEBUG) Log.d(TAG, "getInstance: Creating new instance"); - - // TODO: rohan: bug, each new request for an instance will not have context and listener set to the requested objects + INSTANCE = new AntInterface(context,listener); } else @@ -142,7 +162,7 @@ public class AntInterface { if(!INSTANCE.initService()) { Log.e(TAG, "getInstance: No connection to proxy service"); - + INSTANCE.destroy(); INSTANCE = null; } @@ -185,10 +205,10 @@ public class AntInterface { public static void goToMarket(Context pContext) { if(DEBUG) Log.d(TAG, "goToMarket"); - + goToMarket(pContext, MARKET_SEARCH_TEXT_DEFAULT); } - + /** * Class for interacting with the ANT interface. */ @@ -200,38 +220,12 @@ public class AntInterface { // service through an IDL interface, so get a client-side // representation of that from the raw service object. if(DEBUG) Log.d(TAG, "sIAntConnection onServiceConnected()"); - sAntReceiver = IAnt.Stub.asInterface(pService); + sAntReceiver = IAnt_6.Stub.asInterface(pService); sServiceConnected = true; - try - { - mServiceLibraryVersionCode = sAntReceiver.getServiceLibraryVersionCode(); - } - catch(RemoteException e) - { - Log.e(TAG, "sIAntConnection onServiceConnected: Could not get service library version"); - } - - switch(mServiceLibraryVersionCode) - { - // Fall through from newer versions - case 5: - { - if(!sServiceSettingsConnected) - { - boolean boundServiceSettings = sContext.bindService(new Intent(IServiceSettings.class.getName()), sIServiceSettingsConnection, Context.BIND_AUTO_CREATE); - Log.i(TAG, "sIAntConnection onServiceConnected: Bound with ANT Service Settings: " + boundServiceSettings); - } - else - { - if(DEBUG) Log.d(TAG, "sIAntConnection onServiceConnected: Already initialised Service Settings connection"); - } - } - } - // Notify the attached application if it is registered - if (sServiceListener != null) + if (sServiceListener != null) { sServiceListener.onServiceConnected(); } @@ -248,9 +242,10 @@ public class AntInterface { sAntReceiver = null; sServiceConnected = false; + mServiceLibraryVersionCode = 0; // Notify the attached application if it is registered - if (sServiceListener != null) + if (sServiceListener != null) { sServiceListener.onServiceDisconnected(); } @@ -265,37 +260,6 @@ public class AntInterface { } }; - /** - * Class for interacting with the ANT interface through the ANTLib version 5 functions. - */ - private static ServiceConnection sIServiceSettingsConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName pClassName, IBinder pService) { - // This is called when the connection with the service has been - // established, giving us the service object we can use to - // interact with the service. We are communicating with our - // service through an IDL interface, so get a client-side - // representation of that from the raw service object. - if(DEBUG) Log.d(TAG, "sIServiceSettingsConnection onServiceConnected()"); - sServiceSettingsReceiver = IServiceSettings.Stub.asInterface(pService); - - sServiceSettingsConnected = true; - } - - public void onServiceDisconnected(ComponentName pClassName) { - // This is called when the connection with the service has been - // unexpectedly disconnected -- that is, its process crashed. - if(DEBUG) Log.d(TAG, "sIServiceSettingsConnection onServiceDisconnected()"); - sServiceSettingsReceiver = null; - - sServiceSettingsConnected = false; - - // Try and rebind to the service - INSTANCE.releaseService(); - INSTANCE.initService(); - } - }; - - /** * Binds this activity to the ANT service. * @@ -308,41 +272,36 @@ public class AntInterface { if(!sServiceConnected) { - ret = sContext.bindService(new Intent(IAnt.class.getName()), sIAntConnection, Context.BIND_AUTO_CREATE); - Log.i(TAG, "initService(): Bound with ANT service: " + ret); + ret = sContext.bindService(new Intent(IAnt_6.class.getName()), sIAntConnection, Context.BIND_AUTO_CREATE); + if(DEBUG) Log.i(TAG, "initService(): Bound with ANT service: " + ret); } else { if(DEBUG) Log.d(TAG, "initService: already initialised service"); ret = true; } - + return ret; } - + /** Unbinds this activity from the ANT service. */ private void releaseService() { - if(DEBUG) Log.d(TAG, "releaseService() entered"); - - if(sServiceConnected) - { - sContext.unbindService(sIAntConnection); - sServiceConnected = false; - } - - if(sServiceSettingsConnected) - { - sContext.unbindService(sIServiceSettingsConnection); - sServiceSettingsConnected = false; - } + if(DEBUG) Log.d(TAG, "releaseService() entered"); - if(DEBUG) Log.d(TAG, "releaseService() unbound."); + // TODO Make sure can handle multiple calls to onDestroy + if(sServiceConnected) + { + sContext.unbindService(sIAntConnection); + sServiceConnected = false; + } + + if(DEBUG) Log.d(TAG, "releaseService() unbound."); } /** * True if this activity can communicate with the ANT service. * - * @return true, if is service connected + * @return true, if service is connected * @since 1.2 */ public boolean isServiceConnected() @@ -368,193 +327,209 @@ public class AntInterface { } - /** - * Ant service connection lost. - */ - private void antServiceConnectionLost() - { - Log.e(TAG, "Connection to ANT service lost"); - } - - /** - * Ant service connection to settings binder lost. - */ - private void antServiceSettingsConnectionLost() - { - Log.e(TAG, "Connection to ANT service settings lost"); - } - - ////------------------------------------------------- - + /** * Enable. * - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public boolean enable() + public void enable() throws AntInterfaceException { - if(DEBUG) Log.d(TAG, "enable"); - if(!sServiceConnected) { - // Haven't received 'onConnected' notification yet - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.enable(); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + if(!sAntReceiver.enable()) + { + throw new AntInterfaceException(); + } + } + catch(RemoteException e) + { + throw new AntRemoteException(e); } - return result; } - + /** * Disable. * - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public boolean disable() + public void disable() throws AntInterfaceException { - if(DEBUG) Log.d(TAG, "disable"); - if(!sServiceConnected) { - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.disable(); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + if(!sAntReceiver.disable()) + { + throw new AntInterfaceException(); + } + } + catch(RemoteException e) + { + throw new AntRemoteException(e); } - return result; } - + /** * Checks if is enabled. * - * @return true, if is enabled + * @return true, if is enabled. + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public boolean isEnabled() + public boolean isEnabled() throws AntInterfaceException { - if(DEBUG) Log.d(TAG, "isEnabled"); - if(!sServiceConnected) { - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.isEnabled(); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + return sAntReceiver.isEnabled(); } - return result; - } + catch(RemoteException e) + { + throw new AntRemoteException(e); + } + } /** * ANT tx message. * * @param message the message - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public boolean ANTTxMessage(byte[] message) + public void ANTTxMessage(byte[] message) throws AntInterfaceException { - if(DEBUG) Log.d(TAG, "ANTTxMessage"); + if(DEBUG) Log.d(TAG, "ANTTxMessage"); if(!sServiceConnected) { - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.ANTTxMessage(message); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + if(!sAntReceiver.ANTTxMessage(message)) + { + throw new AntInterfaceException(); + } + } + catch(RemoteException e) + { + throw new AntRemoteException(e); } - return result; } /** * ANT reset system. * - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public boolean ANTResetSystem() + public void ANTResetSystem() throws AntInterfaceException { if(!sServiceConnected) { - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.ANTResetSystem(); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + if(!sAntReceiver.ANTResetSystem()) + { + throw new AntInterfaceException(); + } + } + catch(RemoteException e) + { + throw new AntRemoteException(e); } - return result; } - + /** * ANT unassign channel. * * @param channelNumber the channel number - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public boolean ANTUnassignChannel(byte channelNumber) + public void ANTUnassignChannel(byte channelNumber) throws AntInterfaceException { if(!sServiceConnected) { - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.ANTUnassignChannel(channelNumber); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + if(!sAntReceiver.ANTUnassignChannel(channelNumber)) + { + throw new AntInterfaceException(); + } + } + catch(RemoteException e) + { + throw new AntRemoteException(e); } - return result; } - + /** * ANT assign channel. * * @param channelNumber the channel number * @param channelType the channel type * @param networkNumber the network number - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public boolean ANTAssignChannel(byte channelNumber, byte channelType, byte networkNumber) + public void ANTAssignChannel(byte channelNumber, byte channelType, byte networkNumber) throws AntInterfaceException { if(!sServiceConnected) { - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.ANTAssignChannel(channelNumber, channelType, networkNumber); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + if(!sAntReceiver.ANTAssignChannel(channelNumber, channelType, networkNumber)) + { + throw new AntInterfaceException(); + } + } + catch(RemoteException e) + { + throw new AntRemoteException(e); } - return result; } - + /** * ANT set channel id. * @@ -562,170 +537,210 @@ public class AntInterface { * @param deviceNumber the device number * @param deviceType the device type * @param txType the tx type - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public boolean ANTSetChannelId(byte channelNumber, short deviceNumber, byte deviceType, byte txType) + public void ANTSetChannelId(byte channelNumber, short deviceNumber, byte deviceType, byte txType) throws AntInterfaceException { if(!sServiceConnected) { - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.ANTSetChannelId(channelNumber, deviceNumber, deviceType, txType); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + if(!sAntReceiver.ANTSetChannelId(channelNumber, deviceNumber, deviceType, txType)) + { + throw new AntInterfaceException(); + } } - return result; - } - + catch(RemoteException e) + { + throw new AntRemoteException(e); + } + } + /** * ANT set channel period. * * @param channelNumber the channel number * @param channelPeriod the channel period - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public boolean ANTSetChannelPeriod(byte channelNumber, short channelPeriod) + public void ANTSetChannelPeriod(byte channelNumber, short channelPeriod) throws AntInterfaceException { if(!sServiceConnected) { - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.ANTSetChannelPeriod(channelNumber, channelPeriod); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + if(!sAntReceiver.ANTSetChannelPeriod(channelNumber, channelPeriod)) + { + throw new AntInterfaceException(); + } + } + catch(RemoteException e) + { + throw new AntRemoteException(e); } - return result; } - + /** * ANT set channel rf freq. * * @param channelNumber the channel number * @param radioFrequency the radio frequency - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public boolean ANTSetChannelRFFreq(byte channelNumber, byte radioFrequency) + public void ANTSetChannelRFFreq(byte channelNumber, byte radioFrequency) throws AntInterfaceException { if(!sServiceConnected) { - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.ANTSetChannelRFFreq(channelNumber, radioFrequency); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + if(!sAntReceiver.ANTSetChannelRFFreq(channelNumber, radioFrequency)) + { + throw new AntInterfaceException(); + } + } + catch(RemoteException e) + { + throw new AntRemoteException(e); } - return result; } - + /** * ANT set channel search timeout. * * @param channelNumber the channel number * @param searchTimeout the search timeout - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public boolean ANTSetChannelSearchTimeout(byte channelNumber, byte searchTimeout) + public void ANTSetChannelSearchTimeout(byte channelNumber, byte searchTimeout) throws AntInterfaceException { if(!sServiceConnected) { - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.ANTSetChannelSearchTimeout(channelNumber, searchTimeout); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + if(!sAntReceiver.ANTSetChannelSearchTimeout(channelNumber, searchTimeout)) + { + throw new AntInterfaceException(); + } + } + catch(RemoteException e) + { + throw new AntRemoteException(e); } - return result; } - + /** * ANT set low priority channel search timeout. * * @param channelNumber the channel number * @param searchTimeout the search timeout - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public boolean ANTSetLowPriorityChannelSearchTimeout(byte channelNumber, byte searchTimeout) + public void ANTSetLowPriorityChannelSearchTimeout(byte channelNumber, byte searchTimeout) throws AntInterfaceException { if(!sServiceConnected) { - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.ANTSetLowPriorityChannelSearchTimeout(channelNumber, searchTimeout); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + if(!sAntReceiver.ANTSetLowPriorityChannelSearchTimeout(channelNumber, searchTimeout)) + { + throw new AntInterfaceException(); + } } - return result; - } - + catch(RemoteException e) + { + throw new AntRemoteException(e); + } + } + /** * ANT set proximity search. * * @param channelNumber the channel number * @param searchThreshold the search threshold - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public boolean ANTSetProximitySearch(byte channelNumber, byte searchThreshold) + public void ANTSetProximitySearch(byte channelNumber, byte searchThreshold) throws AntInterfaceException { if(!sServiceConnected) { - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.ANTSetProximitySearch(channelNumber, searchThreshold); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + if(!sAntReceiver.ANTSetProximitySearch(channelNumber, searchThreshold)) + { + throw new AntInterfaceException(); + } + } + catch(RemoteException e) + { + throw new AntRemoteException(e); } - return result; } - + /** * ANT set channel transmit power * @param channelNumber the channel number * @param txPower the transmit power level - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public boolean ANTSetChannelTxPower(byte channelNumber, byte txPower) + public void ANTSetChannelTxPower(byte channelNumber, byte txPower) throws AntInterfaceException { - if(!sServiceConnected) - { - return false; - } - - boolean result = false; - try { - result = sAntReceiver.ANTSetChannelTxPower(channelNumber, txPower); - } catch (RemoteException ex) - { - antServiceConnectionLost(); - } - return result; + if(!sServiceConnected) + { + throw new AntServiceNotConnectedException(); + } + + try + { + if(!sAntReceiver.ANTSetChannelTxPower(channelNumber, txPower)) + { + throw new AntInterfaceException(); + } + } + catch(RemoteException e) + { + throw new AntRemoteException(e); + } } - - + /** * ANT add channel id. * @@ -734,50 +749,62 @@ public class AntInterface { * @param deviceType the device type * @param txType the tx type * @param listIndex the list index - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public boolean ANTAddChannelId(byte channelNumber, short deviceNumber, byte deviceType, byte txType, byte listIndex) + public void ANTAddChannelId(byte channelNumber, short deviceNumber, byte deviceType, byte txType, byte listIndex) throws AntInterfaceException { if(!sServiceConnected) { - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.ANTAddChannelId(channelNumber, deviceNumber, deviceType, txType, listIndex); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + if(!sAntReceiver.ANTAddChannelId(channelNumber, deviceNumber, deviceType, txType, listIndex)) + { + throw new AntInterfaceException(); + } } - return result; - } - + catch(RemoteException e) + { + throw new AntRemoteException(e); + } + } + /** * ANT config list. * * @param channelNumber the channel number * @param listSize the list size * @param exclude the exclude - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public boolean ANTConfigList(byte channelNumber, byte listSize, byte exclude) + public void ANTConfigList(byte channelNumber, byte listSize, byte exclude) throws AntInterfaceException { if(!sServiceConnected) { - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.ANTConfigList(channelNumber, listSize, exclude); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + if(!sAntReceiver.ANTConfigList(channelNumber, listSize, exclude)) + { + throw new AntInterfaceException(); + } + } + catch(RemoteException e) + { + throw new AntRemoteException(e); } - return result; } - + /** * ANT config event buffering. * @@ -785,213 +812,266 @@ public class AntInterface { * @param screenOnFlushBufferThreshold the screen on flush buffer threshold * @param screenOffFlushTimerInterval the screen off flush timer interval * @param screenOffFlushBufferThreshold the screen off flush buffer threshold - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.3 */ - public boolean ANTConfigEventBuffering(short screenOnFlushTimerInterval, short screenOnFlushBufferThreshold, short screenOffFlushTimerInterval, short screenOffFlushBufferThreshold) + public void ANTConfigEventBuffering(short screenOnFlushTimerInterval, short screenOnFlushBufferThreshold, short screenOffFlushTimerInterval, short screenOffFlushBufferThreshold) throws AntInterfaceException { if(!sServiceConnected) { - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.ANTConfigEventBuffering((int)screenOnFlushTimerInterval, (int)screenOnFlushBufferThreshold, (int)screenOffFlushTimerInterval, (int)screenOffFlushBufferThreshold); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + if(!sAntReceiver.ANTConfigEventBuffering((int)screenOnFlushTimerInterval, (int)screenOnFlushBufferThreshold, (int)screenOffFlushTimerInterval, (int)screenOffFlushBufferThreshold)) + { + throw new AntInterfaceException(); + } + } + catch(RemoteException e) + { + throw new AntRemoteException(e); } - return result; } - + /** * ANT disable event buffering. * - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.1 */ - public boolean ANTDisableEventBuffering() + public void ANTDisableEventBuffering() throws AntInterfaceException { if(!sServiceConnected) { - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.ANTDisableEventBuffering(); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + if(!sAntReceiver.ANTDisableEventBuffering()) + { + throw new AntInterfaceException(); + } + } + catch(RemoteException e) + { + throw new AntRemoteException(e); } - return result; } - + /** * ANT open channel. * * @param channelNumber the channel number - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public boolean ANTOpenChannel(byte channelNumber) + public void ANTOpenChannel(byte channelNumber) throws AntInterfaceException { if(!sServiceConnected) { - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.ANTOpenChannel(channelNumber); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + if(!sAntReceiver.ANTOpenChannel(channelNumber)) + { + throw new AntInterfaceException(); + } + } + catch(RemoteException e) + { + throw new AntRemoteException(e); } - return result; } - + /** * ANT close channel. * * @param channelNumber the channel number - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public boolean ANTCloseChannel(byte channelNumber) + public void ANTCloseChannel(byte channelNumber) throws AntInterfaceException { if(!sServiceConnected) { - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.ANTCloseChannel(channelNumber); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + if(!sAntReceiver.ANTCloseChannel(channelNumber)) + { + throw new AntInterfaceException(); + } + } + catch(RemoteException e) + { + throw new AntRemoteException(e); } - return result; } - + /** * ANT request message. * * @param channelNumber the channel number * @param messageID the message id - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public boolean ANTRequestMessage(byte channelNumber, byte messageID) + public void ANTRequestMessage(byte channelNumber, byte messageID) throws AntInterfaceException { if(!sServiceConnected) { - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.ANTRequestMessage(channelNumber, messageID); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + if(!sAntReceiver.ANTRequestMessage(channelNumber, messageID)) + { + throw new AntInterfaceException(); + } + } + catch(RemoteException e) + { + throw new AntRemoteException(e); } - return result; } - + /** * ANT send broadcast data. * * @param channelNumber the channel number * @param txBuffer the tx buffer - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public boolean ANTSendBroadcastData(byte channelNumber, byte[] txBuffer) + public void ANTSendBroadcastData(byte channelNumber, byte[] txBuffer) throws AntInterfaceException { if(!sServiceConnected) { - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.ANTSendBroadcastData(channelNumber, txBuffer); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + if(!sAntReceiver.ANTSendBroadcastData(channelNumber, txBuffer)) + { + throw new AntInterfaceException(); + } + } + catch(RemoteException e) + { + throw new AntRemoteException(e); } - return result; } - + /** * ANT send acknowledged data. * * @param channelNumber the channel number * @param txBuffer the tx buffer - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public boolean ANTSendAcknowledgedData(byte channelNumber, byte[] txBuffer) + public void ANTSendAcknowledgedData(byte channelNumber, byte[] txBuffer) throws AntInterfaceException { if(!sServiceConnected) { - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.ANTSendAcknowledgedData(channelNumber, txBuffer); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + if(!sAntReceiver.ANTSendAcknowledgedData(channelNumber, txBuffer)) + { + throw new AntInterfaceException(); + } } - return result; - } + catch(RemoteException e) + { + throw new AntRemoteException(e); + } + } /** * ANT send burst transfer packet. * * @param control the control * @param txBuffer the tx buffer - * @return true, if successful + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public boolean ANTSendBurstTransferPacket(byte control, byte[] txBuffer) + public void ANTSendBurstTransferPacket(byte control, byte[] txBuffer) throws AntInterfaceException { if(!sServiceConnected) { - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - result = sAntReceiver.ANTSendBurstTransferPacket(control, txBuffer); - } catch (RemoteException ex) { - antServiceConnectionLost(); + try + { + if(!sAntReceiver.ANTSendBurstTransferPacket(control, txBuffer)) + { + throw new AntInterfaceException(); + } } - return result; - } + catch(RemoteException e) + { + throw new AntRemoteException(e); + } + } /** * Transmits the given data on channelNumber as part of a burst message. - * + * * @param channelNumber Which channel to transmit on. * @param txBuffer The data to send. * @param initialPacket Which packet in the burst sequence does the data begin in, 1 is the first. * @param containsEndOfBurst Is this the last of the data to be sent in burst. * @return The number of bytes still to be sent (approximately). 0 if success. + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException */ - public int ANTSendBurstTransfer(byte channelNumber, byte[] txBuffer) + public int ANTSendBurstTransfer(byte channelNumber, byte[] txBuffer) throws AntInterfaceException { - int result = txBuffer.length; - - if(sServiceConnected) + if(!sServiceConnected) { - try { - result = sAntReceiver.ANTSendBurstTransfer(channelNumber, txBuffer); - } catch (RemoteException ex) { - antServiceConnectionLost(); - } + throw new AntServiceNotConnectedException(); + } + + try + { + return sAntReceiver.ANTSendBurstTransfer(channelNumber, txBuffer); + } + catch(RemoteException e) + { + throw new AntRemoteException(e); } - return result; } - + /** * ANT send partial burst. * @@ -999,136 +1079,238 @@ public class AntInterface { * @param txBuffer the tx buffer * @param initialPacket the initial packet * @param containsEndOfBurst the contains end of burst - * @return the int + * @return The number of bytes still to be sent (approximately). 0 if success. + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.0 */ - public int ANTSendPartialBurst(byte channelNumber, byte[] txBuffer, int initialPacket, boolean containsEndOfBurst) + public int ANTSendPartialBurst(byte channelNumber, byte[] txBuffer, int initialPacket, boolean containsEndOfBurst) throws AntInterfaceException { - int result = txBuffer.length; - - if(sServiceConnected) + if(!sServiceConnected) { - try { - result = sAntReceiver.ANTTransmitBurst(channelNumber, txBuffer, initialPacket, containsEndOfBurst); - } catch (RemoteException ex) { - antServiceConnectionLost(); - } + throw new AntServiceNotConnectedException(); + } + + try + { + return sAntReceiver.ANTTransmitBurst(channelNumber, txBuffer, initialPacket, containsEndOfBurst); + } + catch(RemoteException e) + { + throw new AntRemoteException(e); } - return result; } /** * Returns the version code (eg. 1) of ANTLib used by the ANT application service * - * @return the service library version code - * @throws RemoteException the remote exception + * @return the service library version code, or 0 on error. + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.2 */ - public int getServiceLibraryVersionCode() throws RemoteException + public int getServiceLibraryVersionCode() throws AntInterfaceException { + if(!sServiceConnected) + { + throw new AntServiceNotConnectedException(); + } + if(mServiceLibraryVersionCode == 0) { - mServiceLibraryVersionCode = sAntReceiver.getServiceLibraryVersionCode(); + try + { + mServiceLibraryVersionCode = sAntReceiver.getServiceLibraryVersionCode(); + } + catch(RemoteException e) + { + throw new AntRemoteException(e); + } } - + return mServiceLibraryVersionCode; } - + /** * Returns the version name (eg "1.0") of ANTLib used by the ANT application service * - * @return the service library version name - * @throws RemoteException the remote exception + * @return the service library version name, or null on error. + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException * @since 1.2 */ - public String getServiceLibraryVersionName() throws RemoteException + public String getServiceLibraryVersionName() throws AntInterfaceException { - return sAntReceiver.getServiceLibraryVersionName(); - } - - - // - // ------------------- The below functions are for debugging/development and will likely disappear. - // - - /** - * Turn verbose logging on or off in the service. - * - * @param debug Whether debug logging should be enabled. - * @return true, if successful - * @since 1.4 - */ - public boolean debugLogging(boolean debug) - { - if(DEBUG) Log.d(TAG, "debugLogging"); - - if(!sServiceSettingsConnected) + if(!sServiceConnected) { - // Haven't received 'onConnected' notification yet - return false; + throw new AntServiceNotConnectedException(); } - boolean result = false; - try { - sServiceSettingsReceiver.debugLogging(debug); - result = true; - } catch (RemoteException ex) { - antServiceSettingsConnectionLost(); - } - return result; - } - - /** - * Set how many ANT packets should be combined in one request to the hardware during a burst transfer. - * - * @param numPackets Combined packet count. - * @return true, if successful - * @since 1.4 - */ - boolean setNumCombinedBurstPackets(int numPackets) - { - if(DEBUG) Log.d(TAG, "setNumCombinedBurstPackets"); - - if(!sServiceSettingsConnected) + try { - // Haven't received 'onConnected' notification yet - return false; + return sAntReceiver.getServiceLibraryVersionName(); } - - boolean result = false; - try { - sServiceSettingsReceiver.setNumCombinedBurstPackets(numPackets); - result = true; - } catch (RemoteException ex) { - antServiceSettingsConnectionLost(); - } - - return result; - } - - /** - * Get the maximum number of ANT packets which will be combined in one request to the hardware during a burst transfer. - * - * @return The number of combined packets, or -1 on failure. - * @since 1.4 - */ - int getNumCombinedBurstPackets() - { - if(DEBUG) Log.d(TAG, "getNumCombinedBurstPackets"); - - if(!sServiceSettingsConnected) + catch(RemoteException e) { - // Haven't received 'onConnected' notification yet - return -1; + throw new AntRemoteException(e); + } + } + + /** + * Take control of the ANT Radio. + * + * @return True if control has been granted, false if another application has control or the request failed. + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException + * @since 1.5 + */ + public boolean claimInterface() throws AntInterfaceException + { + if(!sServiceConnected) + { + throw new AntServiceNotConnectedException(); } - int result = -1; - try { - result = sServiceSettingsReceiver.getNumCombinedBurstPackets(); - } catch (RemoteException ex) { - antServiceSettingsConnectionLost(); + try + { + return sAntReceiver.claimInterface(); } - - return result; + catch(RemoteException e) + { + throw new AntRemoteException(e); + } + } + + /** + * Give up control of the ANT Radio. + * + * @return True if control has been given up, false if this application did not have control. + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException + * @since 1.5 + */ + public boolean releaseInterface() throws AntInterfaceException + { + if(!sServiceConnected) + { + throw new AntServiceNotConnectedException(); + } + + try + { + return sAntReceiver.releaseInterface(); + } + catch(RemoteException e) + { + throw new AntRemoteException(e); + } + } + + /** + * Claims the interface if it is available. If not the user will be prompted (on the notification bar) if a force claim should be done. + * If the ANT Interface is claimed, an AntInterfaceIntent.ANT_INTERFACE_CLAIMED_ACTION intent will be sent, with the current applications pid. + * + * @param String appName The name if this application, to show to the user. + * @returns false if a claim interface request notification already exists. + * @throws IllegalArgumentException + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException + * @since 1.5 + */ + public boolean requestForceClaimInterface(String appName) throws AntInterfaceException + { + if((null == appName) || ("" == appName)) + { + throw new IllegalArgumentException(); + } + + if(!sServiceConnected) + { + throw new AntServiceNotConnectedException(); + } + + try + { + return sAntReceiver.requestForceClaimInterface(appName); + } + catch(RemoteException e) + { + throw new AntRemoteException(e); + } + } + + /** + * Clears the notification asking the user if they would like to seize control of the ANT Radio. + * + * @returns false if this process is not requesting to claim the interface. + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException + * @since 1.5 + */ + public boolean stopRequestForceClaimInterface() throws AntInterfaceException + { + if(!sServiceConnected) + { + throw new AntServiceNotConnectedException(); + } + + try + { + return sAntReceiver.stopRequestForceClaimInterface(); + } + catch(RemoteException e) + { + throw new AntRemoteException(e); + } + } + + /** + * Check if the calling application has control of the ANT Radio. + * + * @return True if control is currently granted. + * @throws AntInterfaceException + * @throws AntServiceNotConnectedException + * @throws AntRemoteException + * @since 1.5 + */ + public boolean hasClaimedInterface() throws AntInterfaceException + { + if(!sServiceConnected) + { + throw new AntServiceNotConnectedException(); + } + + try + { + return sAntReceiver.hasClaimedInterface(); + } + catch(RemoteException e) + { + throw new AntRemoteException(e); + } + } + + /** + * Check if this device has support for ANT. + * + * @return True if the device supports ANT (may still require ANT Radio Service be installed). + * @since 1.5 + */ + public static boolean hasAntSupport(Context pContext) + { + if(!mCheckedAntSupported) + { + mAntSupported = Arrays.asList(pContext.getPackageManager().getSystemSharedLibraryNames()).contains(ANT_LIBRARY_NAME); + mCheckedAntSupported = true; + } + + return mAntSupported; } } diff --git a/MyTracks/src/com/dsi/ant/AntInterfaceIntent.java b/MyTracks/src/com/dsi/ant/AntInterfaceIntent.java index 1b0b7f7e7..7834e9daa 100644 --- a/MyTracks/src/com/dsi/ant/AntInterfaceIntent.java +++ b/MyTracks/src/com/dsi/ant/AntInterfaceIntent.java @@ -22,13 +22,16 @@ package com.dsi.ant; */ public interface AntInterfaceIntent { - public static final String STATUS = "com.dsi.ant.rx.intent.STATUS"; - public static final String ANT_MESSAGE = "com.dsi.ant.intent.ANT_MESSAGE"; + public static final String STATUS = "com.dsi.ant.rx.intent.STATUS"; + public static final String ANT_MESSAGE = "com.dsi.ant.intent.ANT_MESSAGE"; + public static final String ANT_INTERFACE_CLAIMED_PID = "com.dsi.ant.intent.ANT_INTERFACE_CLAIMED_PID"; public static final String ANT_ENABLED_ACTION = "com.dsi.ant.intent.action.ANT_ENABLED"; public static final String ANT_DISABLED_ACTION = "com.dsi.ant.intent.action.ANT_DISABLED"; public static final String ANT_RESET_ACTION = "com.dsi.ant.intent.action.ANT_RESET"; + public static final String ANT_INTERFACE_CLAIMED_ACTION = "com.dsi.ant.intent.action.ANT_INTERFACE_CLAIMED_ACTION"; + public static final String ANT_RX_MESSAGE_ACTION = "com.dsi.ant.intent.action.ANT_RX_MESSAGE_ACTION"; } diff --git a/MyTracks/src/com/dsi/ant/IAnt_6.aidl b/MyTracks/src/com/dsi/ant/IAnt_6.aidl new file mode 100644 index 000000000..4caccbeb7 --- /dev/null +++ b/MyTracks/src/com/dsi/ant/IAnt_6.aidl @@ -0,0 +1,77 @@ +/* + * Copyright 2011 Dynastream Innovations Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.dsi.ant; + +/** + * The Android Ant API is not finalized, and *will* be updated and expanded. + * This is the base level ANT messaging API and gives any application full + * control over the ANT radio HW. Caution should be exercised when using + * this interface. + * + * Public API for controlling the Ant Service. + * + * {@hide} + */ +interface IAnt_6 { +// Since version 1 (1.0): + boolean enable(); + boolean disable(); + boolean isEnabled(); + + boolean ANTTxMessage(in byte[] message); + + boolean ANTResetSystem(); + boolean ANTUnassignChannel(byte channelNumber); + boolean ANTAssignChannel(byte channelNumber, byte channelType, byte networkNumber); + boolean ANTSetChannelId(byte channelNumber, int deviceNumber, byte deviceType, byte txType); + boolean ANTSetChannelPeriod(byte channelNumber, int channelPeriod); + boolean ANTSetChannelRFFreq(byte channelNumber, byte radioFrequency); + boolean ANTSetChannelSearchTimeout(byte channelNumber, byte searchTimeout); + boolean ANTSetLowPriorityChannelSearchTimeout(byte channelNumber, byte searchTimeout); + boolean ANTSetProximitySearch(byte channelNumber, byte searchThreshold); + boolean ANTSetChannelTxPower(byte channelNumber, byte txPower); + boolean ANTAddChannelId(byte channelNumber, int deviceNumber, byte deviceType, byte txType, byte listIndex); + boolean ANTConfigList(byte channelNumber, byte listSize, byte exclude); + + boolean ANTOpenChannel(byte channelNumber); + boolean ANTCloseChannel(byte channelNumber); + + boolean ANTRequestMessage(byte channelNumber, byte messageID); + boolean ANTSendBroadcastData(byte channelNumber, in byte[] txBuffer); + boolean ANTSendAcknowledgedData(byte channelNumber, in byte[] txBuffer); + + boolean ANTSendBurstTransferPacket(byte control, in byte[] txBuffer); + + int ANTSendBurstTransfer(byte channelNumber, in byte[] txBuffer); + int ANTTransmitBurst(byte channelNumber, in byte[] txBuffer, int initialPacket, boolean containsEndOfBurst); + +// Since version 4 (1.3): + boolean ANTConfigEventBuffering(int screenOnFlushTimerInterval, int screenOnFlushBufferThreshold, int screenOffFlushTimerInterval, int screenOffFlushBufferThreshold); + +// Since version 2 (1.1): + boolean ANTDisableEventBuffering(); + +// Since version 3 (1.2): + int getServiceLibraryVersionCode(); + String getServiceLibraryVersionName(); + +// Since version 6 (1.5): + boolean claimInterface(); + boolean requestForceClaimInterface(String appName); + boolean stopRequestForceClaimInterface(); + boolean releaseInterface(); + boolean hasClaimedInterface(); +} diff --git a/MyTracks/src/com/dsi/ant/Version.java b/MyTracks/src/com/dsi/ant/Version.java new file mode 100644 index 000000000..7d7cb8bd5 --- /dev/null +++ b/MyTracks/src/com/dsi/ant/Version.java @@ -0,0 +1,35 @@ +/* + * Copyright 2011 Dynastream Innovations Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.dsi.ant; + + + +/** + * Defines version numbers + * + * @hide + */ +public class Version { + + ////////////////////////////////////////////// + // Library Version Information + ////////////////////////////////////////////// + public static final int ANT_LIBRARY_VERSION_CODE = 6; + public static final int ANT_LIBRARY_VERSION_MAJOR = 2; + public static final int ANT_LIBRARY_VERSION_MINOR = 0; + public static final String ANT_LIBRARY_VERSION_NAME = String.valueOf(ANT_LIBRARY_VERSION_MAJOR) + "." + String.valueOf(ANT_LIBRARY_VERSION_MINOR); + +} \ No newline at end of file diff --git a/MyTracks/src/com/dsi/ant/exception/AntInterfaceException.java b/MyTracks/src/com/dsi/ant/exception/AntInterfaceException.java new file mode 100644 index 000000000..164374e4a --- /dev/null +++ b/MyTracks/src/com/dsi/ant/exception/AntInterfaceException.java @@ -0,0 +1,20 @@ +package com.dsi.ant.exception; + +public class AntInterfaceException extends Exception +{ + + /** + * + */ + private static final long serialVersionUID = -7278855366167722274L; + + public AntInterfaceException() + { + this("Unknown ANT Interface error"); + } + + public AntInterfaceException(String string) + { + super(string); + } +} diff --git a/MyTracks/src/com/dsi/ant/exception/AntRemoteException.java b/MyTracks/src/com/dsi/ant/exception/AntRemoteException.java new file mode 100644 index 000000000..960740f1c --- /dev/null +++ b/MyTracks/src/com/dsi/ant/exception/AntRemoteException.java @@ -0,0 +1,23 @@ +package com.dsi.ant.exception; + +import android.os.RemoteException; + +public class AntRemoteException extends AntInterfaceException +{ + /** + * + */ + private static final long serialVersionUID = 8950974759973459561L; + + public AntRemoteException(RemoteException e) + { + this("ANT Interface error, ANT Radio Service remote error: "+e.toString(), e); + } + + public AntRemoteException(String string, RemoteException e) + { + super(string); + + this.initCause(e); + } +} diff --git a/MyTracks/src/com/dsi/ant/exception/AntServiceNotConnectedException.java b/MyTracks/src/com/dsi/ant/exception/AntServiceNotConnectedException.java new file mode 100644 index 000000000..fc131cf8f --- /dev/null +++ b/MyTracks/src/com/dsi/ant/exception/AntServiceNotConnectedException.java @@ -0,0 +1,19 @@ +package com.dsi.ant.exception; + +public class AntServiceNotConnectedException extends AntInterfaceException +{ + /** + * + */ + private static final long serialVersionUID = -2085081032170129309L; + + public AntServiceNotConnectedException() + { + this("ANT Interface error, ANT Radio Service not connected."); + } + + public AntServiceNotConnectedException(String string) + { + super(string); + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/AntDirectSensorManager.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/AntDirectSensorManager.java index a26d0e8cd..cc102537a 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/AntDirectSensorManager.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/AntDirectSensorManager.java @@ -1,12 +1,12 @@ /* * Copyright 2009 Google Inc. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -18,10 +18,14 @@ package com.google.android.apps.mytracks.services.sensors; import static com.google.android.apps.mytracks.MyTracksConstants.TAG; import com.google.android.apps.mytracks.MyTracksSettings; import com.google.android.apps.mytracks.content.Sensor; +import com.google.android.apps.mytracks.services.sensors.ant.AntChannelIdMessage; +import com.google.android.apps.mytracks.services.sensors.ant.AntChannelResponseMessage; import com.google.android.maps.mytracks.R; import com.dsi.ant.AntDefine; import com.dsi.ant.AntMesg; +import com.dsi.ant.exception.AntInterfaceException; + import android.content.Context; import android.content.SharedPreferences; import android.util.Log; @@ -49,62 +53,70 @@ public class AntDirectSensorManager extends AntSensorManager { public AntDirectSensorManager(Context context) { super(context); - + deviceNumberHRM = WILDCARD; // First read the the device id that we will be pairing with. SharedPreferences prefs = context.getSharedPreferences( MyTracksSettings.SETTINGS_NAME, 0); if (prefs != null) { - deviceNumberHRM = + deviceNumberHRM = (short) prefs.getInt(context.getString(R.string.ant_heart_rate_sensor_id_key), 0); } - Log.i(TAG, "Pairing with heart rate monitor: " + deviceNumberHRM); + Log.i(TAG, "Will pair with heart rate monitor: " + deviceNumberHRM); } @Override - public void handleMessage(byte[] antMessage) { - int channel = - antMessage[AntMesg.MESG_DATA_OFFSET] & AntDefine.CHANNEL_NUMBER_MASK; + protected boolean handleMessage(byte messageId, byte[] messageData) { + if (super.handleMessage(messageId, messageData)) { + return true; + } + + int channel = messageData[0] & AntDefine.CHANNEL_NUMBER_MASK; switch (channel) { case HRM_CHANNEL: - antDecodeHRM(antMessage); + antDecodeHRM(messageId, messageData); break; default: Log.d(TAG, "Unhandled message: " + channel); } + + return true; } /** * Decode an ant heart rate monitor message. - * @param antMessage The byte array received from the heart rate monitor. + * @param messageData The byte array received from the heart rate monitor. */ - private void antDecodeHRM(byte[] antMessage) { - // message ID == broadcast - switch (antMessage[AntMesg.MESG_ID_OFFSET]) { + private void antDecodeHRM(int messageId, byte[] messageData) { + switch (messageId) { case AntMesg.MESG_BROADCAST_DATA_ID: - handleBroadcastData(antMessage); + handleBroadcastData(messageData); break; case AntMesg.MESG_RESPONSE_EVENT_ID: - handleMessageResponse(antMessage); + handleMessageResponse(messageData); break; case AntMesg.MESG_CHANNEL_ID_ID: - handleChannelId(antMessage); + handleChannelId(messageData); break; default: - Log.e(TAG, "Unexpected message id: " + antMessage[3]); + Log.e(TAG, "Unexpected message id: " + messageId); } } private void handleBroadcastData(byte[] antMessage) { if (deviceNumberHRM == WILDCARD) { - getAntReceiver().ANTRequestMessage(HRM_CHANNEL, - AntMesg.MESG_CHANNEL_ID_ID); + try { + getAntReceiver().ANTRequestMessage(HRM_CHANNEL, + AntMesg.MESG_CHANNEL_ID_ID); + } catch (AntInterfaceException e) { + Log.e(TAG, "ANT error handling broadcast data", e); + } Log.d(TAG, "Requesting channel id id."); } setSensorState(Sensor.SensorState.CONNECTED); - int bpm = (int) antMessage[10] & 0xFF; + int bpm = (int) antMessage[8] & 0xFF; Sensor.SensorData.Builder b = Sensor.SensorData.newBuilder() .setValue(bpm) .setState(Sensor.SensorState.SENDING); @@ -115,11 +127,9 @@ public class AntDirectSensorManager extends AntSensorManager { .build(); } - void handleChannelId(byte[] antMessage) { - // Store the device id. - deviceNumberHRM = - (short) (((int) antMessage[3] & 0xFF | - ((int) (antMessage[4] & 0xFF) << 8)) & 0xFFFF); + void handleChannelId(byte[] rawMessage) { + AntChannelIdMessage message = new AntChannelIdMessage(rawMessage); + deviceNumberHRM = message.getDeviceNumber(); Log.i(TAG, "Found device id: " + deviceNumberHRM); SharedPreferences prefs = context.getSharedPreferences( @@ -129,26 +139,31 @@ public class AntDirectSensorManager extends AntSensorManager { editor.commit(); } - private void handleMessageResponse(byte[] antMessage) { - if (antMessage[3] == AntMesg.MESG_EVENT_ID && - antMessage[4] == AntDefine.EVENT_RX_SEARCH_TIMEOUT) { - // Search timeout - Log.w(TAG, "Search timed out. Unassigning channel."); - getAntReceiver().ANTUnassignChannel((byte) 0); - setSensorState(Sensor.SensorState.DISCONNECTED); - } else if (antMessage[3] == AntMesg.MESG_UNASSIGN_CHANNEL_ID) { - setSensorState(Sensor.SensorState.DISCONNECTED); - Log.i(TAG, "Disconnected from the sensor: " + getSensorState()); + private void handleMessageResponse(byte[] rawMessage) { + AntChannelResponseMessage message = new AntChannelResponseMessage(rawMessage); + switch (message.getMessageId()) { + case AntMesg.MESG_EVENT_ID: + if (message.getMessageCode() == AntDefine.EVENT_RX_SEARCH_TIMEOUT) { + // Search timeout + Log.w(TAG, "Search timed out. Unassigning channel."); + try { + getAntReceiver().ANTUnassignChannel((byte) 0); + } catch (AntInterfaceException e) { + Log.e(TAG, "ANT error unassigning channel", e); + } + setSensorState(Sensor.SensorState.DISCONNECTED); + } + break; + + case AntMesg.MESG_UNASSIGN_CHANNEL_ID: + setSensorState(Sensor.SensorState.DISCONNECTED); + Log.i(TAG, "Disconnected from the sensor: " + getSensorState()); + break; } } - /** - * Open a channel to the SRM head unit. - */ - @Override - public void setupChannel() { - super.setupChannel(); - antChannelSetup(NETWORK_NUMBER, + @Override protected void setupAntSensorChannels() { + setupAntSensorChannel(NETWORK_NUMBER, HRM_CHANNEL, deviceNumberHRM, HEART_RATE_DEVICE_TYPE, @@ -156,7 +171,6 @@ public class AntDirectSensorManager extends AntSensorManager { CHANNEL_PERIOD, RF_FREQUENCY, (byte) 0); - setSensorState(Sensor.SensorState.CONNECTING); } public short getDeviceNumberHRM() { diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/AntSRMSensorManager.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/AntSRMSensorManager.java index 3d038a922..d46718362 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/AntSRMSensorManager.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/AntSRMSensorManager.java @@ -1,12 +1,12 @@ /* * Copyright 2009 Google Inc. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -22,6 +22,8 @@ import com.google.android.maps.mytracks.R; import com.dsi.ant.AntDefine; import com.dsi.ant.AntMesg; +import com.dsi.ant.exception.AntInterfaceException; + import android.content.Context; import android.content.SharedPreferences; import android.util.Log; @@ -104,8 +106,12 @@ public class AntSRMSensorManager extends AntSensorManager { private void handleBroadcastData(byte[] antMessage) { if (deviceId == WILDCARD) { - getAntReceiver().ANTRequestMessage(channel, AntMesg.MESG_CHANNEL_ID_ID); - Log.d(MyTracksConstants.TAG, "Requesting channel id id."); + try { + Log.d(MyTracksConstants.TAG, "Requesting channel id id."); + getAntReceiver().ANTRequestMessage(channel, AntMesg.MESG_CHANNEL_ID_ID); + } catch (AntInterfaceException e) { + Log.e(MyTracksConstants.TAG, "Failed to request channel id id", e); + } } setSensorState(Sensor.SensorState.CONNECTED); switch(antMessage[MESSAGE_TYPE_INDEX]) { @@ -138,7 +144,11 @@ public class AntSRMSensorManager extends AntSensorManager { && antMessage[4] == AntDefine.EVENT_RX_SEARCH_TIMEOUT) { // Search timeout Log.w(MyTracksConstants.TAG, "Search timed out. Unassigning channel."); - getAntReceiver().ANTUnassignChannel(channel); + try { + getAntReceiver().ANTUnassignChannel(channel); + } catch (AntInterfaceException e) { + Log.e(MyTracksConstants.TAG, "Failed to unassign ANT channel", e); + } setSensorState(Sensor.SensorState.DISCONNECTED); } else if (antMessage[3] == AntMesg.MESG_UNASSIGN_CHANNEL_ID) { setSensorState(Sensor.SensorState.DISCONNECTED); @@ -207,12 +217,10 @@ public class AntSRMSensorManager extends AntSensorManager { sensorData = builder.build(); } - /** - * Open a channel to the SRM head unit. - */ @Override - public void setupChannel() { - antChannelSetup(NETWORK_NUMBER, + protected void setupAntSensorChannels() { + lastMessageId = 0; + setupAntSensorChannel(NETWORK_NUMBER, channel, deviceId, DEVICE_TYPE, @@ -220,7 +228,5 @@ public class AntSRMSensorManager extends AntSensorManager { CHANNEL_PERIOD, RF_FREQUENCY, (byte) 0); - lastMessageId = 0; - setSensorState(Sensor.SensorState.CONNECTING); } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/AntSensorManager.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/AntSensorManager.java index eb2230f10..2b0b136b1 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/AntSensorManager.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/AntSensorManager.java @@ -1,12 +1,12 @@ /* * Copyright 2009 Google Inc. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -16,37 +16,56 @@ package com.google.android.apps.mytracks.services.sensors; import com.google.android.apps.mytracks.MyTracksConstants; +import com.google.android.apps.mytracks.content.Sensor; import com.google.android.apps.mytracks.content.Sensor.SensorDataSet; +import com.google.android.apps.mytracks.services.sensors.ant.AntStartupMessage; import com.dsi.ant.AntDefine; import com.dsi.ant.AntInterface; import com.dsi.ant.AntInterfaceIntent; +import com.dsi.ant.AntMesg; +import com.dsi.ant.exception.AntInterfaceException; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.os.Handler; -import android.os.Message; import android.util.Log; /** - * An abstract class for ANT+ based sensors. + * This is the common superclass for ANT-based sensors. It handles tasks which + * apply to the ANT framework as a whole, such as framework initialization and + * destruction. Subclasses are expected to handle the initialization and + * management of individual sensors. + * + * Implementation: + * + * The initialization process is somewhat complicated. This is due in part to + * the asynchronous nature of ANT Radio Service initialization, and in part due + * to an apparent bug in that service. The following is an overview of the + * initialization process. + * + * Initialization begins in {@link #setupChannel}, which is invoked by the + * {@link SensorManager} when track recording begins. {@link #setupChannel} + * asks the ANT Radio Service (via {@link AntInterface}) to start, using a + * {@link AntInterface.ServiceListener} to indicate when the service has + * connected. {@link #serviceConnected} claims and enables the Radio Service, + * and then resets it to a known state for our use. Completion of reset is + * indicated by receipt of a startup message (see {@link AntStartupMessage}). + * Once we've received that message, the ANT service is ready for use, and we + * can start sensor-specific initialization using + * {@link #setupAntSensorChannels}. The initialization of each sensor will + * usually result in a call to {@link #setupAntSensorChannel}. * * @author Sandor Dornbush */ public abstract class AntSensorManager extends SensorManager { - // ANT Service - public static final int EVENT_ANT_ENABLED = 1; - public static final int EVENT_ANT_DISABLED = 2; - // Pairing protected static final short WILDCARD = 0; private AntInterface antReceiver; - private boolean status; - // Flag to know if the ANT App was interrupted // TODO: This code path is not used but probably should be. private boolean antInterrupted; @@ -57,40 +76,29 @@ public abstract class AntSensorManager extends SensorManager { protected SensorDataSet sensorData; protected Context context; - + private static final boolean DEBUGGING = false; - /** - * Receives all of the ANT intents and dispatches to the proper handler. - */ - private final BroadcastReceiver receiver = new BroadcastReceiver() { + /** Receives and logs all status ANT intents. */ + private final BroadcastReceiver statusReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String antAction = intent.getAction(); + Log.i(MyTracksConstants.TAG, "enter status onReceive" + antAction); + } + }; - Log.i(MyTracksConstants.TAG, "enter onReceive" + antAction); - if (antAction.equals(AntInterfaceIntent.ANT_ENABLED_ACTION)) { - Log.i(MyTracksConstants.TAG, "enter onReceive ANT_ENABLED_ACTION " - + antAction); - handler.sendMessage(handler.obtainMessage(EVENT_ANT_ENABLED, 0)); - } - if (antAction.equals(AntInterfaceIntent.ANT_DISABLED_ACTION)) { - Log.i(MyTracksConstants.TAG, "enter onReceive ANT_DISABLED_ACTION " - + antAction); - handler.sendMessage(handler.obtainMessage(EVENT_ANT_DISABLED, 0)); - } + /** Receives all data ANT intents. */ + private final BroadcastReceiver dataReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String antAction = intent.getAction(); + Log.i(MyTracksConstants.TAG, "enter data onReceive" + antAction); if (antAction.equals(AntInterfaceIntent.ANT_RX_MESSAGE_ACTION)) { - byte[] antMessage = - intent.getByteArrayExtra(AntInterfaceIntent.ANT_MESSAGE); + byte[] antMessage = intent.getByteArrayExtra(AntInterfaceIntent.ANT_MESSAGE); if (DEBUGGING) { - StringBuilder sb = - new StringBuilder("ANT_RX_MESSAGE_ACTION Rx:"); - - for (int i = 0; i < antMessage.length; i++) - sb.append(String.format("[%X]]", (antMessage[i] & 0xFF))); - - Log.d(MyTracksConstants.TAG, sb.toString()); + Log.d(MyTracksConstants.TAG, "Received RX message " + messageToString(antMessage)); } handleMessage(antMessage); @@ -99,60 +107,39 @@ public abstract class AntSensorManager extends SensorManager { }; /** - * Handler for all the ANT related events. The events will be handled only - * when we are in the ANT application main menu - **/ - private Handler handler = new Handler() { + * ANT uses this listener to tell us when it has bound to the ANT Radio + * Service. We can't start sending ANT commands until we've been notified + * (via this listener) that the Radio Service has connected. + */ + private AntInterface.ServiceListener antServiceListener = new AntInterface.ServiceListener() { @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case EVENT_ANT_ENABLED: - Log.i(MyTracksConstants.TAG, - "enter handleMessage ----EVENT_ANT_ENABLED"); - break; - case EVENT_ANT_DISABLED: - Log.i(MyTracksConstants.TAG, - "enter handleMessage ----EVENT_ANT_DISABLED"); - break; - } + public void onServiceConnected() { + serviceConnected(); + } + + @Override + public void onServiceDisconnected() { + Log.d(MyTracksConstants.TAG, "ANT interface reports disconnection"); } }; public AntSensorManager(Context context) { this.context = context; - - // Need to enable the ANT if it is not enabled earlier - antReceiver = AntInterface.getInstance(context, null); - if (antReceiver == null) { - return; - } - - status = antReceiver.isEnabled(); - if (!status) { - // Make sure not to call AntInterface.enable() again, if it has been - // already called before - if (antInterrupted == false) { - status = antReceiver.enable(); - if (!status) { - Log.e(MyTracksConstants.TAG, "Can not enable ANT interface"); - } else { - Log.i(MyTracksConstants.TAG, "Powering on Radio"); - } - } else { - Log.i(MyTracksConstants.TAG, "Radio already enabled"); - } - } } @Override - public void setupChannel() { - Log.i(MyTracksConstants.TAG, "Registering for ant intents."); - // Register for ANT intent broadcasts. - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(AntInterfaceIntent.ANT_ENABLED_ACTION); - intentFilter.addAction(AntInterfaceIntent.ANT_DISABLED_ACTION); - intentFilter.addAction(AntInterfaceIntent.ANT_RX_MESSAGE_ACTION); - context.registerReceiver(receiver, intentFilter); + public void onDestroy() { + Log.i(MyTracksConstants.TAG, "destroying AntSensorManager"); + context.unregisterReceiver(statusReceiver); + context.unregisterReceiver(dataReceiver); + + try { + antReceiver.releaseInterface(); + } catch (AntInterfaceException e) { + Log.e(MyTracksConstants.TAG, "failed to release ANT interface", e); + } + + antReceiver.destroy(); } @Override @@ -165,64 +152,184 @@ public abstract class AntSensorManager extends SensorManager { return true; } - public abstract void handleMessage(byte[] antMessage); - - @Override - public void onDestroy() { - Log.i(MyTracksConstants.TAG, "Unregistering for ant intents."); - context.unregisterReceiver(receiver); - } - - /** - * Set up an ANT+ channel. - * - * @return True on success. - */ - protected boolean antChannelSetup(byte networkNumber, byte channelNumber, - short deviceNumber, byte deviceType, byte txType, short channelPeriod, - byte radioFreq, byte proxSearch) { - - // Assign as slave channel on selected network (0 = public, 1 = ANT+, 2 = - // ANTFS) - if (!antReceiver.ANTAssignChannel(channelNumber, - AntDefine.PARAMETER_RX_NOT_TX, networkNumber)) { - return false; - } - - if (!antReceiver.ANTSetChannelId(channelNumber, deviceNumber, deviceType, - txType)) { - return false; - } - - if (!antReceiver.ANTSetChannelPeriod(channelNumber, channelPeriod)) { - return false; - } - - if (!antReceiver.ANTSetChannelRFFreq(channelNumber, radioFreq)) { - return false; - } - - // Disable high priority search - if (!antReceiver.ANTSetChannelSearchTimeout(channelNumber, (byte) 0)) { - return false; - } - - // Set search timeout to 30 seconds (low priority search)) - if (!antReceiver.ANTSetLowPriorityChannelSearchTimeout(channelNumber, (byte) 12)) { - return false; - } - - if (deviceNumber == WILDCARD) { - // Configure proximity search, if using wild card search - if (!antReceiver.ANTSetProximitySearch(channelNumber, proxSearch)) { - return false; - } - } - - return !antReceiver.ANTOpenChannel(channelNumber); - } - public AntInterface getAntReceiver() { return antReceiver; } + + /** + * This is the interface used by the {@link SensorManager} to tell this + * class when to start. It handles initialization of the ANT framework, + * eventually resulting in sensor-specific initialization via + * {@link #setupAntSensorChannels}. + */ + @Override + protected final void setupChannel() { + // We handle this unpleasantly because the UI should've checked for ANT + // support before it even instantiated this class. + if (!AntInterface.hasAntSupport(context)) { + throw new IllegalStateException("device does not have ANT support"); + } + + // We register for ANT intents early because we want to have a record of + // the status intents in the log as we start up. + registerForAntIntents(); + + antReceiver = AntInterface.getInstance(context, antServiceListener); + + if (antReceiver == null) { + Log.e(MyTracksConstants.TAG, "Failed to get ANT Receiver"); + return; + } + + setSensorState(Sensor.SensorState.CONNECTING); + } + + /** + * This method is invoked via the ServiceListener when we're connected to + * the ANT service. If we're just starting up, this is our first opportunity + * to initiate any ANT commands. + */ + private synchronized void serviceConnected() { + Log.d(MyTracksConstants.TAG, "ANT service connected"); + + try { + if (!antReceiver.claimInterface()) { + Log.e(MyTracksConstants.TAG, "failed to claim ANT interface"); + return; + } + + if (!antReceiver.isEnabled()) { + // Make sure not to call AntInterface.enable() again, if it has been + // already called before + if (antInterrupted == false) { + Log.i(MyTracksConstants.TAG, "Powering on Radio"); + antReceiver.enable(); + } + } else { + Log.i(MyTracksConstants.TAG, "Radio already enabled"); + } + } catch (AntInterfaceException e) { + Log.e(MyTracksConstants.TAG, "failed to enable ANT", e); + } + + try { + // We expect this call to throw an exception due to a bug in the ANT + // Radio Service. It won't actually fail, though, as we'll get the + // startup message (see {@link AntStartupMessage}) one normally expects + // after a reset. Channel initialization can proceed once we receive + // that message. + antReceiver.ANTResetSystem(); + } catch (AntInterfaceException e) { + Log.e(MyTracksConstants.TAG, "failed to reset ANT (expected exception)", e); + } + } + + /** + * Process a raw ANT message. + * @param antMessage the ANT message, including the size and message ID bytes + * @deprecated Use {@link #handleMessage(int, byte[])} instead. + */ + protected void handleMessage(byte[] antMessage) { + int len = antMessage[0]; + if (len != antMessage.length - 2 || antMessage.length <= 2) { + Log.e(MyTracksConstants.TAG, "Invalid message: " + messageToString(antMessage)); + return; + } + + byte messageId = antMessage[1]; + // Arrays#copyOfRange doesn't exist?? + byte[] messageData = new byte[antMessage.length - 2]; + System.arraycopy(antMessage, 2, messageData, 0, antMessage.length - 2); + handleMessage(messageId, messageData); + } + + /** + * Process a raw ANT message. + * @param messageId the message ID. See the ANT Message Protocol and Usage + * guide, section 9.3. + * @param messageData the ANT message, without the size and message ID bytes. + * @return true if this method has taken responsibility for the passed + * message; false otherwise. + */ + protected boolean handleMessage(byte messageId, byte[] messageData) { + if (messageId == AntMesg.MESG_STARTUP_MESG_ID) { + Log.d(MyTracksConstants.TAG, String.format( + "Received startup message (reason %02x); initializing channel", + new AntStartupMessage(messageData).getMessage())); + setupAntSensorChannels(); + return true; + } + + return false; + } + + /** + * Subclasses define this method to perform sensor-specific initialization. + * When this method is called, the ANT framework has been enabled, and is + * ready for use. + */ + protected abstract void setupAntSensorChannels(); + + /** + * Used by subclasses to set up an ANT channel for a single sensor. A given + * subclass may invoke this method multiple times if the subclass is + * responsible for more than one sensor. + * + * @return true on success + */ + protected boolean setupAntSensorChannel(byte networkNumber, byte channelNumber, + short deviceNumber, byte deviceType, byte txType, short channelPeriod, + byte radioFreq, byte proxSearch) { + + try { + // Assign as slave channel on selected network (0 = public, 1 = ANT+, 2 = + // ANTFS) + antReceiver.ANTAssignChannel(channelNumber, AntDefine.PARAMETER_RX_NOT_TX, networkNumber); + + antReceiver.ANTSetChannelId(channelNumber, deviceNumber, deviceType, txType); + antReceiver.ANTSetChannelPeriod(channelNumber, channelPeriod); + antReceiver.ANTSetChannelRFFreq(channelNumber, radioFreq); + + // Disable high priority search + antReceiver.ANTSetChannelSearchTimeout(channelNumber, (byte) 0); + + // Set search timeout to 30 seconds (low priority search)) + antReceiver.ANTSetLowPriorityChannelSearchTimeout(channelNumber, (byte) 12); + + if (deviceNumber == WILDCARD) { + // Configure proximity search, if using wild card search + antReceiver.ANTSetProximitySearch(channelNumber, proxSearch); + } + + antReceiver.ANTOpenChannel(channelNumber); + return true; + + } catch (AntInterfaceException e) { + Log.e(MyTracksConstants.TAG, "failed to setup ANT channel", e); + return false; + } + } + + private void registerForAntIntents() { + Log.i(MyTracksConstants.TAG, "Registering for ant intents."); + // Register for ANT intent broadcasts. + IntentFilter statusIntentFilter = new IntentFilter(); + statusIntentFilter.addAction(AntInterfaceIntent.ANT_ENABLED_ACTION); + statusIntentFilter.addAction(AntInterfaceIntent.ANT_DISABLED_ACTION); + statusIntentFilter.addAction(AntInterfaceIntent.ANT_INTERFACE_CLAIMED_ACTION); + statusIntentFilter.addAction(AntInterfaceIntent.ANT_RESET_ACTION); + context.registerReceiver(statusReceiver, statusIntentFilter); + + IntentFilter dataIntentFilter = new IntentFilter(); + dataIntentFilter.addAction(AntInterfaceIntent.ANT_RX_MESSAGE_ACTION); + context.registerReceiver(dataReceiver, dataIntentFilter); + } + + private String messageToString(byte[] message) { + StringBuilder out = new StringBuilder(); + for (byte b : message) { + out.append(String.format("%s%02x", (out.length() == 0 ? "" : " "), b)); + } + return out.toString(); + } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/SensorManager.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/SensorManager.java index 49b421b50..0ebd856a8 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/SensorManager.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/SensorManager.java @@ -41,7 +41,7 @@ public abstract class SensorManager { public static final int RETRY_PERIOD = 30000; private Sensor.SensorState sensorState = Sensor.SensorState.NONE; - + private long sensorStateTimestamp = 0; /** @@ -89,7 +89,11 @@ public abstract class SensorManager { timer.schedule(checkSensorManager, RETRY_PERIOD, RETRY_PERIOD); } - public abstract void setupChannel(); + /** + * This method is used to set up any necessary connections to underlying + * sensor hardware. + */ + protected abstract void setupChannel(); public void shutdown() { timer.cancel(); diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntChannelIdMessage.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntChannelIdMessage.java new file mode 100644 index 000000000..2d866fe1c --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntChannelIdMessage.java @@ -0,0 +1,56 @@ +/* + * Copyright 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.android.apps.mytracks.services.sensors.ant; + +/** + * This class decodes and encapsulates an ANT Channel ID message. + * (ANT Message ID 0x51, Protocol & Usage guide v4.2 section 9.5.7.2) + * + * @author Matthew Simmons + */ +public class AntChannelIdMessage extends AntMessage { + private byte channelNumber; + private short deviceNumber; + private byte deviceTypeId; + private byte transmissionType; + + public AntChannelIdMessage(byte[] messageData) { + channelNumber = messageData[0]; + deviceNumber = decodeShort(messageData[1], messageData[2]); + deviceTypeId = messageData[3]; + transmissionType = messageData[4]; + } + + /** Returns the channel number */ + public byte getChannelNumber() { + return channelNumber; + } + + /** Returns the device number */ + public short getDeviceNumber() { + return deviceNumber; + } + + /** Returns the device type */ + public byte getDeviceTypeId() { + return deviceTypeId; + } + + /** Returns the transmission type */ + public byte getTransmissionType() { + return transmissionType; + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntChannelResponseMessage.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntChannelResponseMessage.java new file mode 100644 index 000000000..a29a0757d --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntChannelResponseMessage.java @@ -0,0 +1,49 @@ +/* + * Copyright 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.android.apps.mytracks.services.sensors.ant; + +/** + * This class decodes and encapsulates an ANT Channel Response / Event message. + * (ANT Message ID 0x40, Protocol & Usage guide v4.2 section 9.5.6.1) + * + * @author Matthew Simmons + */ +public class AntChannelResponseMessage extends AntMessage { + private byte channelNumber; + private byte messageId; + private byte messageCode; + + public AntChannelResponseMessage(byte[] messageData) { + channelNumber = messageData[0]; + messageId = messageData[1]; + messageCode = messageData[2]; + } + + /** Returns the channel number */ + public byte getChannelNumber() { + return channelNumber; + } + + /** Returns the ID of the message being responded to */ + public byte getMessageId() { + return messageId; + } + + /** Returns the code for a specific response or event */ + public byte getMessageCode() { + return messageCode; + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntMessage.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntMessage.java new file mode 100644 index 000000000..3f1b13529 --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntMessage.java @@ -0,0 +1,32 @@ +/* + * Copyright 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.android.apps.mytracks.services.sensors.ant; + +/** + * This is a common superclass for ANT message subclasses. + * + * @author Matthew Simmons + */ +public class AntMessage { + protected AntMessage() {} + + /** Build a short value from its constituent bytes */ + protected static short decodeShort(byte b0, byte b1) { + short value = b0; + value |= ((short) b1) << 8; + return value; + } +} diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntStartupMessage.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntStartupMessage.java new file mode 100644 index 000000000..3e4dc1f68 --- /dev/null +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntStartupMessage.java @@ -0,0 +1,35 @@ +/* + * Copyright 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.android.apps.mytracks.services.sensors.ant; + +/** + * This class decodes and encapsulates an ANT Startup message. + * (ANT Message ID 0x6f, Protocol & Usage guide v4.2 section 9.5.3.1) + * + * @author Matthew Simmons + */ +public class AntStartupMessage extends AntMessage { + private byte message; + + public AntStartupMessage(byte[] messageData) { + message = messageData[0]; + } + + /** Returns the cause of the startup */ + public byte getMessage() { + return message; + } +} diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/AntDirectSensorManagerTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/AntDirectSensorManagerTest.java index 22c2f0fa1..ea0e482ae 100644 --- a/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/AntDirectSensorManagerTest.java +++ b/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/AntDirectSensorManagerTest.java @@ -1,12 +1,12 @@ /* * Copyright 2009 Google Inc. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -28,7 +28,7 @@ public class AntDirectSensorManagerTest extends AndroidTestCase { private SharedPreferences sharedPreferences; private AntDirectSensorManager manager; - + public void setUp() { sharedPreferences = getContext().getSharedPreferences( MyTracksSettings.SETTINGS_NAME, 0); @@ -36,14 +36,16 @@ public class AntDirectSensorManagerTest extends AndroidTestCase { sharedPreferences.edit().clear().commit(); manager = new AntDirectSensorManager(getContext()); } - + + @SuppressWarnings("deprecation") @SmallTest public void testBroadcastData() { manager.setDeviceNumberHRM((short) 42); byte[] buff = new byte[11]; + buff[0] = 9; buff[1] = AntMesg.MESG_BROADCAST_DATA_ID; buff[2] = 0; // HRM CHANNEL - buff[10] = (byte) 220; + buff[10] = (byte) 220; manager.handleMessage(buff); Sensor.SensorDataSet sds = manager.getSensorDataSet(); @@ -52,24 +54,16 @@ public class AntDirectSensorManagerTest extends AndroidTestCase { assertEquals(Sensor.SensorState.SENDING, sds.getHeartRate().getState()); assertEquals(220, sds.getHeartRate().getValue()); - + assertFalse(sds.hasCadence()); assertFalse(sds.hasPower()); } - @SmallTest - public void testHandleChannelId() { - assertEquals(0, manager.getDeviceNumberHRM()); - short id = 42; - byte[] buff = new byte[5]; - buff[3] = 42; - manager.handleChannelId(buff); - assertEquals(id, manager.getDeviceNumberHRM()); - } - + @SuppressWarnings("deprecation") @SmallTest public void testChannelId() { byte[] buff = new byte[11]; + buff[0] = 9; buff[1] = AntMesg.MESG_CHANNEL_ID_ID; buff[3] = 42; manager.handleMessage(buff); @@ -81,17 +75,21 @@ public class AntDirectSensorManagerTest extends AndroidTestCase { assertNull(manager.getSensorDataSet()); } + @SuppressWarnings("deprecation") @SmallTest public void testResponseEvent() { assertEquals(Sensor.SensorState.NONE, manager.getSensorState()); - byte[] buff = new byte[11]; + byte[] buff = new byte[5]; + buff[0] = 3; // length buff[1] = AntMesg.MESG_RESPONSE_EVENT_ID; + buff[2] = 0; // channel buff[3] = AntMesg.MESG_UNASSIGN_CHANNEL_ID; + buff[4] = 0; // code manager.handleMessage(buff); assertEquals(Sensor.SensorState.DISCONNECTED, manager.getSensorState()); assertNull(manager.getSensorDataSet()); } - + // TODO: Test timeout too. } diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/AntSensorManagerTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/AntSensorManagerTest.java new file mode 100644 index 000000000..c636d6eae --- /dev/null +++ b/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/AntSensorManagerTest.java @@ -0,0 +1,85 @@ +/* + * Copyright 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.android.apps.mytracks.services.sensors; + +import android.test.AndroidTestCase; +import android.test.MoreAsserts; + +public class AntSensorManagerTest extends AndroidTestCase { + private class TestAntSensorManager extends AntSensorManager { + public byte messageId; + public byte[] messageData; + + public TestAntSensorManager() { + super(null); + } + + @Override + protected void setupAntSensorChannels() {} + + @SuppressWarnings("deprecation") + @Override + public void handleMessage(byte[] rawMessage) { + super.handleMessage(rawMessage); + } + + @Override + public boolean handleMessage(byte messageId, byte[] messageData) { + this.messageId = messageId; + this.messageData = messageData; + return true; + } + } + + private final TestAntSensorManager sensorManager = new TestAntSensorManager(); + + public void testSimple() { + byte[] rawMessage = { + 0x03, // length + 0x12, // message id + 0x11, 0x22, 0x33, // body + }; + + byte[] expectedBody = { 0x11, 0x22, 0x33 }; + + sensorManager.handleMessage(rawMessage); + assertEquals((byte) 0x12, sensorManager.messageId); + MoreAsserts.assertEquals(expectedBody, sensorManager.messageData); + } + + public void testTooShort() { + byte[] rawMessage = { + 0x53, // length + 0x12 // message id + }; + + sensorManager.handleMessage(rawMessage); + assertEquals(0, sensorManager.messageId); + assertNull(sensorManager.messageData); + } + + public void testLengthWrong() { + byte[] rawMessage = { + 0x53, // length + 0x12, // message id + 0x34, // body + }; + + sensorManager.handleMessage(rawMessage); + assertEquals(0, sensorManager.messageId); + assertNull(sensorManager.messageData); + } +} diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/ant/AntChannelIdMessageTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/ant/AntChannelIdMessageTest.java new file mode 100644 index 000000000..82d37fde9 --- /dev/null +++ b/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/ant/AntChannelIdMessageTest.java @@ -0,0 +1,35 @@ +/* + * Copyright 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.android.apps.mytracks.services.sensors.ant; + +import android.test.AndroidTestCase; + +public class AntChannelIdMessageTest extends AndroidTestCase { + public void testParse() { + byte[] rawMessage = { + 0, // channel number + 0x34, 0x12, // device number + (byte) 0xaa, // device type id + (byte) 0xbb, // transmission type + }; + + AntChannelIdMessage message = new AntChannelIdMessage(rawMessage); + assertEquals(0, message.getChannelNumber()); + assertEquals(0x1234, message.getDeviceNumber()); + assertEquals((byte) 0xaa, message.getDeviceTypeId()); + assertEquals((byte) 0xbb, message.getTransmissionType()); + } +} diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/ant/AntChannelResponseMessageTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/ant/AntChannelResponseMessageTest.java new file mode 100644 index 000000000..9fdcfde24 --- /dev/null +++ b/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/ant/AntChannelResponseMessageTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.android.apps.mytracks.services.sensors.ant; + +import com.dsi.ant.AntDefine; +import com.dsi.ant.AntMesg; + +import android.test.AndroidTestCase; + +public class AntChannelResponseMessageTest extends AndroidTestCase { + public void testParse() { + byte[] rawMessage = { + 0, + AntMesg.MESG_EVENT_ID, + AntDefine.EVENT_RX_SEARCH_TIMEOUT + }; + AntChannelResponseMessage message = new AntChannelResponseMessage(rawMessage); + + assertEquals(0, message.getChannelNumber()); + assertEquals(AntMesg.MESG_EVENT_ID, message.getMessageId()); + assertEquals(AntDefine.EVENT_RX_SEARCH_TIMEOUT, message.getMessageCode()); + } +} diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/ant/AntMessageTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/ant/AntMessageTest.java new file mode 100644 index 000000000..4fda67411 --- /dev/null +++ b/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/ant/AntMessageTest.java @@ -0,0 +1,30 @@ +/* + * Copyright 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.android.apps.mytracks.services.sensors.ant; + +import android.test.AndroidTestCase; + +public class AntMessageTest extends AndroidTestCase { + private static class TestAntMessage extends AntMessage { + public static short decodeShort(byte low, byte high) { + return AntMessage.decodeShort(low, high); + } + } + + public void testDecode() { + assertEquals(0x1234, TestAntMessage.decodeShort((byte) 0x34, (byte) 0x12)); + } +} diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/ant/AntStartupMessageTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/ant/AntStartupMessageTest.java new file mode 100644 index 000000000..5fc0c53e3 --- /dev/null +++ b/MyTracksTest/src/com/google/android/apps/mytracks/services/sensors/ant/AntStartupMessageTest.java @@ -0,0 +1,29 @@ +/* + * Copyright 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.android.apps.mytracks.services.sensors.ant; + +import android.test.AndroidTestCase; + +public class AntStartupMessageTest extends AndroidTestCase { + public void testParse() { + byte[] rawMessage = { + 0x12, + }; + + AntStartupMessage message = new AntStartupMessage(rawMessage); + assertEquals(0x12, message.getMessage()); + } +}