Android 藍牙短信功能開發(fā)

Android藍牙短信功能開發(fā)


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

文檔目錄說明


以下開發(fā)基于Android 9.0 車機版本痊剖,其他版本或存在不同

一、藍牙短信協(xié)議規(guī)范


相關藍牙協(xié)議:MAP(MESSAGE ACCESS PROFILE):藍牙短信訪問協(xié)議規(guī)范垒玲。

借助MAP協(xié)議規(guī)范陆馁,可在車機上通過連接的遠程設備收發(fā)短信。目前不會將短信內(nèi)容存儲在 IVI 本地存儲空間合愈,而是每當連接的遠程設備收到短信時叮贩,IVI 會接收相應短信并對其進行解析,然后在 intent 中廣播消息內(nèi)容佛析,應用便可收到相應內(nèi)容益老。

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

協(xié)議SDK代碼在源碼內(nèi)的路徑:frameworks/base/core/java/android/bluetooth/BluetoothMapClient.java

協(xié)議服務代碼在源碼內(nèi)的路徑:packages/apps/Bluetooth/src/com/android/bluetooth/mapclient/MapClientService.java

二膘茎、協(xié)議SDK文件接口說明

接口名 描述
connect 連接指定設備
disconnect 斷開指定設備
isConnected 判斷指定設備是否連接桃纯,連接則返回true酷誓,否則false
getConnectedDevices 獲有已連接設備列表
getDevicesMatchingConnectionStates 獲得與指定狀態(tài)匹配的設備
getConnectionState 獲得指定設備的連接狀態(tài)
setPriority 設置設備MAP協(xié)議的優(yōu)先級
getPriority 獲得設備MAP協(xié)議的優(yōu)先級
sendMessage 使用指定設備發(fā)送消息至指定的聯(lián)系人
getUnreadMessages 獲得未讀消息

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

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

三、藍牙MapClient協(xié)議支持

若需要車機設備與支持MAP的藍牙設備連接后驮配,可進行短信收發(fā)娘扩,那么我們車機系統(tǒng)就需要在藍牙配對時支持MapClient協(xié)議規(guī)范,那么我們需要修改或overlay frameworks/base/core/res/res/values/config.xml內(nèi)的enable_pbap_pce_profile配置值為true(AutoMotive內(nèi)該值默認overlay為true)壮锻,這樣我們在進行藍牙設備連接時才會將MapClientProfile協(xié)議加入到可連接的藍牙協(xié)議規(guī)范列表內(nèi)琐旁,具體的代碼如下:
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為電話簿訪問協(xié)議規(guī)范
            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);
                }
            }
            ....
                
    }
    ....        
}

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

勾選之后就會進行藍牙短信協(xié)議的連接猜绣。

MapClient連接設備狀態(tài)監(jiān)聽

MapClient設備連接狀態(tài)變化時灰殴,會發(fā)送BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED廣播,我們需要監(jiān)聽該廣播掰邢,并記錄當前連接的藍牙設備牺陶,以備后續(xù)的未讀短信獲取短信發(fā)送功能的開發(fā),具體的代碼如下:

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

    // MapClient設備連接狀態(tài)變化廣播注冊
    IntentFilter filter = new IntentFilter();
    filter.addAction(BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
            BtServiceApplication.getApplication().registerReceiver(mBroadcastReceiver, filter);
            
    // MapClient設備連接狀態(tài)變化廣播監(jiān)聽
    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;
                }
            }
            }
        }
    };
            

四辣之、藍牙短信接收功能開發(fā)

根據(jù)官方協(xié)議來看掰伸,目前可支持的藍牙短信接收功能有如下兩個:

  1. 獲取所有未讀短信;
  2. 實時短信接收怀估;
    針對已讀短信僅通過現(xiàn)有的協(xié)議還無法實現(xiàn)狮鸭,后續(xù)有機會再繼續(xù)研究。
    根據(jù)協(xié)議規(guī)范說明可知多搀,藍牙短信收實際上是通過廣播的形式進行接收的歧蕉,所以我們只需要在代碼監(jiān)聽對應的廣播信息,解析內(nèi)部的短信內(nèi)容即可康铭,實現(xiàn)代碼如下:
    // 短信接收廣播注冊
    IntentFilter filter = new IntentFilter();
    filter.addAction(BluetoothMapClient.ACTION_MESSAGE_RECEIVED);
    BtServiceApplication.getApplication().registerReceiver(mBroadcastReceiver, filter);
     
    // 短信接收廣播監(jiān)聽
    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)) {
                // 收到短信
                // 獲得發(fā)送者的Uri
                String senderUri = intent.getStringExtra(BluetoothMapClient.EXTRA_SENDER_CONTACT_URI);
                if (senderUri == null) {
                    senderUri = "<null>";
                }
                // 獲得發(fā)送者名稱
                String senderName = intent.getStringExtra(BluetoothMapClient.EXTRA_SENDER_CONTACT_NAME);
                if (senderName == null) {
                    senderName = "<null>";
                }
                
                // 獲得短息內(nèi)容
                String message = intent.getStringExtra(android.content.Intent.EXTRA_TEXT);
                // 獲得短信接受的藍牙設備
                String bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            }
        }
    };

