Update portions of GCM implementation

- Fix bug causing unregister to be send multiple times
- More work related to #23, #29 and #31
This commit is contained in:
mar-v-in 2015-10-11 00:46:58 +02:00
parent 02e6ffce4d
commit 766a6a1b47
6 changed files with 100 additions and 59 deletions

View File

@ -35,4 +35,9 @@ public class Constants {
public static final int MSG_TEARDOWN = 30; public static final int MSG_TEARDOWN = 30;
public static final int MSG_CONNECT = 40; public static final int MSG_CONNECT = 40;
public static final int MSG_HEARTBEAT = 41; public static final int MSG_HEARTBEAT = 41;
public static String ACTION_CONNECT = "org.microg.gms.gcm.mcs.CONNECT";
public static String ACTION_RECONNECT = "org.microg.gms.gcm.mcs.RECONNECT";
public static String ACTION_HEARTBEAT = "org.microg.gms.gcm.mcs.HEARTBEAT";
public static String EXTRA_REASON = "org.microg.gms.gcm.mcs.REASON";
} }

View File

@ -72,6 +72,7 @@ public class McsInputStream extends Thread {
try { try {
while (!Thread.currentThread().isInterrupted()) { while (!Thread.currentThread().isInterrupted()) {
Message msg = read(); Message msg = read();
Log.d(TAG, "Incoming message: " + msg);
if (msg != null) { if (msg != null) {
mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_INPUT, msg)); mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_INPUT, msg));
} else { } else {
@ -109,7 +110,7 @@ public class McsInputStream extends Thread {
if (!initialized) { if (!initialized) {
try { try {
version = is.read(); version = is.read();
Log.d(TAG, "Reading from MCS version=" + version); Log.d(TAG, "Reading from MCS version: " + version);
initialized = true; initialized = true;
} catch (IOException e) { } catch (IOException e) {
Log.w(TAG, e); Log.w(TAG, e);

View File

@ -80,6 +80,7 @@ public class McsOutputStream extends Thread implements Handler.Callback {
case MSG_OUTPUT: case MSG_OUTPUT:
try { try {
Message message = (Message) msg.obj; Message message = (Message) msg.obj;
Log.d(TAG, "Outgoing message: " + message);
if (msg.obj instanceof DataMessageStanza) { if (msg.obj instanceof DataMessageStanza) {
writeInternal(message, MCS_DATA_MESSAGE_STANZA_TAG); writeInternal(message, MCS_DATA_MESSAGE_STANZA_TAG);
} else if (msg.obj instanceof LoginRequest) { } else if (msg.obj instanceof LoginRequest) {

View File

@ -17,12 +17,13 @@
package org.microg.gms.gcm; package org.microg.gms.gcm;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.app.IntentService;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.Service;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder;
import android.os.Looper; import android.os.Looper;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.SystemClock; import android.os.SystemClock;
@ -48,6 +49,10 @@ import java.util.Collections;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION.SDK_INT;
import static org.microg.gms.gcm.Constants.ACTION_CONNECT;
import static org.microg.gms.gcm.Constants.ACTION_HEARTBEAT;
import static org.microg.gms.gcm.Constants.ACTION_RECONNECT;
import static org.microg.gms.gcm.Constants.EXTRA_REASON;
import static org.microg.gms.gcm.Constants.MSG_CONNECT; import static org.microg.gms.gcm.Constants.MSG_CONNECT;
import static org.microg.gms.gcm.Constants.MSG_HEARTBEAT; import static org.microg.gms.gcm.Constants.MSG_HEARTBEAT;
import static org.microg.gms.gcm.Constants.MSG_INPUT; import static org.microg.gms.gcm.Constants.MSG_INPUT;
@ -57,12 +62,9 @@ import static org.microg.gms.gcm.Constants.MSG_OUTPUT_ERROR;
import static org.microg.gms.gcm.Constants.MSG_OUTPUT_READY; import static org.microg.gms.gcm.Constants.MSG_OUTPUT_READY;
import static org.microg.gms.gcm.Constants.MSG_TEARDOWN; import static org.microg.gms.gcm.Constants.MSG_TEARDOWN;
public class McsService extends IntentService implements Handler.Callback { public class McsService extends Service implements Handler.Callback {
private static final String TAG = "GmsGcmMcsSvc"; private static final String TAG = "GmsGcmMcsSvc";
public static String ACTION_CONNECT = "org.microg.gms.gcm.mcs.CONNECT";
public static String ACTION_HEARTBEAT = "org.microg.gms.gcm.mcs.HEARTBEAT";
public static final String PREFERENCES_NAME = "mcs"; public static final String PREFERENCES_NAME = "mcs";
public static final String PREF_LAST_PERSISTENT_ID = "last_persistent_id"; public static final String PREF_LAST_PERSISTENT_ID = "last_persistent_id";
@ -81,8 +83,8 @@ public class McsService extends IntentService implements Handler.Callback {
private PendingIntent heartbeatIntent; private PendingIntent heartbeatIntent;
private static MainThread mainThread; private static HandlerThread handlerThread;
private static Handler mainHandler; private static Handler rootHandler;
private AlarmManager alarmManager; private AlarmManager alarmManager;
private PowerManager powerManager; private PowerManager powerManager;
@ -92,20 +94,16 @@ public class McsService extends IntentService implements Handler.Callback {
private Intent connectIntent; private Intent connectIntent;
public McsService() { private class HandlerThread extends Thread {
super(TAG);
}
private class MainThread extends Thread {
@Override @Override
public void run() { public void run() {
Looper.prepare(); Looper.prepare();
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "mcs"); wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "mcs");
wakeLock.setReferenceCounted(false); wakeLock.setReferenceCounted(false);
synchronized (McsService.class) { synchronized (McsService.class) {
mainHandler = new Handler(Looper.myLooper(), McsService.this); rootHandler = new Handler(Looper.myLooper(), McsService.this);
if (connectIntent != null) { if (connectIntent != null) {
mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_CONNECT, connectIntent)); rootHandler.sendMessage(rootHandler.obtainMessage(MSG_CONNECT, connectIntent));
WakefulBroadcastReceiver.completeWakefulIntent(connectIntent); WakefulBroadcastReceiver.completeWakefulIntent(connectIntent);
} }
} }
@ -120,27 +118,34 @@ public class McsService extends IntentService implements Handler.Callback {
alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
powerManager = (PowerManager) getSystemService(POWER_SERVICE); powerManager = (PowerManager) getSystemService(POWER_SERVICE);
synchronized (McsService.class) { synchronized (McsService.class) {
if (mainThread == null) { if (handlerThread == null) {
mainThread = new MainThread(); handlerThread = new HandlerThread();
mainThread.start(); handlerThread.start();
} }
} }
} }
@Override
public IBinder onBind(Intent intent) {
return null;
}
public synchronized 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();
} }
public static void scheduleReconnect(Context context) { public static void scheduleReconnect(Context context) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE); AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
alarmManager.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + getCurrentDelay(), long delay = getCurrentDelay();
PendingIntent.getBroadcast(context, 1, new Intent("org.microg.gms.gcm.RECONNECT", null, context, TriggerReceiver.class), 0)); Log.d(TAG, "Scheduling reconnect in " + delay / 1000 + " seconds...");
alarmManager.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + delay,
PendingIntent.getBroadcast(context, 1, new Intent(ACTION_RECONNECT, null, context, TriggerReceiver.class), 0));
} }
public synchronized static long getCurrentDelay() { public synchronized static long getCurrentDelay() {
long delay = currentDelay == 0 ? 5000 : currentDelay; long delay = currentDelay == 0 ? 5000 : currentDelay;
if (currentDelay < 60000) currentDelay += 5000; if (currentDelay < 60000) currentDelay += 10000;
if (currentDelay >= 60000 && currentDelay < 60000) currentDelay += 60000; if (currentDelay >= 60000 && currentDelay < 600000) currentDelay += 60000;
return delay; return delay;
} }
@ -148,15 +153,18 @@ public class McsService extends IntentService implements Handler.Callback {
currentDelay = 0; currentDelay = 0;
} }
@Override @Override
protected void onHandleIntent(Intent intent) { public int onStartCommand(Intent intent, int flags, int startId) {
synchronized (McsService.class) { synchronized (McsService.class) {
if (mainHandler != null) { if (rootHandler != null) {
wakeLock.acquire(); wakeLock.acquire();
Object reason = intent == null ? "I am so sticky!" :
intent.hasExtra(EXTRA_REASON) ? intent.getExtras().get(EXTRA_REASON) : intent;
if (ACTION_CONNECT.equals(intent.getAction())) { if (ACTION_CONNECT.equals(intent.getAction())) {
mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_CONNECT, intent)); rootHandler.sendMessage(rootHandler.obtainMessage(MSG_CONNECT, reason));
} else if (ACTION_HEARTBEAT.equals(intent.getAction())) { } else if (ACTION_HEARTBEAT.equals(intent.getAction())) {
mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_HEARTBEAT, intent)); rootHandler.sendMessage(rootHandler.obtainMessage(MSG_HEARTBEAT, reason));
} }
WakefulBroadcastReceiver.completeWakefulIntent(intent); WakefulBroadcastReceiver.completeWakefulIntent(intent);
} else if (connectIntent == null) { } else if (connectIntent == null) {
@ -165,6 +173,7 @@ public class McsService extends IntentService implements Handler.Callback {
WakefulBroadcastReceiver.completeWakefulIntent(intent); WakefulBroadcastReceiver.completeWakefulIntent(intent);
} }
} }
return START_STICKY;
} }
private synchronized void connect() { private synchronized void connect() {
@ -174,15 +183,15 @@ public class McsService extends IntentService implements Handler.Callback {
Log.d(TAG, "Connected to " + SERVICE_HOST + ":" + SERVICE_PORT); Log.d(TAG, "Connected to " + SERVICE_HOST + ":" + SERVICE_PORT);
sslSocket = SSLContext.getDefault().getSocketFactory().createSocket(socket, SERVICE_HOST, SERVICE_PORT, true); sslSocket = SSLContext.getDefault().getSocketFactory().createSocket(socket, SERVICE_HOST, SERVICE_PORT, true);
Log.d(TAG, "Activated SSL with " + SERVICE_HOST + ":" + SERVICE_PORT); Log.d(TAG, "Activated SSL with " + SERVICE_HOST + ":" + SERVICE_PORT);
inputStream = new McsInputStream(sslSocket.getInputStream(), mainHandler); inputStream = new McsInputStream(sslSocket.getInputStream(), rootHandler);
outputStream = new McsOutputStream(sslSocket.getOutputStream(), mainHandler); outputStream = new McsOutputStream(sslSocket.getOutputStream(), rootHandler);
inputStream.start(); inputStream.start();
outputStream.start(); outputStream.start();
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), HEARTBEAT_MS, heartbeatIntent); 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)); rootHandler.sendMessage(rootHandler.obtainMessage(MSG_TEARDOWN, e));
} }
} }
@ -278,7 +287,7 @@ public class McsService extends IntentService implements Handler.Callback {
} }
private void send(Message message) { private void send(Message message) {
mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_OUTPUT, message)); rootHandler.sendMessage(rootHandler.obtainMessage(MSG_OUTPUT, message));
} }
private void sendOutputStream(int what, Object obj) { private void sendOutputStream(int what, Object obj) {
@ -286,7 +295,7 @@ public class McsService extends IntentService implements Handler.Callback {
if (os != null) { if (os != null) {
Handler outputHandler = os.getHandler(); Handler outputHandler = os.getHandler();
if (outputHandler != null) if (outputHandler != null)
outputHandler.dispatchMessage(outputHandler.obtainMessage(what, obj)); outputHandler.sendMessage(outputHandler.obtainMessage(what, obj));
} }
} }
@ -294,17 +303,15 @@ public class McsService extends IntentService implements Handler.Callback {
public boolean handleMessage(android.os.Message msg) { public boolean handleMessage(android.os.Message msg) {
switch (msg.what) { switch (msg.what) {
case MSG_INPUT: case MSG_INPUT:
Log.d(TAG, "Incoming message: " + msg.obj);
handleInput((Message) msg.obj); handleInput((Message) msg.obj);
return true; return true;
case MSG_OUTPUT: case MSG_OUTPUT:
Log.d(TAG, "Outgoing message: " + msg.obj);
sendOutputStream(MSG_OUTPUT, msg.obj); sendOutputStream(MSG_OUTPUT, msg.obj);
return true; return true;
case MSG_INPUT_ERROR: case MSG_INPUT_ERROR:
case MSG_OUTPUT_ERROR: case MSG_OUTPUT_ERROR:
Log.d(TAG, "I/O error: " + msg.obj); Log.d(TAG, "I/O error: " + msg.obj);
mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_TEARDOWN, msg.obj)); rootHandler.sendMessage(rootHandler.obtainMessage(MSG_TEARDOWN, msg.obj));
return true; return true;
case MSG_TEARDOWN: case MSG_TEARDOWN:
Log.d(TAG, "Teardown initiated, reason: " + msg.obj); Log.d(TAG, "Teardown initiated, reason: " + msg.obj);
@ -326,6 +333,7 @@ public class McsService extends IntentService implements Handler.Callback {
send(ping.build()); send(ping.build());
} else { } else {
Log.d(TAG, "Ignoring heartbeat, not connected!"); Log.d(TAG, "Ignoring heartbeat, not connected!");
scheduleReconnect(this);
} }
return true; return true;
case MSG_OUTPUT_READY: case MSG_OUTPUT_READY:
@ -354,7 +362,7 @@ public class McsService extends IntentService implements Handler.Callback {
} }
resetCurrentDelay(); resetCurrentDelay();
} catch (Exception e) { } catch (Exception e) {
mainHandler.dispatchMessage(mainHandler.obtainMessage(MSG_TEARDOWN, e)); rootHandler.sendMessage(rootHandler.obtainMessage(MSG_TEARDOWN, e));
} }
} }

