今天我在这里讨论的Android设备唯一标识,是面向在市面上面出售的机型。通过找到这些机器中的唯一标识部分,来区别不同的手机硬件。而对于工程机,我们则没有办法进行很好的界定,因为其中很多参数值并非是标准的。另外,对于Android模拟器,则更是没有办法找到唯一标识。因为对于应用程序获取到的参数值,几乎都是通过Android SDK提供的接口,或者是通过adb shell命令来获取的。而任何的Android SDK的接口理论上在模拟器上面都是可以仿制(提供假的接口,返回自己希望得到的数据)的。而工程机在很大程度上,也可以做到这点。因为这两者都很大程度上基于更改源码来达成。
因此我们在这里仅讨论市面上出售的手机。
1.如何区分真机和模拟器
(1)通过IMEI(移动设备国际身份码)来识别
该值我们可以再手机拨号盘中输入*#06*来查看,默认情况下,模拟器上该值为15个0,区别于真机。真机则为手机电池下面的标签标准的值。
获取方法:
// Get IMEI
public static String getIMEI(Context context) {
String imei = null;
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if (tm != null) {
imei = tm.getDeviceId();
}
Helper.showLog(TAG, "getIMEI:" + imei);
return imei;
}
需要申请权限
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
(2)通过Model (机型)来识别
每个厂商生产的手机,都会对这个Model进行定义,如Coolpad 7019,YPY_S450等,都是机型,代表了不同手机公司的不同机型。而模拟器的该值一般是sdk或者google_sdk。当然可能其他的手机平台商提供的model名称不同,如高通和MTK的模拟器名称可能不相同。因此可以收集这些常见手机平台的sdk名称来做排除。
获取方法:
// Get Model
public static String getModel() {
String model = android.os.Build.MODEL;
Helper.showLog(TAG, "getModel:" + model);
return model;
}
(3)Mac Address来识别
这就是我们常说的WIFI的物理地址,该值理论上每台手机各不相同(当然可以通过平台公司提供的工具来自行改写),但是在模拟器上面,默认情况下没有硬件设备,因此不管是通过SDK提供的API,还是shell命令,获取的都为空值。
//Get Mac Address by Linux command
public static String getMacAddressByCommand() {
String macSerial = null;
String str = "";
try {
Process pp = Runtime.getRuntime().exec("cat /sys/class/net/wlan0/address ");
InputStreamReader ir = new InputStreamReader(pp.getInputStream());
LineNumberReader input = new LineNumberReader(ir);
for (; null != str;) {
str = input.readLine();
if (str != null) {
macSerial = str.trim();
break;
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
Helper.showLog(TAG, "getMacAddressByCommand:" + macSerial);
return macSerial;
}
// Get Mac Address by Java API
public static String getMacAddressByAPI(Context context) {
WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
String macAddressAPI = null;
if (wifi != null) {
//Try to get mac address firstly,if empty,need switch on WIFI
try {
if (macAddressAPI == null) {
WifiInfo info = wifi.getConnectionInfo();
macAddressAPI = info.getMacAddress();
}
} catch (Exception e) {}
if(macAddressAPI == null){
Helper.showLog(TAG, "getMacAddressByAPI:WIFI not init,need to switch on WIFI");
int state =wifi.getWifiState();
boolean isWifiEnabled = (state == WifiManager.WIFI_STATE_ENABLED) || (state == WifiManager.WIFI_STATE_ENABLING);
if (!isWifiEnabled) {
wifi.setWifiEnabled(true);
}
try {
if (macAddressAPI == null) {
WifiInfo info = wifi.getConnectionInfo();
macAddressAPI = info.getMacAddress();
}
} catch (Exception e) {
} finally {
if (!isWifiEnabled) {
wifi.setWifiEnabled(false);
}
}
}else{
Helper.showLog(TAG, "getMacAddressByAPI:no need to switch on WIFI");
}
}
Helper.showLog(TAG, "getMacAddressByAPI:" + macAddressAPI);
return macAddressAPI;
}
需要申请 权限
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
我在这里尝试使用了两种方法的组合去获取mac地址,第一种方法是使用shell命令读取设备节点文件,获取mac地址,但是该值仅在WIFI开启的时候有效。而第二种方法则可以在WIFI关闭的情况下读取,这里也有一个限制,就是WIFI在开机后,有被开启过才行(虽然开启后再关闭也是可以的)。而且WIFI被动开启的期间也是没有办法读取到Mac地址的,因此开启的工作需要在前面完成,因此可以在程序加载的时候就去尝试开机一次WIFI,然后关闭掉。后面需要使用Mac地址的时候,就可以直接读取了。
对于Mac地址的获取,网上海有通过调用Linux的busybox来获取,参考代码如下
/*
*****************************************************************
* 子函数:获得本地MAC地址
*****************************************************************
*/
public String getMacAddress(){
String result = "";
String Mac = "";
result = callCmd("busybox ifconfig","HWaddr");
//如果返回的result == null,则说明网络不可取
if(result==null){
return "网络出错,请检查网络";
}
//对该行数据进行解析
//例如:eth0 Link encap:Ethernet HWaddr 00:16:E8:3E:DF:67
if(result.length()>0 && result.contains("HWaddr")==true){
Mac = result.substring(result.indexOf("HWaddr")+6, result.length()-1);
Log.i("test","Mac:"+Mac+" Mac.length: "+Mac.length());
if(Mac.length()>1){
Mac = Mac.replaceAll(" ", "");
result = "";
String[] tmp = Mac.split(":");
for(int i = 0;i<tmp.length;++i){
result +=tmp[i];
}
}
Log.i("test",result+" result.length: "+result.length());
}
return result;
}
public String callCmd(String cmd,String filter) {
String result = "";
String line = "";
try {
Process proc = Runtime.getRuntime().exec(cmd);
InputStreamReader is = new InputStreamReader(proc.getInputStream());
BufferedReader br = new BufferedReader (is);
//执行命令cmd,只取结果中含有filter的这一行
while ((line = br.readLine ()) != null && line.contains(filter)== false) {
//result += line;
Log.i("test","line: "+line);
}
result = line;
Log.i("test","result: "+result);
}
catch(Exception e) {
e.printStackTrace();
}
return result;
}
但是需要设备支持busybox,但是默认的手机貌似都不支持,需要自己root后填入该工具,这个不适合开发应用。
另外还有一些方案,参照http://www.cnblogs.com/Amandaliu/archive/2011/11/06/2238177.html