Android设备有唯一的ID吗?如果有,使用Java访问它的简单方法是什么?


当前回答

以下代码使用隐藏的Android API返回设备序列号。但是,这个代码在三星Galaxy Tab上不起作用,因为这个设备上没有设置“ro.seriano”。

String serial = null;

try {
    Class<?> c = Class.forName("android.os.SystemProperties");
    Method get = c.getMethod("get", String.class);
    serial = (String) get.invoke(c, "ro.serialno");
}
catch (Exception ignored) {

}

其他回答

#上次更新时间:2015年6月2日


在阅读了每一篇StackOverflow关于创建唯一ID的帖子、Google开发者博客和Android文档后,我觉得“伪ID”似乎是最好的选择。

主要问题:硬件与软件

硬件

用户可以改变他们的硬件、Android平板电脑或手机,因此基于硬件的唯一ID对于追踪用户来说不是好主意对于跟踪硬件,这是一个好主意

软件

用户可以擦除/更改他们的ROM,如果他们是根用户您可以跨平台(iOS、Android、Windows和Web)跟踪用户在个人用户同意的情况下跟踪他们的最佳方式是让他们登录(使用OAuth实现无缝)


#Android系统的总体故障

###-保证API的唯一性(包括根设备)>=9/10(99.5%的Android设备)###-无额外权限

Psuedo代码:

if API >= 9/10: (99.5% of devices)

return unique ID containing serial id (rooted devices may be different)

else

return the unique ID of build information (may overlap data - API < 9)

感谢@stansult发布我们的所有选项(在堆栈溢出问题中)。

##选项列表-为什么/为什么不使用它们:

用户电子邮件-软件用户可以更改电子邮件-极不可能API 5+<使用权限android:name=“android.permission.GET_ACCOUNTS”/>或API 14+<uses permission android:name=“android.ppermission.READ_PROFILE”/><uses权限android:nname=“android.prpermission.RREAD_CONTACTS”/>(如何获取android设备的主电子邮件地址)用户电话号码-软件用户可以更改电话号码-极不可能<uses permission android:name=“android.ppermission.READ_PHONE_STATE”/>IMEI-硬件(仅限手机,需要android。permission.READ_PHONE_STATE)大多数用户讨厌在权限中显示“电话呼叫”。一些用户给出了糟糕的评级,因为他们认为你只是在窃取他们的个人信息,而你真正想做的只是跟踪设备安装。很明显,您正在收集数据。<uses permission android:name=“android.ppermission.READ_PHONE_STATE”/>Android ID-硬件(可以为空,可以在工厂重置时更改,可以在根设备上更改)由于它可以是“null”,我们可以检查“null”并更改其值,但这意味着它将不再是唯一的。如果您的用户具有出厂重置设备,则根设备上的值可能已更改或更改,因此如果您正在跟踪用户安装,则可能存在重复条目。WLAN MAC地址-硬件(需要android.permission.ACCESS_WIFI_STATE)这可能是第二个最佳选项,但您仍在收集和存储直接来自用户的唯一标识符。很明显,您正在收集数据。<uses permission android:name=“android.permission.ACCESS_WIFI_STATE”/>蓝牙MAC地址-硬件(带蓝牙的设备,需要android.permission.蓝牙)市场上的大多数应用程序都不使用蓝牙,因此如果您的应用程序不使用蓝牙并且您包含了这一功能,用户可能会变得可疑。<uses permission android:name=“android.permission.BLUETOOTH”/>伪唯一ID-软件(适用于所有Android设备)很有可能,可能包含冲突-请参阅下面发布的我的方法!这允许您从用户那里获得一个“几乎唯一”的ID,而无需获取任何私人信息。您可以根据设备信息创建自己的匿名ID。


我知道没有任何“完美”的方法可以在不使用权限的情况下获得唯一的ID;然而,有时我们只需要跟踪设备安装。在创建唯一ID时,我们可以仅基于Android API提供的信息创建“伪唯一ID”,而无需使用额外的权限。通过这种方式,我们可以表现出对用户的尊重,并尝试提供良好的用户体验。

使用伪唯一id,您实际上只会遇到这样一个事实:基于存在类似设备的事实,可能存在重复项。您可以调整组合方法,使其更独特;然而,一些开发人员需要跟踪设备的安装情况,这将根据类似的设备来完成技巧或性能。

##API>=9:

如果他们的Android设备是API 9或更高版本,则由于“Build.SSERIAL”字段,这是唯一的。

记住,从技术上讲,你只错过了大约0.5%的API<9的用户。所以你可以专注于剩下的:这是99.5%的用户!

##API<9:

