美文网首页
如何正确获取IMEI及MEID

如何正确获取IMEI及MEID

作者: 若瑄若曦 | 来源:发表于2018-11-06 16:18 被阅读0次

在项目支持的时候,由于后台需要记录日志,而记录日志就或多或少的需要获得手机的IMEI来做标示。在安卓系统版本的迭代情况下,获取IMEI方式会遇到很多问题。下面通过代码来演示如何获取IMEI。

首先了解一下什么是IMEI
IMEI是International Mobile Equipment Identity (国际移动设备标识)的简称 
即通常所说的手机序列号,用于在手机网络中识别每一部独立的手机,是国际上公认的手机标志序号,相当于移动电话的身份证。国际移动装备辨识码一般贴于机身背面与外包装上,同时也存在于手机记忆体中,通过输入*#06#即可查询。
IMEI由15位数字组成的”电子串号”,它与每台手机一一对应,而且该码是全世界唯一的 

由上得出IMEI可以认为是手机唯一标识。它是存在于手机里的。那么,也就是说在pad上它是无法获取到的。
在手机4.0以下,我们可以使用:

/**
 * 系统4.0的时候
 * 获取手机IMEI 或者MEID
 *
 * @return 手机IMEI
 */
public static String getImeiOrMeid(Context ctx) {
    TelephonyManager manager = (TelephonyManager) ctx.getSystemService(Activity.TELEPHONY_SERVICE);
    if (manager != null) {
        return manager.getDeviceId();
    }

    return null;
}

来获取。
这时或许会问为什么方法里会带有meid。那MEID又是什么? 我们通过TelephoneyManager源码来了解一下。(注:源码是在android-28下)

/**
 * Returns the unique device ID, for example, the IMEI for GSM and the MEID
 * or ESN for CDMA phones. Return null if device ID is not available.
 *
 * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
 * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
 *
 * @deprecated Use (@link getImei} which returns IMEI for GSM or (@link getMeid} which returns
 * MEID for CDMA.
 */
@Deprecated
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public String getDeviceId() {
    try {
        ITelephony telephony = getITelephony();
        if (telephony == null)
            return null;
        return telephony.getDeviceId(mContext.getOpPackageName());
    } catch (RemoteException ex) {
        return null;
    } catch (NullPointerException ex) {
        return null;
    }
}

通过注释我们了解到,如果当CDMA情况下,返回的是MEID。那么MEID又是什么呢?

MEID
MEID 移动设备识别码(Mobile Equipment Identifier)是CDMA手机的身份识别码,也是每台CDMA手机或通讯平板唯一的识别码。

那么两者有什么区别呢?也许你已经看出来了,MEID是CDMA手机的识别码,IMEI是GSM、WCDMA手机的识别码,对应到国内的运营商如下
移动:2G使用 GSM, 3G使用 TD-SCDMA 4G使用TD-LTE联通:2G使用 GSM, 3G使用 WCDMA 4G使用TD-LTE、FDD-LTE电信:2G使用 CDMA,3G使用 CDMA2000 4G使用TD-LTE、FDD-LTE
所以,得到结论,IMEI是联通移动手机的标识,MEID是电信手机的标识。当需求人员说要记录一下手机的IMEI时候,提供MEID也是一样的。因为MEID也是唯一标识的。
那么在5.0以上难道getDeviceId方法就不行了么?
不是不行,是返回的结果或许不是我们想象的一样。
因为5.0系统以上,安卓的碎片化更加严重,有单卡槽的,有双卡槽的,有全网通的,有卡槽一个是uim一个是 Sim的,有双SIM的。
5.0的系统如果想获取MEID/IMEI1/IMEI2 ----framework层提供了两个属性值“ril.cdma.meid"和“ril.gsm.imei"获取

/**
 *  5.0统一使用这个获取IMEI IMEI2 MEID
 *
 * @param ctx
 * @return
 */
