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 /bordeaux/service | |
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
Diffstat (limited to 'bordeaux/service')
7 files changed, 182 insertions, 72 deletions
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"); } |