forked from upstream-mirrors/OpenTracks
Upgrade ANT support libraries
Rework AntSensorManager (and subclasses) to use asynchronous ANT interfaces Encapsulate message byte arrays with per-message classes Add tests
This commit is contained in:
@@ -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
|
||||
//////////////////////////////////////////////
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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";
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
+58
-44
@@ -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() {
|
||||
|
||||
+19
-13
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+246
-139
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
+56
@@ -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;
|
||||
}
|
||||
}
|
||||
+49
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
+35
@@ -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;
|
||||
}
|
||||
}
|
||||
+17
-19
@@ -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.
|
||||
}
|
||||
|
||||
+85
@@ -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);
|
||||
}
|
||||
}
|
||||
+35
@@ -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());
|
||||
}
|
||||
}
|
||||
+36
@@ -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());
|
||||
}
|
||||
}
|
||||
+30
@@ -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));
|
||||
}
|
||||
}
|
||||
+29
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user