編輯:關於Android編程
UTDID是集團無線設備統一ID方案,目的是給每一台設備一個ID,作為唯一標識。UTDID由客戶端生成,並在設備中各個客戶端之間共享。UTDID的生成中包含時間戳和隨機數等,因此重新生成的UTDID值一定是會改變的,UTDID的穩定性強依賴於手機存儲,UTDID方案是一個重在持久化存儲的方案。
utdid開發手冊中是強制需要下面3個權限的:
但是隨著Android6.0的發布和targetSDK使用23編譯,很多權限問題都暴露了出來:
1.WRITE_SETTINGS
targetSDK使用23以後,即使申請了WRITE_SETTINGS權限,再想寫settings中的數據會拋出了如下異常。
IllegalArgumentException: You cannot keep your settings in the secure settings.
想寫Setting.System中的數據也已經沒有權限了,即使想修改Settings.Global也需要是系統應用。
2.READ_PHONE_STATE
targetSDK使用23以後,需要手動授權。imei作為utdid生成字段的其中一部分,在生成utdid時,如果無法獲取就使用隨機值代替。這個即使不授權問題也不大。
3.WRITE_EXTERNAL_STORAGE
targetSDK使用23以後,需要手動授權。在settings中沒有寫入utdid的情況下,如果沒有WRITE_EXTERNAL_STORAGE權限,應用外部的utdid是無法獲得的,內部也沒有的情況下,那麼utdid勢必會重新生成了。
如果settings不能保存,那麼應用外就需要寄希望於sdcard的存儲。除了權限問題會導致sdcard中的數據無法取得外,三方的手機管理工具也會對sdcard中的數據做清除。(utdid外存儲目錄用AAA,BBB表示)
以360手機衛士為例:
360深度空間清理時,在可安全清理項->廣告垃圾->將AAA和BBB識別為淘寶應用緩存和阿裡網頁廣告,導致被刪除掉。AAA和BBB文件夾下的所以文件和文件夾被會被刪除。
個人感覺這些文件的識別是依賴360某些配置的下發,因為在斷網並清除360緩存的情況下,是識別不到AAA和BBB的。(360安裝包文件被混淆,網絡數據被加密,很難知道真正的實現。)
應用內的存儲可靠性就更不用說了,卸載應用或清除應用緩存就直接沒有了。
Android系統中並沒有可靠獲取所有廠商設備唯一ID的方法,各個方法都有自己的使用范圍和局限性,這也是目前流行的Android系統版本過多,設備也是來自不同廠商,且沒有統一標准等原因造成的。
假設我們確實需要用到真實設備的標識,可能就需要用到DEVICE_ID。
在以前,我們的Android設備是手機,這個DEVICE_ID可以同通過getSystemService(Context.TELEPHONY_SERVICE).getDeviceId()獲取,它根據不同的手機設備返回IMEI,MEID或者ESN碼,但它在使用的過程中會遇到很多問題:
非手機設備:最開始搭載Android系統都手機設備,而現在也出現了非手機設備:如平板電腦、電子書、電視、音樂播放器等。這些設備沒有通話的硬件功能,系統中也就沒有TELEPHONY_SERVICE,自然也就無法通過上面的方法獲得DEVICE_ID。當設備為手機時,返回設備的唯一ID。手機制式為 GSM 時,返回手機的 IMEI 。手機制式為 CDMA 時,返回手機的 MEID 或 ESN 。非電話設備或者 Device ID 不可用時,返回 null .
權限問題:獲取DEVICE_ID需要READ_PHONE_STATE權限,如果只是為了獲取DEVICE_ID而沒有用到其他的通話功能,申請這個權限一來大才小用,二來部分用戶會懷疑軟件的安全性。 (Android 6.0 以上需要用戶手動賦予該權限)
廠商定制系統中的Bug:少數手機設備上,由於該實現有漏洞,會返回垃圾,如:zeros或者asterisks
wifi mac獲取方法:
WifiManager wifiManager=(WifiManager) getSystemService(Context.WIFI_SERVICE); WifiInfo wifiInfo=wifiManager.getConnectionInfo(); String mac=wifiInfo.getMacAddress();
這種方法比較通用,但是最近在Android 6.0系統上,這個方法失效了,返回了”02:00:00:00:00:00”的常量。這並不是一個BUG,在google的博客中找到如下一段話:
Most notably, Local WiFi and Bluetooth MAC addresses are no longer available. The getMacAddress() method of a WifiInfo object and the BluetoothAdapter.getDefaultAdapter().getAddress() method will both return 02:00:00:00:00:00 from now on.
可以考慮使用NetworkInterface.getHardwareAddress。其原理和cat /sys/class/net/wlan0/address是一模一樣的,但是這個是上層API,不需要自己處理底層數據,在Android 6.0上測試通過。
NetworkInterface networkInterface = NetworkInterface.getByName("wlan0"); byte[] mac = networkInterface.getHardwareAddress();
問題:
1.如果重啟手機後,Wifi沒有打開過,是無法獲取其Mac地址的。(可以考慮授予CHANGE_WIFI_STATE權限,開關一次wifi刷一下。)
2.有一些定制系統的目錄並不一樣。 例如三星的目錄為"cat /sys/class/net/eth0/address",所以是否對所以機型都有效有待驗證。(需要適配)
3.網上也有反映mac變更問題,是不是刷mac或者wifi故障導致,也不確定。
4.並不是所有的設備都有Wifi硬件,硬件不存在自然也就得不到這一信息。(這個還好)
5.需要 ACCESS_WIFI_STATE 權限。(這個還好)
獲取辦法:
String serialNum = android.os.Build.SERIAL;
裝有SIM卡的設備獲取辦法:getSystemService(Context.TELEPHONY_SERVIEC).getSimSerialNumber();
注意對CDMA設備,返回的是一個空值。
在Android 2.3可以通過android.os.Build.SERIAL獲取,非手機設備可以通過該接口獲取。
在少數的一些設備上,會返回垃圾數據。對於沒有通話功能的設備,它可能會返回一個固定的值。
在設備首次啟動時,系統會隨機生成一個64位的數字,並把這個數字以16進制字符串的形式保存下來,這個16進制的字符串就是ANDROID_ID,當設備被恢復出廠設置後該值會被重置。可以通過下面的方法獲取:
import android.provider.Settings; String ANDROID_ID = Settings.System.getString(getContentResolver(), Settings.System.ANDROID_ID);
ANDROID_ID可以作為設備標識,但需要注意:
它在Android <=2.1 or Android >=2.3的版本是可靠、穩定的,但在2.2的版本並不是100%可靠的
。
廠商定制系統的Bug:不同的設備可能會產生相同的ANDROID_ID:9774d56d682e549c。(摩托羅拉好像出現過這個問題)
廠商定制系統的Bug:有些設備返回的值為null。
設備差異:對於CDMA設備,ANDROID_ID和TelephonyManager.getDeviceId() 返回相同的值。
並且,如果某個Andorid手機被Root過的話,這個ID也可以被改變。
文件路徑:/proc/cpuinfo
通過Adb shell 查看:adb shell cat /proc/cpuinfo
但是在Nexus4的Android6.0手機上Serial為0000000000000000
rickydeMacBook-Pro:bin ricky$ adb shell cat /proc/cpuinfo Processor : ARMv7 Processor rev 2 (v7l) processor : 0 BogoMIPS : 13.53 processor : 1 BogoMIPS : 13.53 processor : 2 BogoMIPS : 13.53 processor : 3 BogoMIPS : 13.53 Features : swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt CPU implementer : 0x51 CPU architecture: 7 CPU variant : 0x0 CPU part : 0x06f CPU revision : 2 Hardware : QCT APQ8064 MAKO Revision : 000b Serial : 0000000000000000
文件路徑: /sys/class/net/wlan0/address
通過Adb shell 查看:adb shell cat /sys/class/net/wlan0/address
rickydeMacBook-Pro:bin ricky$ adb shell cat /sys/class/net/wlan0/address 10:68:3f:49:93:7d
我們有時候會需要獲取當前手機的系統版本來進行判斷,或者需要獲取一些當前手機的硬件信息。
android.os.Build類中,包括了這樣的一些信息。我們可以直接調用 而不需要添加任何的權限和方法。
android.os.Build.BOARD:獲取設備基板名稱 android.os.Build.BOOTLOADER:獲取設備引導程序版本號 android.os.Build.BRAND:獲取設備品牌 android.os.Build.CPU_ABI:獲取設備指令集名稱(CPU的類型) android.os.Build.CPU_ABI2:獲取第二個指令集名稱 android.os.Build.DEVICE:獲取設備驅動名稱 android.os.Build.DISPLAY:獲取設備顯示的版本包(在系統設置中顯示為版本號)和ID一樣 android.os.Build.FINGERPRINT:設備的唯一標識。由設備的多個信息拼接合成。 android.os.Build.HARDWARE:設備硬件名稱,一般和基板名稱一樣(BOARD) android.os.Build.HOST:設備主機地址 android.os.Build.ID:設備版本號。 android.os.Build.MODEL :獲取手機的型號 設備名稱。 android.os.Build.MANUFACTURER:獲取設備制造商 android:os.Build.PRODUCT:整個產品的名稱 android:os.Build.RADIO:無線電固件版本號,通常是不可用的 顯示unknown android.os.Build.TAGS:設備標簽。如release-keys 或測試的 test-keys android.os.Build.TIME:時間 android.os.Build.TYPE:設備版本類型 主要為"user" 或"eng". android.os.Build.USER:設備用戶名 基本上都為android-build android.os.Build.VERSION.RELEASE:獲取系統版本字符串。如4.1.2 或2.2 或2.3等 android.os.Build.VERSION.CODENAME:設備當前的系統開發代號,一般使用REL代替 android.os.Build.VERSION.INCREMENTAL:系統源代碼控制值,一個數字或者git hash值 android.os.Build.VERSION.SDK:系統的API級別 一般使用下面大的SDK_INT 來查看 android.os.Build.VERSION.SDK_INT:系統的API級別 數字表示
在Android系統中,/system/build.prop中含有大量系統相關的信息:
rickydeMacBook-Pro:bin ricky$ adb shell cat /system/build.prop # begin build properties # autogenerated by buildinfo.sh ro.build.id=MDB08M ro.build.display.id=cm_mako-userdebug 6.0 MDB08M c28ecd9956 test-keys ro.build.version.incremental=c28ecd9956 ro.build.version.sdk=23 ro.build.version.preview_sdk=0 ro.build.version.codename=REL ro.build.version.all_codenames=REL ro.build.version.release=6.0 ro.build.version.security_patch=2015-11-01 ro.build.version.base_os= ro.build.date=2015年 11月 23日 星期一 17:03:23 CST ro.build.date.utc=1448269403 ro.build.type=userdebug ro.build.user=moonlight ro.build.host=moonlight-roms ro.build.tags=test-keys ro.build.flavor=cm_mako-userdebug ro.product.brand=google ro.product.name=occam ro.product.board=MAKO ... ...
如果有root權限,修改/system/build.prop文件內容,系統相關的信息就會被改變(總會有那麼些無聊的人)。因此設備信息的安全級別不高,但是可以作為參考。此外,可以上傳一些手機root相關信息做參考(雖然也不是100%有效)。
可以提供手機root相關信息,作為build.prop有效性的一個參考值。
在/system/build.prop中的Build.TAGS字段設備標簽。如release-keys 或測試的 test-keys。
test-keys為root手機或第三方ROM
ro.build.type字段設備版本類型。如:user或userdebug等。
查看su文件是否存在,可以參考下面代碼的檢索路徑:
"/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su","/system/bin/failsafe/su", "/data/local/su", "/su/bin/su"
查看/system/app/ 下是否存root後常用軟件。Kinguser.apk、Superuser.apk等,如:
private boolean hasSuperuserApk() { return new File("/system/app/Superuser.apk").exists(); }
執行su命令,推薦使用new ProcessBuilder().command("su").start() 代替Runtime.getRuntime().exec()實現。Runtime.getRuntime().exec()的執行會有些bug,可以參考(Determine if running on a rooted device),記得要Process.destroy()。執行su命令,會喚起root授權對話框,在數據上報的場景是不建議使用,體驗非常不好。
設備唯一標識碼還是以utdid做標識,但是在Android6.0+系統上,外存儲權限越來越難獲取和越來越不可靠的情況下,除考慮加入LocalSocket和Broadcast等機制做多應用間的utdid同步(問題也很明顯)外,必須依賴網絡,構建設備ID庫來提升設備標識的可靠性。
因此需要考慮在服務器上建立utdid與各設備數據間的對應關系,通過做大規模的適配和數據上報,來解決問題。通過可獲得手機參數做服務器請求,服務器的utdid與各設備數據間的對應關系表來尋找最匹配utdid值。
可以考慮的數據關系體系是以Wifi Mac地址、設備序列號、ANDROID_ID為主要基准,配合android.os.Build中手機基本信息為參考(用手機root相關信息采集做修正),DEVICE_ID(用READ_PHONE_STATE權限做修正),常用ip地址等。如果可以的話,還可以參考手機號碼、業務登錄賬號等。目前只是一些初步想法,可行性還有待實際數據驗證,方案還在探索階段。
在安卓中有很多種解析方式。按照大方向有xml解析和json解析。而,細致的分,xml和json解析各有自己的很多解析方式。今天這一篇主要介紹xml解析中的pull解析。對
本文實例講述了Android實現軟件列表的點擊啟動另外一個程序功能。分享給大家供大家參考,具體如下:目前面世的許多軟件中有這麼一個功能:設備中安裝了哪些軟件,他們會以一個
上拉加載更多,下拉刷新,網上比較強大比較全的一個開源庫PullToRefresh,支持Listview、GridView、ScrollView等眾多控件。下載地址:git
以下是andoid項目文件結構圖,常用操作部分已經用矩形框標注好了。。 接下來,我們隊每一個文件夾或者文件說明一下: public static final cl