Android 藍牙短信功能開發

Android藍牙短信功能開發


由于我司是做車機中控的,目前需要在車機上實現與藍牙手機相連,并通過藍牙進行短信發送和接收的功能,針對這一功能的實現方式做個簡單的記錄。

文檔目錄說明


以下開發基于Android 9.0 車機版本,其他版本或存在不同

一、藍牙短信協議規范


相關藍牙協議:MAP(MESSAGE ACCESS PROFILE):藍牙短信訪問協議規范。

借助MAP協議規范,可在車機上通過連接的遠程設備收發短信。目前不會將短信內容存儲在 IVI 本地存儲空間,而是每當連接的遠程設備收到短信時,IVI 會接收相應短信并對其進行解析,然后在 intent 中廣播消息內容,應用便可收到相應內容。

要連接到移動設備以收發短信,IVI 必須啟動 MAP 連接。 MapClientService 中的 MAXIMUM_CONNECTED_DEVICES 指定了 IVI 允許同時連接的 MAP 設備數量上限。同時獲得 IVI 和移動設備的授權時每個連接才能傳輸消息。

協議SDK代碼在源碼內的路徑:frameworks/base/core/java/android/bluetooth/BluetoothMapClient.java

協議服務代碼在源碼內的路徑:packages/apps/Bluetooth/src/com/android/bluetooth/mapclient/MapClientService.java

二、協議SDK文件接口說明

接口名 描述
connect 連接指定設備
disconnect 斷開指定設備
isConnected 判斷指定設備是否連接,連接則返回true,否則false
getConnectedDevices 獲有已連接設備列表
getDevicesMatchingConnectionStates 獲得與指定狀態匹配的設備
getConnectionState 獲得指定設備的連接狀態
setPriority 設置設備MAP協議的優先級
getPriority 獲得設備MAP協議的優先級
sendMessage 使用指定設備發送消息至指定的聯系人
getUnreadMessages 獲得未讀消息

其中我們需要重點關注的接口如下:

    /**
     * 向指定的電話號碼發送SMS消息
     * 
     * @param device 藍牙設備
     * @param contacts 聯系人的Uri[]列表
     * @param message y要發送的消息
     * @param sentIntent 發送消息時發出的意圖 SMS消息發送成功將發送{@link #ACTION_MESSAGE_SENT_SUCCESSFULLY} 廣播
     * @param deliveredIntent 消息傳遞時發出的意圖 SMS消息傳遞成功將發送{@link #ACTION_MESSAGE_DELIVERED_SUCCESSFULLY} 廣播
     * @return 如果消息入隊則返回 true,錯誤則返回 false
     */
    public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message,
            PendingIntent sentIntent, PendingIntent deliveredIntent) 
            
    /**
     * 獲取未讀消息.  未讀消息將發送 {@link #ACTION_MESSAGE_RECEIVED} 廣播。
     *
     * @param device 藍牙設備
     * @return 如果消息入隊則返回 true,錯誤則返回 false
     */
    public boolean getUnreadMessages(BluetoothDevice device)
    

三、藍牙MapClient協議支持

若需要車機設備與支持MAP的藍牙設備連接后,可進行短信收發,那么我們車機系統就需要在藍牙配對時支持MapClient協議規范,那么我們需要修改或overlay frameworks/base/core/res/res/values/config.xml內的enable_pbap_pce_profile配置值為true(AutoMotive內該值默認overlay為true),這樣我們在進行藍牙設備連接時才會將MapClientProfile協議加入到可連接的藍牙協議規范列表內,具體的代碼如下:
framework/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java

