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.content.res.Resources;
28 import android.graphics.BitmapFactory;
29 import android.os.AsyncResult;
30 import android.os.Binder;
31 import android.os.CountDownTimer;
32 import android.os.Handler;
33 import android.os.IBinder;
34 import android.os.Message;
35 import android.os.SystemProperties;
36 import android.util.Log;
37 
38 import com.android.internal.telephony.cdma.CDMAPhone;
39 import com.android.internal.telephony.Phone;
40 import com.android.internal.telephony.PhoneConstants;
41 import com.android.internal.telephony.PhoneFactory;
42 import com.android.internal.telephony.TelephonyIntents;
43 import com.android.internal.telephony.TelephonyProperties;
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 int DEFAULT_ECM_EXIT_TIMER_VALUE = 300000;
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         // Check if it is CDMA phone
78         if ((PhoneFactory.getDefaultPhone().getPhoneType() != PhoneConstants.PHONE_TYPE_CDMA)
79                 && (PhoneFactory.getDefaultPhone().getImsPhone() == null)) {
80             Log.e(LOG_TAG, "Error! Emergency Callback Mode not supported for " +
81                     PhoneFactory.getDefaultPhone().getPhoneName() + " phones");
82             stopSelf();
83         }
84 
85         // Register receiver for intents
86         IntentFilter filter = new IntentFilter();
87         filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
88         filter.addAction(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS);
89         registerReceiver(mEcmReceiver, filter);
90 
91         mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
92 
93         // Register ECM timer reset notfication
94         mPhone = PhoneFactory.getDefaultPhone();
95         mPhone.registerForEcmTimerReset(mHandler, ECM_TIMER_RESET, null);
96 
97         startTimerNotification();
98     }
99 
100     @Override
onDestroy()101     public void onDestroy() {
102         // Unregister receiver
103         unregisterReceiver(mEcmReceiver);
104         // Unregister ECM timer reset notification
105         mPhone.unregisterForEcmTimerReset(mHandler);
106 
107         // Cancel the notification and timer
108         mNotificationManager.cancel(R.string.phone_in_ecm_notification_title);
109         mTimer.cancel();
110     }
111 
112     /**
113      * Listens for Emergency Callback Mode intents
114      */
115     private BroadcastReceiver mEcmReceiver = new BroadcastReceiver() {
116         @Override
117         public void onReceive(Context context, Intent intent) {
118             // Stop the service when phone exits Emergency Callback Mode
119             if (intent.getAction().equals(
120                     TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
121                 if (intent.getBooleanExtra("phoneinECMState", false) == false) {
122                     stopSelf();
123                 }
124             }
125             // Show dialog box
126             else if (intent.getAction().equals(
127                     TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS)) {
128                     context.startActivity(
129                             new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS)
130                     .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
131             }
132         }
133     };
134 
135     /**
136      * Start timer notification for Emergency Callback Mode
137      */
startTimerNotification()138     private void startTimerNotification() {
139         // Get Emergency Callback Mode timeout value
140         long ecmTimeout = SystemProperties.getLong(
141                     TelephonyProperties.PROPERTY_ECM_EXIT_TIMER, DEFAULT_ECM_EXIT_TIMER_VALUE);
142 
143         // Show the notification
144         showNotification(ecmTimeout);
145 
146         // Start countdown timer for the notification updates
147         if (mTimer != null) {
148             mTimer.cancel();
149         } else {
150             mTimer = new CountDownTimer(ecmTimeout, 1000) {
151 
152                 @Override
153                 public void onTick(long millisUntilFinished) {
154                     mTimeLeft = millisUntilFinished;
155                     EmergencyCallbackModeService.this.showNotification(millisUntilFinished);
156                 }
157 
158                 @Override
159                 public void onFinish() {
160                     //Do nothing
161                 }
162 
163             };
164         }
165         mTimer.start();
166     }
167 
168     /**
169      * Shows notification for Emergency Callback Mode
170      */
showNotification(long millisUntilFinished)171     private void showNotification(long millisUntilFinished) {
172         final boolean isInEcm = Boolean.parseBoolean(
173                 SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE));
174         if (!isInEcm) {
175             Log.i(LOG_TAG, "Asked to show notification but not in ECM mode");
176             if (mTimer != null) {
177                 mTimer.cancel();
178             }
179             return;
180         }
181         final Notification.Builder builder = new Notification.Builder(getApplicationContext());
182         builder.setOngoing(true);
183         builder.setPriority(Notification.PRIORITY_HIGH);
184         builder.setSmallIcon(R.drawable.ic_emergency_callback_mode);
185         builder.setTicker(getText(R.string.phone_entered_ecm_text));
186         builder.setContentTitle(getText(R.string.phone_in_ecm_notification_title));
187         builder.setColor(getResources().getColor(R.color.dialer_theme_color));
188 
189         // PendingIntent to launch Emergency Callback Mode Exit activity if the user selects
190         // this notification
191         PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
192                 new Intent(EmergencyCallbackModeExitDialog.ACTION_SHOW_ECM_EXIT_DIALOG), 0);
193         builder.setContentIntent(contentIntent);
194 
195         // Format notification string
196         String text = null;
197         if(mInEmergencyCall) {
198             text = getText(R.string.phone_in_ecm_call_notification_text).toString();
199         } else {
200             int minutes = (int)(millisUntilFinished / 60000);
201             String time = String.format("%d:%02d", minutes, (millisUntilFinished % 60000) / 1000);
202             text = String.format(getResources().getQuantityText(
203                      R.plurals.phone_in_ecm_notification_time, minutes).toString(), time);
204         }
205         builder.setContentText(text);
206 
207         // Show notification
208         mNotificationManager.notify(R.string.phone_in_ecm_notification_title, builder.build());
209     }
210 
211     /**
212      * Handle ECM_TIMER_RESET notification
213      */
resetEcmTimer(AsyncResult r)214     private void resetEcmTimer(AsyncResult r) {
215         boolean isTimerCanceled = ((Boolean)r.result).booleanValue();
216 
217         if (isTimerCanceled) {
218             mInEmergencyCall = true;
219             mTimer.cancel();
220             showNotification(0);
221         } else {
222             mInEmergencyCall = false;
223             startTimerNotification();
224         }
225     }
226 
227     @Override
onBind(Intent intent)228     public IBinder onBind(Intent intent) {
229         return mBinder;
230     }
231 
232     // This is the object that receives interactions from clients.
233     private final IBinder mBinder = new LocalBinder();
234 
235     /**
236      * Class for clients to access
237      */
238     public class LocalBinder extends Binder {
getService()239         EmergencyCallbackModeService getService() {
240             return EmergencyCallbackModeService.this;
241         }
242     }
243 
244     /**
245      * Returns Emergency Callback Mode timeout value
246      */
getEmergencyCallbackModeTimeout()247     public long getEmergencyCallbackModeTimeout() {
248         return mTimeLeft;
249     }
250 
251     /**
252      * Returns Emergency Callback Mode call state
253      */
getEmergencyCallbackModeCallState()254     public boolean getEmergencyCallbackModeCallState() {
255         return mInEmergencyCall;
256     }
257 }
258