package com.samknows.measurement.net; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; import java.util.Locale; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import com.samknows.libcore.SKPorting; import com.samknows.libcore.SKSimpleHttpToJsonQuery; import com.samknows.measurement.SK2AppSettings; import com.samknows.measurement.SKApplication; import com.samknows.measurement.environment.LocationData; import com.samknows.measurement.environment.LocationDataCollector; import com.samknows.measurement.schedule.ScheduleConfig; import com.samknows.measurement.storage.DBHelper; import com.samknows.measurement.storage.PassiveMetric; import com.samknows.measurement.storage.PassiveMetric.METRIC_TYPE; import com.samknows.measurement.storage.ResultsContainer; import com.samknows.measurement.storage.TestResultsManager; import com.samknows.measurement.util.OtherUtils; import android.content.Context; import android.content.Intent; import android.location.Address; import android.location.Geocoder; import android.location.Location; import android.net.Uri; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import android.util.Pair; public class SubmitTestResultsAnonymousAction { static final String TAG = "SubmitTestResAnymAct"; protected final Context context; protected boolean isSuccess = false; public SubmitTestResultsAnonymousAction(Context _context) { context = _context; } // http://stackoverflow.com/questions/6198986/how-can-i-replace-non-printable-unicode-characters-in-java private String getStringWithControlsStripped(String myString) { StringBuilder newString = new StringBuilder(myString.length()); for (int offset = 0; offset < myString.length();) { int codePoint = myString.codePointAt(offset); offset += Character.charCount(codePoint); // Replace invisible control characters and unused code points switch (Character.getType(codePoint)) { case Character.CONTROL: // \p{Cc} case Character.FORMAT: // \p{Cf} case Character.PRIVATE_USE: // \p{Co} case Character.SURROGATE: // \p{Cs} case Character.UNASSIGNED: // \p{Cn} newString.append('?'); break; default: newString.append(Character.toChars(codePoint)); break; } } String result = newString.toString(); return result; } public void execute() { DBHelper dbHelper = new DBHelper(context); List<JSONObject> batches = dbHelper.getTestBatches(); String[] results = TestResultsManager.getJSONDataAsStringArray(context); List<Integer> fail = new ArrayList<>(); for (int i = 0; i < results.length; i++) { byte[] data = new byte[0]; try { // Deal with corrupted UTF-8 data...! results[i] = getStringWithControlsStripped(results[i]); data = results[i].getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { SKPorting.sAssert(false); } if (data == null) { Log.d(TAG, "no results to be submitted"); break; } String dataAsString = results[i]; uploadJsonData(dbHelper, batches, data, dataAsString); if (!isSuccess) { // JSON file failed to upload. Re-save it for future re-upload! SKPorting.sAssert(false); fail.add(i); TestResultsManager.clearResults(context); TestResultsManager.saveSubmittedLogs(context, data); } } TestResultsManager.clearResults(context); for (int i : fail) { TestResultsManager.saveResult(context, results[i]); } } private boolean uploadJsonData(final DBHelper dbHelper, List<JSONObject> batches, final byte[] data, String dataAsString) { double longitude = -999.0; double latitude = -999.0; long batchId = -1; JSONObject jObject = null; try { if (dataAsString.length() > 0) { jObject = new JSONObject(dataAsString); } if (jObject != null) { if (jObject.has("batch_id")) { try { String batchIdAsString = jObject.getString("batch_id"); long thisBatchId = Long.valueOf(batchIdAsString); // long timeStamp = jObject.getLong("timestamp"); // // Finally - do we have have an entry in the database for this? // // select id from test_batch where dtime = 1385458870802 // if (batches != null) { int theCount = batches.size(); int theIndex; for (theIndex = 0; theIndex < theCount; theIndex++) { JSONObject theBatch = batches.get(theIndex); Long theBatchId = theBatch.getLong("_id"); if (theBatchId == thisBatchId) { // Got it! batchId = thisBatchId; break; } } } } catch (JSONException e) { SKPorting.sAssert(getClass(), false); } } if (jObject.has("metrics") == false) { SKPorting.sAssert(false); } else { JSONArray metricsArray = jObject.getJSONArray("metrics"); int items = metricsArray.length(); int metricIndex = 0; for (metricIndex = 0; metricIndex < items; metricIndex++) { JSONObject metric = metricsArray.getJSONObject(metricIndex); if (metric.has("type")) { if (metric.getString("type").equals("location")) { if (metric.has("latitude")) { Double latitudeAsDouble = metric.getDouble("latitude"); latitude = latitudeAsDouble; if (metric.has("longitude")) { Double longitudeAsDouble = metric.getDouble("longitude"); longitude = longitudeAsDouble; } else { SKPorting.sAssert(false); } } else { SKPorting.sAssert(false); } break; } } else { SKPorting.sAssert(false); } } } } } catch (JSONException e) { SKPorting.sAssert(getClass(), false); jObject = null; } // If this is OLD data, it will NOT have a batch id... //SKLogger.sAssert(getClass(), batchId != -1); SK2AppSettings settings = SK2AppSettings.getSK2AppSettingsInstance(); Uri builtUri = Uri.parse(SKApplication.getAppInstance().getBaseUrlForUpload()); String fullUploadUrl = builtUri.buildUpon().path(settings.submit_path).toString(); if (SKApplication.getAppInstance().getShouldTestResultsBeUploadedToTestSpecificServer() == true) { // TODO: For SOME systems, we need to determine the server to use FROM THE DATA! if (jObject == null) { SKPorting.sAssert(false); } else { try { String targetServerUrl = null; JSONArray testArray = jObject.getJSONArray(ResultsContainer.JSON_TESTS); int count = testArray.length(); int index; for (index = 0; index < count; index++) { JSONObject theTestDict = testArray.getJSONObject(index); //NSLog(@"DEBUG: description = %@", jsonObject.description); if (theTestDict.has("target")) { targetServerUrl = theTestDict.getString("target"); break; } } if (targetServerUrl == null) { SKPorting.sAssert(false); } else { Log.d(TAG, "targetServerUrl=" + targetServerUrl); if (targetServerUrl.startsWith("http:")) { // Already starts http } else { // Need to add http:// prefix! targetServerUrl = String.format("http://%s", targetServerUrl); SKPorting.sAssert(targetServerUrl.startsWith("http://")); // Use this overriding server URL! targetServerUrl =String.format("%s/log/receive_mobile.php", targetServerUrl); fullUploadUrl = targetServerUrl; Log.d(TAG, "overriding fullUploadUrl=" + fullUploadUrl); } } } catch (JSONException e) { e.printStackTrace(); } } } final long finalBatchId = batchId; // Get Geocoder data! if ((longitude == -999.0) && (latitude == -999.0)) { Pair<Location,ScheduleConfig.LocationType> lastLocationPair = LocationDataCollector.sGetLastKnownLocation(); if (lastLocationPair != null) { Location lastLocation = lastLocationPair.first; if (lastLocation != null) { latitude = lastLocation.getLatitude(); longitude = lastLocation.getLongitude(); } } /* LocationManager manager = (LocationManager) SKApplication.getAppInstance().getApplicationContext().getSystemService(Context.LOCATION_SERVICE); if (manager == null) { SKLogger.sAssert(false); } else { Location location = manager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER); if (location == null) { location = manager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); } if (location == null) { location = manager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER); } if (location != null) { latitude = location.getLatitude(); longitude = location.getLongitude(); } } */ } if ((longitude != -999.0) && (latitude != -999.0)) { // // There are TWO ways to get geolocation data. // 1) The first one is to use the Geocoder API - but that is very unreliable! // 2) The second one uses an http query direct to Google. // // // 1) First approach - use the Geocoder API - it works sometimes! // List<Address> addressList = null; Geocoder geocoder = new Geocoder(SKApplication.getAppInstance().getApplicationContext(), Locale.getDefault()); if (geocoder == null) { SKPorting.sAssert(false); } else { if (Geocoder.isPresent() == false) { // Maybe we're on the Emulator! SKPorting.sAssert(OtherUtils.isThisDeviceAnEmulator() == true); } else { try { addressList = geocoder.getFromLocation(latitude, longitude, 1); } catch (IOException e) { // https://code.google.com/p/android/issues/detail?id=38009 // This is quite a common problem! //Log.e("LocationData", "Unable connect to Geocoder", e); //SKLogger.sAssert(false); } } } // // 2) Second approach - use an http query direct to Google. // if (addressList == null || addressList.size() == 0) { // https://code.google.com/p/android/issues/detail?id=38009 addressList = LocationData.sGetAddressFromLocation(latitude, longitude, 1); } if (addressList != null) { processGeocoderAddressList(dbHelper, finalBatchId, addressList); } } isSuccess = false; SKSimpleHttpToJsonQuery httpToJsonQuery = new SKSimpleHttpToJsonQuery(fullUploadUrl, data, new SKSimpleHttpToJsonQuery.QueryCompletion() { @Override public void OnQueryCompleted(boolean queryWasSuccessful, final JSONObject jsonResponse) { // try { // String bufferAsUtf8String = new String(data, "UTF-8"); // Log.d("SubmitTestResults", "********* uploaded data: queryWasSuccessful=" + queryWasSuccessful + ", data was=" + bufferAsUtf8String); // } catch (UnsupportedEncodingException e) { // SKLogger.sAssert(false); // } Log.d("SubmitTestResults", "********* uploaded data: queryWasSuccessful=" + queryWasSuccessful); if (queryWasSuccessful == true) { isSuccess = true; //isSuccess = true; JSONObject item1 = new JSONObject(); try { item1.put(PassiveMetric.JSON_METRIC_NAME, "public_ip"); item1.put(PassiveMetric.JSON_DTIME, System.currentTimeMillis()); item1.put(PassiveMetric.JSON_VALUE, jsonResponse.get("public_ip")); item1.put(PassiveMetric.JSON_TYPE, METRIC_TYPE.PUBLICIP); } catch (JSONException e) { SKPorting.sAssert(getClass(), false); } JSONObject item2 = new JSONObject(); try { item2.put(PassiveMetric.JSON_METRIC_NAME, "submission_id"); item2.put(PassiveMetric.JSON_DTIME, System.currentTimeMillis()); item2.put(PassiveMetric.JSON_VALUE, jsonResponse.get("submission_id")); item2.put(PassiveMetric.JSON_TYPE, METRIC_TYPE.SUBMISSIONID); SKApplication.getAppInstance().mLastPublicIp = jsonResponse.get("public_ip").toString(); SKApplication.getAppInstance().mLastSubmissionId = jsonResponse.get("submission_id").toString(); } catch (JSONException e) { SKPorting.sAssert(getClass(), false); } JSONArray jsonArray = new JSONArray(); jsonArray.put(item1); jsonArray.put(item2); if (finalBatchId != -1) { dbHelper.insertPassiveMetric(jsonArray, finalBatchId); } // Force the History screen to re-query, so it can show the submission id/public ip LocalBroadcastManager.getInstance(SKApplication.getAppInstance().getApplicationContext()).sendBroadcast(new Intent("refreshUIMessage")); } } }); httpToJsonQuery.doPerformQuery(); return isSuccess; } private void processGeocoderAddressList(DBHelper dbHelper, long finalBatchId, List<Address> addressList) { if (addressList != null && addressList.size() > 0) { Address address = addressList.get(0); String city = address.getLocality(); String country = address.getCountryName(); String muncipality = city; JSONObject item1 = null; if (muncipality != null) { item1 = new JSONObject(); try { item1.put(PassiveMetric.JSON_METRIC_NAME, "municipality"); item1.put(PassiveMetric.JSON_DTIME, System.currentTimeMillis()); item1.put(PassiveMetric.JSON_VALUE, muncipality); item1.put(PassiveMetric.JSON_TYPE, METRIC_TYPE.MUNICIPALITY); } catch (JSONException e) { SKPorting.sAssert(getClass(), false); } } String countryName = country; JSONObject item2 = null; if (countryName != null) { item2 = new JSONObject(); try { item2.put(PassiveMetric.JSON_METRIC_NAME, "country_name"); item2.put(PassiveMetric.JSON_DTIME, System.currentTimeMillis()); item2.put(PassiveMetric.JSON_VALUE, countryName); item2.put(PassiveMetric.JSON_TYPE, METRIC_TYPE.COUNTRYNAME); } catch (JSONException e) { SKPorting.sAssert(getClass(), false); } } if (item1 != null || item2 != null) { JSONArray jsonArray = new JSONArray(); if (item1 != null) { jsonArray.put(item1); } if (item2 != null) { jsonArray.put(item2); } dbHelper.insertPassiveMetric(jsonArray, finalBatchId); } } else { SKPorting.sAssert(false); } } private String privateBuildUrl() { SK2AppSettings settings = SK2AppSettings.getSK2AppSettingsInstance(); //SKLogger.sAssert(getClass(), settings.protocol_scheme.length() > 0); //Uri.Builder scheme = builder.scheme(settings.protocol_scheme); Uri builtUri = Uri.parse(SKApplication.getAppInstance().getBaseUrlForUpload()); String urlString = builtUri.buildUpon().path(settings.submit_path).toString(); return urlString; // return new Uri.Builder().scheme(settings.protocol_scheme) // .authority(settings.getServerBaseUrl()) // .path(settings.submit_path).build().toString(); } }