105f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal/* 205f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal * Copyright (C) 2017 The Android Open Source Project 305f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal * 405f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal * Licensed under the Apache License, Version 2.0 (the "License"); 505f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal * you may not use this file except in compliance with the License. 605f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal * You may obtain a copy of the License at 705f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal * 805f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal * http://www.apache.org/licenses/LICENSE-2.0 905f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal * 1005f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal * Unless required by applicable law or agreed to in writing, software 1105f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal * distributed under the License is distributed on an "AS IS" BASIS, 1205f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1305f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal * See the License for the specific language governing permissions and 1405f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal * limitations under the License. 1505f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal */ 1605f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyalpackage com.android.launcher3.model; 1705f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal 1805f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyalimport android.content.Context; 1905f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyalimport android.database.sqlite.SQLiteDatabase; 2005f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyalimport android.database.sqlite.SQLiteException; 2105f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyalimport android.util.Log; 2205f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyalimport android.util.SparseArray; 2305f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal 2405f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyalimport com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction; 2505f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyalimport com.android.launcher3.util.IOUtils; 2605f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal 2705f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyalimport org.json.JSONArray; 2805f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyalimport org.json.JSONException; 2905f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyalimport org.json.JSONObject; 3005f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal 3105f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyalimport java.io.File; 3205f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyalimport java.io.FileOutputStream; 3305f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyalimport java.io.IOException; 3405f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyalimport java.io.InputStream; 3505f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyalimport java.util.ArrayList; 3605f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyalimport java.util.Collections; 3705f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal 3805f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal/** 3905f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal * Utility class to handle DB downgrade 4005f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal */ 4105f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyalpublic class DbDowngradeHelper { 4205f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal 4305f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal private static final String TAG = "DbDowngradeHelper"; 4405f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal 4505f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal private static final String KEY_VERSION = "version"; 4605f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal private static final String KEY_DOWNGRADE_TO = "downgrade_to_"; 4705f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal 4805f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal private final SparseArray<String[]> mStatements = new SparseArray<>(); 4905f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal public final int version; 5005f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal 5105f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal private DbDowngradeHelper(int version) { 5205f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal this.version = version; 5305f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal } 5405f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal 5505f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { 5605f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal ArrayList<String> allCommands = new ArrayList<>(); 5705f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal 5805f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal for (int i = oldVersion - 1; i >= newVersion; i--) { 5905f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal String[] commands = mStatements.get(i); 6005f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal if (commands == null) { 6105f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal throw new SQLiteException("Downgrade path not supported to version " + i); 6205f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal } 6305f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal Collections.addAll(allCommands, commands); 6405f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal } 6505f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal 6605f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal try (SQLiteTransaction t = new SQLiteTransaction(db)) { 6705f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal for (String sql : allCommands) { 6805f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal db.execSQL(sql); 6905f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal } 7005f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal t.commit(); 7105f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal } 7205f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal } 7305f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal 7405f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal public static DbDowngradeHelper parse(File file) throws JSONException, IOException { 7505f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal JSONObject obj = new JSONObject(new String(IOUtils.toByteArray(file))); 7605f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal DbDowngradeHelper helper = new DbDowngradeHelper(obj.getInt(KEY_VERSION)); 7705f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal for (int version = helper.version - 1; version > 0; version--) { 7805f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal if (obj.has(KEY_DOWNGRADE_TO + version)) { 7905f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal JSONArray statements = obj.getJSONArray(KEY_DOWNGRADE_TO + version); 8005f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal String[] parsed = new String[statements.length()]; 8105f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal for (int i = 0; i < parsed.length; i++) { 8205f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal parsed[i] = statements.getString(i); 8305f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal } 8405f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal helper.mStatements.put(version, parsed); 8505f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal } 8605f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal } 8705f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal return helper; 8805f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal } 8905f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal 9005f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal public static void updateSchemaFile(File schemaFile, int expectedVersion, 9105f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal Context context, int schemaResId) { 9205f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal try { 9305f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal if (DbDowngradeHelper.parse(schemaFile).version >= expectedVersion) { 9405f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal return; 9505f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal } 9605f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal } catch (Exception e) { 9705f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal // Schema error 9805f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal } 9905f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal 10005f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal // Write the updated schema 10105f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal try (FileOutputStream fos = new FileOutputStream(schemaFile); 10205f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal InputStream in = context.getResources().openRawResource(schemaResId)) { 10305f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal IOUtils.copy(in, fos); 10405f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal } catch (IOException e) { 10505f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal Log.e(TAG, "Error writing schema file", e); 10605f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal } 10705f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal } 10805f30889d6ba0f803c720813067b28c9c4cf2bfdSunny Goyal} 109