/**
  * The call() method that broadcast intents before the measurement starts and after the
  * measurement finishes.
  */
 @Override
 public MeasurementResult call() throws MeasurementError {
   MeasurementResult result = null;
   sendStringMsg("Running:\n" + realTask.toString());
   try {
     PhoneUtils.getPhoneUtils().acquireWakeLock();
     setCurrentTask(realTask);
     broadcastMeasurementStart();
     result = realTask.call();
   } finally {
     setCurrentTask(null);
     broadcastMeasurementEnd(result);
     PhoneUtils.getPhoneUtils().releaseWakeLock();
     sendStringMsg("Done running:\n" + realTask.toString());
     persistState();
   }
   return result;
 }
 @Override
 public void run() {
   Logger.i("checking Speedometer service for new tasks");
   lastCheckinTime = Calendar.getInstance();
   try {
     persistState();
     uploadResults();
     getTasksFromServer();
     // Also reset checkin if we get a success
     resetCheckin();
     // Schedule the new tasks
     handleMeasurement();
   } catch (Exception e) {
     /*
      * Executor stops all subsequent execution of a periodic task if a raised
      * exception is uncaught. We catch all undeclared exceptions here
      */
     Logger.e("Unexpected exceptions caught", e);
     if (checkinRetryCnt > Config.MAX_CHECKIN_RETRY_COUNT) {
       /* If we have retried more than MAX_CHECKIN_RETRY_COUNT times upon a checkin failure,
        * we will stop retrying and wait until the next checkin period*/
       resetCheckin();
     } else if (checkinRetryIntervalSec < checkinIntervalSec) {
       Logger.i("Retrying checkin in " + checkinRetryIntervalSec + " seconds");
       /* Use checkinRetryIntentSender so that the periodic checkin schedule will
        * remain intact
        */
       checkinRetryIntentSender =
           PendingIntent.getBroadcast(
               MeasurementScheduler.this,
               0,
               new UpdateIntent("", UpdateIntent.CHECKIN_RETRY_ACTION),
               PendingIntent.FLAG_CANCEL_CURRENT);
       alarmManager.set(
           AlarmManager.RTC_WAKEUP,
           System.currentTimeMillis() + checkinRetryIntervalSec * 1000,
           checkinRetryIntentSender);
       checkinRetryCnt++;
       checkinRetryIntervalSec =
           Math.min(Config.MAX_CHECKIN_RETRY_INTERVAL_SEC, checkinRetryIntervalSec * 2);
     }
   } finally {
     PhoneUtils.getPhoneUtils().releaseWakeLock();
     updateStatus();
   }
 }
  /** Perform a checkin operation. */
  public void handleCheckin(boolean force) {
    if (!userConsented()) {
      Logger.i("Skipping checkin - User has not consented");
      return;
    }

    if (!force && isPauseRequested()) {
      sendStringMsg("Skipping checkin - app is paused");
      return;
    }
    if (!force && !powerManager.canScheduleExperiment()) {
      sendStringMsg("Skipping checkin - below battery threshold");
      return;
    }
    /* The CPU can go back to sleep immediately after onReceive() returns. Acquire
     * the wake lock for the new thread here and release the lock when the thread finishes
     */
    PhoneUtils.getPhoneUtils().acquireWakeLock();
    new Thread(checkinTask).start();
  }
  // Service objects are by nature singletons enforced by Android
  @Override
  public void onCreate() {
    Logger.d("Service onCreate called");
    PhoneUtils.setGlobalContext(this.getApplicationContext());
    phoneUtils = PhoneUtils.getPhoneUtils();
    phoneUtils.registerSignalStrengthListener();
    this.checkin = new Checkin(this);
    this.checkinRetryIntervalSec = Config.MIN_CHECKIN_RETRY_INTERVAL_SEC;
    this.checkinRetryCnt = 0;
    this.checkinTask = new CheckinTask();

    this.pauseRequested = true;
    this.stopRequested = false;

    this.measurementExecutor = Executors.newSingleThreadExecutor();
    this.taskQueue =
        new PriorityBlockingQueue<MeasurementTask>(
            Config.MAX_TASK_QUEUE_SIZE, new TaskComparator());
    this.pendingTasks = new ConcurrentHashMap<MeasurementTask, Future<MeasurementResult>>();

    this.notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    this.alarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
    this.powerManager = new BatteryCapPowerManager(Config.DEFAULT_BATTERY_THRESH_PRECENT, this);

    restoreState();

    // Register activity specific BroadcastReceiver here
    IntentFilter filter = new IntentFilter();
    filter.addAction(UpdateIntent.PREFERENCE_ACTION);
    filter.addAction(UpdateIntent.MSG_ACTION);
    filter.addAction(UpdateIntent.CHECKIN_ACTION);
    filter.addAction(UpdateIntent.CHECKIN_RETRY_ACTION);
    filter.addAction(UpdateIntent.MEASUREMENT_ACTION);
    filter.addAction(UpdateIntent.MEASUREMENT_PROGRESS_UPDATE_ACTION);

    broadcastReceiver =
        new BroadcastReceiver() {
          // Handles various broadcast intents.
          @Override
          public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(UpdateIntent.PREFERENCE_ACTION)) {
              updateFromPreference();
            } else if (intent.getAction().equals(UpdateIntent.CHECKIN_ACTION)
                || intent.getAction().equals(UpdateIntent.CHECKIN_RETRY_ACTION)) {
              Logger.d("Checkin intent received");
              handleCheckin(false);
            } else if (intent.getAction().equals(UpdateIntent.MEASUREMENT_ACTION)) {
              Logger.d("MeasurementIntent intent received");
              handleMeasurement();
            } else if (intent.getAction().equals(UpdateIntent.MEASUREMENT_PROGRESS_UPDATE_ACTION)) {
              Logger.d("MeasurementIntent update intent received");
              if (intent.getIntExtra(UpdateIntent.PROGRESS_PAYLOAD, Config.INVALID_PROGRESS)
                  == Config.MEASUREMENT_END_PROGRESS) {
                if (intent.getStringExtra(UpdateIntent.ERROR_STRING_PAYLOAD) != null) {
                  failedMeasurementCnt++;
                } else {
                  completedMeasurementCnt++;
                }
                updateResultsConsole(intent);
              }
            } else if (intent.getAction().equals(UpdateIntent.MSG_ACTION)) {
              String msg = intent.getExtras().getString(UpdateIntent.STRING_PAYLOAD);
              Date now = Calendar.getInstance().getTime();
              insertStringToConsole(systemConsole, now + "\n\n" + msg);
            }
          }
        };
    this.registerReceiver(broadcastReceiver, filter);
    // TODO(mdw): Make this a user-selectable option
    // startSpeedomterInForeGround();
  }