1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.cellbroadcastreceiver; 18 19 import static com.android.cellbroadcastreceiver.CellBroadcastReceiver.DBG; 20 21 import android.app.AlarmManager; 22 import android.app.PendingIntent; 23 import android.app.Service; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.SharedPreferences; 27 import android.content.res.Resources; 28 import android.media.AudioManager; 29 import android.media.Ringtone; 30 import android.media.RingtoneManager; 31 import android.net.Uri; 32 import android.os.IBinder; 33 import android.os.SystemClock; 34 import android.os.VibrationEffect; 35 import android.os.Vibrator; 36 import android.preference.PreferenceManager; 37 import android.telephony.SubscriptionManager; 38 import android.util.Log; 39 40 import com.android.internal.annotations.VisibleForTesting; 41 42 /** 43 * Manages alert reminder notification. 44 */ 45 public class CellBroadcastAlertReminder extends Service { 46 private static final String TAG = "CellBroadcastAlertReminder"; 47 48 /** Action to wake up and play alert reminder sound. */ 49 @VisibleForTesting 50 public static final String ACTION_PLAY_ALERT_REMINDER = "ACTION_PLAY_ALERT_REMINDER"; 51 52 /** Extra for alert reminder vibration enabled (from settings). */ 53 @VisibleForTesting 54 public static final String ALERT_REMINDER_VIBRATE_EXTRA = "alert_reminder_vibrate_extra"; 55 56 /** 57 * Pending intent for alert reminder. This is static so that we don't have to start the 58 * service in order to cancel any pending reminders when user dismisses the alert dialog. 59 */ 60 private static PendingIntent sPlayReminderIntent; 61 62 /** 63 * Alert reminder for current ringtone being played. 64 */ 65 private static Ringtone sPlayReminderRingtone; 66 67 @Override onBind(Intent intent)68 public IBinder onBind(Intent intent) { 69 return null; 70 } 71 72 @Override onStartCommand(Intent intent, int flags, int startId)73 public int onStartCommand(Intent intent, int flags, int startId) { 74 // No intent or unrecognized action; tell the system not to restart us. 75 if (intent == null || !ACTION_PLAY_ALERT_REMINDER.equals(intent.getAction())) { 76 stopSelf(); 77 return START_NOT_STICKY; 78 } 79 80 log("playing alert reminder"); 81 playAlertReminderSound(intent.getBooleanExtra(ALERT_REMINDER_VIBRATE_EXTRA, true)); 82 83 int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 84 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); 85 if (queueAlertReminder(this, subId, false)) { 86 return START_STICKY; 87 } else { 88 log("no reminders queued"); 89 stopSelf(); 90 return START_NOT_STICKY; 91 } 92 } 93 94 /** 95 * Use the RingtoneManager to play the alert reminder sound. 96 * 97 * @param enableVibration True to enable vibration when the alert reminder tone is playing, 98 * otherwise false. 99 */ playAlertReminderSound(boolean enableVibration)100 private void playAlertReminderSound(boolean enableVibration) { 101 Uri notificationUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); 102 if (notificationUri == null) { 103 loge("Can't get URI for alert reminder sound"); 104 return; 105 } 106 Ringtone r = RingtoneManager.getRingtone(this, notificationUri); 107 108 if (r != null) { 109 r.setStreamType(AudioManager.STREAM_NOTIFICATION); 110 log("playing alert reminder sound"); 111 r.play(); 112 } else { 113 loge("can't get Ringtone for alert reminder sound"); 114 } 115 116 if (enableVibration) { 117 // Vibrate for 500ms. 118 Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); 119 vibrator.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE)); 120 } 121 } 122 123 /** 124 * Helper method to start the alert reminder service to queue the alert reminder. 125 * 126 * @param context Context. 127 * @param subId Subscription index 128 * @param firstTime True if entering this method for the first time, otherwise false. 129 * 130 * @return true if a pending reminder was set; false if there are no more reminders 131 */ 132 @VisibleForTesting queueAlertReminder(Context context, int subId, boolean firstTime)133 public static boolean queueAlertReminder(Context context, int subId, boolean firstTime) { 134 // Stop any alert reminder sound and cancel any previously queued reminders. 135 cancelAlertReminder(); 136 137 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 138 String prefStr = prefs.getString(CellBroadcastSettings.KEY_ALERT_REMINDER_INTERVAL, 139 null); 140 int reminderIntervalMinutes; 141 142 if (prefStr == null) { 143 if (DBG) log("no preference value for alert reminder"); 144 return false; 145 } 146 try { 147 reminderIntervalMinutes = Integer.valueOf(prefStr); 148 } catch (NumberFormatException ignored) { 149 loge("invalid alert reminder interval preference: " + prefStr); 150 return false; 151 } 152 153 if (reminderIntervalMinutes == 0) { 154 if (DBG) log("Reminder is turned off."); 155 return false; 156 } 157 158 // "1" means remind once, so we should do nothing if this is not the first reminder. 159 if (reminderIntervalMinutes == 1 && !firstTime) { 160 if (DBG) log("Not scheduling reminder. Done for now."); 161 return false; 162 } 163 164 if (firstTime) { 165 Resources res = CellBroadcastSettings.getResources(context, subId); 166 int interval = res.getInteger(R.integer.first_reminder_interval_in_min); 167 // If there is first reminder interval configured, use it. 168 if (interval != 0) { 169 reminderIntervalMinutes = interval; 170 } else if (reminderIntervalMinutes == 1) { 171 reminderIntervalMinutes = 2; // "1" = one reminder after 2 minutes 172 } 173 } 174 175 if (DBG) log("queueAlertReminder() in " + reminderIntervalMinutes + " minutes"); 176 177 Intent playIntent = new Intent(context, CellBroadcastAlertReminder.class); 178 playIntent.setAction(ACTION_PLAY_ALERT_REMINDER); 179 playIntent.putExtra(ALERT_REMINDER_VIBRATE_EXTRA, 180 prefs.getBoolean(CellBroadcastSettings.KEY_ENABLE_ALERT_VIBRATE, true)); 181 playIntent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId); 182 sPlayReminderIntent = PendingIntent.getService(context, 0, playIntent, 183 PendingIntent.FLAG_UPDATE_CURRENT); 184 185 AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 186 if (alarmManager == null) { 187 loge("can't get Alarm Service"); 188 return false; 189 } 190 191 // remind user after 2 minutes or 15 minutes 192 long triggerTime = SystemClock.elapsedRealtime() + (reminderIntervalMinutes * 60000); 193 // We use setExact instead of set because this is for emergency reminder. 194 alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, 195 triggerTime, sPlayReminderIntent); 196 log("Set reminder in " + reminderIntervalMinutes + " minutes"); 197 return true; 198 } 199 200 /** 201 * Stops alert reminder and cancels any queued reminders. 202 */ cancelAlertReminder()203 static void cancelAlertReminder() { 204 if (DBG) log("cancelAlertReminder()"); 205 if (sPlayReminderRingtone != null) { 206 if (DBG) log("stopping play reminder ringtone"); 207 sPlayReminderRingtone.stop(); 208 sPlayReminderRingtone = null; 209 } 210 if (sPlayReminderIntent != null) { 211 if (DBG) log("canceling pending play reminder intent"); 212 sPlayReminderIntent.cancel(); 213 sPlayReminderIntent = null; 214 } 215 } 216 log(String msg)217 private static void log(String msg) { 218 Log.d(TAG, msg); 219 } 220 loge(String msg)221 private static void loge(String msg) { 222 Log.e(TAG, msg); 223 } 224 } 225