package com.xiaomi.xms.sales.xmsf.account.utils;
import android.app.PendingIntent;
import android.content.Context;
import android.telephony.SmsManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import com.xiaomi.xms.sales.request.HostManager.Parameters;
import com.xiaomi.xms.sales.util.Constants;
import com.xiaomi.xms.sales.util.LogUtil;
import com.xiaomi.xms.sales.util.Utils;
import com.xiaomi.xms.sales.xmsf.account.data.AccountInfo;
import com.xiaomi.xms.sales.xmsf.account.exception.AccessDeniedException;
import com.xiaomi.xms.sales.xmsf.account.exception.InvalidCredentialException;
import com.xiaomi.xms.sales.xmsf.account.exception.InvalidResponseException;
import com.xiaomi.xms.sales.xmsf.miui.utils.CarrierSelector;
import com.xiaomi.xms.sales.xmsf.miui.utils.CloudCoder;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
/**
* Helper class to perform account related tasks.
*/
public final class CloudHelper {
private static final String TAG = "CloudHelper";
private static final String LOCATION = "Location";
private static final Integer INT_0 = 0;
/**
* key: imsi, value: array[0] hashed device id, array[1] hashed imsi
*/
private static final Map<String, String> mDeviceInfoMap = new HashMap<String, String>();
private static final CarrierSelector<String> CARRIER_SELECTOR = new CarrierSelector<String>(
CarrierSelector.CARRIER.CHINA_MOBILE);
static {
CARRIER_SELECTOR
.register(CarrierSelector.CARRIER.CHINA_MOBILE, Constants.Account.SMS_GW_CM);
CARRIER_SELECTOR
.register(CarrierSelector.CARRIER.CHINA_UNICOM, Constants.Account.SMS_GW_CU);
CARRIER_SELECTOR
.register(CarrierSelector.CARRIER.CHINA_TELECOM, Constants.Account.SMS_GW_CT);
}
public static AccountInfo getServiceTokenByPassword(String userId,
String password,
String serviceId)
throws IOException, InvalidResponseException,
InvalidCredentialException, AccessDeniedException {
if (userId == null || password == null || serviceId == null) {
throw new NullPointerException("invalid params");
}
EasyMap<String, String> params = new EasyMap<String, String>()
.easyPut("user", userId)
.easyPut("pwd", password)
.easyPut("sid", serviceId);
SimpleRequest.StringContent loginContent = SimpleRequest
.postAsString(Constants.Account.URL_LOGIN_AUTH, params, null, false);
if (loginContent == null) {
throw new IOException("failed to get response from server");
}
return processLoginContent(loginContent);
}
public static AccountInfo getServiceTokenByPassToken(String userId,
String passToken,
String serviceId) throws IOException,
InvalidResponseException, InvalidCredentialException,
AccessDeniedException {
EasyMap<String, String> params = new EasyMap<String, String>()
.easyPut("sid", serviceId);
EasyMap<String, String> cookies = new EasyMap<String, String>()
.easyPut("userId", userId)
.easyPut("passToken", passToken);
LogUtil.d(TAG, "The login url is:" + Constants.Account.URL_LOGIN);
SimpleRequest.StringContent loginContent = SimpleRequest
.getAsString(Constants.Account.URL_LOGIN, params, cookies, false);
if (loginContent == null) {
throw new IOException("failed to get response from service server");
}
return processLoginContent(loginContent);
}
protected static AccountInfo processLoginContent(
SimpleRequest.StringContent loginContent)
throws InvalidResponseException, InvalidCredentialException {
// location where to get the service token
String serviceTokenLocation = loginContent.getHeader(LOCATION);
LogUtil.d(TAG, "The callback url is:" + serviceTokenLocation);
String userId = loginContent.getHeader("userId");
String passToken = loginContent.getHeader("passToken");
String extParams = loginContent.getHeader("extension-pragma");
if (TextUtils.isEmpty(serviceTokenLocation)) {
throw new InvalidCredentialException(
"no get auth location, password mistakes");
}
if (TextUtils.isEmpty(userId)) {
throw new InvalidResponseException("no user Id");
}
if (TextUtils.isEmpty(passToken)) {
throw new InvalidResponseException(
"no passToken in login response");
}
if (TextUtils.isEmpty(extParams)) {
throw new InvalidResponseException("empty extension-pragma");
}
String security = null;
Long nonce = null;
try {
JSONObject jObj = new JSONObject(extParams);
security = jObj.optString("ssecurity");
nonce = jObj.optLong("nonce");
} catch (JSONException e) {
}
if (security == null || nonce == null) {
throw new InvalidResponseException("security or nonce is null");
}
String clientSign = getClientSign(nonce, security);
if (clientSign == null) {
Log.e(TAG, "failed to get client sign");
throw new InvalidResponseException("sign parameters failure");
}
EasyMap<String, String> params = new EasyMap<String, String>("clientSign", clientSign);
// params = params.easyPut("client_id", "180100031013");
SimpleRequest.StringContent serviceTokenContent = null;
try {
serviceTokenContent = SimpleRequest
.getAsString(serviceTokenLocation, params, null, false);
} catch (IOException e) {
e.printStackTrace();
} catch (AccessDeniedException e) {
e.printStackTrace();
}
if (serviceTokenContent == null) {
throw new InvalidResponseException(
"no response when get service token");
}
LogUtil.d(TAG, "The serviceTokenContent is " + serviceTokenContent);
LogUtil.d("test", serviceTokenContent.getHeaders().toString());
String serviceToken = serviceTokenContent
.getHeader("serviceToken");
if (TextUtils.isEmpty(serviceToken)) {
throw new InvalidResponseException(
"no service token contained in response");
}
return new AccountInfo(userId, passToken, serviceToken, security);
}
public static void sendActivateSms(Context context,
PendingIntent sentIntent, String deviceId, String imsi) {
deviceId = hashDeviceInfo(deviceId);
imsi = hashDeviceInfo(imsi);
SmsManager sm = SmsManager.getDefault();
String gw = selectSmsGw(context);
String smsBody = "RP/" + deviceId + "/" + imsi;
sm.sendTextMessage(gw, null, smsBody, sentIntent, null);
}
public static String queryPhone(String deviceId, String imsi)
throws IOException, InvalidResponseException {
deviceId = hashDeviceInfo(deviceId);
imsi = hashDeviceInfo(imsi);
String url = String.format(Constants.Account.URL_QUERY_PHONE, deviceId);
EasyMap<String, String> param = new EasyMap<String, String>("imsi", imsi);
SimpleRequest.MapContent mapContent = null;
try {
mapContent = SimpleRequest
.getAsMap(url, param, null, true);
} catch (AccessDeniedException e) {
e.printStackTrace();
}
if (mapContent == null) {
throw new IOException("failed to get response from server");
}
Object code = mapContent.getFromBody("code");
if (INT_0.equals(code)) {
Object dataObj = mapContent.getFromBody("data");
if (dataObj instanceof Map) {
Map dataMap = (Map) dataObj;
Object phoneObj = dataMap.get("phone");
Object imsiObj = dataMap.get("imsi");
if (imsi.equals(imsiObj)) {
if (phoneObj instanceof String) {
return (String) phoneObj;
}
}
return null;
}
}
throw new InvalidResponseException(
"invalid response from server, description:" + mapContent
.getFromBody("description"));
}
public static void regBySms(Context context, PendingIntent sentIntent,
String deviceId, String imsi, String password) {
deviceId = hashDeviceInfo(deviceId);
imsi = hashDeviceInfo(imsi);
SmsManager sm = SmsManager.getDefault();
String gw = Utils.Preference.getStringPref(context, Constants.Prefence.PREF_KEY_SMS_WG,
Constants.Account.SMS_GW_DEFAULT);
String smsBody = "XM/" + deviceId + "/" + imsi + "/" + password;
sm.sendTextMessage(gw, null, smsBody, sentIntent, null);
}
public static String regByEmail(String email, String password)
throws IOException, InvalidResponseException {
EasyMap<String, String> params = new EasyMap<String, String>()
.easyPut("email", email)
.easyPut("password", password);
SimpleRequest.MapContent regContent = null;
try {
regContent = SimpleRequest
.postAsMap(Constants.Account.URL_REG, params, null, true);
} catch (AccessDeniedException e) {
e.printStackTrace();
}
if (regContent == null) {
throw new IOException("failed to register, no response");
}
Object code = regContent.getFromBody("code");
if (INT_0.equals(code)) {
Object data = regContent.getFromBody("data");
LogUtil.d(TAG, "get data node:" + data);
if (data instanceof Map) {
Map dataMap = (Map) data;
Object userIdObj = dataMap.get("userId");
if (userIdObj instanceof Integer) {
return String.valueOf(userIdObj);
}
}
}
LogUtil.w(TAG, String.format("register failed, code: %s, description: %s",
code, regContent.getFromBody("description")));
throw new InvalidResponseException(
"failed to register due to invalid response from server");
}
public static void sendActivateEmail(String userId, String email)
throws IOException, InvalidResponseException {
EasyMap<String, String> params = new EasyMap<String, String>()
.easyPut("userId", userId)
.easyPut("addressType", "EM")
.easyPut("address", email);
SimpleRequest.MapContent regContent = null;
try {
regContent = SimpleRequest
.getAsMap(Constants.Account.URL_RESEND_EMAIL, params, null, true);
} catch (AccessDeniedException e) {
e.printStackTrace();
}
if (regContent == null) {
throw new IOException("failed to register, no response");
}
Object code = regContent.getFromBody("code");
if (!INT_0.equals(code)) {
throw new InvalidResponseException(
"invalid response, failed to send activate email");
}
}
public static String getUserIdForEmail(String email)
throws IOException, InvalidResponseException {
return getUserId(email, "EM");
}
public static String getUserIdForPhone(String phone)
throws IOException, InvalidResponseException {
return getUserId(phone, "PH");
}
private static String getUserId(String address, String type)
throws IOException, InvalidResponseException {
EasyMap<String, String> param = new EasyMap<String, String>()
.easyPut("type", type)
.easyPut("externalId", address);
SimpleRequest.MapContent mapContent = null;
try {
mapContent = SimpleRequest
.getAsMap(Constants.Account.URL_USER_EXISTS, param, null, true);
} catch (AccessDeniedException e) {
e.printStackTrace();
}
if (mapContent == null) {
throw new IOException(
"failed to get response when getting user id");
}
Object code = mapContent.getFromBody("code");
if (INT_0.equals(code)) {
Object data = mapContent.getFromBody("data");
if (data instanceof Map) {
Map dataMap = (Map) data;
Object userIdObj = dataMap.get("userId");
if (userIdObj instanceof Integer) {
Integer userId = (Integer) userIdObj;
if (userId > 0) {
return String.valueOf(userId);
} else {
LogUtil.d(TAG, "user not registered, id:" + address);
return null;
}
}
LogUtil.w(TAG, "invalid user id:" + userIdObj);
}
}
throw new InvalidResponseException(String.format(
"server error when getting user id, reason:%s, description:%s, code:%s",
mapContent.getFromBody("reason"),
mapContent.getFromBody("description"),
mapContent.getFromBody("code")));
}
protected static String selectSmsGw(Context context) {
TelephonyManager tm = (TelephonyManager) context.getSystemService(
Context.TELEPHONY_SERVICE);
String mccmnc = tm.getNetworkOperator();
return CARRIER_SELECTOR.selectValue(mccmnc);
}
public static String selectSmsGwByServer(Context context) throws IOException,
InvalidResponseException, SocketTimeoutException {
TelephonyManager tm = (TelephonyManager) context.getSystemService(
Context.TELEPHONY_SERVICE);
String mccmnc = tm.getNetworkOperator();
SimpleRequest.StringContent result = null;
EasyMap<String, String> params = new EasyMap<String, String>()
.easyPut("region", mccmnc)
.easyPut(Parameters.Keys.CLIENT_ID, Parameters.Values.CLIENT_ID);
try {
result = SimpleRequest
.getAsString(Constants.Account.URL_QUERY_SMS_GW, params, null, true);
if (result != null) {
if (!TextUtils.isEmpty(result.getBody())) {
JSONObject o = new JSONObject(result.getBody());
int code = o.getInt("code");
if (code != 0) {
throw new InvalidResponseException("fetchSmsGateway: code = " + code);
}
JSONObject data = o.getJSONObject("data");
if (data == null) {
throw new InvalidResponseException("fetchSmsGateway: null data");
}
String gw = data.getString("info");
if (TextUtils.isEmpty(gw)) {
throw new InvalidResponseException("fetchSmsGateway: null gw");
}
return gw;
} else {
throw new InvalidResponseException("fetchSmsGateway: error");
}
} else {
throw new InvalidResponseException("fetchSmsGateway: error");
}
} catch (SocketTimeoutException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (AccessDeniedException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
throw new InvalidResponseException(
"failed to get sms gw due to invalid response from server");
}
protected static String getClientSign(Long nonce, String security) {
TreeMap<String, String> params = new TreeMap<String, String>();
params.put("nonce", String.valueOf(nonce));
return CloudCoder.generateSignature(null, null, params, security);
}
/**
* hash deviceId or imsi
*
* @param plain plain representation of device id or imsi
* @return 16 bytes long hash value
*/
private static String hashDeviceInfo(String plain) {
synchronized (mDeviceInfoMap) {
if (plain == null) {
plain = "0";
}
String hash = mDeviceInfoMap.get(plain);
if (hash == null) {
try {
MessageDigest md = MessageDigest.getInstance("SHA1");
hash = Base64.encodeToString(md.digest(plain.getBytes()),
Base64.URL_SAFE).substring(0, 16);
mDeviceInfoMap.put(plain, hash);
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(
"failed to init SHA1 digest");
}
}
return hash;
}
}
}