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 PendingIntent contentIntent = PendingIntent.getActivity(this, 0, 194 new Intent(EmergencyCallbackModeExitDialog.ACTION_SHOW_ECM_EXIT_DIALOG), 0); 195 builder.setContentIntent(contentIntent); 196 197 // Format notification string 198 String text = null; 199 if(mInEmergencyCall) { 200 text = getText( 201 // During IMS ECM, data restriction hint should be removed. 202 (imsPhone != null && imsPhone.isInImsEcm()) 203 ? R.string.phone_in_ecm_call_notification_text_without_data_restriction_hint 204 : R.string.phone_in_ecm_call_notification_text).toString(); 205 } else { 206 // Calculate the time in ms when the notification will be finished. 207 long finishedCountMs = millisUntilFinished + System.currentTimeMillis(); 208 builder.setShowWhen(true); 209 builder.setChronometerCountDown(true); 210 builder.setUsesChronometer(true); 211 builder.setWhen(finishedCountMs); 212 213 String completeTime = SimpleDateFormat.getTimeInstance(SimpleDateFormat.SHORT).format( 214 finishedCountMs); 215 text = getResources().getString( 216 // During IMS ECM, data restriction hint should be removed. 217 (imsPhone != null && imsPhone.isInImsEcm()) 218 ? R.string.phone_in_ecm_notification_complete_time_without_data_restriction_hint 219 : R.string.phone_in_ecm_notification_complete_time, 220 completeTime); 221 } 222 builder.setContentText(text); 223 builder.setChannelId(NotificationChannelController.CHANNEL_ID_ALERT); 224 225 // Show notification 226 mNotificationManager.notifyAsUser(null, R.string.phone_in_ecm_notification_title, 227 builder.build(), UserHandle.ALL); 228 } 229 230 /** 231 * Handle ECM_TIMER_RESET notification 232 */ resetEcmTimer(AsyncResult r)233 private void resetEcmTimer(AsyncResult r) { 234 boolean isTimerCanceled = ((Boolean)r.result).booleanValue(); 235 236 if (isTimerCanceled) { 237 mInEmergencyCall = true; 238 mTimer.cancel(); 239 showNotification(0); 240 } else { 241 mInEmergencyCall = false; 242 startTimerNotification(); 243 } 244 } 245 246 @Override onBind(Intent intent)247 public IBinder onBind(Intent intent) { 248 return mBinder; 249 } 250 251 // This is the object that receives interactions from clients. 252 private final IBinder mBinder = new LocalBinder(); 253 254 /** 255 * Class for clients to access 256 */ 257 public class LocalBinder extends Binder { getService()258 EmergencyCallbackModeService getService() { 259 return EmergencyCallbackModeService.this; 260 } 261 } 262 263 /** 264 * Returns Emergency Callback Mode timeout value 265 */ getEmergencyCallbackModeTimeout()266 public long getEmergencyCallbackModeTimeout() { 267 return mTimeLeft; 268 } 269 270 /** 271 * Returns Emergency Callback Mode call state 272 */ getEmergencyCallbackModeCallState()273 public boolean getEmergencyCallbackModeCallState() { 274 return mInEmergencyCall; 275 } 276 } 277