edu.cmu.cylab.starslinger.Service.java Source code

Java tutorial

Introduction

Here is the source code for edu.cmu.cylab.starslinger.Service.java

Source

/*
 * The MIT License (MIT)
 * 
 * Copyright (c) 2010-2014 Carnegie Mellon University
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package edu.cmu.cylab.starslinger;

import java.util.Date;

import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.provider.Settings;
import android.support.v4.app.NotificationCompat;
import android.text.TextUtils;
import edu.cmu.cylab.starslinger.model.RecipientDbAdapter;
import edu.cmu.cylab.starslinger.view.HomeActivity;

public class Service extends android.app.Service {
    private final IBinder mBinder = new LocalBinder();

    private long mPassPhraseCacheTtl = 15;
    private Handler mCacheHandler = new Handler();
    static private boolean mIsRunning = false;

    private BroadcastReceiver mAirplaneModeReceiver = new BroadcastReceiver() {
        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
        @Override
        public void onReceive(Context context, Intent intent) {
            boolean airplaneOn = false;
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
                airplaneOn = Settings.System.getInt(context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON,
                        0) != 0;
            } else {
                airplaneOn = Settings.Global.getInt(context.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON,
                        0) != 0;
            }

            // logout when in airplane mode
            if (airplaneOn) {
                SafeSlinger.removeCachedPassPhrase(SafeSlingerPrefs.getKeyIdString());
                stopForeground(true);
            }
        }
    };

    private Runnable mCacheTask = new Runnable() {

        @Override
        public void run() {
            mPassPhraseCacheTtl = SafeSlingerPrefs.getPassPhraseCacheTtl();

            long delay = mPassPhraseCacheTtl * 1000 / 2;

            // make sure the delay is not longer than one minute
            if (delay > 60000) {
                delay = 60000;
            }

            delay = SafeSlinger.cleanUpCache(mPassPhraseCacheTtl, delay);
            // don't check too often, even if we were close
            if (delay < 5000) {
                delay = 5000;
            }

            // ensure service stops when passphrase is absent
            if (SafeSlinger.isCacheEmpty()) {
                stopForeground(true);
            }

            mCacheHandler.postDelayed(this, delay);
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();

        mIsRunning = true;

        registerReceiver(mAirplaneModeReceiver, new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        stopForeground(true);
        mIsRunning = false;

        // attempt to unregister, however we can safely ignore the
        // "IllegalArgumentException: Receiver not registered" called when
        // some hardware experiences a race condition here.
        try {
            unregisterReceiver(mAirplaneModeReceiver);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);

        mPassPhraseCacheTtl = SafeSlingerPrefs.getPassPhraseCacheTtl();

        if (mPassPhraseCacheTtl < 15) {
            mPassPhraseCacheTtl = 15;
        }

        // ensure service runs when passphrase is present
        if (SafeSlinger.isCacheEmpty()) {
            stopForeground(true);
        } else {
            startForeground(HomeActivity.NOTIFY_PASS_CACHED_ID, createCacheNotification());
        }

        // only query exchange status on startup
        queryExchangeStatus();

        // immediate backup delay not critical, it's an optional service
        queryBackupStatus();

        mCacheHandler.removeCallbacks(mCacheTask);
        mCacheHandler.postDelayed(mCacheTask, 1000);
    }

    static public boolean isRunning() {
        return mIsRunning;
    }

    public class LocalBinder extends Binder {
        Service getService() {
            return Service.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    private void queryBackupStatus() {
        long now = new Date().getTime();
        long request = SafeSlingerPrefs.getBackupRequestDate();
        long delayed = (request) + SafeSlingerConfig.BACKUP_DELAY_WARN_MS;
        long complete = SafeSlingerPrefs.getBackupCompleteDate();
        boolean reminder = SafeSlingerPrefs.getRemindBackupDelay();
        boolean exchanged = false;

        // We don't need to give the first backup delayed reminder before the
        // first time a user has exchanged any keys
        RecipientDbAdapter dbRecipient = RecipientDbAdapter.openInstance(this);
        int trustRecips = dbRecipient.getTrustedRecipientCount();
        if (trustRecips > 0) {
            SafeSlingerPrefs.setFirstExchangeComplete(true);
            exchanged = true;
        }

        if (request <= 0) {
            // if never backed up, begin a request...
            SafeSlinger.queueBackup();
        } else if (reminder && exchanged && request > complete && now > delayed) {
            // if backup delayed, send user notification...
            String ns = Context.NOTIFICATION_SERVICE;
            NotificationManager nm = (NotificationManager) getSystemService(ns);
            Notification n = createBackupDelayNotification(request);
            nm.notify(HomeActivity.NOTIFY_BACKUP_DELAY_ID, n);

            // make another request for backup...
            SafeSlinger.queueBackup();
        }
    }

    private void queryExchangeStatus() {
        boolean reminder = SafeSlingerPrefs.getShowSlingKeysReminder();
        boolean exchanged = false;
        boolean setupComplete = !TextUtils.isEmpty(SafeSlingerPrefs.getKeyIdString());
        String ns = Context.NOTIFICATION_SERVICE;
        NotificationManager nm = (NotificationManager) getSystemService(ns);

        // update exchanged status for older versions
        RecipientDbAdapter dbRecipient = RecipientDbAdapter.openInstance(this);
        int trustRecips = dbRecipient.getTrustedRecipientCount();
        if (trustRecips > 0) {
            SafeSlingerPrefs.setFirstExchangeComplete(true);
            exchanged = true;
        }

        // if keys exist, and recipients = 0, remind user to sling keys
        if (setupComplete && !exchanged && reminder) {
            Notification n = createSlingKeysReminderNotification();
            nm.notify(HomeActivity.NOTIFY_SLINGKEYS_REMIND_ID, n);
        } else {
            nm.cancel(HomeActivity.NOTIFY_SLINGKEYS_REMIND_ID);
        }

        // show once per query only
        SafeSlingerPrefs.setShowSlingKeysReminder(false);
    }

    public Notification createBackupDelayNotification(long request) {
        String tickerText = getString(R.string.label_SafeSlingerBackupDelayed);
        String contentTitle = getString(R.string.label_SafeSlingerBackupDelayed);
        String contentText = getString(R.string.label_TouchToConfigureBackupSettingsAndroid);
        Intent intent = new Intent(Service.this, HomeActivity.class);
        intent.setAction(SafeSlingerConfig.Intent.ACTION_BACKUPNOTIFY);
        PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent,
                PendingIntent.FLAG_UPDATE_CURRENT);
        Context ctx = SafeSlinger.getApplication();
        NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx)//
                .setContentIntent(contentIntent)//
                .setSmallIcon(R.drawable.ic_stat_notify_backup)//
                .setTicker(tickerText)//
                .setWhen(request)//
                .setAutoCancel(true)//
                .setContentTitle(contentTitle)//
                .setContentText(contentText)//
                .setVisibility(NotificationCompat.VISIBILITY_SECRET);

        return builder.build();
    }

    public Notification createCacheNotification() {
        String timeout = Integer.toString(SafeSlingerPrefs.getPassPhraseCacheTtl());
        String[] entries = getResources().getStringArray(R.array.pass_phrase_cache_ttl_entries);
        String[] values = getResources().getStringArray(R.array.pass_phrase_cache_ttl_values);
        String setting = getString(R.string.label_undefinedTypeLabel);
        for (int i = 0; i < values.length; i++) {
            if (timeout.equals(values[i]) && entries.length == values.length) {
                setting = entries[i];
                break;
            }
        }

        String tickerText = getString(R.string.label_PassPhraseIsCached);
        String contentTitle = String.format("%s: %s", getString(R.string.label_PassPhraseIsCached), setting);
        String contentText = getString(R.string.label_TouchToConfigureCacheTimeout);
        Intent intent = new Intent(Service.this, HomeActivity.class);
        intent.setAction(SafeSlingerConfig.Intent.ACTION_CHANGESETTINGS);
        PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent,
                PendingIntent.FLAG_UPDATE_CURRENT);
        Context ctx = SafeSlinger.getApplication();
        NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx)//
                .setContentIntent(contentIntent)//
                .setSmallIcon(R.drawable.ic_stat_notify_cache)//
                .setTicker(tickerText)//
                .setContentTitle(contentTitle)//
                .setContentText(contentText)//
                .setWhen(0)//
                .setVisibility(NotificationCompat.VISIBILITY_SECRET);

        return builder.build();
    }

    public Notification createSlingKeysReminderNotification() {
        String tickerText = String.format("%s %s", getString(R.string.app_name),
                getString(R.string.menu_TagExchange));
        String contentTitle = String.format("%s %s", getString(R.string.app_name),
                getString(R.string.menu_TagExchange));
        String contentText = getString(R.string.error_ReminderToSlingKeysToUseApp);
        Intent intent = new Intent(Service.this, HomeActivity.class);
        intent.setAction(SafeSlingerConfig.Intent.ACTION_SLINGKEYSNOTIFY);
        PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent,
                PendingIntent.FLAG_UPDATE_CURRENT);
        Context ctx = SafeSlinger.getApplication();
        NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx)//
                .setContentIntent(contentIntent)//
                .setSmallIcon(R.drawable.ic_stat_notify_msg)//
                .setAutoCancel(true)//
                .setTicker(tickerText)//
                .setContentTitle(contentTitle)//
                .setContentText(contentText)//
                .setWhen(0)//
                .setVisibility(NotificationCompat.VISIBILITY_SECRET);

        return builder.build();
    }

}