/* * Copyright 2012 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 xyz.hoyer.iconsole; import xyz.hoyer.iconsole.ChannelController.ChannelBroadcastListener; import com.dsi.ant.AntService; import com.dsi.ant.channel.AntChannel; import com.dsi.ant.channel.AntChannelProvider; import com.dsi.ant.channel.ChannelNotAvailableException; import com.dsi.ant.channel.PredefinedNetwork; import android.app.Service; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.util.SparseArray; import java.util.ArrayList; public class ChannelService extends Service { private static final String TAG = "ChannelService"; private Object mCreateChannel_LOCK = new Object(); SparseArray mChannelControllerList = new SparseArray(); ChannelChangedListener mListener; int channelDeviceIdCounter = 0; private boolean mAntRadioServiceBound; private AntService mAntRadioService = null; private AntChannelProvider mAntChannelProvider = null; private boolean mAllowAddChannel = false; private ServiceConnection mAntRadioServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { // Must pass in the received IBinder object to correctly construct an AntService object mAntRadioService = new AntService(service); try { // Getting a channel provider in order to acquire channels mAntChannelProvider = mAntRadioService.getChannelProvider(); // Initial check for number of channels available boolean mChannelAvailable = mAntChannelProvider.getNumChannelsAvailable() > 0; // Initial check for if legacy interface is in use. If the // legacy interface is in use, applications can free the ANT // radio by attempting to acquire a channel. boolean legacyInterfaceInUse = mAntChannelProvider.isLegacyInterfaceInUse(); // If there are channels OR legacy interface in use, allow adding channels if(mChannelAvailable || legacyInterfaceInUse) { mAllowAddChannel = true; } else { // If no channels available AND legacy interface is not in use, disallow adding channels mAllowAddChannel = false; } if(mAllowAddChannel) { if(null != mListener) { // Send an event that indicates if adding channels is allowed mListener.onAllowAddChannel(mAllowAddChannel); } } } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { die("Binder Died"); mAntChannelProvider = null; mAntRadioService = null; if(mAllowAddChannel) { mListener.onAllowAddChannel(false); } mAllowAddChannel = false; } }; public interface ChannelChangedListener { /** * Occurs when a Channel's Info has changed (i.e. a newly created * channel, channel has transmitted or received data, or if channel has * been closed. * * @param newInfo The channel's updated info */ void onChannelChanged(ChannelInfo newInfo); /** * Occurs when there is adding a channel is being allowed or disallowed. * * @param addChannelAllowed True if adding channels is allowed. False, otherwise. */ void onAllowAddChannel(boolean addChannelAllowed); } /** * The interface used to communicate with the ChannelService */ public class ChannelServiceComm extends Binder { /** * Sets the listener to be used for channel changed event callbacks. * * @param listener The listener that will receive events */ void setOnChannelChangedListener(ChannelChangedListener listener) { mListener = listener; } /** * Retrieves the current info for all channels currently added. * * @return A list that contains info for all the channels */ ArrayList getCurrentChannelInfoForAllChannels() { ArrayList retList = new ArrayList(); for(int i = 0; i < mChannelControllerList.size(); i++) { ChannelController channel = mChannelControllerList.valueAt(i); retList.add(channel.getCurrentInfo()); } return retList; } /** * Acquires and adds a channel from ANT Radio Service * * @param isMaster True if channel is transmitting, False if channel is receiving * @return The info for the newly acquired and added channel * @throws ChannelNotAvailableException */ ChannelInfo addNewChannel(final boolean isMaster) throws ChannelNotAvailableException { return createNewChannel(isMaster); } /** * Closes all channels currently added. */ void clearAllChannels() { closeAllChannels(); } /** * Queries if adding a channel is allowed. * @return True if adding a channel is allowed. False, otherwise. */ boolean isAddChannelAllowed() { return mAllowAddChannel; } } private void closeAllChannels() { synchronized (mChannelControllerList) { // Closing all channels in the list for(int i = 0; i < mChannelControllerList.size(); i++) { mChannelControllerList.valueAt(i).close(); } mChannelControllerList.clear(); } // Reset the device id counter channelDeviceIdCounter = 0; } AntChannel acquireChannel() throws ChannelNotAvailableException { AntChannel mAntChannel = null; if(null != mAntChannelProvider) { try { /* * If applications require a channel with specific capabilities * (event buffering, background scanning etc.), a Capabilities * object should be created and then the specific capabilities * required set to true. Applications can specify both required * and desired Capabilities with both being passed in * acquireChannel(context, PredefinedNetwork, * requiredCapabilities, desiredCapabilities). */ mAntChannel = mAntChannelProvider.acquireChannel(this, PredefinedNetwork.ANT_PLUS_1); /* NetworkKey mNK = new NetworkKey(new byte[] { (byte)0xb9, (byte)0xa5, (byte)0x21, (byte)0xfb, (byte)0xbd, (byte)0x72, (byte)0xc3, (byte)0x45 }); Log.v(TAG, mNK.toString()); mAntChannel = mAntChannelProvider.acquireChannelOnPrivateNetwork(this, mNK); */ } catch (RemoteException e) { die("ACP Remote Ex"); } } return mAntChannel; } public ChannelInfo createNewChannel(final boolean isMaster) throws ChannelNotAvailableException { ChannelController channelController = null; synchronized(mCreateChannel_LOCK) { // Acquiring a channel from ANT Radio Service AntChannel antChannel = acquireChannel(); if(null != antChannel) { channelDeviceIdCounter += 1; // Constructing a controller that will manage and control the channel channelController = new ChannelController(antChannel, isMaster, channelDeviceIdCounter, new ChannelBroadcastListener() { @Override public void onBroadcastChanged(ChannelInfo newInfo) { // Sending a channel changed event when message from ANT is received mListener.onChannelChanged(newInfo); } }); mChannelControllerList.put(channelDeviceIdCounter, channelController); } } if(null == channelController) return null; return channelController.getCurrentInfo(); } @Override public IBinder onBind(Intent arg0) { return new ChannelServiceComm(); } /** * Receives AntChannelProvider state changes being sent from ANT Radio Service */ private final BroadcastReceiver mChannelProviderStateChangedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if(AntChannelProvider.ACTION_CHANNEL_PROVIDER_STATE_CHANGED.equals(intent.getAction())) { boolean update = false; // Retrieving the data contained in the intent int numChannels = intent.getIntExtra(AntChannelProvider.NUM_CHANNELS_AVAILABLE, 0); boolean legacyInterfaceInUse = intent.getBooleanExtra(AntChannelProvider.LEGACY_INTERFACE_IN_USE, false); if(mAllowAddChannel) { // Was a acquire channel allowed // If no channels available AND legacy interface is not in use, disallow acquiring of channels if(0 == numChannels && !legacyInterfaceInUse) { mAllowAddChannel = false; update = true; } } else { // Acquire channels not allowed // If there are channels OR legacy interface in use, allow acquiring of channels if(numChannels > 0 || legacyInterfaceInUse) { mAllowAddChannel = true; update = true; } } if(update && (null != mListener)) { // AllowAddChannel has been changed, sending event callback mListener.onAllowAddChannel(mAllowAddChannel); } } } }; private void doBindAntRadioService() { if(BuildConfig.DEBUG) Log.v(TAG, "doBindAntRadioService"); // Start listing for channel available intents registerReceiver(mChannelProviderStateChangedReceiver, new IntentFilter(AntChannelProvider.ACTION_CHANNEL_PROVIDER_STATE_CHANGED)); // Creating the intent and calling context.bindService() is handled by // the static bindService() method in AntService mAntRadioServiceBound = AntService.bindService(this, mAntRadioServiceConnection); } private void doUnbindAntRadioService() { if(BuildConfig.DEBUG) Log.v(TAG, "doUnbindAntRadioService"); // Stop listing for channel available intents try{ unregisterReceiver(mChannelProviderStateChangedReceiver); } catch (IllegalArgumentException exception) { if(BuildConfig.DEBUG) Log.d(TAG, "Attempting to unregister a never registered Channel Provider State Changed receiver."); } if(mAntRadioServiceBound) { try { unbindService(mAntRadioServiceConnection); } catch(IllegalArgumentException e) { // Not bound, that's what we want anyway } mAntRadioServiceBound = false; } } @Override public void onCreate() { super.onCreate(); mAntRadioServiceBound = false; doBindAntRadioService(); } @Override public void onDestroy() { closeAllChannels(); doUnbindAntRadioService(); mAntChannelProvider = null; super.onDestroy(); } static void die(String error) { Log.e(TAG, "DIE: "+ error); } }