diff options
Diffstat (limited to 'src/com/android/settings/cmstats/StatsUploadJobService.java')
-rw-r--r-- | src/com/android/settings/cmstats/StatsUploadJobService.java | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/src/com/android/settings/cmstats/StatsUploadJobService.java b/src/com/android/settings/cmstats/StatsUploadJobService.java new file mode 100644 index 0000000..580a20f --- /dev/null +++ b/src/com/android/settings/cmstats/StatsUploadJobService.java @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2015 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.cmstats; + +import android.app.job.JobParameters; +import android.app.job.JobService; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.PersistableBundle; +import android.util.ArrayMap; +import android.util.Log; +import com.android.settings.R; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Collections; +import java.util.Map; + +public class StatsUploadJobService extends JobService { + + private static final String TAG = StatsUploadJobService.class.getSimpleName(); + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + public static final String KEY_JOB_TYPE = "job_type"; + public static final int JOB_TYPE_CYANOGEN = 1; + public static final int JOB_TYPE_CMORG = 2; + + public static final String KEY_UNIQUE_ID = "uniqueId"; + public static final String KEY_DEVICE_NAME = "deviceName"; + public static final String KEY_VERSION = "version"; + public static final String KEY_COUNTRY = "country"; + public static final String KEY_CARRIER = "carrier"; + public static final String KEY_CARRIER_ID = "carrierId"; + public static final String KEY_TIMESTAMP = "timeStamp"; + public static final String KEY_OPT_OUT = "optOut"; + + private final Map<JobParameters, StatsUploadTask> mCurrentJobs + = Collections.synchronizedMap(new ArrayMap<JobParameters, StatsUploadTask>()); + + @Override + public boolean onStartJob(JobParameters jobParameters) { + if (DEBUG) + Log.d(TAG, "onStartJob() called with " + "jobParameters = [" + jobParameters + "]"); + final StatsUploadTask uploadTask = new StatsUploadTask(jobParameters); + mCurrentJobs.put(jobParameters, uploadTask); + uploadTask.execute((Void) null); + return true; + } + + @Override + public boolean onStopJob(JobParameters jobParameters) { + if (DEBUG) + Log.d(TAG, "onStopJob() called with " + "jobParameters = [" + jobParameters + "]"); + + final StatsUploadTask cancelledJob; + cancelledJob = mCurrentJobs.remove(jobParameters); + + if (cancelledJob != null) { + // cancel the ongoing background task + cancelledJob.cancel(true); + return true; // reschedule + } + + return false; + } + + private class StatsUploadTask extends AsyncTask<Void, Void, Boolean> { + + private JobParameters mJobParams; + + public StatsUploadTask(JobParameters jobParams) { + this.mJobParams = jobParams; + } + + @Override + protected Boolean doInBackground(Void... params) { + + PersistableBundle extras = mJobParams.getExtras(); + + String deviceId = extras.getString(KEY_UNIQUE_ID); + String deviceName = extras.getString(KEY_DEVICE_NAME); + String deviceVersion = extras.getString(KEY_VERSION); + String deviceCountry = extras.getString(KEY_COUNTRY); + String deviceCarrier = extras.getString(KEY_CARRIER); + String deviceCarrierId = extras.getString(KEY_CARRIER_ID); + long timeStamp = extras.getLong(KEY_TIMESTAMP); + boolean optOut = extras.getBoolean(KEY_OPT_OUT); + + boolean success = false; + int jobType = extras.getInt(KEY_JOB_TYPE, -1); + if (!isCancelled()) { + switch (jobType) { + case JOB_TYPE_CYANOGEN: + try { + JSONObject json = new JSONObject(); + json.put("optOut", optOut); + json.put("uniqueId", deviceId); + json.put("deviceName", deviceName); + json.put("version", deviceVersion); + json.put("country", deviceCountry); + json.put("carrier", deviceCarrier); + json.put("carrierId", deviceCarrierId); + json.put("timestamp", timeStamp); + + success = uploadToCyanogen(json); + } catch (IOException | JSONException e) { + Log.e(TAG, "Could not upload stats checkin to cyanogen server", e); + success = false; + } + break; + + case JOB_TYPE_CMORG: + try { + success = uploadToCM(deviceId, deviceName, deviceVersion, deviceCountry, + deviceCarrier, deviceCarrierId, optOut); + } catch (IOException e) { + Log.e(TAG, "Could not upload stats checkin to commnity server", e); + success = false; + } + break; + } + } + if (DEBUG) + Log.d(TAG, "job id " + mJobParams.getJobId() + ", has finished with success=" + + success); + return success; + } + + @Override + protected void onPostExecute(Boolean success) { + mCurrentJobs.remove(mJobParams); + jobFinished(mJobParams, !success); + } + } + + + private boolean uploadToCM(String deviceId, String deviceName, String deviceVersion, + String deviceCountry, String deviceCarrier, String deviceCarrierId, + boolean optOut) + throws IOException { + + final Uri uri = Uri.parse(getString(R.string.stats_cm_url)).buildUpon() + .appendQueryParameter("opt_out", optOut ? "1" : "0") + .appendQueryParameter("device_hash", deviceId) + .appendQueryParameter("device_name", deviceName) + .appendQueryParameter("device_version", deviceVersion) + .appendQueryParameter("device_country", deviceCountry) + .appendQueryParameter("device_carrier", deviceCarrier) + .appendQueryParameter("device_carrier_id", deviceCarrierId).build(); + URL url = new URL(uri.toString()); + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + try { + urlConnection.setInstanceFollowRedirects(true); + urlConnection.setDoOutput(true); + urlConnection.connect(); + + final int responseCode = urlConnection.getResponseCode(); + if (DEBUG) Log.d(TAG, "cm server response code=" + responseCode); + final boolean success = responseCode == HttpURLConnection.HTTP_OK; + if (!success) { + Log.w(TAG, "failed sending, server returned: " + getResponse(urlConnection, + !success)); + } + return success; + } finally { + urlConnection.disconnect(); + } + + } + + private boolean uploadToCyanogen(JSONObject json) + throws IOException, JSONException { + String authToken = getAuthToken(); + + if (authToken.isEmpty()) { + Log.w(TAG, "no auth token!"); + } + + URL url = new URL(getString(R.string.stats_cyanogen_url)); + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + try { + urlConnection.setInstanceFollowRedirects(true); + urlConnection.setDoInput(true); + urlConnection.setDoOutput(true); + + urlConnection.setRequestProperty("Accept-Encoding", "identity"); + urlConnection.setRequestProperty("Authorization", authToken); + urlConnection.setRequestProperty("Content-Type", "application/json"); + + OutputStream os = urlConnection.getOutputStream(); + BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8")); + writer.write(json.toString()); + writer.flush(); + writer.close(); + os.close(); + + urlConnection.connect(); + + final int responseCode = urlConnection.getResponseCode(); + final boolean success = responseCode == HttpURLConnection.HTTP_OK; + + final String response = getResponse(urlConnection, !success); + if (DEBUG) + Log.d(TAG, "server responseCode: " + responseCode +", response=" + response); + + if (!success) { + Log.w(TAG, "failed sending, server returned: " + response); + } + return success; + } finally { + urlConnection.disconnect(); + } + } + + private String getAuthToken() { + HttpURLConnection urlConnection = null; + try { + URL url = new URL(getString(R.string.stats_cyanogen_token_url)); + urlConnection = (HttpURLConnection) url.openConnection(); + urlConnection.setInstanceFollowRedirects(true); + urlConnection.setDoInput(true); + + urlConnection.setRequestProperty("Accept-Encoding", "identity"); + urlConnection.setRequestProperty("Content-Type", "text/plain"); + + urlConnection.connect(); + + final int responseCode = urlConnection.getResponseCode(); + final boolean success = responseCode == HttpURLConnection.HTTP_OK; + if (DEBUG) Log.d(TAG, "server auth response code=" + responseCode); + final String response = getResponse(urlConnection, !success); + if (DEBUG) + Log.d(TAG, "server auth response=" + response); + + if (success) { + return response; + } + } catch (IOException e) { + Log.e(TAG, "error getting auth token", e); + } finally { + if (urlConnection != null) { + urlConnection.disconnect(); + } + } + return ""; + } + + private String getResponse(HttpURLConnection httpUrlConnection, boolean errorStream) + throws IOException { + InputStream responseStream = new BufferedInputStream(errorStream + ? httpUrlConnection.getErrorStream() + : httpUrlConnection.getInputStream()); + + BufferedReader responseStreamReader = new BufferedReader( + new InputStreamReader(responseStream)); + String line = ""; + StringBuilder stringBuilder = new StringBuilder(); + while ((line = responseStreamReader.readLine()) != null) { + stringBuilder.append(line).append("\n"); + } + responseStreamReader.close(); + responseStream.close(); + + return stringBuilder.toString(); + } + +} |