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