diff options
author | Steve Kondik <shade@chemlab.org> | 2015-09-28 14:38:16 -0700 |
---|---|---|
committer | Adnan Begovic <adnan@cyngn.com> | 2015-11-23 14:26:13 -0800 |
commit | 84c44757ac58d285aaaaf019c13c3afe9f664e80 (patch) | |
tree | 82e26aa0989a017337214b221df29023aef4f404 /src | |
parent | 53fefc5a6515d3903b7ffdfa4f013253ce68009b (diff) | |
download | packages_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')
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 { |