Android设备中常使用的唯一标识

安卓设备常用的唯一标识有IMEI,MEID,MAC地址,Android_id,UUID等。

IMEI

国际移动设备识别码(International Mobile Equipment Identity,IMEI),即通常所说的手机序列号、手机“串号”,用于在移动电话网络中识别每一部独立的手机等移动通信设备,相当于移动电话的身份证。序列号共有15~17位数字,前8位(TAC)是型号核准号码(早期为6位),是区分手机品牌和型号的编码。接着2位(FAC)是最后装配号(仅在早期机型中存在),代表最终装配地代码。后6位(SNR)是串号,代表生产顺序号。国际移动设备识别码一般贴于机身背面与外包装上,同时也存在于手机存储器中,通过在手机拨号键盘中输入*#06#即可查询

    //需要android.permission.READ_PHONE_STATE
    //安卓8.0以前,都是通过getDeviceId来获取,在8.0 API=26时废弃
    //改用getImei和getMeid来获取
    public  void getUniqueId(Context context) {
    
    
        TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
    
    
            if (ContextCompat.checkSelfPermission(context,Manifest.permission.READ_PHONE_STATE)==PackageManager.PERMISSION_GRANTED){
    
    
                //8.0以后,区分IMEI和MEID
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    
    
                    Log.d(TAG,"getImei:"+manager.getImei());
                    Log.d(TAG,"getMeid:"+manager.getMeid());
                }else{
    
    //6.0-8.0:不区分IMEI和MEID,在安卓8.0废弃
                    Log.d(TAG,"getDeviceId:"+manager.getDeviceId());
                }
            }else{
    
    
                ActivityCompat.requestPermissions(MainActivity.this, new String[]{
    
    
                        Manifest.permission.READ_PHONE_STATE
                }, 1);
            }
        }else{
    
    //6.0以前
            //安卓8.0以前,用getDeviceId()获取唯一标识
            //例如,GSM的IMEI和CDMA电话的MEID或ESN。如果设备ID不可用,则返回null
            Log.d(TAG,"getDeviceId:"+manager.getDeviceId());
        }
    }
 
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    
    
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(requestCode==1){
    
    
            if(grantResults[0]==PackageManager.PERMISSION_GRANTED){
    
    
                TelephonyManager manager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
                //8.0以后,区分IMEI和MEID
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    
    
                    Log.d(TAG,"getImei:"+manager.getImei());
                    Log.d(TAG,"getMeid:"+manager.getMeid());
                }else{
    
    //6.0-8.0:不区分IMEI和MEID,在安卓8.0废弃
                    Log.d(TAG,"getDeviceId:"+manager.getDeviceId());
                }
            }else{
    
    
                //用户拒绝了这个权限
            }
        }
    }

MEID

MEID 移动设备识别码(Mobile Equipment Identifier)是CDMA手机的身份识别码,也是每台CDMA手机或通讯平板唯一的识别码。通过这个识别码,网络端可以对该手机进行跟踪和监管。