public class LocalBluetoothProfileManager {
    ....
    private MapClientProfile mMapClientProfile;
    ....
    LocalBluetoothProfileManager(Context context,
            LocalBluetoothAdapter adapter,
            CachedBluetoothDeviceManager deviceManager,
            BluetoothEventManager eventManager) {
            ....
            
            // pbap為電話簿訪問協議規范
            mUsePbapPce = mContext.getResources().getBoolean(R.bool.enable_pbap_pce_profile);
            // MAP Client 通常用于與 PBAP Client 相同的情況
            mUseMapClient = mContext.getResources().getBoolean(R.bool.enable_pbap_pce_profile);
            
            ....
            if (mUseMapClient) {
                if(mMapClientProfile==null){
                    mMapClientProfile = new MapClientProfile(mContext, mLocalAdapter,
                                        mDeviceManager, this);
                    addProfile(mMapClientProfile, MapClientProfile.NAME,
                        BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
                }
            } else {
                if(mMapProfile==null){
                    mMapProfile = new MapProfile(mContext, mLocalAdapter, mDeviceManager, this);
                    addProfile(mMapProfile, MapProfile.NAME,
                        BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
                }
            }
            ....
                
    }
    ....        
}

之后進行連接就可以在車機的藍牙設備詳情頁面看到對應的協議開關選項,如一個Text Messages選項:

勾選之后就會進行藍牙短信協議的連接。

MapClient連接設備狀態監聽

MapClient設備連接狀態變化時,會發送BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED廣播,我們需要監聽該廣播,并記錄當前連接的藍牙設備,以備后續的未讀短信獲取短信發送功能的開發,具體的代碼如下:

    // 記錄當前連接的藍牙設備
    private BluetoothDevice mBluetoothDevice;

    // MapClient設備連接狀態變化廣播注冊
    IntentFilter filter = new IntentFilter();
    filter.addAction(BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
            BtServiceApplication.getApplication().registerReceiver(mBroadcastReceiver, filter);
            
    // MapClient設備連接狀態變化廣播監聽
    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (action.equals(BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED)) {
                if (intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0) == BluetoothProfile.STATE_CONNECTED) {
                    // 設備連接
                    mBluetoothDevice = (BluetoothDevice) intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                } else if(intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0) == BluetoothProfile.STATE_DISCONNECTED){
                    // 設備斷開
                    BluetoothDevice bluetoothDevice = (BluetoothDevice) intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    if(bluetoothDevice!=null && mBluetoothDevice.getAddress().equals(bluetoothDevice.getAddress())){
                    mBluetoothDevice = null;
                }
            }
            }
        }
    };
            

四、藍牙短信接收功能開發

根據官方協議來看,目前可支持的藍牙短信接收功能有如下兩個:

  1. 獲取所有未讀短信;
  2. 實時短信接收;
    針對已讀短信僅通過現有的協議還無法實現,后續有機會再繼續研究。
    根據協議規范說明可知,藍牙短信收實際上是通過廣播的形式進行接收的,所以我們只需要在代碼監聽對應的廣播信息,解析內部的短信內容即可,實現代碼如下:
    // 短信接收廣播注冊
    IntentFilter filter = new IntentFilter();
    filter.addAction(BluetoothMapClient.ACTION_MESSAGE_RECEIVED);
    BtServiceApplication.getApplication().registerReceiver(mBroadcastReceiver, filter);
     
    // 短信接收廣播監聽
    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (action.equals(BluetoothMapClient.ACTION_MESSAGE_RECEIVED)) {
                // 收到短信
                // 獲得發送者的Uri
                String senderUri = intent.getStringExtra(BluetoothMapClient.EXTRA_SENDER_CONTACT_URI);
                if (senderUri == null) {
                    senderUri = "<null>";
                }
                // 獲得發送者名稱
                String senderName = intent.getStringExtra(BluetoothMapClient.EXTRA_SENDER_CONTACT_NAME);
                if (senderName == null) {
                    senderName = "<null>";
                }
                
                // 獲得短息內容
                String message = intent.getStringExtra(android.content.Intent.EXTRA_TEXT);
                // 獲得短信接受的藍牙設備
                String bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            }
        }
    };

通過以上代碼即可實現實時短信的接收,若要獲得未讀短信,則需要調用BluetoothMapClientgetUnreadMessages接口觸發對應的藍牙設備將未讀短信發送至我們的車機上,接收處理的上面一致,都是通過廣播來進行接收,就不再闡述。

針對BluetoothMapClient的對象創建和getUnreadMessages的調用可參考如下代碼:

public class Test {
    private BluetoothMapClient mMapProfile;
    private BluetoothAdapter mAdapter;
    private final int MAP_CLIENT = 18;
    
    class MapServiceListener implements BluetoothProfile.ServiceListener {
        @Override
        public void onServiceConnected(int profile, BluetoothProfile proxy) {
            synchronized (mLock) {
                mMapProfile = (BluetoothMapClient) proxy;
            }
        }

