use of org.thoughtcrime.securesms.service.NotificationController in project Signal-Android by WhisperSystems.
the class AttachmentUploadJob method onRun.
@Override
public void onRun() throws Exception {
if (!Recipient.self().isRegistered()) {
throw new NotPushRegisteredException();
}
Data inputData = getInputData();
ResumableUploadSpec resumableUploadSpec;
if (forceV2) {
Log.d(TAG, "Forcing utilization of V2");
resumableUploadSpec = null;
} else if (inputData != null && inputData.hasString(ResumableUploadSpecJob.KEY_RESUME_SPEC)) {
Log.d(TAG, "Using attachments V3");
resumableUploadSpec = ResumableUploadSpec.deserialize(inputData.getString(ResumableUploadSpecJob.KEY_RESUME_SPEC));
} else {
Log.d(TAG, "Using attachments V2");
resumableUploadSpec = null;
}
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
AttachmentDatabase database = SignalDatabase.attachments();
DatabaseAttachment databaseAttachment = database.getAttachment(attachmentId);
if (databaseAttachment == null) {
throw new InvalidAttachmentException("Cannot find the specified attachment.");
}
long timeSinceUpload = System.currentTimeMillis() - databaseAttachment.getUploadTimestamp();
if (timeSinceUpload < UPLOAD_REUSE_THRESHOLD && !TextUtils.isEmpty(databaseAttachment.getLocation())) {
Log.i(TAG, "We can re-use an already-uploaded file. It was uploaded " + timeSinceUpload + " ms ago. Skipping.");
return;
} else if (databaseAttachment.getUploadTimestamp() > 0) {
Log.i(TAG, "This file was previously-uploaded, but too long ago to be re-used. Age: " + timeSinceUpload + " ms");
}
Log.i(TAG, "Uploading attachment for message " + databaseAttachment.getMmsId() + " with ID " + databaseAttachment.getAttachmentId());
try (NotificationController notification = getNotificationForAttachment(databaseAttachment)) {
SignalServiceAttachment localAttachment = getAttachmentFor(databaseAttachment, notification, resumableUploadSpec);
SignalServiceAttachmentPointer remoteAttachment = messageSender.uploadAttachment(localAttachment.asStream());
Attachment attachment = PointerAttachment.forPointer(Optional.of(remoteAttachment), null, databaseAttachment.getFastPreflightId()).get();
database.updateAttachmentAfterUpload(databaseAttachment.getAttachmentId(), attachment, remoteAttachment.getUploadTimestamp());
} catch (NonSuccessfulResumableUploadResponseCodeException e) {
if (e.getCode() == 400) {
Log.w(TAG, "Failed to upload due to a 400 when getting resumable upload information. Downgrading to attachments v2", e);
forceV2 = true;
}
}
}
use of org.thoughtcrime.securesms.service.NotificationController in project Signal-Android by WhisperSystems.
the class LocalBackupJob method onRun.
@Override
public void onRun() throws NoExternalStorageException, IOException {
Log.i(TAG, "Executing backup job...");
BackupFileIOError.clearNotification(context);
if (!Permissions.hasAll(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
throw new IOException("No external storage permission!");
}
ProgressUpdater updater = new ProgressUpdater();
try (NotificationController notification = GenericForegroundService.startForegroundTask(context, context.getString(R.string.LocalBackupJob_creating_signal_backup), NotificationChannels.BACKUPS, R.drawable.ic_signal_backup)) {
updater.setNotification(notification);
EventBus.getDefault().register(updater);
notification.setIndeterminateProgress();
String backupPassword = BackupPassphrase.get(context);
File backupDirectory = StorageUtil.getOrCreateBackupDirectory();
String timestamp = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US).format(new Date());
String fileName = String.format("signal-%s.backup", timestamp);
File backupFile = new File(backupDirectory, fileName);
deleteOldTemporaryBackups(backupDirectory);
if (backupFile.exists()) {
throw new IOException("Backup file already exists?");
}
if (backupPassword == null) {
throw new IOException("Backup password is null");
}
File tempFile = File.createTempFile(TEMP_BACKUP_FILE_PREFIX, TEMP_BACKUP_FILE_SUFFIX, backupDirectory);
try {
FullBackupExporter.export(context, AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(), SignalDatabase.getBackupDatabase(), tempFile, backupPassword, this::isCanceled);
if (!tempFile.renameTo(backupFile)) {
Log.w(TAG, "Failed to rename temp file");
throw new IOException("Renaming temporary backup file failed!");
}
} catch (FullBackupExporter.BackupCanceledException e) {
Log.w(TAG, "Backup cancelled");
throw e;
} catch (IOException e) {
BackupFileIOError.postNotificationForException(context, e, getRunAttempt());
throw e;
} finally {
if (tempFile.exists()) {
if (tempFile.delete()) {
Log.w(TAG, "Backup failed. Deleted temp file");
} else {
Log.w(TAG, "Backup failed. Failed to delete temp file " + tempFile);
}
}
}
BackupUtil.deleteOldBackups();
} finally {
EventBus.getDefault().unregister(updater);
updater.setNotification(null);
}
}
use of org.thoughtcrime.securesms.service.NotificationController in project Signal-Android by WhisperSystems.
the class LocalBackupJobApi29 method onRun.
@Override
public void onRun() throws IOException {
Log.i(TAG, "Executing backup job...");
BackupFileIOError.clearNotification(context);
if (!BackupUtil.isUserSelectionRequired(context)) {
throw new IOException("Wrong backup job!");
}
Uri backupDirectoryUri = SignalStore.settings().getSignalBackupDirectory();
if (backupDirectoryUri == null || backupDirectoryUri.getPath() == null) {
throw new IOException("Backup Directory has not been selected!");
}
ProgressUpdater updater = new ProgressUpdater();
try (NotificationController notification = GenericForegroundService.startForegroundTask(context, context.getString(R.string.LocalBackupJob_creating_signal_backup), NotificationChannels.BACKUPS, R.drawable.ic_signal_backup)) {
updater.setNotification(notification);
EventBus.getDefault().register(updater);
notification.setIndeterminateProgress();
String backupPassword = BackupPassphrase.get(context);
DocumentFile backupDirectory = DocumentFile.fromTreeUri(context, backupDirectoryUri);
String timestamp = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US).format(new Date());
String fileName = String.format("signal-%s.backup", timestamp);
if (backupDirectory == null || !backupDirectory.canWrite()) {
BackupFileIOError.ACCESS_ERROR.postNotification(context);
throw new IOException("Cannot write to backup directory location.");
}
deleteOldTemporaryBackups(backupDirectory);
if (backupDirectory.findFile(fileName) != null) {
throw new IOException("Backup file already exists!");
}
String temporaryName = String.format(Locale.US, "%s%s%s", TEMP_BACKUP_FILE_PREFIX, UUID.randomUUID(), TEMP_BACKUP_FILE_SUFFIX);
DocumentFile temporaryFile = backupDirectory.createFile("application/octet-stream", temporaryName);
if (temporaryFile == null) {
throw new IOException("Failed to create temporary backup file.");
}
if (backupPassword == null) {
throw new IOException("Backup password is null");
}
try {
FullBackupExporter.export(context, AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret(), SignalDatabase.getBackupDatabase(), temporaryFile, backupPassword, this::isCanceled);
if (!temporaryFile.renameTo(fileName)) {
Log.w(TAG, "Failed to rename temp file");
throw new IOException("Renaming temporary backup file failed!");
}
} catch (FullBackupExporter.BackupCanceledException e) {
Log.w(TAG, "Backup cancelled");
throw e;
} catch (IOException e) {
Log.w(TAG, "Error during backup!", e);
BackupFileIOError.postNotificationForException(context, e, getRunAttempt());
throw e;
} finally {
DocumentFile fileToCleanUp = backupDirectory.findFile(temporaryName);
if (fileToCleanUp != null) {
if (fileToCleanUp.delete()) {
Log.w(TAG, "Backup failed. Deleted temp file");
} else {
Log.w(TAG, "Backup failed. Failed to delete temp file " + temporaryName);
}
}
}
BackupUtil.deleteOldBackups();
} finally {
EventBus.getDefault().unregister(updater);
updater.setNotification(null);
}
}
use of org.thoughtcrime.securesms.service.NotificationController in project Signal-Android by WhisperSystems.
the class AttachmentCompressionJob method transcodeVideoIfNeededToDatabase.
@NonNull
private static DatabaseAttachment transcodeVideoIfNeededToDatabase(@NonNull Context context, @NonNull AttachmentDatabase attachmentDatabase, @NonNull DatabaseAttachment attachment, @NonNull MediaConstraints constraints, @NonNull EventBus eventBus, @NonNull TranscoderCancelationSignal cancelationSignal) throws UndeliverableMessageException {
AttachmentDatabase.TransformProperties transformProperties = attachment.getTransformProperties();
boolean allowSkipOnFailure = false;
if (!MediaConstraints.isVideoTranscodeAvailable()) {
if (transformProperties.isVideoEdited()) {
throw new UndeliverableMessageException("Video edited, but transcode is not available");
}
return attachment;
}
try (NotificationController notification = GenericForegroundService.startForegroundTask(context, context.getString(R.string.AttachmentUploadJob_compressing_video_start))) {
notification.setIndeterminateProgress();
try (MediaDataSource dataSource = attachmentDatabase.mediaDataSourceFor(attachment.getAttachmentId())) {
if (dataSource == null) {
throw new UndeliverableMessageException("Cannot get media data source for attachment.");
}
allowSkipOnFailure = !transformProperties.isVideoEdited();
TranscoderOptions options = null;
if (transformProperties.isVideoTrim()) {
options = new TranscoderOptions(transformProperties.getVideoTrimStartTimeUs(), transformProperties.getVideoTrimEndTimeUs());
}
if (FeatureFlags.useStreamingVideoMuxer() || !MemoryFileDescriptor.supported()) {
StreamingTranscoder transcoder = new StreamingTranscoder(dataSource, options, constraints.getCompressedVideoMaxSize(context));
if (transcoder.isTranscodeRequired()) {
Log.i(TAG, "Compressing with streaming muxer");
AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret();
File file = SignalDatabase.attachments().newFile();
file.deleteOnExit();
try {
try (OutputStream outputStream = ModernEncryptingPartOutputStream.createFor(attachmentSecret, file, true).second) {
transcoder.transcode(percent -> {
notification.setProgress(100, percent);
eventBus.postSticky(new PartProgressEvent(attachment, PartProgressEvent.Type.COMPRESSION, 100, percent));
}, outputStream, cancelationSignal);
}
MediaStream mediaStream = new MediaStream(ModernDecryptingPartInputStream.createFor(attachmentSecret, file, 0), MimeTypes.VIDEO_MP4, 0, 0);
attachmentDatabase.updateAttachmentData(attachment, mediaStream, transformProperties.isVideoEdited());
} finally {
if (!file.delete()) {
Log.w(TAG, "Failed to delete temp file");
}
}
attachmentDatabase.markAttachmentAsTransformed(attachment.getAttachmentId());
return Objects.requireNonNull(attachmentDatabase.getAttachment(attachment.getAttachmentId()));
} else {
Log.i(TAG, "Transcode was not required");
}
} else {
try (InMemoryTranscoder transcoder = new InMemoryTranscoder(context, dataSource, options, constraints.getCompressedVideoMaxSize(context))) {
if (transcoder.isTranscodeRequired()) {
Log.i(TAG, "Compressing with android in-memory muxer");
MediaStream mediaStream = transcoder.transcode(percent -> {
notification.setProgress(100, percent);
eventBus.postSticky(new PartProgressEvent(attachment, PartProgressEvent.Type.COMPRESSION, 100, percent));
}, cancelationSignal);
attachmentDatabase.updateAttachmentData(attachment, mediaStream, transformProperties.isVideoEdited());
attachmentDatabase.markAttachmentAsTransformed(attachment.getAttachmentId());
return Objects.requireNonNull(attachmentDatabase.getAttachment(attachment.getAttachmentId()));
} else {
Log.i(TAG, "Transcode was not required (in-memory transcoder)");
}
}
}
}
} catch (VideoSourceException | EncodingException | MemoryFileException e) {
if (attachment.getSize() > constraints.getVideoMaxSize(context)) {
throw new UndeliverableMessageException("Duration not found, attachment too large to skip transcode", e);
} else {
if (allowSkipOnFailure) {
Log.w(TAG, "Problem with video source, but video small enough to skip transcode", e);
} else {
throw new UndeliverableMessageException("Failed to transcode and cannot skip due to editing", e);
}
}
} catch (IOException | MmsException e) {
throw new UndeliverableMessageException("Failed to transcode", e);
}
return attachment;
}
Aggregations