summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSteve Kondik <shade@chemlab.org>2015-09-28 14:38:16 -0700
committerAdnan Begovic <adnan@cyngn.com>2015-11-23 14:26:13 -0800
commit84c44757ac58d285aaaaf019c13c3afe9f664e80 (patch)
tree82e26aa0989a017337214b221df29023aef4f404 /src
parent53fefc5a6515d3903b7ffdfa4f013253ce68009b (diff)
downloadpackages_apps_Settings-84c44757ac58d285aaaaf019c13c3afe9f664e80.zip
packages_apps_Settings-84c44757ac58d285aaaaf019c13c3afe9f664e80.tar.gz
packages_apps_Settings-84c44757ac58d285aaaaf019c13c3afe9f664e80.tar.bz2
Settings: add opt out stats event
- Get rid of keeping track of queued jobs, job scheduler does this and all we really want is a new job id to not interfere with old ones (unless we hit the max jobs allowed, then start the count over). - Persist whether we have reported metrics for the owner user (once per device) Ref: CYNGNOS-1131 Change-Id: Ib5bac2944b5ca4259ea82a357b24708377c1dc4c
Diffstat (limited to 'src')
-rw-r--r--src/com/android/settings/cmstats/AnonymousStats.java80
-rw-r--r--src/com/android/settings/cmstats/ReportingService.java56
-rw-r--r--src/com/android/settings/cmstats/ReportingServiceManager.java51
-rw-r--r--src/com/android/settings/cmstats/StatsUploadJobService.java48
4 files changed, 117 insertions, 118 deletions
diff --git a/src/com/android/settings/cmstats/AnonymousStats.java b/src/com/android/settings/cmstats/AnonymousStats.java
index 732a579..d85e24d 100644
--- a/src/com/android/settings/cmstats/AnonymousStats.java
+++ b/src/com/android/settings/cmstats/AnonymousStats.java
@@ -20,20 +20,27 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
-import android.util.ArraySet;
+import android.os.UserHandle;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.preference.SwitchPreference;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
-
-import java.util.Set;
+import cyanogenmod.providers.CMSettings;
public class AnonymousStats extends SettingsPreferenceFragment {
+
private static final String PREF_FILE_NAME = "CMStats";
/* package */ static final String ANONYMOUS_OPT_IN = "pref_anonymous_opt_in";
/* package */ static final String ANONYMOUS_LAST_CHECKED = "pref_anonymous_checked_in";
- /* package */ static final String KEY_JOB_QUEUE = "pref_job_queue";
+ /* package */ static final String KEY_LAST_JOB_ID = "last_job_id";
/* package */ static final int QUEUE_MAX_THRESHOLD = 1000;
+ public static final String KEY_STATS = "stats_collection";
+
+ SwitchPreference mStatsSwitch;
+
public static SharedPreferences getPreferences(Context context) {
return context.getSharedPreferences(PREF_FILE_NAME, 0);
}
@@ -42,62 +49,51 @@ public class AnonymousStats extends SettingsPreferenceFragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.anonymous_stats);
+ mStatsSwitch = (SwitchPreference) findPreference(KEY_STATS);
}
- public static Set<String> getJobQueue(Context context) {
- return getPreferences(context).getStringSet(KEY_JOB_QUEUE, new ArraySet<String>());
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
+ if (preference == mStatsSwitch) {
+ boolean checked = mStatsSwitch.isChecked();
+ if (checked) {
+ // clear opt out flags
+ CMSettings.Secure.putIntForUser(getContentResolver(),
+ CMSettings.Secure.STATS_COLLECTION_REPORTED, 0, UserHandle.USER_OWNER);
+ }
+ // will initiate opt out sequence if necessary
+ ReportingServiceManager.setAlarm(getActivity());
+ return true;
+ }
+ return super.onPreferenceTreeClick(preferenceScreen, preference);
}
- public static void clearJobQueue(Context context) {
+ public static void updateLastSynced(Context context) {
getPreferences(context)
.edit()
- .remove(KEY_JOB_QUEUE)
+ .putLong(ANONYMOUS_LAST_CHECKED,System.currentTimeMillis())
.commit();
}
- public static void addJob(Context context, int jobId) {
- Set<String> jobQueue = getJobQueue(context);
- jobQueue.add(String.valueOf(jobId));
-
- getPreferences(context)
- .edit()
- .putStringSet(KEY_JOB_QUEUE, jobQueue)
- .commit();
+ private static int getLastJobId(Context context) {
+ return getPreferences(context).getInt(KEY_LAST_JOB_ID, 0);
}
- public static void removeJob(Context context, int jobId) {
- Set<String> jobQueue = getJobQueue(context);
- jobQueue.remove(String.valueOf(jobId));
+ private static void setLastJobId(Context context, int id) {
getPreferences(context)
.edit()
- .remove(KEY_JOB_QUEUE)
- .commit();
-
- getPreferences(context)
- .edit()
- .putStringSet(KEY_JOB_QUEUE, jobQueue)
+ .putInt(KEY_LAST_JOB_ID, id)
.commit();
}
- /**
- * @param context context to use to get prefs
- * @return Returns the next unused int in the job queue, up until {@link #QUEUE_MAX_THRESHOLD}
- * is reached, then it will return -1
- */
public static int getNextJobId(Context context) {
- Set<String> currentQueue = getJobQueue(context);
-
- if (currentQueue == null) {
- return 1;
- } else if (currentQueue.size() >= QUEUE_MAX_THRESHOLD) {
- return -1;
+ int lastId = getLastJobId(context);
+ if (lastId >= QUEUE_MAX_THRESHOLD) {
+ lastId = 1;
} else {
- int i = 1;
- while (currentQueue.contains(String.valueOf(i))) {
- i++;
- }
- return i;
-
+ lastId += 1;
}
+ setLastJobId(context, lastId);
+ return lastId;
}
}
diff --git a/src/com/android/settings/cmstats/ReportingService.java b/src/com/android/settings/cmstats/ReportingService.java
index 61e7af1..8410143 100644
--- a/src/com/android/settings/cmstats/ReportingService.java
+++ b/src/com/android/settings/cmstats/ReportingService.java
@@ -22,15 +22,18 @@ import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.os.PersistableBundle;
+import android.os.UserHandle;
import android.util.Log;
+import cyanogenmod.providers.CMSettings;
import java.util.List;
public class ReportingService extends IntentService {
/* package */ static final String TAG = "CMStats";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ public static final String EXTRA_OPTING_OUT = "cmstats::opt_out";
public ReportingService() {
super(ReportingService.class.getSimpleName());
@@ -40,42 +43,21 @@ public class ReportingService extends IntentService {
protected void onHandleIntent(Intent intent) {
JobScheduler js = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
- if (AnonymousStats.getNextJobId(this) == -1) {
- // if we've filled up to the threshold, we may have some stale job queue ids, purge them
- // then re-add what hasn't executed yet
- AnonymousStats.clearJobQueue(this);
-
- final List<JobInfo> allPendingJobs = js.getAllPendingJobs();
-
- // add two extra jobs to the size for what we will schedule below so we *always*
- // have room for both.
- if (js.getAllPendingJobs().size() + 2 >= AnonymousStats.QUEUE_MAX_THRESHOLD) {
- // there are still as many actual pending jobs as our threshold allows.
- // since we are past the threshold we will be losing data if we don't schedule
- // another job here, so just clear out all the old data and start fresh
- js.cancelAll();
- } else {
- for (JobInfo pendingJob : allPendingJobs) {
- AnonymousStats.addJob(this, pendingJob.getId());
- }
- }
- }
-
- int cyanogenJobId, cmOrgJobId;
- AnonymousStats.addJob(this, cyanogenJobId = AnonymousStats.getNextJobId(this));
- AnonymousStats.addJob(this, cmOrgJobId = AnonymousStats.getNextJobId(this));
-
- if (DEBUG) Log.d(TAG, "scheduling jobs id: " + cyanogenJobId + ", " + cmOrgJobId);
-
- // get snapshot and persist it
String deviceId = Utilities.getUniqueID(getApplicationContext());
String deviceName = Utilities.getDevice();
String deviceVersion = Utilities.getModVersion();
String deviceCountry = Utilities.getCountryCode(getApplicationContext());
String deviceCarrier = Utilities.getCarrier(getApplicationContext());
String deviceCarrierId = Utilities.getCarrierId(getApplicationContext());
+ boolean optOut = intent.getBooleanExtra(EXTRA_OPTING_OUT, false);
+
+ final int cyanogenJobId = AnonymousStats.getNextJobId(getApplicationContext());
+ final int cmOrgJobId = AnonymousStats.getNextJobId(getApplicationContext());
+
+ if (DEBUG) Log.d(TAG, "scheduling jobs id: " + cyanogenJobId + ", " + cmOrgJobId);
PersistableBundle cyanogenBundle = new PersistableBundle();
+ cyanogenBundle.putBoolean(StatsUploadJobService.KEY_OPT_OUT, optOut);
cyanogenBundle.putString(StatsUploadJobService.KEY_DEVICE_NAME, deviceName);
cyanogenBundle.putString(StatsUploadJobService.KEY_UNIQUE_ID, deviceId);
cyanogenBundle.putString(StatsUploadJobService.KEY_VERSION, deviceVersion);
@@ -84,6 +66,7 @@ public class ReportingService extends IntentService {
cyanogenBundle.putString(StatsUploadJobService.KEY_CARRIER_ID, deviceCarrierId);
cyanogenBundle.putLong(StatsUploadJobService.KEY_TIMESTAMP, System.currentTimeMillis());
+ // get snapshot and persist it
PersistableBundle cmBundle = new PersistableBundle(cyanogenBundle);
// set job types
@@ -110,11 +93,14 @@ public class ReportingService extends IntentService {
.setPersisted(true)
.build());
+ if (optOut) {
+ // we've successfully scheduled the opt out.
+ CMSettings.Secure.putIntForUser(getContentResolver(),
+ CMSettings.Secure.STATS_COLLECTION_REPORTED, 1, UserHandle.USER_OWNER);
+ }
+
// reschedule
- final SharedPreferences prefs = AnonymousStats.getPreferences(this);
- prefs.edit().putLong(AnonymousStats.ANONYMOUS_LAST_CHECKED,
- System.currentTimeMillis()).apply();
- ReportingServiceManager.setAlarm(this, 0);
+ AnonymousStats.updateLastSynced(this);
+ ReportingServiceManager.setAlarm(this);
}
-
}
diff --git a/src/com/android/settings/cmstats/ReportingServiceManager.java b/src/com/android/settings/cmstats/ReportingServiceManager.java
index e7ca159..bce1372 100644
--- a/src/com/android/settings/cmstats/ReportingServiceManager.java
+++ b/src/com/android/settings/cmstats/ReportingServiceManager.java
@@ -18,17 +18,22 @@ package com.android.settings.cmstats;
import android.app.AlarmManager;
import android.app.PendingIntent;
+import android.app.job.JobScheduler;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.os.UserHandle;
import android.util.Log;
+import cyanogenmod.providers.CMSettings;
public class ReportingServiceManager extends BroadcastReceiver {
private static final long MILLIS_PER_HOUR = 60L * 60L * 1000L;
private static final long MILLIS_PER_DAY = 24L * MILLIS_PER_HOUR;
private static final long UPDATE_INTERVAL = 1L * MILLIS_PER_DAY;
+ private static final String TAG = ReportingServiceManager.class.getSimpleName();
+
public static final String ACTION_LAUNCH_SERVICE =
"com.android.settings.action.TRIGGER_REPORT_METRICS";
public static final String EXTRA_FORCE = "force";
@@ -36,33 +41,41 @@ public class ReportingServiceManager extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
- setAlarm(context, 0);
+ setAlarm(context);
} else if (intent.getAction().equals(ACTION_LAUNCH_SERVICE)){
launchService(context, intent.getBooleanExtra(EXTRA_FORCE, false));
}
}
- public static void setAlarm(Context context, long millisFromNow) {
+ /**
+ * opt out if we haven't yet
+ */
+ public static void initiateOptOut(Context context) {
+ final boolean optOutReported = CMSettings.Secure.getIntForUser(context.getContentResolver(),
+ CMSettings.Secure.STATS_COLLECTION_REPORTED, 0, UserHandle.USER_OWNER) == 1;
+ if (!optOutReported) {
+ Intent intent = new Intent();
+ intent.setClass(context, ReportingService.class);
+ intent.putExtra(ReportingService.EXTRA_OPTING_OUT, true);
+ context.startServiceAsUser(intent, UserHandle.OWNER);
+ }
+ }
+
+ public static void setAlarm(Context context) {
SharedPreferences prefs = AnonymousStats.getPreferences(context);
if (prefs.contains(AnonymousStats.ANONYMOUS_OPT_IN)) {
migrate(context, prefs);
}
if (!Utilities.isStatsCollectionEnabled(context)) {
+ initiateOptOut(context);
return;
}
-
- if (millisFromNow <= 0) {
- long lastSynced = prefs.getLong(AnonymousStats.ANONYMOUS_LAST_CHECKED, 0);
- if (lastSynced == 0) {
- // never synced, so let's fake out that the last sync was just now.
- // this will allow the user tFrame time to opt out before it will start
- // sending up anonymous stats.
- lastSynced = System.currentTimeMillis();
- prefs.edit().putLong(AnonymousStats.ANONYMOUS_LAST_CHECKED, lastSynced).apply();
- Log.d(ReportingService.TAG, "Set alarm for first sync.");
- }
- millisFromNow = (lastSynced + UPDATE_INTERVAL) - System.currentTimeMillis();
+ long lastSynced = prefs.getLong(AnonymousStats.ANONYMOUS_LAST_CHECKED, 0);
+ if (lastSynced == 0) {
+ launchService(context, true); // service will reschedule the next alarm
+ return;
}
+ long millisFromNow = (lastSynced + UPDATE_INTERVAL) - System.currentTimeMillis();
Intent intent = new Intent(ACTION_LAUNCH_SERVICE);
intent.setClass(context, ReportingServiceManager.class);
@@ -70,8 +83,8 @@ public class ReportingServiceManager extends BroadcastReceiver {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + millisFromNow,
PendingIntent.getBroadcast(context, 0, intent, 0));
- Log.d(ReportingService.TAG, "Next sync attempt in : "
- + millisFromNow / MILLIS_PER_HOUR + " hours");
+ Log.d(TAG, "Next sync attempt in : "
+ + (millisFromNow / MILLIS_PER_HOUR) + " hours");
}
public static void launchService(Context context, boolean force) {
@@ -84,13 +97,13 @@ public class ReportingServiceManager extends BroadcastReceiver {
if (!force) {
long lastSynced = prefs.getLong(AnonymousStats.ANONYMOUS_LAST_CHECKED, 0);
if (lastSynced == 0) {
- setAlarm(context, 0);
+ setAlarm(context);
return;
}
long timeElapsed = System.currentTimeMillis() - lastSynced;
if (timeElapsed < UPDATE_INTERVAL) {
long timeLeft = UPDATE_INTERVAL - timeElapsed;
- Log.d(ReportingService.TAG, "Waiting for next sync : "
+ Log.d(TAG, "Waiting for next sync : "
+ timeLeft / MILLIS_PER_HOUR + " hours");
return;
}
@@ -98,7 +111,7 @@ public class ReportingServiceManager extends BroadcastReceiver {
Intent intent = new Intent();
intent.setClass(context, ReportingService.class);
- context.startService(intent);
+ context.startServiceAsUser(intent, UserHandle.OWNER);
}
private static void migrate(Context context, SharedPreferences prefs) {
diff --git a/src/com/android/settings/cmstats/StatsUploadJobService.java b/src/com/android/settings/cmstats/StatsUploadJobService.java
index 2ff428f..77b26e6 100644
--- a/src/com/android/settings/cmstats/StatsUploadJobService.java
+++ b/src/com/android/settings/cmstats/StatsUploadJobService.java
@@ -43,7 +43,7 @@ import java.util.Map;
public class StatsUploadJobService extends JobService {
private static final String TAG = StatsUploadJobService.class.getSimpleName();
- private static final boolean DEBUG = false;
+ 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;
@@ -56,6 +56,7 @@ public class StatsUploadJobService extends JobService {
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>());
@@ -107,16 +108,25 @@ public class StatsUploadJobService extends JobService {
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()) {
- int jobType = extras.getInt(KEY_JOB_TYPE, -1);
-
switch (jobType) {
case JOB_TYPE_CYANOGEN:
try {
- success = uploadToCyanogen(deviceId, deviceName, deviceVersion,
- deviceCountry, deviceCarrier, deviceCarrierId, timeStamp);
+ 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;
@@ -126,7 +136,7 @@ public class StatsUploadJobService extends JobService {
case JOB_TYPE_CMORG:
try {
success = uploadToCM(deviceId, deviceName, deviceVersion, deviceCountry,
- deviceCarrier, deviceCarrierId);
+ deviceCarrier, deviceCarrierId, optOut);
} catch (IOException e) {
Log.e(TAG, "Could not upload stats checkin to commnity server", e);
success = false;
@@ -138,7 +148,6 @@ public class StatsUploadJobService extends JobService {
if (success) {
// we hit the server, succeed either which way.
mCurrentJobs.remove(mJobParams);
- AnonymousStats.removeJob(StatsUploadJobService.this, mJobParams.getJobId());
}
if (DEBUG)
@@ -151,10 +160,12 @@ public class StatsUploadJobService extends JobService {
private boolean uploadToCM(String deviceId, String deviceName, String deviceVersion,
- String deviceCountry, String deviceCarrier, String deviceCarrierId)
+ 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)
@@ -182,9 +193,7 @@ public class StatsUploadJobService extends JobService {
}
- private boolean uploadToCyanogen(String deviceId, String deviceName, String deviceVersion,
- String deviceCountry, String carrier, String carrierId,
- long timeStamp)
+ private boolean uploadToCyanogen(JSONObject json)
throws IOException, JSONException {
String authToken = getAuthToken();
@@ -192,15 +201,6 @@ public class StatsUploadJobService extends JobService {
Log.w(TAG, "no auth token!");
}
- JSONObject json = new JSONObject();
- json.put("uniqueId", deviceId);
- json.put("deviceName", deviceName);
- json.put("version", deviceVersion);
- json.put("country", deviceCountry);
- json.put("carrier", carrier);
- json.put("carrierId", carrierId);
- json.put("timestamp", timeStamp);
-
URL url = new URL(getString(R.string.stats_cyanogen_url));
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
@@ -223,9 +223,13 @@ public class StatsUploadJobService extends JobService {
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: " + getResponse(urlConnection,
- !success));
+ Log.w(TAG, "failed sending, server returned: " + response);
}
return success;
} finally {