DatabaseHelper.java revision 110f569b47bc21fb38ec25b6110ee302ce137e06
1/**
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.voiceinteraction;
18
19import android.content.ContentValues;
20import android.content.Context;
21import android.database.Cursor;
22import android.database.sqlite.SQLiteDatabase;
23import android.database.sqlite.SQLiteOpenHelper;
24import android.hardware.soundtrigger.SoundTrigger;
25import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
26import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
27import android.os.UserManager;
28import android.text.TextUtils;
29import android.util.Slog;
30
31import java.util.ArrayList;
32import java.util.List;
33import java.util.UUID;
34
35/**
36 * Helper to manage the database of the sound models that have been registered on the device.
37 *
38 * @hide
39 */
40public class DatabaseHelper extends SQLiteOpenHelper {
41    static final String TAG = "SoundModelDBHelper";
42    static final boolean DBG = false;
43
44    private static final String NAME = "sound_model.db";
45    private static final int VERSION = 2;
46
47    public static interface KeyphraseContract {
48        public static final String TABLE = "keyphrase";
49        public static final String KEY_ID = "_id";
50        public static final String KEY_RECOGNITION_MODES = "modes";
51        public static final String KEY_LOCALE = "locale";
52        public static final String KEY_HINT_TEXT = "hint_text";
53        public static final String KEY_USERS = "users";
54        public static final String KEY_SOUND_MODEL_ID = "sound_model_id";
55    }
56
57    public static interface SoundModelContract {
58        public static final String TABLE = "sound_model";
59        public static final String KEY_ID = "_id";
60        public static final String KEY_TYPE = "type";
61        public static final String KEY_DATA = "data";
62    }
63
64    // Table Create Statements
65    private static final String CREATE_TABLE_KEYPRHASES = "CREATE TABLE "
66            + KeyphraseContract.TABLE + "("
67            + KeyphraseContract.KEY_ID + " INTEGER PRIMARY KEY,"
68            + KeyphraseContract.KEY_RECOGNITION_MODES + " INTEGER,"
69            + KeyphraseContract.KEY_USERS + " TEXT,"
70            + KeyphraseContract.KEY_SOUND_MODEL_ID + " TEXT,"
71            + KeyphraseContract.KEY_LOCALE + " TEXT,"
72            + KeyphraseContract.KEY_HINT_TEXT + " TEXT" + ")";
73
74    private static final String CREATE_TABLE_SOUND_MODEL = "CREATE TABLE "
75            + SoundModelContract.TABLE + "("
76            + SoundModelContract.KEY_ID + " TEXT PRIMARY KEY,"
77            + SoundModelContract.KEY_TYPE + " INTEGER,"
78            + SoundModelContract.KEY_DATA + " BLOB" + ")";
79
80    private final UserManager mUserManager;
81
82    public DatabaseHelper(Context context) {
83        super(context, NAME, null, VERSION);
84        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
85    }
86
87    @Override
88    public void onCreate(SQLiteDatabase db) {
89        // creating required tables
90        db.execSQL(CREATE_TABLE_KEYPRHASES);
91        db.execSQL(CREATE_TABLE_SOUND_MODEL);
92    }
93
94    @Override
95    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
96        // TODO: For now, drop older tables and recreate new ones.
97        db.execSQL("DROP TABLE IF EXISTS " + KeyphraseContract.TABLE);
98        db.execSQL("DROP TABLE IF EXISTS " + SoundModelContract.TABLE);
99        onCreate(db);
100    }
101
102    public boolean addOrUpdateKeyphraseSoundModel(KeyphraseSoundModel soundModel) {
103        SQLiteDatabase db = getWritableDatabase();
104        ContentValues values = new ContentValues();
105        // Generate a random ID for the model.
106        values.put(SoundModelContract.KEY_ID, soundModel.uuid.toString());
107        values.put(SoundModelContract.KEY_DATA, soundModel.data);
108        values.put(SoundModelContract.KEY_TYPE, SoundTrigger.SoundModel.TYPE_KEYPHRASE);
109
110        boolean status = true;
111        if (db.insertWithOnConflict(
112                SoundModelContract.TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE) != -1) {
113            for (Keyphrase keyphrase : soundModel.keyphrases) {
114                status &= addOrUpdateKeyphrase(db, soundModel.uuid, keyphrase);
115            }
116            db.close();
117            return status;
118        } else {
119            Slog.w(TAG, "Failed to persist sound model to database");
120            db.close();
121            return false;
122        }
123    }
124
125    private boolean addOrUpdateKeyphrase(SQLiteDatabase db, UUID modelId, Keyphrase keyphrase) {
126        ContentValues values = new ContentValues();
127        values.put(KeyphraseContract.KEY_ID, keyphrase.id);
128        values.put(KeyphraseContract.KEY_RECOGNITION_MODES, keyphrase.recognitionModes);
129        values.put(KeyphraseContract.KEY_SOUND_MODEL_ID, modelId.toString());
130        values.put(KeyphraseContract.KEY_HINT_TEXT, keyphrase.text);
131        values.put(KeyphraseContract.KEY_LOCALE, keyphrase.locale);
132        values.put(KeyphraseContract.KEY_USERS, getCommaSeparatedString(keyphrase.users));
133        if (db.insertWithOnConflict(
134                KeyphraseContract.TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE) != -1) {
135            return true;
136        } else {
137            Slog.w(TAG, "Failed to persist keyphrase to database");
138            return false;
139        }
140    }
141
142    /**
143     * Deletes the sound model and associated keyphrases.
144     */
145    public boolean deleteKeyphraseSoundModel(UUID uuid) {
146        SQLiteDatabase db = getWritableDatabase();
147        String modelId = uuid.toString();
148        String soundModelClause = SoundModelContract.KEY_ID + "=" + modelId;
149        boolean status = true;
150        if (db.delete(SoundModelContract.TABLE, soundModelClause, null) == 0) {
151            Slog.w(TAG, "No sound models deleted from the database");
152            status = false;
153        }
154        String keyphraseClause = KeyphraseContract.KEY_SOUND_MODEL_ID + "=" + modelId;
155        if (db.delete(KeyphraseContract.TABLE, keyphraseClause, null) == 0) {
156            Slog.w(TAG, "No keyphrases deleted from the database");
157            status = false;
158        }
159        db.close();
160        return status;
161    }
162
163    /**
164     * Lists all the keyphrase sound models currently registered with the system.
165     */
166    public List<KeyphraseSoundModel> getKephraseSoundModels() {
167        List<KeyphraseSoundModel> models = new ArrayList<>();
168        String selectQuery = "SELECT  * FROM " + SoundModelContract.TABLE;
169        SQLiteDatabase db = getReadableDatabase();
170        Cursor c = db.rawQuery(selectQuery, null);
171
172        // looping through all rows and adding to list
173        if (c.moveToFirst()) {
174            do {
175                int type = c.getInt(c.getColumnIndex(SoundModelContract.KEY_TYPE));
176                if (type != SoundTrigger.SoundModel.TYPE_KEYPHRASE) {
177                    // Ignore non-keyphrase sound models.
178                    continue;
179                }
180                String id = c.getString(c.getColumnIndex(SoundModelContract.KEY_ID));
181                byte[] data = c.getBlob(c.getColumnIndex(SoundModelContract.KEY_DATA));
182                // Get all the keyphrases for this this sound model.
183                // Validate the sound model.
184                if (id == null) {
185                    Slog.w(TAG, "Ignoring sound model since it doesn't specify an ID");
186                    continue;
187                }
188                KeyphraseSoundModel model = new KeyphraseSoundModel(
189                        UUID.fromString(id), data, getKeyphrasesForSoundModel(db, id));
190                if (DBG) {
191                    Slog.d(TAG, "Adding model: " + model);
192                }
193                models.add(model);
194            } while (c.moveToNext());
195        }
196        c.close();
197        db.close();
198        return models;
199    }
200
201    private Keyphrase[] getKeyphrasesForSoundModel(SQLiteDatabase db, String modelId) {
202        List<Keyphrase> keyphrases = new ArrayList<>();
203        String selectQuery = "SELECT  * FROM " + KeyphraseContract.TABLE
204                + " WHERE " + KeyphraseContract.KEY_SOUND_MODEL_ID + " = '" + modelId + "'";
205        Cursor c = db.rawQuery(selectQuery, null);
206
207        // looping through all rows and adding to list
208        if (c.moveToFirst()) {
209            do {
210                int id = c.getInt(c.getColumnIndex(KeyphraseContract.KEY_ID));
211                int modes = c.getInt(c.getColumnIndex(KeyphraseContract.KEY_RECOGNITION_MODES));
212                int[] users = getArrayForCommaSeparatedString(
213                        c.getString(c.getColumnIndex(KeyphraseContract.KEY_USERS)));
214                String locale = c.getString(c.getColumnIndex(KeyphraseContract.KEY_LOCALE));
215                String hintText = c.getString(c.getColumnIndex(KeyphraseContract.KEY_HINT_TEXT));
216
217                // Only add keyphrases meant for the current user.
218                if (users == null) {
219                    // No users present in the keyphrase.
220                    Slog.w(TAG, "Ignoring keyphrase since it doesn't specify users");
221                    continue;
222                }
223                boolean isAvailableForCurrentUser = false;
224                int currentUser = mUserManager.getUserHandle();
225                for (int user : users) {
226                    if (currentUser == user) {
227                        isAvailableForCurrentUser = true;
228                        break;
229                    }
230                }
231                if (!isAvailableForCurrentUser) {
232                    Slog.w(TAG, "Ignoring keyphrase since it's not for the current user");
233                    continue;
234                }
235
236                keyphrases.add(new Keyphrase(id, modes, locale, hintText, users));
237            } while (c.moveToNext());
238        }
239        Keyphrase[] keyphraseArr = new Keyphrase[keyphrases.size()];
240        keyphrases.toArray(keyphraseArr);
241        c.close();
242        return keyphraseArr;
243    }
244
245
246    private String getCommaSeparatedString(int[] users) {
247        if (users == null || users.length == 0) {
248            return "";
249        }
250        String csv = "";
251        for (int user : users) {
252            csv += String.valueOf(user);
253            csv += ",";
254        }
255        return csv.substring(0, csv.length() - 1);
256    }
257
258    private int[] getArrayForCommaSeparatedString(String text) {
259        if (TextUtils.isEmpty(text)) {
260            return null;
261        }
262        String[] usersStr = text.split(",");
263        int[] users = new int[usersStr.length];
264        for (int i = 0; i < usersStr.length; i++) {
265            users[i] = Integer.valueOf(usersStr[i]);
266        }
267        return users;
268    }
269}
270