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