diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml index 82308568..fa15b346 100644 --- a/play-services-core/src/main/AndroidManifest.xml +++ b/play-services-core/src/main/AndroidManifest.xml @@ -93,6 +93,7 @@ + diff --git a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinService.java b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinService.java index 9fc69307..009d9d44 100644 --- a/play-services-core/src/main/java/org/microg/gms/checkin/CheckinService.java +++ b/play-services-core/src/main/java/org/microg/gms/checkin/CheckinService.java @@ -29,6 +29,7 @@ import android.util.Log; import com.google.android.gms.checkin.internal.ICheckinService; import org.microg.gms.auth.AuthConstants; +import org.microg.gms.common.ForegroundServiceContext; import org.microg.gms.gcm.McsService; import org.microg.gms.people.PeopleManager; @@ -55,6 +56,7 @@ public class CheckinService extends IntentService { @Override protected void onHandleIntent(Intent intent) { try { + ForegroundServiceContext.completeForegroundService(this, intent, TAG); if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(PREF_ENABLE_CHECKIN, false)) { LastCheckinInfo info = CheckinManager.checkin(this, intent.getBooleanExtra(EXTRA_FORCE_CHECKIN, false)); if (info != null) { diff --git a/play-services-core/src/main/java/org/microg/gms/checkin/TriggerReceiver.java b/play-services-core/src/main/java/org/microg/gms/checkin/TriggerReceiver.java index fbb8aaca..08df8cdb 100644 --- a/play-services-core/src/main/java/org/microg/gms/checkin/TriggerReceiver.java +++ b/play-services-core/src/main/java/org/microg/gms/checkin/TriggerReceiver.java @@ -24,6 +24,10 @@ import android.preference.PreferenceManager; import android.support.v4.content.WakefulBroadcastReceiver; import android.util.Log; +import org.microg.gms.common.ForegroundServiceContext; + +import static org.microg.gms.checkin.CheckinService.EXTRA_FORCE_CHECKIN; + public class TriggerReceiver extends WakefulBroadcastReceiver { private static final String TAG = "GmsCheckinTrigger"; public static final String PREF_ENABLE_CHECKIN = "checkin_enable_service"; @@ -31,23 +35,27 @@ public class TriggerReceiver extends WakefulBroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - boolean force = "android.provider.Telephony.SECRET_CODE".equals(intent.getAction()); - ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + try { + boolean force = "android.provider.Telephony.SECRET_CODE".equals(intent.getAction()); + ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(PREF_ENABLE_CHECKIN, false) || force) { - if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction()) && - LastCheckinInfo.read(context).lastCheckin > System.currentTimeMillis() - REGULAR_CHECKIN_INTERVAL) { - return; - } + if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(PREF_ENABLE_CHECKIN, false) || force) { + if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction()) && + LastCheckinInfo.read(context).lastCheckin > System.currentTimeMillis() - REGULAR_CHECKIN_INTERVAL) { + return; + } - NetworkInfo networkInfo = cm.getActiveNetworkInfo(); - if (networkInfo != null && networkInfo.isConnected() || force) { - Intent subIntent = new Intent(context, CheckinService.class); - subIntent.putExtra(CheckinService.EXTRA_FORCE_CHECKIN, force); - startWakefulService(context, subIntent); + NetworkInfo networkInfo = cm.getActiveNetworkInfo(); + if (networkInfo != null && networkInfo.isConnected() || force) { + Intent subIntent = new Intent(context, CheckinService.class); + subIntent.putExtra(EXTRA_FORCE_CHECKIN, force); + startWakefulService(new ForegroundServiceContext(context), subIntent); + } + } else { + Log.d(TAG, "Ignoring " + intent + ": checkin is disabled"); } - } else { - Log.d(TAG, "Ignoring " + intent + ": checkin is disabled"); + } catch (Exception e) { + Log.w(TAG, e); } } } diff --git a/play-services-core/src/main/java/org/microg/gms/common/ForegroundServiceContext.java b/play-services-core/src/main/java/org/microg/gms/common/ForegroundServiceContext.java new file mode 100644 index 00000000..f2fc2026 --- /dev/null +++ b/play-services-core/src/main/java/org/microg/gms/common/ForegroundServiceContext.java @@ -0,0 +1,80 @@ +package org.microg.gms.common; + +import android.app.ActivityManager; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.Service; +import android.content.ComponentName; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.Intent; +import android.os.Build; +import android.os.PowerManager; +import android.support.annotation.RequiresApi; +import android.util.Log; + +import com.google.android.gms.R; + +import java.util.List; + +public class ForegroundServiceContext extends ContextWrapper { + private static final String TAG = "ForegroundService"; + public static final String EXTRA_FOREGROUND = "foreground"; + + public ForegroundServiceContext(Context base) { + super(base); + } + + @Override + public ComponentName startService(Intent service) { + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !isIgnoringBatteryOptimizations() && !isAppOnForeground()) { + Log.d(TAG, "Starting in foreground mode."); + service.putExtra(EXTRA_FOREGROUND, true); + return super.startForegroundService(service); + } + return super.startService(service); + } + + @RequiresApi(api = Build.VERSION_CODES.M) + private boolean isIgnoringBatteryOptimizations() { + PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE); + return powerManager.isIgnoringBatteryOptimizations(getPackageName()); + } + + private boolean isAppOnForeground() { + ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); + List appProcesses = activityManager.getRunningAppProcesses(); + if (appProcesses == null) { + return false; + } + final String packageName = getPackageName(); + for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) { + if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName.equals(packageName)) { + return true; + } + } + return false; + } + + public static void completeForegroundService(Service service, Intent intent, String tag) { + if (intent.getBooleanExtra(EXTRA_FOREGROUND, false) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + Log.d(tag, "Started in foreground mode."); + service.startForeground(tag.hashCode(), buildForegroundNotification(service)); + } + } + + @RequiresApi(api = Build.VERSION_CODES.O) + private static Notification buildForegroundNotification(Context context) { + NotificationChannel channel = new NotificationChannel("foreground-service", "Foreground Service", NotificationManager.IMPORTANCE_NONE); + channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); + channel.setShowBadge(false); + channel.setVibrationPattern(new long[0]); + context.getSystemService(NotificationManager.class).createNotificationChannel(channel); + return new Notification.Builder(context, channel.getId()) + .setOngoing(true) + .setContentTitle("Running in background") + .setSmallIcon(R.drawable.gcm_bell) + .build(); + } +} diff --git a/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java b/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java index a2b65de1..6ca24440 100644 --- a/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java +++ b/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java @@ -31,6 +31,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import static android.os.Build.VERSION.SDK_INT; @@ -198,8 +199,11 @@ public class PackageUtils { ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); if (manager == null) return null; if (pid <= 0) return null; - for (ActivityManager.RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) { - if (processInfo.pid == pid) return processInfo.processName; + List runningAppProcesses = manager.getRunningAppProcesses(); + if (runningAppProcesses != null) { + for (ActivityManager.RunningAppProcessInfo processInfo : runningAppProcesses) { + if (processInfo.pid == pid) return processInfo.processName; + } } return null; } diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/GcmPrefs.java b/play-services-core/src/main/java/org/microg/gms/gcm/GcmPrefs.java index d0cc000f..30a95703 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/GcmPrefs.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/GcmPrefs.java @@ -28,7 +28,6 @@ import java.util.Collections; import java.util.List; public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListener { - public static final String PREF_HEARTBEAT = "gcm_heartbeat_interval"; public static final String PREF_FULL_LOG = "gcm_full_log"; public static final String PREF_LAST_PERSISTENT_ID = "gcm_last_persistent_id"; public static final String PREF_CONFIRM_NEW_APPS = "gcm_confirm_new_apps"; @@ -43,6 +42,9 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe public static final String PREF_LEARNT_WIFI = "gcm_learnt_wifi"; public static final String PREF_LEARNT_OTHER = "gcm_learnt_other"; + private static final int MIN_INTERVAL = 5 * 60 * 1000; // 5 minutes + private static final int MAX_INTERVAL = 30 * 60 * 1000; // 30 minutes + private static GcmPrefs INSTANCE; public static GcmPrefs get(Context context) { @@ -53,7 +55,6 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe return INSTANCE; } - private int heartbeatMs = 300000; private boolean gcmLogEnabled = true; private String lastPersistedId = ""; private boolean confirmNewApps = false; @@ -79,7 +80,6 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe } public void update() { - heartbeatMs = Integer.parseInt(defaultPreferences.getString(PREF_HEARTBEAT, "300")) * 1000; gcmLogEnabled = defaultPreferences.getBoolean(PREF_FULL_LOG, true); lastPersistedId = defaultPreferences.getString(PREF_LAST_PERSISTENT_ID, ""); confirmNewApps = defaultPreferences.getBoolean(PREF_CONFIRM_NEW_APPS, false); @@ -95,10 +95,6 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe learntOther = defaultPreferences.getInt(PREF_LEARNT_OTHER, 300000); } - public int getHeartbeatMs() { - return heartbeatMs; - } - public String getNetworkPrefForInfo(NetworkInfo info) { if (info == null) return PREF_NETWORK_OTHER; if (info.isRoaming()) return PREF_NETWORK_ROAMING; @@ -158,7 +154,7 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe learntOther *= 0.95; break; } - defaultPreferences.edit().putInt(PREF_LEARNT_MOBILE, learntMobile).putInt(PREF_LEARNT_WIFI, learntWifi).putInt(PREF_LEARNT_OTHER, learntOther).apply(); + updateLearntValues(); } public void learnReached(String pref, long time) { @@ -178,6 +174,13 @@ public class GcmPrefs implements SharedPreferences.OnSharedPreferenceChangeListe learntOther *= 1.02; break; } + updateLearntValues(); + } + + private void updateLearntValues() { + learntMobile = Math.max(MIN_INTERVAL, Math.min(learntMobile, MAX_INTERVAL)); + learntWifi = Math.max(MIN_INTERVAL, Math.min(learntWifi, MAX_INTERVAL)); + learntOther = Math.max(MIN_INTERVAL, Math.min(learntOther, MAX_INTERVAL)); defaultPreferences.edit().putInt(PREF_LEARNT_MOBILE, learntMobile).putInt(PREF_LEARNT_WIFI, learntWifi).putInt(PREF_LEARNT_OTHER, learntOther).apply(); } diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java b/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java index 66679e5c..8766055b 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java @@ -17,13 +17,15 @@ package org.microg.gms.gcm; import android.app.AlarmManager; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; -import android.content.pm.PermissionInfo; import android.content.pm.ResolveInfo; import android.net.ConnectivityManager; import android.os.Build; @@ -36,12 +38,14 @@ import android.os.Parcelable; import android.os.PowerManager; import android.os.SystemClock; import android.os.UserHandle; +import android.support.annotation.RequiresApi; import android.support.v4.content.WakefulBroadcastReceiver; import android.util.Log; import com.squareup.wire.Message; import org.microg.gms.checkin.LastCheckinInfo; +import org.microg.gms.common.ForegroundServiceContext; import org.microg.gms.common.PackageUtils; import org.microg.gms.gcm.mcs.AppData; import org.microg.gms.gcm.mcs.Close; @@ -66,6 +70,7 @@ import okio.ByteString; import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; import static android.os.Build.VERSION.SDK_INT; +import static org.microg.gms.common.ForegroundServiceContext.EXTRA_FOREGROUND; import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_RECEIVE; import static org.microg.gms.gcm.GcmConstants.EXTRA_APP; import static org.microg.gms.gcm.GcmConstants.EXTRA_COLLAPSE_KEY; @@ -173,11 +178,16 @@ public class McsService extends Service implements Handler.Callback { heartbeatIntent = PendingIntent.getService(this, 0, new Intent(ACTION_HEARTBEAT, null, this, McsService.class), 0); alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); powerManager = (PowerManager) getSystemService(POWER_SERVICE); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission("android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST") == PackageManager.PERMISSION_GRANTED) { try { - Field field = Context.class.getField("DEVICE_IDLE_CONTROLLER"); + String deviceIdleControllerName = "deviceidle"; + try { + Field field = Context.class.getField("DEVICE_IDLE_CONTROLLER"); + deviceIdleControllerName = (String) field.get(null); + } catch (Exception ignored) { + } IBinder binder = (IBinder) Class.forName("android.os.ServiceManager") - .getMethod("getService", String.class).invoke(null, field.get(null)); + .getMethod("getService", String.class).invoke(null, deviceIdleControllerName); if (binder != null) { deviceIdleController = Class.forName("android.os.IDeviceIdleController$Stub") .getMethod("asInterface", IBinder.class).invoke(null, binder); @@ -240,8 +250,12 @@ public class McsService extends Service implements Handler.Callback { AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE); long delay = getCurrentDelay(); logd("Scheduling reconnect in " + delay / 1000 + " seconds..."); - alarmManager.set(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + delay, - PendingIntent.getBroadcast(context, 1, new Intent(ACTION_RECONNECT, null, context, TriggerReceiver.class), 0)); + PendingIntent pi = PendingIntent.getBroadcast(context, 1, new Intent(ACTION_RECONNECT, null, context, TriggerReceiver.class), 0); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + alarmManager.setExactAndAllowWhileIdle(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + delay, pi); + } else { + alarmManager.set(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + delay, pi); + } } public void scheduleHeartbeat(Context context) { @@ -252,13 +266,16 @@ public class McsService extends Service implements Handler.Callback { closeAll(); } logd("Scheduling heartbeat in " + heartbeatMs / 1000 + " seconds..."); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - alarmManager.set(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + heartbeatMs, heartbeatIntent); - } else { - // with KitKat, the alarms become inexact by default, but with the newly available setWindow we can get inexact alarms with guarantees. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + // This is supposed to work even when running in idle and without battery optimization disabled + alarmManager.setExactAndAllowWhileIdle(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + heartbeatMs, heartbeatIntent); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + // With KitKat, the alarms become inexact by default, but with the newly available setWindow we can get inexact alarms with guarantees. // Schedule the alarm to fire within the interval [heartbeatMs/3*4, heartbeatMs] alarmManager.setWindow(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + heartbeatMs / 4 * 3, heartbeatMs / 4, heartbeatIntent); + } else { + alarmManager.set(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + heartbeatMs, heartbeatIntent); } } @@ -277,6 +294,7 @@ public class McsService extends Service implements Handler.Callback { @Override public int onStartCommand(Intent intent, int flags, int startId) { + ForegroundServiceContext.completeForegroundService(this, intent, TAG); synchronized (McsService.class) { if (rootHandler != null) { if (intent == null) return START_REDELIVER_INTENT; @@ -299,6 +317,19 @@ public class McsService extends Service implements Handler.Callback { return START_REDELIVER_INTENT; } + @RequiresApi(api = Build.VERSION_CODES.O) + private Notification buildForegroundNotification() { + NotificationChannel channel = new NotificationChannel("foreground-service", "Foreground Service", NotificationManager.IMPORTANCE_LOW); + channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); + channel.setShowBadge(false); + getSystemService(NotificationManager.class).createNotificationChannel(channel); + return new Notification.Builder(this, channel.getId()) + .setOngoing(true) + .setContentTitle("Running in background") + .setSmallIcon(android.R.drawable.stat_notify_sync) + .build(); + } + private void handleSendMessage(Intent intent) { String messageId = intent.getStringExtra(EXTRA_MESSAGE_ID); String collapseKey = intent.getStringExtra(EXTRA_COLLAPSE_KEY); diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/TriggerReceiver.java b/play-services-core/src/main/java/org/microg/gms/gcm/TriggerReceiver.java index 1a7bdb8b..50a7a03b 100644 --- a/play-services-core/src/main/java/org/microg/gms/gcm/TriggerReceiver.java +++ b/play-services-core/src/main/java/org/microg/gms/gcm/TriggerReceiver.java @@ -25,6 +25,7 @@ import android.support.v4.content.WakefulBroadcastReceiver; import android.util.Log; import org.microg.gms.checkin.LastCheckinInfo; +import org.microg.gms.common.ForegroundServiceContext; import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION_CODES.N; @@ -51,51 +52,55 @@ public class TriggerReceiver extends WakefulBroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - boolean force = "android.provider.Telephony.SECRET_CODE".equals(intent.getAction()) || FORCE_TRY_RECONNECT.equals(intent.getAction()); - ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + try { + boolean force = "android.provider.Telephony.SECRET_CODE".equals(intent.getAction()) || FORCE_TRY_RECONNECT.equals(intent.getAction()); + ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - if (!GcmPrefs.get(context).isEnabled() && !force) { - Log.d(TAG, "Ignoring " + intent + ": gcm is disabled"); - return; - } - - if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) { - McsService.resetCurrentDelay(); - } - - if (LastCheckinInfo.read(context).androidId == 0) { - Log.d(TAG, "Ignoring " + intent + ": need to checkin first."); - return; - } - - force |= "android.intent.action.BOOT_COMPLETED".equals(intent.getAction()); - - NetworkInfo networkInfo = cm.getActiveNetworkInfo(); - - if (!force) { - if (networkInfo == null || !networkInfo.isConnected()) { - Log.d(TAG, "Ignoring " + intent + ": network is offline, scheduling new attempt."); - McsService.scheduleReconnect(context); - return; - } else if (!GcmPrefs.get(context).isEnabledFor(networkInfo)) { - Log.d(TAG, "Ignoring " + intent + ": gcm is disabled for " + networkInfo.getTypeName()); + if (!GcmPrefs.get(context).isEnabled() && !force) { + Log.d(TAG, "Ignoring " + intent + ": gcm is disabled"); return; } - } - if (!McsService.isConnected() || force) { - Log.d(TAG, "Not connected to GCM but should be, asking the service to start up. Triggered by: " + intent); - startWakefulService(context, new Intent(ACTION_CONNECT, null, context, McsService.class) - .putExtra(EXTRA_REASON, intent)); - } else { if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) { - Log.d(TAG, "Ignoring " + intent + ": service is running. schedule reconnect instead."); - McsService.scheduleReconnect(context); - } else { - Log.d(TAG, "Ignoring " + intent + ": service is running. heartbeat instead."); - startWakefulService(context, new Intent(ACTION_HEARTBEAT, null, context, McsService.class) - .putExtra(EXTRA_REASON, intent)); + McsService.resetCurrentDelay(); } + + if (LastCheckinInfo.read(context).androidId == 0) { + Log.d(TAG, "Ignoring " + intent + ": need to checkin first."); + return; + } + + force |= "android.intent.action.BOOT_COMPLETED".equals(intent.getAction()); + + NetworkInfo networkInfo = cm.getActiveNetworkInfo(); + + if (!force) { + if (networkInfo == null || !networkInfo.isConnected()) { + Log.d(TAG, "Ignoring " + intent + ": network is offline, scheduling new attempt."); + McsService.scheduleReconnect(context); + return; + } else if (!GcmPrefs.get(context).isEnabledFor(networkInfo)) { + Log.d(TAG, "Ignoring " + intent + ": gcm is disabled for " + networkInfo.getTypeName()); + return; + } + } + + if (!McsService.isConnected() || force) { + Log.d(TAG, "Not connected to GCM but should be, asking the service to start up. Triggered by: " + intent); + startWakefulService(new ForegroundServiceContext(context), new Intent(ACTION_CONNECT, null, context, McsService.class) + .putExtra(EXTRA_REASON, intent)); + } else { + if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) { + Log.d(TAG, "Ignoring " + intent + ": service is running. schedule reconnect instead."); + McsService.scheduleReconnect(context); + } else { + Log.d(TAG, "Ignoring " + intent + ": service is running. heartbeat instead."); + startWakefulService(new ForegroundServiceContext(context), new Intent(ACTION_HEARTBEAT, null, context, McsService.class) + .putExtra(EXTRA_REASON, intent)); + } + } + } catch (Exception e) { + Log.w(TAG, e); } }