package com.samknows.libcore;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.telephony.TelephonyManager;
import android.util.Log;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.security.MessageDigest;
import java.util.Date;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
import com.samknows.measurement.util.OtherUtils;
import com.samknows.measurement.util.SKDateFormat;
public class SKOperators {
public enum SKOperators_Return {
SKOperators_Return_NoThrottleQuery,
SKOperators_Return_FiredThrottleQueryAwaitCallback
}
public class SKThrottledQueryResult {
public SKOperators_Return returnCode;
public final String timestamp; // Unix time - seconds since 1970
//public String datetimeUTCMilliZ; // UTC string
public final String datetimeUTCSimple; // UTC string
public String carrier;
public SKThrottledQueryResult() {
Date now = new Date();
returnCode = SKOperators_Return.SKOperators_Return_NoThrottleQuery;
timestamp = String.valueOf(now.getTime());
//datetimeUTCMilliZ = SKDateFormat.sGetDateAsIso8601StringMilliZ(now);
datetimeUTCSimple = SKDateFormat.sGetDateAsIso8601String(now);
carrier = "";
}
}
public interface ISKQueryCompleted {
void onQueryCompleted(Exception e, long responseCode, String responseDataAsString);
}
private Context mContext = null;
JSONArray mpOperatorArray = null;
private String loadOperatorData() {
InputStream theInputStream = null;
try {
theInputStream = this.mContext.getAssets().open("operators.json");
} catch (IOException e) {
return null;
}
Writer writer = new StringWriter();
char[] buffer = new char[1024];
try {
Reader reader = new BufferedReader(new InputStreamReader(theInputStream));
int n;
while ((n = reader.read(buffer)) != -1) {
writer.write(buffer, 0, n);
}
} catch (UnsupportedEncodingException e) {
SKPorting.sAssert(getClass(), false);
return null;
} catch (IOException e) {
SKPorting.sAssert(getClass(), false);
return null;
} finally {
try {
theInputStream.close();
} catch (IOException e) {
SKPorting.sAssert(getClass(), false);
}
}
String jsonString = writer.toString();
return jsonString;
}
private SKOperators(Context context) {
this.mContext = context;
String jsonString = loadOperatorData();
if (jsonString == null) {
return;
}
try {
JSONObject mainObject = new JSONObject(jsonString);
mpOperatorArray = mainObject.getJSONArray("operators");
int items = mpOperatorArray.length();
if (items <= 0) {
SKPorting.sAssert(getClass(), false);
}
int index = 0;
for (index = 0; index < items; index++) {
JSONObject operator = mpOperatorArray.getJSONObject(index);
try {
String value = operator.getString("name");
SKPorting.sAssert(getClass(), value.length() > 0);
} catch (JSONException e) {
SKPorting.sAssert(getClass(), false);
}
try {
String value = operator.getString("class");
SKPorting.sAssert(getClass(), value.length() > 0);
SKPorting.sAssert(getClass(), (value.equals("isthrottledwebservice")) || (value.equals("isthrottledwebservice_test")));
} catch (JSONException e) {
SKPorting.sAssert(getClass(), false);
}
try {
JSONArray value = operator.getJSONArray("mcc+mnc");
SKPorting.sAssert(getClass(), value.length() > 0);
int theMccMncArrayItems = value.length();
int theMccMncIndex = 0;
for (theMccMncIndex = 0; theMccMncIndex < theMccMncArrayItems; theMccMncIndex++) {
String theText = value.getString(theMccMncIndex);
SKPorting.sAssert(getClass(), theText.length() >= 5);
SKPorting.sAssert(getClass(), theText.length() <= 6);
}
} catch (JSONException e) {
SKPorting.sAssert(getClass(), false);
}
try {
String value = operator.getString("url");
SKPorting.sAssert(getClass(), value.length() > 0);
} catch (JSONException e) {
SKPorting.sAssert(getClass(), false);
}
try {
String value = operator.getString("username");
SKPorting.sAssert(getClass(), value.length() > 0);
} catch (JSONException e) {
SKPorting.sAssert(getClass(), false);
}
try {
String value = operator.getString("password");
SKPorting.sAssert(getClass(), value.length() > 0);
} catch (JSONException e) {
SKPorting.sAssert(getClass(), false);
}
}
} catch (JSONException e) {
SKPorting.sAssert(getClass(), false);
}
}
//
// Singleton method...
//
private static SKOperators sOperatorInstance = null;
public static synchronized SKOperators getInstance(Context context) {
if (sOperatorInstance == null) {
sOperatorInstance = new SKOperators(context);
}
return sOperatorInstance;
}
/**
* Returns the SHA-1 hashcode of the given string.
*
* @param s the string to be hashed.
* @return the SHA-1 hashcode of {@code s}.
*/
// http://stackoverflow.com/questions/5980658/how-to-sha1-hash-a-string-in-android
private String getSha1(String s) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(s.getBytes());
byte[] bytes = md.digest();
StringBuilder buffer = new StringBuilder();
for (byte aByte : bytes) {
String tmp = Integer.toString((aByte & 0xff) + 0x100, 16).substring(1);
buffer.append(tmp);
}
return buffer.toString();
}
//
//
//
// Returns object containing immediate results.
// The async part of the result, will occur later (if at all!)
// Private method (can be used for mock testing purposes)
public SKThrottledQueryResult fireThrottledWebServiceQueryForDeviceMccMnc(String deviceMccMnc, final ISKQueryCompleted callback) {
final SKThrottledQueryResult throttledQueryResult = new SKThrottledQueryResult();
if (mpOperatorArray == null) {
// No operators at all!
return throttledQueryResult;
}
String lookForService = "isthrottledwebservice";
if (OtherUtils.isThisDeviceAnEmulator() == true) {
if (OtherUtils.isDebuggable(mContext)) {
// On emulator in debug mode...!
SKPorting.sAssert(getClass(), deviceMccMnc.length() == 0);
lookForService = "isthrottledwebservice_test";
if (deviceMccMnc.length() == 0) {
deviceMccMnc = "tester";
}
}
}
Log.d(getClass().getName(), "DEBUG: search for (" + lookForService + "), using deviceMccMnc=(" + deviceMccMnc + ")");
try {
// Every entry must contain valid data!
int items = mpOperatorArray.length();
if (items <= 0) {
SKPorting.sAssert(getClass(), false);
}
int index = 0;
for (index = 0; index < items; index++) {
JSONObject operator = mpOperatorArray.getJSONObject(index);
String theClassName = null;
theClassName = operator.getString("class");
if (theClassName.equals(lookForService)) {
// This is a potential match by MMC/MNC!
JSONArray value = operator.getJSONArray("mcc+mnc");
SKPorting.sAssert(getClass(), value.length() > 0);
int theMccMncArrayItems = value.length();
int theMccMncIndex = 0;
for (theMccMncIndex = 0; theMccMncIndex < theMccMncArrayItems; theMccMncIndex++) {
String mccMnc = value.getString(theMccMncIndex);
if (mccMnc.equals(deviceMccMnc)) {
// Match!
final String urlString = operator.getString("url");
throttledQueryResult.carrier = operator.getString("name");
/*
The following items shall be sent to the API via the REQUEST HEADERS:
username - provide
password - combination (concatenation of first 8 numbers/characters of UTC field
and the CODEWORD, and then SHA-1 hashed ...!)
UTC - UTC time.
For example, the password will be sha1sum(2014-02-codeword) ...
*/
String username = operator.getString("username");
String utcDateTimeSimple = throttledQueryResult.datetimeUTCSimple;
String passwordBase = operator.getString("password");
StringBuilder passwordSb = new StringBuilder();
passwordSb.append(utcDateTimeSimple.substring(0, 8));
passwordSb.append(passwordBase);
// The following will be something like "2014-03-dummypassword"
String passwordTemp = passwordSb.toString();
// The following will be something like "f4401e54f4472a576281375e3f89f87a8e7547af"
String password = getSha1(passwordTemp);
throttledQueryResult.returnCode = SKOperators_Return.SKOperators_Return_FiredThrottleQueryAwaitCallback;
AsyncHttpClient client = new AsyncHttpClient();
client.getHttpClient().getParams().setParameter("username", username);
client.getHttpClient().getParams().setParameter("UTC", utcDateTimeSimple);
client.getHttpClient().getParams().setParameter("password", password);
Log.d(getClass().getName(), "DEBUG: fire throttle query at (" + urlString + ")");
final AsyncHttpClient theClient = client;
// The internal can interfere with the external run loop.
// So, we fire this query under ownership the MAIN UI thread, to prevent the hang.
// TODO - investigate if we could fire this instead in a separate thread.
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
// Code here will run in UI thread
theClient.get(urlString, new AsyncHttpResponseHandler() {
int responseCode = 200;
@Override
public void sendResponseMessage(HttpResponse response) {
responseCode = response.getStatusLine().getStatusCode();
try {
super.sendResponseMessage(response);
} catch (IOException e) {
SKPorting.sAssert(getClass(), false);
}
}
@Override
public void onSuccess(int statusCode,
Header[] headers, byte[] responseBody) {
String response = String.valueOf(responseBody);
String trimmed = response.trim();
callback.onQueryCompleted(null, responseCode, trimmed);
}
@Override
public void onFailure(int statusCode,
Header[] headers, byte[] responseBody,
Throwable error) {
callback.onQueryCompleted(new Exception(error), responseCode, "");
}
});
}
});
return throttledQueryResult;
}
}
}
}
} catch (JSONException e) {
// An error! Ignore this...
SKPorting.sAssert(getClass(), false);
throttledQueryResult.returnCode = SKOperators_Return.SKOperators_Return_NoThrottleQuery;
} catch (Exception e) {
SKPorting.sAssert(getClass(), false);
throttledQueryResult.returnCode = SKOperators_Return.SKOperators_Return_NoThrottleQuery;
}
// No operator match!
Log.d(getClass().getName(), "DEBUG: no isthrottledwebservice found for device...");
return throttledQueryResult;
}
// TODO: Public method
public SKThrottledQueryResult fireThrottledWebServiceQueryWithCallback(ISKQueryCompleted callback) {
TelephonyManager manager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
String deviceMccMnc = manager.getSimOperator();
if (OtherUtils.isThisDeviceAnEmulator() == true) {
if (OtherUtils.isDebuggable(mContext)) {
deviceMccMnc = "";
}
}
return fireThrottledWebServiceQueryForDeviceMccMnc(deviceMccMnc, callback);
}
}