Adding Bordeaux AggregatorManager
and BordeauxPredictor
and SmartApplauncherWidget
and logging activity data in protobuff

Change-Id: Iddf9d97e27595426d4bc73145c894a0eea84667d
Signed-off-by: saberian <saberian@google.com>
diff --git a/bordeaux/apps/Android.mk b/bordeaux/apps/Android.mk
new file mode 100644
index 0000000..0918da5
--- /dev/null
+++ b/bordeaux/apps/Android.mk
@@ -0,0 +1,18 @@
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+TOP_LOCAL_PATH:= $(call my-dir)
+
+include $(call all-subdir-makefiles)
diff --git a/bordeaux/apps/AppLauncherWidget/Android.mk b/bordeaux/apps/AppLauncherWidget/Android.mk
new file mode 100644
index 0000000..b6af305
--- /dev/null
+++ b/bordeaux/apps/AppLauncherWidget/Android.mk
@@ -0,0 +1,42 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+TOP_LOCAL_PATH:= $(call my-dir)
+
+# Build activity
+
+LOCAL_PATH:= $(TOP_LOCAL_PATH)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES :=  $(call all-proto-files-under, src) \
+                    $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := bordeaux_learners bordeaux_service j2meproto google-common
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := micro
+
+LOCAL_PACKAGE_NAME := App_Launcher_Widget
+LOCAL_CERTIFICATE := platform
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
+
+# ============================================================
+
+# Also build all of the sub-targets under this one: the shared library.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/bordeaux/apps/AppLauncherWidget/AndroidManifest.xml b/bordeaux/apps/AppLauncherWidget/AndroidManifest.xml
new file mode 100644
index 0000000..d9d4e9d
--- /dev/null
+++ b/bordeaux/apps/AppLauncherWidget/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.bordeaux.applauncherwidget"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-sdk android:minSdkVersion="15" />
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+    <application
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name" >
+        <activity
+            android:name=".AppLauncherWidgetActivity"
+            android:label="@string/app_name" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <receiver android:name=".AppLauncherWidgetProvider"
+                  android:label="Smart App Launcher">
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+                <action android:name="android.appwidget.action.APPWIDGET_ENABLED" />
+                <action android:name="android.appwidget.action.APPWIDGET_DISABLED" />
+                <action android:name="android.appwidget.action.ACTION_APPWIDGET_DELETED" />
+            </intent-filter>
+            <meta-data android:name="android.appwidget.provider"
+                       android:resource="@xml/widget_info" />
+        </receiver>
+        <service android:name=".AppLauncherWidgetProvider$UpdateService" />
+        <service android:name=".AppLauncherWidgetProvider$GetStatService" />
+    </application>
+</manifest>
diff --git a/bordeaux/apps/AppLauncherWidget/res/drawable/background.xml b/bordeaux/apps/AppLauncherWidget/res/drawable/background.xml
new file mode 100644
index 0000000..44ba71f
--- /dev/null
+++ b/bordeaux/apps/AppLauncherWidget/res/drawable/background.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/Corners">
+    <gradient android:startColor="#CC111111" android:endColor="#CC7f7f7f" android:angle="45"/>
+    <padding android:left="4dp" android:top="1dp" android:right="4dp" android:bottom="1dp" />
+    <corners android:radius="4dp" />
+    <stroke android:width="2dp" android:color="#FFAfAfAf"/>
+</shape>
diff --git a/bordeaux/apps/AppLauncherWidget/res/drawable/ic_launcher.png b/bordeaux/apps/AppLauncherWidget/res/drawable/ic_launcher.png
new file mode 100644
index 0000000..8074c4c
--- /dev/null
+++ b/bordeaux/apps/AppLauncherWidget/res/drawable/ic_launcher.png
Binary files differ
diff --git a/bordeaux/apps/AppLauncherWidget/res/layout/main.xml b/bordeaux/apps/AppLauncherWidget/res/layout/main.xml
new file mode 100644
index 0000000..74699ca
--- /dev/null
+++ b/bordeaux/apps/AppLauncherWidget/res/layout/main.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical" >
+
+    <TextView
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/hello" />
+
+</LinearLayout>
+
diff --git a/bordeaux/apps/AppLauncherWidget/res/layout/widget.xml b/bordeaux/apps/AppLauncherWidget/res/layout/widget.xml
new file mode 100644
index 0000000..29519c3
--- /dev/null
+++ b/bordeaux/apps/AppLauncherWidget/res/layout/widget.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+  xmlns:android="http://schemas.android.com/apk/res/android"
+  android:layout_width="fill_parent"
+  android:layout_height="fill_parent"
+  android:orientation="horizontal"
+  android:background="@drawable/background"
+  android:padding = "8dp">
+    <LinearLayout
+         android:id="@+id/linearLayout0"
+         android:layout_width="match_parent"
+         android:layout_height="match_parent"
+         android:orientation="vertical"
+         android:layout_weight="1">
+         <ImageButton
+             android:id="@+id/button_app0"
+             android:layout_width="match_parent"
+             android:layout_height="52dp"
+             android:background="@android:color/transparent"
+             android:scaleType="fitCenter"/>
+         <TextView
+             android:id="@+id/text_app0"
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+             android:gravity="center"
+             android:singleLine="true"/>
+    </LinearLayout>
+    <LinearLayout
+         android:id="@+id/linearLayout1"
+         android:layout_width="match_parent"
+         android:layout_height="match_parent"
+         android:orientation="vertical"
+         android:layout_weight= "1">
+         <ImageButton
+             android:id="@+id/button_app1"
+             android:layout_width="match_parent"
+             android:layout_height="52dp"
+             android:background="@android:color/transparent"
+             android:scaleType="fitCenter"/>
+         <TextView
+             android:id="@+id/text_app1"
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+             android:gravity="center"
+             android:singleLine="true"/>
+    </LinearLayout>
+    <LinearLayout
+         android:id="@+id/linearLayout2"
+         android:layout_width="match_parent"
+         android:layout_height="match_parent"
+         android:orientation="vertical"
+         android:layout_weight= "1">
+         <ImageButton
+             android:id="@+id/button_app2"
+             android:layout_width="match_parent"
+             android:layout_height="52dp"
+             android:background="@android:color/transparent"
+             android:scaleType="fitCenter"/>
+         <TextView
+             android:id="@+id/text_app2"
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+             android:gravity="center"
+             android:singleLine="true"/>
+    </LinearLayout>
+    <LinearLayout
+         android:id="@+id/linearLayout3"
+         android:layout_width="match_parent"
+         android:layout_height="match_parent"
+         android:orientation="vertical"
+         android:layout_weight= "1">
+         <ImageButton
+             android:id="@+id/button_app3"
+             android:layout_width="match_parent"
+             android:layout_height="52dp"
+             android:background="@android:color/transparent"
+             android:scaleType="fitCenter"/>
+         <TextView
+             android:id="@+id/text_app3"
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+             android:gravity="center"
+             android:singleLine="true"/>
+    </LinearLayout>
+</LinearLayout>
diff --git a/bordeaux/apps/AppLauncherWidget/res/values/strings.xml b/bordeaux/apps/AppLauncherWidget/res/values/strings.xml
new file mode 100644
index 0000000..4414116
--- /dev/null
+++ b/bordeaux/apps/AppLauncherWidget/res/values/strings.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <string name="hello"> Activity for Smart App Launcher</string>
+    <string name="app_name">Smart App Launcher</string>
+
+</resources>
diff --git a/bordeaux/apps/AppLauncherWidget/res/xml/widget_info.xml b/bordeaux/apps/AppLauncherWidget/res/xml/widget_info.xml
new file mode 100644
index 0000000..67d57c3
--- /dev/null
+++ b/bordeaux/apps/AppLauncherWidget/res/xml/widget_info.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="296dp"
+    android:minHeight="36dp"
+    android:updatePeriodMillis="1000"
+    android:initialLayout="@layout/widget" >
+</appwidget-provider>
diff --git a/bordeaux/apps/AppLauncherWidget/src/android/bordeaux/applauncherwidget/AppLauncherWidgetActivity.java b/bordeaux/apps/AppLauncherWidget/src/android/bordeaux/applauncherwidget/AppLauncherWidgetActivity.java
new file mode 100644
index 0000000..c75dbed
--- /dev/null
+++ b/bordeaux/apps/AppLauncherWidget/src/android/bordeaux/applauncherwidget/AppLauncherWidgetActivity.java
@@ -0,0 +1,13 @@
+package android.bordeaux.applauncherwidget;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class AppLauncherWidgetActivity extends Activity {
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+}
diff --git a/bordeaux/apps/AppLauncherWidget/src/android/bordeaux/applauncherwidget/AppLauncherWidgetProvider.java b/bordeaux/apps/AppLauncherWidget/src/android/bordeaux/applauncherwidget/AppLauncherWidgetProvider.java
new file mode 100644
index 0000000..60ecb29
--- /dev/null
+++ b/bordeaux/apps/AppLauncherWidget/src/android/bordeaux/applauncherwidget/AppLauncherWidgetProvider.java
@@ -0,0 +1,397 @@
+package android.bordeaux.applauncherwidget;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.Comparator;
+import java.util.ArrayList;
+import java.io.*;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.app.AlarmManager;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.RemoteViews;
+import android.widget.Button;
+import android.widget.TextView;
+import android.content.Intent;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ApplicationInfo;
+import android.content.ComponentName;
+import android.content.BroadcastReceiver;
+import android.graphics.drawable.Drawable;
+import android.content.IntentFilter;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.IBinder;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.DropBoxManager;
+import com.android.internal.os.PkgUsageStats;
+import com.android.internal.app.IUsageStats;
+// libraries for bordeaux service
+import android.bordeaux.services.BordeauxPredictor;
+import android.bordeaux.services.BordeauxAggregatorManager;
+import android.bordeaux.services.BordeauxManagerService;
+// libraries for using protobuffer
+import com.google.common.io.protocol.ProtoBuf;
+import com.google.common.io.protocol.ProtoBufType;
+import com.google.protobuf.micro.*;
+import android.bordeaux.applauncherwidget.proto.ActivityRecordProto;
+import android.bordeaux.applauncherwidget.proto.ActivityRecordProto.ActivityRecordProtoBuff;
+
+public class AppLauncherWidgetProvider extends AppWidgetProvider {
+    String TAG = "AppLauncherWidgetProvider";
+    final static int STAT_SAMPLING_TIME = 3000; // milliseconds
+    final static int WIDGET_UPDATE_TIME = 8000; // milliseconds
+    // These numbers are low for debugging purpose and should be changed for final version
+    final static String PROTO_FILE_NAME = "ActivityLogProto";
+    final static String PREDICTOR_NAME ="SmartAppLauncher";
+    final static String CURRENT_TIME = "Current Time";
+    final static String CURRENT_LOCATION = "Current Location";
+    final static String CURRENT_MOTION = "Current Motion";
+    final static String EXP_TIME_STRING = "120";
+
+    static private PendingIntent sWidgetUpdateService = null;
+    static private PendingIntent sGetStatService = null;
+    static private AlarmManager sAlarmManager;
+    static PkgUsageStats[] sStatsNew;
+    static PkgUsageStats[] sStatsOld;
+    static PackageManager sPackageManager;
+    static IUsageStats sUsageStatsService;
+    static BordeauxPredictor sPredictor;
+    static BordeauxAggregatorManager sAggregatorManager;
+    static volatile boolean sGetPredictor = false;
+    static volatile boolean sSetPredictor = false;
+    static volatile boolean sNewAppLaunched = false;
+    static boolean sWidgetFirstRun;
+    static boolean sWasScreenOn = true;
+
+    public void onEnabled(Context context) {
+        sWidgetFirstRun = true;
+        sPackageManager = context.getPackageManager();
+        sAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+        sUsageStatsService = IUsageStats.Stub.asInterface(ServiceManager.getService("usagestats"));
+        if (sUsageStatsService == null) {
+            Log.e(TAG, "Failed to retrieve usagestats service");
+            return;
+        }
+    }
+
+    public void onDisabled(Context context) {
+        sAlarmManager.cancel(sWidgetUpdateService);
+        sAlarmManager.cancel(sGetStatService);
+    }
+
+    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
+        //Log.i(TAG, "onUpdate widget");
+        setServices(context);
+    }
+
+    public static void setServices(Context context) {
+        Intent i = new Intent(context, UpdateService.class);
+        if (sWidgetUpdateService == null)
+        {
+            sWidgetUpdateService = PendingIntent.getService(context, 0, i,
+                                                            PendingIntent.FLAG_CANCEL_CURRENT);
+        }
+        sAlarmManager.set(AlarmManager.RTC, System.currentTimeMillis(), sWidgetUpdateService);
+        i = new Intent(context, GetStatService.class);
+        if (sGetStatService == null)
+        {
+            sGetStatService = PendingIntent.getService(context, 1, i,
+                                                       PendingIntent.FLAG_CANCEL_CURRENT);
+        }
+        sAlarmManager.set(AlarmManager.RTC, System.currentTimeMillis(), sGetStatService);
+    }
+    //broadcast reciever for screen on/off
+    public static class ScreenReceiver extends BroadcastReceiver {
+        String TAG = "ScreenReceiver";
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
+                if (sWasScreenOn) {
+                    //Log.i(TAG,"Screen is off");
+                    sAlarmManager.cancel(sWidgetUpdateService);
+                    sAlarmManager.cancel(sGetStatService);
+                    sWasScreenOn = false;
+                }
+            } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
+                if (!sWasScreenOn) {
+                    //Log.i(TAG,"Screen is on");
+                    setServices(context);
+                    sWasScreenOn = true;
+                }
+            }
+        }
+    }
+    // Service for updating widget icons
+    public static class UpdateService extends Service {
+        String TAG = "updating Service";
+
+        @Override
+        public void onStart(Intent intent, int startId) {
+            Log.i(TAG,"update widget layout ");
+            //if ((mNewAppLaunched)|| (mWidgetFirstRun)) {
+                RemoteViews updateViews = buildUpdate(this);
+                ComponentName thisWidget = new ComponentName(this, AppLauncherWidgetProvider.class);
+                AppWidgetManager manager = AppWidgetManager.getInstance(this);
+                manager.updateAppWidget(thisWidget, updateViews);
+                sNewAppLaunched = false;
+                sWidgetFirstRun = false;
+            //}
+            sAlarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + WIDGET_UPDATE_TIME,
+                              sWidgetUpdateService);
+        }
+
+        @Override
+        public IBinder onBind(Intent intent) {
+            return null;
+        }
+
+        public RemoteViews buildUpdate(Context context) {
+            final int APP_NUM = 4;
+            ApplicationInfo[] appInfo = new ApplicationInfo[APP_NUM];
+            PendingIntent[] pendingIntent = new PendingIntent[APP_NUM];
+            CharSequence[] labels = new CharSequence[APP_NUM];
+            Bitmap[] icons = new Bitmap[APP_NUM];
+            String[] appList = new String [APP_NUM];
+            RemoteViews  views = new RemoteViews(context.getPackageName(), R.layout.widget);
+            // get the app list from the Bordeaux Service
+            if (!sGetPredictor) {
+                sAggregatorManager = new BordeauxAggregatorManager(this);
+                sPredictor = new BordeauxPredictor(this, PREDICTOR_NAME);
+                sGetPredictor = true;
+            }
+
+            if (!sPredictor.retrievePredictor()) {
+                Log.i(TAG,"Predictor is not availible yet");
+                appList[0] = "com.google.android.gm";
+                appList[1] = "com.google.android.talk";
+                appList[2] = "com.google.android.browser";
+                appList[3] = "com.google.android.deskclock";
+            } else {
+                if (!sSetPredictor) {
+                    sPredictor.setParameter("Set Feature", CURRENT_TIME);
+                    sPredictor.setParameter("Set Feature", CURRENT_LOCATION);
+                    sPredictor.setParameter("Set Feature", CURRENT_MOTION);
+                    sPredictor.setParameter("SetExpireTime", EXP_TIME_STRING);
+                    //sPredictor.setParameter("UseHistory", "true");
+                    sSetPredictor = true;
+                }
+                appList = getSortedAppList(sPredictor, APP_NUM);
+            }
+            // Get intent, icon and label for each app
+            for (int j = 0; j < APP_NUM; j++){
+                try {
+                    appInfo[j] = sPackageManager.getApplicationInfo(appList[j],
+                                                                    sPackageManager.GET_META_DATA);
+                    labels[j] = sPackageManager.getApplicationLabel(appInfo[j]);
+                    Intent intent = sPackageManager.getLaunchIntentForPackage(appList[j]);
+                    pendingIntent[j] = PendingIntent.getActivity(context, 0, intent, 0);
+                    icons[j] = ((BitmapDrawable) sPackageManager
+                            .getApplicationIcon(appInfo[j])).getBitmap();
+                } catch (NameNotFoundException e ) {
+                    Log.e(TAG,"package name is not found");
+                }
+            }
+            // Set Application names
+            views.setTextViewText(R.id.text_app0, labels[0]);
+            views.setTextViewText(R.id.text_app1, labels[1]);
+            views.setTextViewText(R.id.text_app2, labels[2]);
+            views.setTextViewText(R.id.text_app3, labels[3]);
+            // Set Application Icons
+            views.setImageViewBitmap(R.id.button_app0, icons[0]);
+            views.setImageViewBitmap(R.id.button_app1, icons[1]);
+            views.setImageViewBitmap(R.id.button_app2, icons[2]);
+            views.setImageViewBitmap(R.id.button_app3, icons[3]);
+            // Set Application Intents
+            views.setOnClickPendingIntent(R.id.button_app0, pendingIntent[0]);
+            views.setOnClickPendingIntent(R.id.button_app1, pendingIntent[1]);
+            views.setOnClickPendingIntent(R.id.button_app2, pendingIntent[2]);
+            views.setOnClickPendingIntent(R.id.button_app3, pendingIntent[3]);
+            return views;
+        }
+
+        private String[] getSortedAppList(BordeauxPredictor predictor, int n) {
+            class TComp implements Comparator{
+               public int compare(Object o1, Object o2) {
+                    float f1 = ((Float)o1).floatValue();
+                    float f2 = ((Float)o2).floatValue();
+                    if (f1 < f2)
+                        return +1;
+                    return -1;
+               }
+            }
+            TreeMap<Float, String> tMap = new TreeMap<Float, String>(new TComp());
+            for (PackageInfo info : sPackageManager.getInstalledPackages(0)) {
+                float f = predictor.getProbability(info.packageName);
+                tMap.put( f, info.packageName);
+            }
+            String[] sortedlist = new String[n];
+            ArrayList<String> sortedApps = new ArrayList<String>(tMap.values());
+            int l = sortedApps.size();
+            int j = 0;
+            for (int i = 0; i < n ; i++) {
+                while ((sPackageManager.getLaunchIntentForPackage(sortedApps.get(j))==null)&(j<l)){
+                    j++;
+                    if (j>= l)
+                        break;
+                }
+                if (j>= l)
+                    break;
+                sortedlist[i] = sortedApps.get(j);
+                j++;
+            }
+            return sortedlist;
+        }
+    }
+    // Service for getting stats about application usage.
+    public static class GetStatService extends Service {
+        String TAG = "get stat Service";
+        private class appRecord {
+            String pkgName;
+            int launchNum;
+            float duration;
+            public void set(appRecord r){
+                this.pkgName = r.pkgName;
+                this.launchNum = r.launchNum;
+                this.duration = r.duration;
+            }
+        }
+
+        public GetStatService() {
+            super();
+            try {
+                sStatsOld = sUsageStatsService.getAllPkgUsageStats();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to get usage stats of applications");
+            }
+        }
+
+        @Override
+        public void onStart(Intent intent, int startId) {
+            // registering broadcast reciever for screen on/off
+            IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
+            filter.addAction(Intent.ACTION_SCREEN_OFF);
+            BroadcastReceiver receiver = new ScreenReceiver();
+            this.registerReceiver(receiver, filter);
+
+            try {
+                sStatsNew = sUsageStatsService.getAllPkgUsageStats();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to get usage stats of applications");
+            }
+            ArrayList<appRecord> newLaunchedApps = extractLaunchedApps(sStatsNew, sStatsOld);
+            Log.i(TAG, "Serivce got the stats " + newLaunchedApps.size());
+            if (!sGetPredictor) {
+                sAggregatorManager = new BordeauxAggregatorManager(this);
+                sPredictor = new BordeauxPredictor(this, PREDICTOR_NAME);
+                sGetPredictor = true;
+            }
+
+            if ((newLaunchedApps.size() > 0 ) && sPredictor.retrievePredictor()) {
+                sNewAppLaunched = true;
+                if (!sSetPredictor) {
+                    sPredictor.setParameter("Set Feature", CURRENT_TIME);
+                    sPredictor.setParameter("Set Feature", CURRENT_LOCATION);
+                    sPredictor.setParameter("Set Feature", CURRENT_MOTION);
+                    sPredictor.setParameter("SetExpireTime", EXP_TIME_STRING);
+                    //sPredictor.setParameter("UseHistory", "true");
+                    sSetPredictor = true;
+                }
+                for (int i = 0 ; i < newLaunchedApps.size(); i ++ ) {
+                    sPredictor.pushSample(newLaunchedApps.get(i).pkgName);
+                }
+                sStatsOld = (PkgUsageStats[]) sStatsNew.clone();
+                //put activity logs in DropBox
+                putLogsInDropBox(this, newLaunchedApps);
+                // TODO Maybe Wait for sometime and then put the logs in DropBox
+            }
+            sAlarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + STAT_SAMPLING_TIME,
+                              sGetStatService);
+        }
+
+        private void putLogsInDropBox(Context con, ArrayList<appRecord> activityList) {
+            // write new activities in protobuffer
+            ActivityRecordProtoBuff activityRecordProto = new ActivityRecordProtoBuff();
+            String feaNum = CURRENT_TIME;
+            String currTime = sAggregatorManager.GetData(feaNum).get(feaNum);
+            feaNum = CURRENT_LOCATION;
+            String currLocation = sAggregatorManager.GetData(feaNum).get(feaNum);
+            feaNum = CURRENT_MOTION;
+            String currMotion = sAggregatorManager.GetData(feaNum).get(feaNum);
+            for (int i = 0 ; i < activityList.size(); i ++ ) {
+                ActivityRecordProtoBuff.activityInfo appInfo =
+                        new ActivityRecordProtoBuff.activityInfo();
+                appInfo.setPkgName(activityList.get(i).pkgName);
+                appInfo.setTime(currTime);
+                appInfo.setLocation(currLocation);
+                appInfo.setMotion(currMotion);
+                activityRecordProto.addActivityLog(appInfo);
+            }
+            // SEND TO DropBox
+            DropBoxManager db = (DropBoxManager) con.getSystemService(Context.DROPBOX_SERVICE);
+            try {
+                File file = File.createTempFile(PROTO_FILE_NAME, "proto", con.getFilesDir());
+                FileOutputStream fos = new FileOutputStream(file);
+                CodedOutputStreamMicro cos = CodedOutputStreamMicro.newInstance(fos);
+                activityRecordProto.writeTo(cos);
+                cos.flush();
+                fos.close();
+                db.addFile(PROTO_FILE_NAME, file, 0);
+            } catch (IOException e) {
+                Log.e(TAG, "Couldn't write log file.", e);
+            }
+        }
+
+        @Override
+        public IBinder onBind(Intent intent) {
+            return null;
+        }
+
+        private ArrayList<appRecord> extractLaunchedApps(PkgUsageStats[] statsNew,
+                                                         PkgUsageStats[] statsOld) {
+            ArrayList<appRecord> tmpRecord = new ArrayList<appRecord>();
+            for (int i = 0; i < statsNew.length; i++) {
+                if ( statsNew[i].launchCount > 0 ) {
+                    boolean found = false;
+                    String tmpName = statsNew[i].packageName;
+                    if (sPackageManager.getLaunchIntentForPackage(tmpName) == null )
+                        continue;
+                    if (statsOld != null) {
+                        for (int j =0; j < statsOld.length; j++) {
+                            if  (tmpName.equals(statsOld[j].packageName)) {
+                                if (statsNew[i].launchCount > statsOld[j].launchCount) {
+                                    appRecord r = new appRecord();
+                                    r.pkgName = tmpName;
+                                    r.launchNum = statsNew[i].launchCount - statsOld[j].launchCount;
+                                    r.duration = statsNew[i].usageTime - statsOld[j].usageTime;
+                                    tmpRecord.add(r);
+                                }
+                                found = true;
+                                break;
+                            }
+                        }
+                    }
+                    if (!found){
+                        appRecord r = new appRecord();
+                        r.pkgName = tmpName;
+                        r.launchNum = statsNew[i].launchCount;
+                        r.duration = statsNew[i].usageTime;
+                        tmpRecord.add(r);
+                    }
+                }
+            }
+            return tmpRecord;
+        }
+    }
+}
diff --git a/bordeaux/apps/AppLauncherWidget/src/android/bordeaux/applauncherwidget/proto/activity_record.proto b/bordeaux/apps/AppLauncherWidget/src/android/bordeaux/applauncherwidget/proto/activity_record.proto
new file mode 100644
index 0000000..fbd759f
--- /dev/null
+++ b/bordeaux/apps/AppLauncherWidget/src/android/bordeaux/applauncherwidget/proto/activity_record.proto
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+//option optimize_for = LITE_RUNTIME;
+//option java_package = "android.bordeaux.protobufdemo.proto";
+option java_outer_classname = "ActivityRecordProto";
+
+package android.bordeaux.applauncherwidget.proto;
+
+message ActivityRecordProtoBuff {
+    message activityInfo {
+        required string pkg_name = 1;
+        required string location = 2;
+        required string time = 3;
+        required string motion = 4;
+    };
+    repeated activityInfo activity_log = 1;
+}
diff --git a/bordeaux/learning/Android.mk b/bordeaux/learning/Android.mk
index 86e4209..f61259a 100644
--- a/bordeaux/learning/Android.mk
+++ b/bordeaux/learning/Android.mk
@@ -43,7 +43,8 @@
 
 LOCAL_MODULE_TAGS := samples tests
 
