1 /*
2  * Copyright (C) 2017 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 package com.android.launcher3.model;
17 
18 import android.content.Context;
19 import android.database.sqlite.SQLiteDatabase;
20 import android.database.sqlite.SQLiteException;
21 import android.util.Log;
22 import android.util.SparseArray;
23 
24 import com.android.launcher3.R;
25 import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
26 import com.android.launcher3.util.IOUtils;
27 
28 import org.json.JSONArray;
29 import org.json.JSONException;
30 import org.json.JSONObject;
31 
32 import java.io.File;
33 import java.io.FileOutputStream;
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.util.ArrayList;
37 import java.util.Collections;
38 
39 /**
40  * Utility class to handle DB downgrade
41  */
42 public class DbDowngradeHelper {
43 
44     private static final String TAG = "DbDowngradeHelper";
45 
46     private static final String KEY_VERSION = "version";
47     private static final String KEY_DOWNGRADE_TO = "downgrade_to_";
48 
49     private final SparseArray<String[]> mStatements = new SparseArray<>();
50     public final int version;
51 
DbDowngradeHelper(int version)52     private DbDowngradeHelper(int version) {
53         this.version = version;
54     }
55 
onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion)56     public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
57         ArrayList<String> allCommands = new ArrayList<>();
58 
59         for (int i = oldVersion - 1; i >= newVersion; i--) {
60             String[] commands = mStatements.get(i);
61             if (commands == null) {
62                 throw new SQLiteException("Downgrade path not supported to version " + i);
63             }
64             Collections.addAll(allCommands, commands);
65         }
66 
67         try (SQLiteTransaction t = new SQLiteTransaction(db)) {
68             for (String sql : allCommands) {
69                 db.execSQL(sql);
70             }
71             t.commit();
72         }
73     }
74 
parse(File file)75     public static DbDowngradeHelper parse(File file) throws JSONException, IOException {
76         JSONObject obj = new JSONObject(new String(IOUtils.toByteArray(file)));
77         DbDowngradeHelper helper = new DbDowngradeHelper(obj.getInt(KEY_VERSION));
78         for (int version = helper.version - 1; version > 0; version--) {
79             if (obj.has(KEY_DOWNGRADE_TO + version)) {
80                 JSONArray statements = obj.getJSONArray(KEY_DOWNGRADE_TO + version);
81                 String[] parsed = new String[statements.length()];
82                 for (int i = 0; i < parsed.length; i++) {
83                     parsed[i] = statements.getString(i);
84                 }
85                 helper.mStatements.put(version, parsed);
86             }
87         }
88         return helper;
89     }
90 
updateSchemaFile(File schemaFile, int expectedVersion, Context context)91     public static void updateSchemaFile(File schemaFile, int expectedVersion, Context context) {
92         try {
93             if (DbDowngradeHelper.parse(schemaFile).version >= expectedVersion) {
94                 return;
95             }
96         } catch (Exception e) {
97             // Schema error
98         }
99 
100         // Write the updated schema
101         try (FileOutputStream fos = new FileOutputStream(schemaFile);
102             InputStream in = context.getResources().openRawResource(R.raw.downgrade_schema)) {
103             IOUtils.copy(in, fos);
104         } catch (IOException e) {
105             Log.e(TAG, "Error writing schema file", e);
106         }
107     }
108 }
109