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