Android 獲取手機狀態信息


在開發中我們經常會用到一些手機系統信息,如IMEI、IMSI、MAC、SERIALNO等等,下面給出這些信息的一些獲取方法

 1. android.os.SystemProperties  (the access to System Property store)

 這是android系統屬性的控制類,控制系統的各屬性值,用於記錄和管理系統的配置和狀態。每個屬性都以pair(key/value)的形式存儲,可以在adb shell下使用getprop查看系統屬性(列出部分):

[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):

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(建議使用下面的封裝類),如:

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

 

 2. android.os.Build (Information about the current build, extracted from system properties)

 從備注可以看出,此類是基於系統信息的,提供了一些系統屬性值作為類變量,以供使用。它大致可以算是基於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);

 

 3. android.telephony.TelephonyManager(access to information about the telephony services

 電話管理器,用於管理手機通話狀態、獲取電話信息、偵聽電話狀態以及可以調用電話撥號器撥打電話。這里有我們所需要的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" />

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 ());

 

 4.android.provider.settings(The Settings provider contains global system-level device preferences)

  其實,這個就是與我們的設置頁面對應的數據庫,以鍵值對存儲數據,比如 ADB_ENABLED、BLUETOOTH_ON、WIFI_COUNTRY_CODE等等,通過函數 getXxx(ContentResolver resolver, String name)可以獲取鍵值name所對應的數值,而如果你要設置新的屬性值,通過putXxx(ContentResolver resolver, String name, Xxx value),可以設置應用所私有的,也可以設置全局的。當然,有些重要的需要設置權限:Manifest.permission.WRITE_SECURE_SETTINGS

  當然,如果需要修改系統屬性值,通常不建議應用通過這個類的屬性來設置,而是通過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;

 

 5. 獲取MAC地址

 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));  
}  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM