package info.guardianproject.otr.app.im.service; import info.guardianproject.otr.app.im.app.NetworkConnectivityListener; import info.guardianproject.otr.app.im.provider.Imps; import android.app.AlarmManager; import android.app.PendingIntent; import android.app.Service; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.SystemClock; import android.util.Log; /** * This service exists because a foreground service receiving a wakeup alarm from the OS will cause * the service process to lose its foreground status and be killed. This service runs in the UI process instead. * * @author devrandom * */ public class HeartbeatService extends Service { public static final String HEARTBEAT_ACTION = "info.guardianproject.otr.app.im.SERVICE.HEARTBEAT"; public static final String NETWORK_STATE_ACTION = "info.guardianproject.otr.app.im.SERVICE.NETWORK_STATE"; public static final String NETWORK_STATE_EXTRA = "state"; public static final String NETWORK_INFO_EXTRA = "info"; private static final String TAG = "GB.HeartbeatService"; private PendingIntent mPendingIntent; private Intent mRelayIntent; private ServiceHandler mServiceHandler; private NetworkConnectivityListener mNetworkConnectivityListener; private static final int EVENT_NETWORK_STATE_CHANGED = 200; // Our heartbeat interval in seconds. // The user controlled preference heartbeat interval is in these units (i.e. minutes). private static final long HEARTBEAT_INTERVAL = 1000 * 60; private long mHeartbeatInterval = HEARTBEAT_INTERVAL; @Override public void onCreate() { super.onCreate(); this.mPendingIntent = PendingIntent.getService(this, 0, new Intent(HEARTBEAT_ACTION, null, this, HeartbeatService.class), 0); this.mRelayIntent = new Intent(HEARTBEAT_ACTION, null, this, RemoteImService.class); Imps.ProviderSettings.QueryMap settings = getGlobalSettings(); if (settings != null) { mHeartbeatInterval = settings.getHeartbeatInterval() * HEARTBEAT_INTERVAL; settings.close(); } startHeartbeat(mHeartbeatInterval); mServiceHandler = new ServiceHandler(); mNetworkConnectivityListener = new NetworkConnectivityListener(); NetworkConnectivityListener.registerHandler(mServiceHandler, EVENT_NETWORK_STATE_CHANGED); mNetworkConnectivityListener.startListening(this); } void startHeartbeat(long interval) { AlarmManager alarmManager = (AlarmManager)this.getSystemService(ALARM_SERVICE); alarmManager.cancel(mPendingIntent); if (interval > 0) { //alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + interval, interval, mPendingIntent); alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + interval, interval, mPendingIntent); } } @Override public void onDestroy() { startHeartbeat(0); NetworkConnectivityListener.unregisterHandler(mServiceHandler); mNetworkConnectivityListener.stopListening(); mNetworkConnectivityListener = null; super.onDestroy(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (intent != null && HEARTBEAT_ACTION.equals(intent.getAction())) { startHeartbeat(mHeartbeatInterval); startService(mRelayIntent); } return START_STICKY; } @Override public IBinder onBind(Intent arg0) { return null; } public static void startBeating(Context context) { context.startService(new Intent(context, HeartbeatService.class)); } public static void stopBeating(Context context) { context.stopService(new Intent(context, HeartbeatService.class)); } void networkStateChanged() { // Callback may be async if (mNetworkConnectivityListener == null) return; Intent intent = new Intent(NETWORK_STATE_ACTION, null, this, RemoteImService.class); intent.putExtra(NETWORK_INFO_EXTRA, mNetworkConnectivityListener.getNetworkInfo()); intent.putExtra(NETWORK_STATE_EXTRA, mNetworkConnectivityListener.getState().ordinal()); startService(intent); } private final class ServiceHandler extends Handler { public ServiceHandler() { } @Override public void handleMessage(Message msg) { switch (msg.what) { case EVENT_NETWORK_STATE_CHANGED: // Log.d(TAG, "network"); networkStateChanged(); break; default: } } } private Imps.ProviderSettings.QueryMap getGlobalSettings() { ContentResolver contentResolver = getContentResolver(); Cursor cursor = contentResolver.query(Imps.ProviderSettings.CONTENT_URI,new String[] {Imps.ProviderSettings.NAME, Imps.ProviderSettings.VALUE},Imps.ProviderSettings.PROVIDER + "=?",new String[] { Long.toString(Imps.ProviderSettings.PROVIDER_ID_FOR_GLOBAL_SETTINGS)},null); if (cursor == null) return null; return new Imps.ProviderSettings.QueryMap(cursor, contentResolver, Imps.ProviderSettings.PROVIDER_ID_FOR_GLOBAL_SETTINGS, true, null); } }