11dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua/* 21dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua * Copyright (C) 2012 The Android Open Source Project 31dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua * 41dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua * Licensed under the Apache License, Version 2.0 (the "License"); 51dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua * you may not use this file except in compliance with the License. 61dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua * You may obtain a copy of the License at 71dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua * 81dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua * http://www.apache.org/licenses/LICENSE-2.0 91dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua * 101dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua * Unless required by applicable law or agreed to in writing, software 111dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua * distributed under the License is distributed on an "AS IS" BASIS, 121dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua * See the License for the specific language governing permissions and 141dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua * limitations under the License. 151dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua */ 161dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua 171dd8ef56681617db46caec7776c9bf416f01d8ddWei Huapackage android.bordeaux.services; 181dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua 191dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaimport android.bordeaux.services.IBordeauxLearner.ModelChangeCallback; 201dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaimport android.content.Context; 211dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaimport android.os.IBinder; 221dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaimport android.util.Log; 231dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua 241dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaimport java.lang.NoSuchMethodException; 251dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaimport java.lang.InstantiationException; 261dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaimport java.util.concurrent.ConcurrentHashMap; 271dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaimport java.util.HashMap; 281dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaimport java.util.ArrayList; 291dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaimport java.util.List; 301dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaimport java.util.Map; 311dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaimport java.util.Set; 321dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua 331dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua// This class manages the learning sessions from multiple applications. 341dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua// The learning sessions are automatically backed up to the storage. 351dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua// 361dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaclass BordeauxSessionManager { 371dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua 381dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua static private final String TAG = "BordeauxSessionManager"; 391dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua private BordeauxSessionStorage mSessionStorage; 401dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua 411dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua static class Session { 421dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua Class learnerClass; 431dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua IBordeauxLearner learner; 441dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua boolean modified = false; 451dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua }; 461dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua 471dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua static class SessionKey { 481dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua String value; 491dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua }; 501dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua 511dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua // Thread to periodically save the sessions to storage 521dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua class PeriodicSave extends Thread implements Runnable { 531dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua long mSavingInterval = 60000; // 60 seconds 541dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua boolean mQuit = false; 551dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua PeriodicSave() {} 561dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua public void run() { 571dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua while (!mQuit) { 581dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua try { 591dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua sleep(mSavingInterval); 601dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } catch (InterruptedException e) { 611dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua // thread waked up. 621dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua // ignore 631dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 641dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua saveSessions(); 651dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 661dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 671dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 681dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua 691dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua PeriodicSave mSavingThread = new PeriodicSave(); 701dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua 711dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua private ConcurrentHashMap<String, Session> mSessions = 721dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua new ConcurrentHashMap<String, Session>(); 731dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua 741dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua public BordeauxSessionManager(final Context context) { 751dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua mSessionStorage = new BordeauxSessionStorage(context); 761dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua mSavingThread.start(); 771dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 781dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua 791dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua class LearningUpdateCallback implements ModelChangeCallback { 801dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua private String mKey; 811dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua 821dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua public LearningUpdateCallback(String key) { 831dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua mKey = key; 841dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 851dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua 861dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua public void modelChanged(IBordeauxLearner learner) { 871dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua // Save the session 881dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua Session session = mSessions.get(mKey); 891dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua if (session != null) { 901dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua synchronized(session) { 911dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua if (session.learner != learner) { 921dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua throw new RuntimeException("Session data corrupted!"); 931dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 941dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua session.modified = true; 951dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 961dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 971dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 981dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 991dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua 1001dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua // internal unique key that identifies the learning instance. 1011dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua // Composed by the package id of the calling process, learning class name 1021dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua // and user specified name. 1031dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua public SessionKey getSessionKey(String callingUid, Class learnerClass, String name) { 1041dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua SessionKey key = new SessionKey(); 1051dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua key.value = callingUid + "#" + "_" + name + "_" + learnerClass.getName(); 1061dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua return key; 1071dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 1081dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua 1091dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua public IBinder getSessionBinder(Class learnerClass, SessionKey key) { 1101dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua if (mSessions.containsKey(key.value)) { 1111dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua return mSessions.get(key.value).learner.getBinder(); 1121dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 1131dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua // not in memory cache 1141dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua try { 1151dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua // try to find it in the database 1161dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua Session stored = mSessionStorage.getSession(key.value); 1171dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua if (stored != null) { 1181dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua // set the callback, so that we can save the state 1191dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua stored.learner.setModelChangeCallback(new LearningUpdateCallback(key.value)); 1201dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua // found session in the storage, put in the cache 1211dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua mSessions.put(key.value, stored); 1221dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua return stored.learner.getBinder(); 1231dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 1241dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua 1251dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua // if session is not already stored, create a new one. 1261dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua Log.i(TAG, "create a new learning session: " + key.value); 1271dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua IBordeauxLearner learner = 1281dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua (IBordeauxLearner) learnerClass.getConstructor().newInstance(); 1291dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua // set the callback, so that we can save the state 1301dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua learner.setModelChangeCallback(new LearningUpdateCallback(key.value)); 1311dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua Session session = new Session(); 1321dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua session.learnerClass = learnerClass; 1331dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua session.learner = learner; 1341dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua mSessions.put(key.value, session); 1351dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua return learner.getBinder(); 1361dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } catch (Exception e) { 1371dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua throw new RuntimeException("Can't instantiate class: " + 1381dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua learnerClass.getName()); 1391dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 1401dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 1411dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua 1421dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua public void saveSessions() { 1431dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua for (Map.Entry<String, Session> session : mSessions.entrySet()) { 1441dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua synchronized(session) { 1451dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua // Save the session if it's modified. 1461dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua if (session.getValue().modified) { 1471dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua SessionKey skey = new SessionKey(); 1481dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua skey.value = session.getKey(); 1491dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua saveSession(skey); 1501dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 1511dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 1521dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 1531dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 1541dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua 1551dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua public boolean saveSession(SessionKey key) { 1561dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua Session session = mSessions.get(key.value); 1571dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua if (session != null) { 1581dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua synchronized(session) { 1591dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua byte[] model = session.learner.getModel(); 1601dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua 1611dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua // write to database 1621dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua boolean res = mSessionStorage.saveSession(key.value, session.learnerClass, model); 1631dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua if (res) 1641dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua session.modified = false; 1651dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua else { 1661dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua Log.e(TAG, "Can't save session: " + key.value); 1671dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 1681dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua return res; 1691dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 1701dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 1711dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua Log.e(TAG, "Session not found: " + key.value); 1721dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua return false; 1731dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 1741dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua 1751dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua // Load all session data into memory. 1761dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua // The session data will be loaded into the memory from the database, even 1771dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua // if this method is not called. 1781dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua public void loadSessions() { 1791dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua synchronized(mSessions) { 1801dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua mSessionStorage.getAllSessions(mSessions); 1811dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua for (Map.Entry<String, Session> session : mSessions.entrySet()) { 1821dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua // set the callback, so that we can save the state 1831dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua session.getValue().learner.setModelChangeCallback( 1841dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua new LearningUpdateCallback(session.getKey())); 1851dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 1861dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 1871dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 1881dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua 1891dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua public void removeAllSessionsFromCaller(String callingUid) { 1901dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua // remove in the hash table 1911dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua ArrayList<String> remove_keys = new ArrayList<String>(); 1921dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua for (Map.Entry<String, Session> session : mSessions.entrySet()) { 1931dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua if (session.getKey().startsWith(callingUid + "#")) { 1941dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua remove_keys.add(session.getKey()); 1951dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 1961dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 1971dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua for (String key : remove_keys) { 1981dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua mSessions.remove(key); 1991dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 2001dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua // remove all session data from the callingUid in database 2011dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua // % is used as wild match for the rest of the string in sql 2021dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua int nDeleted = mSessionStorage.removeSessions(callingUid + "#%"); 2031dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua if (nDeleted > 0) 2041dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua Log.i(TAG, "Successfully deleted " + nDeleted + "sessions"); 2051dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua } 2061dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua} 207