-LOCAL_SRC_FILES :=$(call all-java-files-under, multiclass_pa stochastic_linear_ranker)
+LOCAL_SRC_FILES := $(call all-java-files-under, multiclass_pa stochastic_linear_ranker ) \
+                   $(call all-java-files-under, predictor_histogram)
 
 LOCAL_MODULE :=  bordeaux_learners
 
diff --git a/bordeaux/learning/predictor_histogram/Android.mk b/bordeaux/learning/predictor_histogram/Android.mk
new file mode 100644
index 0000000..1c352f1
--- /dev/null
+++ b/bordeaux/learning/predictor_histogram/Android.mk
@@ -0,0 +1,16 @@
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
diff --git a/bordeaux/learning/predictor_histogram/java/android/bordeaux/learning/predictorHist.java b/bordeaux/learning/predictor_histogram/java/android/bordeaux/learning/predictorHist.java
new file mode 100644
index 0000000..7924424
--- /dev/null
+++ b/bordeaux/learning/predictor_histogram/java/android/bordeaux/learning/predictorHist.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.bordeaux.learning;
+
+import java.util.HashMap;
+import java.util.Map;
+import android.util.Log;
+
+/**
+ * A simple impelentation of histograms with sparse enteries using HashMap.
+ * User can push examples or extract probabilites from this histogram.
+ */
+public class predictorHist {
+    private HashMap<String, Integer> mCountHist;
+    private int mSampleCount;
+    String TAG = "PredicrtHist";
+
+    public predictorHist() {
+        mCountHist = new HashMap<String, Integer>();
+        mSampleCount = 0;
+    }
+
+    // reset histogram
+    public void ResetPredictorHist() {
+        mCountHist.clear();
+        mSampleCount = 0;
+    }
+
+    // getters
+    public final HashMap<String, Integer> getHist() {
+        return mCountHist;
+    }
+
+    public int getHistCounts() {
+        return mSampleCount;
+    }
+
+    //setter
+    public void set(HashMap<String, Integer> hist) {
+        ResetPredictorHist();
+        for (Map.Entry<String, Integer> x : hist.entrySet()) {
+            mCountHist.put(x.getKey(), x.getValue());
+            mSampleCount = mSampleCount + x.getValue();
+        }
+    }
+
+    /**
+     * pushes a new example to the histogram
+     */
+    public void pushSample( String fs) {
+        int histValue = 1;
+        if (mCountHist.get(fs) != null )
+            histValue = histValue + mCountHist.get(fs);
+        mCountHist.put(fs,histValue);
+        mSampleCount++;
+    }
+
+    /**
+     * return probabilty of an exmple using the histogram
+     */
+    public float getProbability(String fs) {
+        float res = 0;
+        if (mCountHist.get(fs) != null )
+            res = ((float) mCountHist.get(fs)) / ((float)mSampleCount);
+        return res;
+    }
+}
diff --git a/bordeaux/service/Android.mk b/bordeaux/service/Android.mk
index c49a6d6..b6509ef 100644
--- a/bordeaux/service/Android.mk
+++ b/bordeaux/service/Android.mk
@@ -1,13 +1,15 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_MODULE_TAGS := samples tests
+LOCAL_MODULE_TAGS := optional
 
 # Only compile source java files in this apk.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SRC_FILES += \
         src/android/bordeaux/services/IBordeauxServiceCallback.aidl \
