2012-08-05 19:41:31 +00:00
|
|
|
/**
|
2011-12-20 18:20:44 +00:00
|
|
|
* Copyright (C) 2011 Whisper Systems
|
2012-08-05 19:41:31 +00:00
|
|
|
*
|
2011-12-20 18:20:44 +00:00
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
2012-08-05 19:41:31 +00:00
|
|
|
*
|
2011-12-20 18:20:44 +00:00
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
package org.thoughtcrime.securesms.service;
|
|
|
|
|
|
|
|
import android.app.Service;
|
|
|
|
import android.content.BroadcastReceiver;
|
|
|
|
import android.content.ComponentName;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.IntentFilter;
|
|
|
|
import android.content.ServiceConnection;
|
|
|
|
import android.os.Handler;
|
|
|
|
import android.os.IBinder;
|
|
|
|
import android.os.Message;
|
|
|
|
import android.util.Log;
|
|
|
|
import android.widget.Toast;
|
|
|
|
|
2012-08-05 19:41:31 +00:00
|
|
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
|
|
|
import org.thoughtcrime.securesms.database.CanonicalSessionMigrator;
|
|
|
|
import org.thoughtcrime.securesms.util.WorkerThread;
|
|
|
|
|
|
|
|
import java.util.Iterator;
|
|
|
|
import java.util.LinkedList;
|
|
|
|
import java.util.List;
|
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
/**
|
|
|
|
* Services that handles sending/receiving of SMS/MMS.
|
2012-08-05 19:41:31 +00:00
|
|
|
*
|
2011-12-20 18:20:44 +00:00
|
|
|
* @author Moxie Marlinspike
|
|
|
|
*/
|
|
|
|
|
|
|
|
public class SendReceiveService extends Service {
|
|
|
|
|
|
|
|
public static final String SEND_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.SEND_SMS_ACTION";
|
|
|
|
public static final String SENT_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.SENT_SMS_ACTION";
|
2013-01-07 05:38:36 +00:00
|
|
|
public static final String DELIVERED_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DELIVERED_SMS_ACTION";
|
2011-12-20 18:20:44 +00:00
|
|
|
public static final String RECEIVE_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.RECEIVE_SMS_ACTION";
|
|
|
|
public static final String SEND_MMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.SEND_MMS_ACTION";
|
|
|
|
public static final String SEND_MMS_CONNECTIVITY_ACTION = "org.thoughtcrime.securesms.SendReceiveService.SEND_MMS_CONNECTIVITY_ACTION";
|
|
|
|
public static final String RECEIVE_MMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.RECEIVE_MMS_ACTION";
|
|
|
|
public static final String DOWNLOAD_MMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DOWNLOAD_MMS_ACTION";
|
|
|
|
public static final String DOWNLOAD_MMS_CONNECTIVITY_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DOWNLOAD_MMS_CONNECTIVITY_ACTION";
|
2013-09-16 07:55:01 +00:00
|
|
|
public static final String DOWNLOAD_MMS_PENDING_APN_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DOWNLOAD_MMS_PENDING_APN_ACTION";
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
private static final int SEND_SMS = 0;
|
|
|
|
private static final int RECEIVE_SMS = 1;
|
|
|
|
private static final int SEND_MMS = 2;
|
|
|
|
private static final int RECEIVE_MMS = 3;
|
|
|
|
private static final int DOWNLOAD_MMS = 4;
|
2013-09-16 07:55:01 +00:00
|
|
|
private static final int DOWNLOAD_MMS_PENDING = 5;
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
private ToastHandler toastHandler;
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2013-04-01 02:16:06 +00:00
|
|
|
private SmsReceiver smsReceiver;
|
|
|
|
private SmsSender smsSender;
|
|
|
|
private MmsReceiver mmsReceiver;
|
|
|
|
private MmsSender mmsSender;
|
|
|
|
private MmsDownloader mmsDownloader;
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
private MasterSecret masterSecret;
|
2013-02-08 19:57:54 +00:00
|
|
|
private boolean hasSecret;
|
|
|
|
|
|
|
|
private NewKeyReceiver newKeyReceiver;
|
|
|
|
private ClearKeyReceiver clearKeyReceiver;
|
2011-12-20 18:20:44 +00:00
|
|
|
private List<Runnable> workQueue;
|
|
|
|
private List<Runnable> pendingSecretList;
|
|
|
|
private Thread workerThread;
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
@Override
|
2013-02-08 19:57:54 +00:00
|
|
|
public void onCreate() {
|
2011-12-20 18:20:44 +00:00
|
|
|
initializeHandlers();
|
|
|
|
initializeProcessors();
|
|
|
|
initializeAddressCanonicalization();
|
|
|
|
initializeWorkQueue();
|
|
|
|
initializeMasterSecret();
|
|
|
|
}
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
@Override
|
|
|
|
public void onStart(Intent intent, int startId) {
|
2012-08-05 19:41:31 +00:00
|
|
|
if (intent == null) return;
|
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
if (intent.getAction().equals(SEND_SMS_ACTION))
|
|
|
|
scheduleSecretRequiredIntent(SEND_SMS, intent);
|
|
|
|
else if (intent.getAction().equals(RECEIVE_SMS_ACTION))
|
|
|
|
scheduleIntent(RECEIVE_SMS, intent);
|
|
|
|
else if (intent.getAction().equals(SENT_SMS_ACTION))
|
2013-02-15 03:15:40 +00:00
|
|
|
scheduleIntent(SEND_SMS, intent);
|
2013-01-07 05:38:36 +00:00
|
|
|
else if (intent.getAction().equals(DELIVERED_SMS_ACTION))
|
2013-02-15 03:15:40 +00:00
|
|
|
scheduleIntent(SEND_SMS, intent);
|
2011-12-20 18:20:44 +00:00
|
|
|
else if (intent.getAction().equals(SEND_MMS_ACTION) || intent.getAction().equals(SEND_MMS_CONNECTIVITY_ACTION))
|
|
|
|
scheduleSecretRequiredIntent(SEND_MMS, intent);
|
|
|
|
else if (intent.getAction().equals(RECEIVE_MMS_ACTION))
|
|
|
|
scheduleIntent(RECEIVE_MMS, intent);
|
|
|
|
else if (intent.getAction().equals(DOWNLOAD_MMS_ACTION) || intent.getAction().equals(DOWNLOAD_MMS_CONNECTIVITY_ACTION))
|
|
|
|
scheduleSecretRequiredIntent(DOWNLOAD_MMS, intent);
|
2013-09-16 07:55:01 +00:00
|
|
|
else if (intent.getAction().equals(DOWNLOAD_MMS_PENDING_APN_ACTION))
|
|
|
|
scheduleSecretRequiredIntent(DOWNLOAD_MMS_PENDING, intent);
|
2012-08-05 19:41:31 +00:00
|
|
|
else
|
2011-12-20 18:20:44 +00:00
|
|
|
Log.w("SendReceiveService", "Received intent with unknown action: " + intent.getAction());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public IBinder onBind(Intent intent) {
|
|
|
|
return null;
|
|
|
|
}
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2013-02-08 19:57:54 +00:00
|
|
|
@Override
|
|
|
|
public void onDestroy() {
|
|
|
|
Log.w("SendReceiveService", "onDestroy()...");
|
|
|
|
super.onDestroy();
|
|
|
|
|
|
|
|
if (newKeyReceiver != null)
|
|
|
|
unregisterReceiver(newKeyReceiver);
|
|
|
|
|
|
|
|
if (clearKeyReceiver != null)
|
|
|
|
unregisterReceiver(clearKeyReceiver);
|
|
|
|
}
|
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
private void initializeHandlers() {
|
|
|
|
toastHandler = new ToastHandler();
|
|
|
|
}
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
private void initializeProcessors() {
|
2013-02-15 03:15:40 +00:00
|
|
|
smsReceiver = new SmsReceiver(this);
|
|
|
|
smsSender = new SmsSender(this, toastHandler);
|
2011-12-20 18:20:44 +00:00
|
|
|
mmsReceiver = new MmsReceiver(this);
|
2013-02-09 23:17:55 +00:00
|
|
|
mmsSender = new MmsSender(this, toastHandler);
|
2011-12-20 18:20:44 +00:00
|
|
|
mmsDownloader = new MmsDownloader(this, toastHandler);
|
|
|
|
}
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
private void initializeWorkQueue() {
|
|
|
|
pendingSecretList = new LinkedList<Runnable>();
|
|
|
|
workQueue = new LinkedList<Runnable>();
|
|
|
|
workerThread = new WorkerThread(workQueue, "SendReceveService-WorkerThread");
|
|
|
|
|
|
|
|
workerThread.start();
|
2012-08-05 19:41:31 +00:00
|
|
|
}
|
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
private void initializeMasterSecret() {
|
2013-02-08 19:57:54 +00:00
|
|
|
hasSecret = false;
|
|
|
|
newKeyReceiver = new NewKeyReceiver();
|
|
|
|
clearKeyReceiver = new ClearKeyReceiver();
|
|
|
|
|
|
|
|
IntentFilter newKeyFilter = new IntentFilter(KeyCachingService.NEW_KEY_EVENT);
|
|
|
|
registerReceiver(newKeyReceiver, newKeyFilter, KeyCachingService.KEY_PERMISSION, null);
|
|
|
|
|
|
|
|
IntentFilter clearKeyFilter = new IntentFilter(KeyCachingService.CLEAR_KEY_EVENT);
|
|
|
|
registerReceiver(clearKeyReceiver, clearKeyFilter, KeyCachingService.KEY_PERMISSION, null);
|
2011-12-20 18:20:44 +00:00
|
|
|
|
|
|
|
Intent bindIntent = new Intent(this, KeyCachingService.class);
|
|
|
|
bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
|
|
|
|
}
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
private void initializeWithMasterSecret(MasterSecret masterSecret) {
|
2013-02-08 19:57:54 +00:00
|
|
|
Log.w("SendReceiveService", "SendReceive service got master secret.");
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
if (masterSecret != null) {
|
|
|
|
synchronized (workQueue) {
|
|
|
|
this.masterSecret = masterSecret;
|
2013-02-08 19:57:54 +00:00
|
|
|
this.hasSecret = true;
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
Iterator<Runnable> iterator = pendingSecretList.iterator();
|
2012-08-05 19:41:31 +00:00
|
|
|
while (iterator.hasNext())
|
2011-12-20 18:20:44 +00:00
|
|
|
workQueue.add(iterator.next());
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
workQueue.notifyAll();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void initializeAddressCanonicalization() {
|
|
|
|
CanonicalSessionMigrator.migrateSessions(this);
|
|
|
|
}
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
private void scheduleIntent(int what, Intent intent) {
|
|
|
|
Runnable work = new SendReceiveWorkItem(intent, what);
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
synchronized (workQueue) {
|
|
|
|
workQueue.add(work);
|
|
|
|
workQueue.notifyAll();
|
|
|
|
}
|
|
|
|
}
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
private void scheduleSecretRequiredIntent(int what, Intent intent) {
|
|
|
|
Runnable work = new SendReceiveWorkItem(intent, what);
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
synchronized (workQueue) {
|
2013-02-08 19:57:54 +00:00
|
|
|
if (hasSecret) {
|
2011-12-20 18:20:44 +00:00
|
|
|
workQueue.add(work);
|
|
|
|
workQueue.notifyAll();
|
2012-08-05 19:41:31 +00:00
|
|
|
} else {
|
|
|
|
pendingSecretList.add(work);
|
2011-12-20 18:20:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
private class SendReceiveWorkItem implements Runnable {
|
|
|
|
private final Intent intent;
|
|
|
|
private final int what;
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
public SendReceiveWorkItem(Intent intent, int what) {
|
|
|
|
this.intent = intent;
|
|
|
|
this.what = what;
|
|
|
|
}
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2013-02-08 19:57:54 +00:00
|
|
|
@Override
|
2011-12-20 18:20:44 +00:00
|
|
|
public void run() {
|
|
|
|
switch (what) {
|
2013-09-16 07:55:01 +00:00
|
|
|
case RECEIVE_SMS: smsReceiver.process(masterSecret, intent); return;
|
|
|
|
case SEND_SMS: smsSender.process(masterSecret, intent); return;
|
|
|
|
case RECEIVE_MMS: mmsReceiver.process(masterSecret, intent); return;
|
|
|
|
case SEND_MMS: mmsSender.process(masterSecret, intent); return;
|
|
|
|
case DOWNLOAD_MMS: mmsDownloader.process(masterSecret, intent); return;
|
|
|
|
case DOWNLOAD_MMS_PENDING: mmsDownloader.process(masterSecret, intent); return;
|
2011-12-20 18:20:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
public class ToastHandler extends Handler {
|
|
|
|
public void makeToast(String toast) {
|
|
|
|
Message message = this.obtainMessage();
|
|
|
|
message.obj = toast;
|
|
|
|
this.sendMessage(message);
|
|
|
|
}
|
|
|
|
@Override
|
2013-02-08 19:57:54 +00:00
|
|
|
public void handleMessage(Message message) {
|
2011-12-20 18:20:44 +00:00
|
|
|
Toast.makeText(SendReceiveService.this, (String)message.obj, Toast.LENGTH_LONG).show();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private ServiceConnection serviceConnection = new ServiceConnection() {
|
2013-02-08 19:57:54 +00:00
|
|
|
@Override
|
2011-12-20 18:20:44 +00:00
|
|
|
public void onServiceConnected(ComponentName className, IBinder service) {
|
|
|
|
KeyCachingService keyCachingService = ((KeyCachingService.KeyCachingBinder)service).getService();
|
|
|
|
MasterSecret masterSecret = keyCachingService.getMasterSecret();
|
|
|
|
|
|
|
|
initializeWithMasterSecret(masterSecret);
|
|
|
|
|
2012-08-05 19:41:31 +00:00
|
|
|
SendReceiveService.this.unbindService(this);
|
2011-12-20 18:20:44 +00:00
|
|
|
}
|
|
|
|
|
2013-02-08 19:57:54 +00:00
|
|
|
@Override
|
2011-12-20 18:20:44 +00:00
|
|
|
public void onServiceDisconnected(ComponentName name) {}
|
|
|
|
};
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2011-12-20 18:20:44 +00:00
|
|
|
private class NewKeyReceiver extends BroadcastReceiver {
|
|
|
|
@Override
|
|
|
|
public void onReceive(Context context, Intent intent) {
|
|
|
|
Log.w("SendReceiveService", "Got a MasterSecret broadcast...");
|
|
|
|
initializeWithMasterSecret((MasterSecret)intent.getParcelableExtra("master_secret"));
|
|
|
|
}
|
2013-02-08 19:57:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This class receives broadcast notifications to clear the MasterSecret.
|
|
|
|
*
|
|
|
|
* We don't want to clear it immediately, since there are potentially jobs
|
|
|
|
* in the work queue which require the master secret. Instead, we reset a
|
|
|
|
* flag so that new incoming jobs will be evaluated as if no mastersecret is
|
|
|
|
* present.
|
|
|
|
*
|
|
|
|
* Then, we add a job to the end of the queue which actually clears the masterSecret
|
|
|
|
* value. That way all jobs before this moment will be processed correctly, and all
|
|
|
|
* jobs after this moment will be evaluated as if no mastersecret is present (and potentially
|
|
|
|
* held).
|
|
|
|
*
|
|
|
|
* When we go to actually clear the mastersecret, we ensure that the flag is still false.
|
|
|
|
* This allows a new mastersecret broadcast to come in correctly without us clobbering it.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
private class ClearKeyReceiver extends BroadcastReceiver {
|
|
|
|
@Override
|
|
|
|
public void onReceive(Context context, Intent intent) {
|
|
|
|
Log.w("SendReceiveService", "Got a clear mastersecret broadcast...");
|
|
|
|
|
|
|
|
synchronized (workQueue) {
|
|
|
|
SendReceiveService.this.hasSecret = false;
|
|
|
|
workQueue.add(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
Log.w("SendReceiveService", "Running clear key work item...");
|
|
|
|
|
|
|
|
synchronized (workQueue) {
|
|
|
|
if (!SendReceiveService.this.hasSecret) {
|
|
|
|
Log.w("SendReceiveService", "Actually clearing key...");
|
|
|
|
SendReceiveService.this.masterSecret = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2012-08-05 19:41:31 +00:00
|
|
|
|
2013-02-08 19:57:54 +00:00
|
|
|
workQueue.notifyAll();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2011-12-20 18:20:44 +00:00
|
|
|
}
|