如果用户的Android设备低于API 9;希望他们没有进行工厂重置,他们的“Secure.ANDROID_ID”将被保留或不为“null”。(参见http://developer.android.com/about/dashboards/index.html)

##如果所有其他操作都失败:

如果所有其他操作都失败了,如果用户的API低于API 9(低于姜饼),重置了设备,或者“Secure.ANDROID_ID”返回“null”,那么返回的ID将仅基于他们的ANDROID设备信息。这就是碰撞可能发生的地方。

变化:

删除了“Android.SECURE_ID”,因为工厂重置可能会导致值更改在API上编辑要更改的代码更改了伪

请查看以下方法:

/**
 * Return pseudo unique ID
 * @return ID
 */
public static String getUniquePsuedoID() {
    // If all else fails, if the user does have lower than API 9 (lower
    // than Gingerbread), has reset their device or 'Secure.ANDROID_ID'
    // returns 'null', then simply the ID returned will be solely based
    // off their Android device information. This is where the collisions
    // can happen.
    // Thanks http://www.pocketmagic.net/?p=1662!
    // Try not to use DISPLAY, HOST or ID - these items could change.
    // If there are collisions, there will be overlapping data
    String m_szDevIDShort = "35" + (Build.BOARD.length() % 10) + (Build.BRAND.length() % 10) + (Build.CPU_ABI.length() % 10) + (Build.DEVICE.length() % 10) + (Build.MANUFACTURER.length() % 10) + (Build.MODEL.length() % 10) + (Build.PRODUCT.length() % 10);

    // Thanks to @Roman SL!
    // https://stackoverflow.com/a/4789483/950427
    // Only devices with API >= 9 have android.os.Build.SERIAL
    // http://developer.android.com/reference/android/os/Build.html#SERIAL
    // If a user upgrades software or roots their device, there will be a duplicate entry
    String serial = null;
    try {
        serial = android.os.Build.class.getField("SERIAL").get(null).toString();

        // Go ahead and return the serial for api => 9
        return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
    } catch (Exception exception) {
        // String needs to be initialized
        serial = "serial"; // some value
    }

    // Thanks @Joe!
    // https://stackoverflow.com/a/2853253/950427
    // Finally, combine the values we have found by using the UUID class to create a unique identifier
    return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
}

#新增(适用于带有广告和Google Play服务的应用程序):

从Google Play开发者控制台:

从2014年8月1日开始,Google Play开发者计划政策需要所有新的应用程序上载和更新才能使用中的广告ID代替用于任何广告目的的任何其他持久标识符。了解更多信息

实施:

许可:

<uses-permission android:name="android.permission.INTERNET" />

代码:

import com.google.android.gms.ads.identifier.AdvertisingIdClient;
import com.google.android.gms.ads.identifier.AdvertisingIdClient.Info;
import com.google.android.gms.common.GooglePlayServicesAvailabilityException;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import java.io.IOException;
...

// Do not call this function from the main thread. Otherwise, 
// an IllegalStateException will be thrown.
public void getIdThread() {

  Info adInfo = null;
  try {
    adInfo = AdvertisingIdClient.getAdvertisingIdInfo(mContext);

  } catch (IOException exception) {
    // Unrecoverable error connecting to Google Play services (e.g.,
    // the old version of the service doesn't support getting AdvertisingId).
 
  } catch (GooglePlayServicesAvailabilityException exception) {
    // Encountered a recoverable error connecting to Google Play services. 

  } catch (GooglePlayServicesNotAvailableException exception) {
    // Google Play services is not available entirely.
  }
  final String id = adInfo.getId();
  final boolean isLAT = adInfo.isLimitAdTrackingEnabled();
}

来源/文档:

http://developer.android.com/google/play-services/id.htmlhttp://developer.android.com/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient.html

##重要信息:

广告ID旨在完全取代现有的为广告目的使用其他标识符(例如使用ANDROID_ID在Settings.Secure中)。案例Google Play服务不可用的位置由引发的GooglePlayServicesNotAvailableException获取广告IdInfo()。

##警告,用户可以重置:

http://en.kioskea.net/faq/34732-android-reset-your-advertising-id

我试图引用我从中获取信息的每个链接。如果你失踪了,需要加入,请评论!

Google Player服务实例ID

https://developers.google.com/instance-id/

API级别9(Android 2.3-姜饼)中的Build类中添加了一个Serial字段。文档表示它代表硬件序列号。因此,如果设备上存在,它应该是唯一的。

我不知道API级别>=9的所有设备是否都支持(=不为空)。

序列号是通过android.os.Build.Serial提供的唯一设备ID。

public static String getSerial() {
    String serial = "";
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
        serial = Build.getSerial();
    }else{ 
        serial = Build.SERIAL;    
    }
    return serial;
}

在调用getSerial()之前,请确保您具有READ_PHONE_STATE权限。

注意:-它不适用于没有电话的设备(如仅支持wifi的平板电脑)。

只是提醒大家阅读更多最新信息。对于Android O,系统管理这些ID的方式有一些变化。

https://android-developers.googleblog.com/2017/04/changes-to-device-identifiers-in.html

tl;dr Serial将需要PHONE权限,Android ID将根据不同的应用程序的包名和签名进行更改。

此外,谷歌还编制了一份很好的文档,提供了有关何时使用硬件和软件ID的建议。

https://developer.android.com/training/articles/user-data-ids.html

另一种方法是在没有任何权限的应用程序中使用/sys/class/android_usb/android0/iSerial。

user@creep:~$ adb shell ls -l /sys/class/android_usb/android0/iSerial
-rw-r--r-- root     root         4096 2013-01-10 21:08 iSerial
user@creep:~$ adb shell cat /sys/class/android_usb/android0/iSerial
0A3CXXXXXXXXXX5

要在Java中实现这一点,只需使用FileInputStream打开iSerial文件并读取字符。只需确保将其包装在异常处理程序中,因为并非所有设备都有此文件。

已知至少以下设备具有此文件世界可读性:

银河天枢Nexus S公司摩托罗拉Xoom 3G东芝AT300HTC一V迷你MK802三星Galaxy S II

你也可以看到我的博客文章“泄露Android硬件序列号给未经授权的应用程序”,在这里我讨论了哪些其他文件可供参考。