package eu.siacs.conversations.services; import android.app.AlarmManager; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.IBinder; import android.support.v4.app.NotificationCompat; import android.preference.PreferenceManager; import android.util.Log; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.Calendar; import java.util.concurrent.atomic.AtomicBoolean; import javax.crypto.NoSuchPaddingException; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.persistance.DatabaseBackend; import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.utils.EncryptDecryptFile; public class BackupService extends Service { private static final int NOTIFICATION_ID = 1; private static AtomicBoolean running = new AtomicBoolean(false); private AlarmManager alarmMgr; private PendingIntent alarmIntent; @Override public void onCreate() { /* alarmIntent = PendingIntent.getService(this, 0, new Intent(this, BackupService.class), 0); Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); calendar.set(Calendar.HOUR_OF_DAY, 4); // With setInexactRepeating(), you have to use one of the AlarmManager interval // constants--in this case, AlarmManager.INTERVAL_DAY. alarmMgr = (AlarmManager)(this.getSystemService(Context.ALARM_SERVICE)); alarmMgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, alarmIntent); */ } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (running.compareAndSet(false, true)) { new Thread(() -> { export(); stopForeground(true); running.set(false); stopSelf(); }).start(); } return START_NOT_STICKY; } private void export() { try{ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); String encryptionKey = prefs.getString("backup_password", null); if (encryptionKey == null || encryptionKey.length() < 3) { Log.d(Config.LOGTAG, "BackupService: failed to write encryted backup to sdcard because of missing password"); return; } NotificationManager mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext(), "backup"); mBuilder.setContentTitle(getString(R.string.notification_backup_create)) .setSmallIcon(R.drawable.ic_import_export_white_24dp); startForeground(NOTIFICATION_ID, mBuilder.build()); Log.d(Config.LOGTAG, "BackupService: start creating backup"); // Get hold of the db: FileInputStream inputFile = new FileInputStream(this.getDatabasePath(DatabaseBackend.DATABASE_NAME)); // Set the output folder on the SDcard File directory = new File(FileBackend.getBackupDirectory()); // Create the folder if it doesn't exist: if (!directory.exists()) { boolean directory_created = directory.mkdirs(); Log.d(Config.LOGTAG, "BackupService: backup directory created " + directory_created); } //Delete old database export file File tempDBFile = new File(directory + "/database.bak"); if (tempDBFile.exists()) { Log.d(Config.LOGTAG, "BackupService: Delete temp database backup file from " + tempDBFile.toString()); boolean temp_db_file_deleted = tempDBFile.delete(); Log.d(Config.LOGTAG, "BackupService: old backup file deleted " + temp_db_file_deleted); } // Set the output file stream up: FileOutputStream outputFile = new FileOutputStream(directory.getPath() + "/database.db.crypt"); // encrypt database from the input file to the output file try { EncryptDecryptFile.encrypt(inputFile, outputFile, encryptionKey); Log.d(Config.LOGTAG, "BackupService: starting encrypted output to " + outputFile.toString()); } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { Log.d(Config.LOGTAG, "BackupService: Database exporter: encryption failed with " + e); e.printStackTrace(); } catch (InvalidKeyException e) { Log.d(Config.LOGTAG, "BackupService: Database exporter: encryption failed (invalid key) with " + e); e.printStackTrace(); } catch (IOException e) { Log.d(Config.LOGTAG, "BackupService: Database exporter: encryption failed (IO) with " + e); e.printStackTrace(); } finally { Log.d(Config.LOGTAG, "BackupService: backup job finished"); } mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build()); } catch (IOException e) { e.printStackTrace(); } } @Override public IBinder onBind(Intent intent) { return null; } }