From 6e0f29b08a040d14576d7053c1206a8439936570 Mon Sep 17 00:00:00 2001
From: yy1717 <fctom1215@outlook.com>
Date: 星期二, 24 十一月 2020 14:18:03 +0800
Subject: [PATCH] 坐标

---
 lib/src/main/AndroidManifest.xml                                            |    5 
 lib/src/main/cpp/driver_test.cpp                                            |   10 
 lib/src/main/cpp/native-lib.h                                               |    2 
 lib/src/main/java/com/anyun/exam/lib/util/BluetoothChatServiceCallback.java |    5 
 lib/src/main/java/com/anyun/exam/lib/util/Constants.java                    |   40 +
 lib/src/main/java/com/anyun/exam/lib/Ota.java                               |   55 +
 lib/src/main/cpp/rtk_module/rtk.cpp                                         |   68 +
 lib/src/main/cpp/test_common/Geometry.cpp                                   |   13 
 lib/src/main/cpp/test_items/park_bottom.cpp                                 |    2 
 lib/src/main/cpp/test_items/park_edge.cpp                                   |    2 
 lib/src/main/cpp/mcu/mcu_if.h                                               |    1 
 lib/src/main/cpp/master/comm_if.cpp                                         |   77 ++
 lib/src/main/cpp/native-lib.cpp                                             |   88 +++
 lib/src/main/cpp/test_items2/through_something.cpp                          |    4 
 lib/src/main/cpp/test_items2/road_exam.cpp                                  |  165 +++++
 lib/src/main/cpp/test_common/Geometry.h                                     |    1 
 lib/src/main/cpp/rtk_platform/platform.cpp                                  |   33 +
 lib/src/main/cpp/test_items/driving_curve.cpp                               |    2 
 lib/src/main/java/com/anyun/exam/lib/util/BluetoothChatService.java         |  540 ++++++++++++++++++
 lib/src/main/cpp/rtk_module/rtk.h                                           |    2 
 lib/src/main/cpp/driver_test.h                                              |    2 
 lib/src/main/cpp/master/comm_if.h                                           |    2 
 lib/src/main/cpp/test_items/turn_a90.cpp                                    |    2 
 lib/src/main/java/com/anyun/exam/lib/util/ClsUtils.java                     |  138 ++++
 lib/src/main/java/com/anyun/exam/lib/util/Bluetooth.java                    |  152 +++++
 lib/src/main/cpp/rtk_platform/platform.h                                    |    2 
 lib/src/main/cpp/mcu/mcu_if.cpp                                             |   85 ++
 lib/src/main/cpp/rtk_module/virtual_rtk.cpp                                 |   48 +
 lib/src/main/cpp/rtk_module/virtual_rtk.h                                   |    1 
 lib/src/main/java/com/anyun/exam/lib/RemoteService.java                     |  165 +++++
 30 files changed, 1,642 insertions(+), 70 deletions(-)

diff --git a/lib/src/main/AndroidManifest.xml b/lib/src/main/AndroidManifest.xml
index 270fa4d..4041f1c 100644
--- a/lib/src/main/AndroidManifest.xml
+++ b/lib/src/main/AndroidManifest.xml
@@ -1,6 +1,11 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.anyun.exam.lib" >
 
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+
     <application>
         <service android:name=".RemoteService"  android:process=":reomte" android:exported="true"/>
     </application>
diff --git a/lib/src/main/cpp/driver_test.cpp b/lib/src/main/cpp/driver_test.cpp
index 2e08710..b90ea78 100644
--- a/lib/src/main/cpp/driver_test.cpp
+++ b/lib/src/main/cpp/driver_test.cpp
@@ -1133,6 +1133,16 @@
     MA_SendCrossingStatus(&brief2);
 }
 
