package com.samknows.measurement; import android.app.IntentService; import android.content.Context; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.os.PowerManager; import android.util.Log; import com.samknows.libcore.SKPorting; import com.samknows.libcore.SKConstants; import com.samknows.measurement.TestRunner.SKTestRunner; import com.samknows.measurement.environment.TrafficStatsCollector; import com.samknows.measurement.schedule.ScheduleConfig; import com.samknows.measurement.TestRunner.BackgroundTestRunner; import com.samknows.measurement.statemachine.state.StateEnum; import com.samknows.measurement.util.OtherUtils; public class MainService extends IntentService { static final String TAG = "MainService"; public static final String FORCE_EXECUTION_EXTRA = "force_execution"; public static final String EXECUTE_CONTINUOUS_EXTRA = "execute_continuous"; private PowerManager.WakeLock wakeLock; private TrafficStatsCollector collector; private SK2AppSettings appSettings; private static boolean isExecuting; private final static Object sync = new Object(); public MainService() { super(MainService.class.getName()); } //the binder is used by the continuous testing in order to stop //the testing. private final IBinder mMainServiceBinder = new MainServiceBinder(); public class MainServiceBinder extends Binder{ public MainService getService(){ return MainService.this; } } @Override public IBinder onBind(Intent intent) { Log.d(TAG, "+++++DEBUG+++++ MainService onBind"); return mMainServiceBinder; } @Override public void onCreate() { super.onCreate(); Log.d(TAG, "+++++DEBUG+++++ MainService onCreate"); } @Override public void onDestroy() { super.onDestroy(); Log.d(TAG, "+++++DEBUG+++++ MainService onDestroy"); } long accumulatedTestBytes = 0L; @Override protected void onHandleIntent(Intent intent) { accumulatedTestBytes = 0L; Log.d(TAG, "+++++DEBUG+++++ MainService onHandleIntent" + intent.toString()); boolean force_execution = intent.getBooleanExtra(FORCE_EXECUTION_EXTRA, false); try { appSettings = SK2AppSettings.getSK2AppSettingsInstance(); ScheduleConfig config = CachingStorage.getInstance().loadScheduleConfig(); // This looks first at user preferences - if the user has set a true/false value, that value is returned. // otherwise, we return the "background_test" value last delivered from the config/schedule. boolean backgroundTest = SKApplication.getAppInstance().getIsBackgroundTestingEnabledInUserPreferences(); onBegin(); /* * The tests have to be executed when background test is set in the config file and the service * is enabled in the app settings. * Moreover the tests have executed whenever the app has forced the activation (force_execution = true) * In case the device is in roaming the, the test manager shouldn't run any test * * if the intent contains the EXECUTE_CONTINUOUS_EXTRA run the continuous testing procedure and ignore the rest. * */ if((backgroundTest && SKApplication.getAppInstance().getIsBackgroundTestingEnabledInUserPreferences()) || force_execution ) { if (appSettings.run_in_roaming || !OtherUtils.isRoaming(this)) { SKTestRunner.SKTestRunnerObserver observerNull = null; BackgroundTestRunner backgroundTestRunner = new BackgroundTestRunner(observerNull); accumulatedTestBytes = backgroundTestRunner.startTestRunning_RunToEndBlocking_ReturnNumberOfTestBytes(); } else { Log.d(TAG, "+++++DEBUG+++++ Service disabled(roaming), exiting."); OtherUtils.reschedule(this, SKConstants.SERVICE_RESCHEDULE_IF_ROAMING_OR_DATACAP); } } else { if(!backgroundTest) Log.d(TAG, "+++++DEBUG+++++ Service disabled(config file), exiting."); if (!SKApplication.getAppInstance().getIsBackgroundTestingEnabledInUserPreferences()) Log.d(TAG, "+++++DEBUG+++++ Service disabled(manual), exiting."); } } catch (Throwable th) { //if an error happened we want to restart from StateEnum.NONE SKPorting.sAssert(false); appSettings.saveState(StateEnum.NONE); Log.d(TAG, "+++++DEBUG+++++ caught throwable, th=" + th.toString()); Log.d(TAG, "+++++DEBUG+++++ call OtherUtils.rescheduleWakeup"); OtherUtils.rescheduleWakeup(this, appSettings.rescheduleTime); SKPorting.sAssertE(this, "failed in service ", th); } finally { onEnd(); } } public static boolean isExecuting(){ synchronized(sync){ return isExecuting; } } public void onBegin() { Log.d(TAG, "+++++DEBUG+++++ MainService onBegin (begin)"); synchronized(sync){ isExecuting = true; } // obtain wake lock, other way our service may stop executing PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE); wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, MainService.class.getName()); wakeLock.acquire(); // reschedule service in the beginning to ensure it will be started if // killed. OtherUtils.rescheduleRTC(this, appSettings.rescheduleServiceTime); Log.d(TAG, "+++++DEBUG+++++ MainService onBegin - create collector and call start()"); collector = new TrafficStatsCollector(this); collector.start(); Log.d(TAG, "+++++DEBUG+++++ MainService onBegin (end)"); } private void onEnd() { Log.d(TAG, "+++++DEBUG+++++ MainService onEnd (begin)"); if (wakeLock != null) { synchronized (this) { if (wakeLock.isHeld()) { // http://stackoverflow.com/questions/12140844/java-lang-runtimeexception-wakelock-under-locked-c2dm-lib wakeLock.release(); } } } long bytes = collector.finish(); if (bytes == 0) { // If data logging via Android TrafficStats failed to return anything, then // fall-back to using accumulatedTestBytes, which is an accumulation of data // from all tests run, where the values used to accumulated are based on estimated values // in the schedule XML. bytes = accumulatedTestBytes; } appSettings.appendUsedBytes(bytes); if(!SKApplication.getAppInstance().getIsBackgroundTestingEnabledInUserPreferences()){ Log.d(TAG, "+++++DEBUG+++++ MainService onEnd, service not enabled - cancelling alarm"); OtherUtils.cancelAlarm(this); } synchronized(sync){ isExecuting = false; } Log.d(TAG, "+++++DEBUG+++++ MainService onEnd... (end)"); } //Start service public static void poke(Context ctx) { ctx.startService(new Intent(ctx, MainService.class)); } }