blob: 012a6705a25cb4dc5500e8e564afffa10f3a0ed8 [file] [log] [blame]
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001/*
2 * Copyright (C) 2009 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
17package com.android.phone;
18
19import android.app.Notification;
20import android.app.NotificationManager;
21import android.app.PendingIntent;
22import android.app.Service;
23import android.content.BroadcastReceiver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070027import android.os.AsyncResult;
28import android.os.Binder;
29import android.os.CountDownTimer;
30import android.os.Handler;
31import android.os.IBinder;
32import android.os.Message;
Inseob Kim8d298d42018-12-13 17:11:34 +090033import android.sysprop.TelephonyProperties;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070034import android.util.Log;
35
Santos Cordon7d4ddf62013-07-10 11:58:08 -070036import com.android.internal.telephony.Phone;
37import com.android.internal.telephony.PhoneConstants;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070038import com.android.internal.telephony.TelephonyIntents;
fionaxu8b7620d2017-05-01 16:22:17 -070039import com.android.internal.telephony.util.NotificationChannelController;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070040
Brad Ebingercd3601b2017-05-09 15:27:27 -070041import java.text.SimpleDateFormat;
42
Santos Cordon7d4ddf62013-07-10 11:58:08 -070043/**
44 * Application service that inserts/removes Emergency Callback Mode notification and
45 * updates Emergency Callback Mode countdown clock in the notification
46 *
47 * @see EmergencyCallbackModeExitDialog
48 */
49public class EmergencyCallbackModeService extends Service {
50
51 // Default Emergency Callback Mode timeout value
Inseob Kim8d298d42018-12-13 17:11:34 +090052 private static final long DEFAULT_ECM_EXIT_TIMER_VALUE = 300000L;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070053 private static final String LOG_TAG = "EmergencyCallbackModeService";
54
55 private NotificationManager mNotificationManager = null;
56 private CountDownTimer mTimer = null;
57 private long mTimeLeft = 0;
58 private Phone mPhone = null;
59 private boolean mInEmergencyCall = false;
60
61 private static final int ECM_TIMER_RESET = 1;
62
63 private Handler mHandler = new Handler () {
64 public void handleMessage(Message msg) {
65 switch (msg.what) {
66 case ECM_TIMER_RESET:
67 resetEcmTimer((AsyncResult) msg.obj);
68 break;
69 }
70 }
71 };
72
73 @Override
74 public void onCreate() {
Sandeep Kuntade73a6a2014-10-15 18:45:56 +053075 Phone phoneInEcm = PhoneGlobals.getInstance().getPhoneInEcm();
Santos Cordon7d4ddf62013-07-10 11:58:08 -070076 // Check if it is CDMA phone
Sandeep Kuntade73a6a2014-10-15 18:45:56 +053077 if (phoneInEcm == null || ((phoneInEcm.getPhoneType() != PhoneConstants.PHONE_TYPE_CDMA)
78 && (phoneInEcm.getImsPhone() == null))) {
79 Log.e(LOG_TAG, "Error! Emergency Callback Mode not supported for " + phoneInEcm);
Santos Cordon7d4ddf62013-07-10 11:58:08 -070080 stopSelf();
Sandeep Kuntade73a6a2014-10-15 18:45:56 +053081 return;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070082 }
83
84 // Register receiver for intents
85 IntentFilter filter = new IntentFilter();
86 filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
87 filter.addAction(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS);
88 registerReceiver(mEcmReceiver, filter);
89
90 mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
91
92 // Register ECM timer reset notfication
Sandeep Kuntade73a6a2014-10-15 18:45:56 +053093 mPhone = phoneInEcm;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070094 mPhone.registerForEcmTimerReset(mHandler, ECM_TIMER_RESET, null);
95
96 startTimerNotification();
97 }
98
99 @Override
100 public void onDestroy() {
Sandeep Kunta55a6ae82015-12-14 17:49:02 +0530101 if (mPhone != null) {
102 // Unregister receiver
103 unregisterReceiver(mEcmReceiver);
104 // Unregister ECM timer reset notification
105 mPhone.unregisterForEcmTimerReset(mHandler);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700106
Sandeep Kunta55a6ae82015-12-14 17:49:02 +0530107 // Cancel the notification and timer
108 mNotificationManager.cancel(R.string.phone_in_ecm_notification_title);
109 mTimer.cancel();
110 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700111 }
112
113 /**
114 * Listens for Emergency Callback Mode intents
115 */
116 private BroadcastReceiver mEcmReceiver = new BroadcastReceiver() {
117 @Override
118 public void onReceive(Context context, Intent intent) {
119 // Stop the service when phone exits Emergency Callback Mode
120 if (intent.getAction().equals(
121 TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
122 if (intent.getBooleanExtra("phoneinECMState", false) == false) {
123 stopSelf();
124 }
125 }
126 // Show dialog box
127 else if (intent.getAction().equals(
128 TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS)) {
129 context.startActivity(
130 new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS)
131 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
132 }
133 }
134 };
135
136 /**
137 * Start timer notification for Emergency Callback Mode
138 */
139 private void startTimerNotification() {
140 // Get Emergency Callback Mode timeout value
Inseob Kim8d298d42018-12-13 17:11:34 +0900141 long ecmTimeout = TelephonyProperties.ecm_exit_timer().orElse(DEFAULT_ECM_EXIT_TIMER_VALUE);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700142
143 // Show the notification
144 showNotification(ecmTimeout);
145
146 // Start countdown timer for the notification updates
Yorke Lee34a72cb2014-10-12 13:17:04 -0700147 if (mTimer != null) {
148 mTimer.cancel();
149 } else {
150 mTimer = new CountDownTimer(ecmTimeout, 1000) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700151
Yorke Lee34a72cb2014-10-12 13:17:04 -0700152 @Override
153 public void onTick(long millisUntilFinished) {
154 mTimeLeft = millisUntilFinished;
Yorke Lee34a72cb2014-10-12 13:17:04 -0700155 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700156
Yorke Lee34a72cb2014-10-12 13:17:04 -0700157 @Override
158 public void onFinish() {
159 //Do nothing
160 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700161
Yorke Lee34a72cb2014-10-12 13:17:04 -0700162 };
163 }
164 mTimer.start();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700165 }
166
167 /**
168 * Shows notification for Emergency Callback Mode
169 */
170 private void showNotification(long millisUntilFinished) {
Amit Mahajan6a2f8752016-07-25 11:38:10 -0700171 Phone imsPhone = mPhone.getImsPhone();
172 boolean isInEcm = mPhone.isInEcm() || (imsPhone != null && imsPhone.isInEcm());
Yorke Lee34a72cb2014-10-12 13:17:04 -0700173 if (!isInEcm) {
174 Log.i(LOG_TAG, "Asked to show notification but not in ECM mode");
175 if (mTimer != null) {
176 mTimer.cancel();
177 }
178 return;
179 }
fionaxu8b7620d2017-05-01 16:22:17 -0700180 final Notification.Builder builder = new Notification.Builder(getApplicationContext());
Tyler Gunn625eb0b2014-08-27 20:37:32 -0700181 builder.setOngoing(true);
182 builder.setPriority(Notification.PRIORITY_HIGH);
183 builder.setSmallIcon(R.drawable.ic_emergency_callback_mode);
184 builder.setTicker(getText(R.string.phone_entered_ecm_text));
185 builder.setContentTitle(getText(R.string.phone_in_ecm_notification_title));
186 builder.setColor(getResources().getColor(R.color.dialer_theme_color));
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700187
188 // PendingIntent to launch Emergency Callback Mode Exit activity if the user selects
189 // this notification
190 PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
191 new Intent(EmergencyCallbackModeExitDialog.ACTION_SHOW_ECM_EXIT_DIALOG), 0);
Tyler Gunn625eb0b2014-08-27 20:37:32 -0700192 builder.setContentIntent(contentIntent);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700193
194 // Format notification string
195 String text = null;
196 if(mInEmergencyCall) {
Wei Huang821f3d92019-04-11 11:19:13 +0900197 text = getText(
198 // During IMS ECM, data restriction hint should be removed.
199 (imsPhone != null && imsPhone.isInImsEcm())
200 ? R.string.phone_in_ecm_call_notification_text_without_data_restriction_hint
201 : R.string.phone_in_ecm_call_notification_text).toString();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700202 } else {
Brad Ebingercd3601b2017-05-09 15:27:27 -0700203 // Calculate the time in ms when the notification will be finished.
204 long finishedCountMs = millisUntilFinished + System.currentTimeMillis();
205 builder.setShowWhen(true);
206 builder.setChronometerCountDown(true);
207 builder.setUsesChronometer(true);
208 builder.setWhen(finishedCountMs);
209
210 String completeTime = SimpleDateFormat.getTimeInstance(SimpleDateFormat.SHORT).format(
211 finishedCountMs);
Wei Huang821f3d92019-04-11 11:19:13 +0900212 text = getResources().getString(
213 // During IMS ECM, data restriction hint should be removed.
214 (imsPhone != null && imsPhone.isInImsEcm())
215 ? R.string.phone_in_ecm_notification_complete_time_without_data_restriction_hint
216 : R.string.phone_in_ecm_notification_complete_time,
Brad Ebingercd3601b2017-05-09 15:27:27 -0700217 completeTime);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700218 }
Tyler Gunn625eb0b2014-08-27 20:37:32 -0700219 builder.setContentText(text);
fionaxu8b7620d2017-05-01 16:22:17 -0700220 builder.setChannelId(NotificationChannelController.CHANNEL_ID_ALERT);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700221
222 // Show notification
Tyler Gunn625eb0b2014-08-27 20:37:32 -0700223 mNotificationManager.notify(R.string.phone_in_ecm_notification_title, builder.build());
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700224 }
225
226 /**
227 * Handle ECM_TIMER_RESET notification
228 */
229 private void resetEcmTimer(AsyncResult r) {
230 boolean isTimerCanceled = ((Boolean)r.result).booleanValue();
231
232 if (isTimerCanceled) {
233 mInEmergencyCall = true;
234 mTimer.cancel();
235 showNotification(0);
236 } else {
237 mInEmergencyCall = false;
238 startTimerNotification();
239 }
240 }
241
242 @Override
243 public IBinder onBind(Intent intent) {
244 return mBinder;
245 }
246
247 // This is the object that receives interactions from clients.
248 private final IBinder mBinder = new LocalBinder();
249
250 /**
251 * Class for clients to access
252 */
253 public class LocalBinder extends Binder {
254 EmergencyCallbackModeService getService() {
255 return EmergencyCallbackModeService.this;
256 }
257 }
258
259 /**
260 * Returns Emergency Callback Mode timeout value
261 */
262 public long getEmergencyCallbackModeTimeout() {
263 return mTimeLeft;
264 }
265
266 /**
267 * Returns Emergency Callback Mode call state
268 */
269 public boolean getEmergencyCallbackModeCallState() {
270 return mInEmergencyCall;
271 }
272}