+/***************************************************************
+ * 杞﹂�熷垽鏂渶瑕�1绉掑墠鍚庣殑瀵规瘮锛屽疄娴嬪仠杞︾‘璁ら渶瑕�1.5绉掑乏鍙筹紝鍥哄噺鍘昏繖涓椂闂�
+ * @param src
+ * @return
+ */
+int CorrectPauseCriteria(int src)
+{
+    return (src > 1500) ? src - 1500 : 0;
+}
+
 /*******************************************************************
  * @brief 鐢变富澶╃嚎鍧愭爣璁$畻杞﹁韩鐐瑰潗鏍�
  * @param azimuth
diff --git a/lib/src/main/cpp/driver_test.h b/lib/src/main/cpp/driver_test.h
index 11d5134..a4e609e 100644
--- a/lib/src/main/cpp/driver_test.h
+++ b/lib/src/main/cpp/driver_test.h
@@ -323,4 +323,6 @@
 
 void MasterInqRoadStatus(void);
 
+int CorrectPauseCriteria(int src);
+
 #endif //RTKDRIVERTEST_DRIVER_TEST_H
diff --git a/lib/src/main/cpp/master/comm_if.cpp b/lib/src/main/cpp/master/comm_if.cpp
index 3c01860..4ea6f8b 100644
--- a/lib/src/main/cpp/master/comm_if.cpp
+++ b/lib/src/main/cpp/master/comm_if.cpp
@@ -41,6 +41,7 @@
 #define ID_MS_ROAD_MAP2         0x8013
 #define ID_MS_SCHEME            0x8017
 #define ID_MS_EXAM_PARAM        0x8019
+#define ID_MS_BLUETOOTH_NAME    0x8020
 
 #define ID_SM_READ_CAR          0x0007
 #define ID_MS_CAR               0x8007
@@ -71,6 +72,7 @@
 #define ID_MS_INQ_ROAD_CROSSING 0x8018              // Master鏌ヨ褰撳墠璺鍜岃矾鍙�
 
 #define ID_SM_CAN_BRIEF         0x0021
+#define ID_SM_BLUETOOTH_BRIEF   0x0022
 
 #define MA_OUT_GPS_BRIEF        0x0001
 #define MA_OUT_RTK_BRIEF        0x0002
@@ -1290,6 +1292,9 @@
         case ID_MS_SCHEME: {
             Document doc;
             doc.Parse(value);
+
+            DEBUG("鏂规 %s", value);
+
             if (!doc.HasParseError()) {
                 DEBUG("寮�濮嬭В鏋愯矾鑰冩柟妗�");
                 vector<scheme_t> schemes;
@@ -1379,87 +1384,104 @@
                 if (doc.HasMember("hold_start_key_limit_time")) {
                     const Value &a = doc["hold_start_key_limit_time"];
                     examParam.hold_start_key_limit_time = a.GetInt();
+                    DEBUG("hold_start_key_limit_time = %d", examParam.hold_start_key_limit_time);
                 }
 
                 if (doc.HasMember("curve_pause_criteria")) {
                     const Value &a = doc["curve_pause_criteria"];
                     examParam.curve_pause_criteria = a.GetInt();
+                    DEBUG("curve_pause_criteria = %d", examParam.curve_pause_criteria);
                 }
 
                 if (doc.HasMember("park_bottom_pause_criteria")) {
                     const Value &a = doc["park_bottom_pause_criteria"];
                     examParam.park_bottom_pause_criteria = a.GetInt();
+                    DEBUG("park_bottom_pause_criteria = %d", examParam.park_bottom_pause_criteria);
                 }
 
                 if (doc.HasMember("park_bottom_limit_time")) {
                     const Value &a = doc["park_bottom_limit_time"];
                     examParam.park_bottom_limit_time = D_SEC(a.GetInt());
+                    DEBUG("park_bottom_limit_time = %d", examParam.park_bottom_limit_time);
                 }
 
                 if (doc.HasMember("park_edge_pause_criteria")) {
                     const Value &a = doc["park_edge_pause_criteria"];
                     examParam.park_edge_pause_criteria = a.GetInt();
+                    DEBUG("park_edge_pause_criteria = %d", examParam.park_edge_pause_criteria);
                 }
 
                 if (doc.HasMember("park_edge_limit_time")) {
                     const Value &a = doc["park_edge_limit_time"];
                     examParam.park_edge_limit_time = D_SEC(a.GetInt());
+                    DEBUG("park_edge_limit_time = %d", examParam.park_edge_limit_time);
                 }
 
                 if (doc.HasMember("turn_a90_pause_criteria")) {
                     const Value &a = doc["turn_a90_pause_criteria"];
                     examParam.turn_a90_pause_criteria = a.GetInt();
+                    DEBUG("turn_a90_pause_criteria = %d", examParam.turn_a90_pause_criteria);
                 }
 
                 if (doc.HasMember("ramp_stoppoint_red_distance")) {
                     const Value &a = doc["ramp_stoppoint_red_distance"];
                     examParam.ramp_stoppoint_red_distance = a.GetDouble();
+                    DEBUG("ramp_stoppoint_red_distance = %f", examParam.ramp_stoppoint_red_distance);
                 }
 
                 if (doc.HasMember("ramp_edge_yellow_distance")) {
                     const Value &a = doc["ramp_edge_yellow_distance"];
                     examParam.ramp_edge_yellow_distance = a.GetDouble();
+                    DEBUG("ramp_edge_yellow_distance = %f", examParam.ramp_edge_yellow_distance);
                 }
 
                 if (doc.HasMember("ramp_edge_red_distance")) {
                     const Value &a = doc["ramp_edge_red_distance"];
                     examParam.ramp_edge_red_distance = a.GetDouble();
+                    DEBUG("ramp_edge_red_distance = %f", examParam.ramp_edge_red_distance);
                 }
 
                 if (doc.HasMember("ramp_slide_yellow_distance")) {
                     const Value &a = doc["ramp_slide_yellow_distance"];
                     examParam.ramp_slide_yellow_distance = a.GetDouble();
+                    DEBUG("ramp_slide_yellow_distance = %f", examParam.ramp_slide_yellow_distance);
                 }
 
                 if (doc.HasMember("ramp_slide_red_distance")) {
                     const Value &a = doc["ramp_slide_red_distance"];
                     examParam.ramp_slide_red_distance = a.GetDouble();
+                    DEBUG("ramp_slide_red_distance = %f", examParam.ramp_slide_red_distance);
                 }
 
                 if (doc.HasMember("ramp_start_car_limit_time")) {
                     const Value &a = doc["ramp_start_car_limit_time"];
                     examParam.ramp_start_car_limit_time = D_SEC(a.GetInt());
+                    DEBUG("ramp_start_car_limit_time = %d", examParam.ramp_start_car_limit_time);
                 }
 
                 if (doc.HasMember("road_slide_yellow_distance")) {
                     const Value &a = doc["road_slide_yellow_distance"];
                     examParam.road_slide_yellow_distance = a.GetDouble();
+                    DEBUG("road_slide_yellow_distance = %f", examParam.road_slide_yellow_distance);
                 }
 
                 double road_slide_red_distance;
                 if (doc.HasMember("road_slide_red_distance")) {
                     const Value &a = doc["road_slide_red_distance"];
                     examParam.road_slide_red_distance = a.GetDouble();
+                    DEBUG("road_slide_red_distance = %f", examParam.road_slide_red_distance);
                 }
 
                 if (doc.HasMember("road_total_distance")) {
                     const Value &a = doc["road_total_distance"];
-                    examParam.road_total_distance = a.GetDouble();
+                    examParam.road_total_distance = a.GetInt();
+                    DEBUG("road_total_distance = %d", examParam.road_total_distance);
                 }
 
                 if (doc.HasMember("road_max_speed")) {
                     const Value &a = doc["road_max_speed"];
                     examParam.road_max_speed = a.GetInt();
+                    DEBUG("road_max_speed = %d", examParam.road_max_speed);
                 }
 
                 if (doc.HasMember("gear_speed_table")) {
@@ -1467,12 +1489,17 @@
 
                     int m = 0, n = 0;
 
-                    for (Value::ConstValueIterator itr = a.Begin();
-                         itr != a.End() && m < 6; ++itr, ++m) {
-                        n = 0;
-                        for (Value::ConstValueIterator itr2 = (*itr).Begin();
-                             itr2 != (*itr).End() && n < 2; ++itr2, ++n) {
-                            examParam.gear_speed_table[m][n] = (*itr2).GetInt();
+                    if (a.IsArray()) {
+                        for (Value::ConstValueIterator itr = a.Begin();
+                             itr != a.End() && m < 6; ++itr, ++m) {
+                            n = 0;
+                            if ((*itr).IsArray()) {
+                                for (Value::ConstValueIterator itr2 = (*itr).Begin();
+                                     itr2 != (*itr).End() && n < 2; ++itr2, ++n) {
+                                    examParam.gear_speed_table[m][n] = (*itr2).GetInt();
+                                    DEBUG("gear_speed_table[%d][%d] = %d", m, n, examParam.gear_speed_table[m][n]);
+                                }
+                            }
                         }
                     }
                 }
@@ -1480,118 +1507,141 @@
                 if (doc.HasMember("gear_n_allow_time")) {
                     const Value &a = doc["gear_n_allow_time"];
                     examParam.gear_n_allow_time = D_SEC(a.GetInt());
+                    DEBUG("gear_n_allow_time = %d", examParam.gear_n_allow_time);
                 }
 
 
                 if (doc.HasMember("same_gear_min_time")) {
                     const Value &a = doc["same_gear_min_time"];
                     examParam.same_gear_min_time = D_SEC(a.GetInt());
+                    DEBUG("same_gear_min_time = %d", examParam.same_gear_min_time);
                 }
 
                 if (doc.HasMember("gear_speed_error_cumulative_time")) {
                     const Value &a = doc["gear_speed_error_cumulative_time"];
                     examParam.gear_speed_error_cumulative_time = D_SEC(a.GetInt());
+                    DEBUG("gear_speed_error_cumulative_time = %d", examParam.gear_speed_error_cumulative_time);
                 }
 
                 if (doc.HasMember("road_pause_criteria")) {
                     const Value &a = doc["road_pause_criteria"];
                     examParam.road_pause_criteria = a.GetInt();
+                    DEBUG("road_pause_criteria = %d", examParam.road_pause_criteria);
                 }
 
                 if (doc.HasMember("continuous_change_lane_min_time")) {
                     const Value &a = doc["continuous_change_lane_min_time"];
                     examParam.continuous_change_lane_min_time = D_SEC(a.GetInt());
+                    DEBUG("continuous_change_lane_min_time = %d", examParam.continuous_change_lane_min_time);
                 }
 
                 if (doc.HasMember("crash_dotted_line_cumulative_time")) {
                     const Value &a = doc["crash_dotted_line_cumulative_time"];
                     examParam.crash_dotted_line_cumulative_time = D_SEC(a.GetInt());
+                    DEBUG("crash_dotted_line_cumulative_time = %d", examParam.crash_dotted_line_cumulative_time);
                 }
 
                 if (doc.HasMember("turn_signal_min_advance")) {
                     const Value &a = doc["turn_signal_min_advance"];
                     examParam.turn_signal_min_advance = D_SEC(a.GetInt());
+                    DEBUG("turn_signal_min_advance = %d", examParam.turn_signal_min_advance);
                 }
 
                 if (doc.HasMember("start_car_max_rpm")) {
                     const Value &a = doc["start_car_max_rpm"];
                     examParam.start_car_max_rpm = a.GetInt();
+                    DEBUG("start_car_max_rpm = %d", examParam.start_car_max_rpm);
                 }
 
                 if (doc.HasMember("start_car_limit_distance")) {
                     const Value &a = doc["start_car_limit_distance"];
                     examParam.start_car_limit_distance = a.GetInt();
+                    DEBUG("start_car_limit_distance = %d", examParam.start_car_limit_distance);
                 }
 
                 if (doc.HasMember("open_door_drive_allow_distance")) {
                     const Value &a = doc["open_door_drive_allow_distance"];
                     examParam.open_door_drive_allow_distance = a.GetDouble();
+                    DEBUG("open_door_drive_allow_distance = %f", examParam.open_door_drive_allow_distance);
                 }
 
                 if (doc.HasMember("change_lane_limit_distance")) {
                     const Value &a = doc["change_lane_limit_distance"];
                     examParam.change_lane_limit_distance = a.GetInt();
+                    DEBUG("change_lane_limit_distance = %d", examParam.change_lane_limit_distance);
                 }
 
                 if (doc.HasMember("shift_limit_distance")) {
                     const Value &a = doc["shift_limit_distance"];
                     examParam.shift_limit_distance = a.GetInt();
+                    DEBUG("shift_limit_distance = %d", examParam.shift_limit_distance);
                 }
 
                 if (doc.HasMember("shift_hold_time")) {
                     const Value &a = doc["shift_hold_time"];
                     examParam.shift_hold_time = D_SEC(a.GetInt());
+                    DEBUG("shift_hold_time = %d", examParam.shift_hold_time);
                 }
 
                 int straight_limit_distance;
                 if (doc.HasMember("straight_limit_distance")) {
                     const Value &a = doc["straight_limit_distance"];
                     examParam.straight_limit_distance = a.GetInt();
+                    DEBUG("straight_limit_distance = %d", examParam.straight_limit_distance);
                 }
 
                 if (doc.HasMember("straight_max_offset")) {
                     const Value &a = doc["straight_max_offset"];
                     examParam.straight_max_offset = a.GetDouble();
+                    DEBUG("straight_max_offset = %f", examParam.straight_max_offset);
                 }
 
                 if (doc.HasMember("overtake_limit_distance")) {
                     const Value &a = doc["overtake_limit_distance"];
                     examParam.overtake_limit_distance = a.GetInt();
+                    DEBUG("overtake_limit_distance = %d", examParam.overtake_limit_distance);
                 }
 
                 if (doc.HasMember("stop_car_limit_distance")) {
                     const Value &a = doc["stop_car_limit_distance"];
                     examParam.stop_car_limit_distance = a.GetInt();
+                    DEBUG("stop_car_limit_distance = %d", examParam.stop_car_limit_distance);
                 }
 
                 if (doc.HasMember("stop_car_open_door_allow_time")) {
                     const Value &a = doc["stop_car_open_door_allow_time"];
                     examParam.stop_car_open_door_allow_time = D_SEC(a.GetInt());
+                    DEBUG("stop_car_open_door_allow_time = %d", examParam.stop_car_open_door_allow_time);
                 }
 
                 if (doc.HasMember("stop_car_edge_red_distance")) {
                     const Value &a = doc["stop_car_edge_red_distance"];
                     examParam.stop_car_edge_red_distance = a.GetDouble();
+                    DEBUG("stop_car_edge_red_distance = %f", examParam.stop_car_edge_red_distance);
                 }
 
                 if (doc.HasMember("stop_car_edge_yellow_distance")) {
                     const Value &a = doc["stop_car_edge_yellow_distance"];
                     examParam.stop_car_edge_yellow_distance = a.GetDouble();
+                    DEBUG("stop_car_edge_yellow_distance = %f", examParam.stop_car_edge_yellow_distance);
                 }
 
                 if (doc.HasMember("crossing_stop_valid_distance")) {
                     const Value &a = doc["crossing_stop_valid_distance"];
                     examParam.crossing_stop_valid_distance = a.GetDouble();
+                    DEBUG("crossing_stop_valid_distance = %f", examParam.crossing_stop_valid_distance);
                 }
 
                 if (doc.HasMember("cross_school_max_speed")) {
                     const Value &a = doc["cross_school_max_speed"];
                     examParam.cross_school_max_speed = a.GetInt();
+                    DEBUG("cross_school_max_speed = %d", examParam.cross_school_max_speed);
                 }
 
                 if (doc.HasMember("crossing_break_valid_distance")) {
                     const Value &a = doc["crossing_break_valid_distance"];
                     examParam.crossing_break_valid_distance = a.GetInt();
+                    DEBUG("crossing_break_valid_distance = %d", examParam.crossing_break_valid_distance);
                 }
             }
             break;
@@ -1975,3 +2025,16 @@
 {
     SendMsgToMainProcIndep(ID_SM_LIGHT_EXAM_REQ, NULL);
 }
+
+void MA_SendBlueStatus(int status)
+{
+    StringBuffer sb;
+    Writer<StringBuffer> writer(sb);
+
+    writer.StartObject();
+    writer.Key("bluetooth_status");
+    writer.Int(status);
+    writer.EndObject();
+
+    SendMsgToMainProcIndep(ID_SM_BLUETOOTH_BRIEF, sb.GetString());
+}
diff --git a/lib/src/main/cpp/master/comm_if.h b/lib/src/main/cpp/master/comm_if.h
index 9eacc38..1b82f52 100644
--- a/lib/src/main/cpp/master/comm_if.h
+++ b/lib/src/main/cpp/master/comm_if.h
@@ -153,4 +153,6 @@
 void MA_SendCrossingStatus(const struct crossingStatusBrief *brief);
 void MA_SendCanStatus(const struct canBrief *brief);
 
+void MA_SendBlueStatus(int status);
+
 #endif //MYAPPLICATION2_COMM_IF_H
diff --git a/lib/src/main/cpp/mcu/mcu_if.cpp b/lib/src/main/cpp/mcu/mcu_if.cpp
index 492cd4d..080f233 100644
--- a/lib/src/main/cpp/mcu/mcu_if.cpp
+++ b/lib/src/main/cpp/mcu/mcu_if.cpp
@@ -12,6 +12,8 @@
 #include "../jni_log.h"
 #include "../common/serial_port.h"
 #include "../rtk_platform/platform.h"
+#include "../rtk_module/parse_gps.h"
+#include "../native-lib.h"
 
 #define DEBUG(fmt, args...)     LOGD("<mcu_if> <%s>: " fmt, __func__, ##args)
 
@@ -46,6 +48,8 @@
 #define ID_CM_READ_RFCARD         0x0009
 #define ID_MC_RFCARD_RSP        0x8009
 #define ID_CM_SHUTDOWN          0x0020
+#define ID_MC_GNSS_DATA         0x800A
+#define ID_CM_SINAN_CMD_DATA    0x000B
 
 static parse_status_t parse_status;
 
@@ -65,15 +69,27 @@
 const int DFU_MAX_TRY = 3;
 const int DFU_FILE_BLOCK_SIZE = 896;
 
+static int (*WriteMcu)(int id, const void *buf, int len);
+
+static int WriteBluetooth(int id, const void *buf, int len);
 
 static void *UartThread1(void *p);
 static void ParseMcuTimeout(union sigval sig);
-static void McuCommandEntry(uint16_t id, const uint8_t *data, int lenth);
+static void McuCommandEntry(uint16_t id, const uint8_t *data, int length);
 
 static void SendDfuFile(int fileLen, int sentLen, int blockLen, const uint8_t *data);
 static void GoNextDfuLater(union sigval sig);
 static void GoNextDfu(void);
 static void ReadCardTimeout(union sigval sig);
+
+void McuCommModeSel(int mode)
+{
+    if (mode == 0) {
+        WriteMcu = WriteSerialPort;
+    } else {
+        WriteMcu = WriteBluetooth;
+    }
+}
 
 void ParseMcuInit(void)
 {
@@ -83,8 +99,15 @@
     parse_status = SYNC_HEAD_ONE;
     AppTimer_delete(ParseMcuTimeout);
 
-    SendMcuCommand(ID_CM_APP_BOOT, NULL, 0);
+//    SendMcuCommand(ID_CM_APP_BOOT, NULL, 0);
 }
+
+static int WriteBluetooth(int id, const void *buf, int len)
+{
+    SendToBluetooth((uint8_t *)buf, len);
+    return len;
+}
+
 
 #define PARSE_BUFF_SIZE         4096
 
@@ -251,13 +274,15 @@
     buffer[x++] = HI_UINT16(crc16);
     buffer[x++] = LO_UINT16(crc16);
 
-    WriteSerialPort(MCU_UART, buffer, x);
+    WriteMcu(MCU_UART, buffer, x);
 }
 
 void ConfigMCU(void)
 {
+    McuCommModeSel(1);
+
     // TODO
-    static struct serial_config serialConfig;
+  /*  static struct serial_config serialConfig;
 
     strcpy(serialConfig.name, "/dev/ttyHSL1");
     serialConfig.baud = 115200;
@@ -270,7 +295,7 @@
     pthread_attr_t attr;
     pthread_attr_init(&attr);
     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//detached
-    pthread_create(&pid, &attr, UartThread1, &serialConfig);
+    pthread_create(&pid, &attr, UartThread1, &serialConfig);*/
 }
 
 void SendRtkToMcu(const uint8_t *data, int length)
@@ -296,20 +321,23 @@
     AppTimer_add(sendrtk, D_SEC(1));
 }
 
