• 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.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