/** * 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(); }