-static void McuCommandEntry(uint16_t id, const uint8_t *data, int lenth)
+static uint8_t GnssBuf[PARSE_BUFF_SIZE];
+static int GnssBufLen = 0;
+
+static void McuCommandEntry(uint16_t id, const uint8_t *data, int length)
 {
     static int ii = 0;
 
     switch (id) {
         case ID_MC_MCU_BOOT:
             DEBUG("MCU BOOT");
-            if (lenth == 8) {
-                PlatformStatusChanged(CAN_UPDATE_EVT, data, lenth);
+            if (length == 8) {
+                PlatformStatusChanged(CAN_UPDATE_EVT, data, length);
             }
 
             break;
         case ID_MC_DFU_RSP:
-            DEBUG("ID_MC_DFU_RSP %d len %d", data[0], lenth);
+            DEBUG("ID_MC_DFU_RSP %d len %d", data[0], length);
 
             if (data[0] == 0) {
                 // 绗竴鍖呬紶杈撴垚鍔�
@@ -329,12 +357,12 @@
 
                 int total = dfuFileLength / DFU_FILE_BLOCK_SIZE + ((dfuFileLength % DFU_FILE_BLOCK_SIZE)?1:0);
                 int a = 0, b = 0;
-                for (int i = 1; i < lenth; ++i) {
+                for (int i = 1; i < length; ++i) {
                     for (int j = 0; j < 8; ++j) {
                         if ((data[i] & BV(j))) b++;
                         a++;
                         if (a == total) {
-                            i = lenth;
+                            i = length;
                             break;
                         }
                     }
@@ -343,7 +371,7 @@
                 DEBUG("BITMAP total %d succ %d", total, b);
 
                 //memset(dfuFileBitmap, 0, sizeof(dfuFileBitmap));
-                memcpy(dfuFileBitmap, data + 1, lenth - 1);
+                memcpy(dfuFileBitmap, data + 1, length - 1);
 
                 if (total % 8) {
                     dfuFileBitmap[total/8] &= ~BV((total%8) - 1);
@@ -362,16 +390,16 @@
         case ID_MC_MCU_DFU_RSP:
             break;
         case ID_MC_CAR_INFO2:
-//            DEBUG("ID_MC_CAR_INFO2 %d", lenth);
-            if (lenth > 0) {
-                PlatformStatusChanged(CAR_SENSOR_UPDATE_EVT, data, lenth);
+//            DEBUG("ID_MC_CAR_INFO2 %d", length);
+            if (length > 0) {
+                PlatformStatusChanged(CAR_SENSOR_UPDATE_EVT, data, length);
             }
             break;
         case ID_MC_CAR_INFO: {
-//            DEBUG("ID_MC_CAR_INFO %d", lenth);
+//            DEBUG("ID_MC_CAR_INFO %d", length);
 
-            if (lenth > 0)
-                PlatformStatusChanged(MCU_UPDATE_EVT, data, lenth);
+            if (length > 0)
+                PlatformStatusChanged(MCU_UPDATE_EVT, data, length);
             break;
         }
         case ID_MC_RTK_DATA:
@@ -381,9 +409,26 @@
             DEBUG("ID_MC_RFCARD_RSP");
             AppTimer_delete(ReadCardTimeout);
 
-            if (lenth > 0)
-                PlatformStatusChanged(CARD_UPDATE_EVT, data, lenth);
+            if (length > 0)
+                PlatformStatusChanged(CARD_UPDATE_EVT, data, length);
             break;
+        case ID_MC_GNSS_DATA: {
+            length = (length > PARSE_BUFF_SIZE - GnssBufLen) ? (PARSE_BUFF_SIZE - GnssBufLen) : length;
+
+            memcpy(GnssBuf + GnssBufLen, data, length);
+            GnssBufLen += length;
+            if (GnssBufLen > 0) {
+                const uint8_t *ptr = parseGPS(GnssBuf, GnssBuf + GnssBufLen);
+                if (ptr != GnssBuf) {
+                    memcpy(GnssBuf, ptr, GnssBufLen - (ptr - GnssBuf));
+                    GnssBufLen -= ptr - GnssBuf;
+                } else if (GnssBufLen == PARSE_BUFF_SIZE) {        //濉弧浜嗭紝涓旀病鏈変竴涓猏r锛岄兘鎶涘純
+                    DEBUG("Parse GPS error");
+                    GnssBufLen = 0;
+                }
+            }
+            break;
+        }
         default:
             break;
     }
diff --git a/lib/src/main/cpp/mcu/mcu_if.h b/lib/src/main/cpp/mcu/mcu_if.h
index 86613ca..7f451b1 100644
--- a/lib/src/main/cpp/mcu/mcu_if.h
+++ b/lib/src/main/cpp/mcu/mcu_if.h
@@ -7,6 +7,7 @@
 
 #include <cstdint>
 
+void McuCommModeSel(int mode);
 void ConfigMCU(void);
 void SendRtkToMcu(const uint8_t *data, int length);
 void ParseMcuInit(void);
diff --git a/lib/src/main/cpp/native-lib.cpp b/lib/src/main/cpp/native-lib.cpp
index 2eb0fc9..0c9d356 100644
--- a/lib/src/main/cpp/native-lib.cpp
+++ b/lib/src/main/cpp/native-lib.cpp
@@ -286,6 +286,72 @@
         }
     }
 }
+
+void SendToBluetooth(const uint8_t *data, int length)
+{
+    JNIEnv *env;
+    bool ready_in_java_env = false;
+
+    if (sg_jvm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK) {
+        // Attach涓荤嚎绋�
+        if (sg_jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {
+            LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
+            return;
+        }
+    } else {
+        ready_in_java_env = true;
+    }
+
+    jclass cls = env->GetObjectClass(sg_obj);
+    jmethodID fun = env->GetMethodID(cls, "WriteBluetooth", "([B)V");
+
+    jbyteArray array = env->NewByteArray(length);
+
+    env->SetByteArrayRegion(array, 0, length, (jbyte *) data);
+
+    env->CallVoidMethod(sg_obj, fun, array);
+
+    env->DeleteLocalRef(array);
+    env->DeleteLocalRef(cls);
+
+    if (!ready_in_java_env) {
+        //Detach涓荤嚎绋�
+        if (sg_jvm->DetachCurrentThread() != JNI_OK) {
+            LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
+        }
+    }
+}
+
+void ConnectToBluetooth(const char *addr, const char *pin)
+{
+    JNIEnv *env;
+    bool ready_in_java_env = false;
+
+    if (sg_jvm->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK) {
+        // Attach涓荤嚎绋�
+        if (sg_jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {
+            LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
+            return;
+        }
+    } else {
+        ready_in_java_env = true;
+    }
+
+    jclass cls = env->GetObjectClass(sg_obj);
+    jmethodID fun = env->GetMethodID(cls, "ConnectBluetooth", "(Ljava/lang/String;Ljava/lang/String;)V");
+
+    env->CallVoidMethod(sg_obj, fun, env->NewStringUTF(addr), env->NewStringUTF(pin));
+
+    env->DeleteLocalRef(cls);
+
+    if (!ready_in_java_env) {
+        //Detach涓荤嚎绋�
+        if (sg_jvm->DetachCurrentThread() != JNI_OK) {
+            LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
+        }
+    }
+}
+
 extern "C"
 JNIEXPORT void JNICALL
 Java_com_anyun_exam_lib_RemoteService_startNative(JNIEnv *env, jobject thiz) {
@@ -405,3 +471,25 @@
         env->ReleaseByteArrayElements(rom, c_dat, NULL);
     }
 }
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_anyun_exam_lib_RemoteService_BluetoothDataComeIn(JNIEnv *env, jobject thiz,
+                                                          jbyteArray data, jint length) {
+    // TODO: implement BluetoothDataComeIn()
+    jbyte *c_dat = env->GetByteArrayElements(data, NULL);
+
+    PlatformStatusChanged(BLUETOOTH_DATA_EVT, (uint8_t *)c_dat, length);
+
+    env->ReleaseByteArrayElements(data, c_dat, NULL);
+}
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_anyun_exam_lib_RemoteService_BluetoothStatusChange(JNIEnv *env, jobject thiz,
+                                                            jint status) {
+    // TODO: implement BluetoothStatusChange()
+    uint8_t  sta = status;
+
+    PlatformStatusChanged(BLUETOOTH_STATUS_EVT, &sta, 1);
+}
diff --git a/lib/src/main/cpp/native-lib.h b/lib/src/main/cpp/native-lib.h
index bff3f5d..2040ef6 100644
--- a/lib/src/main/cpp/native-lib.h
+++ b/lib/src/main/cpp/native-lib.h
@@ -28,5 +28,7 @@
 int PlayTTS(const char *string, void (*callback)(int));
 int PlayTTS(std::string &tts, void (*callback)(int));
 void PlayRing(void);
+void SendToBluetooth(const uint8_t *data, int length);
+void ConnectToBluetooth(const char *addr, const char *pin);
 
 #endif //RTKBASESTATION_NATIVE_LIB_H
diff --git a/lib/src/main/cpp/rtk_module/rtk.cpp b/lib/src/main/cpp/rtk_module/rtk.cpp
index a5c75d3..244c0b3 100644
--- a/lib/src/main/cpp/rtk_module/rtk.cpp
+++ b/lib/src/main/cpp/rtk_module/rtk.cpp
@@ -18,6 +18,8 @@
 #include "../common/apptimer.h"
 #include "../rtk_platform/platform.h"
 #include "../native-lib.h"
+#include "virtual_rtk.h"
+#include "../mcu/mcu_if.h"
 
 #define DEBUG(fmt, args...)     LOGD("<rtk> <%s>: " fmt, __func__, ##args)
 
@@ -37,12 +39,25 @@
 
 static gpsStatus_t gpsStatus;
 
+static int (*WriteRtk)(int id, const void *buf, int len);
+
 static rtk_info CurrRTKInfo;
 static bool needSetPjk = false;
+
 static void CheckPjkParam(void);
 static void CheckPjkParamTimeout(union sigval sig);
+static int WriteBluetooth(int id, const void *buf, int len);
 
 static void *UartThread(void *p);
+
+void RtkCommModeSel(int mode)
+{
+    if (mode == 0) {
+        WriteRtk = WriteSerialPort;
+    } else {
+        WriteRtk = WriteBluetooth;
+    }
+}
 
 void ConfigRTKModule(void)
 {
@@ -55,7 +70,9 @@
     memset(&gpsStatus, 0, sizeof(gpsStatus));
     gpsStatus.hh = -1;
 
-    static struct serial_config serialConfig;
+    RtkCommModeSel(1);
+
+    /*static struct serial_config serialConfig;
 
     strcpy(serialConfig.name, "/dev/ttyHSL0");
     serialConfig.baud = 115200;
@@ -68,17 +85,23 @@
     pthread_attr_t attr;
     pthread_attr_init(&attr);
     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//detached
-    pthread_create(&pid, &attr, UartThread, &serialConfig);
+    pthread_create(&pid, &attr, UartThread, &serialConfig);*/
+}
+
+void ConfigRTKModuleLater(void)
+{
+    AppTimer_delete(CheckPjkParamTimeout);
+    AppTimer_add(CheckPjkParamTimeout, D_SEC(1));
 }
 
 void FactorySettings(void)
 {
-    WriteSerialPort(RTK_MODULE_UART, FACTORY, strlen(FACTORY));
+    WriteRtk(RTK_MODULE_UART, FACTORY, strlen(FACTORY));
 }
 
 void RebootModule(void)
 {
-    WriteSerialPort(RTK_MODULE_UART, REBOOT, strlen(REBOOT));
+    WriteRtk(RTK_MODULE_UART, REBOOT, strlen(REBOOT));
 }
 
 void handleRTKRebootComp(const struct nmea *s)
@@ -88,6 +111,7 @@
 }
 
 void handlePJKParam(const struct nmea *s) {
+    DEBUG("handlePJKParam");
 //PJK Parameter: A:6378137.000, 1/F:298.257223563, B0:0.000000deg, L0:120.000000, N0:0.000, E0:500000.000.
 //PJK Parameter: A:6378137.000, 1/F:298.257223563, B0:29.512453deg, L0:106.455336, N0:0.000, E0:0.000.
 //    bool setparam = true;
@@ -140,8 +164,8 @@
 
 void SetAYFactoryParam(int freq)
 {
-    WriteSerialPort(RTK_MODULE_UART, UNLOGALL, strlen(UNLOGALL));
-    WriteSerialPort(RTK_MODULE_UART, IFCOM2, strlen(IFCOM2));
+    WriteRtk(RTK_MODULE_UART, UNLOGALL, strlen(UNLOGALL));
+    WriteRtk(RTK_MODULE_UART, IFCOM2, strlen(IFCOM2));
 
     if (freq == 0)
         freq = 5;
@@ -149,13 +173,13 @@
     for (int i = 0; i < sizeof(PJKITEMS)/ sizeof(PJKITEMS[0]); ++i) {
         char cmd[64];
         sprintf(cmd, "log com1 %s ontime %0.1f\r\n", PJKITEMS[i], 1.0/(double)freq);
-        WriteSerialPort(RTK_MODULE_UART, cmd, strlen(cmd));
+        WriteRtk(RTK_MODULE_UART, cmd, strlen(cmd));
     }
 
     for (int i = 0; i <  sizeof(GPSITEMS)/ sizeof(GPSITEMS[0]); ++i) {
         char cmd[64];
         sprintf(cmd, "log com1 %s ontime %0.1f\r\n", GPSITEMS[i], 1.0/(double)freq);
-        WriteSerialPort(RTK_MODULE_UART, cmd, strlen(cmd));
+        WriteRtk(RTK_MODULE_UART, cmd, strlen(cmd));
     }
 
 //    WriteSerialPort(RTK_MODULE_UART, AY_PJKPARAM, strlen(AY_PJKPARAM));
@@ -168,7 +192,7 @@
 
     sprintf(buff, "set pjkpara 6378137 298.257223563 0 %d 0 500000\r\n", centLon);
 
-    WriteSerialPort(RTK_MODULE_UART, buff, strlen(buff));
+    WriteRtk(RTK_MODULE_UART, buff, strlen(buff));
 
     DEBUG("%s", buff);
 }
@@ -215,13 +239,17 @@
 
         if (RxBufLen > 0) {
 #if 1
-            const uint8_t *ptr = parseGPS(RxBuf, RxBuf + RxBufLen);
-            if(ptr != RxBuf) {
-                memcpy(RxBuf, ptr, RxBufLen - (ptr - RxBuf));
-                RxBufLen -= ptr - RxBuf;
-            } else if(RxBufLen == PARSE_BUFF_SIZE) {        //濉弧浜嗭紝涓旀病鏈変竴涓猏r锛岄兘鎶涘純
-                DEBUG("Parse GPS error");
+            if (VirtualIsConnected()) {     //PC妯℃嫙鐢ㄦ椂
                 RxBufLen = 0;
+            } else {
+                const uint8_t *ptr = parseGPS(RxBuf, RxBuf + RxBufLen);
+                if (ptr != RxBuf) {
+                    memcpy(RxBuf, ptr, RxBufLen - (ptr - RxBuf));
+                    RxBufLen -= ptr - RxBuf;
+                } else if (RxBufLen == PARSE_BUFF_SIZE) {        //濉弧浜嗭紝涓旀病鏈変竴涓猏r锛岄兘鎶涘純
+                    DEBUG("Parse GPS error");
+                    RxBufLen = 0;
+                }
             }
 #else
             RxBufLen = 0;           //PC妯℃嫙鐢ㄦ椂
@@ -233,7 +261,6 @@
     }
     pthread_exit(NULL);
 }
-
 
 void handleUnrecognisedNMEA(const uint8_t *data, uint16_t length) {
 //    char buff[4096] = {0};
@@ -464,7 +491,7 @@
 
 static void CheckPjkParam(void)
 {
-    int n = WriteSerialPort(RTK_MODULE_UART, INQ_PJK_PARAM, strlen(INQ_PJK_PARAM));
+    int n = WriteRtk(RTK_MODULE_UART, INQ_PJK_PARAM, strlen(INQ_PJK_PARAM));
     DEBUG("CN = %d", n);
     AppTimer_delete(CheckPjkParamTimeout);
     AppTimer_add(CheckPjkParamTimeout, D_SEC(3));
@@ -479,3 +506,10 @@
 
     CheckPjkParam();
 }
+
+static int WriteBluetooth(int id, const void *buf, int len)
+{
+//    SendToBluetooth((uint8_t *)buf, len);
+    SendMcuCommand(0x000B, (uint8_t *)buf, len);
+    return len;
+}
diff --git a/lib/src/main/cpp/rtk_module/rtk.h b/lib/src/main/cpp/rtk_module/rtk.h
index 82a5d54..3b81937 100644
--- a/lib/src/main/cpp/rtk_module/rtk.h
+++ b/lib/src/main/cpp/rtk_module/rtk.h
@@ -39,7 +39,9 @@
     double y;
 }rtk_info;
 
+void RtkCommModeSel(int mode);
 void ConfigRTKModule(void);
+void ConfigRTKModuleLater(void);
 void FactorySettings(void);
 void RebootModule(void);
 void SetAYFactoryParam(int freq);
diff --git a/lib/src/main/cpp/rtk_module/virtual_rtk.cpp b/lib/src/main/cpp/rtk_module/virtual_rtk.cpp
index 2b9df51..83dee44 100644
--- a/lib/src/main/cpp/rtk_module/virtual_rtk.cpp
+++ b/lib/src/main/cpp/rtk_module/virtual_rtk.cpp
@@ -15,30 +15,55 @@
 
 #define DEBUG(fmt, args...)     LOGD("<virtual_device> <%s>: " fmt, __func__, ##args)
 
+#define PARSE_BUFF_SIZE         4096
+
 struct vSocket {
     char domain_name[32];
     int port;
-};
+} VAddr;
 
+static bool virtRtkIsValid = false;
+static int connectCnt = 0;
+
+static void ConnectLater(union sigval sig);
+static void ConnectV(void);
 static void *VDataListenThread(void *p);
 
 void InitVirtualDevice(const char *domain_name, int port)
 {
     DEBUG("InitVirtualDevice %s: %d", domain_name, port);
 
-    struct vSocket *ptr = (struct vSocket *)malloc(sizeof(struct vSocket));
+    strcpy(VAddr.domain_name, domain_name);
+    VAddr.port = port;
 
-    strcpy(ptr->domain_name, domain_name);
-    ptr->port = port;
+    ConnectV();
+}
 
+bool VirtualIsConnected(void)
+{
+    bool temp;
+
+    do {
+        temp = virtRtkIsValid;
+    } while (temp != virtRtkIsValid);
+
+    return temp;
+}
+
+static void ConnectLater(union sigval sig) {
+    AppTimer_delete(ConnectLater);
+
+    ConnectV();
+}
+
+static void ConnectV(void)
+{
     pthread_t pid;
     pthread_attr_t attr;
     pthread_attr_init(&attr);
     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//detached
-    pthread_create(&pid, &attr, VDataListenThread, ptr);
+    pthread_create(&pid, &attr, VDataListenThread, &VAddr);
 }
-
-#define PARSE_BUFF_SIZE         4096
 
 static void *VDataListenThread(void *p) {
     struct vSocket *vs = (struct vSocket *)p;
@@ -53,10 +78,14 @@
     int fd = -1;
     int RxBufLen = 0;
 
+    connectCnt++;
+
     fd = ConnectTCP(vs->domain_name, vs->port);
 
     if (fd > 0) {
         DEBUG("铏氭嫙骞冲彴杩炴帴鎴愬姛");
+        virtRtkIsValid = true;
+        connectCnt = 0;
 //        PlayTTS("妯℃嫙鍣ㄨ繛鎺�", NULL);
     } else {
         DEBUG("铏氭嫙骞冲彴杩炴帴澶辫触");
@@ -96,8 +125,11 @@
     if (fd > 0) {
         DisconnectTCP(fd);
     }
+    virtRtkIsValid = false;
 
-    free(vs);
+    if (connectCnt < 5) {
+        AppTimer_add(ConnectLater, D_SEC(3));
+    }
 
 //    PlayTTS("妯℃嫙鍣ㄦ柇寮�", NULL);
 
diff --git a/lib/src/main/cpp/rtk_module/virtual_rtk.h b/lib/src/main/cpp/rtk_module/virtual_rtk.h
index 783bfa8..b26d441 100644
--- a/lib/src/main/cpp/rtk_module/virtual_rtk.h
+++ b/lib/src/main/cpp/rtk_module/virtual_rtk.h
@@ -6,5 +6,6 @@
 #define MYAPPLICATION2_VIRTUAL_RTK_H
 
 void InitVirtualDevice(const char *domain_name, int port);
+bool VirtualIsConnected(void);
 
 #endif //MYAPPLICATION2_VIRTUAL_RTK_H
diff --git a/lib/src/main/cpp/rtk_platform/platform.cpp b/lib/src/main/cpp/rtk_platform/platform.cpp
index a2e10ba..e3e0bb7 100644
--- a/lib/src/main/cpp/rtk_platform/platform.cpp
+++ b/lib/src/main/cpp/rtk_platform/platform.cpp
@@ -110,7 +110,7 @@
 
 static void AddEvnet(uint32_t event, const uint8_t *data, int length)
 {
-    DEBUG("AddEvnet 0x%04X length %d", event, length);
+//    DEBUG("AddEvnet 0x%04X length %d", event, length);
 
     struct event_queue_t *nw = (struct event_queue_t *)malloc(sizeof(struct event_queue_t));
     nw->next = NULL;
@@ -378,6 +378,9 @@
     }
     if (events & RTK_UPDATE_EVT) {
         DEBUG("RTK_UPDATE_EVT length %d", length);
+
+//        uint32_t ost = AppTimer_GetTickCount();
+
         const rtk_info *rtk = (rtk_info *)data;
 
         rbf.qf = rtk->qf;
@@ -400,6 +403,8 @@
         }
 
         UpdateRTKInfo(rtk);
+
+//        DEBUG("driver_test 璇勫垽鑰楁椂 %ld", AppTimer_GetTickCount() - ost);
 
         DEBUG("RTK_UPDATE_EVT =================");
     }
@@ -569,6 +574,32 @@
             MA_MainProcMsgEntry(c.a, (char *) data + 4);
         }
     }
+    if (events & BLUETOOTH_STATUS_EVT) {
+        DEBUG("BLUETOOTH_STATUS_EVT %d", data[0]);
+
+        if (data[0] == 3) {
+            // Connected
+            ParseMcuInit();
+            ConfigRTKModuleLater();
+
+            PlayTTS("钃濈墮杩炴帴", NULL);
+        } else if (data[0] == 2) {
+            // Disconnect
+            PlayTTS("钃濈墮鏂紑", NULL);
+        } else if (data[0] == 1) {
+            // Open
+            ConnectToBluetooth("00:1B:35:16:20:4A", "3800");
+//            ConnectToBluetooth("00:1D:43:9A:E0:79", "1900");
+//            ConnectToBluetooth("DESKTOP-IE9V7U8", "0000");
+            PlayTTS("钃濈墮鍚姩", NULL);
+        } else {
+            // Close
+            PlayTTS("钃濈墮鍏抽棴", NULL);
+        }
+    }
+    if (events & BLUETOOTH_DATA_EVT) {
+        ParseMcu(data, length);
+    }
 }
 
 static void *PlatformDataListenThread(void *p) {
diff --git a/lib/src/main/cpp/rtk_platform/platform.h b/lib/src/main/cpp/rtk_platform/platform.h
index 241d133..24d3c81 100644
--- a/lib/src/main/cpp/rtk_platform/platform.h
+++ b/lib/src/main/cpp/rtk_platform/platform.h
@@ -19,6 +19,8 @@
 #define CAR_SENSOR_UPDATE_EVT                       0x0200
 #define MASTER_COMM_EVT                             0x0400
 #define CAN_UPDATE_EVT                              0x0800
+#define BLUETOOTH_DATA_EVT                          0x1000
+#define BLUETOOTH_STATUS_EVT                        0x2000
 
 typedef struct {
     char domain_name[32];
diff --git a/lib/src/main/cpp/test_common/Geometry.cpp b/lib/src/main/cpp/test_common/Geometry.cpp
index 03c9d4b..bc05767 100644
--- a/lib/src/main/cpp/test_common/Geometry.cpp
+++ b/lib/src/main/cpp/test_common/Geometry.cpp
@@ -358,6 +358,19 @@
     return deg;
 }
 
+double DeltaYaw(double yaw1, double yaw2)
+{
+    double deltaAng;
+
+    if (fabs(yaw1 - yaw2) > 180.0) {
+        deltaAng = 360.0 - fabs(yaw1 - yaw2);
+    } else {
+        deltaAng = fabs(yaw1 - yaw2);
+    }
+
+    return deltaAng;
+}
+
 /**********************************************************
  * base 鍜� dest鐨勭浜岀偣閲嶅悎鏃跺舰鎴愮殑澶硅
  * @param base
diff --git a/lib/src/main/cpp/test_common/Geometry.h b/lib/src/main/cpp/test_common/Geometry.h
index 6ae52a3..bd34181 100644
--- a/lib/src/main/cpp/test_common/Geometry.h
+++ b/lib/src/main/cpp/test_common/Geometry.h
@@ -52,6 +52,7 @@
 double DistanceOf(PointF point1, PointF point2);
 double DistanceOf(PointF point, Line line);
 double YawOf(PointF p1, PointF p2);
+double DeltaYaw(double yaw1, double yaw2);
 double CalculateAngle(Line base, Line dest);
 PointF rotatePoint(PointF oldPoint, PointF centre, double degree);
 bool InsidePolygon(const Polygon *t1, const Polygon *t2);
diff --git a/lib/src/main/cpp/test_items/driving_curve.cpp b/lib/src/main/cpp/test_items/driving_curve.cpp
index 085a8d0..dd3bcc9 100644
--- a/lib/src/main/cpp/test_items/driving_curve.cpp
+++ b/lib/src/main/cpp/test_items/driving_curve.cpp
@@ -130,7 +130,7 @@
     } else if (moveDirect == 0) {
         uint32_t tp = TimeMakeComposite(rtkTime->hh, rtkTime->mm, rtkTime->ss, rtkTime->mss*10);
 
-        if (tp - stopTimepoint >= examParam.curve_pause_criteria && !reportStopCarTimeout) {
+        if (tp - stopTimepoint >= CorrectPauseCriteria(examParam.curve_pause_criteria) && !reportStopCarTimeout) {
             // 鍋滆溅瓒�2绉掞紝涓嶅悎鏍�
             AddExamFault(28, rtkTime);
             DEBUG("涓�斿仠杞�");
diff --git a/lib/src/main/cpp/test_items/park_bottom.cpp b/lib/src/main/cpp/test_items/park_bottom.cpp
index 40350b5..261e425 100644
--- a/lib/src/main/cpp/test_items/park_bottom.cpp
+++ b/lib/src/main/cpp/test_items/park_bottom.cpp
@@ -175,7 +175,7 @@
 
             if (moveDirect == storeMoveDirectBeforeStop) {
                 // 鍚屾柟鍚戝啀鍚姩锛岀户缁垽鏂槸鍚﹀仠杞﹁秴鏃�
-                if (tp - stopTimepoint >= examParam.park_bottom_pause_criteria && reverseCar) {
+                if (tp - stopTimepoint >= CorrectPauseCriteria(examParam.park_bottom_pause_criteria) && reverseCar) {
                     // 鍋滆溅瓒�2绉掞紝姣忔鎵�5鍒�
                     AddExamFault(11, rtkTime);
                     DEBUG("涓�斿仠杞�");
diff --git a/lib/src/main/cpp/test_items/park_edge.cpp b/lib/src/main/cpp/test_items/park_edge.cpp
index 6ad7c5e..8b33d8b 100644
--- a/lib/src/main/cpp/test_items/park_edge.cpp
+++ b/lib/src/main/cpp/test_items/park_edge.cpp
@@ -143,7 +143,7 @@
 
             if (moveStatus == storeMoveStatusBeforeStop) {
                 // 鍚屾柟鍚戝啀鍚姩锛岀户缁垽鏂槸鍚﹀仠杞﹁秴鏃�
-                if (tp - stopTimepoint >= examParam.park_edge_pause_criteria && occurMoveBack) {
+                if (tp - stopTimepoint >= CorrectPauseCriteria(examParam.park_edge_pause_criteria) && occurMoveBack) {
                     // 鍋滆溅瓒�2绉掞紝姣忔鎵�5鍒�
                     AddExamFault(26, rtkTime);
                     DEBUG("鍋滆溅瓒呮椂");
diff --git a/lib/src/main/cpp/test_items/turn_a90.cpp b/lib/src/main/cpp/test_items/turn_a90.cpp
index be8b131..a9443d7 100644
--- a/lib/src/main/cpp/test_items/turn_a90.cpp
+++ b/lib/src/main/cpp/test_items/turn_a90.cpp
@@ -99,7 +99,7 @@
     } else if (moveDirect == 0) {
         uint32_t tp = TimeMakeComposite(rtkTime->hh, rtkTime->mm, rtkTime->ss, rtkTime->mss*10);
 
-        if (tp - stopTimepoint >= examParam.turn_a90_pause_criteria && !reportStopCarTimeout) {
+        if (tp - stopTimepoint >= CorrectPauseCriteria(examParam.turn_a90_pause_criteria) && !reportStopCarTimeout) {
             // 鍋滆溅瓒�2绉掞紝姣忔鎵�5鍒�
             AddExamFault(31, rtkTime);
             DEBUG("涓�斿仠杞�");
diff --git a/lib/src/main/cpp/test_items2/road_exam.cpp b/lib/src/main/cpp/test_items2/road_exam.cpp
index f3164f5..22102d0 100644
--- a/lib/src/main/cpp/test_items2/road_exam.cpp
+++ b/lib/src/main/cpp/test_items2/road_exam.cpp
@@ -106,6 +106,12 @@
 static map<int, int> CrossingHint;
 static map<int, bool> ErrorLaneReport;
 
+typedef struct {
+    int road_id;
+    double distance;
+} trigger_detect_t;
+static map<int, trigger_detect_t> TriggerDetect;           // 璺濈鏈矾娈靛悇瑙﹀彂鐐圭殑璺濈
+
 #define ROAD_EXAM_READY_NEXT            0
 #define ROAD_EXAM_FREE_RUN              1
 #define ROAD_EXAM_ITEM_CAR_START            2
@@ -134,7 +140,7 @@
 static int prevTurnWise;
 
 static void ItemExam(road_exam_map &RoadMap, int roadIndex, const car_model *car, LIST_CAR_MODEL &CarModelList, double speed, int moveDirect, const struct RtkTime *rtkTime, double straight, double road_end);
-static void ItemExam2(road_exam_map &RoadMap, int roadIndex, const car_model *car, LIST_CAR_MODEL &CarModelList);
+static void ItemExam2(road_exam_map &RoadMap, int roadIndex, const car_model *car, LIST_CAR_MODEL &CarModelList, int forward, int moveDirect);
 static int isTurn(int currYaw, int prevYaw, int thres);
 static void ResetTurnDetect(const car_model *car);
 static void DetectTurn(const car_model *car, int moveDirect, const struct RtkTime *rtkTime);
@@ -142,7 +148,7 @@
 
 static int NearbyCrossingGuide(road_exam_map &RoadMap, int &stopLineIndex, int roadIndex, road_t &road, const car_model *car);
 
-static int EntryItem(int index, road_exam_map &RoadMap, const car_model *car, LIST_CAR_MODEL &CarModelList);
+static int EntryItem(int index, road_exam_map &RoadMap, const car_model *car, LIST_CAR_MODEL &CarModelList, int forward, int moveDirect);
 
 static int CalcRoadIndex(int currRoadIndex, road_exam_map &RoadMap, const car_model *car);
 
@@ -187,6 +193,8 @@
     RoadExamItem[ROAD_EXAM_ITEM_CAR_STOP] = ROAD_EXAM_ITEM_NOT_EXEC;
 
     RoadExamStatus = ROAD_EXAM_READY_NEXT;
+
+    TriggerDetect.clear();
 
     win = false;
 }
@@ -1276,7 +1284,7 @@
 void TestRoadGeneral(road_exam_map &RoadMap, const car_model *car, LIST_CAR_MODEL &CarModelList, double speed, int moveDirect, const struct RtkTime *rtkTime)
 {
     double BigStraightRoadFree = 0, RoadCrossingFree = 0, TargetFree = 0;
-
+    int forward = 0;                    // 杞﹁締鐩稿褰撳墠閬撹矾鐨�
     UpdateCarSensor(rtkTime);
 
     UpdataOdo(speed, moveDirect, rtkTime);
@@ -1436,7 +1444,7 @@
             prevMoveDirect = moveDirect;
         } else if (moveDirect == 0) {
             // 鎸佺画鍋滆溅
-            if (TimeGetDiff(rtkTime, &stopTimepoint) >= examParam.road_pause_criteria && !StopCarOnRedArea &&
+            if (TimeGetDiff(rtkTime, &stopTimepoint) >= CorrectPauseCriteria(examParam.road_pause_criteria) && !StopCarOnRedArea &&
                 StopOnRedArea(RoadMap, car)) {
                 // 鍋滆溅瓒�2绉掞紝鍋滃湪绾㈠尯锛屼笉鍚堟牸
                 AddExamFault(16, rtkTime);
@@ -1491,6 +1499,34 @@
         car_sensor_value_t brk = ReadCarSensorValue(BREAK);
         // 妫�娴嬮�氳繃璺彛銆佷汉琛岄亾绛夊尯鍩熸椂锛岄噴鏀惧埞杞︽垨鍑忛��
         TargetFree = ApproachTarget(RoadMap, car, currExamMapIndex, (brk.value == BREAK_ACTIVE), speed, moveDirect, rtkTime);
+        // 妫�鏌�
+        projection_t projection;
+
+        uint32_t tm1 = AppTimer_GetTickCount();
+
+        projection = CalcProjectionWithRoadEdgeEx(RoadMap.roads[currExamMapIndex].rightEdge, car->carXY[car->axial[AXIAL_FRONT]]);
+
+        if (projection.edgeIndex < RoadMap.roads[currExamMapIndex].rightEdge.size()) {
+            if (projection.pointIndex < RoadMap.roads[currExamMapIndex].rightEdge[projection.edgeIndex].points.size() - 1) {
+                double roadYaw = YawOf(RoadMap.roads[currExamMapIndex].rightEdge[projection.edgeIndex].points[projection.pointIndex + 1],
+                      RoadMap.roads[currExamMapIndex].rightEdge[projection.edgeIndex].points[projection.pointIndex]);
+
+                int deltaAng;
+
+                if (ABS((int)roadYaw - (int)car->yaw) > 180) {
+                    deltaAng = 360 - ABS((int)roadYaw - (int)car->yaw);
+                } else {
+                    deltaAng = ABS((int)roadYaw - (int)car->yaw);
+                }
+
+                if (deltaAng < 90) {
+                    forward = 1;
+                } else {
+                    forward = -1;
+                }
+//                DEBUG("璺鏂瑰悜 %f 杞﹁締鏂瑰悜 %f 宸�� %d 鑰楁椂 %ld", roadYaw, car->yaw, deltaAng, AppTimer_GetTickCount() - tm1);
+            }
+        }
     }
     ExitTarget(RoadMap, car, CarModelList, rtkTime);
 
@@ -1554,7 +1590,7 @@
         ItemExam(RoadMap, currExamMapIndex, car, CarModelList, speed, moveDirect, rtkTime,
                  BigStraightRoadFree,
                  TargetFree > RoadCrossingFree ? RoadCrossingFree : TargetFree);
-        ItemExam2(RoadMap, currExamMapIndex, car, CarModelList);
+        ItemExam2(RoadMap, currExamMapIndex, car, CarModelList, forward, moveDirect);
     }
 }
 
@@ -1621,6 +1657,7 @@
         }
     } else if (RoadExamStatus == ROAD_EXAM_FREE_RUN) {
         if (ReadOdo() - freeRunDistance > freeRunExceptDistance) {
+            DEBUG("瀵绘壘涓嬩竴涓瓙椤圭洰");
             RoadExamStatus = ROAD_EXAM_READY_NEXT;
         }
     } else {
@@ -1649,6 +1686,7 @@
         }
 
         if (!testing) {
+            DEBUG("褰撳墠瀛愰」缁撴潫");
             RoadExamItem[RoadExamStatus] = ROAD_EXAM_ITEM_EXECED;
             if (RoadExamStatus == ROAD_EXAM_ITEM_CAR_START) {
                 freeRunExceptDistance = 60.0;
@@ -1665,9 +1703,13 @@
     }
 }
 
-static void ItemExam2(road_exam_map &RoadMap, int roadIndex, const car_model *car, LIST_CAR_MODEL &CarModelList)
+static void ItemExam2(road_exam_map &RoadMap, int roadIndex, const car_model *car, LIST_CAR_MODEL &CarModelList, int forward, int moveDirect)
 {
-    int item = EntryItem(roadIndex, RoadMap, car, CarModelList);
+    int item = 0;
+
+    if (RoadExamStatus == ROAD_EXAM_READY_NEXT) {
+        item = EntryItem(roadIndex, RoadMap, car, CarModelList, forward, moveDirect);
+    }
 
     if (RoadExamStatus == ROAD_EXAM_READY_NEXT && item == 3) {
         StartDriveStraightExam();
@@ -1919,13 +1961,35 @@
     return false;
 }
 
-static int EntryItem(int index, road_exam_map &RoadMap, const car_model *car, LIST_CAR_MODEL &CarModelList)
+static int EntryItem(int index, road_exam_map &RoadMap, const car_model *car, LIST_CAR_MODEL &CarModelList, int forward, int moveDirect)
 {
-    if (index < 0 || index >= RoadMap.roads.size() || RoadMap.examScheme.size() == 0)
+    if (index < 0 || index >= RoadMap.roads.size() || RoadMap.examScheme.size() == 0 || CarModelList.size() < 2)
         return -1;
+
+    list<car_model *>::iterator iter = CarModelList.begin();
+
+    Line trace;
+    PointF p1, p2;
+
+    p1 = ((car_model *)(*iter))->carXY[((car_model *)(*iter))->axial[AXIAL_FRONT]];
+    ++iter;
+    p2 = ((car_model *)(*iter))->carXY[((car_model *)(*iter))->axial[AXIAL_FRONT]];
+    MakeLine(&trace, &p1, &p2);
+
+    double trace_length = DistanceOf(p1, p2);
+
+    if (TriggerDetect.size() > 0) {
+        auto it = TriggerDetect.begin();
+
+        if (it->second.road_id != RoadMap.roads[index].id) {
+            TriggerDetect.clear();
+        }
+    }
 
     for (int j = 0; j < RoadMap.examScheme[0].triggerLines.size(); ++j) {
         if (RoadMap.examScheme[0].triggerLines[j].road == RoadMap.roads[index].id) {
+
+
             /*Line triggerLine;
 
             PointF p2 = CalcProjectionWithRoadEdge(RoadMap.roads[index].leftEdge, RoadMap.triggerLines[i].points[0]);
@@ -1937,7 +2001,7 @@
                 return RoadMap.triggerLines[i].active;
             }*/
 
-            vector<double> vec;
+           /* vector<double> vec;
 
             PointF p1 = CalcProjectionWithRoadEdge(RoadMap.roads[index].rightEdge,
                                                    car->carXY[car->axial[AXIAL_FRONT]]);
@@ -1954,6 +2018,87 @@
             if (fabs(vec[0] + vec[1] - vec[2]) < 0.1) {
                 DEBUG("瑙﹀彂椤圭洰 %d", RoadMap.examScheme[0].triggerLines[j].active);
                 return RoadMap.examScheme[0].triggerLines[j].active;
+            }*/
+
+//            double angleOf = DeltaYaw(YawOf(RoadMap.examScheme[0].triggerLines[j].points[0], car->carXY[car->axial[AXIAL_FRONT]]), car->yaw);
+//            auto it = TriggerDetect.find(j);
+//
+//            if (it != TriggerDetect.end()) {
+//                if (fabs(angleOf - it->second.angleOf) > 2.0) {
+//                    bool trigger = false;
+//
+//                    trigger_detect_t up;
+//
+//                    if (moveDirect == 1 && forward == 1 && it->second.angleOf <= 90 && angleOf > 90) {
+//                        trigger = true;
+//                    }
+//                    DEBUG("瑙傚療瀛�<%d> 鏇存柊 %f -> %f", j, it->second.angleOf, angleOf);
+//
+//                    up.angleOf = angleOf;
+//                    up.road_id = RoadMap.roads[index].id;
+//
+//                    TriggerDetect[j] = up;
+//
+//
+//
+//                    if (trigger) {
+//                        DEBUG("pos %d 瑙﹀彂椤圭洰 %d", j, RoadMap.examScheme[0].triggerLines[j].active);
+//                        return RoadMap.examScheme[0].triggerLines[j].active;
+//                    }
+//                }
+//            } else {
+//                trigger_detect_t up;
+//
+//                up.road_id = RoadMap.roads[index].id;
+//                up.angleOf = angleOf;
+//
+//                TriggerDetect.insert(pair<int, trigger_detect_t>(j, up));
+//
+//                DEBUG("瑙傚療瀛�<%d> 鍔犲叆 %f", j, angleOf);
+//            }
+
+//            double dist = DistanceOf(car->carXY[car->axial[AXIAL_FRONT]],
+//                       RoadMap.examScheme[0].triggerLines[j].points[0]);
+            auto it = TriggerDetect.find(j);
+
+            if (it != TriggerDetect.end()) {
+                PointF verticalPoint;
+
+                if (moveDirect == 1 &&
+                    forward == 1 &&
+                    trace_length > 0.01 &&
+                    VerticalPointOnLine(RoadMap.examScheme[0].triggerLines[j].points[0], trace, verticalPoint) &&
+                    DistanceOf(verticalPoint, RoadMap.examScheme[0].triggerLines[j].points[0]) < it->second.distance) {
+                    DEBUG("pos %d 瑙﹀彂椤圭洰 %d", j, RoadMap.examScheme[0].triggerLines[j].active);
+
+                    DEBUG("(%f, %f) ON (%f, %f)-(%f, %f) IS (%f, %f) trace %f",
+                          RoadMap.examScheme[0].triggerLines[j].points[0].X,
+                          RoadMap.examScheme[0].triggerLines[j].points[0].Y,
+                          trace.X1,
+                          trace.Y1,
+                          trace.X2,
+                          trace.Y2,
+                          verticalPoint.X,
+                          verticalPoint.Y,
+                          trace_length);
+
+                    return RoadMap.examScheme[0].triggerLines[j].active;
+                }
+            } else {
+                PointF p1 = CalcProjectionWithRoadEdge(RoadMap.roads[index].leftEdge, RoadMap.examScheme[0].triggerLines[j].points[0]);
+                PointF p2 = CalcProjectionWithRoadEdge(RoadMap.roads[index].rightEdge, RoadMap.examScheme[0].triggerLines[j].points[0]);
+
+                double dist1 = DistanceOf(p1, RoadMap.examScheme[0].triggerLines[j].points[0]);
+                double dist2 = DistanceOf(p2, RoadMap.examScheme[0].triggerLines[j].points[0]);
+
+                trigger_detect_t up;
+
+                up.road_id = RoadMap.roads[index].id;
+                up.distance = MAX(dist1, dist2);
+
+                TriggerDetect.insert(pair<int, trigger_detect_t>(j, up));
+
+                DEBUG("瑙傚療瀛�<%d> 鍔犲叆 %f", j, MAX(dist1, dist2));
             }
         }
     }
diff --git a/lib/src/main/cpp/test_items2/through_something.cpp b/lib/src/main/cpp/test_items2/through_something.cpp
index 67583d7..31b0850 100644
--- a/lib/src/main/cpp/test_items2/through_something.cpp
+++ b/lib/src/main/cpp/test_items2/through_something.cpp
@@ -228,6 +228,10 @@
         Line line;
         int roadIndex = 0;
 
+        if (RoadMap.specialAreas[x].type == SCHOOL_AREA) {          // 绂诲紑瀛︽牎鍖哄煙涓嶆寜璺ㄧ嚎鍒ゅ畾
+            continue;
+        }
+
         for (; roadIndex < RoadMap.roads.size(); ++roadIndex) {
             if (RoadMap.roads[roadIndex].id == RoadMap.specialAreas[x].road)
                 break;
diff --git a/lib/src/main/java/com/anyun/exam/lib/Ota.java b/lib/src/main/java/com/anyun/exam/lib/Ota.java
new file mode 100644
index 0000000..09b6fa5
--- /dev/null
+++ b/lib/src/main/java/com/anyun/exam/lib/Ota.java
@@ -0,0 +1,55 @@
+package com.anyun.exam.lib;
+
+import android.content.Context;
+import android.os.Build;
+import android.os.RecoverySystem;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+
+public class Ota {
+    private final static String TAG = "OTA";
+
+    static String dest = "/data/media/0/update.zip";
+
+    public boolean excuteUpdateZip(Context context, String path) {
+        boolean verifyPass = false;
+        // TODO Auto-generated method stub
+        try {
+            File ota = new File(dest);
+
+
+            Log.d(TAG, "path = " + ota.getCanonicalPath() + "  " + ota.exists());
+
+            Log.d(TAG, "Product Model: " + Build.DISPLAY);
+
+            try {
+                RecoverySystem.verifyPackage(ota, recoveryVerifyListener, null);
+                verifyPass = true;
+                Log.d(TAG, "Verify OK");
+            } catch (Exception e) {
+                verifyPass = false;
+                Log.d(TAG, "Zip Verify Fail!");
+            }
+
+            if (verifyPass) {
+                RecoverySystem.installPackage(context, ota);
+            }
+
+        } catch (IOException e) {
+            // TODO Auto-generated catch block
+            Log.e(TAG, "OTA 閿欒 " + e.getMessage());
+        } finally {
+            return verifyPass;
+        }
+    }
+
+    RecoverySystem.ProgressListener recoveryVerifyListener = new RecoverySystem.ProgressListener() {
+        public void onProgress(int progress) {
+            Log.d(TAG, "verify progress" + progress);
+//            final int progress1=progress;
+//            showinfo("verify progress " + progress1+" %");
+        }
+    };
+}
diff --git a/lib/src/main/java/com/anyun/exam/lib/RemoteService.java b/lib/src/main/java/com/anyun/exam/lib/RemoteService.java
index 76ec97f..f368d69 100644
--- a/lib/src/main/java/com/anyun/exam/lib/RemoteService.java
+++ b/lib/src/main/java/com/anyun/exam/lib/RemoteService.java
@@ -1,22 +1,31 @@
 package com.anyun.exam.lib;
 
+import android.Manifest;
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.media.AudioManager;
 import android.media.MediaPlayer;
 import android.media.Ringtone;
 import android.media.RingtoneManager;
 import android.net.Uri;
+import android.os.Build;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Message;
 import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.util.Base64;
 import android.util.Log;
 
+import com.anyun.exam.lib.util.Bluetooth;
+import com.anyun.exam.lib.util.BluetoothChatService;
+import com.anyun.exam.lib.util.BluetoothChatServiceCallback;
+import com.anyun.exam.lib.util.Constants;
 import com.anyun.exam.lib.util.DESUtil;
 import com.anyun.exam.lib.util.NetUtils;
 import com.anyun.exam.lib.util.ProperUtil;
@@ -24,6 +33,8 @@
 import com.anyun.exam.lib.util.SpeakerCallback;
 
 import androidx.annotation.Nullable;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -32,6 +43,9 @@
 import java.util.ArrayList;
 import java.util.Properties;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.regex.Pattern;
+
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
 /**
  * MyApplication2
@@ -52,6 +66,13 @@
     private Uri ringUri = null;
 
     private Upgrade upgrade = null;
+
+    private Bluetooth mBluetooth = null;
+    private BluetoothChatService mChatService = null;
+    private String mConnectedDeviceName = null;
+    private String mTargetBluetooth = null;
+    private String mTargetBluetoothAddr = null;
+    private boolean mDiscoveryBluetooth = false;
 
     private IRemoteInterface.Stub iRemoteInterface = new IRemoteInterface.Stub(){
         @Override
@@ -75,7 +96,7 @@
             if (cmd == 0x8100) {
                 try {
                     byte [] file = value.getBytes("ISO-8859-1");
-                    Log.d(TAG, byte2hex(file));
+//                    Log.d(TAG, byte2hex(file));
                     MainProcBinMsgEntry(cmd, file, file.length);
                 } catch (UnsupportedEncodingException e) {
                     Log.d(TAG, "UnsupportedEncodingException");
@@ -102,7 +123,7 @@
 
         new Thread(new TestThread()).start();
 
-        new Thread(new AutoUpdateMcuThread(this)).start();
+//        new Thread(new AutoUpdateMcuThread(this)).start();
 
         upgrade = Upgrade.getInstance(getApplicationContext());
         upgrade.CheckUpgrade();
@@ -114,6 +135,11 @@
         Log.d(TAG, "鍩哄甫鐗堟湰 " + getBaseband_Ver());
 
         String ver = getBaseband_Ver();
+
+        mBluetooth = Bluetooth.getInstance(getApplicationContext(), mHandler);
+        mBluetooth.OpenBluetooth();
+        // Initialize the BluetoothChatService to perform mBluetooth connections
+        mChatService = new BluetoothChatService(this, mHandler);
     }
 
     class AutoUpdateMcuThread implements Runnable {
@@ -232,7 +258,10 @@
         @Override
         public void run() {
             try {
-                Thread.sleep(500);
+                Thread.sleep(30000);
+
+//                Ota ota = new Ota();
+//                ota.excuteUpdateZip(getApplicationContext(), "");
             } catch (InterruptedException e) {
 
             }
@@ -250,6 +279,15 @@
     public void onDestroy() {
         super.onDestroy();
         Log.i(TAG,"onDestroy");
+
+        if (mChatService != null) {
+            mChatService.stop();
+        }
+        if (mBluetooth != null) {
+            mBluetooth.CloseBluetooth();
+            mBluetooth.unRegisterReceiver();
+        }
+
         mIsServiceDestroyed.set(true);
 
         Log.d(TAG, "onDestory executed 杩涚▼ " + Process.myPid());
@@ -362,6 +400,125 @@
         }
     }
 
+    public void ConnectBluetooth(String name, String pin) {
+        if (mBluetooth != null && mChatService != null) {
+            mBluetooth.SetPin(pin);
+            mTargetBluetooth = name;
+            if (isMac(name)) {
+                mTargetBluetoothAddr = name;
+                handlerConnectBluetooth.removeCallbacks(runnableConnectBluetooth);
+                handlerConnectBluetooth.postDelayed(runnableConnectBluetooth, 500);
+            } else {
+                mDiscoveryBluetooth = true;
+                mBluetooth.doDiscovery(name);
+            }
+        }
+    }
+
+    Handler handlerConnectBluetooth = new Handler();
+    Runnable runnableConnectBluetooth = new Runnable() {
+        @Override
+        public void run() {
+            if (mChatService != null && mTargetBluetoothAddr != null) {
+                mChatService.connect(mTargetBluetoothAddr, true);
+            }
+        }
+    };
+
+    public boolean isMac(String addr) {
+        String pattern = "^([0-9A-Fa-f]{2})(:[0-9A-Fa-f]{2}){5}$";
+        return Pattern.matches(pattern, addr);
+    }
+
+    public void WriteBluetooth(byte []data) {
+        if (mChatService != null && mChatService.getState() == BluetoothChatService.STATE_CONNECTED) {
+            mChatService.write(data);
+        }
+    }
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case Constants.MESSAGE_BLUETOOTH_STATUS:
+                    if (msg.arg1 == 1) {
+                        Log.d(TAG, "钃濈墮寮�鍚�");
+//                        mBluetooth.doDiscovery();
+                        if (mChatService != null) {
+//                            mChatService.connect("00:1D:43:9A:E0:79", true);
+//                            mChatService.connect("00:1A:7D:DA:71:11", true);
+                        }
+                        BluetoothStatusChange(1);
+                    } else {
+                        Log.d(TAG, "钃濈墮鍏抽棴");
+                        BluetoothStatusChange(0);
+                    }
+                    break;
+                case Constants.MESSAGE_BLUETOOTH_FOUND:
+                    String btaddr = msg.getData().getString(Constants.DEVICE_ADDRESS);
+                    Log.d(TAG, "鍙戠幇璁惧 " + btaddr);
+                    if (mDiscoveryBluetooth) {
+                        mTargetBluetoothAddr = btaddr;
+                    }
+                    break;
+                case Constants.MESSAGE_BLUETOOTH_DISCOVERY_FINISHED:
+                    Log.d(TAG, "鎼滅储缁撴潫");
+                    if (mDiscoveryBluetooth) {
+                        handlerConnectBluetooth.removeCallbacks(runnableConnectBluetooth);
+                        handlerConnectBluetooth.postDelayed(runnableConnectBluetooth, 500);
+                    }
+                    mDiscoveryBluetooth = false;
+                    break;
+                case Constants.MESSAGE_STATE_CHANGE:
+                    switch (msg.arg1) {
+                        case BluetoothChatService.STATE_CONNECTED:
+                            Log.d(TAG, "钃濈墮宸茶繛鎺�");
+                            BluetoothStatusChange(3);
+                            break;
+                        case BluetoothChatService.STATE_CONNECTING:
+                            Log.d(TAG, "钃濈墮杩炴帴涓�...");
+                            break;
+                        case BluetoothChatService.STATE_LISTEN:
+                            Log.d(TAG, "钃濈墮鐩戝惉");
+                            BluetoothStatusChange(2);
+                            break;
+                        case BluetoothChatService.STATE_NONE:
+                            Log.d(TAG, "钃濈墮鏃犳晥");
+                            BluetoothStatusChange(2);
+                            handlerConnectBluetooth.removeCallbacks(runnableConnectBluetooth);
+                            handlerConnectBluetooth.postDelayed(runnableConnectBluetooth, 5000);
+                            break;
+                    }
+                    break;
+                case Constants.MESSAGE_WRITE:
+                    break;
+                case Constants.MESSAGE_READ:
+//                    Log.d(TAG, String.format("钃濈墮鏁版嵁 length = %d 绾跨▼ %d", msg.arg1, Thread.currentThread().getId()));
+                    BluetoothDataComeIn((byte[]) msg.obj, msg.arg1);
+                    /*if (ndkUtils != null) {
+                        byte[] readBuf = (byte[]) msg.obj;
+                        ndkUtils.bluetoothMoreData(readBuf, msg.arg1);
+                    }*/
+                    break;
+                case Constants.MESSAGE_DEVICE_NAME:
+                    // save the connected device's name
+                    mConnectedDeviceName = msg.getData().getString(Constants.DEVICE_NAME);
+                    Log.d(TAG, "MESSAGE_DEVICE_NAME: " + mConnectedDeviceName);
+                    break;
+                case Constants.MESSAGE_TOAST:
+                    break;
+                    default:break;
+            }
+        }
+    };
+
+    class DataCallback implements BluetoothChatServiceCallback {
+        @Override
+        public void iDataComeIn(byte []data, int length) {
+
+        }
+    }
+
     // Used to load the 'native-lib' library on application startup.
     static {
         System.loadLibrary("native-lib");
@@ -372,4 +529,6 @@
     public native void MainProcBinMsgEntry(int cmd, byte []data, int length);
     public native void UpgradeMcu(String vercode, byte []rom);
     public native void TextSpeakEnd(int id);
+    public native void BluetoothStatusChange(int status);
+    public native void BluetoothDataComeIn(byte []data, int length);
 }
