summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDanesh Mondegarian <daneshm90@gmail.com>2012-02-03 21:02:21 -0500
committerAdnan Begovic <adnan@cyngn.com>2015-11-23 14:26:11 -0800
commit261830431922efba4d33e19013380a5ca1266835 (patch)
tree67564a410121135592ab42bf4556b241945f7aa5 /src
parentc9aebf6e83e6d4d996a1a3cea899c4a28792dfb7 (diff)
downloadpackages_apps_Settings-261830431922efba4d33e19013380a5ca1266835.zip
packages_apps_Settings-261830431922efba4d33e19013380a5ca1266835.tar.gz
packages_apps_Settings-261830431922efba4d33e19013380a5ca1266835.tar.bz2
Settings: Anonymous Statistics (CMStats)
CMStats is now integrated into settings. Change-Id: Ice1b2c0c4882620eef774aca26b91a39d27f7b47
Diffstat (limited to 'src')
-rw-r--r--src/com/android/settings/Settings.java1
-rw-r--r--src/com/android/settings/cmstats/AnonymousStats.java126
-rw-r--r--src/com/android/settings/cmstats/PreviewData.java49
-rw-r--r--src/com/android/settings/cmstats/ReportingService.java149
-rw-r--r--src/com/android/settings/cmstats/ReportingServiceManager.java102
-rw-r--r--src/com/android/settings/cmstats/Utilities.java78
6 files changed, 505 insertions, 0 deletions
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 06f0149..2f9901f 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -120,4 +120,5 @@ public class Settings extends SettingsActivity {
public static class AppWriteSettingsActivity extends SettingsActivity { /* empty */ }
public static class LiveDisplayActivity extends SettingsActivity { /* empty */ }
public static class BlacklistSettingsActivity extends SettingsActivity { /* empty */ }
+ public static class AnonymousStatsActivity extends Settings { /* empty */ }
}
diff --git a/src/com/android/settings/cmstats/AnonymousStats.java b/src/com/android/settings/cmstats/AnonymousStats.java
new file mode 100644
index 0000000..4f1e10a
--- /dev/null
+++ b/src/com/android/settings/cmstats/AnonymousStats.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2012 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.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+
+public class AnonymousStats extends SettingsPreferenceFragment implements
+ DialogInterface.OnClickListener, DialogInterface.OnDismissListener,
+ Preference.OnPreferenceChangeListener {
+ private static final String VIEW_STATS = "pref_view_stats";
+
+ 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";
+
+ private CheckBoxPreference mEnableReporting;
+ private Preference mViewStats;
+
+ private Dialog mOkDialog;
+ private boolean mOkClicked;
+
+ private SharedPreferences mPrefs;
+
+ public static SharedPreferences getPreferences(Context context) {
+ return context.getSharedPreferences(PREF_FILE_NAME, 0);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ addPreferencesFromResource(R.xml.anonymous_stats);
+
+ mPrefs = getPreferences(getActivity());
+
+ PreferenceScreen prefSet = getPreferenceScreen();
+ mEnableReporting = (CheckBoxPreference) prefSet.findPreference(ANONYMOUS_OPT_IN);
+ mViewStats = (Preference) prefSet.findPreference(VIEW_STATS);
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
+ if (preference == mEnableReporting) {
+ if (mEnableReporting.isChecked()) {
+ // Display the confirmation dialog
+ mOkClicked = false;
+ if (mOkDialog != null) {
+ mOkDialog.dismiss();
+ }
+ mOkDialog = new AlertDialog.Builder(getActivity())
+ .setMessage(R.string.anonymous_statistics_warning)
+ .setTitle(R.string.anonymous_statistics_warning_title)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setPositiveButton(android.R.string.yes, this)
+ .setNeutralButton(R.string.anonymous_learn_more, this)
+ .setNegativeButton(android.R.string.no, this)
+ .show();
+ mOkDialog.setOnDismissListener(this);
+ } else {
+ // Disable reporting
+ mPrefs.edit().putBoolean(ANONYMOUS_OPT_IN, false).apply();
+ }
+ } else if (preference == mViewStats) {
+ // Display the stats page
+ Uri uri = Uri.parse("http://stats.cyanogenmod.org");
+ startActivity(new Intent(Intent.ACTION_VIEW, uri));
+ } else {
+ // If we didn't handle it, let preferences handle it.
+ return super.onPreferenceTreeClick(preferenceScreen, preference);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ return false;
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ if (!mOkClicked) {
+ mEnableReporting.setChecked(false);
+ }
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ mOkClicked = true;
+ mPrefs.edit().putBoolean(ANONYMOUS_OPT_IN, true).apply();
+ ReportingServiceManager.launchService(getActivity());
+ } else if (which == DialogInterface.BUTTON_NEGATIVE) {
+ mEnableReporting.setChecked(false);
+ } else {
+ Uri uri = Uri.parse("http://www.cyanogenmod.org/blog/cmstats-what-it-is-and-why-you-should-opt-in");
+ startActivity(new Intent(Intent.ACTION_VIEW, uri));
+ }
+ }
+}
diff --git a/src/com/android/settings/cmstats/PreviewData.java b/src/com/android/settings/cmstats/PreviewData.java
new file mode 100644
index 0000000..7a78aea
--- /dev/null
+++ b/src/com/android/settings/cmstats/PreviewData.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2012 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.content.Context;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+
+public class PreviewData extends SettingsPreferenceFragment {
+ private static final String UNIQUE_ID = "preview_id";
+ private static final String DEVICE = "preview_device";
+ private static final String VERSION = "preview_version";
+ private static final String COUNTRY = "preview_country";
+ private static final String CARRIER = "preview_carrier";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ addPreferencesFromResource(R.xml.preview_data);
+
+ final PreferenceScreen prefSet = getPreferenceScreen();
+ final Context context = getActivity();
+
+ prefSet.findPreference(UNIQUE_ID).setSummary(Utilities.getUniqueID(context));
+ prefSet.findPreference(DEVICE).setSummary(Utilities.getDevice());
+ prefSet.findPreference(VERSION).setSummary(Utilities.getModVersion());
+ prefSet.findPreference(COUNTRY).setSummary(Utilities.getCountryCode(context));
+ prefSet.findPreference(CARRIER).setSummary(Utilities.getCarrier(context));
+ }
+}
diff --git a/src/com/android/settings/cmstats/ReportingService.java b/src/com/android/settings/cmstats/ReportingService.java
new file mode 100644
index 0000000..c76dc04
--- /dev/null
+++ b/src/com/android/settings/cmstats/ReportingService.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2012 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.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.AsyncTask;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.settings.R;
+import com.android.settings.Settings;
+
+import com.google.analytics.tracking.android.GoogleAnalytics;
+import com.google.analytics.tracking.android.Tracker;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicNameValuePair;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ReportingService extends Service {
+ /* package */ static final String TAG = "CMStats";
+
+ private StatsUploadTask mTask;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public int onStartCommand (Intent intent, int flags, int startId) {
+ Log.d(TAG, "User has opted in -- reporting.");
+
+ if (mTask == null || mTask.getStatus() == AsyncTask.Status.FINISHED) {
+ mTask = new StatsUploadTask();
+ mTask.execute();
+ }
+
+ return Service.START_REDELIVER_INTENT;
+ }
+
+ private class StatsUploadTask extends AsyncTask<Void, Void, Boolean> {
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ 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());
+
+ Log.d(TAG, "SERVICE: Device ID=" + deviceId);
+ Log.d(TAG, "SERVICE: Device Name=" + deviceName);
+ Log.d(TAG, "SERVICE: Device Version=" + deviceVersion);
+ Log.d(TAG, "SERVICE: Country=" + deviceCountry);
+ Log.d(TAG, "SERVICE: Carrier=" + deviceCarrier);
+ Log.d(TAG, "SERVICE: Carrier ID=" + deviceCarrierId);
+
+ // report to google analytics
+ GoogleAnalytics ga = GoogleAnalytics.getInstance(ReportingService.this);
+ Tracker tracker = ga.getTracker(getString(R.string.ga_trackingId));
+ tracker.sendEvent(deviceName, deviceVersion, deviceCountry, null);
+
+ // this really should be set at build time...
+ // format of version should be:
+ // version[-date-type]-device
+ String[] parts = deviceVersion.split("-");
+ String deviceVersionNoDevice = null;
+ if (parts.length == 2) {
+ deviceVersionNoDevice = parts[0];
+ } else if (parts.length == 4) {
+ deviceVersionNoDevice = parts[0] + "-" + parts[2];
+ }
+
+ if (deviceVersionNoDevice != null) {
+ tracker.sendEvent("checkin", deviceName, deviceVersionNoDevice, null);
+ }
+ tracker.close();
+
+ // report to the cmstats service
+ HttpClient httpClient = new DefaultHttpClient();
+ HttpPost httpPost = new HttpPost("http://stats.cyanogenmod.org/submit");
+ boolean success = false;
+
+ try {
+ List<NameValuePair> kv = new ArrayList<NameValuePair>(5);
+ kv.add(new BasicNameValuePair("device_hash", deviceId));
+ kv.add(new BasicNameValuePair("device_name", deviceName));
+ kv.add(new BasicNameValuePair("device_version", deviceVersion));
+ kv.add(new BasicNameValuePair("device_country", deviceCountry));
+ kv.add(new BasicNameValuePair("device_carrier", deviceCarrier));
+ kv.add(new BasicNameValuePair("device_carrier_id", deviceCarrierId));
+
+ httpPost.setEntity(new UrlEncodedFormEntity(kv));
+ httpClient.execute(httpPost);
+
+ success = true;
+ } catch (IOException e) {
+ Log.w(TAG, "Could not upload stats checkin", e);
+ }
+
+ return success;
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ final Context context = ReportingService.this;
+ long interval;
+
+ if (result) {
+ final SharedPreferences prefs = AnonymousStats.getPreferences(context);
+ prefs.edit().putLong(AnonymousStats.ANONYMOUS_LAST_CHECKED,
+ System.currentTimeMillis()).apply();
+ // use set interval
+ interval = 0;
+ } else {
+ // error, try again in 3 hours
+ interval = 3L * 60L * 60L * 1000L;
+ }
+
+ ReportingServiceManager.setAlarm(context, interval);
+ stopSelf();
+ }
+ }
+}
diff --git a/src/com/android/settings/cmstats/ReportingServiceManager.java b/src/com/android/settings/cmstats/ReportingServiceManager.java
new file mode 100644
index 0000000..354d0d3
--- /dev/null
+++ b/src/com/android/settings/cmstats/ReportingServiceManager.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2012 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.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.util.Log;
+
+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;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
+ setAlarm(context, 0);
+ } else {
+ launchService(context);
+ }
+ }
+
+ public static void setAlarm(Context context, long millisFromNow) {
+ SharedPreferences prefs = AnonymousStats.getPreferences(context);
+ boolean optedIn = prefs.getBoolean(AnonymousStats.ANONYMOUS_OPT_IN, true);
+ if (!optedIn) {
+ 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();
+ }
+
+ Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
+ intent.setClass(context, ReportingServiceManager.class);
+
+ 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");
+ }
+
+ public static void launchService(Context context) {
+ ConnectivityManager cm = (ConnectivityManager)
+ context.getSystemService(Context.CONNECTIVITY_SERVICE);
+
+ NetworkInfo networkInfo = cm.getActiveNetworkInfo();
+ if (networkInfo == null || !networkInfo.isConnected()) {
+ return;
+ }
+
+ SharedPreferences prefs = AnonymousStats.getPreferences(context);
+ boolean optedIn = prefs.getBoolean(AnonymousStats.ANONYMOUS_OPT_IN, true);
+ if (!optedIn) {
+ return;
+ }
+ long lastSynced = prefs.getLong(AnonymousStats.ANONYMOUS_LAST_CHECKED, 0);
+ if (lastSynced == 0) {
+ setAlarm(context, 0);
+ return;
+ }
+ long timeLeft = System.currentTimeMillis() - lastSynced;
+ if (timeLeft < UPDATE_INTERVAL) {
+ Log.d(ReportingService.TAG, "Waiting for next sync : " + timeLeft / MILLIS_PER_HOUR + " hours");
+ return;
+ }
+
+ Intent intent = new Intent();
+ intent.setClass(context, ReportingService.class);
+ context.startService(intent);
+ }
+}
diff --git a/src/com/android/settings/cmstats/Utilities.java b/src/com/android/settings/cmstats/Utilities.java
new file mode 100644
index 0000000..496f398
--- /dev/null
+++ b/src/com/android/settings/cmstats/Utilities.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2012 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.content.Context;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
+import java.math.BigInteger;
+import java.net.NetworkInterface;
+import java.security.MessageDigest;
+
+public class Utilities {
+ public static String getUniqueID(Context context) {
+ final String id = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
+ return digest(context.getPackageName() + id);
+ }
+
+ public static String getCarrier(Context context) {
+ TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ String carrier = tm.getNetworkOperatorName();
+ if (TextUtils.isEmpty(carrier)) {
+ carrier = "Unknown";
+ }
+ return carrier;
+ }
+
+ public static String getCarrierId(Context context) {
+ TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ String carrierId = tm.getNetworkOperator();
+ if (TextUtils.isEmpty(carrierId)) {
+ carrierId = "0";
+ }
+ return carrierId;
+ }
+
+ public static String getCountryCode(Context context) {
+ TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ String countryCode = tm.getNetworkCountryIso();
+ if (TextUtils.isEmpty(countryCode)) {
+ countryCode = "Unknown";
+ }
+ return countryCode;
+ }
+
+ public static String getDevice() {
+ return SystemProperties.get("ro.cm.device");
+ }
+
+ public static String getModVersion() {
+ return SystemProperties.get("ro.cm.version");
+ }
+
+ public static String digest(String input) {
+ try {
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ return new BigInteger(1, md.digest(input.getBytes())).toString(16).toUpperCase();
+ } catch (Exception e) {
+ return null;
+ }
+ }
+}