package uk.co.thomseddon.relay; import android.app.Service; import android.content.ContentValues; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.Handler; import android.os.IBinder; import android.provider.ContactsContract; import android.telephony.SmsManager; import android.util.Log; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.java_websocket.client.WebSocketClient; import org.java_websocket.handshake.ServerHandshake; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Calendar; /** * Created by thom on 4/14/14. */ public class SocketService extends Service { private static String TAG = "RE"; WebSocketClient mWebSocketClient; ObjectMapper mMapper = new ObjectMapper(); private static int DIRTY_INTERVAL = 5 * 1000; // 5 Seconds private Long mDirtyLoopLastTime = 0L; private Handler mDirtyLooper; private Runnable mDirtyLoop; private Calendar mCalendar; @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "Service Started"); // if (mReceiver == null) // initReceiver(); if (mCalendar == null) mCalendar = Calendar.getInstance(); if (mDirtyLoop == null) initDirtyLoop(); if (mWebSocketClient != null) mWebSocketClient.close(); // Attempt to connect to websocket if (intent != null && reconnect(intent.getStringExtra("uri"))) { return START_STICKY; } else { return START_NOT_STICKY; } } @Override public IBinder onBind(Intent intent) { return null; } private boolean reconnect(final String host) { URI uri; try { Log.i(TAG, host); uri = new URI(host); } catch (URISyntaxException e) { e.printStackTrace(); return false; } mWebSocketClient = new WebSocketClient(uri) { @Override public void onOpen(ServerHandshake serverHandshake) { Log.i(TAG, "WS open"); mDirtyLoop.run(); } @Override public void onMessage(String message) { int pos = message.indexOf(" "); String name = pos == -1 ? message : message.substring(0, pos); String data = pos == -1 ? null : message.substring(pos + 1); Log.i(TAG, "WS Message: " + name); switch (name) { case "client:listSMS": getSMS(data); break; case "client:listContacts": getContacts(); break; case "client:sendText": Log.i(TAG, data); sendText(data); break; } } @Override public void onClose(int i, String s, boolean b) { Log.i(TAG, "WS close: " + s); mDirtyLooper.removeCallbacks(mDirtyLoop); // TODO: Exponential back-off/rediscovery reconnect(host); } @Override public void onError(Exception e) { Log.i(TAG, "WS onError"); e.printStackTrace(); } }; mWebSocketClient.connect(); return true; } private void getContacts() { Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI; if (uri == null) return; Cursor cursor = getContentResolver().query(uri, null, null, null, null); if (cursor == null) return; ArrayList<Contact> contacts = new ArrayList<>(); cursor.moveToFirst(); do { if (cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)) <= 0) continue; Contact contact = new Contact(); contact._id = cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts._ID)); contact.name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)); contact.number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); contacts.add(contact); } while (cursor.moveToNext()); cursor.close(); // Serialize and send try { mWebSocketClient.send("device:listContacts " + mMapper.writeValueAsString(contacts)); } catch (JsonProcessingException e) { e.printStackTrace(); } } private void getSMS() { getSMS(null); } private void getSMS(String query) { ArrayList<SMS> smss = new ArrayList<>(); // Inbox querySMS("content://sms/inbox", query, smss); // Sent querySMS("content://sms/sent", query, smss); // Don't bother if it's empty if (smss.size() == 0) return; // Serialize and send try { mWebSocketClient.send("device:listSMS " + mMapper.writeValueAsString(smss)); } catch (JsonProcessingException e) { e.printStackTrace(); } } private void querySMS (String uri, String query, ArrayList<SMS> smss) { // Log.i("RE", "Query: " + query); // TODO: Add projection Cursor cursor = getContentResolver().query(Uri.parse(uri), null, query, null, null); if (cursor == null) return; if (!cursor.moveToFirst()) { // No results cursor.close(); return; } do { SMS sms = new SMS(); sms._id = cursor.getInt(cursor.getColumnIndex("_id")); sms.threadId = cursor.getInt(cursor.getColumnIndex("thread_id")); sms.address = cursor.getString(cursor.getColumnIndex("address")); sms.date = cursor.getLong(cursor.getColumnIndex("date")) / 1000; sms.dateSent = cursor.getLong(cursor.getColumnIndex("date_sent")) / 1000; sms.read = cursor.getInt(cursor.getColumnIndex("read")); sms.type = cursor.getInt(cursor.getColumnIndex("type")); sms.body = cursor.getString(cursor.getColumnIndex("body")); sms.seen = cursor.getInt(cursor.getColumnIndex("seen")); smss.add(sms); // Log.i("RE", "SMS: " + sms.body + ", date:" + sms.date); } while (cursor.moveToNext()); cursor.close(); } private void sendText(String data) { try { SMS sms = mMapper.readValue(data, SMS.class); // Send SmsManager smsManager = SmsManager.getDefault(); smsManager.sendTextMessage(sms.address, null, sms.body, null, null); ContentValues values = new ContentValues(); values.put("address", sms.address); values.put("body", sms.body); getContentResolver().insert(Uri.parse("content://sms/sent"), values); } catch (IOException e) { e.printStackTrace(); } } private void initDirtyLoop() { mDirtyLooper = new Handler(); mDirtyLoopLastTime = mCalendar.getTimeInMillis(); mDirtyLoop = new Runnable() { @Override public void run() { // Get SMS ArrayList<SMS> smss = new ArrayList<>(); querySMS("content://sms/inbox", "date > " + mDirtyLoopLastTime, smss); querySMS("content://sms/sent", "date > " + mDirtyLoopLastTime, smss); if (smss.size() == 0) { SMS sms = new SMS(); sms._id = 123; sms.threadId = 123; sms.address = "+447970314392"; sms.date = mDirtyLoopLastTime; sms.dateSent = mDirtyLoopLastTime; sms.read = 0; sms.type = 0; sms.body = "TEST NEW"; sms.seen = 0; smss.add(sms); } // Fake "new message" for (SMS sms : smss) { try { mWebSocketClient.send("device:newSMS " + mMapper.writeValueAsString(sms)); } catch (IOException e) { e.printStackTrace(); } } mDirtyLoopLastTime = mCalendar.getTimeInMillis(); mDirtyLooper.postDelayed(mDirtyLoop, DIRTY_INTERVAL); } }; } // private final BroadcastReceiver mReceiver = new BroadcastReceiver() { // @Override // public void onReceive(Context context, Intent intent) { // String action = intent.getAction(); // Log.i(TAG, "GOT SMS, action: " + action); // switch (action) { // case "android.provider.Telephony.SMS_RECEIVED": // getSMS(); // break; // } // } // }; // private void initReceiver() { // Log.i(TAG, "initReceiver"); // IntentFilter intentFilter = new IntentFilter(); // intentFilter.addAction("android.provider.Telephony.SMS_RECEIVED"); // registerReceiver(mReceiver, intentFilter); // } }