Improve mcs connection

wake up device when reconnecting, fix teardown incomplete on null message (#24)
This commit is contained in:
mar-v-in 2015-08-17 23:28:01 +02:00
parent c826702bb9
commit 1a83f8281f
4 changed files with 77 additions and 43 deletions

View File

@ -55,7 +55,7 @@
<permission <permission
android:name="org.microg.gms.STATUS_BROADCAST" android:name="org.microg.gms.STATUS_BROADCAST"
android:label="@string/perm_status_broadcast_label" android:label="@string/perm_status_broadcast_label"
android:protectionLevel="dangerous" /> android:protectionLevel="normal" />
<uses-permission android:name="android.permission.FAKE_PACKAGE_SIGNATURE" /> <uses-permission android:name="android.permission.FAKE_PACKAGE_SIGNATURE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

View File

@ -42,6 +42,7 @@ import static org.microg.gms.gcm.Constants.MCS_LOGIN_REQUEST_TAG;
import static org.microg.gms.gcm.Constants.MCS_LOGIN_RESPONSE_TAG; import static org.microg.gms.gcm.Constants.MCS_LOGIN_RESPONSE_TAG;
import static org.microg.gms.gcm.Constants.MSG_INPUT; import static org.microg.gms.gcm.Constants.MSG_INPUT;
import static org.microg.gms.gcm.Constants.MSG_INPUT_ERROR; import static org.microg.gms.gcm.Constants.MSG_INPUT_ERROR;
import static org.microg.gms.gcm.Constants.MSG_TEARDOWN;
public class McsInputStream extends Thread { public class McsInputStream extends Thread {
private static final String TAG = "GmsGcmMcsInput"; private static final String TAG = "GmsGcmMcsInput";
@ -70,15 +71,20 @@ public class McsInputStream extends Thread {
public void run() { public void run() {
try { try {
while (!Thread.currentThread().isInterrupted()) { while (!Thread.currentThread().isInterrupted()) {
mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_INPUT, read())); Message msg = read();
if (msg != null) {
mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_INPUT, msg));
} else {
mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_TEARDOWN, "null message"));
}
} }
} catch (IOException e) { } catch (IOException e) {
try {
is.close();
} catch (IOException ignored) {
}
mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_INPUT_ERROR, e)); mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_INPUT_ERROR, e));
} }
try {
is.close();
} catch (IOException ignored) {
}
} }
public void close() { public void close() {
@ -115,7 +121,10 @@ public class McsInputStream extends Thread {
ensureVersionRead(); ensureVersionRead();
int mcsTag = is.read(); int mcsTag = is.read();
int mcsSize = readVarint(); int mcsSize = readVarint();
if (mcsTag < 0 || mcsSize < 0) return null; if (mcsTag < 0 || mcsSize < 0) {
Log.w(TAG, "mcsTag: " + mcsTag + " mcsSize: " + mcsSize);
return null;
}
byte[] bytes = new byte[mcsSize]; byte[] bytes = new byte[mcsSize];
int len = 0, read = 0; int len = 0, read = 0;
while (len < mcsSize && read >= 0) { while (len < mcsSize && read >= 0) {

View File

@ -26,6 +26,7 @@ import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.SystemClock; import android.os.SystemClock;
import android.support.v4.content.WakefulBroadcastReceiver;
import android.util.Log; import android.util.Log;
import com.squareup.wire.Message; import com.squareup.wire.Message;
@ -82,12 +83,15 @@ public class McsService extends IntentService implements Handler.Callback {
private static MainThread mainThread; private static MainThread mainThread;
private static Handler mainHandler; private static Handler mainHandler;
private boolean initialized = false;
private AlarmManager alarmManager; private AlarmManager alarmManager;
private PowerManager powerManager; private PowerManager powerManager;
private static PowerManager.WakeLock wakeLock; private static PowerManager.WakeLock wakeLock;
private static int delay = 0;
private Intent connectIntent;
public McsService() { public McsService() {
super(TAG); super(TAG);
} }
@ -96,8 +100,15 @@ public class McsService extends IntentService implements Handler.Callback {
@Override @Override
public void run() { public void run() {
Looper.prepare(); Looper.prepare();
mainHandler = new Handler(Looper.myLooper(), McsService.this); wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "mcs");
mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_CONNECT)); wakeLock.setReferenceCounted(false);
synchronized (McsService.class) {
mainHandler = new Handler(Looper.myLooper(), McsService.this);
if (connectIntent != null) {
mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_CONNECT, connectIntent));
WakefulBroadcastReceiver.completeWakefulIntent(connectIntent);
}
}
Looper.loop(); Looper.loop();
} }
} }
@ -105,36 +116,42 @@ public class McsService extends IntentService implements Handler.Callback {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
if (mainThread == null) {
mainThread = new MainThread();
mainThread.start();
}
heartbeatIntent = PendingIntent.getService(this, 0, new Intent(ACTION_HEARTBEAT, null, this, McsService.class), 0); heartbeatIntent = PendingIntent.getService(this, 0, new Intent(ACTION_HEARTBEAT, null, this, McsService.class), 0);
alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
powerManager = (PowerManager) getSystemService(POWER_SERVICE); powerManager = (PowerManager) getSystemService(POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "mcs"); synchronized (McsService.class) {
wakeLock.setReferenceCounted(false); if (mainThread == null) {
mainThread = new MainThread();
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + HEARTBEAT_MS, HEARTBEAT_MS, heartbeatIntent); mainThread.start();
}
}
} }
public static boolean isConnected() { public synchronized static boolean isConnected() {
return inputStream != null && inputStream.isAlive() && outputStream != null && outputStream.isAlive(); return inputStream != null && inputStream.isAlive() && outputStream != null && outputStream.isAlive();
} }
@Override @Override
protected void onHandleIntent(Intent intent) { protected void onHandleIntent(Intent intent) {
wakeLock.acquire(); synchronized (McsService.class) {
if (mainHandler != null) { if (mainHandler != null) {
if (ACTION_CONNECT.equals(intent.getAction())) { wakeLock.acquire();
mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_CONNECT, intent)); if (ACTION_CONNECT.equals(intent.getAction())) {
} else if (ACTION_HEARTBEAT.equals(intent.getAction())) { mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_CONNECT, intent));
mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_HEARTBEAT, intent)); } else if (ACTION_HEARTBEAT.equals(intent.getAction())) {
mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_HEARTBEAT, intent));
}
WakefulBroadcastReceiver.completeWakefulIntent(intent);
} else if (connectIntent == null) {
connectIntent = intent;
} else {
WakefulBroadcastReceiver.completeWakefulIntent(intent);
} }
} }
} }
private synchronized void connect() { private synchronized void connect() {
if (delay < 60000) delay += 5000;
try { try {
Log.d(TAG, "Starting MCS connection..."); Log.d(TAG, "Starting MCS connection...");
Socket socket = new Socket(SERVICE_HOST, SERVICE_PORT); Socket socket = new Socket(SERVICE_HOST, SERVICE_PORT);
@ -145,6 +162,8 @@ public class McsService extends IntentService implements Handler.Callback {
outputStream = new McsOutputStream(sslSocket.getOutputStream(), mainHandler); outputStream = new McsOutputStream(sslSocket.getOutputStream(), mainHandler);
inputStream.start(); inputStream.start();
outputStream.start(); outputStream.start();
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), HEARTBEAT_MS, heartbeatIntent);
} catch (Exception e) { } catch (Exception e) {
Log.w(TAG, "Exception while connecting!", e); Log.w(TAG, "Exception while connecting!", e);
mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_TEARDOWN, e)); mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_TEARDOWN, e));
@ -260,11 +279,7 @@ public class McsService extends IntentService implements Handler.Callback {
switch (msg.what) { switch (msg.what) {
case MSG_INPUT: case MSG_INPUT:
Log.d(TAG, "Incoming message: " + msg.obj); Log.d(TAG, "Incoming message: " + msg.obj);
if (msg.obj != null) { handleInput((Message) msg.obj);
handleInput((Message) msg.obj);
} else {
mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_TEARDOWN, "null message"));
}
return true; return true;
case MSG_OUTPUT: case MSG_OUTPUT:
Log.d(TAG, "Outgoing message: " + msg.obj); Log.d(TAG, "Outgoing message: " + msg.obj);
@ -321,6 +336,7 @@ public class McsService extends IntentService implements Handler.Callback {
} else { } else {
Log.w(TAG, "Unknown message: " + message); Log.w(TAG, "Unknown message: " + message);
} }
delay = 0;
} catch (Exception e) { } catch (Exception e) {
mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_TEARDOWN, e)); mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_TEARDOWN, e));
} }
@ -330,13 +346,20 @@ public class McsService extends IntentService implements Handler.Callback {
sendOutputStream(MSG_TEARDOWN, msg.obj); sendOutputStream(MSG_TEARDOWN, msg.obj);
if (inputStream != null) { if (inputStream != null) {
inputStream.close(); inputStream.close();
inputStream = null;
} }
try { try {
sslSocket.close(); sslSocket.close();
} catch (Exception ignored) { } catch (Exception ignored) {
} }
sendBroadcast(new Intent("org.microg.gms.gcm.RECONNECT"), "org.microg.gms.STATUS_BROADCAST"); if (delay == 0) {
sendBroadcast(new Intent("org.microg.gms.gcm.RECONNECT"), "org.microg.gms.STATUS_BROADCAST");
} else {
alarmManager.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + delay, PendingIntent.getBroadcast(this, 1, new Intent(this, TriggerReceiver.class), 0));
}
alarmManager.cancel(heartbeatIntent); alarmManager.cancel(heartbeatIntent);
wakeLock.release(); if (wakeLock != null) {
wakeLock.release();
}
} }
} }

