DatabaseServiceNew.java 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
  1. /* _____ _
  2. * |_ _| |_ _ _ ___ ___ _ __ __ _
  3. * | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. * |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. *
  6. * Threema for Android
  7. * Copyright (c) 2015-2021 Threema GmbH
  8. *
  9. * This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU Affero General Public License, version 3,
  11. * as published by the Free Software Foundation.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU Affero General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Affero General Public License
  19. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. */
  21. package ch.threema.storage;
  22. import android.content.Context;
  23. import android.text.format.DateUtils;
  24. import android.widget.Toast;
  25. import net.sqlcipher.DatabaseErrorHandler;
  26. import net.sqlcipher.database.SQLiteDatabase;
  27. import net.sqlcipher.database.SQLiteDatabaseHook;
  28. import net.sqlcipher.database.SQLiteException;
  29. import net.sqlcipher.database.SQLiteOpenHelper;
  30. import org.slf4j.Logger;
  31. import org.slf4j.LoggerFactory;
  32. import java.io.File;
  33. import java.io.IOException;
  34. import androidx.annotation.MainThread;
  35. import ch.threema.app.exceptions.DatabaseMigrationFailedException;
  36. import ch.threema.app.exceptions.DatabaseMigrationLockedException;
  37. import ch.threema.app.services.UpdateSystemService;
  38. import ch.threema.app.services.systemupdate.SystemUpdateToVersion10;
  39. import ch.threema.app.services.systemupdate.SystemUpdateToVersion11;
  40. import ch.threema.app.services.systemupdate.SystemUpdateToVersion12;
  41. import ch.threema.app.services.systemupdate.SystemUpdateToVersion13;
  42. import ch.threema.app.services.systemupdate.SystemUpdateToVersion14;
  43. import ch.threema.app.services.systemupdate.SystemUpdateToVersion15;
  44. import ch.threema.app.services.systemupdate.SystemUpdateToVersion16;
  45. import ch.threema.app.services.systemupdate.SystemUpdateToVersion17;
  46. import ch.threema.app.services.systemupdate.SystemUpdateToVersion18;
  47. import ch.threema.app.services.systemupdate.SystemUpdateToVersion19;
  48. import ch.threema.app.services.systemupdate.SystemUpdateToVersion20;
  49. import ch.threema.app.services.systemupdate.SystemUpdateToVersion21;
  50. import ch.threema.app.services.systemupdate.SystemUpdateToVersion23;
  51. import ch.threema.app.services.systemupdate.SystemUpdateToVersion24;
  52. import ch.threema.app.services.systemupdate.SystemUpdateToVersion25;
  53. import ch.threema.app.services.systemupdate.SystemUpdateToVersion27;
  54. import ch.threema.app.services.systemupdate.SystemUpdateToVersion28;
  55. import ch.threema.app.services.systemupdate.SystemUpdateToVersion30;
  56. import ch.threema.app.services.systemupdate.SystemUpdateToVersion31;
  57. import ch.threema.app.services.systemupdate.SystemUpdateToVersion32;
  58. import ch.threema.app.services.systemupdate.SystemUpdateToVersion33;
  59. import ch.threema.app.services.systemupdate.SystemUpdateToVersion34;
  60. import ch.threema.app.services.systemupdate.SystemUpdateToVersion35;
  61. import ch.threema.app.services.systemupdate.SystemUpdateToVersion36;
  62. import ch.threema.app.services.systemupdate.SystemUpdateToVersion37;
  63. import ch.threema.app.services.systemupdate.SystemUpdateToVersion38;
  64. import ch.threema.app.services.systemupdate.SystemUpdateToVersion39;
  65. import ch.threema.app.services.systemupdate.SystemUpdateToVersion4;
  66. import ch.threema.app.services.systemupdate.SystemUpdateToVersion40;
  67. import ch.threema.app.services.systemupdate.SystemUpdateToVersion41;
  68. import ch.threema.app.services.systemupdate.SystemUpdateToVersion42;
  69. import ch.threema.app.services.systemupdate.SystemUpdateToVersion43;
  70. import ch.threema.app.services.systemupdate.SystemUpdateToVersion44;
  71. import ch.threema.app.services.systemupdate.SystemUpdateToVersion45;
  72. import ch.threema.app.services.systemupdate.SystemUpdateToVersion46;
  73. import ch.threema.app.services.systemupdate.SystemUpdateToVersion47;
  74. import ch.threema.app.services.systemupdate.SystemUpdateToVersion48;
  75. import ch.threema.app.services.systemupdate.SystemUpdateToVersion49;
  76. import ch.threema.app.services.systemupdate.SystemUpdateToVersion50;
  77. import ch.threema.app.services.systemupdate.SystemUpdateToVersion51;
  78. import ch.threema.app.services.systemupdate.SystemUpdateToVersion52;
  79. import ch.threema.app.services.systemupdate.SystemUpdateToVersion53;
  80. import ch.threema.app.services.systemupdate.SystemUpdateToVersion54;
  81. import ch.threema.app.services.systemupdate.SystemUpdateToVersion55;
  82. import ch.threema.app.services.systemupdate.SystemUpdateToVersion56;
  83. import ch.threema.app.services.systemupdate.SystemUpdateToVersion58;
  84. import ch.threema.app.services.systemupdate.SystemUpdateToVersion59;
  85. import ch.threema.app.services.systemupdate.SystemUpdateToVersion6;
  86. import ch.threema.app.services.systemupdate.SystemUpdateToVersion60;
  87. import ch.threema.app.services.systemupdate.SystemUpdateToVersion61;
  88. import ch.threema.app.services.systemupdate.SystemUpdateToVersion62;
  89. import ch.threema.app.services.systemupdate.SystemUpdateToVersion63;
  90. import ch.threema.app.services.systemupdate.SystemUpdateToVersion64;
  91. import ch.threema.app.services.systemupdate.SystemUpdateToVersion65;
  92. import ch.threema.app.services.systemupdate.SystemUpdateToVersion66;
  93. import ch.threema.app.services.systemupdate.SystemUpdateToVersion7;
  94. import ch.threema.app.services.systemupdate.SystemUpdateToVersion8;
  95. import ch.threema.app.services.systemupdate.SystemUpdateToVersion9;
  96. import ch.threema.app.utils.FileUtil;
  97. import ch.threema.app.utils.RuntimeUtil;
  98. import ch.threema.app.utils.TestUtil;
  99. import ch.threema.storage.factories.BallotChoiceModelFactory;
  100. import ch.threema.storage.factories.BallotModelFactory;
  101. import ch.threema.storage.factories.BallotVoteModelFactory;
  102. import ch.threema.storage.factories.ContactModelFactory;
  103. import ch.threema.storage.factories.ConversationTagFactory;
  104. import ch.threema.storage.factories.DistributionListMemberModelFactory;
  105. import ch.threema.storage.factories.DistributionListMessageModelFactory;
  106. import ch.threema.storage.factories.DistributionListModelFactory;
  107. import ch.threema.storage.factories.GroupBallotModelFactory;
  108. import ch.threema.storage.factories.GroupMemberModelFactory;
  109. import ch.threema.storage.factories.GroupMessageModelFactory;
  110. import ch.threema.storage.factories.GroupMessagePendingMessageIdModelFactory;
  111. import ch.threema.storage.factories.GroupModelFactory;
  112. import ch.threema.storage.factories.GroupRequestSyncLogModelFactory;
  113. import ch.threema.storage.factories.IdentityBallotModelFactory;
  114. import ch.threema.storage.factories.MessageModelFactory;
  115. import ch.threema.storage.factories.ModelFactory;
  116. import ch.threema.storage.factories.WebClientSessionModelFactory;
  117. public class DatabaseServiceNew extends SQLiteOpenHelper {
  118. private static final Logger logger = LoggerFactory.getLogger(DatabaseServiceNew.class);
  119. public static final String DATABASE_NAME = "threema.db";
  120. public static final String DATABASE_NAME_V4 = "threema4.db";
  121. public static final String DATABASE_BACKUP_EXT = ".backup";
  122. private static final int DATABASE_VERSION = 66;
  123. private final Context context;
  124. private final String key;
  125. private final UpdateSystemService updateSystemService;
  126. private ContactModelFactory contactModelFactory;
  127. private MessageModelFactory messageModelFactory;
  128. private GroupModelFactory groupModelFactory;
  129. private GroupMemberModelFactory groupMemberModelFactory;
  130. private GroupMessageModelFactory groupMessageModelFactory;
  131. private DistributionListModelFactory distributionListModelFactory;
  132. private DistributionListMemberModelFactory distributionListMemberModelFactory;
  133. private DistributionListMessageModelFactory distributionListMessageModelFactory;
  134. private GroupRequestSyncLogModelFactory groupRequestSyncLogModelFactory;
  135. private BallotModelFactory ballotModelFactory;
  136. private BallotChoiceModelFactory ballotChoiceModelFactory;
  137. private BallotVoteModelFactory ballotVoteModelFactory;
  138. private IdentityBallotModelFactory identityBallotModelFactory;
  139. private GroupBallotModelFactory groupBallotModelFactory;
  140. private GroupMessagePendingMessageIdModelFactory groupMessagePendingMessageIdModelFactory;
  141. private WebClientSessionModelFactory webClientSessionModelFactory;
  142. private ConversationTagFactory conversationTagFactory;
  143. public DatabaseServiceNew(final Context context,
  144. final String databaseKey,
  145. UpdateSystemService updateSystemService,
  146. int sqlcipherVersion) {
  147. super(
  148. context,
  149. sqlcipherVersion == 4 ? DATABASE_NAME_V4 : DATABASE_NAME,
  150. null,
  151. DATABASE_VERSION,
  152. new SQLiteDatabaseHook() {
  153. @Override
  154. public void preKey(SQLiteDatabase sqLiteDatabase) {
  155. if (sqlcipherVersion == 4) {
  156. sqLiteDatabase.rawExecSQL("PRAGMA cipher_default_kdf_iter = 1;");
  157. } else {
  158. sqLiteDatabase.rawExecSQL(
  159. "PRAGMA cipher_default_page_size = 1024;" +
  160. "PRAGMA cipher_default_kdf_iter = 4000;" +
  161. "PRAGMA cipher_default_hmac_algorithm = HMAC_SHA1;" +
  162. "PRAGMA cipher_default_kdf_algorithm = PBKDF2_HMAC_SHA1;");
  163. }
  164. }
  165. @Override
  166. public void postKey(SQLiteDatabase sqLiteDatabase) {
  167. if (sqlcipherVersion == 4) {
  168. sqLiteDatabase.rawExecSQL("PRAGMA kdf_iter = 1;");
  169. // turn off memory wiping for now due to https://github.com/sqlcipher/android-database-sqlcipher/issues/411
  170. sqLiteDatabase.rawExecSQL("PRAGMA cipher_memory_security = OFF;");
  171. } else {
  172. sqLiteDatabase.rawExecSQL(
  173. "PRAGMA cipher_page_size = 1024;" +
  174. "PRAGMA kdf_iter = 4000;" +
  175. "PRAGMA cipher_hmac_algorithm = HMAC_SHA1;" +
  176. "PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_SHA1;");
  177. }
  178. }
  179. }
  180. ,
  181. new DatabaseErrorHandler() {
  182. @Override
  183. public void onCorruption(SQLiteDatabase sqLiteDatabase) {
  184. logger.error("Database corrupted");
  185. RuntimeUtil.runOnUiThread(new Runnable() {
  186. @Override
  187. public void run() {
  188. if (context != null) {
  189. Toast.makeText(context, "Database corrupted. Please save all data!", Toast.LENGTH_LONG).show();
  190. }
  191. }
  192. });
  193. // close database
  194. if (sqLiteDatabase.isOpen()) {
  195. try {
  196. sqLiteDatabase.close();
  197. } catch (Exception e) {
  198. //
  199. }
  200. }
  201. System.exit(2);
  202. }
  203. }
  204. );
  205. logger.info("instantiated");
  206. this.updateSystemService = updateSystemService;
  207. this.context = context;
  208. SQLiteDatabase.loadLibs(context);
  209. this.key = databaseKey;
  210. }
  211. public synchronized SQLiteDatabase getWritableDatabase() throws SQLiteException {
  212. return super.getWritableDatabase(this.key);
  213. }
  214. public synchronized SQLiteDatabase getReadableDatabase() {
  215. return super.getReadableDatabase(this.key);
  216. }
  217. @Override
  218. public void onCreate(SQLiteDatabase sqLiteDatabase) {
  219. for(ModelFactory f: new ModelFactory[] {this.getContactModelFactory(),
  220. this.getMessageModelFactory(),
  221. this.getGroupModelFactory(),
  222. this.getGroupMemberModelFactory(),
  223. this.getGroupMessageModelFactory(),
  224. this.getDistributionListModelFactory(),
  225. this.getDistributionListMemberModelFactory(),
  226. this.getDistributionListMessageModelFactory(),
  227. this.getGroupRequestSyncLogModelFactory(),
  228. this.getBallotModelFactory(),
  229. this.getBallotChoiceModelFactory(),
  230. this.getBallotVoteModelFactory(),
  231. this.getIdentityBallotModelFactory(),
  232. this.getGroupBallotModelFactory(),
  233. this.getGroupMessagePendingMessageIdModelFactory(),
  234. this.getWebClientSessionModelFactory(),
  235. this.getConversationTagFactory()})
  236. {
  237. String[] createTableStatement = f.getStatements();
  238. if(createTableStatement != null) {
  239. for (String statement : createTableStatement) {
  240. if (!TestUtil.empty(statement)) {
  241. sqLiteDatabase.execSQL(statement);
  242. }
  243. }
  244. }
  245. }
  246. }
  247. public ContactModelFactory getContactModelFactory() {
  248. if(this.contactModelFactory == null) {
  249. this.contactModelFactory = new ContactModelFactory(this);
  250. }
  251. return this.contactModelFactory;
  252. }
  253. public MessageModelFactory getMessageModelFactory() {
  254. if(this.messageModelFactory == null) {
  255. this.messageModelFactory = new MessageModelFactory(this);
  256. }
  257. return this.messageModelFactory;
  258. }
  259. public GroupModelFactory getGroupModelFactory() {
  260. if(this.groupModelFactory == null) {
  261. this.groupModelFactory = new GroupModelFactory(this);
  262. }
  263. return this.groupModelFactory;
  264. }
  265. public GroupMemberModelFactory getGroupMemberModelFactory() {
  266. if(this.groupMemberModelFactory == null) {
  267. this.groupMemberModelFactory = new GroupMemberModelFactory(this);
  268. }
  269. return this.groupMemberModelFactory;
  270. }
  271. public GroupMessageModelFactory getGroupMessageModelFactory() {
  272. if(this.groupMessageModelFactory == null) {
  273. this.groupMessageModelFactory = new GroupMessageModelFactory(this);
  274. }
  275. return this.groupMessageModelFactory;
  276. }
  277. public DistributionListModelFactory getDistributionListModelFactory() {
  278. if(this.distributionListModelFactory == null) {
  279. this.distributionListModelFactory = new DistributionListModelFactory(this);
  280. }
  281. return this.distributionListModelFactory;
  282. }
  283. public DistributionListMemberModelFactory getDistributionListMemberModelFactory() {
  284. if(this.distributionListMemberModelFactory == null) {
  285. this.distributionListMemberModelFactory = new DistributionListMemberModelFactory(this);
  286. }
  287. return this.distributionListMemberModelFactory;
  288. }
  289. public DistributionListMessageModelFactory getDistributionListMessageModelFactory() {
  290. if(this.distributionListMessageModelFactory == null) {
  291. this.distributionListMessageModelFactory = new DistributionListMessageModelFactory(this);
  292. }
  293. return this.distributionListMessageModelFactory;
  294. }
  295. public GroupRequestSyncLogModelFactory getGroupRequestSyncLogModelFactory() {
  296. if(this.groupRequestSyncLogModelFactory == null) {
  297. this.groupRequestSyncLogModelFactory = new GroupRequestSyncLogModelFactory(this);
  298. }
  299. return this.groupRequestSyncLogModelFactory;
  300. }
  301. public BallotModelFactory getBallotModelFactory() {
  302. if(this.ballotModelFactory == null) {
  303. this.ballotModelFactory = new BallotModelFactory(this);
  304. }
  305. return this.ballotModelFactory;
  306. }
  307. public BallotChoiceModelFactory getBallotChoiceModelFactory() {
  308. if (this.ballotChoiceModelFactory == null) {
  309. this.ballotChoiceModelFactory = new BallotChoiceModelFactory(this);
  310. }
  311. return this.ballotChoiceModelFactory;
  312. }
  313. public BallotVoteModelFactory getBallotVoteModelFactory() {
  314. if(this.ballotVoteModelFactory == null) {
  315. this.ballotVoteModelFactory = new BallotVoteModelFactory(this);
  316. }
  317. return this.ballotVoteModelFactory;
  318. }
  319. public IdentityBallotModelFactory getIdentityBallotModelFactory() {
  320. if(this.identityBallotModelFactory == null) {
  321. this.identityBallotModelFactory = new IdentityBallotModelFactory(this);
  322. }
  323. return this.identityBallotModelFactory;
  324. }
  325. public GroupBallotModelFactory getGroupBallotModelFactory() {
  326. if(this.groupBallotModelFactory == null) {
  327. this.groupBallotModelFactory = new GroupBallotModelFactory(this);
  328. }
  329. return this.groupBallotModelFactory;
  330. }
  331. public GroupMessagePendingMessageIdModelFactory getGroupMessagePendingMessageIdModelFactory() {
  332. if(this.groupMessagePendingMessageIdModelFactory == null) {
  333. this.groupMessagePendingMessageIdModelFactory = new GroupMessagePendingMessageIdModelFactory(this);
  334. }
  335. return this.groupMessagePendingMessageIdModelFactory;
  336. }
  337. public WebClientSessionModelFactory getWebClientSessionModelFactory() {
  338. if(this.webClientSessionModelFactory == null) {
  339. this.webClientSessionModelFactory = new WebClientSessionModelFactory(this);
  340. }
  341. return this.webClientSessionModelFactory;
  342. }
  343. public ConversationTagFactory getConversationTagFactory() {
  344. if(this.conversationTagFactory == null) {
  345. this.conversationTagFactory = new ConversationTagFactory(this);
  346. }
  347. return this.conversationTagFactory;
  348. }
  349. // Note: Enable this to allow database downgrades.
  350. //
  351. //@Override
  352. //public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  353. // logger.info("onDowngrade, version {} -> {}", oldVersion, newVersion);
  354. //}
  355. @Override
  356. public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
  357. logger.info("onUpgrade, version {} -> {}", oldVersion, newVersion);
  358. if (oldVersion < 4) {
  359. this.updateSystemService.addUpdate(new SystemUpdateToVersion4(sqLiteDatabase));
  360. }
  361. if (oldVersion < 6) {
  362. this.updateSystemService.addUpdate(new SystemUpdateToVersion6(this.context, sqLiteDatabase));
  363. }
  364. if (oldVersion < 7) {
  365. this.updateSystemService.addUpdate(new SystemUpdateToVersion7(sqLiteDatabase));
  366. }
  367. if (oldVersion < 8) {
  368. this.updateSystemService.addUpdate(new SystemUpdateToVersion8(this, sqLiteDatabase));
  369. }
  370. if (oldVersion < 9) {
  371. this.updateSystemService.addUpdate(new SystemUpdateToVersion9(sqLiteDatabase));
  372. }
  373. if (oldVersion < 10) {
  374. this.updateSystemService.addUpdate(new SystemUpdateToVersion10(sqLiteDatabase));
  375. }
  376. if (oldVersion < 11) {
  377. this.updateSystemService.addUpdate(new SystemUpdateToVersion11(sqLiteDatabase));
  378. }
  379. if (oldVersion < 12) {
  380. this.updateSystemService.addUpdate(new SystemUpdateToVersion12(this.context, sqLiteDatabase));
  381. }
  382. if (oldVersion < 13) {
  383. this.updateSystemService.addUpdate(new SystemUpdateToVersion13(sqLiteDatabase));
  384. }
  385. if (oldVersion < 14) {
  386. this.updateSystemService.addUpdate(new SystemUpdateToVersion14());
  387. }
  388. if (oldVersion < 15) {
  389. this.updateSystemService.addUpdate(new SystemUpdateToVersion15(this, sqLiteDatabase));
  390. }
  391. if (oldVersion < 16) {
  392. this.updateSystemService.addUpdate(new SystemUpdateToVersion16(sqLiteDatabase));
  393. }
  394. if (oldVersion < 17) {
  395. this.updateSystemService.addUpdate(new SystemUpdateToVersion17(sqLiteDatabase));
  396. }
  397. if (oldVersion < 18) {
  398. this.updateSystemService.addUpdate(new SystemUpdateToVersion18(sqLiteDatabase));
  399. }
  400. if (oldVersion < 19) {
  401. this.updateSystemService.addUpdate(new SystemUpdateToVersion19(sqLiteDatabase));
  402. }
  403. if (oldVersion < 20) {
  404. this.updateSystemService.addUpdate(new SystemUpdateToVersion20(sqLiteDatabase));
  405. }
  406. if (oldVersion < 21) {
  407. this.updateSystemService.addUpdate(new SystemUpdateToVersion21(this, sqLiteDatabase));
  408. }
  409. if (oldVersion < 23) {
  410. this.updateSystemService.addUpdate(new SystemUpdateToVersion23(sqLiteDatabase));
  411. }
  412. if (oldVersion < 24) {
  413. this.updateSystemService.addUpdate(new SystemUpdateToVersion24(sqLiteDatabase));
  414. }
  415. if (oldVersion < 25) {
  416. this.updateSystemService.addUpdate(new SystemUpdateToVersion25(this, sqLiteDatabase));
  417. }
  418. if (oldVersion < 27) {
  419. this.updateSystemService.addUpdate(new SystemUpdateToVersion27(sqLiteDatabase));
  420. }
  421. if (oldVersion < 28) {
  422. this.updateSystemService.addUpdate(new SystemUpdateToVersion28(sqLiteDatabase));
  423. }
  424. if (oldVersion < 30) {
  425. this.updateSystemService.addUpdate(new SystemUpdateToVersion30(sqLiteDatabase));
  426. }
  427. if (oldVersion < 31) {
  428. this.updateSystemService.addUpdate(new SystemUpdateToVersion31(this.context));
  429. }
  430. if (oldVersion < 32) {
  431. this.updateSystemService.addUpdate(new SystemUpdateToVersion32(sqLiteDatabase));
  432. }
  433. if (oldVersion < 33) {
  434. this.updateSystemService.addUpdate(new SystemUpdateToVersion33(this, sqLiteDatabase));
  435. }
  436. if (oldVersion < 34) {
  437. this.updateSystemService.addUpdate(new SystemUpdateToVersion34(sqLiteDatabase));
  438. }
  439. if (oldVersion < 35) {
  440. this.updateSystemService.addUpdate(new SystemUpdateToVersion35(sqLiteDatabase));
  441. }
  442. if (oldVersion < 36) {
  443. this.updateSystemService.addUpdate(new SystemUpdateToVersion36(sqLiteDatabase));
  444. }
  445. if (oldVersion < 37) {
  446. this.updateSystemService.addUpdate(new SystemUpdateToVersion37(this, sqLiteDatabase));
  447. }
  448. if (oldVersion < 38) {
  449. this.updateSystemService.addUpdate(new SystemUpdateToVersion38(this, sqLiteDatabase));
  450. }
  451. if (oldVersion < 39) {
  452. this.updateSystemService.addUpdate(new SystemUpdateToVersion39());
  453. }
  454. if (oldVersion < 40) {
  455. this.updateSystemService.addUpdate(new SystemUpdateToVersion40(sqLiteDatabase));
  456. }
  457. if (oldVersion < 41) {
  458. this.updateSystemService.addUpdate(new SystemUpdateToVersion41(sqLiteDatabase));
  459. }
  460. if (oldVersion < 42) {
  461. this.updateSystemService.addUpdate(new SystemUpdateToVersion42(sqLiteDatabase));
  462. }
  463. if (oldVersion < 43) {
  464. this.updateSystemService.addUpdate(new SystemUpdateToVersion43(sqLiteDatabase));
  465. }
  466. if (oldVersion < 44) {
  467. this.updateSystemService.addUpdate(new SystemUpdateToVersion44(sqLiteDatabase));
  468. }
  469. if (oldVersion < 45) {
  470. this.updateSystemService.addUpdate(new SystemUpdateToVersion45(this, sqLiteDatabase));
  471. }
  472. if (oldVersion < 46) {
  473. this.updateSystemService.addUpdate(new SystemUpdateToVersion46());
  474. }
  475. if (oldVersion < 47) {
  476. this.updateSystemService.addUpdate(new SystemUpdateToVersion47(sqLiteDatabase));
  477. }
  478. if (oldVersion < 48) {
  479. this.updateSystemService.addUpdate(new SystemUpdateToVersion48(this.context));
  480. }
  481. if (oldVersion < 49) {
  482. this.updateSystemService.addUpdate(new SystemUpdateToVersion49(sqLiteDatabase));
  483. }
  484. if (oldVersion < 50) {
  485. this.updateSystemService.addUpdate(new SystemUpdateToVersion50(sqLiteDatabase));
  486. }
  487. if (oldVersion < 51) {
  488. this.updateSystemService.addUpdate(new SystemUpdateToVersion51(sqLiteDatabase));
  489. }
  490. if (oldVersion < 52) {
  491. this.updateSystemService.addUpdate(new SystemUpdateToVersion52(sqLiteDatabase));
  492. }
  493. if (oldVersion < 53) {
  494. this.updateSystemService.addUpdate(new SystemUpdateToVersion53());
  495. }
  496. if (oldVersion < 54) {
  497. this.updateSystemService.addUpdate(new SystemUpdateToVersion54(this.context));
  498. }
  499. if (oldVersion < 55) {
  500. this.updateSystemService.addUpdate(new SystemUpdateToVersion55());
  501. }
  502. if (oldVersion < 56) {
  503. this.updateSystemService.addUpdate(new SystemUpdateToVersion56(sqLiteDatabase));
  504. }
  505. if (oldVersion < 58) {
  506. this.updateSystemService.addUpdate(new SystemUpdateToVersion58(sqLiteDatabase));
  507. }
  508. if (oldVersion < 59) {
  509. this.updateSystemService.addUpdate(new SystemUpdateToVersion59(sqLiteDatabase));
  510. }
  511. if (oldVersion < 60) {
  512. this.updateSystemService.addUpdate(new SystemUpdateToVersion60(sqLiteDatabase));
  513. }
  514. if (oldVersion < 61) {
  515. this.updateSystemService.addUpdate(new SystemUpdateToVersion61(sqLiteDatabase));
  516. }
  517. if (oldVersion < 62) {
  518. this.updateSystemService.addUpdate(new SystemUpdateToVersion62(sqLiteDatabase));
  519. }
  520. if (oldVersion < 63) {
  521. this.updateSystemService.addUpdate(new SystemUpdateToVersion63(this.context));
  522. }
  523. if (oldVersion < 64) {
  524. this.updateSystemService.addUpdate(new SystemUpdateToVersion64(this.context));
  525. }
  526. if (oldVersion < 65) {
  527. this.updateSystemService.addUpdate(new SystemUpdateToVersion65(this.context));
  528. }
  529. if (oldVersion < 66) {
  530. this.updateSystemService.addUpdate(new SystemUpdateToVersion66(sqLiteDatabase));
  531. }
  532. }
  533. public void executeNull() throws SQLiteException {
  534. this.getWritableDatabase().rawExecSQL("SELECT NULL");
  535. }
  536. @MainThread
  537. public static synchronized void tryMigrateToV4(Context context, final String databaseKey) throws DatabaseMigrationFailedException, DatabaseMigrationLockedException {
  538. File oldDatabaseFile = context.getDatabasePath(DATABASE_NAME);
  539. File newDatabaseFile = context.getDatabasePath(DATABASE_NAME_V4);
  540. final boolean[] migrateSuccess = {false};
  541. logger.info("check if v4 database migration is necessary");
  542. if (oldDatabaseFile.exists()) {
  543. File lockfile = new File(context.getFilesDir(), ".dbv4-lock");
  544. if (lockfile.exists()) {
  545. long lastModified = lockfile.lastModified();
  546. long now = System.currentTimeMillis();
  547. if ((now - lastModified) > (5 * DateUtils.MINUTE_IN_MILLIS)) {
  548. FileUtil.deleteFileOrWarn(lockfile, "Lockfile", logger);
  549. if (newDatabaseFile.exists()) {
  550. FileUtil.deleteFileOrWarn(newDatabaseFile, "New Database File", logger);
  551. }
  552. } else {
  553. logger.info("Lockfile exists...exiting");
  554. throw new DatabaseMigrationLockedException();
  555. }
  556. }
  557. try {
  558. FileUtil.createNewFileOrLog(lockfile, logger);
  559. } catch (IOException e) {
  560. logger.error("Exception", e);
  561. }
  562. if (!newDatabaseFile.exists()) {
  563. logger.info("Database migration to v4 required");
  564. long usableSpace = oldDatabaseFile.getUsableSpace();
  565. long fileSize = oldDatabaseFile.length();
  566. if (usableSpace < (fileSize * 2)) {
  567. FileUtil.deleteFileOrWarn(lockfile, "Lockfile", logger);
  568. throw new DatabaseMigrationFailedException("Not enough space left on device");
  569. }
  570. Thread migrateThread = new Thread(new Runnable() {
  571. @Override
  572. public void run() {
  573. try {
  574. // migrate
  575. SQLiteDatabase.loadLibs(context);
  576. SQLiteDatabaseHook hook = new SQLiteDatabaseHook() {
  577. @Override
  578. public void preKey(SQLiteDatabase sqLiteDatabase) {}
  579. @Override
  580. public void postKey(SQLiteDatabase sqLiteDatabase) {
  581. // old settings
  582. sqLiteDatabase.rawExecSQL(
  583. "PRAGMA cipher_page_size = 1024;" +
  584. "PRAGMA kdf_iter = 4000;" +
  585. "PRAGMA cipher_hmac_algorithm = HMAC_SHA1;" +
  586. "PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_SHA1;");
  587. }
  588. };
  589. final int databaseVersion;
  590. try (SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(oldDatabaseFile.getAbsolutePath(), databaseKey, null, hook)) {
  591. if (database.isOpen()) {
  592. databaseVersion = database.getVersion();
  593. logger.info("Original database version: {}", databaseVersion);
  594. database.rawExecSQL(
  595. "PRAGMA key = '" + databaseKey + "';" +
  596. "PRAGMA cipher_page_size = 1024;" +
  597. "PRAGMA kdf_iter = 4000;" +
  598. "PRAGMA cipher_hmac_algorithm = HMAC_SHA1;" +
  599. "PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_SHA1;" +
  600. "ATTACH DATABASE '" + newDatabaseFile.getAbsolutePath() + "' AS threema4 KEY '" + databaseKey + "';" +
  601. "PRAGMA threema4.kdf_iter = 1;" +
  602. "PRAGMA threema4.cipher_memory_security = OFF;" +
  603. "SELECT sqlcipher_export('threema4');" +
  604. "PRAGMA threema4.user_version = " + databaseVersion + ";" +
  605. "DETACH DATABASE threema4;");
  606. database.close();
  607. logger.info("Database successfully migrated");
  608. if (checkNewDatabase(newDatabaseFile, databaseKey, databaseVersion)) {
  609. migrateSuccess[0] = true;
  610. }
  611. }
  612. }
  613. } catch (Exception e) {
  614. logger.info("Database migration FAILED");
  615. logger.error("Exception", e);
  616. FileUtil.deleteFileOrWarn(newDatabaseFile, "New Database File", logger);
  617. }
  618. }
  619. });
  620. migrateThread.start();
  621. try {
  622. migrateThread.join();
  623. } catch (InterruptedException e) {
  624. logger.error("Exception", e);
  625. migrateSuccess[0] = false;
  626. }
  627. if (migrateSuccess[0]) {
  628. Toast.makeText(context, "Database successfully migrated", Toast.LENGTH_LONG).show();
  629. logger.info("Migration finished");
  630. } else {
  631. logger.info("Migration failed");
  632. FileUtil.deleteFileOrWarn(newDatabaseFile, "New Database File", logger);
  633. FileUtil.deleteFileOrWarn(lockfile, "New Database File", logger);
  634. throw new DatabaseMigrationFailedException();
  635. }
  636. } else {
  637. try {
  638. SQLiteDatabase.loadLibs(context);
  639. if (checkNewDatabase(newDatabaseFile, databaseKey, DATABASE_VERSION)) {
  640. logger.info("Delete old format database");
  641. FileUtil.deleteFileOrWarn(oldDatabaseFile, "Old Database File", logger);
  642. } else {
  643. throw new Exception();
  644. }
  645. } catch (Exception e) {
  646. logger.info("Database checking FAILED");
  647. FileUtil.deleteFileOrWarn(newDatabaseFile, "New Database File", logger);
  648. FileUtil.deleteFileOrWarn(lockfile, "Lockfile", logger);
  649. throw new DatabaseMigrationFailedException();
  650. }
  651. }
  652. FileUtil.deleteFileOrWarn(lockfile, "Lockfile", logger);
  653. } else {
  654. logger.info("No old database file found. No migration necessary");
  655. logger.info("New database file exists = {}", newDatabaseFile.exists());
  656. }
  657. }
  658. private static boolean checkNewDatabase(File newDatabaseFile, String databaseKey, int databaseVersion) {
  659. // test new database
  660. try (SQLiteDatabase newDatabase = SQLiteDatabase.openDatabase(newDatabaseFile.getAbsolutePath(), databaseKey, null, 0, new SQLiteDatabaseHook() {
  661. @Override
  662. public void preKey(SQLiteDatabase sqLiteDatabase) {
  663. sqLiteDatabase.rawExecSQL("PRAGMA cipher_default_kdf_iter = 1;");
  664. }
  665. @Override
  666. public void postKey(SQLiteDatabase sqLiteDatabase) {
  667. sqLiteDatabase.rawExecSQL(
  668. "PRAGMA kdf_iter = 1;" +
  669. "PRAGMA cipher_memory_security = OFF;");
  670. }
  671. })) {
  672. if (newDatabase.isOpen()) {
  673. if (newDatabase.getVersion() == databaseVersion) {
  674. newDatabase.rawExecSQL("SELECT NULL;");
  675. logger.info("New database successfully checked. Version set to {}", databaseVersion);
  676. return true;
  677. } else {
  678. logger.info("Database version mismatch. old = {} new = {}", databaseVersion, newDatabase.getVersion());
  679. }
  680. } else {
  681. logger.info("Could not open new database");
  682. }
  683. }
  684. return false;
  685. }
  686. }