android使用at命令
① android平台用AT命令發簡訊
需要COM口連接簡訊貓,然後根據簡訊貓中的AT命令來操作。
② android添加藍牙電量
1. HFP 命令AT+IPHONEACCEV
描述:報告耳機的狀態變更
發起者:耳機
格式:AT+IPHONEACCEV=[Number of key/value pairs ],[key1 ],[val1 ],[key2
],[val2 ],…
參數:
Number of key/value pairs :接下來參數的數量
key:被報告狀態變化的類型
1 =電量等級
2 =暫停狀態
val:更改的值
Battery events:0-9之間數字的字元串 A
string value between 『0』 and 『9』.
Dock state: 0 = undocked, 1 = docked.
Example:AT+IPHONEACCEV=1,1,3
2.android framework 修改點
packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
BluetoothAssignedNumbers.APPLE可以隨便用哪個公司的,但注冊廣播時要一致就行。
static {
classInitNative();
型亂磨 VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID = new HashMap();
VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put("+XEVENT",BluetoothAssignedNumbers.PLANTRONICS);
VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put("+ANDROID",BluetoothAssignedNumbers.GOOGLE); 陪絕 +VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put("+XAPL",BluetoothAssignedNumbers.APPLE);
+VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.put("+IPHONEACCEV",BluetoothAssignedNumbers.APPLE);
}
獲取藍牙電量需要向藍牙發送回復的at命令:
private (String atString) {
log("processVendorSpecificAt - atString = " + atString);
// Currently we accept only SET type commands.
int indexOfEqual = atString.indexOf("=");
if (indexOfEqual == -1) {
Log.e(TAG, "processVendorSpecificAt: command type error in " +atString);
return false;
}
String command = atString.substring(0, indexOfEqual);
Integer companyId = VENDOR_SPECIFIC_AT_COMMAND_COMPANY_ID.get(command);
if (companyId == null) {
Log.e(TAG, "processVendorSpecificAt: unsupported command: " +atString);
卜斗 return false;
}
String arg = atString.substring(indexOfEqual + 1);
if (arg.startsWith("?")) {
Log.e(TAG, "processVendorSpecificAt: command type error in " +atString);
return false;
}
Object[] args = generateArgs(arg);
+ if ("+XAPL".equals(command)) {
+ processAtXapl(args);
+ }
(command,
companyId,
BluetoothHeadset.AT_CMD_TYPE_SET,
args,
mCurrentDevice);
atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0);
return true;
}
/**
* Process AT+XAPL AT command
* @param args command arguments after theequal sign
* @param device Remote device that hassent this command
*/
private void processAtXapl(Object[] args){
if (args.length != 2) {
Log.w(TAG, "processAtXapl()args length must be 2: " + String.valueOf(args.length));
return;
}
if (!(args[0] instanceof String) ||!(args[1] instanceof Integer)) {
Log.w(TAG, "processAtXapl()argument types not match");
return;
}
// feature = 2 indicates that wesupport battery level reporting only
Log.d("tsq77","+XAPL=iPhone,");
atResponseStringNative("+XAPL=iPhone," + String.valueOf(2));
}
2.上層app監聽廣播獲取電量
packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothSettings.java
在settings中的藍牙界面中注冊廣播,然後把電量顯示出來。
//aaron
IntentFilter filter=new IntentFilter();
filter.addAction(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
//filter.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY+"."+BluetoothAssignedNumbers.
APPLE);
filter.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY+"."+BluetoothAssignedNumbers.
APPLE);
getActivity().registerReceiver(mIntentReceiver,filter);
Log.i("a", "registerReceiver ");
//end
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if(action.equals(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT)) {
Log.i("a","intent "+intent);
String command=intent.getStringExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD);
if("+IPHONEACCEV".equals(command)) {
Object[] args = (Object[])intent.getSerializableExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS);
if (args.length >= 3&& args[0] instanceof Integer &&((Integer)args[0])*2+1<=args.length) {
for (inti=0;i<((Integer)args[0]);i++) {
if(!(args[i*2+1] instanceof Integer) || !(args[i*2+2] instanceof Integer)) {
continue;
}
if(args[i*2+1].equals(1)) {
floatlevel=(((Integer)args[i*2+2])+1)/10.0f;//獲取的電量百分比
break;
}
}
}
}
}
}
};
③ Android 發送AT指令
1、hardware\ril\reference-ril\Reference-ril.c 中 onRequest() 處理RIL_REQUEST_DIAL 時會 調用requestDial();
2、requestDial()中轉換RIL_REQUEST_DIAL 請求為AT指令;
3、通過at_send_command()下發;
4、經過at_send_command_full,at_send_command_full_nolock ,writeline()寫到設備中。
④ 如何修改android模擬器上的IMEI,IMSI,SIM card serial number
手機使用IMEI和IMSI登錄到GSM網路的,由GSM網路側負責將IMSI和映射成手機號(MSISDN),以及執行相反方向的映射。
(一)、SIM card 號的修改:
SIM card號就是印製在SIM上的一串數字。
讀SIM card號的AT命令為:AT+CRSM=176,12258,0,0,10
因此在andorid模擬其源碼中找到該AT命令——在sim_card.c中:
const char*
asimcard_io( ASimCard sim, const char* cmd )
{
int nn;
#if ENABLE_DYNAMIC_RECORDS
int command, id, p1, p2, p3;
#endif
static const struct { const char* cmd; const char* answer; } answers[] =
{
{ "+CRSM=192,28436,0,0,15", "+CRSM: 144,0," },
{ "+CRSM=176,28436,0,0,20", "+CRSM: 144,0," },
{ "+CRSM=192,28433,0,0,15", "+CRSM: 144,0," },
{ "+CRSM=176,28433,0,0,1", "+CRSM: 144,0,55" },
{ "+CRSM=192,12258,0,0,15", "+CRSM: 144,0," },
{ "+CRSM=176,12258,0,0,10", "+CRSM: 144,0,98101430121181157002" },
...
...
因此用UE二進制方式打開emulator-arm.exe 或 emulator-x86.exe,並搜索字元串「98101430121181157002」,然後將其修改成需要的SIM card號。
比如:
00209a00h: 31 30 00 00 2B 43 52 53 4D 3A 20 31 34 34 2C 30 ; 10..+CRSM: 144,0
00209a10h: 2C 39 38 31 30 31 34 33 30 31 32 31 31 38 31 31 ; ,981014301211811
00209a20h: 35 37 30 30 32 00 2B 43 52 53 4D 3D 31 39 32 2C ; 57002.+CRSM=192,
(二)、IMEI、IMSI號的修改:
Java代碼中獲取手機的IMEI號與ISMI號途徑為:
TelephonyManager manager = (TelephonyManager)getSystemService(TELEPHONY_SERVICE);
String imei = manager.getDeviceId();
String imsi = manager.getSubscriberId();
在android的源碼樹中找到類TelephonyManager的實現:
成員函數getDeviceId:
/**
* Returns the unique device ID, for example, the IMEI for GSM and the MEID
* or ESN for CDMA phones. Return null if device ID is not available.
*
* <p>Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
*/
public String getDeviceId() {
try {
return getSubscriberInfo().getDeviceId();
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
return null;
}
}
成員函數getSubscriberId:
/**
* Returns the unique subscriber ID, for example, the IMSI for a GSM phone.
* Return null if it is unavailable.
* <p>
* Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
*/
public String getSubscriberId() {
try {
return getSubscriberInfo().getSubscriberId();
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
// This could happen before phone restarts e to crashing
return null;
}
}
上面兩個成員函數最終調用共同的一個私有成員函數getSubscriberInfo():
private IPhoneSubInfo getSubscriberInfo() {
// get it each time because that process crashes a lot
return IPhoneSubInfo.Stub.asInterface(ServiceManager.getService("iphonesubinfo"));
}
而上面私有函數getSubscriberInfo獲取的手機IMSI和IMEI號被硬編碼在文件android_modem.c中:
/* the Android GSM stack checks that the operator's name has changed
* when roaming is on. If not, it will not update the Roaming status icon
*
* this means that we need to emulate two distinct operators:
* - the first one for the 'home' registration state, must also correspond
* to the emulated user's IMEI
*
* - the second one for the 'roaming' registration state, must have a
* different name and MCC/MNC
*/
#define OPERATOR_HOME_INDEX 0
#define OPERATOR_HOME_MCC 310
#define OPERATOR_HOME_MNC 260
#define OPERATOR_HOME_NAME "Android"
#define OPERATOR_HOME_MCCMNC STRINGIFY(OPERATOR_HOME_MCC) \
STRINGIFY(OPERATOR_HOME_MNC)
#define OPERATOR_ROAMING_INDEX 1
#define OPERATOR_ROAMING_MCC 310
#define OPERATOR_ROAMING_MNC 295
#define OPERATOR_ROAMING_NAME "TelKila"
#define OPERATOR_ROAMING_MCCMNC STRINGIFY(OPERATOR_ROAMING_MCC) \
STRINGIFY(OPERATOR_ROAMING_MNC)
/* a function used to deal with a non-trivial request */
typedef const char* (*ResponseHandler)(const char* cmd, AModem modem);
static const struct {
const char* cmd; /* command coming from libreference-ril.so, if first
character is '!', then the rest is a prefix only */
const char* answer; /* default answer, NULL if needs specific handling or
if OK is good enough */
ResponseHandler handler; /* specific handler, ignored if 'answer' is not NULL,
NULL if OK is good enough */
} sDefaultResponses[] =
{
/* see onRadioPowerOn() */
{ "%CPHS=1", NULL, NULL },
{ "%CTZV=1", NULL, NULL },
...
{ "!+VTS=", NULL, handleSetDialTone },
{ "+CIMI", OPERATOR_HOME_MCCMNC "000000000", NULL }, /* request internation subscriber identification number */
{ "+CGSN", "000000000000000", NULL }, /* request model version */
{ "+CUSD=2",NULL, NULL }, /* Cancel USSD */
...
/* end of list */
{NULL, NULL, NULL}
};
因此用UE二進制方式打開emulator-arm.exe 或 emulator-x86.exe,並搜索字元串"+CGSN"修改為需要的IMEI號;搜索"+CIMI"修改為需要的IMSI號。需要注意的是 IMSI 號的頭六個數字"310260"不能修改,否則模擬器無法與網路連接。
例如:
001fc700h: 33 00 41 00 48 00 21 2B 56 54 53 3D 00 2B 43 49 ; 3.A.H.!+VTS=.+CI
001fc710h: 4D 49 00 33 31 30 32 36 30 30 30 30 30 30 30 30 ; MI.3102600000000
001fc720h: 30 30 00 2B 43 47 53 4E 00 30 30 30 30 30 30 30 ; 00.+CGSN.0000000
001fc730h: 30 30 30 30 30 30 30 30 00 2B 43 55 53 44 3D 32 ; 00000000.+CUSD=2
⑤ android 怎麼通過藍牙向一個硬體發送AT指令
將16進制的字元串轉換成bytes,通過hexstring2bytes轉換,從而發送指令。
⑥ Android平台到底能不能通過串口發送AT指令呢,急!!!
AT命令(Attention)在手機中,用於對modem(也就是移動模塊)通過串口命令進行操作,處理與語音電話、簡訊和數據。
關於AT命令:
Android系統與AT命令
對於智能手機,AP和BP分離的情況,在AP上的系統通過串口和BP通信是個不錯方式。在Android的源碼中有一個內部包com.android.internal.telephony中有對AT命令的封裝和解析,但這種internal的包開發者不能調用的SDK部分,可以用來封裝ROM。這說明Android對AT command的方式是支持的。
對於Android如何調用AT command
用root登錄命令行,直接對串口進行操作,如echo -e "AT " > /dev/smd0
具體的串口,不同設備會有不同,甚至不一定會提供。這種方式,開發者是可以調用的,通過Runtime.exec直接執行命令行命令,但要求是root,例如echo -e "ATD123456789; " > /dev/smd0,撥打123456789的號碼。
目前最新的AT命令標准發布與2014.6.27,似乎還活得挺滋潤的。但是給出的keywords是UMTS, GSM, command, terminal, LTE這說明CDMA確實很可能不是採用AT命令的方式。