+        src/android/bordeaux/services/IAggregatorManager.aidl \
         src/android/bordeaux/services/ILearning_MulticlassPA.aidl \
+        src/android/bordeaux/services/IPredictor.aidl \
         src/android/bordeaux/services/ILearning_StochasticLinearRanker.aidl \
         src/android/bordeaux/services/IBordeauxService.aidl
 
@@ -15,7 +17,6 @@
 
 LOCAL_PACKAGE_NAME := bordeaux
 
-LOCAL_SDK_VERSION := current
 
 include $(BUILD_PACKAGE)
 
@@ -29,13 +30,25 @@
 LOCAL_SRC_FILES += \
         src/android/bordeaux/services/IntFloat.java \
         src/android/bordeaux/services/StringFloat.java \
+        src/android/bordeaux/services/StringString.java \
         src/android/bordeaux/services/BordeauxClassifier.java \
         src/android/bordeaux/services/BordeauxRanker.java \
+        src/android/bordeaux/services/BordeauxPredictor.java \
+        src/android/bordeaux/services/BordeauxAggregatorManager.java \
         src/android/bordeaux/services/BordeauxManagerService.java \
         src/android/bordeaux/services/IBordeauxLearner.java \
         src/android/bordeaux/services/Learning_StochasticLinearRanker.java \
         src/android/bordeaux/services/StochasticLinearRankerWithPrior.java \
