在開發中咱們常常會用到一些手機系統信息,如IMEI、IMSI、MAC、SERIALNO等等,下面給出這些信息的一些獲取方法html
這是android系統屬性的控制類,控制系統的各屬性值,用於記錄和管理系統的配置和狀態。每一個屬性都以pair(key/value)的形式存儲,能夠在adb shell下使用getprop查看系統屬性(列出部分):java
[dhcp.wlan0.dns1]: [172.26.210.1]
[dhcp.wlan0.dns2]: [218.2.135.1]
[dhcp.wlan0.gateway]: [172.26.210.1]
[dhcp.wlan0.ipaddress]: [172.26.210.24]
[dhcp.wlan0.mask]: [255.255.0.0]
[ro.build.display.id]: [G750-T01-CM11-NiuNai]
[ro.product.model]: [G750-T01]
[ro.product.locale.language]: [zh]
[ro.product.locale.region]: [CN]
[ro.serialno]: [DQBALFPNNBKRIVSO]
[gsm.serial]: [BY2PJU1496058698]
SystemProperties提供了get、set函數來訪問和設置屬性值,而具體的處理由C++實現(下面給出部分源碼,一些重載的get、set函數省略。若是想具體瞭解函數實現的細節,能夠參考http://www.cnblogs.com/bastard/archive/2012/10/11/2720314.html):android
public class SystemProperties { //JNI調用函數 private static native String native_get(String key); private static native void native_set(String key, String def); public static String get(String key) { if (TRACK_KEY_ACCESS) onKeyAccess(key); return native_get(key); } public static void set(String key, String val) { if (val != null && val.length() > PROP_VALUE_MAX) { throw newValueTooLargeException(key, val); } if (TRACK_KEY_ACCESS) onKeyAccess(key); native_set(key, val); } }
這個類是隱藏的,上層程序開發沒法直接使用。若是要用,須要用到java的反射機制,而且須要知道所需屬性的具體標識key(建議使用下面的封裝類),如:shell
Class clazz = Class.forName("android.os.SystemProperties"); Method MethodGet = clazz.getDeclaredMethod("get", String.class); String serialno = (String) MethodGet.invoke(null,"ro.serialno"); Log.d("yi"," serialno: " + serialno); 日誌:D/yi: serialno: DQBALFPNNBKRIVSO
從備註能夠看出,此類是基於系統信息的,提供了一些系統屬性值做爲類變量,以供使用。它大體能夠算是基於SystemProperties的一個封裝類,大部分特徵都是經過SystemProperties的get函數獲取:數據庫
public class Build { //系統版本號 public static final String RELEASE = getString("ro.build.version.release"); //型號 public static final String MODEL = getString("ro.product.model"); //硬件識別碼 public static final String FINGERPRINT = deriveFingerprint(); private static String deriveFingerprint() { String finger = SystemProperties.get("ro.build.fingerprint"); if (TextUtils.isEmpty(finger)) { finger = getString("ro.product.brand") + '/' + getString("ro.product.name") + '/' + getString("ro.product.device") + ':' + getString("ro.build.version.release") + '/' + getString("ro.build.id") + '/' + getString("ro.build.version.incremental") + ':' + getString("ro.build.type") + '/' + getString("ro.build.tags"); } return finger; } //生產商 public static final String BRAND = getString("ro.product.brand"); private static String getString(String property) { return SystemProperties.get(property, UNKNOWN); } }
咱們在使用的時候能夠直接經過Build類調用,用此方法可獲取經常使用的系統版本號、手機型號、硬件識別碼、序列號等等:網絡
StringBuilder out = new StringBuilder(); out.append("MODEL: " + android.os.Build.MODEL); out.append("; BRAND: " + android.os.Build.BRAND); //新版SDK已不推薦經過此方法獲取SERIAL,而是用getSerial():封裝了IDeviceIdentifiersPolicyService的getSerial()方法 out.append("; SERIAL: " + android.os.Build.SERIAL);
電話管理器,用於管理手機通話狀態、獲取電話信息、偵聽電話狀態以及能夠調用電話撥號器撥打電話。這裏有咱們所須要的IMEI、IMSI等:多線程
public class TelephonyManager { //惟一設備號(GSM爲IMEI,CDMA爲MEID或ESN) public String getDeviceId() { ITelephony telephony = getITelephony(); if (telephony == null) return null; return telephony.getDeviceId(mContext.getOpPackageName()); } //IMEI public String getImei() { return getImei(getSlotIndex()); } public String getImei(int slotIndex) { ITelephony telephony = getITelephony(); if (telephony == null) return null; return telephony.getImeiForSlot(slotIndex, getOpPackageName()); } //IMSI public String getSimSerialNumber() { return getSimSerialNumber(getSubId()); } public String getSimSerialNumber(int subId) { IPhoneSubInfo info = getSubscriberInfo(); if (info == null) return null; return info.getIccSerialNumberForSubscriber(subId, mContext.getOpPackageName()); } //當擁有多卡,且爲GSM手機時,用這個 @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getSubscriberId() { return getSubscriberId(getSubId()); } }
構造對象直接調用函數便可,在AndroidManifest.xml中添加訪問手機狀態的權限:<uses-permission android:name="android.permission.READ_PHONE_STATE" />app
TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE); Log.d("yi"," DeviceId: " + tm.getDeviceId()); Log.d("yi"," Imei: " + tm.getImei()); //SDK26才提供 Log.d("yi"," Imsi: " + tm.getSimSerialNumber ());
其實,這個就是與咱們的設置頁面對應的數據庫,以鍵值對存儲數據,好比 ADB_ENABLED、BLUETOOTH_ON、WIFI_COUNTRY_CODE等等,經過函數 getXxx(ContentResolver resolver, String name)能夠獲取鍵值name所對應的數值,而若是你要設置新的屬性值,經過putXxx(ContentResolver resolver, String name, Xxx value),能夠設置應用所私有的,也能夠設置全局的。固然,有些重要的須要設置權限:Manifest.permission.WRITE_SECURE_SETTINGSide
固然,若是須要修改系統屬性值,一般不建議應用經過這個類的屬性來設置,而是經過UI界面來修改(像內部類Secure中的屬性值,應用只能讀取而沒法改寫),利用ACTION_SETTINGS等屬性構造INTENT能夠打開對應的設置頁面進行手動修改。函數
這裏咱們所要獲取的手機系統屬性值就是在內部類Secure中,毫無疑問,這些值是沒法被應用修改的,別說是應用了,咱們都改不了好很差 -_-
public final class Settings { public static final class Secure extends NameValueTable { private static final HashSet<String> MOVED_TO_LOCK_SETTINGS; private static final HashSet<String> MOVED_TO_GLOBAL; static { MOVED_TO_LOCK_SETTINGS = new HashSet<>(3); MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_ENABLED); MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_VISIBLE); MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED); MOVED_TO_GLOBAL = new HashSet<>(); MOVED_TO_GLOBAL.add(Settings.Global.ADB_ENABLED); MOVED_TO_GLOBAL.add(Settings.Global.ASSISTED_GPS_ENABLED); 。。。。。。 } public static String getString(ContentResolver resolver, String name) { //這個源碼就很少看了,涉及到庫查找、可修改與不可修改、多線程同步等 return getStringForUser(resolver, name, UserHandle.myUserId()); } } public static final class System extends NameValueTable { private static final HashSet<String> MOVED_TO_SECURE; static { MOVED_TO_SECURE = new HashSet<>(30); //android_id MOVED_TO_SECURE.add(Secure.ANDROID_ID); MOVED_TO_SECURE.add(Secure.HTTP_PROXY); MOVED_TO_SECURE.add(Secure.LOCATION_PROVIDERS_ALLOWED); MOVED_TO_SECURE.add(Secure.LOCK_BIOMETRIC_WEAK_FLAGS); MOVED_TO_SECURE.add(Secure.LOCK_PATTERN_ENABLED); MOVED_TO_SECURE.add(Secure.LOCK_PATTERN_VISIBLE); } } }
而後簡單看下使用:
Context cont = this.getApplicationContext(); String msg; msg = Settings.Secure.getString(cont.getContentResolver(),"android_id"); 對應Smali: invoke-virtual {p0}, Landroid/content/Context;->getContentResolver()Landroid/content/ContentResolver; move-result-object v1 const-string/jumbo v2, "android_id" invoke-static {v1, v2}, Landroid/provider/Settings$Secure;->getString(Landroid/content/ContentResolver;Ljava/lang/String;)Ljava/lang/String;
MAC地址獲取方式與以上的一些特徵有點區別,能夠用Android的API直接獲取,也可使用Linux命令獲取
在使用WIFI上網時可直接使用android.net.wifi.WifiManager系統調用,使用時須要在AndroidManifest.xml中添加訪問手機WIFI狀態的權限:<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
WifiManager pwifi = (WifiManager) getSystemService(Context.WIFI_SERVICE); WifiInfo pinfo = pwifi.getConnectionInfo(); Log.d("yi","MAC: " + pinfo.getMacAddress()); Log.d("yi","IP: " + pinfo.getIpAddress()); //ip整數形式
若是是在使用移動網絡的狀況下,可直接使用java.net.NetworkInterface系統調用,使用時須要設置手機上網權限:<uses-permission android:name="android.permission.INTERNET"></uses-permission>
Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();// 返回全部網絡接口的一個枚舉實例 while (e.hasMoreElements()) { NetworkInterface network = e.nextElement();// 得到一個網絡接口 if (network != null && network.getName().equals("wlan0")) { //從衆多網絡接口中選擇你要的那個 if (network.getHardwareAddress() != null) { // 得到MAC地址 byte[] addres = network.getHardwareAddress(); StringBuffer mac = new StringBuffer(); for (int i = 0; i < addres.length; i++) { int intValue = addres[i]; if (intValue < 0) intValue = 256 + intValue; mac.append(Integer.toHexString(intValue)); if(i < addres.length - 1) mac.append(":"); } Log.d("yi","Mac: " + mac); } Enumeration<InetAddress> ips = network.getInetAddresses(); // 獲取ip for(;ips.hasMoreElements();) { InetAddress ip = ips.nextElement(); if(ip instanceof InetAddress) Log.d("yi","IP: " + ip);; } } }
也可以使用可獲取MAC的Linux命令,cat /sys/class/net/wlan0/address 或 ifconfig等均可以,而後使用讀寫函數將MAC地址部分數據搞出來就好了(WifiInfo給MAC的默認初始值爲「02:00:00:00:00:00」,有些設備並未在WifiInfo中設置MAC地址值,這樣調用WifiManager得到的就只是這個初始值),下面藉助busybox調用Linux命令獲取MAC:
String readLine = ""; Process process = Runtime.getRuntime().exec("busybox ifconfig"); BufferedReader bufferedReader = new BufferedReader (new InputStreamReader(process.getInputStream())); while ((readLine = bufferedReader.readLine ()) != null) {//只取結果中含有"HWaddr"的這一行: Link encap:Ethernet HWaddr 00:08:22:76:CF:FB if(readLine.contains("HWaddr")){ Log.d("yi","Mac: " + readLine.substring(readLine.indexOf("HWaddr")+6, readLine.length()-1)); }