DatabaseServiceNew.java 33 KB

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