+        src/android/bordeaux/services/AggregatorManager.java \
+        src/android/bordeaux/services/Predictor.java \
+        src/android/bordeaux/services/Aggregator.java \
+        src/android/bordeaux/services/FeatureAssembly.java \
+        src/android/bordeaux/services/LocationStatsAggregator.java \
+        src/android/bordeaux/services/TimeStatsAggregator.java \
+        src/android/bordeaux/services/MotionStatsAggregator.java \
         src/android/bordeaux/services/IBordeauxServiceCallback.aidl \
+        src/android/bordeaux/services/IAggregatorManager.aidl \
+        src/android/bordeaux/services/IPredictor.aidl \
         src/android/bordeaux/services/ILearning_MulticlassPA.aidl \
         src/android/bordeaux/services/ILearning_StochasticLinearRanker.aidl \
         src/android/bordeaux/services/IBordeauxService.aidl \
diff --git a/bordeaux/service/src/android/bordeaux/services/Aggregator.java b/bordeaux/service/src/android/bordeaux/services/Aggregator.java
new file mode 100644
index 0000000..a5aced0
--- /dev/null
+++ b/bordeaux/service/src/android/bordeaux/services/Aggregator.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bordeaux.services;
+
+import java.util.Map;
+
+abstract class Aggregator {
+    protected AggregatorManager mAggregatorManager;
+    public void setManager(AggregatorManager m) {
+        mAggregatorManager = m;
+    }
+    abstract public String[] getListOfFeatures();
+    abstract public Map<String,String> getFeatureValue(String featureName);
+}
diff --git a/bordeaux/service/src/android/bordeaux/services/AggregatorManager.java b/bordeaux/service/src/android/bordeaux/services/AggregatorManager.java
new file mode 100644
index 0000000..42ccf9f
--- /dev/null
+++ b/bordeaux/service/src/android/bordeaux/services/AggregatorManager.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bordeaux.services;
+
+
+import android.bordeaux.services.StringString;
+import android.content.Context;
+import android.util.Log;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ArrayList;
+
+class AggregatorManager extends IAggregatorManager.Stub  {
+    private final String TAG = "AggregatorMnager";
+    private static HashMap<String, Aggregator> sFeatureMap;
+    private static AggregatorManager mManager = null;
+
+    private AggregatorManager() {
+        sFeatureMap = new HashMap<String, Aggregator>();
+    }
+
+    public static AggregatorManager getInstance() {
+        if (mManager == null )
+            mManager = new AggregatorManager();
+        return mManager;
+    }
+
+    public String[] getListOfFeatures() {
+        String[] s = new String[sFeatureMap.size()];
+        int i = 0;
+        for (Map.Entry<String, Aggregator> x : sFeatureMap.entrySet()) {
+           s[i] = x.getKey();
+           i++;
+        }
+        return s;
+    }
+
+    public void registerAggregator(Aggregator agg, AggregatorManager m) {
+        agg.setManager(m);
+        String[] fl = agg.getListOfFeatures();
+        for ( int i  = 0; i< fl.length; i ++)
+            sFeatureMap.put(fl[i], agg);
+    }
+
+    public ArrayList<StringString> getData(String dataName) {
+        return getList(getDataMap(dataName));
+    }
+
+    public Map<String, String> getDataMap(String dataName) {
+        if (sFeatureMap.get(dataName) != null)
+            return sFeatureMap.get(dataName).getFeatureValue(dataName);
+        else
+            Log.e(TAG, "There is no feature called " + dataName);
+        return null;
+    }
+
+    private ArrayList<StringString> getList(final Map<String, String> sample) {
+        ArrayList<StringString> StringString_sample = new ArrayList<StringString>();
+        for (Map.Entry<String, String> x : sample.entrySet()) {
+           StringString v = new StringString();
+           v.key = x.getKey();
+           v.value = x.getValue();
+           StringString_sample.add(v);
+        }
+        return StringString_sample;
+    }
+}
diff --git a/bordeaux/service/src/android/bordeaux/services/BordeauxAggregatorManager.java b/bordeaux/service/src/android/bordeaux/services/BordeauxAggregatorManager.java
new file mode 100644
index 0000000..a26e9cd
--- /dev/null
+++ b/bordeaux/service/src/android/bordeaux/services/BordeauxAggregatorManager.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bordeaux.services;
+
+import android.bordeaux.services.IAggregatorManager;
+import android.bordeaux.services.StringString;
+import android.content.Context;
+import android.os.RemoteException;
+import android.util.Log;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+/** AggregatorManger for Learning framework.
+ */
+public class BordeauxAggregatorManager {
+    static final String TAG = "BordeauxAggregatorManager";
+    static final String AggregatorManager_NOTAVAILABLE = "AggregatorManager not Available";
+    private Context mContext;
+    private IAggregatorManager mAggregatorManager;
+
+    public boolean retrieveAggregatorManager() {
+        if (mAggregatorManager == null)
+            mAggregatorManager = BordeauxManagerService.getAggregatorManager(mContext);
+        if (mAggregatorManager == null) {
+            Log.e(TAG, AggregatorManager_NOTAVAILABLE);
+            return false;
+        }
+        return true;
+    }
+
+    public BordeauxAggregatorManager (Context context) {
+        mContext = context;
+        mAggregatorManager = BordeauxManagerService.getAggregatorManager(mContext);
+    }
+
+    public Map<String, String> GetData(final String dataName) {
+        if (!retrieveAggregatorManager())
+            throw new RuntimeException(AggregatorManager_NOTAVAILABLE);
+        try {
+            return getMap(mAggregatorManager.getData(dataName));
+        } catch (RemoteException e) {
+            Log.e(TAG,"Exception in Getting " + dataName);
+            throw new RuntimeException(AggregatorManager_NOTAVAILABLE);
+        }
+    }
+
+    private Map<String, String> getMap(final List<StringString> sample) {
+        HashMap<String, String> m = new HashMap<String, String>();
+        for (int i =0; i < sample.size(); i++)
+            m.put(sample.get(i).key, sample.get(i).value);
+        return (Map) m;
+    }
+}
diff --git a/bordeaux/service/src/android/bordeaux/services/BordeauxManagerService.java b/bordeaux/service/src/android/bordeaux/services/BordeauxManagerService.java
index e136ca6..65ffdda 100644
--- a/bordeaux/service/src/android/bordeaux/services/BordeauxManagerService.java
+++ b/bordeaux/service/src/android/bordeaux/services/BordeauxManagerService.java
@@ -44,6 +44,8 @@
     static private final String TAG = "BordeauxMangerService";
     static private IBordeauxService mService = null;
     static private ILearning_StochasticLinearRanker mRanker = null;