View File

@ -16,35 +16,37 @@
package org.microg.gms.gcm; package org.microg.gms.gcm;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.NetworkInfo; import android.net.NetworkInfo;
import android.os.SystemClock;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.v4.content.WakefulBroadcastReceiver;
import android.util.Log;
public class TriggerReceiver extends BroadcastReceiver { public class TriggerReceiver extends WakefulBroadcastReceiver {
private static final String TAG = "GmsGcmTrigger";
private static final String PREF_ENABLE_GCM = "gcm_enable_mcs_service"; private static final String PREF_ENABLE_GCM = "gcm_enable_mcs_service";
private static final long pendingDelay = 1000;
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
boolean force = "android.provider.Telephony.SECRET_CODE".equals(intent.getAction()); boolean force = "android.provider.Telephony.SECRET_CODE".equals(intent.getAction());
if (!McsService.isConnected() || force) { if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(PREF_ENABLE_GCM, false) || force) {
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(PREF_ENABLE_GCM, false) || force) { if (!McsService.isConnected() || force) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = cm.getActiveNetworkInfo(); NetworkInfo networkInfo = cm.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected() || force) { if (networkInfo != null && networkInfo.isConnected() || force) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); startWakefulService(context, new Intent(McsService.ACTION_CONNECT, null, context, McsService.class));
PendingIntent pendingIntent = PendingIntent.getService(context, 0, new Intent(McsService.ACTION_CONNECT, null, context, McsService.class), 0); } else {
alarmManager.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + pendingDelay, pendingIntent); Log.d(TAG, "Ignoring " + intent + ": network is offline");
} }
} else {
Log.d(TAG, "Ignoring " + intent + ": service is running");
} }
} else {
Log.d(TAG, "Ignoring " + intent + ": gcm is disabled");
} }
} }
} }