@TargetApi(Build.VERSION_CODES.M)
public static Map getImeiAndMeid(Context ctx) {
    Map<String, String> map = new HashMap<String, String>();
    TelephonyManager mTelephonyManager = (TelephonyManager) ctx.getSystemService(Activity.TELEPHONY_SERVICE);
    Class<?> clazz = null;
    Method method = null;//(int slotId)

    try {
        clazz = Class.forName("android.os.SystemProperties");
        method = clazz.getMethod("get", String.class, String.class);
        String gsm = (String) method.invoke(null, "ril.gsm.imei", "");
        String meid = (String) method.invoke(null, "ril.cdma.meid", "");
        map.put("meid", meid);
        if (!TextUtils.isEmpty(gsm)) {
            //the value of gsm like:xxxxxx,xxxxxx
            String imeiArray[] = gsm.split(",");
            if (imeiArray != null && imeiArray.length > 0) {
                map.put("imei1", imeiArray[0]);

                if (imeiArray.length > 1) {
                    map.put("imei2", imeiArray[1]);
                } else {
                    map.put("imei2", mTelephonyManager.getDeviceId(1));
                }
            } else {
                map.put("imei1", mTelephonyManager.getDeviceId(0));
                map.put("imei2", mTelephonyManager.getDeviceId(1));
            }
        } else {
            map.put("imei1", mTelephonyManager.getDeviceId(0));
            map.put("imei2", mTelephonyManager.getDeviceId(1));

        }

    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
    return map;
}

而在6.0系统以后,安卓官方已经支持双卡的api了。获取更加简单,此时如果IMEI不存在直接返回null。所以直接去拿MEID。

@TargetApi(Build.VERSION_CODES.O)
public static Map getIMEIforO(Context context) {
    Map<String, String> map = new HashMap<String, String>();
    TelephonyManager tm = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE));
    String imei1 = tm.getImei(0);
    String imei2 = tm.getImei(1);
    if(TextUtils.isEmpty(imei1)&&TextUtils.isEmpty(imei2)){

        map.put("imei1", tm.getMeid()); //如果CDMA制式手机返回MEID
    }else {
        map.put("imei1", imei1);

        map.put("imei2", imei2);
    }
    return map;
}

看一下getImei源码:

/**
 * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
 * available.
 *
 * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
 * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
 *
 * @param slotIndex of which IMEI is returned
 */
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public String getImei(int slotIndex) {
    ITelephony telephony = getITelephony();
    if (telephony == null) return null;

    try {
        return telephony.getImeiForSlot(slotIndex, getOpPackageName());
    } catch (RemoteException ex) {
        return null;
    } catch (NullPointerException ex) {
        return null;
    }
}

以上就是不同系统如何获取唯一标识的方式。 下面放上完整工具类文件。

/**
 * 工程里关于imei和meid获取不正确。避免修改后影响工程特单独写一份。
 * 或许二次开发使用本工具类
 */
public class PhoneInfoUtils {


    /**
     * 获取当前手机系统版本号
     *
     * @return 系统版本号
     */
    public static String getSystemVersion() {
        return Build.DISPLAY;
        //return android.os.Build.VERSION.RELEASE;

    }


    /**
     * 获取手机型号
     *
     * @return 手机型号
     */
    public static String getSystemModel() {
        return Build.MODEL;
    }

    /**
     * 获取手机厂商
     *
     * @return 手机厂商
     */
    public static String getDeviceBrand() {
        return Build.BRAND;
    }


    /**
     * 获取SN
     *
     * @return
     */
    public static String getSn(Context ctx) {
        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) {

        }

