发送手机物理标识请求

注册 Vultr VPS 送你10美金 免费玩4个月

作为应用的重要关注指标-激活, 需要在每次启动时, 都上传手机的物理标识到服务器. 我来简述一下需要上传的信息.

Android

1. 获取

激活信息主要包含:
1. AppId: 公司拥有多款应用, 标示本应用的类型.
2. Platform: 平台Android或者iOS.
3. SystemVersion: 在Android中, 代表Android版本号.
4. Version: 本应用的版本.
5. AppVersion: 本应用的服务器接口版本.
6. IMEI(International Mobile Equipment Identity): 国际移动设备标识, 手机身份.
7. DeviceId: 设备标示, 一般是IMEI, 也可以是Mac地址.
8. Mac: WIFI的Mac地址.
9. SecureId: 设备随机生成的第一个引导, 间接获取设备寿命.
10. InstallId: 应用安装时间.
11. MODEL: 设备型号
12. MANUFACTURER: 设备生产工厂.

其中获取DeviceId需要权限: READ_PHONE_STATE; 获取Mac地址需要权限ACCESS_WIFI_STATE.

/**
 * 获取手机标示, 用于激活信息, 需要权限
 * READ_PHONE_STATE和ACCESS_WIFI_STATE
 * <p/>
 * Created by wangchenlong on 15/12/11.
 */
public class ActiveInfo {

    @SuppressWarnings("unused")
    private static final String TAG = "DEBUG-WCL: " + ActiveInfo.class.getSimpleName();

    @SuppressWarnings("all")
    private static final String STAT_INFO_STR =
            "app=%s&platform=android&systemVer=%s&version=%s&app_ver=%s&imei=%s"
                    + "&device_id=%s&mac=%s&secureId=%s&installId=%s&phoneType=%s_by_%s&vendor=";

    private static final String APP_ID = "512";
    private static final String VENDOR = "PedometerLibrary";

    private static final int HTTP_OK = 200; // 请求成功
    private static final int TIME_OUT = 5 * 1000; // 连接超时时间

    private static ActiveInfo sInstance;

    private final Context mContext;
    private final SharedPreferences mPrefs;
    private final TelephonyManager mTelephonyManager;
    private final WifiManager mWifiManager;

    private ActiveInfo() {
        mContext = PedometerManager.getInstance().getContext();
        mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
        mTelephonyManager = (TelephonyManager) mContext
                .getSystemService(Context.TELEPHONY_SERVICE);
        mWifiManager = (WifiManager) (mContext.getSystemService(Context.WIFI_SERVICE));
    }

    public static ActiveInfo getInstance() {
        if (sInstance == null) {
            sInstance = new ActiveInfo();
        }
        return sInstance;
    }

    // 获取签名信息
    private String getPhoneSignature() {
        String signature; // 手机标示
        signature = String.format(STAT_INFO_STR,
                getUrlString(APP_ID), // 应用ID
                getUrlString(android.os.Build.VERSION.RELEASE), // 系统版本
                getUrlString(BuildConfig.VERSION_NAME), // SDK版本
                getUrlString(BuildConfig.VERSION_NAME), // 服务器版本
                getUrlString(getDeviceId()), // IMEI
                getUrlString(getDeviceId()), // 设备ID
                getUrlString(getMac()), // Mac地址
                getUrlString(getSecureId()), // 安全ID
                getUrlString(getInstallId()), // 安装时间
                getUrlString(android.os.Build.MODEL), // 设备型号
                getUrlString(android.os.Build.MANUFACTURER)); // 设备工厂
        signature += VENDOR;

        return signature;
    }

    private String getUrlString(String s) {
        //noinspection deprecation
        return URLEncoder.encode(s);
    }

    // 获取设备ID, 需要权限READ_PHONE_STATE
    private String getDeviceId() {
        if (mPrefs.contains(PrefsConsts.PHONE_INFO_DEVICE_ID_PREFS)) {
            return mPrefs.getString(PrefsConsts.PHONE_INFO_DEVICE_ID_PREFS, "");
        } else {
            mPrefs.edit().putString(
                    PrefsConsts.PHONE_INFO_DEVICE_ID_PREFS,
                    mTelephonyManager.getDeviceId())
                    .apply();
            return mPrefs.getString(PrefsConsts.PHONE_INFO_DEVICE_ID_PREFS, "");
        }
    }

    // 获取Mac地址, 需要权限ACCESS_WIFI_STATE
    private String getMac() {
        if (mPrefs.contains(PrefsConsts.PHONE_INFO_MAC_ADDRESS_PREFS)) {
            return mPrefs.getString(PrefsConsts.PHONE_INFO_MAC_ADDRESS_PREFS, "");
        } else {
            mPrefs.edit().putString(
                    PrefsConsts.PHONE_INFO_MAC_ADDRESS_PREFS,
                    mWifiManager.getConnectionInfo().getMacAddress())
                    .apply();
            return mPrefs.getString(PrefsConsts.PHONE_INFO_MAC_ADDRESS_PREFS, "");
        }
    }

    // 获取安全ID
    private String getSecureId() {
        if (mPrefs.contains(PrefsConsts.PHONE_INFO_SECURE_ID_PREFS)) {
            return mPrefs.getString(PrefsConsts.PHONE_INFO_SECURE_ID_PREFS, "");
        } else {
            mPrefs.edit().putString(
                    PrefsConsts.PHONE_INFO_SECURE_ID_PREFS,
                    Settings.Secure.getString(
                            mContext.getContentResolver(), Settings.Secure.ANDROID_ID))
                    .apply();
            return mPrefs.getString(PrefsConsts.PHONE_INFO_SECURE_ID_PREFS, "");
        }
    }

    // 获取安装时的系统时间
    private String getInstallId() {
        if (mPrefs.contains(PrefsConsts.PHONE_INFO_INSTALL_ID_PREFS)) {
            return mPrefs.getString(PrefsConsts.PHONE_INFO_INSTALL_ID_PREFS, "");
        } else {
            mPrefs.edit().putString(
                    PrefsConsts.PHONE_INFO_INSTALL_ID_PREFS,
                    String.format("%d", System.currentTimeMillis()))
                    .apply();
            return mPrefs.getString(PrefsConsts.PHONE_INFO_INSTALL_ID_PREFS, "");
        }
    }

    // 发送激活信息
    public void postActiveInfo() {
        String path = "http://api.chunyu.me/api/launch_request/?";
        path += getPhoneSignature();
        Log.d(TAG, "url: " + path);
        try {
            URL url = new URL(path);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setConnectTimeout(TIME_OUT); // 连接超时
            connection.connect();
            if (connection.getResponseCode() == HTTP_OK) {
                Log.d(TAG, " 发送激活信息成功");
            } else {
                Log.e(TAG, " 发送激活信息失败: " + connection.getResponseCode());
            }
            connection.disconnect();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

为了减少获取代价, 可以存储一些关键值到首选项.

2. 上传

网络请求必须使用异步线程, 我们只是上传激活, 并不关心返回值, 比较简单.

new PostActiveTask().execute(); // 发送激活信息
    // 发送激活任务的线程
    private class PostActiveTask extends AsyncTask<Void, Void, Void> {
        @Override
        protected Void doInBackground(Void... params) {
            ActiveInfo.getInstance().postActiveInfo();
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
        }
    }

返回均为空值, 在doInBackground中, 需要注明return null.

OK, Enjoy It.

注册 Vultr VPS 送你10美金 免费玩4个月