+    static private IAggregatorManager mAggregatorManager = null;
+    static private IPredictor mPredictor = null;
     static private ILearning_MulticlassPA mClassifier = null;
     static private boolean mStarted = false;
 
@@ -72,6 +74,33 @@
         return mService;
     }
 
+    static public synchronized IAggregatorManager getAggregatorManager(Context context) {
+        if (mService == null) {
+            bindServices(context);
+            return null;
+        }
+        try {
+            mAggregatorManager = IAggregatorManager.Stub.asInterface(
+                    mService.getAggregatorManager());
+        } catch (RemoteException e) {
+            mAggregatorManager = null;
+        }
+        return mAggregatorManager;
+    }
+
+    static public synchronized IPredictor getPredictor(Context context, String name) {
+        if (mService == null) {
+            bindServices(context);
+            return null;
+        }
+        try {
+            mPredictor = IPredictor.Stub.asInterface(mService.getPredictor(name));
+        } catch (RemoteException e) {
+            mPredictor = null;
+        }
+        return mPredictor;
+    }
+
     static public synchronized ILearning_StochasticLinearRanker
             getRanker(Context context, String name) {
         if (mService == null) {
diff --git a/bordeaux/service/src/android/bordeaux/services/BordeauxPredictor.java b/bordeaux/service/src/android/bordeaux/services/BordeauxPredictor.java
new file mode 100644
index 0000000..cd0e57e
--- /dev/null
+++ b/bordeaux/service/src/android/bordeaux/services/BordeauxPredictor.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bordeaux.services;
+
+import android.bordeaux.services.IPredictor;
+import android.content.Context;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
+
+/** Predictor for the Learning framework.
+ */
+public class BordeauxPredictor {
+    static final String TAG = "BordeauxPredictor";
+    static final String PREDICTOR_NOTAVAILABLE = "Predictor is not available.";
+    private Context mContext;
+    private String mName;
+    private IPredictor mPredictor;
+
+    public boolean retrievePredictor() {
+        if (mPredictor == null)
+            mPredictor = BordeauxManagerService.getPredictor(mContext, mName);
+        if (mPredictor == null) {
+            Log.e(TAG, PREDICTOR_NOTAVAILABLE);
+            return false;
+        }
+        return true;
+    }
+
+    public BordeauxPredictor(Context context) {
+        mContext = context;
+        mName = "defaultPredictor";
+        mPredictor = BordeauxManagerService.getPredictor(context, mName);
+    }
+
+    public BordeauxPredictor(Context context, String name) {
+        mContext = context;
+        mName = name;
+        mPredictor = BordeauxManagerService.getPredictor(context, mName);
+    }
+
+    public boolean reset() {
+        if (!retrievePredictor()){
+            Log.e(TAG, PREDICTOR_NOTAVAILABLE);
+            return false;
+        }
+        try {
+            mPredictor.ResetPredictor();
+            return true;
+        } catch (RemoteException e) {
+        }
+        return false;
+    }
+
+    public void pushSample(String s) {
+        if (!retrievePredictor())
+            throw new RuntimeException(PREDICTOR_NOTAVAILABLE);
+        try {
+            mPredictor.pushNewSample(s);
+        } catch (RemoteException e) {
+            Log.e(TAG,"Exception: pushing a new example");
+            throw new RuntimeException(PREDICTOR_NOTAVAILABLE);
+        }
+    }
+
+    public float getProbability(String s) {
+        if (!retrievePredictor())
+            throw new RuntimeException(PREDICTOR_NOTAVAILABLE);
+        try {
+            return mPredictor.getSampleProbability(s);
+        } catch (RemoteException e) {
+            Log.e(TAG,"Exception: getting sample probability");
+            throw new RuntimeException(PREDICTOR_NOTAVAILABLE);
+        }
+    }
+
+    public boolean setParameter(String key, String value) {
+        if (!retrievePredictor())
+            throw new RuntimeException(PREDICTOR_NOTAVAILABLE);
+        try {
+            return mPredictor.setPredictorParameter(key, value);
+        } catch (RemoteException e) {
+            Log.e(TAG,"Exception: setting predictor parameter");
+            throw new RuntimeException(PREDICTOR_NOTAVAILABLE);
+        }
+    }
+}
diff --git a/bordeaux/service/src/android/bordeaux/services/BordeauxRanker.java b/bordeaux/service/src/android/bordeaux/services/BordeauxRanker.java
index 1977ce1..1ae5fcb 100644
--- a/bordeaux/service/src/android/bordeaux/services/BordeauxRanker.java
+++ b/bordeaux/service/src/android/bordeaux/services/BordeauxRanker.java
@@ -132,7 +132,7 @@
         try {
             return mRanker.SetModelParameter(key, value);
         } catch (RemoteException e) {
-            Log.e(TAG,"Exception: scoring the sample with prior.");
+            Log.e(TAG,"Exception: Setting Parameter");
             throw new RuntimeException(RANKER_NOTAVAILABLE);
         }
     }
diff --git a/bordeaux/service/src/android/bordeaux/services/BordeauxService.java b/bordeaux/service/src/android/bordeaux/services/BordeauxService.java
index 84a6df0..7c85fac 100644
--- a/bordeaux/service/src/android/bordeaux/services/BordeauxService.java
+++ b/bordeaux/service/src/android/bordeaux/services/BordeauxService.java
@@ -64,12 +64,24 @@
     NotificationManager mNotificationManager;
 
     BordeauxSessionManager mSessionManager;
+    AggregatorManager mAggregatorManager;
+    TimeStatsAggregator mTimeStatsAggregator;
+    LocationStatsAggregator mLocationStatsAggregator;
+    MotionStatsAggregator mMotionStatsAggregator;
 
     @Override
     public void onCreate() {
         Log.i(TAG, "Bordeaux service created.");
         mNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
         mSessionManager = new BordeauxSessionManager(this);
+        mMotionStatsAggregator = new MotionStatsAggregator();
+        mLocationStatsAggregator = new LocationStatsAggregator();
+        mTimeStatsAggregator = new TimeStatsAggregator();
+        mAggregatorManager = AggregatorManager.getInstance();
+        mAggregatorManager.registerAggregator(mMotionStatsAggregator, mAggregatorManager);
+        mAggregatorManager.registerAggregator(mLocationStatsAggregator, mAggregatorManager);
+        mAggregatorManager.registerAggregator(mTimeStatsAggregator, mAggregatorManager);
+        //Log.i(TAG, "Bordeaux aggregators were registered");
 
         // Display a notification about us starting.
         // TODO: don't display the notification after the service is
@@ -131,6 +143,14 @@
             return getLearningSession(Learning_StochasticLinearRanker.class, name);
         }
 
+        public IBinder getPredictor(String name) {
+            return getLearningSession(Predictor.class, name);
+        }
+
+        public IBinder getAggregatorManager() {
+            return (IBinder) mAggregatorManager;
+        }
+
         public void registerCallback(IBordeauxServiceCallback cb) {
             if (cb != null) mCallbacks.register(cb);
         }
diff --git a/bordeaux/service/src/android/bordeaux/services/BordeauxSessionStorage.java b/bordeaux/service/src/android/bordeaux/services/BordeauxSessionStorage.java
index 89aa370..9979d84 100644
--- a/bordeaux/service/src/android/bordeaux/services/BordeauxSessionStorage.java
+++ b/bordeaux/service/src/android/bordeaux/services/BordeauxSessionStorage.java
@@ -116,13 +116,18 @@
         Cursor cursor = mDbSessions.query(true, SESSION_TABLE,
                 new String[]{COLUMN_KEY, COLUMN_CLASS, COLUMN_MODEL, COLUMN_TIME},
                 COLUMN_KEY + "=\"" + key + "\"", null, null, null, null, null);
-        if (cursor == null) return null;
-        if (cursor.getCount() == 0) return null;
+        if ((cursor == null) | (cursor.getCount() == 0)) {
+            cursor.close();
+            return null;
+        }
         if (cursor.getCount() > 1) {
+            cursor.close();
             throw new RuntimeException("Unexpected duplication in session table for key:" + key);
         }
         cursor.moveToFirst();
-        return getSessionFromCursor(cursor);
+        BordeauxSessionManager.Session s = getSessionFromCursor(cursor);
+        cursor.close();
+        return s;
     }
 
     void getAllSessions(ConcurrentHashMap<String, BordeauxSessionManager.Session> sessions) {
diff --git a/bordeaux/service/src/android/bordeaux/services/FeatureAssembly.java b/bordeaux/service/src/android/bordeaux/services/FeatureAssembly.java
new file mode 100644
index 0000000..8dae57c
--- /dev/null
+++ b/bordeaux/service/src/android/bordeaux/services/FeatureAssembly.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bordeaux.services;
+
+import android.os.IBinder;
+import android.util.Log;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.List;
+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;
+
+public class FeatureAssembly {
+    private static final String TAG = "FeatureAssembly";
+    private List<String> mPossibleFeatures;
+    private HashSet<String> mUseFeatures;
+    private AggregatorManager mAggregatorManager;
+
+    public FeatureAssembly() {
+        mAggregatorManager = AggregatorManager.getInstance();
+        mPossibleFeatures = Arrays.asList(mAggregatorManager.getListOfFeatures());
+        mUseFeatures = new HashSet<String>();
+    }
+
+    public boolean registerFeature(String s) {
+        boolean res = mPossibleFeatures.contains(s);
+        if (res){
+            if (!mUseFeatures.contains(s))
+                mUseFeatures.add(s);
+        }
+        return res;
+    }
+
+    public Set<String> getUsedFeatures() {
+        return (Set) mUseFeatures;
+    }
+
+    public String augmentFeatureInputString(String s) {
+        String fs = s;
+        Iterator itr = mUseFeatures.iterator();
+        while(itr.hasNext()) {
+            String f = (String) itr.next();
+            fs = fs + "+" + mAggregatorManager.getDataMap(f).get(f);
+        }
+        return fs;
+    }
+}
diff --git a/bordeaux/service/src/android/bordeaux/services/IAggregatorManager.aidl b/bordeaux/service/src/android/bordeaux/services/IAggregatorManager.aidl
new file mode 100644
index 0000000..65028be
--- /dev/null
+++ b/bordeaux/service/src/android/bordeaux/services/IAggregatorManager.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bordeaux.services;
+
+import android.bordeaux.services.StringString;
+
+interface IAggregatorManager {
+    List<StringString> getData(in String dataName);
+}
diff --git a/bordeaux/service/src/android/bordeaux/services/IBordeauxService.aidl b/bordeaux/service/src/android/bordeaux/services/IBordeauxService.aidl
index 059d62e..4aa4f08 100644
--- a/bordeaux/service/src/android/bordeaux/services/IBordeauxService.aidl
+++ b/bordeaux/service/src/android/bordeaux/services/IBordeauxService.aidl
@@ -31,6 +31,12 @@
     */
     IBinder getClassifier(String name);
 
+    /* Request to access AggregatorManager
+    */
+    IBinder getAggregatorManager();
+    /* Request a Predictor
+    */
+    IBinder getPredictor(String name);
     /**
      * Often you want to allow a service to call back to its clients.
      * This shows how to do so, by registering a callback interface with
diff --git a/bordeaux/service/src/android/bordeaux/services/IPredictor.aidl b/bordeaux/service/src/android/bordeaux/services/IPredictor.aidl
new file mode 100644
index 0000000..d2f6036
--- /dev/null
+++ b/bordeaux/service/src/android/bordeaux/services/IPredictor.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bordeaux.services;
+
+interface IPredictor {
+    boolean setPredictorParameter( in String s, in String f );
+    void pushNewSample(in String s);
+    void ResetPredictor();
+    float getSampleProbability(in String s);
+}
diff --git a/bordeaux/service/src/android/bordeaux/services/Learning_StochasticLinearRanker.java b/bordeaux/service/src/android/bordeaux/services/Learning_StochasticLinearRanker.java
index ab51f94..c648bd2 100644
--- a/bordeaux/service/src/android/bordeaux/services/Learning_StochasticLinearRanker.java
+++ b/bordeaux/service/src/android/bordeaux/services/Learning_StochasticLinearRanker.java
@@ -112,7 +112,6 @@
             objStream.writeObject(model);
             //return byteStream.toByteArray();
             byte[] bytes = byteStream.toByteArray();
-            Log.i(TAG, "getModel: " + bytes);
             return bytes;
         } catch (IOException e) {
             throw new RuntimeException("Can't get model");
@@ -128,7 +127,6 @@
             if (mLearningSlRanker == null)
                 mLearningSlRanker = new StochasticLinearRankerWithPrior();
             boolean res = mLearningSlRanker.loadModel(model);
-            Log.i(TAG, "LoadModel: " + modelData);
             return res;
         } catch (IOException e) {
             throw new RuntimeException("Can't load model");
diff --git a/bordeaux/service/src/android/bordeaux/services/LocationStatsAggregator.java b/bordeaux/service/src/android/bordeaux/services/LocationStatsAggregator.java
new file mode 100644
index 0000000..6294df8
--- /dev/null
+++ b/bordeaux/service/src/android/bordeaux/services/LocationStatsAggregator.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bordeaux.services;
+
+import android.util.Log;
+import java.util.HashMap;
+import java.util.Map;
+
+class LocationStatsAggregator extends Aggregator {
+    final String TAG = "LocationStatsAggregator";
+    public static final String CURRENT_LOCATION = "Current Location";
+    public String[] getListOfFeatures(){
+        String [] list = new String[1];
+        list[0] = CURRENT_LOCATION;
+        return list;
+    }
+    public Map<String,String> getFeatureValue(String featureName) {
+        HashMap<String,String> m = new HashMap<String,String>();
+        if (featureName.equals(CURRENT_LOCATION))
+            m.put(CURRENT_LOCATION, "Here");  //TODO put location resutls here
+        else
+            Log.e(TAG, "There is no Location feature called " + featureName);
+        return (Map) m;
+    }
+}
diff --git a/bordeaux/service/src/android/bordeaux/services/MotionStatsAggregator.java b/bordeaux/service/src/android/bordeaux/services/MotionStatsAggregator.java
new file mode 100644
index 0000000..c9344fd
--- /dev/null
+++ b/bordeaux/service/src/android/bordeaux/services/MotionStatsAggregator.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bordeaux.services;
+
+import android.util.Log;
+import java.util.HashMap;
+import java.util.Map;
+
+class MotionStatsAggregator extends Aggregator {
+    final String TAG = "MotionStatsAggregator";
+    public static final String CURRENT_MOTION = "Current Motion";
+    public String[] getListOfFeatures(){
+        String [] list = new String[1];
+        list[0] = CURRENT_MOTION;
+        return list;
+    }
+    public Map<String,String> getFeatureValue(String featureName) {
+        HashMap<String,String> m = new HashMap<String,String>();
+        if (featureName.equals(CURRENT_MOTION))
+            m.put(CURRENT_MOTION,"Running"); //TODO maybe use clustering for user motion
+        else
+            Log.e(TAG, "There is no motion feature called " + featureName);
+        return (Map) m;
+    }
+}
diff --git a/bordeaux/service/src/android/bordeaux/services/Predictor.java b/bordeaux/service/src/android/bordeaux/services/Predictor.java
new file mode 100644
index 0000000..8bfd82e
--- /dev/null
+++ b/bordeaux/service/src/android/bordeaux/services/Predictor.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bordeaux.services;
+
+import android.os.IBinder;
+import android.util.Log;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.io.Serializable;
+import java.io.*;
+import java.lang.Boolean;
+import android.bordeaux.services.FeatureAssembly;
+import android.bordeaux.learning.predictorHist;
+
+/**
+ * This is interface to implement Prediction based on histogram that
+ * uses predictor_histogram from learnerning section
+ */
+public class Predictor extends IPredictor.Stub
+        implements IBordeauxLearner {
+    private ModelChangeCallback modelChangeCallback = null;
+    private final String TAG = "Predictor";
+    private final String SET_EXPIRE_TIME = "SetExpireTime";
+    private final String USE_HISTORY = "Use History";
+    private final String SET_FEATURE = "Set Feature";
+    private long mExpireTime = 3 * 60;
+    private long mLastSampleTime = 0;
+    private boolean mUseHistoryFlag = false;
+    private final String NEW_START = "New Start";
+
+    static public class Model implements Serializable {
+        public HashMap<String, Integer> countHistogram = new HashMap<String, Integer>();
+        public HashSet<String> usedFeatures = new HashSet<String>();
+        public int sampleCounts;
+        public boolean useHistoryFlag;
+        public long expireTime;
+        public long lastSampleTime;
+    }
+
+    private predictorHist mPredictorHist = new predictorHist();
+    private String mLastSample = NEW_START;
+    public FeatureAssembly mFeatureAssembly = new FeatureAssembly();
+
+    /**
+     * Reset the Predictor
+     */
+    public void ResetPredictor(){
+        printModel(getPredictionModel());
+        mPredictorHist.ResetPredictorHist();
+        mUseHistoryFlag = false;
+        mLastSampleTime = 0;
+        mLastSample = NEW_START;
+        mFeatureAssembly = new FeatureAssembly();
+        printModel(getPredictionModel());
+        if (modelChangeCallback != null) {
+            modelChangeCallback.modelChanged(this);
+        }
+    }
+
+    /**
+     * Augment input string with buildin features such as time, location
+     */
+    private String buildDataPoint(String sampleName) {
+        String fs = mFeatureAssembly.augmentFeatureInputString(sampleName);
+        if (mUseHistoryFlag) {
+             if (((System.currentTimeMillis()- mLastSampleTime)/1000) > mExpireTime) {
+                 mLastSample  = NEW_START;
+             }
+             fs = fs + "+" + mLastSample;
+        }
+        return fs;
+    }
+
+    /**
+     * Input is a sampleName e.g.action name. This input is then augmented with requested build-in
+     * features such as time and location to create sampleFeatures. The sampleFeatures is then
+     * pushed to the histogram
+     */
+    public void pushNewSample(String sampleName) {
+        String sampleFeatures = buildDataPoint(sampleName);
+        mLastSample = sampleName;
+        mLastSampleTime = System.currentTimeMillis();
+        mPredictorHist.pushSample(sampleFeatures);
+        if (modelChangeCallback != null) {
+            modelChangeCallback.modelChanged(this);
+        }
+        //printModel(getPredictionModel());
+    }
+
+    /**
+     * return probabilty of an exmple using the histogram
+     */
+    public float getSampleProbability(String sampleName) {
+        String sampleFeatures = buildDataPoint(sampleName);
+        return mPredictorHist.getProbability(sampleFeatures);
+    }
+
+    /**
+     * Set parameters for 1) using History in probability estimations e.g. consider the last event
+     * and 2) featureAssembly e.g. time and location.
+     */
+    public boolean setPredictorParameter(String s, String f) {
+        boolean res = false;
+        if (s.equals(USE_HISTORY)) {
+            if (f.equals("true")){
+                mUseHistoryFlag = true;
+                res = true;
+            }
+            else if (f.equals("false")) {
+                mUseHistoryFlag = false;
+                res = true;
+            }
+        } else if (s.equals(SET_EXPIRE_TIME)) {
+            mExpireTime = Long.valueOf(f);
+            res = true;
+        } else if (s.equals(SET_FEATURE)) {
+            res = mFeatureAssembly.registerFeature(f);
+        }
+        if (!res)
+            Log.e(TAG,"Setting parameter " + s + " with " + f + " is not valid");
+        return res;
+    }
+
+    public Model getPredictionModel() {
+        Model m = new Model();
+        m.countHistogram.putAll(mPredictorHist.getHist());
+        m.sampleCounts = mPredictorHist.getHistCounts();
+        m.expireTime = mExpireTime;
+        m.usedFeatures = (HashSet) mFeatureAssembly.getUsedFeatures();
+        m.useHistoryFlag = mUseHistoryFlag;
+        m.lastSampleTime = mLastSampleTime;
+        return m;
+    }
+
+    public boolean loadModel(Model m) {
+        //Log.i(TAG,"on loadModel");
+        //printModel(m);
+        mPredictorHist = new predictorHist();
+        mPredictorHist.set(m.countHistogram);
+        mExpireTime = m.expireTime;
+        mUseHistoryFlag = m.useHistoryFlag;
+        mLastSampleTime = m.lastSampleTime;
+        mFeatureAssembly = new FeatureAssembly();
+        boolean res = false;
+        Iterator itr = m.usedFeatures.iterator();
+        while(itr.hasNext()) {
+            res = res & mFeatureAssembly.registerFeature((String) itr.next());
+        }
+        return res;
+    }
+
+    public void printModel(Model m) {
+        Log.i(TAG,"histogram is : " + m.countHistogram.toString());
+        Log.i(TAG,"number of counts in histogram is : " + m.sampleCounts);
+        Log.i(TAG,"ExpireTime time is : " + m.expireTime);
+        Log.i(TAG,"useHistoryFlag is : " + m.useHistoryFlag);
+        Log.i(TAG,"used features are : " + m.usedFeatures.toString());
+    }
+
+    // Beginning of the IBordeauxLearner Interface implementation
+    public byte [] getModel() {
+        Model model = getPredictionModel();
+        //Log.i(TAG,"on getModel");
+        printModel(model);
+        try {
+            ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+            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");
+        }
+    }
+
+    public boolean setModel(final byte [] modelData) {
+        //Log.i(TAG,"on setModel");
+        try {
+            ByteArrayInputStream input = new ByteArrayInputStream(modelData);
+            ObjectInputStream objStream = new ObjectInputStream(input);
+            Model model = (Model) objStream.readObject();
+            boolean res = loadModel(model);
+            //Log.i(TAG, "LoadModel: " + modelData);
+            return res;
+        } catch (IOException e) {
+            throw new RuntimeException("Can't load model");
+        } catch (ClassNotFoundException e) {
+            throw new RuntimeException("Learning class not found");
+        }
+    }
+
+    public IBinder getBinder() {
+        return this;
+    }
+
+    public void setModelChangeCallback(ModelChangeCallback callback) {
+        modelChangeCallback = callback;
+    }
+    // End of IBordeauxLearner Interface implemenation
+}
diff --git a/bordeaux/service/src/android/bordeaux/services/StringString.aidl b/bordeaux/service/src/android/bordeaux/services/StringString.aidl
new file mode 100644
index 0000000..3cb89b9
--- /dev/null
+++ b/bordeaux/service/src/android/bordeaux/services/StringString.aidl
@@ -0,0 +1,3 @@
+package android.bordeaux.services;
+
+parcelable StringString;
diff --git a/bordeaux/service/src/android/bordeaux/services/StringString.java b/bordeaux/service/src/android/bordeaux/services/StringString.java
new file mode 100644
index 0000000..109462e
--- /dev/null
+++ b/bordeaux/service/src/android/bordeaux/services/StringString.java
@@ -0,0 +1,41 @@
+package android.bordeaux.services;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public final class StringString implements Parcelable {
+    public String key;
+    public String value;
+
+    public static final Parcelable.Creator<StringString>
+                        CREATOR = new Parcelable.Creator<StringString>() {
+        public StringString createFromParcel(Parcel in) {
+            return new StringString(in);
+        }
+
+        public StringString[] newArray(int size) {
+            return new StringString[size];
+        }
+    };
+
+    public StringString() {
+    }
+
+    private StringString(Parcel in) {
+        readFromParcel(in);
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(key);
+        out.writeString(value);
+    }
+
+    public void readFromParcel(Parcel in) {
+        key  = in.readString();
+        value = in.readString();
+    }
+}
diff --git a/bordeaux/service/src/android/bordeaux/services/TimeStatsAggregator.java b/bordeaux/service/src/android/bordeaux/services/TimeStatsAggregator.java
new file mode 100644
index 0000000..8626189
--- /dev/null
+++ b/bordeaux/service/src/android/bordeaux/services/TimeStatsAggregator.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bordeaux.services;
+
+import java.util.Date;
+import android.util.Log;
+import java.util.HashMap;
+import java.util.Map;
+
+class TimeStatsAggregator extends Aggregator {
+    final String TAG = "TimeStatsAggregator";
+    public static final String CURRENT_TIME = "Current Time";
+    final String EARLY_MORNING = "EarlyMorning";
+    final String MORNING = "Morning";
+    final String NOON = "Noon";
+    final String AFTERNOON = "AfterNoon";
+    final String NIGHT = "Night";
+    final String LATE_NIGHT = "LateNight";
+
+    public String[] getListOfFeatures(){
+        String [] list = new String[1];
+        list[0] = CURRENT_TIME;
+        return list;
+    }
+
+    public Map<String,String> getFeatureValue(String featureName) {
+        HashMap<String,String> m = new HashMap<String,String>();
+        if (featureName.equals(CURRENT_TIME))
+            m.put(CURRENT_TIME, getCurrentTimeLabel());
+        else
+            Log.e(TAG, "There is no Time feature called " + featureName);
+        return (Map) m;
+    }
+
+    private String getCurrentTimeLabel(){
+        Date  d = new Date(System.currentTimeMillis());
+        String t = "";  //TODO maybe learn thresholds
+        int h = d.getHours();
+        if ((h > 5) & (h <= 7) )
+            t = EARLY_MORNING;
+        else if ((h > 7) & (h <= 11) )
+            t = MORNING;
+        else if ((h > 11) & (h <= 15))
+            t = NOON;
+        else if ((h > 15) & (h <= 20))
+            t = AFTERNOON;
+        else if ((h > 20) & (h <= 24))
+            t = NIGHT;
+        else if ((h > 0) & (h <= 5))
+            t = LATE_NIGHT;
+        return t;
+    }
+}