1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 package org.mozilla.mozstumbler.service.uploadthread; 6 7 import android.app.AlarmManager; 8 import android.app.IntentService; 9 import android.app.PendingIntent; 10 import android.content.BroadcastReceiver; 11 import android.content.Context; 12 import android.content.Intent; 13 import android.util.Log; 14 15 import org.mozilla.mozstumbler.service.AppGlobals; 16 import org.mozilla.mozstumbler.service.Prefs; 17 import org.mozilla.mozstumbler.service.stumblerthread.datahandling.DataStorageManager; 18 import org.mozilla.mozstumbler.service.utils.NetworkUtils; 19 20 // Only if data is queued and device awake: check network availability and upload. 21 // MozStumbler use: this alarm is periodic and repeating. 22 // Fennec use: The alarm is single-shot and it is set to run -if there is data in the queue- 23 // under these conditions: 24 // 1) Fennec start/pause (actually gecko start which is ~4 sec after Fennec start). 25 // 2) Changing the pref in Fennec to stumble or not. 26 // 3) Boot intent (and SD card app available intent). 27 // 28 // Threading: 29 // - scheduled from the stumbler thread 30 // - triggered from the main thread 31 // - actual work is done the upload thread (AsyncUploader) 32 public class UploadAlarmReceiver extends BroadcastReceiver { 33 private static final String LOG_TAG = AppGlobals.makeLogTag(UploadAlarmReceiver.class.getSimpleName()); 34 private static final String EXTRA_IS_REPEATING = "is_repeating"; 35 private static boolean sIsAlreadyScheduled; 36 UploadAlarmReceiver()37 public UploadAlarmReceiver() {} 38 39 public static class UploadAlarmService extends IntentService { 40 UploadAlarmService(String name)41 public UploadAlarmService(String name) { 42 super(name); 43 // makes the service START_NOT_STICKY, that is, the service is not auto-restarted 44 setIntentRedelivery(false); 45 } 46 UploadAlarmService()47 public UploadAlarmService() { 48 this(LOG_TAG); 49 } 50 51 @Override onHandleIntent(Intent intent)52 protected void onHandleIntent(Intent intent) { 53 if (intent == null) { 54 return; 55 } 56 boolean isRepeating = intent.getBooleanExtra(EXTRA_IS_REPEATING, true); 57 if (DataStorageManager.getInstance() == null) { 58 DataStorageManager.createGlobalInstance(this, null); 59 } 60 upload(isRepeating); 61 } 62 upload(boolean isRepeating)63 void upload(boolean isRepeating) { 64 if (!isRepeating) { 65 sIsAlreadyScheduled = false; 66 } 67 68 // Defensive approach: if it is too old, delete all data 69 long oldestMs = DataStorageManager.getInstance().getOldestBatchTimeMs(); 70 int maxWeeks = DataStorageManager.getInstance().getMaxWeeksStored(); 71 if (oldestMs > 0) { 72 long currentTime = System.currentTimeMillis(); 73 long msPerWeek = 604800 * 1000; 74 if (currentTime - oldestMs > maxWeeks * msPerWeek) { 75 DataStorageManager.getInstance().deleteAll(); 76 UploadAlarmReceiver.cancelAlarm(this, isRepeating); 77 return; 78 } 79 } 80 81 NetworkUtils networkUtils = new NetworkUtils(this); 82 if (networkUtils.isWifiAvailable() && 83 !AsyncUploader.isUploading()) { 84 Log.d(LOG_TAG, "Alarm upload(), call AsyncUploader"); 85 AsyncUploader.AsyncUploadArgs settings = 86 new AsyncUploader.AsyncUploadArgs(networkUtils, 87 Prefs.getInstance(this).getWifiScanAlways(), 88 Prefs.getInstance(this).getUseWifiOnly()); 89 AsyncUploader uploader = new AsyncUploader(settings, null); 90 uploader.setNickname(Prefs.getInstance(this).getNickname()); 91 uploader.execute(); 92 // we could listen for completion and cancel, instead, cancel on next alarm when db empty 93 } 94 } 95 } 96 createIntent(Context c, boolean isRepeating)97 static PendingIntent createIntent(Context c, boolean isRepeating) { 98 Intent intent = new Intent(c, UploadAlarmReceiver.class); 99 intent.putExtra(EXTRA_IS_REPEATING, isRepeating); 100 PendingIntent pi = PendingIntent.getBroadcast(c, 0, intent, 0); 101 return pi; 102 } 103 cancelAlarm(Context c, boolean isRepeating)104 public static void cancelAlarm(Context c, boolean isRepeating) { 105 Log.d(LOG_TAG, "cancelAlarm"); 106 // this is to stop scheduleAlarm from constantly rescheduling, not to guard cancellation. 107 sIsAlreadyScheduled = false; 108 AlarmManager alarmManager = (AlarmManager) c.getSystemService(Context.ALARM_SERVICE); 109 PendingIntent pi = createIntent(c, isRepeating); 110 alarmManager.cancel(pi); 111 } 112 scheduleAlarm(Context c, long secondsToWait, boolean isRepeating)113 public static void scheduleAlarm(Context c, long secondsToWait, boolean isRepeating) { 114 if (sIsAlreadyScheduled) { 115 return; 116 } 117 118 long intervalMsec = secondsToWait * 1000; 119 Log.d(LOG_TAG, "schedule alarm (ms):" + intervalMsec); 120 121 sIsAlreadyScheduled = true; 122 AlarmManager alarmManager = (AlarmManager) c.getSystemService(Context.ALARM_SERVICE); 123 PendingIntent pi = createIntent(c, isRepeating); 124 125 long triggerAtMs = System.currentTimeMillis() + intervalMsec; 126 if (isRepeating) { 127 alarmManager.setInexactRepeating(AlarmManager.RTC, triggerAtMs, intervalMsec, pi); 128 } else { 129 alarmManager.set(AlarmManager.RTC, triggerAtMs, pi); 130 } 131 } 132 133 @Override onReceive(final Context context, Intent intent)134 public void onReceive(final Context context, Intent intent) { 135 Intent startServiceIntent = new Intent(context, UploadAlarmService.class); 136 context.startService(startServiceIntent); 137 } 138 } 139