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