/** * * Funf: Open Sensing Framework * Copyright (C) 2010-2011 Nadav Aharony, Wei Pan, Alex Pentland. * Acknowledgments: Alan Gardner * Contact: nadav@media.mit.edu * * This file is part of Funf. * * Funf is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * Funf is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with Funf. If not, see <http://www.gnu.org/licenses/>. * */ package edu.mit.media.funf.storage; import java.io.File; import java.util.HashMap; import java.util.Map; import android.app.IntentService; import android.content.Intent; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.os.Binder; import android.os.IBinder; import android.util.Log; import edu.mit.media.funf.util.LogUtil; /** * Simple database service that is able to write timestamp, name, value tuples. * * There is a single separate thread responsible for all writes and maintenance, * so there is no need to deal with synchronization. * * This class can be subclassed to provide custom implementations of Archive and * SQliteHelper.Subclasses have no access to data queue. * @author alangardner * */ public abstract class DatabaseService extends IntentService { public static final String ACTION_RECORD = "edu.mit.media.funf.RECORD", ACTION_ARCHIVE = "edu.mit.media.funf.ARCHIVE"; public static final String DATABASE_NAME_KEY = "DATABASE_NAME"; private Map<String, SQLiteOpenHelper> databaseHelpers; // Cache open database helpers public DatabaseService() { super("FunfDatabaseService"); // Name only used for debugging } @Override public void onDestroy() { super.onDestroy(); if (databaseHelpers != null) { for (SQLiteOpenHelper dbHelper : databaseHelpers.values()) { dbHelper.close(); } } Log.i(LogUtil.TAG, "Destroyed"); } @Override public void onHandleIntent(Intent intent) { Log.i(LogUtil.TAG, "Started"); final String databaseName = intent.getStringExtra(DATABASE_NAME_KEY); if (databaseName == null) { Log.e(LogUtil.TAG, "Database name not specified."); return; } String action = intent.getAction(); if (action == null || action.equals(ACTION_RECORD)) { writeToDatabase(databaseName, intent); } else if (action.equals(ACTION_ARCHIVE)) { runArchive(databaseName); } } /** * Returns the file archive for the database. * @param databaseName * @return */ public FileArchive getArchive(String databaseName) { return DefaultArchive.getArchive(this, databaseName); } /** * The NameValueDatabaseHelper * @param name * @return */ protected abstract SQLiteOpenHelper getDatabaseHelper(String name); /** * Pulls data from the intent to update the database. This method is called from within a transaction. * Throw a SQLException to signify a failure and rollback the transaction. * @param db * @param intent */ protected abstract void updateDatabase(SQLiteDatabase db, Intent intent) throws SQLException; private void runArchive(String databaseName) { SQLiteOpenHelper dbHelper = getOrCreateDatabaseHelper(databaseName); File dbFile = new File(dbHelper.getReadableDatabase().getPath()); Log.i(LogUtil.TAG, "Running archive: " + dbFile.getAbsolutePath()); dbHelper.close(); FileArchive archive = getArchive(databaseName); if (archive.add(dbFile)) { dbFile.delete(); } } private SQLiteOpenHelper getOrCreateDatabaseHelper(String databaseName) { if (databaseHelpers == null) { databaseHelpers = new HashMap<String, SQLiteOpenHelper>(); } SQLiteOpenHelper dbHelper = databaseHelpers.get(databaseName); if (dbHelper == null) { Log.i(LogUtil.TAG, "DataBaseService: Creating database '" + databaseName + "'"); dbHelper = getDatabaseHelper(databaseName); databaseHelpers.put(databaseName, dbHelper); } return dbHelper; } /** * Write data to the database in a transaction * NOTE: Should only be called by one thread at a time (the writeThread) * @param datum */ private void writeToDatabase(String databaseName, Intent intent) { Log.i(LogUtil.TAG, "Writing to database"); SQLiteOpenHelper dbHelper = getOrCreateDatabaseHelper(databaseName); SQLiteDatabase db = dbHelper.getWritableDatabase(); try { db.beginTransaction(); updateDatabase(db, intent); db.setTransactionSuccessful(); Log.i(LogUtil.TAG, "Writing successful"); } catch (Exception e) { Log.e(LogUtil.TAG, "DataBaseService: save error",e); } finally { db.endTransaction(); } db.close(); } /** * Binder interface to the probe */ public class LocalBinder extends Binder { public DatabaseService getService() { return DatabaseService.this; } } private final IBinder mBinder = new LocalBinder(); @Override public IBinder onBind(Intent intent) { return mBinder; } }