diff --git a/lib/src/main/java/com/anyun/exam/lib/util/Bluetooth.java b/lib/src/main/java/com/anyun/exam/lib/util/Bluetooth.java
new file mode 100644
index 0000000..91b66b1
--- /dev/null
+++ b/lib/src/main/java/com/anyun/exam/lib/util/Bluetooth.java
@@ -0,0 +1,152 @@
+package com.anyun.exam.lib.util;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import java.lang.reflect.Method;
+
+public class Bluetooth extends BroadcastReceiver {
+    private static final String TAG = Bluetooth.class.getCanonicalName();
+    public static final String DEFAULT_BT_PASSWORD = "0000";
+
+    private static Bluetooth instance = null;
+    private Context context;
+    private BluetoothAdapter mBtAdapter = null;
+    private Handler mHandler;
+    private String pin = DEFAULT_BT_PASSWORD;
+    private String mName = null;
+
+    public static Bluetooth getInstance(Context context, Handler handler) {
+        if (instance == null) {
+            synchronized (Bluetooth.class) {
+                if (instance == null) {
+                    instance = new Bluetooth(context, handler);
+                }
+            }
+        }
+        return instance;
+    }
+
+    private Bluetooth(Context context, Handler handler) {
+        this.context = context;
+        this.mHandler = handler;
+        mBtAdapter = BluetoothAdapter.getDefaultAdapter();
+
+        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
+        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
+        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+        filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
+        filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
+        filter.addAction("android.bluetooth.device.action.PAIRING_REQUEST");
+        context.registerReceiver(this, filter);
+    }
+
+    public void OpenBluetooth() {
+        if (!mBtAdapter.isEnabled()) {
+            mHandler.obtainMessage(Constants.MESSAGE_BLUETOOTH_STATUS, 0, -1, null)
+                    .sendToTarget();
+            mBtAdapter.enable();
+        } else {
+//            Intent discoveryIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
+//            discoveryIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,300);
+//            discoveryIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+//            context.startActivity(discoveryIntent);
+
+            mHandler.obtainMessage(Constants.MESSAGE_BLUETOOTH_STATUS, 1, -1, null)
+                    .sendToTarget();
+        }
+    }
+
+    public void CloseBluetooth() {
+        if (mBtAdapter.isEnabled()) {
+            mBtAdapter.disable();
+        }
+    }
+
+    public void unRegisterReceiver() {
+        context.unregisterReceiver(this);
+    }
+
+    public void SetPin(String pin) {
+        this.pin = pin;
+    }
+
+    /**
+     * Start device discover with the BluetoothAdapter
+     */
+    public void doDiscovery(String name) {
+        Log.d(TAG, "doDiscovery()");
+
+        mName = name;
+
+        // If we're already discovering, stop it
+        if (mBtAdapter.isDiscovering()) {
+            mBtAdapter.cancelDiscovery();
+        }
+
+        // Request discover from BluetoothAdapter
+        mBtAdapter.startDiscovery();
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
+
+        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
+            // Get the BluetoothDevice object from the Intent
+            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+            if (device.getName() != null && mName != null && device.getName().equals(mName)) {
+                Message msg = mHandler.obtainMessage(Constants.MESSAGE_BLUETOOTH_FOUND);
+                Bundle bundle = new Bundle();
+                bundle.putString(Constants.DEVICE_ADDRESS, device.getAddress());
+                msg.setData(bundle);
+                mHandler.sendMessage(msg);
+
+                mBtAdapter.cancelDiscovery();
+            }
+        } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
+
+            Message msg = mHandler.obtainMessage(Constants.MESSAGE_BLUETOOTH_DISCOVERY_FINISHED);
+            mHandler.sendMessage(msg);
+        } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
+            int status = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);
+            int status2 = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, BluetoothAdapter.STATE_OFF);
+
+            if (status == BluetoothAdapter.STATE_ON) {
+                OpenBluetooth();
+            } else if (status == BluetoothAdapter.STATE_OFF) {
+                OpenBluetooth();
+            }
+        } else if (action.equals("android.bluetooth.device.action.PAIRING_REQUEST")) {
+            Log.d(TAG, "鍖归厤璇锋眰");
+            // Get the BluetoothDevice object from the Intent
+            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+            /*if (device.getName().equals(BLUETOOTH_OBD_NAME) || device.getName().equals(BLUETOOTH_TARGET2_NAME)) */{
+                try {
+                    ClsUtils.setPairingConfirmation(device.getClass(), device, true);
+                    Log.d(TAG, "isOrderedBroadcast:"+isOrderedBroadcast()+",isInitialStickyBroadcast:"+isInitialStickyBroadcast());
+                    if (isOrderedBroadcast()) {
+                        abortBroadcast();
+                    }
+
+                    ClsUtils.setPin(device.getClass(), device, pin);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        } else if (BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
+
+        } else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) {
+            int status = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE);
+            Log.d(TAG, "鎵弿妯″紡 " + status);
+        }
+    }
+}
diff --git a/lib/src/main/java/com/anyun/exam/lib/util/BluetoothChatService.java b/lib/src/main/java/com/anyun/exam/lib/util/BluetoothChatService.java
new file mode 100644
index 0000000..3cd6e67
--- /dev/null
+++ b/lib/src/main/java/com/anyun/exam/lib/util/BluetoothChatService.java
@@ -0,0 +1,540 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.anyun.exam.lib.util;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothServerSocket;
+import android.bluetooth.BluetoothSocket;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.UUID;
+
+/**
+ * This class does all the work for setting up and managing Bluetooth
+ * connections with other devices. It has a thread that listens for
+ * incoming connections, a thread for connecting with a device, and a
+ * thread for performing data transmissions when connected.
+ */
+public class BluetoothChatService {
+    // Debugging
+    private static final String TAG = "BluetoothChatService";
+
+    // Name for the SDP record when creating server socket
+    private static final String NAME_SECURE = "BluetoothChatSecure";
+    private static final String NAME_INSECURE = "BluetoothChatInsecure";
+
+    // Unique UUID for this application
+    private static final UUID MY_UUID_SECURE =
+            UUID.fromString(/*"fa87c0d0-afac-11de-8a39-0800200c9a66"*/"00001101-0000-1000-8000-00805F9B34FB");
+    private static final UUID MY_UUID_INSECURE =
+            UUID.fromString(/*"8ce255c0-200a-11e0-ac64-0800200c9a66"*/"00001101-0000-1000-8000-00805F9B34FB");
+
+    // Member fields
+    private final BluetoothAdapter mAdapter;
+    private final Handler mHandler;
+    private AcceptThread mSecureAcceptThread;
+    private AcceptThread mInsecureAcceptThread;
+    private ConnectThread mConnectThread;
+    private ConnectedThread mConnectedThread;
+    private int mState;
+    private Context mGuestContext = null;
+    private BluetoothChatServiceCallback mCallback = null;
+
+    // Constants that indicate the current connection state
+    public static final int STATE_NONE = 0;       // we're doing nothing
+    public static final int STATE_LISTEN = 1;     // now listening for incoming connections
+    public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
+    public static final int STATE_CONNECTED = 3;  // now connected to a remote device
+
+    /**
+     * Constructor. Prepares a new BluetoothChat session.
+     *
+     * @param context The UI Activity Context
+     * @param handler A Handler to send messages back to the UI Activity
+     */
+    public BluetoothChatService(Context context, Handler handler) {
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        mState = STATE_NONE;
+        mHandler = handler;
+        mGuestContext = context;
+    }
+    
+    public void RegisterDataCallback(BluetoothChatServiceCallback cb) {
+        mCallback = cb;
+    }
+    
+    /**
+     * Set the current state of the chat connection
+     *
+     * @param state An integer defining the current connection state
+     */
+    private synchronized void setState(int state) {
+        Log.d(TAG, "setState() " + mState + " -> " + state);
+        mState = state;
+
+        // Give the new state to the Handler so the UI Activity can update
+        mHandler.obtainMessage(Constants.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();
+    }
+
+    /**
+     * Return the current connection state.
+     */
+    public synchronized int getState() {
+        return mState;
+    }
+
+    /**
+     * Start the chat service. Specifically start AcceptThread to begin a
+     * session in listening (server) mode. Called by the Activity onResume()
+     */
+    public synchronized void start() {
+        Log.d(TAG, "start");
+
+        // Cancel any thread attempting to make a connection
+        if (mConnectThread != null) {
+            mConnectThread.cancel();
+            mConnectThread = null;
+        }
+
+        // Cancel any thread currently running a connection
+        if (mConnectedThread != null) {
+            mConnectedThread.cancel();
+            mConnectedThread = null;
+        }
+
+        setState(STATE_NONE);
+
+        // Start the thread to listen on a BluetoothServerSocket
+        /*if (mSecureAcceptThread == null) {
+            mSecureAcceptThread = new AcceptThread(true);
+            mSecureAcceptThread.start();
+        }*/
+        /*if (mInsecureAcceptThread == null) {
+            mInsecureAcceptThread = new AcceptThread(false);
+            mInsecureAcceptThread.start();
+        }*/
+    }
+
+    /**
+     * Start the ConnectThread to initiate a connection to a remote device.
+     *
+     * @param device The BluetoothDevice to connect
+     * @param secure Socket Security type - Secure (true) , Insecure (false)
+     */
+    public synchronized void connect(BluetoothDevice device, boolean secure) {
+        Log.d(TAG, "connect to: " + device);
+
+        // Cancel any thread attempting to make a connection
+        if (mState == STATE_CONNECTING) {
+            if (mConnectThread != null) {
+                mConnectThread.cancel();
+                mConnectThread = null;
+            }
+        }
+
+        // Cancel any thread currently running a connection
+        if (mConnectedThread != null) {
+            mConnectedThread.cancel();
+            mConnectedThread = null;
+        }
+
+        // Start the thread to connect with the given device
+        mConnectThread = new ConnectThread(device, secure);
+        mConnectThread.start();
+        setState(STATE_CONNECTING);
+    }
+
+    public void connect(String address, boolean secure) {
+        if (address != null) {
+            BluetoothDevice device = mAdapter.getRemoteDevice(address);
+            connect(device, secure);
+        }
+    }
+
+    /**
+     * Start the ConnectedThread to begin managing a Bluetooth connection
+     *
+     * @param socket The BluetoothSocket on which the connection was made
+     * @param device The BluetoothDevice that has been connected
+     */
+    public synchronized void connected(BluetoothSocket socket, BluetoothDevice
+            device, final String socketType) {
+        Log.d(TAG, "connected, Socket Type:" + socketType + " class: " + device.getBluetoothClass().toString() +  " name: " + device.getName() + " bdaddr: " + device.getAddress());
+
+        // Cancel the thread that completed the connection
+        if (mConnectThread != null) {
+            mConnectThread.cancel();
+            mConnectThread = null;
+        }
+
+        // Cancel any thread currently running a connection
+        if (mConnectedThread != null) {
+            mConnectedThread.cancel();
+            mConnectedThread = null;
+        }
+
+        // Cancel the accept thread because we only want to connect to one device
+        if (mSecureAcceptThread != null) {
+            mSecureAcceptThread.cancel();
+            mSecureAcceptThread = null;
+        }
+        if (mInsecureAcceptThread != null) {
+            mInsecureAcceptThread.cancel();
+            mInsecureAcceptThread = null;
+        }
+
+        // Send the name of the connected device back to the UI Activity
+        Message msg = mHandler.obtainMessage(Constants.MESSAGE_DEVICE_NAME);
+        Bundle bundle = new Bundle();
+        bundle.putString(Constants.DEVICE_NAME, device.getName());
+        msg.setData(bundle);
+        mHandler.sendMessage(msg);
+
+        setState(STATE_CONNECTED);
+
+        // Start the thread to manage the connection and perform transmissions
+        mConnectedThread = new ConnectedThread(socket, socketType);
+        mConnectedThread.start();
+    }
+
+    /**
+     * Stop all threads
+     */
+    public synchronized void stop() {
+        Log.d(TAG, "stop");
+
+        if (mConnectThread != null) {
+            mConnectThread.cancel();
+            mConnectThread = null;
+        }
+
+        if (mConnectedThread != null) {
+            mConnectedThread.cancel();
+            mConnectedThread = null;
+        }
+
+        if (mSecureAcceptThread != null) {
+            mSecureAcceptThread.cancel();
+            mSecureAcceptThread = null;
+        }
+
+        if (mInsecureAcceptThread != null) {
+            mInsecureAcceptThread.cancel();
+            mInsecureAcceptThread = null;
+        }
+
+        mGuestContext = null;
+        mCallback = null;
+
+        setState(STATE_NONE);
+    }
+
+    /**
+     * Write to the ConnectedThread in an unsynchronized manner
+     *
+     * @param out The bytes to write
+     * @see ConnectedThread#write(byte[])
+     */
+    public void write(byte[] out) {
+        // Create temporary object
+        ConnectedThread r;
+        // Synchronize a copy of the ConnectedThread
+        synchronized (this) {
+            if (mState != STATE_CONNECTED) return;
+            r = mConnectedThread;
+        }
+        // Perform the write unsynchronized
+        r.write(out);
+    }
+
+    /**
+     * Indicate that the connection attempt failed and notify the UI Activity.
+     */
+    private void connectionFailed() {
+        // Send a failure message back to the Activity
+        Message msg = mHandler.obtainMessage(Constants.MESSAGE_TOAST);
+        Bundle bundle = new Bundle();
+        bundle.putString(Constants.TOAST, "Unable to connect device");
+        msg.setData(bundle);
+        mHandler.sendMessage(msg);
+
+        // Start the service over to restart listening mode
+        BluetoothChatService.this.start();
+    }
+
+    /**
+     * Indicate that the connection was lost and notify the UI Activity.
+     */
+    private void connectionLost() {
+        // Send a failure message back to the Activity
+        Message msg = mHandler.obtainMessage(Constants.MESSAGE_TOAST);
+        Bundle bundle = new Bundle();
+        bundle.putString(Constants.TOAST, "Device connection was lost");
+        msg.setData(bundle);
+        mHandler.sendMessage(msg);
+
+        // Start the service over to restart listening mode
+        BluetoothChatService.this.start();
+    }
+
+    /**
+     * This thread runs while listening for incoming connections. It behaves
+     * like a server-side client. It runs until a connection is accepted
+     * (or until cancelled).
+     */
+    private class AcceptThread extends Thread {
+        // The local server socket
+        private final BluetoothServerSocket mmServerSocket;
+        private String mSocketType;
+
+        public AcceptThread(boolean secure) {
+            BluetoothServerSocket tmp = null;
+            mSocketType = secure ? "Secure" : "Insecure";
+
+            // Create a new listening server socket
+            try {
+                if (secure) {
+                    tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE,
+                            MY_UUID_SECURE);
+                } else {
+                    tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(
+                            NAME_INSECURE, MY_UUID_INSECURE);
+                }
+            } catch (IOException e) {
+                Log.e(TAG, "Socket Type: " + mSocketType + " listen() failed", e);
+            }
+            mmServerSocket = tmp;
+        }
+
+        public void run() {
+            Log.d(TAG, "Socket Type: " + mSocketType +
+                    " BEGIN mAcceptThread " + this);
+
+            BluetoothSocket socket = null;
+
+            // Listen to the server socket if we're not connected
+            while (mState != STATE_CONNECTED && mmServerSocket != null) {
+                try {
+                    // This is a blocking call and will only return on a
+                    // successful connection or an exception
+                    socket = mmServerSocket.accept();
+                } catch (IOException e) {
+                    Log.e(TAG, "Socket Type: " + mSocketType + "accept() failed", e);
+                    break;
+                }
+
+                // If a connection was accepted
+                if (socket != null) {
+                    synchronized (BluetoothChatService.this) {
+                        switch (mState) {
+                            case STATE_LISTEN:
+                            case STATE_CONNECTING:
+                                // Situation normal. Start the connected thread.
+                                connected(socket, socket.getRemoteDevice(),
+                                        mSocketType);
+                                break;
+                            case STATE_NONE:
+                            case STATE_CONNECTED:
+                                // Either not ready or already connected. Terminate new socket.
+                                try {
+                                    socket.close();
+                                } catch (IOException e) {
+                                    Log.e(TAG, "Could not close unwanted socket", e);
+                                }
+                                break;
+                        }
+                    }
+                }
+            }
+            Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType);
+
+        }
+
+        public void cancel() {
+            Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this);
+            try {
+                if (mmServerSocket != null)
+                    mmServerSocket.close();
+            } catch (IOException e) {
+                Log.e(TAG, "Socket Type" + mSocketType + "close() of server failed", e);
+            }
+        }
+    }
+
+
+    /**
+     * This thread runs while attempting to make an outgoing connection
+     * with a device. It runs straight through; the connection either
+     * succeeds or fails.
+     */
+    private class ConnectThread extends Thread {
+        private final BluetoothSocket mmSocket;
+        private final BluetoothDevice mmDevice;
+        private String mSocketType;
+
+        public ConnectThread(BluetoothDevice device, boolean secure) {
+            mmDevice = device;
+            BluetoothSocket tmp = null;
+            mSocketType = secure ? "Secure" : "Insecure";
+
+            // Get a BluetoothSocket for a connection with the
+            // given BluetoothDevice
+            try {
+                if (secure) {
+                    tmp = device.createRfcommSocketToServiceRecord(
+                            MY_UUID_SECURE);
+                } else {
+                    tmp = device.createInsecureRfcommSocketToServiceRecord(
+                            MY_UUID_INSECURE);
+                }
+            } catch (IOException e) {
+                Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);
+            }
+            mmSocket = tmp;
+        }
+
+        public void run() {
+            Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType);
+
+            // Always cancel discovery because it will slow down a connection
+            mAdapter.cancelDiscovery();
+
+            // Make a connection to the BluetoothSocket
+            try {
+                // This is a blocking call and will only return on a
+                // successful connection or an exception
+                mmSocket.connect();
+            } catch (IOException e) {
+                // Close the socket
+                try {
+                    mmSocket.close();
+                } catch (IOException e2) {
+                    Log.e(TAG, "unable to close() " + mSocketType +
+                            " socket during connection failure", e2);
+                }
+                connectionFailed();
+                return;
+            }
+
+            // Reset the ConnectThread because we're done
+            synchronized (BluetoothChatService.this) {
+                mConnectThread = null;
+            }
+
+            // Start the connected thread
+            connected(mmSocket, mmDevice, mSocketType);
+        }
+
+        public void cancel() {
+            try {
+                mmSocket.close();
+            } catch (IOException e) {
+                Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e);
+            }
+        }
+    }
+
+    /**
+     * This thread runs during a connection with a remote device.
+     * It handles all incoming and outgoing transmissions.
+     */
+    private class ConnectedThread extends Thread {
+        private final BluetoothSocket mmSocket;
+        private final InputStream mmInStream;
+        private final OutputStream mmOutStream;
+
+        public ConnectedThread(BluetoothSocket socket, String socketType) {
+            Log.d(TAG, "create ConnectedThread: " + socketType);
+            mmSocket = socket;
+            InputStream tmpIn = null;
+            OutputStream tmpOut = null;
+
+            // Get the BluetoothSocket input and output streams
+            try {
+                tmpIn = socket.getInputStream();
+                tmpOut = socket.getOutputStream();
+            } catch (IOException e) {
+                Log.e(TAG, "temp sockets not created", e);
+            }
+
+            mmInStream = tmpIn;
+            mmOutStream = tmpOut;
+        }
+
+        public void run() {
+            Log.i(TAG, "BEGIN mConnectedThread");
+            // Keep listening to the InputStream while connected
+            while (mState == STATE_CONNECTED) {
+                try {
+                    byte[] buffer = new byte[4096];
+                    int bytes;
+                    // Read from the InputStream
+                    bytes = mmInStream.read(buffer);
+
+                    if (mCallback != null) {
+                        mCallback.iDataComeIn(buffer, bytes);
+                    } else {
+                        // Send the obtained bytes to the UI Activity
+                    mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, buffer)
+                            .sendToTarget();
+                    }
+                } catch (IOException e) {
+                    Log.e(TAG, "disconnected", e);
+                    connectionLost();
+                    // Start the service over to restart listening mode
+//                    BluetoothChatService.this.start();
+                    break;
+                }
+            }
+
+            Log.i(TAG, "END mConnectedThread");
+        }
+
+        /**
+         * Write to the connected OutStream.
+         *
+         * @param buffer The bytes to write
+         */
+        public void write(byte[] buffer) {
+            try {
+                mmOutStream.write(buffer);
+
+                // Share the sent message back to the UI Activity
+//                mHandler.obtainMessage(Constants.MESSAGE_WRITE, -1, -1, buffer)
+//                        .sendToTarget();
+            } catch (IOException e) {
+                Log.e(TAG, "Exception during write", e);
+            }
+        }
+
+        public void cancel() {
+            try {
+                mmSocket.close();
+            } catch (IOException e) {
+                Log.e(TAG, "close() of connect socket failed", e);
+            }
+        }
+    }
+}
diff --git a/lib/src/main/java/com/anyun/exam/lib/util/BluetoothChatServiceCallback.java b/lib/src/main/java/com/anyun/exam/lib/util/BluetoothChatServiceCallback.java
new file mode 100644
index 0000000..938b628
--- /dev/null
+++ b/lib/src/main/java/com/anyun/exam/lib/util/BluetoothChatServiceCallback.java
@@ -0,0 +1,5 @@
+package com.anyun.exam.lib.util;
+
+public interface BluetoothChatServiceCallback {
+    void iDataComeIn(byte[] data, int length);
+}
diff --git a/lib/src/main/java/com/anyun/exam/lib/util/ClsUtils.java b/lib/src/main/java/com/anyun/exam/lib/util/ClsUtils.java
new file mode 100644
index 0000000..97625c5
--- /dev/null
+++ b/lib/src/main/java/com/anyun/exam/lib/util/ClsUtils.java
@@ -0,0 +1,138 @@
+package com.anyun.exam.lib.util;
+
+/************************************ 钃濈墮閰嶅鍑芥暟 * **************/
+
+import android.bluetooth.BluetoothDevice;
+import android.util.Log;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class ClsUtils   
+{
+    /**
+     * 涓庤澶囬厤瀵� 鍙傝�冩簮鐮侊細platform/packages/apps/Settings.git
+     * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
+     */
+    static public boolean createBond(Class btClass, BluetoothDevice btDevice)
+    throws Exception
+    {  
+        Method createBondMethod = btClass.getMethod("createBond");
+        Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice);
+        return returnValue.booleanValue();  
+    }
+
+    /**
+     * 涓庤澶囪В闄ら厤瀵� 鍙傝�冩簮鐮侊細platform/packages/apps/Settings.git
+     * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
+     */
+    static public boolean removeBond(Class<?> btClass, BluetoothDevice btDevice)
+            throws Exception
+    {  
+        Method removeBondMethod = btClass.getMethod("removeBond");
+        Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice);
+        return returnValue.booleanValue();  
+    }  
+   
+    static public boolean setPin(Class<? extends BluetoothDevice> btClass, BluetoothDevice btDevice,
+                                 String str) throws Exception
+    {  
+        try  
+        {  
+            Method removeBondMethod = btClass.getDeclaredMethod("setPin",
+                    new Class[]
+                    {byte[].class});  
+            Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice,
+                    new Object[]
+                    {str.getBytes()});  
+            Log.e("returnValue", "" + returnValue);
+        }  
+        catch (SecurityException e)
+        {  
+            // throw new RuntimeException(e.getMessage());  
+            e.printStackTrace();  
+        }  
+        catch (IllegalArgumentException e)
+        {  
+            // throw new RuntimeException(e.getMessage());  
+            e.printStackTrace();  
+        }  
+        catch (Exception e)
+        {  
+            // TODO Auto-generated catch block  
+            e.printStackTrace();  
+        }  
+        return true;  
+   
+    }
+
+    // 鍙栨秷鐢ㄦ埛杈撳叆
+    static public boolean cancelPairingUserInput(Class<?> btClass,
+                                                 BluetoothDevice device)  throws Exception
+    {  
+        Method createBondMethod = btClass.getMethod("cancelPairingUserInput");
+//        cancelBondProcess(btClass, device);
+        Boolean returnValue = (Boolean) createBondMethod.invoke(device);
+        return returnValue.booleanValue();  
+    }
+
+    // 鍙栨秷閰嶅
+    static public boolean cancelBondProcess(Class<?> btClass,
+                                            BluetoothDevice device)
+   
+    throws Exception
+    {  
+        Method createBondMethod = btClass.getMethod("cancelBondProcess");
+        Boolean returnValue = (Boolean) createBondMethod.invoke(device);
+        return returnValue.booleanValue();  
+    }
+
+    //纭閰嶅
+    
+    static public void setPairingConfirmation(Class<?> btClass, BluetoothDevice device, boolean isConfirm)throws Exception
+    {
+    	Method setPairingConfirmation = btClass.getDeclaredMethod("setPairingConfirmation",boolean.class);
+    	setPairingConfirmation.invoke(device,isConfirm);
+    }
+    
+   
+    /** 
+     * 
+     * @param clsShow 
+     */  
+    static public void printAllInform(Class clsShow)
+    {  
+        try  
+        {
+            // 鍙栧緱鎵�鏈夋柟娉�
+            Method[] hideMethod = clsShow.getMethods();
+            int i = 0;  
+            for (; i < hideMethod.length; i++)  
+            {  
+                Log.e("method name", hideMethod[i].getName() + ";and the i is:"
+                        + i);  
+            }
+            // 鍙栧緱鎵�鏈夊父閲�
+            Field[] allFields = clsShow.getFields();
+            for (i = 0; i < allFields.length; i++)  
+            {  
+                Log.e("Field name", allFields[i].getName());
+            }
+        }  
+        catch (SecurityException e)
+        {  
+            // throw new RuntimeException(e.getMessage());  
+            e.printStackTrace();  
+        }  
+        catch (IllegalArgumentException e)
+        {  
+            // throw new RuntimeException(e.getMessage());  
+            e.printStackTrace();  
+        }  
+        catch (Exception e)
+        {  
+            // TODO Auto-generated catch block  
+            e.printStackTrace();  
+        }  
+    }  
+}
diff --git a/lib/src/main/java/com/anyun/exam/lib/util/Constants.java b/lib/src/main/java/com/anyun/exam/lib/util/Constants.java
new file mode 100644
index 0000000..a1e52bd
--- /dev/null
+++ b/lib/src/main/java/com/anyun/exam/lib/util/Constants.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.anyun.exam.lib.util;
+
+/**
+ * Defines several constants used between {@link BluetoothChatService} and the UI.
+ */
+public interface Constants {
+
+    // Message types sent from the BluetoothChatService Handler
+    public static final int MESSAGE_STATE_CHANGE = 1;
+    public static final int MESSAGE_READ = 2;
+    public static final int MESSAGE_WRITE = 3;
+    public static final int MESSAGE_DEVICE_NAME = 4;
+    public static final int MESSAGE_TOAST = 5;
+
+    public static final int MESSAGE_BLUETOOTH_STATUS = 6;
+    public static final int MESSAGE_BLUETOOTH_FOUND = 7;
+    public static final int MESSAGE_BLUETOOTH_DISCOVERY_FINISHED = 8;
+
+    // Key names received from the BluetoothChatService Handler
+    public static final String DEVICE_NAME = "device_name";
+    public static final String DEVICE_ADDRESS = "device_address";
+    public static final String TOAST = "toast";
+
+}

--
Gitblit v1.8.0