summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuei-sung Lin <rslin@google.com>2012-09-19 18:12:24 -0700
committerRuei-sung Lin <rslin@google.com>2012-10-10 16:01:36 -0700
commit47c0dc05cde9e9d9cc57e1393429006bf8b23b32 (patch)
tree2b34455aad8103d39086bbd5f83b6e280645060a
parent1253e9fb0b5570ab8adaed222655a5b052aa072e (diff)
downloadml-47c0dc05cde9e9d9cc57e1393429006bf8b23b32.tar.gz
1. avoid battery drain in location aggregator
2. add thresholding in histogram predictor 3. add paired (loction and time) features Change-Id: I2f9e59cc1da454c6dc77dd7395e2082195993ac2
-rw-r--r--bordeaux/learning/predictor_histogram/java/android/bordeaux/learning/HistogramPredictor.java235
-rw-r--r--bordeaux/service/src/android/bordeaux/services/AggregatorManager.java3
-rw-r--r--bordeaux/service/src/android/bordeaux/services/BordeauxPredictor.java4
-rw-r--r--bordeaux/service/src/android/bordeaux/services/BordeauxService.java2
-rw-r--r--bordeaux/service/src/android/bordeaux/services/ClusterManager.java10
-rw-r--r--bordeaux/service/src/android/bordeaux/services/FeatureAssembly.java46
-rw-r--r--bordeaux/service/src/android/bordeaux/services/LocationStatsAggregator.java142
-rw-r--r--bordeaux/service/src/android/bordeaux/services/Predictor.java47
8 files changed, 327 insertions, 162 deletions
diff --git a/bordeaux/learning/predictor_histogram/java/android/bordeaux/learning/HistogramPredictor.java b/bordeaux/learning/predictor_histogram/java/android/bordeaux/learning/HistogramPredictor.java
index ef57939fd..e63f40dd9 100644
--- a/bordeaux/learning/predictor_histogram/java/android/bordeaux/learning/HistogramPredictor.java
+++ b/bordeaux/learning/predictor_histogram/java/android/bordeaux/learning/HistogramPredictor.java
@@ -14,11 +14,9 @@
* limitations under the License.
*/
-
package android.bordeaux.learning;
import android.util.Log;
-import android.util.Pair;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -35,17 +33,19 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-
+import java.util.concurrent.ConcurrentHashMap;
/**
- * A histogram based predictor which records co-occurrences of applations with a speficic feature,
- * for example, location, * time of day, etc. The histogram is kept in a two level hash table.
- * The first level key is the feature value and the second level key is the app id.
+ * A histogram based predictor which records co-occurrences of applations with a speficic
+ * feature, for example, location, * time of day, etc. The histogram is kept in a two level
+ * hash table. The first level key is the feature value and the second level key is the app
+ * id.
*/
// TODOS:
// 1. Use forgetting factor to downweight istances propotional to the time
// 2. Different features could have different weights on prediction scores.
-// 3. Make prediction (on each feature) only when the histogram has collected
-// sufficient counts.
+// 3. Add function to remove sampleid (i.e. remove apps that are uninstalled).
+
+
public class HistogramPredictor {
final static String TAG = "HistogramPredictor";
@@ -53,10 +53,33 @@ public class HistogramPredictor {
new HashMap<String, HistogramCounter>();
private HashMap<String, Integer> mClassCounts = new HashMap<String, Integer>();
- private int mTotalClassCount = 0;
-
- private static final double FEATURE_INACTIVE_LIKELIHOOD = 0.00000001;
- private static final double LOG_INACTIVE = Math.log(FEATURE_INACTIVE_LIKELIHOOD);
+ private HashSet<String> mBlacklist = new HashSet<String>();
+
+ private static final int MINIMAL_FEATURE_VALUE_COUNTS = 5;
+ private static final int MINIMAL_APP_APPEARANCE_COUNTS = 5;
+
+ // This parameter ranges from 0 to 1 which determines the effect of app prior.
+ // When it is set to 0, app prior means completely neglected. When it is set to 1
+ // the predictor is a standard naive bayes model.
+ private static final int PRIOR_K_VALUE = 1;
+
+ private static final String[] APP_BLACKLIST = {
+ "com.android.contacts",
+ "com.android.chrome",
+ "com.android.providers.downloads.ui",
+ "com.android.settings",
+ "com.android.vending",
+ "com.android.mms",
+ "com.google.android.gm",
+ "com.google.android.gallery3d",
+ "com.google.android.apps.googlevoice",
+ };
+
+ public HistogramPredictor(String[] blackList) {
+ for (String appName : blackList) {
+ mBlacklist.add(appName);
+ }
+ }
/*
* This class keeps the histogram counts for each feature and provide the
@@ -65,27 +88,18 @@ public class HistogramPredictor {
private class HistogramCounter {
private HashMap<String, HashMap<String, Integer> > mCounter =
new HashMap<String, HashMap<String, Integer> >();
- private int mTotalCount;
public HistogramCounter() {
- resetCounter();
+ mCounter.clear();
}
public void setCounter(HashMap<String, HashMap<String, Integer> > counter) {
resetCounter();
mCounter.putAll(counter);
-
- // get total count
- for (Map.Entry<String, HashMap<String, Integer> > entry : counter.entrySet()) {
- for (Integer value : entry.getValue().values()) {
- mTotalCount += value.intValue();
- }
- }
}
public void resetCounter() {
mCounter.clear();
- mTotalCount = 0;
}
public void addSample(String className, String featureValue) {
@@ -100,50 +114,95 @@ public class HistogramPredictor {
int count = (classCounts.containsKey(className)) ?
classCounts.get(className) + 1 : 1;
classCounts.put(className, count);
- mTotalCount++;
}
public HashMap<String, Double> getClassScores(String featureValue) {
HashMap<String, Double> classScores = new HashMap<String, Double>();
- double logTotalCount = Math.log((double) mTotalCount);
if (mCounter.containsKey(featureValue)) {
+ int totalCount = 0;
for(Map.Entry<String, Integer> entry :
mCounter.get(featureValue).entrySet()) {
- double score =
- Math.log((double) entry.getValue()) - logTotalCount;
- classScores.put(entry.getKey(), score);
+ String app = entry.getKey();
+ int count = entry.getValue();
+
+ // For apps with counts less than or equal to one, we treated
+ // those as having count one. Hence their score, i.e. log(count)
+ // would be zero. classScroes stores only apps with non-zero scores.
+ // Note that totalCount also neglect app with single occurrence.
+ if (count > 1) {
+ double score = Math.log((double) count);
+ classScores.put(app, score);
+ totalCount += count;
+ }
+ }
+ if (totalCount < MINIMAL_FEATURE_VALUE_COUNTS) {
+ classScores.clear();
}
}
return classScores;
}
+ public byte[] getModel() {
+ try {
+ ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+ ObjectOutputStream objStream = new ObjectOutputStream(byteStream);
+ synchronized(mCounter) {
+ objStream.writeObject(mCounter);
+ }
+ byte[] bytes = byteStream.toByteArray();
+ return bytes;
+ } catch (IOException e) {
+ throw new RuntimeException("Can't get model");
+ }
+ }
+
+ public boolean setModel(final byte[] modelData) {
+ mCounter.clear();
+ HashMap<String, HashMap<String, Integer> > model;
+
+ try {
+ ByteArrayInputStream input = new ByteArrayInputStream(modelData);
+ ObjectInputStream objStream = new ObjectInputStream(input);
+ model = (HashMap<String, HashMap<String, Integer> >) objStream.readObject();
+ } catch (IOException e) {
+ throw new RuntimeException("Can't load model");
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Learning class not found");
+ }
+
+ synchronized(mCounter) {
+ mCounter.putAll(model);
+ }
+
+ return true;
+ }
+
+
public HashMap<String, HashMap<String, Integer> > getCounter() {
return mCounter;
}
- }
-
- private double getDefaultLikelihood(Map<String, String> features) {
- int featureCount = 0;
- for(String featureName : features.keySet()) {
- if (mPredictor.containsKey(featureName)) {
- featureCount++;
+ public String toString() {
+ String result = "";
+ for (Map.Entry<String, HashMap<String, Integer> > entry :
+ mCounter.entrySet()) {
+ result += "{ " + entry.getKey() + " : " +
+ entry.getValue().toString() + " }";
}
+ return result;
}
- return LOG_INACTIVE * featureCount;
}
/*
- * Given a map of feature name -value pairs returns the mostly likely apps to
- * be launched with corresponding likelihoods.
+ * Given a map of feature name -value pairs returns topK mostly likely apps to
+ * be launched with corresponding likelihoods. If topK is set zero, it will return
+ * the whole list.
*/
public List<Map.Entry<String, Double> > findTopClasses(Map<String, String> features, int topK) {
// Most sophisticated function in this class
HashMap<String, Double> appScores = new HashMap<String, Double>();
- double defaultLikelihood = getDefaultLikelihood(features);
-
- HashMap<String, Integer> appearCounts = new HashMap<String, Integer>();
+ int validFeatureCount = 0;
// compute all app scores
for (Map.Entry<String, HistogramCounter> entry : mPredictor.entrySet()) {
@@ -154,44 +213,48 @@ public class HistogramPredictor {
String featureValue = features.get(featureName);
HashMap<String, Double> scoreMap = counter.getClassScores(featureValue);
+ if (scoreMap.isEmpty()) {
+ continue;
+ }
+ validFeatureCount++;
+
for (Map.Entry<String, Double> item : scoreMap.entrySet()) {
String appName = item.getKey();
double appScore = item.getValue();
- double score = (appScores.containsKey(appName)) ?
- appScores.get(appName) : defaultLikelihood;
- score += appScore - LOG_INACTIVE;
- appScores.put(appName, score);
-
- int count = (appearCounts.containsKey(appName)) ?
- appearCounts.get(appName) + 1 : 1;
- appearCounts.put(appName, count);
+ if (appScores.containsKey(appName)) {
+ appScore += appScores.get(appName);
+ }
+ appScores.put(appName, appScore);
}
}
}
- // TODO: this check should be unnecessary
- if (mClassCounts.size() != 0 && mTotalClassCount != 0) {
- for (Map.Entry<String, Double> entry : appScores.entrySet()) {
- String appName = entry.getKey();
- double appScore = entry.getValue();
- if (!appearCounts.containsKey(appName)) {
- throw new RuntimeException("appearance count error!");
- }
- int appearCount = appearCounts.get(appName);
-
- if (!mClassCounts.containsKey(appName)) {
- throw new RuntimeException("class count error!");
- }
- double appPrior =
- Math.log(mClassCounts.get(appName)) - Math.log(mTotalClassCount);
- appScores.put(appName, appScore - appPrior * (appearCount - 1));
+ HashMap<String, Double> appCandidates = new HashMap<String, Double>();
+ for (Map.Entry<String, Double> entry : appScores.entrySet()) {
+ String appName = entry.getKey();
+ if (mBlacklist.contains(appName)) {
+ Log.i(TAG, appName + " is in blacklist");
+ continue;
+ }
+ if (!mClassCounts.containsKey(appName)) {
+ throw new RuntimeException("class count error!");
}
+ int appCount = mClassCounts.get(appName);
+ if (appCount < MINIMAL_APP_APPEARANCE_COUNTS) {
+ Log.i(TAG, appName + " doesn't have enough counts");
+ continue;
+ }
+
+ double appScore = entry.getValue();
+ double appPrior = Math.log((double) appCount);
+ appCandidates.put(appName,
+ appScore - appPrior * (validFeatureCount - PRIOR_K_VALUE));
}
// sort app scores
List<Map.Entry<String, Double> > appList =
- new ArrayList<Map.Entry<String, Double> >(appScores.size());
- appList.addAll(appScores.entrySet());
+ new ArrayList<Map.Entry<String, Double> >(appCandidates.size());
+ appList.addAll(appCandidates.entrySet());
Collections.sort(appList, new Comparator<Map.Entry<String, Double> >() {
public int compare(Map.Entry<String, Double> o1,
Map.Entry<String, Double> o2) {
@@ -199,22 +262,23 @@ public class HistogramPredictor {
}
});
- Log.v(TAG, "findTopApps appList: " + appList);
- return appList;
+ if (topK == 0) {
+ topK = appList.size();
+ }
+ return appList.subList(0, Math.min(topK, appList.size()));
}
/*
* Add a new observation of given sample id and features to the histograms
*/
public void addSample(String sampleId, Map<String, String> features) {
- for (Map.Entry<String, HistogramCounter> entry : mPredictor.entrySet()) {
+ for (Map.Entry<String, String> entry : features.entrySet()) {
String featureName = entry.getKey();
- HistogramCounter counter = entry.getValue();
+ String featureValue = entry.getValue();
- if (features.containsKey(featureName)) {
- String featureValue = features.get(featureName);
- counter.addSample(sampleId, featureValue);
- }
+ useFeature(featureName);
+ HistogramCounter counter = mPredictor.get(featureName);
+ counter.addSample(sampleId, featureValue);
}
int sampleCount = (mClassCounts.containsKey(sampleId)) ?
@@ -231,18 +295,7 @@ public class HistogramPredictor {
counter.resetCounter();
}
mPredictor.clear();
-
mClassCounts.clear();
- mTotalClassCount = 0;
- }
-
- /*
- * specify a feature to used for prediction
- */
- public void useFeature(String featureName) {
- if (!mPredictor.containsKey(featureName)) {
- mPredictor.put(featureName, new HistogramCounter());
- }
}
/*
@@ -261,7 +314,6 @@ public class HistogramPredictor {
ObjectOutputStream objStream = new ObjectOutputStream(byteStream);
objStream.writeObject(model);
byte[] bytes = byteStream.toByteArray();
- //Log.i(TAG, "getModel: " + bytes);
return bytes;
} catch (IOException e) {
throw new RuntimeException("Can't get model");
@@ -308,13 +360,12 @@ public class HistogramPredictor {
HashMap<String, HashMap<String, Integer> > counter =
mPredictor.get(TIME_OF_WEEK).getCounter();
- mTotalClassCount = 0;
mClassCounts.clear();
for (HashMap<String, Integer> map : counter.values()) {
for (Map.Entry<String, Integer> entry : map.entrySet()) {
int classCount = entry.getValue();
String className = entry.getKey();
- mTotalClassCount += classCount;
+ // mTotalClassCount += classCount;
if (mClassCounts.containsKey(className)) {
classCount += mClassCounts.get(className);
@@ -322,8 +373,12 @@ public class HistogramPredictor {
mClassCounts.put(className, classCount);
}
}
+ Log.i(TAG, "class counts: " + mClassCounts);
+ }
- Log.e(TAG, "class counts: " + mClassCounts + ", total count: " +
- mTotalClassCount);
+ private void useFeature(String featureName) {
+ if (!mPredictor.containsKey(featureName)) {
+ mPredictor.put(featureName, new HistogramCounter());
+ }
}
}
diff --git a/bordeaux/service/src/android/bordeaux/services/AggregatorManager.java b/bordeaux/service/src/android/bordeaux/services/AggregatorManager.java
index 87b79258c..0fea228a7 100644
--- a/bordeaux/service/src/android/bordeaux/services/AggregatorManager.java
+++ b/bordeaux/service/src/android/bordeaux/services/AggregatorManager.java
@@ -60,7 +60,7 @@ class AggregatorManager extends IAggregatorManager.Stub {
public void registerAggregator(Aggregator agg, AggregatorManager m) {
if (mAggregators.get(agg.getClass().getName()) != null) {
// only one instance
- throw new RuntimeException("Can't register more than one instance");
+ // throw new RuntimeException("Can't register more than one instance");
}
mAggregators.put(agg.getClass().getName(), agg);
agg.setManager(m);
@@ -68,6 +68,7 @@ class AggregatorManager extends IAggregatorManager.Stub {
for ( int i = 0; i< fl.length; i ++)
sFeatureMap.put(fl[i], agg);
}
+
// Start of IAggregatorManager interface
public ArrayList<StringString> getData(String dataName) {
return getList(getDataMap(dataName));
diff --git a/bordeaux/service/src/android/bordeaux/services/BordeauxPredictor.java b/bordeaux/service/src/android/bordeaux/services/BordeauxPredictor.java
index 4e0223fa7..ac46af11f 100644
--- a/bordeaux/service/src/android/bordeaux/services/BordeauxPredictor.java
+++ b/bordeaux/service/src/android/bordeaux/services/BordeauxPredictor.java
@@ -50,7 +50,7 @@ public class BordeauxPredictor {
public boolean reset() {
if (!retrievePredictor()){
- Log.e(TAG, PREDICTOR_NOTAVAILABLE);
+ Log.e(TAG, "reset: " + PREDICTOR_NOTAVAILABLE);
return false;
}
try {
@@ -66,7 +66,7 @@ public class BordeauxPredictor {
mPredictor = BordeauxManagerService.getPredictor(mContext, mName);
}
if (mPredictor == null) {
- Log.e(TAG, PREDICTOR_NOTAVAILABLE);
+ Log.e(TAG, "retrievePredictor: " + PREDICTOR_NOTAVAILABLE);
return false;
}
return true;
diff --git a/bordeaux/service/src/android/bordeaux/services/BordeauxService.java b/bordeaux/service/src/android/bordeaux/services/BordeauxService.java
index 41ee11026..48d41d942 100644
--- a/bordeaux/service/src/android/bordeaux/services/BordeauxService.java
+++ b/bordeaux/service/src/android/bordeaux/services/BordeauxService.java
@@ -103,6 +103,8 @@ public class BordeauxService extends Service {
// Unregister all callbacks.
mCallbacks.kill();
+ mLocationStatsAggregator.release();
+
Log.i(TAG, "Bordeaux service stopped.");
}
diff --git a/bordeaux/service/src/android/bordeaux/services/ClusterManager.java b/bordeaux/service/src/android/bordeaux/services/ClusterManager.java
index 249be860b..4f0dd6806 100644
--- a/bordeaux/service/src/android/bordeaux/services/ClusterManager.java
+++ b/bordeaux/service/src/android/bordeaux/services/ClusterManager.java
@@ -50,8 +50,8 @@ public class ClusterManager {
// stayed for at least 10 minutes (600 seconds) within a day.
private static final long SEMANTIC_CLUSTER_THRESHOLD = 600; // seconds
- // Reset location cluters every 12 hours (43200 seconds).
- private static final long LOCATION_REFRESH_PERIOD = 43200; // seconds
+ // Reset location cluters every 24 hours (86400 seconds).
+ private static final long LOCATION_REFRESH_PERIOD = 86400; // seconds
private static String UNKNOWN_LOCATION = "Unknown Location";
@@ -111,6 +111,9 @@ public class ClusterManager {
long currentTime = location.getTime() / 1000; // measure time in seconds
if (mLastLocation != null) {
+ if (location.getTime() == mLastLocation.getTime()) {
+ return;
+ }
// get the duration spent in the last location
long duration = (location.getTime() - mLastLocation.getTime()) / 1000;
mClusterDuration += duration;
@@ -204,7 +207,8 @@ public class ClusterManager {
String bestClusterId = "Unused Id";
for (BaseCluster cluster : mSemanticClusters) {
float distance = cluster.distanceToCluster(candidate);
- Log.v(TAG, distance + "distance to semantic cluster: " + cluster.getSemanticId());
+ Log.v(TAG, distance + "distance to semantic cluster: " +
+ cluster.getSemanticId());
if (distance < bestClusterDistance) {
bestClusterDistance = distance;
diff --git a/bordeaux/service/src/android/bordeaux/services/FeatureAssembly.java b/bordeaux/service/src/android/bordeaux/services/FeatureAssembly.java
index 3566950d2..75a4a9f28 100644
--- a/bordeaux/service/src/android/bordeaux/services/FeatureAssembly.java
+++ b/bordeaux/service/src/android/bordeaux/services/FeatureAssembly.java
@@ -18,6 +18,8 @@ package android.bordeaux.services;
import android.os.IBinder;
import android.util.Log;
+import android.util.Pair;
+import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
@@ -25,29 +27,41 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.Iterator;
-import android.bordeaux.services.AggregatorManager;
-import android.bordeaux.services.Aggregator;
-import java.io.Serializable;
class FeatureAssembly {
private static final String TAG = "FeatureAssembly";
private List<String> mPossibleFeatures;
private HashSet<String> mUseFeatures;
+ private HashSet<Pair<String, String> > mUsePairedFeatures;
private AggregatorManager mAggregatorManager;
public FeatureAssembly() {
mAggregatorManager = AggregatorManager.getInstance();
mPossibleFeatures = Arrays.asList(mAggregatorManager.getListOfFeatures());
mUseFeatures = new HashSet<String>();
+ mUsePairedFeatures = new HashSet<Pair<String, String> >();
}
public boolean registerFeature(String s) {
- boolean res = mPossibleFeatures.contains(s);
- if (res){
- if (!mUseFeatures.contains(s))
- mUseFeatures.add(s);
+ if (mPossibleFeatures.contains(s)) {
+ mUseFeatures.add(s);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public boolean registerFeaturePair(String[] features) {
+ if (features.length != 2 ||
+ !mPossibleFeatures.contains(features[0]) ||
+ !mPossibleFeatures.contains(features[1])) {
+ return false;
+ } else {
+ mUseFeatures.add(features[0]);
+ mUseFeatures.add(features[1]);
+ mUsePairedFeatures.add(Pair.create(features[0], features[1]));
+ return true;
}
- return res;
}
public Set<String> getUsedFeatures() {
@@ -66,12 +80,26 @@ class FeatureAssembly {
if (features.size() > 1) {
throw new RuntimeException("Incorrect feature format extracted from aggregator.");
}
-
featureMap.putAll(features);
}
+
+ if (!mUsePairedFeatures.isEmpty()) {
+ itr = mUsePairedFeatures.iterator();
+ while(itr.hasNext()) {
+ Pair<String, String> pair = (Pair<String, String>) itr.next();
+ if (featureMap.containsKey(pair.first) &&
+ featureMap.containsKey(pair.second)) {
+ String key = pair.first + Predictor.FEATURE_SEPARATOR + pair.second;
+ String value = featureMap.get(pair.first) + Predictor.FEATURE_SEPARATOR +
+ featureMap.get(pair.second);
+ featureMap.put(key, value);
+ }
+ }
+ }
return (Map)featureMap;
}
+
public String augmentFeatureInputString(String s) {
String fs = s;
Iterator itr = mUseFeatures.iterator();
diff --git a/bordeaux/service/src/android/bordeaux/services/LocationStatsAggregator.java b/bordeaux/service/src/android/bordeaux/services/LocationStatsAggregator.java
index 0390b7fbf..6f792be77 100644
--- a/bordeaux/service/src/android/bordeaux/services/LocationStatsAggregator.java
+++ b/bordeaux/service/src/android/bordeaux/services/LocationStatsAggregator.java
@@ -16,7 +16,12 @@
package android.bordeaux.services;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
@@ -27,6 +32,8 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.os.Process;
+import android.os.SystemClock;
+import android.text.format.Time;
import android.util.Log;
import java.util.HashMap;
import java.util.List;
@@ -40,13 +47,9 @@ public class LocationStatsAggregator extends Aggregator {
public static final String CURRENT_SPEED = "Current Speed";
public static final String UNKNOWN_LOCATION = "Unknown Location";
- // TODO: Collect location on every minute (60000 milliseconds)
- private static final long MINIMUM_TIME = 60000;
+ private static final long REPEAT_INTERVAL = 120000;
- // reset best location provider on every 10 minutes (300000 milliseconds)
- private static final int BEST_PROVIDER_DURATION = 600000;
-
- private static final float MINIMUM_DISTANCE = 0f; // meter
+ private static final long FRESH_THRESHOLD = 90000;
private static final int LOCATION_CHANGE = 1;
@@ -55,17 +58,57 @@ public class LocationStatsAggregator extends Aggregator {
private Handler mHandler;
private HandlerThread mHandlerThread;
+ private AlarmManager mAlarmManager;
private LocationManager mLocationManager;
+
private ClusterManager mClusterManager;
+ private Criteria mCriteria = new Criteria();
+
+ private LocationUpdater mLocationUpdater;
+
+ private Context mContext;
+ private PendingIntent mPendingIntent;
+
// Fake location, used for testing.
private String mFakeLocation = null;
public LocationStatsAggregator(final Context context) {
mLocationManager =
(LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
+ mAlarmManager =
+ (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+
setClusteringThread(context);
- requestLocationUpdate();
+
+ mCriteria.setAccuracy(Criteria.ACCURACY_COARSE);
+ mCriteria.setPowerRequirement(Criteria.POWER_LOW);
+ /*
+ mCriteria.setAltitudeRequired(false);
+ mCriteria.setBearingRequired(false);
+ mCriteria.setSpeedRequired(true);
+ */
+ mCriteria.setCostAllowed(true);
+
+
+ IntentFilter filter = new IntentFilter(LocationUpdater.LOCATION_UPDATE);
+ mLocationUpdater = new LocationUpdater();
+ context.registerReceiver(mLocationUpdater, filter);
+
+ Intent intent = new Intent(LocationUpdater.LOCATION_UPDATE);
+
+ mContext = context;
+ mPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+
+ mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + 30000, //
+ REPEAT_INTERVAL,
+ mPendingIntent);
+ }
+
+ public void release() {
+ mContext.unregisterReceiver(mLocationUpdater);
+ mAlarmManager.cancel(mPendingIntent);
}
public String[] getListOfFeatures(){
@@ -106,6 +149,54 @@ public class LocationStatsAggregator extends Aggregator {
else mFakeLocation = null;
}
+ private Location getLastKnownLocation() {
+ List<String> providers = mLocationManager.getAllProviders();
+ Location bestResult = null;
+ float bestAccuracy = Float.MAX_VALUE;
+ long bestTime;
+
+ // get the latest location data
+ long currTime = System.currentTimeMillis();
+ for (String provider : providers) {
+ Location location = mLocationManager.getLastKnownLocation(provider);
+
+ if (location != null) {
+ float accuracy = location.getAccuracy();
+ long time = location.getTime();
+
+ if (currTime - time < FRESH_THRESHOLD && accuracy < bestAccuracy) {
+ bestResult = location;
+ bestAccuracy = accuracy;
+ bestTime = time;
+ }
+ }
+ }
+ if (bestResult != null) {
+ Log.i(TAG, "found location for free: " + bestResult);
+ }
+ return bestResult;
+ }
+
+ private class LocationUpdater extends BroadcastReceiver {
+ String TAG = "LocationUpdater";
+
+ public static final String LOCATION_UPDATE = "android.bordeaux.services.LOCATION_UPDATE";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Location location = getLastKnownLocation();
+
+ if (location == null) {
+ String provider = mLocationManager.getBestProvider(mCriteria, true);
+ Log.i(TAG, "Best Available Location Provider: " + provider);
+ mLocationManager.requestSingleUpdate(provider, mLocationListener,
+ mHandlerThread.getLooper());
+ } else {
+ mHandler.sendMessage(mHandler.obtainMessage(LOCATION_CHANGE, location));
+ }
+ }
+ }
+
private void setClusteringThread(Context context) {
mClusterManager = new ClusterManager(context);
@@ -131,43 +222,12 @@ public class LocationStatsAggregator extends Aggregator {
};
}
- private void requestLocationUpdate() {
- Criteria criteria = new Criteria();
- criteria.setAccuracy(Criteria.ACCURACY_COARSE);
- criteria.setPowerRequirement(Criteria.POWER_LOW);
- /*
- criteria.setAltitudeRequired(false);
- criteria.setBearingRequired(false);
- criteria.setSpeedRequired(true);
- */
- criteria.setCostAllowed(true);
-
- String bestProvider = mLocationManager.getBestProvider(criteria, false);
- Log.i(TAG, "Best Location Provider: " + bestProvider);
-
- String bestAvailableProvider = mLocationManager.getBestProvider(criteria, true);
- Log.i(TAG, "Best Available Location Provider: " + bestAvailableProvider);
-
- mProviderSetTime = System.currentTimeMillis();
- if (bestAvailableProvider != null) {
- mLocationManager.requestLocationUpdates(
- bestAvailableProvider, MINIMUM_TIME, MINIMUM_DISTANCE, mLocationListener);
- }
- }
-
private final LocationListener mLocationListener = new LocationListener() {
+ private static final String TAG = "LocationListener";
+
public void onLocationChanged(Location location) {
- long currentTime = location.getTime();
- if (currentTime - mProviderSetTime < MINIMUM_TIME) {
- return;
- }
mHandler.sendMessage(mHandler.obtainMessage(LOCATION_CHANGE, location));
- // search again for the location service
- if (currentTime - mProviderSetTime > BEST_PROVIDER_DURATION) {
- mLocationManager.removeUpdates(this);
- Log.e(TAG, "reselect best location provider");
- requestLocationUpdate();
- }
+ mLocationManager.removeUpdates(this);
}
public void onStatusChanged(String provider, int status, Bundle extras) { }
diff --git a/bordeaux/service/src/android/bordeaux/services/Predictor.java b/bordeaux/service/src/android/bordeaux/services/Predictor.java
index e2bad2e8b..b3c0d4554 100644
--- a/bordeaux/service/src/android/bordeaux/services/Predictor.java
+++ b/bordeaux/service/src/android/bordeaux/services/Predictor.java
@@ -39,19 +39,35 @@ public class Predictor extends IPredictor.Stub
private final String TAG = "Predictor";
private ModelChangeCallback modelChangeCallback = null;
- private HistogramPredictor mPredictor = new HistogramPredictor();
private FeatureAssembly mFeatureAssembly = new FeatureAssembly();
- public static final String SET_FEATURE = "Set Feature";
- public static final String USE_HISTORY = "Use History";
-
- public static final String PREVIOUS_SAMPLE = "Previous Sample";
+ public static final String SET_FEATURE = "SetFeature";
+ public static final String SET_PAIRED_FEATURES = "SetPairedFeatures";
+ public static final String FEATURE_SEPARATOR = ":";
+ public static final String USE_HISTORY = "UseHistory";
+ public static final String PREVIOUS_SAMPLE = "PreviousSample";
private boolean mUseHistory = false;
private long mHistorySpan = 0;
private String mPrevSample;
private long mPrevSampleTime;
+ // TODO: blacklist should be set outside Bordeaux service!
+ private static final String[] APP_BLACKLIST = {
+ "com.android.contacts",
+ "com.android.chrome",
+ "com.android.providers.downloads.ui",
+ "com.android.settings",
+ "com.android.vending",
+ "com.android.mms",
+ "com.google.android.gm",
+ "com.google.android.gallery3d",
+ "com.google.android.apps.googlevoice",
+ };
+
+ private HistogramPredictor mPredictor = new HistogramPredictor(APP_BLACKLIST);
+
+
/**
* Reset the Predictor
*/
@@ -70,7 +86,7 @@ public class Predictor extends IPredictor.Stub
*/
public void pushNewSample(String sampleName) {
Map<String, String> sampleFeatures = getSampleFeatures();
- Log.e(TAG, "pushNewSample " + sampleName + ": " + sampleFeatures);
+ Log.i(TAG, "pushNewSample " + sampleName + ": " + sampleFeatures);
// TODO: move to the end of the function?
mPrevSample = sampleName;
@@ -99,16 +115,11 @@ public class Predictor extends IPredictor.Stub
* return probabilty of an exmple using the histogram
*/
public List<StringFloat> getTopCandidates(int topK) {
- ArrayList<StringFloat> result = new ArrayList<StringFloat>(topK);
Map<String, String> features = getSampleFeatures();
-
List<Map.Entry<String, Double> > topApps = mPredictor.findTopClasses(features, topK);
int listSize = topApps.size();
- if (topK > 0) {
- listSize = Math.min(topK, listSize);
- }
-
+ ArrayList<StringFloat> result = new ArrayList<StringFloat>(listSize);
for (int i = 0; i < listSize; ++i) {
Map.Entry<String, Double> entry = topApps.get(i);
result.add(new StringFloat(entry.getKey(), entry.getValue().floatValue()));
@@ -121,18 +132,22 @@ public class Predictor extends IPredictor.Stub
* and 2) featureAssembly e.g. time and location.
*/
public boolean setPredictorParameter(String key, String value) {
+ Log.i(TAG, "setParameter : " + key + ", " + value);
boolean result = true;
if (key.equals(SET_FEATURE)) {
result = mFeatureAssembly.registerFeature(value);
- if (result) {
- mPredictor.useFeature(value);
- } else {
+ if (!result) {
Log.e(TAG,"Setting on feauture: " + value + " which is not available");
}
+ } else if (key.equals(SET_PAIRED_FEATURES)) {
+ String[] features = value.split(FEATURE_SEPARATOR);
+ result = mFeatureAssembly.registerFeaturePair(features);
+ if (!result) {
+ Log.e(TAG,"Setting feauture pair: " + value + " is not valid");
+ }
} else if (key.equals(USE_HISTORY)) {
mUseHistory = true;
mHistorySpan = Long.valueOf(value);
- mPredictor.useFeature(PREVIOUS_SAMPLE);
} else {
Log.e(TAG,"Setting parameter " + key + " with " + value + " is not valid");
}