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
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;
27import android.content.res.Resources;
28import android.graphics.BitmapFactory;
29import android.os.AsyncResult;
30import android.os.Binder;
31import android.os.CountDownTimer;
32import android.os.Handler;
33import android.os.IBinder;
34import android.os.Message;
35import android.os.SystemProperties;
36import android.util.Log;
37
38import com.android.internal.telephony.Phone;
39import com.android.internal.telephony.PhoneConstants;
40import com.android.internal.telephony.PhoneFactory;
41import com.android.internal.telephony.TelephonyIntents;
42import com.android.internal.telephony.TelephonyProperties;
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 */
50public 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
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
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     */
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                    EmergencyCallbackModeService.this.showNotification(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     */
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(R.string.phone_in_ecm_call_notification_text).toString();
201        } else {
202            int minutes = (int)(millisUntilFinished / 60000);
203            String time = String.format("%d:%02d", minutes, (millisUntilFinished % 60000) / 1000);
204            text = String.format(getResources().getQuantityText(
205                     R.plurals.phone_in_ecm_notification_time, minutes).toString(), time);
206        }
207        builder.setContentText(text);
208
209        // Show notification
210        mNotificationManager.notify(R.string.phone_in_ecm_notification_title, builder.build());
211    }
212
213    /**
214     * Handle ECM_TIMER_RESET notification
215     */
216    private void resetEcmTimer(AsyncResult r) {
217        boolean isTimerCanceled = ((Boolean)r.result).booleanValue();
218
219        if (isTimerCanceled) {
220            mInEmergencyCall = true;
221            mTimer.cancel();
222            showNotification(0);
223        } else {
224            mInEmergencyCall = false;
225            startTimerNotification();
226        }
227    }
228
229    @Override
230    public IBinder onBind(Intent intent) {
231        return mBinder;
232    }
233
234    // This is the object that receives interactions from clients.
235    private final IBinder mBinder = new LocalBinder();
236
237    /**
238     * Class for clients to access
239     */
240    public class LocalBinder extends Binder {
241        EmergencyCallbackModeService getService() {
242            return EmergencyCallbackModeService.this;
243        }
244    }
245
246    /**
247     * Returns Emergency Callback Mode timeout value
248     */
249    public long getEmergencyCallbackModeTimeout() {
250        return mTimeLeft;
251    }
252
253    /**
254     * Returns Emergency Callback Mode call state
255     */
256    public boolean getEmergencyCallbackModeCallState() {
257        return mInEmergencyCall;
258    }
259}
260