package org.nightscout.lasso.mqtt; import android.annotation.TargetApi; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.os.Build; import android.util.Log; import com.nightscout.core.mqtt.Constants; import com.nightscout.core.mqtt.MqttPinger; import com.nightscout.core.mqtt.MqttPingerObservable; import com.nightscout.core.mqtt.MqttPingerObserver; import org.eclipse.paho.client.mqttv3.MqttClient; import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.paho.client.mqttv3.MqttMessage; import java.util.ArrayList; import java.util.List; public class AndroidMqttPinger implements MqttPinger, MqttPingerObservable { private static final String TAG = AndroidMqttPinger.class.getSimpleName(); private Context context; private MqttPingerReceiver pingerReceiver; private PendingIntent pingerPendingIntent; private int instanceId; private MqttClient mqttClient = null; private boolean active = false; private AlarmManager alarmMgr; private int keepAliveInterval; private String keepAliveTopic = "/users/%s/keepalive"; private List<MqttPingerObserver> observers; public AndroidMqttPinger(Context context, int instanceId, MqttClient mqttClient, int keepAliveInterval) { this.context = context; this.instanceId = instanceId; this.alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); this.observers = new ArrayList<>(); this.mqttClient = mqttClient; this.keepAliveInterval = keepAliveInterval; } public void setMqttClient(MqttClient mqttClient) { this.mqttClient = mqttClient; } public void setKeepAliveTopic(String keepAliveTopic) { this.keepAliveTopic = keepAliveTopic; } @Override public void ping() { if (!isActive()) { Log.d(TAG, "Can't ping because connection is not active"); return; } MqttMessage message = new MqttMessage(Constants.MQTT_KEEP_ALIVE_MESSAGE); message.setQos(Constants.MQTT_KEEP_ALIVE_QOS); try { mqttClient.publish(String.format(keepAliveTopic, mqttClient.getClientId()), message); Log.i(TAG, "Successful ping"); } catch (MqttException e) { Log.wtf(TAG, "Exception during ping. Reason code:" + e.getReasonCode() + " Message: " + e.getMessage()); for (MqttPingerObserver observer : observers) { observer.onFailedPing(); } } } @Override public void start() { Log.i(TAG, "Starting ping"); if (!isActive()) { pingerReceiver = new MqttPingerReceiver(this); context.registerReceiver(pingerReceiver, new IntentFilter(Constants.KEEPALIVE_INTENT_FILTER)); active = true; reset(); Log.d(TAG, "Pinger started"); } else { Log.w(TAG, "Can't start pinger because it is already active"); } } @Override public void stop() { Log.i(TAG, "Stopping ping"); alarmMgr.cancel(pingerPendingIntent); if (isActive()) { context.unregisterReceiver(pingerReceiver); active = false; Log.d(TAG, "Pinger stopped"); } else { Log.d(TAG, "Can't stop pinger because it is not active"); } } @Override public boolean isActive() { return active; } @Override public void setKeepAliveInterval(int ms) { keepAliveInterval = ms; } @TargetApi(Build.VERSION_CODES.KITKAT) @Override public void reset() { if (!isActive()) { Log.d(TAG, "Can't reset pinger because it is not active"); return; } alarmMgr.cancel(pingerPendingIntent); Log.d(TAG, "Setting next keep alive to trigger in " + (Constants.KEEPALIVE_INTERVAL - 3000) / 1000 + " seconds"); Intent pingerIntent = new Intent(Constants.KEEPALIVE_INTENT_FILTER); pingerIntent.putExtra("device", instanceId); pingerPendingIntent = PendingIntent.getBroadcast(context, 61, pingerIntent, 0); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { alarmMgr.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + keepAliveInterval - 3000, pingerPendingIntent); } else { alarmMgr.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + keepAliveInterval - 3000, pingerPendingIntent); } } @Override public boolean isNetworkActive() { ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); return (cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isAvailable() && cm.getActiveNetworkInfo().isConnected()); } @Override public void registerObserver(MqttPingerObserver observer) { if (!observers.contains(observer)) { observers.add(observer); } else { Log.d(TAG, "Observer already registered"); } } @Override public void unregisterObserver(MqttPingerObserver observer) { if (!observers.contains(observer)) { observers.remove(observer); } else { Log.d(TAG, "Observer is not registered"); } } // TODO(klee): honor disable background data setting.. }