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