Android 各個版本獲取IMEI、MEID

MEID/IMEI1/IMEI2 (可通過在手機撥號鍵盤中輸入 *#06# 即可查詢)

IMEI:(International Mobile Equipment Identity)國際移動設備身份碼的縮寫,由15~17(中國基本都15)位數字組成的電子串號與每臺手機一一對應,且該碼全世界唯一。

MEID:(Mobile Equipment Identifier)移動設備識別碼,是CDMA手機的身份識別碼,也是每臺CDMA手機或通訊平板唯一的識別碼,由14位數字組成。

在中國 移動卡和聯通卡使用的是GSM制式 即使用IMEI , 電信卡使用CDMA制式 即使用MEID

Android個版本獲取MEID/IMEI情況

  • 版本 <= 4.x 如果想獲取MEID/IMEI1/IMEI2 ----其實是很難做到的。 因爲你只能只用getDeviceId() 這個方法
  • 版本 = 5.x 的系統如果想獲取MEID/IMEI1/IMEI2 ----framework層提供了兩個屬性值“ril.cdma.meid"和“ril.gsm.imei"獲取meid、imei1、imei2(必須使用反射方法) 也可以用getDeviceId()獲取
  • 6.0 >= 版本 < 8 的系統如果想獲取MEID/IMEI1/IMEI2 ---- 可以通過 getDeviceId() 獲取
  • 8.0 <= 版本 <10 可以通過個getMeid/getImei 優先獲取對應值 也可以通過 getDeviceId() 獲取
  • 版本 >= 10 系統API限制,無法獲取到imei/meid 以上方法均無效

注意:
getDeviceId(): 能獲取到imei 或 meid ,會根據插得卡返回對應值,不插卡或插移動聯通卡默認返回imei 插電信卡則返回meid
getMeid():8.0及以後調用 返回meid
getImei():8.0及以後調用 返回imei

工具類

 public class IMEIUtil {

    /**
     * 獲取默認的imei  一般都是IMEI 1
     *
     * @param context
     * @return
     */
    public static String getIMEI1(Context context) {
        //優先獲取IMEI(即使是電信卡)  不行的話就獲取MEID
        return getImeiOrMeid(context, 0);

    }

    /**
     * 獲取imei2
     *
     * @param context
     * @return
     */
    public static String getIMEI2(Context context) {
        //imei2必須與 imei1不一樣
        String imeiDefault = getIMEI1(context);
        if (TextUtils.isEmpty(imeiDefault)) {
            //默認的 imei 竟然爲空,說明權限還沒拿到,或者是平板
            //這種情況下,返回 imei2也應該是空串
            return "";
        }

        //注意,拿第一個 IMEI 是傳0,第2個 IMEI 是傳1,別搞錯了
        String imei1 = getImeiOrMeid(context, 0);
        String imei2 = getImeiOrMeid(context, 1);
        //sim 卡換卡位時,imei1與 imei2有可能互換,而 imeidefault 有可能不變
        if (!TextUtils.equals(imei2, imeiDefault)) {
            //返回與 imeiDefault 不一樣的
            return imei2;
        }
        if (!TextUtils.equals(imei1, imeiDefault)) {
            return imei1;
        }
        return "";
    }

    /**
     * 獲取 Imei/Meid    優先獲取IMEI(即使是電信卡)  不行的話就獲取MEID
     * <p>
     * 如果裝有CDMA制式的SIM卡(電信卡) ,在Android 8 以下 只能獲取MEID ,無法獲取到該卡槽的IMEI
     * 8及以上可以通過 #imei 方法獲取IMEI  通過 #deviceId 方法獲取的是MEID
     *
     * @param context
     * @param slotId  slotId爲卡槽Id,它的值爲 0、1;
     * @return
     */
    public static String getImeiOrMeid(Context context, int slotId) {
        String imei = "";

        //Android 6.0 以後需要獲取動態權限  檢查權限
        if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
            return imei;
        }

        try {
            TelephonyManager manager = (TelephonyManager) context.getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE);
            if (manager != null) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {// android 8 即以後建議用getImei 方法獲取 不會獲取到MEID
                    Method method = manager.getClass().getMethod("getImei", int.class);
                    imei = (String) method.invoke(manager, slotId);
                } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    //5.0的系統如果想獲取MEID/IMEI1/IMEI2  ----framework層提供了兩個屬性值“ril.cdma.meid"和“ril.gsm.imei"獲取
                    imei = getSystemPropertyByReflect("ril.gsm.imei");
                    //如果獲取不到 就調用 getDeviceId 方法獲取

                } else {//5.0以下獲取imei/meid只能通過 getDeviceId  方法去取
                }
            }
        } catch (Exception e) {
        }

        if (TextUtils.isEmpty(imei)) {
            imei = getDeviceId(context, slotId);
        }
        return imei;
    }


    /**
     * 僅獲取 Imei  如果獲取到的是meid 或空  均返回空字符串
     *
     * @param slotId slotId爲卡槽Id,它的值爲 0、1;
     * @return
     */
    public static String getImeiOnly(Context context, int slotId) {
        String imei = "";

        //Android 6.0 以後需要獲取動態權限  檢查權限
        if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
            return imei;
        }

        try {
            TelephonyManager manager = (TelephonyManager) context.getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE);
            if (manager != null) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {// android 8 即以後建議用getImei 方法獲取 不會獲取到MEID
                    Method method = manager.getClass().getMethod("getImei", int.class);
                    imei = (String) method.invoke(manager, slotId);
                } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    //5.0的系統如果想獲取MEID/IMEI1/IMEI2  ----framework層提供了兩個屬性值“ril.cdma.meid"和“ril.gsm.imei"獲取
                    imei = getSystemPropertyByReflect("ril.gsm.imei");
                    //如果獲取不到 就調用 getDeviceId 方法獲取

                } else {//5.0以下獲取imei/meid只能通過 getDeviceId  方法去取
                }
            }
        } catch (Exception e) {
        }

        if (TextUtils.isEmpty(imei)) {
            String imeiOrMeid = getDeviceId(context, slotId);
            //長度15 的是imei  14的是meid
            if (!TextUtils.isEmpty(imeiOrMeid) && imeiOrMeid.length() >= 15) {
                imei = imeiOrMeid;
            }
        }

        return imei;
    }

    /**
     * 僅獲取 Meid  如果獲取到的是imei 或空  均返回空字符串
     * 一般只有一個 meid  即獲取到的二個是相同的
     *
     * @param context
     * @param slotId  slotId爲卡槽Id,它的值爲 0、1;
     * @return
     */
    public static String getMeidOnly(Context context, int slotId) {
        String meid = "";
        //Android 6.0 以後需要獲取動態權限  檢查權限
        if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
            return meid;
        }
        try {
            TelephonyManager manager = (TelephonyManager) context.getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE);
            if (manager != null) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {// android 8 即以後建議用getMeid 方法獲取 不會獲取到Imei
                    Method method = manager.getClass().getMethod("getMeid", int.class);
                    meid = (String) method.invoke(manager, slotId);
                } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    //5.0的系統如果想獲取MEID/IMEI1/IMEI2  ----framework層提供了兩個屬性值“ril.cdma.meid"和“ril.gsm.imei"獲取
                    meid = getSystemPropertyByReflect("ril.cdma.meid");
                    //如果獲取不到 就調用 getDeviceId 方法獲取

                } else {//5.0以下獲取imei/meid只能通過 getDeviceId  方法去取
                }
            }
        } catch (Exception e) {
        }

        if (TextUtils.isEmpty(meid)) {
            String imeiOrMeid = getDeviceId(context, slotId);
            //長度15 的是imei  14的是meid
            if (imeiOrMeid.length() == 14) {
                meid = imeiOrMeid;
            }
        }
        return meid;
    }


    private static String getSystemPropertyByReflect(String key) {
        try {
            @SuppressLint("PrivateApi")
            Class<?> clz = Class.forName("android.os.SystemProperties");
            Method getMethod = clz.getMethod("get", String.class, String.class);
            return (String) getMethod.invoke(clz, key, "");
        } catch (Exception e) {/**/}
        return "";
    }

    /**
     * 獲取 IMEI/MEID
     *
     * @param context 上下文
     * @return 獲取到的值 或者 空串""
     */
    public static String getDeviceId(Context context) {
        String imei = "";
        //Android 6.0 以後需要獲取動態權限  檢查權限
        if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
            return imei;
        }

        // 1. 嘗試通過系統api獲取imei
        imei = getDeviceIdFromSystemApi(context);
        if (TextUtils.isEmpty(imei)) {
            imei = getDeviceIdByReflect(context);
        }
        return imei;
    }

    /**
     * 獲取 IMEI/MEID
     *
     * @param context 上下文
     * @param slotId  slotId爲卡槽Id,它的值爲 0、1;
     * @return 獲取到的值 或者 空串""
     */
    public static String getDeviceId(Context context, int slotId) {
        String imei = "";
        // 1. 嘗試通過系統api獲取imei
        imei = getDeviceIdFromSystemApi(context, slotId);
        if (TextUtils.isEmpty(imei)) {
            imei = getDeviceIdByReflect(context, slotId);
        }
        return imei;
    }

    /**
     * 調用系統接口獲取 IMEI/MEID
     * <p>
     * Android 6.0之後如果用戶不允許通過 {@link Manifest.permission#READ_PHONE_STATE} 權限的話,
     * 那麼是沒辦法通過系統api進行獲取 IMEI/MEID 的,但是可以通過{@linkplain #getDeviceIdByReflect(Context)} 反射}繞過權限進行獲取
     *
     * @param context 上下文
     * @return 獲取到的值 或者 空串""
     */
    public static String getDeviceIdFromSystemApi(Context context, int slotId) {
        String imei = "";
        try {
            TelephonyManager telephonyManager =
                    (TelephonyManager) context.getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE);
            if (telephonyManager != null) {
                imei = telephonyManager.getDeviceId(slotId);
            }
        } catch (Throwable e) {
        }
        return imei;
    }

    public static String getDeviceIdFromSystemApi(Context context) {
        String imei = "";
        try {
            TelephonyManager telephonyManager =
                    (TelephonyManager) context.getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE);
            if (telephonyManager != null) {
                imei = telephonyManager.getDeviceId();
            }
        } catch (Throwable e) {
        }
        return imei;
    }


    /**
     * 反射獲取 IMEI/MEID
     * <p>
     * Android 6.0之後如果用戶不允許通過 {@link Manifest.permission#READ_PHONE_STATE} 權限的話,
     * 那麼是沒辦法通過系統api進行獲取 IMEI/MEID 的,但是可以通過這個反射來嘗試繞過權限進行獲取
     *
     * @param context 上下文
     * @return 獲取到的值 或者 空串""
     */
    public static String getDeviceIdByReflect(Context context) {
        try {
            TelephonyManager tm = (TelephonyManager) context.getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE);
            if (Build.VERSION.SDK_INT >= 21) {
                Method simMethod = TelephonyManager.class.getDeclaredMethod("getDefaultSim");
                Object sim = simMethod.invoke(tm);
                Method method = TelephonyManager.class.getDeclaredMethod("getDeviceId", int.class);
                return method.invoke(tm, sim).toString();
            } else {
                Class<?> clazz = Class.forName("com.android.internal.telephony.IPhoneSubInfo");
                Method subInfoMethod = TelephonyManager.class.getDeclaredMethod("getSubscriberInfo");
                subInfoMethod.setAccessible(true);
                Object subInfo = subInfoMethod.invoke(tm);
                Method method = clazz.getDeclaredMethod("getDeviceId");
                return method.invoke(subInfo).toString();
            }
        } catch (Throwable e) {

        }
        return "";
    }

    /**
     * 反射獲取 deviceId
     *
     * @param context
     * @param slotId  slotId爲卡槽Id,它的值爲 0、1;
     * @return
     */
    public static String getDeviceIdByReflect(Context context, int slotId) {
        try {
            TelephonyManager tm = (TelephonyManager) context.getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE);
            Method method = tm.getClass().getMethod("getDeviceId", int.class);
            return method.invoke(tm, slotId).toString();
        } catch (Throwable e) {
        }
        return "";
    }


}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章