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