summaryrefslogtreecommitdiffstats
path: root/mobile/android/thirdparty/com/keepsafe/switchboard/SwitchBoard.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/thirdparty/com/keepsafe/switchboard/SwitchBoard.java')
-rw-r--r--mobile/android/thirdparty/com/keepsafe/switchboard/SwitchBoard.java390
1 files changed, 0 insertions, 390 deletions
diff --git a/mobile/android/thirdparty/com/keepsafe/switchboard/SwitchBoard.java b/mobile/android/thirdparty/com/keepsafe/switchboard/SwitchBoard.java
deleted file mode 100644
index e99144045..000000000
--- a/mobile/android/thirdparty/com/keepsafe/switchboard/SwitchBoard.java
+++ /dev/null
@@ -1,390 +0,0 @@
-/*
- Copyright 2012 KeepSafe Software Inc.
-
- 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.keepsafe.switchboard;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.MissingResourceException;
-import java.util.zip.CRC32;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.json.JSONArray;
-
-import android.content.Context;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Build;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.util.Log;
-
-
-/**
- * SwitchBoard is the core class of the KeepSafe Switchboard mobile A/B testing framework.
- * This class provides a bunch of static methods that can be used in your app to run A/B tests.
- *
- * The SwitchBoard supports production and staging environment.
- *
- * For usage <code>initDefaultServerUrls</code> for first time usage. Server URLs can be updates from
- * a remote location with <code>initConfigServerUrl</code>.
- *
- * To run a experiment use <code>isInExperiment()</code>. The experiment name has to match the one you
- * setup on the server.
- * All functions are design to be safe for programming mistakes and network connection issues. If the
- * experiment does not exists it will return false and pretend the user is not part of it.
- *
- * @author Philipp Berner
- *
- */
-public class SwitchBoard {
-
- private static final String TAG = "SwitchBoard";
-
- /** Set if the application is run in debug mode. */
- public static boolean DEBUG = true;
-
- // Top-level experiment keys.
- private static final String KEY_DATA = "data";
- private static final String KEY_NAME = "name";
- private static final String KEY_MATCH = "match";
- private static final String KEY_BUCKETS = "buckets";
- private static final String KEY_VALUES = "values";
-
- // Match keys.
- private static final String KEY_APP_ID = "appId";
- private static final String KEY_COUNTRY = "country";
- private static final String KEY_DEVICE = "device";
- private static final String KEY_LANG = "lang";
- private static final String KEY_MANUFACTURER = "manufacturer";
- private static final String KEY_VERSION = "version";
-
- // Bucket keys.
- private static final String KEY_MIN = "min";
- private static final String KEY_MAX = "max";
-
- /**
- * Loads a new config for a user. This method does network I/O, so it
- * should not be called on the main thread.
- *
- * @param c ApplicationContext
- * @param serverUrl Server URL endpoint.
- */
- static void loadConfig(Context c, @NonNull String serverUrl) {
- final URL url;
- try {
- url = new URL(serverUrl);
- } catch (MalformedURLException e) {
- Log.e(TAG, "Exception creating server URL", e);
- return;
- }
-
- final String result = readFromUrlGET(url);
- if (DEBUG) Log.d(TAG, "Result: " + result);
- if (result == null) {
- return;
- }
-
- // Cache result locally in shared preferences.
- Preferences.setDynamicConfigJson(c, result);
- }
-
- public static boolean isInBucket(Context c, int low, int high) {
- final int userBucket = getUserBucket(c);
- return (userBucket >= low) && (userBucket < high);
- }
-
- /**
- * Looks up in config if user is in certain experiment. Returns false as a default value when experiment
- * does not exist.
- * Experiment names are defined server side as Key in array for return values.
- * @param experimentName Name of the experiment to lookup
- * @return returns value for experiment or false if experiment does not exist.
- */
- public static boolean isInExperiment(Context c, String experimentName) {
- final Boolean override = Preferences.getOverrideValue(c, experimentName);
- if (override != null) {
- return override;
- }
-
- final String config = Preferences.getDynamicConfigJson(c);
- if (config == null) {
- return false;
- }
-
- try {
- // TODO: cache the array into a mapping so we don't do a loop everytime we are looking for a experiment key
- final JSONArray experiments = new JSONObject(config).getJSONArray(KEY_DATA);
- JSONObject experiment = null;
-
- for (int i = 0; i < experiments.length(); i++) {
- JSONObject entry = experiments.getJSONObject(i);
- final String name = entry.getString(KEY_NAME);
- if (name.equals(experimentName)) {
- experiment = entry;
- break;
- }
- }
-
- if (experiment == null) {
- return false;
- }
-
- if (!isMatch(c, experiment.optJSONObject(KEY_MATCH))) {
- return false;
- }
-
- final JSONObject buckets = experiment.getJSONObject(KEY_BUCKETS);
- final boolean inExperiment = isInBucket(c, buckets.getInt(KEY_MIN), buckets.getInt(KEY_MAX));
-
- if (DEBUG) {
- Log.d(TAG, experimentName + " = " + inExperiment);
- }
- return inExperiment;
- } catch (JSONException e) {
- // If the experiment name is not found in the JSON, just return false.
- // There is no need to log an error, since we don't really care if an
- // inactive experiment is missing from the config.
- return false;
- }
- }
-
- private static List<String> getExperimentNames(Context c) throws JSONException {
- // TODO: cache the array into a mapping so we don't do a loop everytime we are looking for a experiment key
- final List<String> returnList = new ArrayList<>();
- final String config = Preferences.getDynamicConfigJson(c);
- final JSONArray experiments = new JSONObject(config).getJSONArray(KEY_DATA);
-
- for (int i = 0; i < experiments.length(); i++) {
- JSONObject entry = experiments.getJSONObject(i);
- returnList.add(entry.getString(KEY_NAME));
- }
- return returnList;
- }
-
- @Nullable
- private static JSONObject getExperiment(Context c, String experimentName) throws JSONException {
- // TODO: cache the array into a mapping so we don't do a loop everytime we are looking for a experiment key
- final String config = Preferences.getDynamicConfigJson(c);
- final JSONArray experiments = new JSONObject(config).getJSONArray(KEY_DATA);
- JSONObject experiment = null;
-
- for (int i = 0; i < experiments.length(); i++) {
- JSONObject entry = experiments.getJSONObject(i);
- if (entry.getString(KEY_NAME).equals(experimentName)) {
- experiment = entry;
- break;
- }
- }
- return experiment;
- }
-
- private static boolean isMatch(Context c, @Nullable JSONObject matchKeys) {
- // If no match keys are specified, default to enabling the experiment.
- if (matchKeys == null) {
- return true;
- }
-
- if (matchKeys.has(KEY_APP_ID)) {
- final String packageName = c.getPackageName();
- try {
- if (!packageName.matches(matchKeys.getString(KEY_APP_ID))) {
- return false;
- }
- } catch (JSONException e) {
- Log.e(TAG, "Exception matching appId", e);
- }
- }
-
- if (matchKeys.has(KEY_COUNTRY)) {
- try {
- final String country = Locale.getDefault().getISO3Country();
- if (!country.matches(matchKeys.getString(KEY_COUNTRY))) {
- return false;
- }
- } catch (MissingResourceException|JSONException e) {
- Log.e(TAG, "Exception matching country", e);
- }
- }
-
- if (matchKeys.has(KEY_DEVICE)) {
- final String device = Build.DEVICE;
- try {
- if (!device.matches(matchKeys.getString(KEY_DEVICE))) {
- return false;
- }
- } catch (JSONException e) {
- Log.e(TAG, "Exception matching device", e);
- }
-
- }
- if (matchKeys.has(KEY_LANG)) {
- try {
- final String lang = Locale.getDefault().getISO3Language();
- if (!lang.matches(matchKeys.getString(KEY_LANG))) {
- return false;
- }
- } catch (MissingResourceException|JSONException e) {
- Log.e(TAG, "Exception matching lang", e);
- }
- }
- if (matchKeys.has(KEY_MANUFACTURER)) {
- final String manufacturer = Build.MANUFACTURER;
- try {
- if (!manufacturer.matches(matchKeys.getString(KEY_MANUFACTURER))) {
- return false;
- }
- } catch (JSONException e) {
- Log.e(TAG, "Exception matching manufacturer", e);
- }
- }
-
- if (matchKeys.has(KEY_VERSION)) {
- try {
- final String version = c.getPackageManager().getPackageInfo(c.getPackageName(), 0).versionName;
- if (!version.matches(matchKeys.getString(KEY_VERSION))) {
- return false;
- }
- } catch (NameNotFoundException|JSONException e) {
- Log.e(TAG, "Exception matching version", e);
- }
- }
-
- // Default to return true if no matches failed.
- return true;
- }
-
- /**
- * @return a list of all active experiments.
- */
- public static List<String> getActiveExperiments(Context c) {
- final List<String> returnList = new ArrayList<>();
-
- final String config = Preferences.getDynamicConfigJson(c);
- if (config == null) {
- return returnList;
- }
-
- try {
- final JSONObject data = new JSONObject(config);
- final List<String> experiments = getExperimentNames(c);
-
- for (int i = 0; i < experiments.size(); i++) {
- final String name = experiments.get(i);
-
- // Check override value before reading saved JSON.
- Boolean isActive = Preferences.getOverrideValue(c, name);
- if (isActive == null) {
- // TODO: This is inefficient because it will check all the match cases on all experiments.
- isActive = isInExperiment(c, name);
- }
- if (isActive) {
- returnList.add(name);
- }
- }
- } catch (JSONException e) {
- // Something went wrong!
- }
-
- return returnList;
- }
-
- /**
- * Checks if a certain experiment has additional values.
- * @param c ApplicationContext
- * @param experimentName Name of the experiment
- * @return true when experiment exists
- */
- public static boolean hasExperimentValues(Context c, String experimentName) {
- return getExperimentValuesFromJson(c, experimentName) != null;
- }
-
- /**
- * Returns the experiment value as a JSONObject.
- * @param experimentName Name of the experiment
- * @return Experiment value as String, null if experiment does not exist.
- */
- @Nullable
- public static JSONObject getExperimentValuesFromJson(Context c, String experimentName) {
- final String config = Preferences.getDynamicConfigJson(c);
-
- if (config == null) {
- return null;
- }
-
- try {
- final JSONObject experiment = getExperiment(c, experimentName);
- if (experiment == null) {
- return null;
- }
- return experiment.getJSONObject(KEY_VALUES);
- } catch (JSONException e) {
- Log.e(TAG, "Could not create JSON object from config string", e);
- }
-
- return null;
- }
-
- /**
- * Returns a String containing the server response from a GET request
- * @param url URL for GET request.
- * @return Returns String from server or null when failed.
- */
- @Nullable private static String readFromUrlGET(URL url) {
- try {
- HttpURLConnection connection = (HttpURLConnection) url.openConnection();
- connection.setRequestMethod("GET");
- connection.setUseCaches(false);
-
- InputStream is = connection.getInputStream();
- InputStreamReader inputStreamReader = new InputStreamReader(is);
- BufferedReader bufferReader = new BufferedReader(inputStreamReader, 8192);
- String line;
- StringBuilder resultContent = new StringBuilder();
- while ((line = bufferReader.readLine()) != null) {
- resultContent.append(line);
- }
- bufferReader.close();
-
- return resultContent.toString();
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- return null;
- }
-
- /**
- * Return the bucket number of the user. There are 100 possible buckets.
- */
- private static int getUserBucket(Context c) {
- final DeviceUuidFactory df = new DeviceUuidFactory(c);
- final String uuid = df.getDeviceUuid().toString();
-
- CRC32 crc = new CRC32();
- crc.update(uuid.getBytes());
- long checksum = crc.getValue();
- return (int)(checksum % 100L);
- }
-}