View File

@ -20,8 +20,8 @@ import android.app.IntentService;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build; import android.os.Build;
import android.os.Bundle;
import android.os.Message; import android.os.Message;
import android.os.Messenger; import android.os.Messenger;
import android.util.Log; import android.util.Log;
@ -35,8 +35,16 @@ import java.io.IOException;
public class PushRegisterService extends IntentService { public class PushRegisterService extends IntentService {
private static final String TAG = "GmsGcmRegisterSvc"; private static final String TAG = "GmsGcmRegisterSvc";
private static final String REMOVED = "%%REMOVED%%";
private static final String ERROR = "%%ERROR%%";
public PushRegisterService() { public PushRegisterService() {
super(TAG); super(TAG);
setIntentRedelivery(false);
}
private SharedPreferences getSharedPreferences() {
return getSharedPreferences("gcm_registrations", MODE_PRIVATE);
} }
@Override @Override
@ -68,17 +76,20 @@ public class PushRegisterService extends IntentService {
PendingIntent pendingIntent = intent.getParcelableExtra("app"); PendingIntent pendingIntent = intent.getParcelableExtra("app");
String sender = intent.getStringExtra("sender"); String sender = intent.getStringExtra("sender");
String app = packageFromPendingIntent(pendingIntent); String app = packageFromPendingIntent(pendingIntent);
Bundle extras = intent.getExtras(); Log.d(TAG, "register[res]: " + intent.toString() + " extras=" + intent.getExtras());
extras.keySet();
Log.d(TAG, "register[req]: " + extras);
Intent outIntent = new Intent("com.google.android.c2dm.intent.REGISTRATION"); Intent outIntent = new Intent("com.google.android.c2dm.intent.REGISTRATION");
outIntent.setPackage(app); String appSignature = PackageUtils.firstSignatureDigest(this, app);
String regId = register(this, app, sender, null, false).token;
String regId = register(this, app, appSignature, sender, null, false).token;
if (regId != null) { if (regId != null) {
outIntent.putExtra("registration_id", regId); outIntent.putExtra("registration_id", regId);
getSharedPreferences().edit().putString(app + ":" + appSignature, regId).apply();
} else { } else {
outIntent.putExtra("error", "SERVICE_NOT_AVAILABLE"); outIntent.putExtra("error", "SERVICE_NOT_AVAILABLE");
getSharedPreferences().edit().putString(app + ":" + appSignature, "-").apply();
} }
Log.d(TAG, "register[res]: " + outIntent + " extras=" + outIntent.getExtras()); Log.d(TAG, "register[res]: " + outIntent + " extras=" + outIntent.getExtras());
try { try {
if (intent.hasExtra("google.messenger")) { if (intent.hasExtra("google.messenger")) {
@ -91,17 +102,19 @@ public class PushRegisterService extends IntentService {
} catch (Exception e) { } catch (Exception e) {
Log.w(TAG, e); Log.w(TAG, e);
} }
outIntent.setPackage(app);
sendOrderedBroadcast(outIntent, null); sendOrderedBroadcast(outIntent, null);
} }
public static RegisterResponse register(Context context, String app, String sender, String info, boolean delete) { public static RegisterResponse register(Context context, String app, String appSignature, String sender, String info, boolean delete) {
try { try {
RegisterResponse response = new RegisterRequest() RegisterResponse response = new RegisterRequest()
.build(Utils.getBuild(context)) .build(Utils.getBuild(context))
.sender(sender) .sender(sender)
.info(info) .info(info)
.checkin(LastCheckinInfo.read(context)) .checkin(LastCheckinInfo.read(context))
.app(app, PackageUtils.firstSignatureDigest(context, app), PackageUtils.versionCode(context, app)) .app(app, appSignature, PackageUtils.versionCode(context, app))
.delete(delete) .delete(delete)
.getResponse(); .getResponse();
Log.d(TAG, "received response: " + response); Log.d(TAG, "received response: " + response);
@ -116,24 +129,27 @@ public class PushRegisterService extends IntentService {
private void unregister(Intent intent) { private void unregister(Intent intent) {
PendingIntent pendingIntent = intent.getParcelableExtra("app"); PendingIntent pendingIntent = intent.getParcelableExtra("app");
String app = packageFromPendingIntent(pendingIntent); String app = packageFromPendingIntent(pendingIntent);
Log.d(TAG, "unregister[res]: " + intent.toString() + " extras=" + intent.getExtras());
Intent outIntent = new Intent("com.google.android.c2dm.intent.REGISTRATION"); Intent outIntent = new Intent("com.google.android.c2dm.intent.REGISTRATION");
outIntent.setPackage(app); String appSignature = PackageUtils.firstSignatureDigest(this, app);
RegisterResponse response = register(this, app, null, null, true); if (REMOVED.equals(getSharedPreferences().getString(app + ":" + appSignature, null))) {
if (!app.equals(response.deleted)) {
outIntent.putExtra("error", "SERVICE_NOT_AVAILABLE");
long retry = 0;
if (response.retryAfter != null && !response.retryAfter.contains(":")) {
retry = Long.parseLong(response.retryAfter);
}
if (retry > 0) {
outIntent.putExtra("Retry-After", retry);
}
} else {
outIntent.putExtra("unregistered", app); outIntent.putExtra("unregistered", app);
} else {
RegisterResponse response = register(this, app, appSignature, null, null, true);
if (!app.equals(response.deleted)) {
outIntent.putExtra("error", "SERVICE_NOT_AVAILABLE");
getSharedPreferences().edit().putString(app + ":" + PackageUtils.firstSignatureDigest(this, app), ERROR).apply();
long retry = 0;
if (response.retryAfter != null && !response.retryAfter.contains(":")) {
outIntent.putExtra("Retry-After", Long.parseLong(response.retryAfter));
}
} else {
outIntent.putExtra("unregistered", app);
getSharedPreferences().edit().putString(app + ":" + PackageUtils.firstSignatureDigest(this, app), REMOVED).apply();
}
} }
Log.d(TAG, "unregister[res]: " + outIntent.toString() + " extras=" + outIntent.getExtras()); Log.d(TAG, "unregister[res]: " + outIntent.toString() + " extras=" + outIntent.getExtras());
@ -148,6 +164,8 @@ public class PushRegisterService extends IntentService {
} catch (Exception e) { } catch (Exception e) {
Log.w(TAG, e); Log.w(TAG, e);
} }
outIntent.setPackage(app);
sendOrderedBroadcast(outIntent, null); sendOrderedBroadcast(outIntent, null);
} }
} }

View File

@ -26,6 +26,10 @@ import android.util.Log;
import org.microg.gms.checkin.LastCheckinInfo; import org.microg.gms.checkin.LastCheckinInfo;
import static org.microg.gms.gcm.Constants.ACTION_CONNECT;
import static org.microg.gms.gcm.Constants.ACTION_HEARTBEAT;
import static org.microg.gms.gcm.Constants.EXTRA_REASON;
public class TriggerReceiver extends WakefulBroadcastReceiver { public class TriggerReceiver extends WakefulBroadcastReceiver {
private static final String TAG = "GmsGcmTrigger"; 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";
@ -41,19 +45,23 @@ public class TriggerReceiver extends WakefulBroadcastReceiver {
} }
if (LastCheckinInfo.read(context).androidId == 0) { if (LastCheckinInfo.read(context).androidId == 0) {
Log.d(TAG, "Ignoring " + intent + ": need to checkin first."); Log.d(TAG, "Ignoring " + intent + ": need to checkin first.");
return;
} }
NetworkInfo networkInfo = cm.getActiveNetworkInfo(); NetworkInfo networkInfo = cm.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected() || force) { if (networkInfo != null && networkInfo.isConnected() || force) {
if (!McsService.isConnected() || force) { if (!McsService.isConnected() || force) {
startWakefulService(context, new Intent(McsService.ACTION_CONNECT, null, context, McsService.class)); Log.d(TAG, "Not connected to GCM but should be, asking the service to start up");
startWakefulService(context, new Intent(ACTION_CONNECT, null, context, McsService.class)
.putExtra(EXTRA_REASON, intent));
} else { } else {
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) { if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
Log.d(TAG, "Ignoring " + intent + ": service is running. schedule reconnect instead."); Log.d(TAG, "Ignoring " + intent + ": service is running. schedule reconnect instead.");
McsService.scheduleReconnect(context); McsService.scheduleReconnect(context);
} else { } else {
Log.d(TAG, "Ignoring " + intent + ": service is running. heartbeat instead."); Log.d(TAG, "Ignoring " + intent + ": service is running. heartbeat instead.");
startWakefulService(context, new Intent(McsService.ACTION_HEARTBEAT, null, context, McsService.class)); startWakefulService(context, new Intent(ACTION_HEARTBEAT, null, context, McsService.class)
.putExtra(EXTRA_REASON, intent));
} }
} }
} else { } else {