MD5 校验值:ecd580f5871ea641118d82ebcc32ef4d
DatabasePersistence.java 文件包含反编译后的源代码,请注意,该内容仅供学习和参考使用,不得用于非法用途。
package com.microsoft.appcenter.persistence; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteFullException; import android.database.sqlite.SQLiteQueryBuilder; import com.microsoft.appcenter.Constants; import com.microsoft.appcenter.Flags; import com.microsoft.appcenter.ingestion.models.Log; import com.microsoft.appcenter.ingestion.models.one.CommonSchemaLog; import com.microsoft.appcenter.ingestion.models.one.PartAUtils; import com.microsoft.appcenter.persistence.Persistence; import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.crypto.CryptoUtils; import com.microsoft.appcenter.utils.storage.DatabaseManager; import com.microsoft.appcenter.utils.storage.FileManager; import com.microsoft.appcenter.utils.storage.SQLiteUtils; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import org.json.JSONException; public class DatabasePersistence extends Persistence { private static final String COLUMN_DATA_TYPE = "type"; static final String COLUMN_GROUP = "persistence_group"; static final String COLUMN_LOG = "log"; static final String COLUMN_PRIORITY = "priority"; static final String COLUMN_TARGET_KEY = "target_key"; static final String COLUMN_TARGET_TOKEN = "target_token"; static final String CREATE_LOGS_SQL = "CREATE TABLE IF NOT EXISTS `logs`(`oid` INTEGER PRIMARY KEY AUTOINCREMENT,`target_token` TEXT,`type` TEXT,`priority` INTEGER,`log` TEXT,`persistence_group` TEXT,`target_key` TEXT);"; private static final String CREATE_PRIORITY_INDEX_LOGS = "CREATE INDEX `ix_logs_priority` ON logs (`priority`)"; static final String DATABASE = "com.microsoft.appcenter.persistence"; private static final String DROP_LOGS_SQL = "DROP TABLE `logs`"; private static final String GET_SORT_ORDER = "priority DESC, oid"; private static final String PAYLOAD_FILE_EXTENSION = ".json"; private static final String PAYLOAD_LARGE_DIRECTORY = "/appcenter/database_large_payloads"; static final int PAYLOAD_MAX_SIZE = 1992294; static final ContentValues SCHEMA = getContentValues("", "", "", "", "", 0); static final String TABLE = "logs"; private static final int VERSION = 6; static final int VERSION_TIMESTAMP_COLUMN = 5; private final Context mContext; final DatabaseManager mDatabaseManager; private final File mLargePayloadDirectory; private long mLargePayloadsSize; final Set<Long> mPendingDbIdentifiers; final Map<String, List<Long>> mPendingDbIdentifiersGroups; public DatabasePersistence(Context context) { this(context, 6, SCHEMA); } DatabasePersistence(Context context, int version, final ContentValues schema) { this.mContext = context; this.mPendingDbIdentifiersGroups = new HashMap(); this.mPendingDbIdentifiers = new HashSet(); this.mDatabaseManager = new DatabaseManager(context, DATABASE, TABLE, version, schema, CREATE_LOGS_SQL, new DatabaseManager.Listener() { @Override public void onCreate(SQLiteDatabase db) { db.execSQL(DatabasePersistence.CREATE_PRIORITY_INDEX_LOGS); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(DatabasePersistence.DROP_LOGS_SQL); db.execSQL(DatabasePersistence.CREATE_LOGS_SQL); db.execSQL(DatabasePersistence.CREATE_PRIORITY_INDEX_LOGS); } }); File file = new File(Constants.FILES_PATH + PAYLOAD_LARGE_DIRECTORY); this.mLargePayloadDirectory = file; file.mkdirs(); this.mLargePayloadsSize = checkLargePayloadFilesAndCollectTheirSize(); } private static ContentValues getContentValues(String group, String logJ, String targetToken, String type, String targetKey, int priority) { ContentValues contentValues = new ContentValues(); contentValues.put(COLUMN_GROUP, group); contentValues.put(COLUMN_LOG, logJ); contentValues.put(COLUMN_TARGET_TOKEN, targetToken); contentValues.put("type", type); contentValues.put(COLUMN_TARGET_KEY, targetKey); contentValues.put("priority", Integer.valueOf(priority)); return contentValues; } @Override public boolean setMaxStorageSize(long maxStorageSizeInBytes) { boolean maxSize = this.mDatabaseManager.setMaxSize(maxStorageSizeInBytes); deleteLogsThatNotFitMaxSize(); return maxSize; } @Override public long putLog(Log log, String group, int flags) throws Persistence.PersistenceException { String str; String str2; try { try { AppCenterLog.debug("AppCenter", "Storing a log to the Persistence database for log type " + log.getType() + " with flags=" + flags); String serializeLog = getLogSerializer().serializeLog(log); int length = serializeLog.getBytes("UTF-8").length; boolean z = length >= PAYLOAD_MAX_SIZE; Long l = null; if (!(log instanceof CommonSchemaLog)) { str = null; str2 = null; } else { if (z) { throw new Persistence.PersistenceException("Log is larger than 1992294 bytes, cannot send to OneCollector."); } String next = log.getTransmissionTargetTokens().iterator().next(); String targetKey = PartAUtils.getTargetKey(next); str = CryptoUtils.getInstance(this.mContext).encrypt(next); str2 = targetKey; } long maxSize = this.mDatabaseManager.getMaxSize(); if (maxSize == -1) { throw new Persistence.PersistenceException("Failed to store a log to the Persistence database."); } long j = length; if (maxSize <= j) { throw new Persistence.PersistenceException("Log is too large (" + length + " bytes) to store in database. Current maximum database size is " + maxSize + " bytes."); } int persistenceFlag = Flags.getPersistenceFlag(flags, false); long j2 = maxSize; try { ContentValues contentValues = getContentValues(group, z ? null : serializeLog, str, log.getType(), str2, persistenceFlag); while (z) { if (getStoredDataSize() + j <= j2) { break; } AppCenterLog.debug("AppCenter", "Storage is full, trying to delete the oldest log that has the lowest priority which is lower or equal priority than the new log."); long j3 = j2; if (deleteTheOldestLog(persistenceFlag) == -1) { throw new Persistence.PersistenceException("Failed to clear space for new log record."); } j2 = j3; } while (l == null) { try { l = Long.valueOf(this.mDatabaseManager.put(contentValues)); } catch (SQLiteFullException unused) { AppCenterLog.debug("AppCenter", "Storage is full, trying to delete the oldest log that has the lowest priority which is lower or equal priority than the new log."); if (deleteTheOldestLog(persistenceFlag) == -1) { l = -1L; } } } if (l.longValue() == -1) { throw new Persistence.PersistenceException("Failed to store a log to the Persistence database for log type " + log.getType() + "."); } AppCenterLog.debug("AppCenter", "Stored a log to the Persistence database for log type " + log.getType() + " with databaseId=" + l); if (z) { AppCenterLog.debug("AppCenter", "Payload is larger than what SQLite supports, storing payload in a separate file."); File largePayloadGroupDirectory = getLargePayloadGroupDirectory(group); largePayloadGroupDirectory.mkdir(); File largePayloadFile = getLargePayloadFile(largePayloadGroupDirectory, l.longValue()); try { FileManager.write(largePayloadFile, serializeLog); this.mLargePayloadsSize += largePayloadFile.length(); AppCenterLog.verbose("AppCenter", "Store extra " + largePayloadFile.length() + " KB as a separated payload file."); AppCenterLog.debug("AppCenter", "Payload written to " + largePayloadFile); } catch (IOException e) { this.mDatabaseManager.delete(l.longValue()); throw e; } } deleteLogsThatNotFitMaxSize(); return l.longValue(); } catch (IOException e2) { e = e2; throw new Persistence.PersistenceException("Cannot save large payload in a file.", e); } catch (JSONException e3) { e = e3; throw new Persistence.PersistenceException("Cannot convert to JSON string.", e); } } catch (IOException e4) { e = e4; } } catch (JSONException e5) { e = e5; } } File getLargePayloadGroupDirectory(String group) { return new File(this.mLargePayloadDirectory, group); } File getLargePayloadFile(File directory, long databaseId) { return new File(directory, databaseId + ".json"); } private void deleteLog(File groupLargePayloadDirectory, long id) { getLargePayloadFile(groupLargePayloadDirectory, id).delete(); this.mDatabaseManager.delete(id); } @Override public void deleteLogs(String group, String id) { AppCenterLog.debug("AppCenter", "Deleting logs from the Persistence database for " + group + " with " + id); AppCenterLog.debug("AppCenter", "The IDs for deleting log(s) is/are:"); List<Long> remove = this.mPendingDbIdentifiersGroups.remove(group + id); File largePayloadGroupDirectory = getLargePayloadGroupDirectory(group); if (remove != null) { for (Long l : remove) { AppCenterLog.debug("AppCenter", "\t" + l); deleteLog(largePayloadGroupDirectory, l.longValue()); this.mPendingDbIdentifiers.remove(l); } } } @Override public void deleteLogs(String group) { AppCenterLog.debug("AppCenter", "Deleting all logs from the Persistence database for " + group); File largePayloadGroupDirectory = getLargePayloadGroupDirectory(group); File[] listFiles = largePayloadGroupDirectory.listFiles(); if (listFiles != null) { for (File file : listFiles) { file.delete(); } } largePayloadGroupDirectory.delete(); AppCenterLog.debug("AppCenter", "Deleted " + this.mDatabaseManager.delete(COLUMN_GROUP, group) + " logs."); Iterator<String> it = this.mPendingDbIdentifiersGroups.keySet().iterator(); while (it.hasNext()) { if (it.next().startsWith(group)) { it.remove(); } } } @Override public int countLogs(String group) { SQLiteQueryBuilder newSQLiteQueryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); newSQLiteQueryBuilder.appendWhere("persistence_group = ?"); int i = 0; try { Cursor cursor = this.mDatabaseManager.getCursor(newSQLiteQueryBuilder, new String[]{"COUNT(*)"}, new String[]{group}, null); try { cursor.moveToNext(); i = cursor.getInt(0); cursor.close(); } catch (Throwable th) { cursor.close(); throw th; } } catch (RuntimeException e) { AppCenterLog.error("AppCenter", "Failed to get logs count: ", e); } return i; } @Override public String getLogs(String group, Collection<String> pausedTargetKeys, int limit, List<Log> outLogs) { Cursor cursor; AppCenterLog.debug("AppCenter", "Trying to get " + limit + " logs from the Persistence database for " + group); SQLiteQueryBuilder newSQLiteQueryBuilder = SQLiteUtils.newSQLiteQueryBuilder(); newSQLiteQueryBuilder.appendWhere("persistence_group = ?"); ArrayList arrayList = new ArrayList(); arrayList.add(group); if (!pausedTargetKeys.isEmpty()) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < pausedTargetKeys.size(); i++) { sb.append("?,"); } sb.deleteCharAt(sb.length() - 1); newSQLiteQueryBuilder.appendWhere(" AND "); newSQLiteQueryBuilder.appendWhere("target_key NOT IN (" + sb.toString() + ")"); arrayList.addAll(pausedTargetKeys); } LinkedHashMap linkedHashMap = new LinkedHashMap(); ArrayList arrayList2 = new ArrayList(); File largePayloadGroupDirectory = getLargePayloadGroupDirectory(group); String[] strArr = (String[]) arrayList.toArray(new String[0]); try { cursor = this.mDatabaseManager.getCursor(newSQLiteQueryBuilder, null, strArr, GET_SORT_ORDER); } catch (RuntimeException e) { AppCenterLog.error("AppCenter", "Failed to get logs: ", e); cursor = null; } int i2 = 0; while (cursor != null) { ContentValues nextValues = this.mDatabaseManager.nextValues(cursor); if (nextValues == null || i2 >= limit) { break; } Long asLong = nextValues.getAsLong(DatabaseManager.PRIMARY_KEY); if (asLong == null) { AppCenterLog.error("AppCenter", "Empty database record, probably content was larger than 2MB, need to delete as it's now corrupted."); Iterator<Long> it = getLogsIds(newSQLiteQueryBuilder, strArr).iterator(); while (true) { if (it.hasNext()) { Long next = it.next(); if (!this.mPendingDbIdentifiers.contains(next) && !linkedHashMap.containsKey(next)) { deleteLog(largePayloadGroupDirectory, next.longValue()); AppCenterLog.error("AppCenter", "Empty database corrupted empty record deleted, id=" + next); break; } } } } else if (!this.mPendingDbIdentifiers.contains(asLong)) { try { String asString = nextValues.getAsString(COLUMN_LOG); if (asString == null) { File largePayloadFile = getLargePayloadFile(largePayloadGroupDirectory, asLong.longValue()); AppCenterLog.debug("AppCenter", "Read payload file " + largePayloadFile); asString = FileManager.read(largePayloadFile); if (asString == null) { throw new JSONException("Log payload is null and not stored as a file."); break; } } Log deserializeLog = getLogSerializer().deserializeLog(asString, nextValues.getAsString("type")); String asString2 = nextValues.getAsString(COLUMN_TARGET_TOKEN); if (asString2 != null) { deserializeLog.addTransmissionTarget(CryptoUtils.getInstance(this.mContext).decrypt(asString2).getDecryptedData()); } linkedHashMap.put(asLong, deserializeLog); i2++; } catch (JSONException e2) { AppCenterLog.error("AppCenter", "Cannot deserialize a log in the database", e2); arrayList2.add(asLong); } } } if (cursor != null) { try { cursor.close(); } catch (RuntimeException unused) { } } if (arrayList2.size() > 0) { Iterator it2 = arrayList2.iterator(); while (it2.hasNext()) { deleteLog(largePayloadGroupDirectory, ((Long) it2.next()).longValue()); } AppCenterLog.warn("AppCenter", "Deleted logs that cannot be deserialized"); } if (linkedHashMap.size() <= 0) { AppCenterLog.debug("AppCenter", "No logs found in the Persistence database at the moment"); return null; } String uuid = UUID.randomUUID().toString(); AppCenterLog.debug("AppCenter", "Returning " + linkedHashMap.size() + " log(s) with an ID, " + uuid); AppCenterLog.debug("AppCenter", "The SID/ID pairs for returning log(s) is/are:"); ArrayList arrayList3 = new ArrayList(); for (Map.Entry entry : linkedHashMap.entrySet()) { Long l = (Long) entry.getKey(); this.mPendingDbIdentifiers.add(l); arrayList3.add(l); outLogs.add((Log) entry.getValue()); AppCenterLog.debug("AppCenter", "\t" + ((Log) entry.getValue()).getSid() + " / " + l); } this.mPendingDbIdentifiersGroups.put(group + uuid, arrayList3); return uuid; } @Override public void clearPendingLogState() { this.mPendingDbIdentifiers.clear(); this.mPendingDbIdentifiersGroups.clear(); AppCenterLog.debug("AppCenter", "Cleared pending log states"); } @Override public void close() { this.mDatabaseManager.close(); } public void deleteLogsThatNotFitMaxSize() { int persistenceFlag = Flags.getPersistenceFlag(1, false); while (getStoredDataSize() >= this.mDatabaseManager.getMaxSize() && deleteTheOldestLog(persistenceFlag) != -1) { } } private long getStoredDataSize() { return this.mDatabaseManager.getCurrentSize() + this.mLargePayloadsSize; } private long deleteTheOldestLog(int priority) { HashSet hashSet = new HashSet(); hashSet.add(DatabaseManager.PRIMARY_KEY); hashSet.add(COLUMN_GROUP); ContentValues deleteTheOldestRecord = this.mDatabaseManager.deleteTheOldestRecord(hashSet, "priority", priority); if (deleteTheOldestRecord == null) { return -1L; } long longValue = deleteTheOldestRecord.getAsLong(DatabaseManager.PRIMARY_KEY).longValue(); File largePayloadFile = getLargePayloadFile(getLargePayloadGroupDirectory(deleteTheOldestRecord.getAsString(COLUMN_GROUP)), longValue); if (!largePayloadFile.exists()) { return longValue; } long length = largePayloadFile.length(); if (largePayloadFile.delete()) { this.mLargePayloadsSize -= length; AppCenterLog.verbose("AppCenter", "Large payload file with id " + longValue + " has been deleted. " + length + " KB of memory has been freed."); } else { AppCenterLog.warn("AppCenter", "Cannot delete large payload file with id " + longValue); } return longValue; } private long checkLargePayloadFilesAndCollectTheirSize() { FilenameFilter filenameFilter = new FilenameFilter() { @Override public boolean accept(File file, String fileName) { return fileName.endsWith(".json"); } }; Set<Long> logsIds = getLogsIds(SQLiteUtils.newSQLiteQueryBuilder(), new String[0]); File[] listFiles = this.mLargePayloadDirectory.listFiles(); long j = 0; if (listFiles == null) { return 0L; } for (File file : listFiles) { File[] listFiles2 = file.listFiles(filenameFilter); if (listFiles2 != null) { for (File file2 : listFiles2) { try { long parseInt = Integer.parseInt(FileManager.getNameWithoutExtension(file2)); if (logsIds.contains(Long.valueOf(parseInt))) { j += file2.length(); } else if (file2.delete()) { AppCenterLog.debug("AppCenter", "Lasted large payload file with name " + file2.getName() + " has been deleted."); } else { AppCenterLog.warn("AppCenter", "Cannot delete redundant large payload file with id " + parseInt); } } catch (NumberFormatException unused) { AppCenterLog.warn("AppCenter", "A file was found whose name does not match the pattern of naming log files: " + file2.getName()); } } } } return j; } private Set<Long> getLogsIds(SQLiteQueryBuilder builder, String... selectionArgs) { HashSet hashSet = new HashSet(); try { Cursor cursor = this.mDatabaseManager.getCursor(builder, DatabaseManager.SELECT_PRIMARY_KEY, selectionArgs, null); while (cursor.moveToNext()) { try { hashSet.add(this.mDatabaseManager.buildValues(cursor).getAsLong(DatabaseManager.PRIMARY_KEY)); } catch (Throwable th) { cursor.close(); throw th; } } cursor.close(); } catch (RuntimeException e) { AppCenterLog.error("AppCenter", "Failed to get corrupted ids: ", e); } return hashSet; } }