CDMA作为一项新兴技术,CDMA2000正迅速风靡全球并已占据20%的无线市场。截止2012年,全球CDMA2000用户已超过2.56亿,遍布70个国家的 156家运营商已经商用3G CDMA业务。包含高通授权LICENSE的安可信通信技术有限公司在内全球有数十家OEM厂商推出EVDO移动智能终端·2002年,高通公司芯片销售创历史佳绩;1994年至今,高通公司已向全球包括中国在内的众多制造商提供了累计超过75亿多枚芯片。

    //需要android.permission.READ_PHONE_STATE
    //安卓8.0以前,都是通过getDeviceId来获取,在8.0 API=26时废弃
    //改用getImei和getMeid来获取
    public  void getUniqueId(Context context) {
    
    
        TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
    
    
            if (ContextCompat.checkSelfPermission(context,Manifest.permission.READ_PHONE_STATE)==PackageManager.PERMISSION_GRANTED){
    
    
                //8.0以后,区分IMEI和MEID
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    
    
                    Log.d(TAG,"getImei:"+manager.getImei());
                    Log.d(TAG,"getMeid:"+manager.getMeid());
                }else{
    
    //6.0-8.0:不区分IMEI和MEID,在安卓8.0废弃
                    Log.d(TAG,"getDeviceId:"+manager.getDeviceId());
                }
            }else{
    
    
                ActivityCompat.requestPermissions(MainActivity.this, new String[]{
    
    
                        Manifest.permission.READ_PHONE_STATE
                }, 1);
            }
        }else{
    
    //6.0以前
            //安卓8.0以前,用getDeviceId()获取唯一标识
            //例如,GSM的IMEI和CDMA电话的MEID或ESN。如果设备ID不可用,则返回null
            Log.d(TAG,"getDeviceId:"+manager.getDeviceId());
        }
    }
 
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    
    
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(requestCode==1){
    
    
            if(grantResults[0]==PackageManager.PERMISSION_GRANTED){
    
    
                TelephonyManager manager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
                //8.0以后,区分IMEI和MEID
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    
    
                    Log.d(TAG,"getImei:"+manager.getImei());
                    Log.d(TAG,"getMeid:"+manager.getMeid());
                }else{
    
    //6.0-8.0:不区分IMEI和MEID,在安卓8.0废弃
                    Log.d(TAG,"getDeviceId:"+manager.getDeviceId());
                }
            }else{
    
    
                //用户拒绝了这个权限
            }
        }
    }

MEID和手机通信芯片有关系,一般用作智能芯片手机,而IMEI和手机卡槽有关系,包含老式手机和智能手机

MAC地址

MAC地址(英语:Media Access Control Address),直译为媒体存取控制位址,也称为局域网地址(LAN Address),MAC位址,以太网地址(Ethernet Address)或物理地址(Physical Address),它是一个用来确认网络设备位置的位址。MAC位址由数据链路层则负责 。MAC地址用于在网络中唯一标示一个网卡,一台设备若有一或多个网卡,则每个网卡都需要并会有一个唯一的MAC地址。

MAC地址的长度为48位(6个字节),通常表示为12个16进制数,如:00-16-EA-AE-3C-40就是一个MAC地址,其中前3个字节,16进制数00-16-EA代表网络硬件制造商的编号,它由IEEE(电气与电子工程师协会)分配,而后3个字节,16进制数AE-3C-40代表该制造商所制造的某个网络产品(如网卡)的系列号。只要不更改自己的MAC地址,MAC地址在世界是唯一的。形象地说,MAC地址就如同身份证上的身份证号码,具有唯一性。
Android中:通过WifiManager获取mac地址,需要如下权限

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
public class PhoneUtils {
    
    
 
