• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.phone;
18 
19 import android.app.Notification;
20 import android.app.NotificationManager;
21 import android.app.PendingIntent;
22 import android.app.Service;
23 import android.content.BroadcastReceiver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.os.AsyncResult;
28 import android.os.Binder;
29 import android.os.CountDownTimer;
30 import android.os.Handler;
31 import android.os.IBinder;
32 import android.os.Message;
33 import android.os.UserHandle;
34 import android.sysprop.TelephonyProperties;
35 import android.telephony.TelephonyManager;
36 import android.util.Log;
37 
38 import com.android.internal.telephony.Phone;
39 import com.android.internal.telephony.PhoneConstants;
40 import com.android.internal.telephony.TelephonyIntents;
41 import com.android.internal.telephony.util.NotificationChannelController;
42 
43 import java.text.SimpleDateFormat;
44 
45 /**
46  * Application service that inserts/removes Emergency Callback Mode notification and
47  * updates Emergency Callback Mode countdown clock in the notification
48  *
49  * @see EmergencyCallbackModeExitDialog
50  */
51 public class EmergencyCallbackModeService extends Service {
52 
53     // Default Emergency Callback Mode timeout value
54     private static final long DEFAULT_ECM_EXIT_TIMER_VALUE = 300000L;
55     private static final String LOG_TAG = "EmergencyCallbackModeService";
56 
57     private NotificationManager mNotificationManager = null;
58     private CountDownTimer mTimer = null;
59     private long mTimeLeft = 0;
60     private Phone mPhone = null;
61     private boolean mInEmergencyCall = false;
62 
63     private static final int ECM_TIMER_RESET = 1;
64 
65     private Handler mHandler = new Handler () {
66         public void handleMessage(Message msg) {
67             switch (msg.what) {
68                 case ECM_TIMER_RESET:
69                     resetEcmTimer((AsyncResult) msg.obj);
70                     break;
71             }
72         }
73     };
74 
75     @Override
onCreate()76     public void onCreate() {
77          Phone phoneInEcm = PhoneGlobals.getInstance().getPhoneInEcm();
78         // Check if it is CDMA phone
79         if (phoneInEcm == null || ((phoneInEcm.getPhoneType() != PhoneConstants.PHONE_TYPE_CDMA)
80                 && (phoneInEcm.getImsPhone() == null))) {
81             Log.e(LOG_TAG, "Error! Emergency Callback Mode not supported for " + phoneInEcm);
82             stopSelf();
83             return;
84         }
85 
86         // Register receiver for intents
87         IntentFilter filter = new IntentFilter();
88         filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
89         filter.addAction(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS);
90         registerReceiver(mEcmReceiver, filter);
91 
92         mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
93 
94         // Register ECM timer reset notfication
95         mPhone = phoneInEcm;
96         mPhone.registerForEcmTimerReset(mHandler, ECM_TIMER_RESET, null);
97 
98         startTimerNotification();
99     }
100 
101     @Override
onDestroy()102     public void onDestroy() {
103         if (mPhone != null) {
104             // Unregister receiver
105             unregisterReceiver(mEcmReceiver);
106             // Unregister ECM timer reset notification
107             mPhone.unregisterForEcmTimerReset(mHandler);
108 
109             // Cancel the notification and timer
110             mNotificationManager.cancelAsUser(null, R.string.phone_in_ecm_notification_title,
111                     UserHandle.ALL);
112             mTimer.cancel();
113         }
114     }
115 
116     /**
117      * Listens for Emergency Callback Mode intents
118      */
119     private BroadcastReceiver mEcmReceiver = new BroadcastReceiver() {
120         @Override
121         public void onReceive(Context context, Intent intent) {
122             // Stop the service when phone exits Emergency Callback Mode
123             if (intent.getAction().equals(
124                     TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
125                 if (!intent.getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false)) {
126                     stopSelf();
127                 }
128             }
129             // Show dialog box
130             else if (intent.getAction().equals(
131                     TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS)) {
132                     context.startActivity(
133                             new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS)
134                     .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
135             }
136         }
137     };
138 
139     /**
140      * Start timer notification for Emergency Callback Mode
141      */
startTimerNotification()142     private void startTimerNotification() {
143         // Get Emergency Callback Mode timeout value
144         long ecmTimeout = TelephonyProperties.ecm_exit_timer().orElse(DEFAULT_ECM_EXIT_TIMER_VALUE);
145 
146         // Show the notification
147         showNotification(ecmTimeout);
148 
149         // Start countdown timer for the notification updates
150         if (mTimer != null) {
151             mTimer.cancel();
152         } else {
153             mTimer = new CountDownTimer(ecmTimeout, 1000) {
154 
155                 @Override
156                 public void onTick(long millisUntilFinished) {
157                     mTimeLeft = millisUntilFinished;
158                 }
159 
160                 @Override
161                 public void onFinish() {
162                     //Do nothing
163                 }
164 
165             };
166         }
167         mTimer.start();
168     }
169 
170     /**
171      * Shows notification for Emergency Callback Mode
172      */
showNotification(long millisUntilFinished)173     private void showNotification(long millisUntilFinished) {
174         Phone imsPhone = mPhone.getImsPhone();
175         boolean isInEcm = mPhone.isInEcm() || (imsPhone != null && imsPhone.isInEcm());
176         if (!isInEcm) {
177             Log.i(LOG_TAG, "Asked to show notification but not in ECM mode");
178             if (mTimer != null) {
179                 mTimer.cancel();
180             }
181             return;
182         }
183         final Notification.Builder builder = new Notification.Builder(getApplicationContext());
184         builder.setOngoing(true);
185         builder.setPriority(Notification.PRIORITY_HIGH);
186         builder.setSmallIcon(R.drawable.ic_emergency_callback_mode);
187         builder.setTicker(getText(R.string.phone_entered_ecm_text));
188         builder.setContentTitle(getText(R.string.phone_in_ecm_notification_title));
189         builder.setColor(getResources().getColor(R.color.dialer_theme_color));
190 
191         // PendingIntent to launch Emergency Callback Mode Exit activity if the user selects
192         // this notification
193         Intent intent = new Intent(this, EmergencyCallbackModeExitDialog.class);
194         intent.setAction(EmergencyCallbackModeExitDialog.ACTION_SHOW_ECM_EXIT_DIALOG);
195         PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent,
196                 PendingIntent.FLAG_IMMUTABLE);
197         builder.setContentIntent(contentIntent);
198 
199         // Format notification string
200         String text = null;
201         if(mInEmergencyCall) {
202             text = getText(
203                     // During IMS ECM, data restriction hint should be removed.
204                     (imsPhone != null && imsPhone.isInImsEcm())
205                     ? R.string.phone_in_ecm_call_notification_text_without_data_restriction_hint
206                     : R.string.phone_in_ecm_call_notification_text).toString();
207         } else {
208             // Calculate the time in ms when the notification will be finished.
209             long finishedCountMs = millisUntilFinished + System.currentTimeMillis();
210             builder.setShowWhen(true);
211             builder.setChronometerCountDown(true);
212             builder.setUsesChronometer(true);
213             builder.setWhen(finishedCountMs);
214 
215             String completeTime = SimpleDateFormat.getTimeInstance(SimpleDateFormat.SHORT).format(
216                     finishedCountMs);
217             text = getResources().getString(
218                     // During IMS ECM, data restriction hint should be removed.
219                     (imsPhone != null && imsPhone.isInImsEcm())
220                     ? R.string.phone_in_ecm_notification_complete_time_without_data_restriction_hint
221                     : R.string.phone_in_ecm_notification_complete_time,
222                     completeTime);
223         }
224         builder.setContentText(text);
225         builder.setChannelId(NotificationChannelController.CHANNEL_ID_ALERT);
226 
227         // Show notification
228         mNotificationManager.notifyAsUser(null, R.string.phone_in_ecm_notification_title,
229                 builder.build(), UserHandle.ALL);
230     }
231 
232     /**
233      * Handle ECM_TIMER_RESET notification
234      */
resetEcmTimer(AsyncResult r)235     private void resetEcmTimer(AsyncResult r) {
236         boolean isTimerCanceled = ((Boolean)r.result).booleanValue();
237 
238         if (isTimerCanceled) {
239             mInEmergencyCall = true;
240             mTimer.cancel();
241             showNotification(0);
242         } else {
243             mInEmergencyCall = false;
244             startTimerNotification();
245         }
246     }
247 
248     @Override
onBind(Intent intent)249     public IBinder onBind(Intent intent) {
250         return mBinder;
251     }
252 
253     // This is the object that receives interactions from clients.
254     private final IBinder mBinder = new LocalBinder();
255 
256     /**
257      * Class for clients to access
258      */
259     public class LocalBinder extends Binder {
getService()260         EmergencyCallbackModeService getService() {
261             return EmergencyCallbackModeService.this;
262         }
263     }
264 
265     /**
266      * Returns Emergency Callback Mode timeout value
267      */
getEmergencyCallbackModeTimeout()268     public long getEmergencyCallbackModeTimeout() {
269         return mTimeLeft;
270     }
271 
272     /**
273      * Returns Emergency Callback Mode call state
274      */
getEmergencyCallbackModeCallState()275     public boolean getEmergencyCallbackModeCallState() {
276         return mInEmergencyCall;
277     }
278 }
279