创建一个全球唯一的安卓标识符

6
谈到唯一的Android ID,我相信每个人都看过这个,但是我也在努力想出一种解决方案来唯一地识别任何Android设备。我很乐意使用公开发布的类,但我还没有找到一个。
在我的情况下,要求能够唯一地识别任何具有某种形式的互联网连接(如GPRS或Wi-Fi)并运行于API级别8(v2.2 Froyo)的设备。我不知道是否有任何没有Wi-Fi或SIM卡的设备,如果有,请告诉我!
我们通过使用Wi-Fi mac地址的哈希值来解决iOS中的这个问题,因为所有iOS设备应该都有这个。然而对于Android,技术方法(例如上面提到的SO帖子中的答案)涉及到可以返回nullTelephonyManager类(例如当设备没有SIM连接时,如Nexus 7),或者getContext().getContentResolver(),Secure.ANDROID_ID,根据这里,在Froyo之前的版本上并不完全可靠(这对我来说是幸运的)。但它没有说明在Froyo之后是否存在任何问题,如果有,请告诉我!我还读到了这可能会返回null。根据这里,它可以在出厂重置时更改,但这不是一个主要的问题。
所以我组合了这个来生成一个希望唯一的GUID: [此方法尚未完成!]
public static String GetDeviceId(Context context)
{
    // Custom String Hash 1
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append("someRandomData"); // Not really needed, but means the stringBuilders value won't ever be null

    // TM Device String
    final TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
    String tmDeviceId = tm.getDeviceId(); // Could well be set to null!
    LogMsg.Tmp("TM Device String [" + tmDeviceId + "]");

    // Custom String Hash 2
    stringBuilder.append(tmDeviceId);
    int customHash = stringBuilder.toString().hashCode();
    LogMsg.Tmp("Custom String hash [" + customHash + "]");

    // Device ID String
    String androidIDString = android.provider.Settings.Secure.getString(context.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
    LogMsg.Tmp("Device ID String [" + androidIDString + "]");

    // Combined hashes as GUID
    UUID deviceUuid = new UUID(androidIDString.hashCode(), ((long)customHash << 32));
    LogMsg.Tmp("Combined hashes as GUID [" + deviceUuid.toString() + "]");

    return deviceUuid.toString();
}

因此,您可能会发现tmDeviceId被设置为null,在这种情况下,customHash将是相同的,无论设备如何,但是这与androidIDString结合使用应该是全局唯一的。我想。显然,如果tmDeviceIdandroidIDString不可用,我需要确定是否会抛出异常。

那么... 这是否有些过度了?如果是这样,只使用context.getContentResolver(),android.provider.Settings.Secure.ANDROID_ID是否安全?

1
在我的情况下,要求能够唯一识别任何设备。如果您需要覆盖已root的设备用户,则据我所知这是不可能的。当然,使用的任何部件都可以被拥有root权限的人修改。 我不知道是否有任何没有Wi-Fi或SIM卡的设备,如果有,请告诉我!Google TV设备可能没有WiFi,而是使用以太网。如果您要分发到Play Store之外,可能会有其他没有移动数据或WiFi的设备。 - CommonsWare
@CommonsWare 感谢您的回复。我的初步想法是,如果我在问题中提到的所有方法都返回 null,那么就不支持该设备。您认为在 Froyo 上使用 Secure.ANDROID_ID 是安全的吗?此外,我认为经过 Root 的设备并不是一个主要的问题。我猜这会允许某个人伪造别人的不同设备,至少如果他们知道自己在做什么的话,但我认为通过添加一个密钥(在我有 stringBuilder.append("someRandomData"); 的地方)会使得生成最终返回的设备 ID 变得困难。 - ingh.am
1
“我假设你会通过混淆来实现这个目的?”--使用ProGuard无法对数据进行混淆。DexGuard是ProGuard制造商提供的一款商业工具,可以实现此功能。然而,由于某人可以简单地修改APK以拦截口令,因此这将有限制用途。 - CommonsWare
1
@Warpzit:我没有发布答案,因为这个评论线程并没有真正回答问题,除了指出问题本身的问题。有时候,如果我发表了评论,并且结果证明它确实是答案,我会将其转换为答案。 - CommonsWare
你尝试过使用Google Cloud Messaging API吗?String regId= GoogleCloudMessaging.getInstance(context) = gcm.register(SENDER_ID); 它不是UUID,但它对于你的应用程序是唯一的。 - Raúl Juárez
显示剩余9条评论
1个回答

3
为什么不像在iOS中一样获取设备的MAC地址呢?这也可以在Android设备上执行。
我将提供一段代码片段,用于获取设备的MAC地址...
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class HardwareUtil {

    public static String getMacAddress()
    {
        try{
            Process process = Runtime.getRuntime().exec("cat /sys/class/net/eth0/address");
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            int read;
            char[] buffer = new char[4096];
            StringBuffer output = new StringBuffer();
            while ((read = reader.read(buffer)) > 0){
                output.append(buffer, 0, read);
            }
            reader.close();
            process.waitFor();
            String hwaddr = output.toString();
            return hwaddr;
        }catch (IOException e) {
            e.printstacktrace();
        }catch (InterruptedException e){
            e.printstacktrace();
        }
    }

}

HardwareUtil.getMacAddress()将返回设备的mac地址。

编辑:如果mac地址不适合您的情况。以下内容可能会有用!

public static String getDeviceId(Context context) {
    final String deviceId = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId();
    if (deviceId != null) {
        return deviceId;
    } else {
        return android.os.Build.SERIAL;
    }
}

如果您使用getDeviceId方法,请不要忘记将以下权限添加到您的AndoridManifest.xml文件中。
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

这是我最终完成的内容,问题在于如果你拥有 root 访问权限,我相信你可以欺骗 MAC 地址。 - ingh.am
是的,你说得完全正确。如果你有root设备,你可以这样做。你试过android.os.Build.SERIAL吗?你可以使用它来获取硬件序列号。看看这个链接:http://developer.android.com/reference/android/os/Build.html#SERIAL 它可能对你有帮助。 - gokhanakkurt
谢谢,我会去查看一下。android.os.Build.SERIAL 这个东西是保证存在的吗? - ingh.am
我不确定这一点。我唯一知道的是设备需要报告该数字。然而,API文档中说:“如果可用,则为硬件序列号。”因此,我们可以简单地推导出一个方程式“这可能并非所有设备都可用”。顺便问一下,你所说的所有设备是什么意思?平板电脑?智能电视?微波炉 :) - gokhanakkurt
顺便说一下,我已经给你答案了,因为那正是我最终做的。 - ingh.am
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接