@Override public Boolean call() throws Exception { return dbHelper.addCompoundMessageFiles(associatedFiles); } });
/** * This method is called when an app is upgraded. Add alter table statements here for each version in a non-breaking * switch, so that all the necessary upgrades occur for each older version. */ @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { ApptentiveLog.d(DATABASE, "Upgrade database from %d to %d", oldVersion, newVersion); try { DatabaseMigrator migrator = createDatabaseMigrator(oldVersion, newVersion); if (migrator != null) { migrator.onUpgrade(db, oldVersion, newVersion); } } catch (Exception e) { ApptentiveLog.e(DATABASE, e, "Exception while trying to migrate database from %d to %d", oldVersion, newVersion); logException(e); // if migration failed - create new table db.execSQL(SQL_DELETE_PAYLOAD_TABLE); onCreate(db); } }
private String tryDecryptString(byte[] bytes, String defaultValue, boolean printError) { try { return decryptString(bytes); } catch (Exception e) { if (printError) { ApptentiveLog.e(e, "Failed to decrypt string"); } return defaultValue; } }
private void removeCorruptedPayloads() { Cursor cursor = null; try { SQLiteDatabase db = getWritableDatabase(); cursor = db.rawQuery(SQL_REMOVE_INCOMPLETE_PAYLOADS, null); cursor.moveToFirst(); // we need to move a cursor in order to update database ApptentiveLog.v(DATABASE, "Removed incomplete payloads"); } catch (SQLException e) { ApptentiveLog.e(e, "Exception while removing incomplete payloads"); logException(e); } finally { ensureClosed(cursor); } }
PayloadData getOldestUnsentPayload() { if (ApptentiveLog.canLog(ApptentiveLog.Level.VERBOSE)) { printPayloadTable("getOldestUnsentPayload"); Cursor cursor = null; try { db = getWritableDatabase(); cursor = db.rawQuery(SQL_SELECT_PAYLOADS_IN_SEND_ORDER, null); int count = cursor.getCount(); final String authToken = tryDecryptString(cursor.getBlob(PayloadEntry.COLUMN_AUTH_TOKEN.index), ""); if (authToken != null && authToken.length() == 0) { ApptentiveLog.w(PAYLOADS, "Oldest unsent payload auth token can't be decrypted. Deleting..."); deletePayload(nonce); continue; deletePayload(nonce); continue; final String httpRequestPath = updatePayloadRequestPath(cursor.getString(PayloadEntry.COLUMN_PATH.index), conversationId); File file = getPayloadBodyFile(nonce); if (!file.exists()) { ApptentiveLog.w(PAYLOADS, "Oldest unsent payload had no data file. Deleting..."); deletePayload(nonce); continue; byte[] data = tryReadFromFile(file, !authenticated); // only anonymous payloads get encrypted upon write (authenticated payloads get encrypted on serialization) if (data == null) { ApptentiveLog.w(PAYLOADS, "Oldest unsent payload file can't be read. Deleting...");
void updateIncompletePayloads(String conversationId, String authToken, String localConversationId, boolean legacyPayloads) { if (ApptentiveLog.canLog(ApptentiveLog.Level.VERBOSE)) { printPayloadTable("updateIncompletePayloads BEFORE"); } if (StringUtils.isNullOrEmpty(conversationId)) { throw new IllegalArgumentException("Conversation id is null or empty"); } if (StringUtils.isNullOrEmpty(authToken)) { throw new IllegalArgumentException("Token is null or empty"); } try { SQLiteDatabase db = getWritableDatabase(); db.execSQL(legacyPayloads ? SQL_UPDATE_LEGACY_PAYLOADS : SQL_UPDATE_INCOMPLETE_PAYLOADS, new Object[] { encrypt(authToken), conversationId, localConversationId }); ApptentiveLog.v(DATABASE, "Updated missing conversation ids"); } catch (Exception e) { ApptentiveLog.e(e, "Exception while updating missing conversation ids"); logException(e); } // remove incomplete payloads which don't belong to an active conversation removeCorruptedPayloads(); if (ApptentiveLog.canLog(ApptentiveLog.Level.VERBOSE)) { printPayloadTable("updateIncompletePayloads AFTER"); } }
SQLiteDatabase db = null; try { db = getWritableDatabase(); db.beginTransaction(); values.put(PayloadEntry.COLUMN_AUTH_TOKEN.name, encrypt(payload.getConversationToken())); // might be null ); File dest = getPayloadBodyFile(payload.getNonce()); ApptentiveLog.v(DATABASE, "Saving payload body to: %s", dest); writeToFile(dest, payload.renderData(), !payload.isAuthenticated()); // only anonymous payloads get encrypted upon write (authenticated payloads get encrypted on serialization) printPayloadTable("Added payload");
Cursor cursor = null; try { db = getWritableDatabase(); cursor = db.rawQuery(SQL_SELECT_PAYLOADS_IN_SEND_ORDER, null); int payloadCount = cursor.getCount(); cursor.getInt(PayloadEntry.COLUMN_AUTHENTICATED.index), cursor.getString(PayloadEntry.COLUMN_LOCAL_CONVERSATION_ID.index), hideIfSanitized(tryDecryptString(cursor.getBlob(PayloadEntry.COLUMN_AUTH_TOKEN.index), "<CORRUPTED>", false)) }; } catch (Exception e) { ApptentiveLog.e(CONVERSATION, e, "Exception while printing metadata"); logException(e); } finally { ensureClosed(cursor);
List<StoredFile> getAssociatedFiles(String nonce) { SQLiteDatabase db = null; Cursor cursor = null; List<StoredFile> associatedFiles = new ArrayList<StoredFile>(); try { db = getReadableDatabase(); cursor = db.rawQuery(QUERY_MESSAGE_FILES_GET_BY_NONCE, new String[]{nonce}); StoredFile ret; if (cursor.moveToFirst()) { do { ret = new StoredFile(); ret.setId(nonce); ret.setLocalFilePath(cursor.getString(2)); ret.setMimeType(cursor.getString(3)); ret.setSourceUriOrPath(cursor.getString(4)); ret.setApptentiveUri(cursor.getString(5)); ret.setCreationTime(cursor.getLong(6)); associatedFiles.add(ret); } while (cursor.moveToNext()); } } catch (SQLException sqe) { ApptentiveLog.e(DATABASE, "getAssociatedFiles EXCEPTION: " + sqe.getMessage()); logException(sqe); } finally { ensureClosed(cursor); } return associatedFiles.size() > 0 ? associatedFiles : null; }
@Override public void run() { try { dbHelper.addPayload(payload); sendNextPayloadSync(); } catch (Exception e) { ApptentiveLog.e(PAYLOADS, e, "Exception while adding a payload: %s", payload); logException(e); } } });
@Override public void run() { try { dbHelper.deletePayload(payloadIdentifier); sendNextPayloadSync(); } catch (Exception e) { ApptentiveLog.e(PAYLOADS, e, "Exception while deleting a payload: %s", payloadIdentifier); logException(e); } } });
@Override public void run() { try { dbHelper.deleteAllPayloads(); } catch (Exception e) { ApptentiveLog.e(PAYLOADS, e, "Exception while deleting all payloads"); logException(e); } } });
@Override public void run() { try { dbHelper.deleteAssociatedFiles(messageNonce); } catch (Exception e) { ApptentiveLog.e(PAYLOADS, e, "Exception while deleting associated file: %s", messageNonce); logException(e); } } });
public ApptentiveTaskManager(Context context, ApptentiveHttpClient apptentiveHttpClient, EncryptionKey encryptionKey) { dbHelper = new ApptentiveDatabaseHelper(context, encryptionKey); /* When a new database task is submitted, the executor has the following behaviors: * 1. If the thread pool has no thread yet, it creates a single worker thread. * 2. If the single worker thread is running with tasks, it queues tasks. * 3. If the queue is full, the task will be rejected and run on caller thread. * */ singleThreadExecutor = new ThreadPoolExecutor(1, 1, 30L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadPoolExecutor.CallerRunsPolicy()); // If no new task arrives in 30 seconds, the worker thread terminates; otherwise it will be reused singleThreadExecutor.allowCoreThreadTimeOut(true); // Create payload sender object with a custom 'retry' policy payloadSender = new PayloadSender(apptentiveHttpClient, new HttpRequestRetryPolicyDefault() { @Override public boolean shouldRetryRequest(int responseCode, int retryAttempt) { return false; // don't use built-in retry logic for payloads since payload sender listener // would handle it properly } }); payloadSender.setListener(this); ApptentiveNotificationCenter.defaultCenter() .addObserver(NOTIFICATION_CONVERSATION_STATE_DID_CHANGE, this) .addObserver(NOTIFICATION_APP_ENTERED_BACKGROUND, this) .addObserver(NOTIFICATION_APP_ENTERED_FOREGROUND, this); }