        return serial;
    }

    /**
     * 系统4.0的时候
     * 获取手机IMEI 或者MEID
     *
     * @return 手机IMEI
     */
    public static String getImeiOrMeid(Context ctx) {
        TelephonyManager manager = (TelephonyManager) ctx.getSystemService(Activity.TELEPHONY_SERVICE);
        if (manager != null) {
            return manager.getDeviceId();
        }

        return null;
    }

    /**
     * 拿到imei或者meid后判断是有多少位数
     *
     * @param ctx
     * @return
     */
    public static int getNumber(Context ctx) {
        return getImeiOrMeid(ctx).trim().length();
    }


    /**
     *  5.0统一使用这个获取IMEI IMEI2 MEID
     *
     * @param ctx
     * @return
     */
    @TargetApi(Build.VERSION_CODES.M)
    public static Map getImeiAndMeid(Context ctx) {
        Map<String, String> map = new HashMap<String, String>();
        TelephonyManager mTelephonyManager = (TelephonyManager) ctx.getSystemService(Activity.TELEPHONY_SERVICE);
        Class<?> clazz = null;
        Method method = null;//(int slotId)

        try {
            clazz = Class.forName("android.os.SystemProperties");
            method = clazz.getMethod("get", String.class, String.class);
            String gsm = (String) method.invoke(null, "ril.gsm.imei", "");
            String meid = (String) method.invoke(null, "ril.cdma.meid", "");
            map.put("meid", meid);
            if (!TextUtils.isEmpty(gsm)) {
                //the value of gsm like:xxxxxx,xxxxxx
                String imeiArray[] = gsm.split(",");
                if (imeiArray != null && imeiArray.length > 0) {
                    map.put("imei1", imeiArray[0]);

                    if (imeiArray.length > 1) {
                        map.put("imei2", imeiArray[1]);
                    } else {
                        map.put("imei2", mTelephonyManager.getDeviceId(1));
                    }
                } else {
                    map.put("imei1", mTelephonyManager.getDeviceId(0));
                    map.put("imei2", mTelephonyManager.getDeviceId(1));
                }
            } else {
                map.put("imei1", mTelephonyManager.getDeviceId(0));
                map.put("imei2", mTelephonyManager.getDeviceId(1));

            }

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return map;
    }

    @TargetApi(Build.VERSION_CODES.O)
    public static Map getIMEIforO(Context context) {
        Map<String, String> map = new HashMap<String, String>();
        TelephonyManager tm = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE));
        String imei1 = tm.getImei(0);
        String imei2 = tm.getImei(1);
        if(TextUtils.isEmpty(imei1)&&TextUtils.isEmpty(imei2)){

            map.put("imei1", tm.getMeid()); //如果CDMA制式手机返回MEID
        }else {
            map.put("imei1", imei1);

            map.put("imei2", imei2);
        }
        return map;
    }


    /**
     * 获取版本号
     *
     * @param context
     * @return
     */
    public static int getVerCode(Context context) {
        int vercoe = 0;

        try {
            PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
            vercoe = packageInfo.versionCode;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();

        }
        return vercoe;
    }




    public static String getIMEI(Context ctx){
        String imei = "";
        if(Build.VERSION.SDK_INT<Build.VERSION_CODES.M){  //4.0以下 直接获取
            imei = getImeiOrMeid(ctx);
        }else if(Build.VERSION.SDK_INT<Build.VERSION_CODES.O){ //5。0,6。0系统
            Map imeiMaps = getImeiAndMeid(ctx);
            imei = getTransform(imeiMaps);
        }else{
            Map imeiMaps = getIMEIforO(ctx);

            imei = getTransform(imeiMaps);
        }

        return imei;
    }


    private static String getTransform( Map imeiMaps){
        String imei = "";
        if(imeiMaps!=null){
            String imei1 = (String) imeiMaps.get("imei1");
            if(TextUtils.isEmpty(imei1)){
                return imei;
            }
            String imei2 = (String) imeiMaps.get("imei2");
            if(imei2!=null) {
                if (imei1.trim().length() == 15 && imei2.trim().length() == 15) {
                    //如果两个位数都是15。说明都是有效IMEI。根据从大到小排列
                    long i1 = Long.parseLong(imei1.trim());
                    long i2 = Long.parseLong(imei2.trim());
                    if(i1 > i2){
                        imei = imei2+";"+imei1;
                    }else{
                        imei = imei1+";"+imei2;
                    }

                }else{  //
                    if(imei1.trim().length() == 15){
                        //如果只有imei1是有效的
                        imei = imei1;
                    }else if (imei2.trim().length() == 15){
                        //如果只有imei2是有效的
                        imei = imei2;
                    }else{
                        //如果都无效那么都为meid。只取一个就可以
                        imei = imei1;
                    }

                }
            }else{
                imei = imei1;
            }
        }
        return imei;
    }
}

参考链接:
https://www.jianshu.com/p/740e9116281f
https://blog.csdn.net/yangbin0513/article/details/68490291

相关文章

网友评论

      本文标题:如何正确获取IMEI及MEID

      本文链接:https://www.haomeiwen.com/subject/wdmuxqtx.html