/// <summary> /// Called when an intent is received from "StartService" requesting some action. /// The command is put onto the message queue. /// </summary> /// <param name="intent">The received intent</param> /// <param name="flags">not used</param> /// <param name="startId">not used</param> /// <returns></returns> public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId) { string[] extras = new string[] { BundleConst.bundleCmdTag, BundleConst.bundleAddrTag, BundleConst.bundleValTag }; AppLog.Log("RCS-StartCommandResult"); if (intent == null) { AppLog.Log("RCS-StartCommandResult - Intent is null"); return(StartCommandResult.Sticky);; } try { Message msg = handler.ObtainMessage(); msg.Arg1 = startId; msg.What = (int)AppConst.MsgPrio.low; foreach (string s in extras) { if (intent.Extras.ContainsKey(s)) { msg.Data.PutString(s, intent.GetStringExtra(s)); } } if (intent.Extras.ContainsKey(BundleConst.bundleDataTag)) { msg.Data.PutByteArray(BundleConst.bundleDataTag, intent.GetByteArrayExtra(BundleConst.bundleDataTag)); } handler.SendMessage(msg); } catch (Throwable e) { AppLog.Log("RCS-StartCommandResult - exception: " + e.ToString()); } return(StartCommandResult.Sticky); }
/// <summary> /// The actual read thread /// </summary> /// <param name="service">Service instance to access public members</param> public void ReadProc(object service) { int byteRead; // We read one byte at a time bool twoByteFlag = false; // Set if we've read the first byte of a two-byte sequence int secondByte = 0; // The second byte of the sequence (may denoted device type) Message msg; serviceContext = (RemoteCommunicationService)service; try { while ((byteRead = inputStream.ReadByte()) >= 0) { AppLog.Log("RCS-ReadProc; reading: " + byteRead.ToString()); switch (serviceContext.serviceState) { case AppConst.ActivityState.idle: // We shouldn't actually get here as the read thread should only be started in connected state serviceContext.lastReport = 0; twoByteFlag = false; break; case AppConst.ActivityState.connected: if (twoByteFlag) // We are connected, but the handshake is in progress. Check if we're reading the second byte of a sequence { secondByte = byteRead; twoByteFlag = false; if (secondByte < 128) { UpdateState(AppConst.ActivityState.synced); serviceContext.lastReport = 0; } break; } if (byteRead < 127) // As soon as we have a byte with the top bit clear, the handshake is complete (may occur immediately on reconnecting to device) { UpdateState(AppConst.ActivityState.synced); serviceContext.lastReport = 0; twoByteFlag = false; } else if (byteRead == 128) // If we've received a byte with the top bit set, send "key up" message to handshake the recorder { twoByteFlag = true; msg = serviceContext.handler.ObtainMessage(); msg.What = (int)AppConst.MsgPrio.low; msg.Data.PutString(BundleConst.bundleCmdTag, BundleConst.cmdSend); msg.Data.PutByteArray(BundleConst.bundleDataTag, promptSequence); serviceContext.handler.SendMessage(msg); } break; case AppConst.ActivityState.synced: // If top bit set, the handshake has been lost (eg bluetooth device has been unplugged from recorder) if (byteRead > 127) { UpdateState(AppConst.ActivityState.connected); twoByteFlag = true; } else { twoByteFlag = false; if (serviceContext.lastReport != byteRead) { SendData(byteRead); } serviceContext.lastReport = byteRead; } break; } } } // Most likely called when the other thread closes the bluetooth socket on disconnecting catch (Throwable e) { AppLog.Log("RCS-ReadProc; exception: " + e.ToString()); try { inputStream.Close(); } catch (Throwable) { AppLog.Log("RCS-ReadProc; stream close exception: " + e.ToString()); } } }
/// <summary> /// Process a message from the queue (CONNECT, DISCONNECT, SEND or STOP) /// </summary> /// <param name="msg">The message being delivered by the Looper</param> public override void HandleMessage(Message msg) { string command = msg.Data.GetString(BundleConst.bundleCmdTag); Intent statusIntent; bool done = true; if (!done) { return; } AppLog.Log("RCS-HandleMessage: " + command); try { switch (command) { case BundleConst.cmdConnect: // Connection request if (serviceContext.serviceState != AppConst.ActivityState.idle && serviceContext.serviceState != AppConst.ActivityState.error) { // If we're already connected, prod the UI with the last reported status serviceContext.serviceState = AppConst.ActivityState.connected; outputStream.Write(promptSequence, 0, promptSequence.Length); // Try to elicit a response in case we were previously connected. SendData(serviceContext.lastReport); return; } try { string deviceAddr = msg.Data.GetString(BundleConst.bundleAddrTag); // Get the Bluetooth device address btDevice = BluetoothAdapter.DefaultAdapter.GetRemoteDevice(deviceAddr); btSocket = btDevice.CreateInsecureRfcommSocketToServiceRecord(AppConst.uuid); btSocket.Connect(); // Connect the socket AppLog.Log("RCS-HandleMessage: " + command + "; connected device " + deviceAddr); inputStream = Stream.Synchronized(btSocket.InputStream); outputStream = Stream.Synchronized(btSocket.OutputStream); serviceContext.serviceState = AppConst.ActivityState.connected; readThreadObject = new ReadThread(inputStream); readThread = new System.Threading.Thread(readThreadObject.ReadProc); // Start the read thread readThread.Start(serviceContext); outputStream.Write(promptSequence, 0, promptSequence.Length); // Try to elicit a response in case we were previously connected. AppLog.Log("RCS-HandleMessage: " + command + "; created threads"); } catch (Throwable e) { try { if (btSocket != null) { btSocket.Close(); } } catch { } AppLog.Log("RCS-HandleMessage: " + command + "; exception: " + e.ToString()); done = false; } statusIntent = new Intent(AppConst.rxIntentName); // Send the status to the activity statusIntent.PutExtra(BundleConst.bundleCmdTag, BundleConst.bundleStatusTag); if (done) { serviceContext.serviceState = AppConst.ActivityState.connected; } else { serviceContext.serviceState = AppConst.ActivityState.error; } statusIntent.PutExtra(BundleConst.bundleValTag, (int)serviceContext.serviceState); serviceContext.SendBroadcast(statusIntent); if (!done) { CloseConnection(msg.Arg1); } break; case BundleConst.cmdDisconnect: // Disconnect request CloseConnection(msg.Arg1); serviceContext.serviceState = AppConst.ActivityState.idle; statusIntent = new Intent(AppConst.rxIntentName); statusIntent.PutExtra(BundleConst.bundleCmdTag, BundleConst.bundleStatusTag); statusIntent.PutExtra(BundleConst.bundleValTag, (int)serviceContext.serviceState); serviceContext.SendBroadcast(statusIntent); // Update status break; case BundleConst.cmdSend: // Send data if (serviceContext.serviceState == AppConst.ActivityState.connected || serviceContext.serviceState == AppConst.ActivityState.synced) { try { byte[] devCmd; devCmd = msg.Data.GetByteArray(BundleConst.bundleDataTag); outputStream.Write(devCmd, 0, devCmd.Length); AppLog.Log("RCS-HandleMessage: " + command + "; sent: " + devCmd.ToString()); } catch (Throwable e) { AppLog.Log("RCS-HandleMessage: " + command + "; exception: " + e.ToString()); done = false; } } else { done = false; } if (!done) // Update status on error { statusIntent = new Intent(AppConst.rxIntentName); statusIntent.PutExtra(BundleConst.bundleCmdTag, BundleConst.bundleStatusTag); statusIntent.PutExtra(BundleConst.bundleValTag, (int)AppConst.ActivityState.error); serviceContext.SendBroadcast(statusIntent); CloseConnection(msg.Arg1); } break; } } catch (Throwable e) { AppLog.Log("RCS-HandleMessage: " + command + "; exception: " + e.ToString()); } }