    public static String getMac(Context context) {
    
    
        String strMac;
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
    
    
            strMac = getLocalMacAddressFromWifiInfo(context);
            Log.e("=====", "6.0以下 strMac = " + strMac);
            Toast.makeText(context, "6.0以下", Toast.LENGTH_SHORT).show();
            return strMac;
        } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
    
    
            strMac = getMacAddress();
            Log.e("=====", "6.0以上7.0以下 strMac = " + strMac);
            Toast.makeText(context, "6.0以上7.0以下", Toast.LENGTH_SHORT).show();
            return strMac;
        } else {
    
    
            Log.e("=====", "7.0以上");
            if (!TextUtils.isEmpty(getMacAddress())) {
    
    
                strMac = getMacAddress();
                Log.e("=====", "7.0以上1 strMac = " + strMac);
                Toast.makeText(context, "7.0以上1", Toast.LENGTH_SHORT).show();
                return strMac;
            } else if (!TextUtils.isEmpty(getMachineHardwareAddress())) {
    
    
                strMac = getMachineHardwareAddress();
                Log.e("=====", "7.0以上2 strMac = " + strMac);
                Toast.makeText(context, "7.0以上2", Toast.LENGTH_SHORT).show();
                return strMac;
            } else {
    
    
                strMac = getLocalMacAddressFromBusybox();
                Log.e("=====", "7.0以上3 strMac = " + strMac);
                Toast.makeText(context, "7.0以上3", Toast.LENGTH_SHORT).show();
                return strMac;
            }
        }
    }
 
 
    /**
     * 根据wifi信息获取本地mac
     *
     * @param context
     * @return
     */
    private static String getLocalMacAddressFromWifiInfo(Context context) {
    
    
        WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        WifiInfo winfo = wifi.getConnectionInfo();
        String mac = winfo.getMacAddress();
        return mac;
 
    }
 
    /**
     * 根据IP地址获取MAC地址
     *
     * @return
     */
    private static String getMacAddress() {
    
    
        String strMacAddr = null;
        try {
    
    
            // 获得IpD地址
            InetAddress ip = getLocalInetAddress();
            byte[] b = NetworkInterface.getByInetAddress(ip)
                    .getHardwareAddress();
            StringBuffer buffer = new StringBuffer();
            for (int i = 0; i < b.length; i++) {
    
    
                if (i != 0) {
    
    
                    buffer.append(':');
                }
                String str = Integer.toHexString(b[i] & 0xFF);
                buffer.append(str.length() == 1 ? 0 + str : str);
            }
            strMacAddr = buffer.toString().toUpperCase();
        } catch (Exception e) {
    
    
        }
        return strMacAddr;
    }
 
    /**
     * 获取移动设备本地IP
     *
     * @return
     */
    private static InetAddress getLocalInetAddress() {
    
    
        InetAddress ip = null;
        try {
    
    
            // 列举
            Enumeration<NetworkInterface> en_netInterface = NetworkInterface
                    .getNetworkInterfaces();
            while (en_netInterface.hasMoreElements()) {
    
    // 是否还有元素
                NetworkInterface ni = (NetworkInterface) en_netInterface
                        .nextElement();// 得到下一个元素
                Enumeration<InetAddress> en_ip = ni.getInetAddresses();// 得到一个ip地址的列举
                while (en_ip.hasMoreElements()) {
    
    
                    ip = en_ip.nextElement();
                    if (!ip.isLoopbackAddress()
                            && ip.getHostAddress().indexOf(":") == -1)
                        break;
                    else
                        ip = null;
                }
 
                if (ip != null) {
    
    
                    break;
                }
            }
        } catch (SocketException e) {
    
    
 
            e.printStackTrace();
        }
        return ip;
    }
 
    /**
     * 获取本地IP
     *
     * @return
     */
    private static String getLocalIpAddress() {
    
    
        try {
    
    
            for (Enumeration<NetworkInterface> en = NetworkInterface
                    .getNetworkInterfaces(); en.hasMoreElements(); ) {
    
    
                NetworkInterface intf = en.nextElement();
                for (Enumeration<InetAddress> enumIpAddr = intf
                        .getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
    
    
                    InetAddress inetAddress = enumIpAddr.nextElement();
                    if (!inetAddress.isLoopbackAddress()) {
    
    
                        return inetAddress.getHostAddress().toString();
                    }
                }
            }
        } catch (SocketException ex) {
    
    
            ex.printStackTrace();
        }
        return null;
    }
    /**
     * android 7.0及以上 (2)扫描各个网络接口获取mac地址
     *
     */
    /**
     * 获取设备HardwareAddress地址
     *
     * @return
     */
    private static String getMachineHardwareAddress() {
    
    
        Enumeration<NetworkInterface> interfaces = null;
        try {
    
    
            interfaces = NetworkInterface.getNetworkInterfaces();
        } catch (SocketException e) {
    
    
            e.printStackTrace();
        }
        String hardWareAddress = null;
        NetworkInterface iF = null;
        if (interfaces == null) {
    
    
            return null;
        }
        while (interfaces.hasMoreElements()) {
    
    
            iF = interfaces.nextElement();
            try {
    
    
                hardWareAddress = bytesToString(iF.getHardwareAddress());
                if (hardWareAddress != null)
                    break;
            } catch (SocketException e) {
    
    
                e.printStackTrace();
            }
        }
        return hardWareAddress;
    }
 
    /***
     * byte转为String
     *
     * @param bytes
     * @return
     */
    private static String bytesToString(byte[] bytes) {
    
    
        if (bytes == null || bytes.length == 0) {
    
    
            return null;
        }
        StringBuilder buf = new StringBuilder();
        for (byte b : bytes) {
    
    
            buf.append(String.format("%02X:", b));
        }
        if (buf.length() > 0) {
    
    
            buf.deleteCharAt(buf.length() - 1);
        }
        return buf.toString();
    }
