• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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