diff options
author | Ruei-sung Lin <rslin@google.com> | 2012-09-19 18:12:24 -0700 |
---|---|---|
committer | Ruei-sung Lin <rslin@google.com> | 2012-10-10 16:01:36 -0700 |
commit | 47c0dc05cde9e9d9cc57e1393429006bf8b23b32 (patch) | |
tree | 2b34455aad8103d39086bbd5f83b6e280645060a | |
parent | 1253e9fb0b5570ab8adaed222655a5b052aa072e (diff) | |
download | ml-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
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"); } |