        @Override
        public void onServiceDisconnected(int profile) {
            synchronized (mLock) {
                mMapProfile = null;
            }
        }
    }
    
    public Test(Contexst context){
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        mAdapter.getProfileProxy(mContext, new MapServiceListener(), MAP_CLIENT);
    }


    // 獲得未讀短信
        public void syncUnreadMessages(String address) {
        synchronized (mLock) {
            // 觸發同步
            BluetoothDevice remoteDevice;
            try {
                // 獲得對應Address的藍牙遠程設備
                remoteDevice = mAdapter.getRemoteDevice(address);
            } catch (java.lang.IllegalArgumentException e) {
                return;
            }

            if (mMapProfile != null) {
                // 觸發未讀短信獲取
                boolean isSuccess = mMapProfile.getUnreadMessages(remoteDevice);
            }
        }
    }
}

五、藍牙短信發送功能開發

藍牙短信發送可參考代碼如下(BluetoothMapClient對象創建過程省略):

    // 藍牙短信發送成功廣播注冊
    IntentFilter filter = new IntentFilter();
    filter.addAction(BluetoothMapClient.ACTION_MESSAGE_SENT_SUCCESSFULLY);
    filter.addAction(BluetoothMapClient.ACTION_MESSAGE_DELIVERED_SUCCESSFULLY);
    BtServiceApplication.getApplication().registerReceiver(mBroadcastReceiver, filter);
        
    // 藍牙短信發送成功廣播監聽
    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (action.equals(BluetoothMapClient.ACTION_MESSAGE_SENT_SUCCESSFULLY)) {
                Logcat.d("Message sent successfully");
            } else if (action.equals(BluetoothMapClient.ACTION_MESSAGE_DELIVERED_SUCCESSFULLY)) {
                Logcat.d("Message delivered successfully");
            }
        }
    };
    

    /**
     * 藍牙短信發送
     * 
     * @param context   
     * @param address    藍牙設備對應的Address
     * @param recipients 收件人號碼
     * @param message    短信內容
     */
    private void sendMessage(Context context, String address, Uri[] recipients, String message) {
        BluetoothDevice remoteDevice;
        try {
            remoteDevice = mAdapter.getRemoteDevice(address);
        } catch (java.lang.IllegalArgumentException e) {
            Logcat.d(e.toString());
            return;
        }
        if (mMapProfile != null) {
            Logcat.d("Sending reply");
            if (recipients == null) {
                Logcat.d("Recipients is null");
                return;
            }
            if (address == null) {
                Logcat.d("BluetoothDevice is null");
                return;
            }

            // 以下兩個Intent設置后,短信發送成就會有對應的廣播發送回來,若只需要一個,另一個可直接設置為null,簡單看了下源碼,目前未看到兩個Intent的明顯差異,后續再細究
            mSentIntent = PendingIntent.getBroadcast(context, 0, mSendIntent, PendingIntent.FLAG_ONE_SHOT);
            mDeliveredIntent = PendingIntent.getBroadcast(context, 0, mDeliveryIntent, PendingIntent.FLAG_ONE_SHOT);
            mMapProfile.sendMessage(remoteDevice, recipients, message, mSentIntent, mDeliveredIntent);
        }
    }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,844評論 2 372

推薦閱讀更多精彩內容

  • 藍牙(英語:Bluetooth),一種無線通訊技術標準,用來讓固定與移動設備,在短距離間交換資料,以形成個人局域網...
    仕明同學閱讀 7,630評論 1 17
  • 前言 上一篇主要介紹了部分ESC/POS指令集,包括一些常用的排版指令,打印位圖指令等。另外,還介紹了將圖片轉換成...
    趙夢楠閱讀 3,994評論 6 14
  • 本文介紹了藍牙的概念以及具體的使用步驟. 一.藍牙概念 藍牙2.0為傳統藍牙,傳統藍牙也稱為經典藍牙.藍牙4.0因...
    o惜樂o閱讀 18,126評論 41 59
  • 1.藍牙 短距離無線電通訊 基礎來自于跳頻擴頻(FHSS)技術藍牙是一種支持設備短距離通信的無線電技術,能在包括...
    南方的東東閱讀 527評論 0 0
  • iOS藍牙4.0 BLE開發總結 非自寫,轉過來做個筆記,原文來自這里[http://mideablog.gith...
    突刺刺閱讀 1,138評論 0 0