/**
 * android 7.0及以上 (3)通过busybox获取本地存储的mac地址
 *
 */
 
    /**
     * 根据busybox获取本地Mac
     *
     * @return
     */
    private static String getLocalMacAddressFromBusybox() {
    
    
        String result = "";
        String Mac = "";
        result = callCmd("busybox ifconfig", "HWaddr");
        // 如果返回的result == null,则说明网络不可取
        if (result == null) {
    
    
            return "网络异常";
        }
        // 对该行数据进行解析
        // 例如:eth0 Link encap:Ethernet HWaddr 00:16:E8:3E:DF:67
        if (result.length() > 0 && result.contains("HWaddr") == true) {
    
    
            Mac = result.substring(result.indexOf("HWaddr") + 6,
                    result.length() - 1);
            result = Mac;
        }
        return result;
    }
 
    private static String callCmd(String cmd, String filter) {
    
    
        String result = "";
        String line = "";
        try {
    
    
            Process proc = Runtime.getRuntime().exec(cmd);
            InputStreamReader is = new InputStreamReader(proc.getInputStream());
            BufferedReader br = new BufferedReader(is);
 
            while ((line = br.readLine()) != null
                    && line.contains(filter) == false) {
    
    
                result += line;
            }
 
            result = line;
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        return result;
    }
}

Android_id

Android ID的生成是基于设备的硬件信息和操作系统的版本号等,因此它在同一台设备上是固定的,但是在不同的设备上是不同的。Android ID是非常重要的,因为它可以用于许多方面,如广告跟踪、应用程序授权和设备管理等。

在Android系统中,Android ID是在设备首次启动时生成的。它存储在设备的/data/data/com.android.providers.settings/databases/settings.db数据库中的secure表中。在设备首次启动时,系统会检查secure表是否存在一个名为android_id的条目,如果不存在,则会生成一个唯一的Android ID,并将其插入到secure表中。如果设备被恢复出厂设置,则会重新生成一个新的Android ID。

虽然Android ID是唯一的,但是它也有一些限制。首先,它不是100%可靠的,因为它可以被某些应用程序修改或篡改。其次,如果用户重置设备,Android ID也会被重置。此外,如果用户刷机或者更换了ROM,Android ID也会被重置。

    //在 Android 8.0(API 级别 26)及更高版本中,SSAID(AndroidID) 提供了一个在由同一开发者签名密钥签名的应用之间通用的标识符。
    // 借助它,您可以在这些应用之间共享状态,而无需要求用户登录帐号。
    //当设备恢复出厂设置,或者Root过的话,OTA升级系统,该值会被改变
    public static  void getAndroidId(Context context){
    
    
        String androidId = Settings.Secure.getString(context.getApplicationContext().getContentResolver(),
                Settings.Secure.ANDROID_ID);
        Log.d(TAG, "androidId:" + androidId);
    }

UUID

UUID(Universally Unique Identifier)全局唯一标识符,定义为一个字符串主键,采用32位数字组成,编码采用16进制,定义了在时间和空间都完全唯一的系统信息。是国际标准化组织(ISO)提出的一个概念。

UUID是一个128比特的数值,这个数值可以通过一定的算法计算出来。UUID用来识别属性类型,在所有空间和时间上被视为唯一的标识。

UUID是基于当前时间、计数器(counter)和硬件标识(通常为无线网卡的MAC地址)等数据计算生成的。

UUID的编码规则:

1)1~8位采用系统时间,在系统时间上精确到毫秒级保证时间上的唯一性;

2)9~16位采用底层的IP地址,在服务器集群中的唯一性;

3)17~24位采用当前对象的HashCode值,在一个内部对象上的唯一性;

4)25~32位采用调用方法的一个随机数,在一个对象内的毫秒级的唯一性。

通过以上4种策略可以保证唯一性

    //在大多数非广告用例中,可用于跟踪已注销用户的偏好设置,这是建议的解决方案
    public static void getUUID(){
    
    
        String uniqueID = UUID.randomUUID().toString();
        Log.d(TAG, "UUID:" + uniqueID);
    }

猜你喜欢

转载自blog.csdn.net/ChenYiRan123456/article/details/134038373