通過以上代碼即可實現(xiàn)實時短信的接收惯退,若要獲得未讀短信,則需要調(diào)用BluetoothMapClientgetUnreadMessages接口觸發(fā)對應的藍牙設備將未讀短信發(fā)送至我們的車機上从藤,接收處理的上面一致催跪,都是通過廣播來進行接收,就不再闡述呛哟。

針對BluetoothMapClient的對象創(chuàng)建和getUnreadMessages的調(diào)用可參考如下代碼:

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) {
            // 觸發(fā)同步
            BluetoothDevice remoteDevice;
            try {
                // 獲得對應Address的藍牙遠程設備
                remoteDevice = mAdapter.getRemoteDevice(address);
            } catch (java.lang.IllegalArgumentException e) {
                return;
            }

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

五叠荠、藍牙短信發(fā)送功能開發(fā)

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

    // 藍牙短信發(fā)送成功廣播注冊
    IntentFilter filter = new IntentFilter();
    filter.addAction(BluetoothMapClient.ACTION_MESSAGE_SENT_SUCCESSFULLY);
    filter.addAction(BluetoothMapClient.ACTION_MESSAGE_DELIVERED_SUCCESSFULLY);
    BtServiceApplication.getApplication().registerReceiver(mBroadcastReceiver, filter);
        
    // 藍牙短信發(fā)送成功廣播監(jiān)聽
    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");
            }
        }
    };
    

    /**
     * 藍牙短信發(fā)送
     * 
     * @param context   
     * @param address    藍牙設備對應的Address
     * @param recipients 收件人號碼
     * @param message    短信內(nèi)容
     */
    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設置后匿沛,短信發(fā)送成就會有對應的廣播發(fā)送回來扫责,若只需要一個,另一個可直接設置為null逃呼,簡單看了下源碼鳖孤,目前未看到兩個Intent的明顯差異者娱,后續(xù)再細究
            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);
        }
    }
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市苏揣,隨后出現(xiàn)的幾起案子黄鳍,更是在濱河造成了極大的恐慌,老刑警劉巖平匈,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件框沟,死亡現(xiàn)場離奇詭異,居然都是意外死亡增炭,警方通過查閱死者的電腦和手機忍燥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來隙姿,“玉大人梅垄,你說我怎么就攤上這事∈溏瑁” “怎么了队丝?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長欲鹏。 經(jīng)常有香客問我机久,道長,這世上最難降的妖魔是什么貌虾? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任吞加,我火速辦了婚禮,結果婚禮上尽狠,老公的妹妹穿的比我還像新娘衔憨。我一直安慰自己,他們只是感情好袄膏,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布践图。 她就那樣靜靜地躺著,像睡著了一般沉馆。 火紅的嫁衣襯著肌膚如雪码党。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天斥黑,我揣著相機與錄音揖盘,去河邊找鬼。 笑死锌奴,一個胖子當著我的面吹牛兽狭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼箕慧,長吁一口氣:“原來是場噩夢啊……” “哼服球!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起颠焦,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤斩熊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后伐庭,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體粉渠,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年圾另,在試婚紗的時候發(fā)現(xiàn)自己被綠了渣叛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡盯捌,死狀恐怖淳衙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布杰妓,位于F島的核電站秒咨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望梢睛。 院中可真熱鬧,春花似錦识椰、人聲如沸绝葡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽藏畅。三九已至,卻和暖如春功咒,著一層夾襖步出監(jiān)牢的瞬間愉阎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工力奋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留榜旦,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓景殷,卻偏偏與公主長得像溅呢,于是被迫代替她去往敵國和親锉走。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容

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