blob: 522a35a5fb71f8362e15368139fe30c187ac2d7f [file] [log] [blame]
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001/*
2 * Copyright (C) 2006 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 com.android.internal.telephony.Call;
20import com.android.internal.telephony.CallManager;
21import com.android.internal.telephony.CallerInfo;
22import com.android.internal.telephony.CallerInfoAsyncQuery;
23import com.android.internal.telephony.Connection;
24import com.android.internal.telephony.Phone;
25import com.android.internal.telephony.PhoneConstants;
26import com.android.internal.telephony.PhoneBase;
27import com.android.internal.telephony.TelephonyCapabilities;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070028import com.android.internal.telephony.cdma.CdmaInformationRecords.CdmaDisplayInfoRec;
29import com.android.internal.telephony.cdma.CdmaInformationRecords.CdmaSignalInfoRec;
30import com.android.internal.telephony.cdma.SignalToneUtil;
31
32import android.app.ActivityManagerNative;
33import android.bluetooth.BluetoothAdapter;
34import android.bluetooth.BluetoothHeadset;
35import android.bluetooth.BluetoothProfile;
36import android.content.Context;
John Spurlock6ee06d02014-07-18 20:06:20 -040037import android.media.AudioAttributes;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070038import android.media.AudioManager;
39import android.media.ToneGenerator;
40import android.net.Uri;
41import android.os.AsyncResult;
42import android.os.Handler;
43import android.os.Message;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070044import android.os.SystemProperties;
45import android.os.SystemVibrator;
46import android.os.Vibrator;
47import android.provider.CallLog.Calls;
48import android.provider.Settings;
Anders Kristensen0b35f042014-02-27 14:31:07 -080049import android.telephony.DisconnectCause;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070050import android.telephony.PhoneNumberUtils;
51import android.telephony.PhoneStateListener;
52import android.telephony.TelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070053import android.util.EventLog;
54import android.util.Log;
55
56/**
57 * Phone app module that listens for phone state changes and various other
58 * events from the telephony layer, and triggers any resulting UI behavior
Santos Cordon5422a8d2014-09-12 04:20:56 -070059 * (like starting the Incoming Call UI, playing in-call tones,
Santos Cordon7d4ddf62013-07-10 11:58:08 -070060 * updating notifications, writing call log entries, etc.)
61 */
Santos Cordon5422a8d2014-09-12 04:20:56 -070062public class CallNotifier extends Handler {
Santos Cordon7d4ddf62013-07-10 11:58:08 -070063 private static final String LOG_TAG = "CallNotifier";
64 private static final boolean DBG =
65 (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
66 private static final boolean VDBG = (PhoneGlobals.DBG_LEVEL >= 2);
67
Anthony Leee9468532014-11-15 15:21:00 -080068 // Time to display the message from the underlying phone layers.
69 private static final int SHOW_MESSAGE_NOTIFICATION_TIME = 3000; // msec
Santos Cordon7d4ddf62013-07-10 11:58:08 -070070
John Spurlock6ee06d02014-07-18 20:06:20 -040071 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
72 .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
73 .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
74 .build();
75
Santos Cordon7d4ddf62013-07-10 11:58:08 -070076 /** The singleton instance. */
77 private static CallNotifier sInstance;
78
Santos Cordon7d4ddf62013-07-10 11:58:08 -070079 // values used to track the query state
80 private static final int CALLERINFO_QUERY_READY = 0;
81 private static final int CALLERINFO_QUERYING = -1;
82
83 // the state of the CallerInfo Query.
84 private int mCallerInfoQueryState;
85
86 // object used to synchronize access to mCallerInfoQueryState
87 private Object mCallerInfoQueryStateGuard = new Object();
88
Santos Cordon7d4ddf62013-07-10 11:58:08 -070089 private PhoneGlobals mApplication;
90 private CallManager mCM;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070091 private BluetoothHeadset mBluetoothHeadset;
92 private CallLogger mCallLogger;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070093
94 // ToneGenerator instance for playing SignalInfo tones
95 private ToneGenerator mSignalInfoToneGenerator;
96
97 // The tone volume relative to other sounds in the stream SignalInfo
98 private static final int TONE_RELATIVE_VOLUME_SIGNALINFO = 80;
99
100 private Call.State mPreviousCdmaCallState;
101 private boolean mVoicePrivacyState = false;
102 private boolean mIsCdmaRedialCall = false;
103
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700104 // Cached AudioManager
105 private AudioManager mAudioManager;
106
Santos Cordon27a3c1f2013-08-06 07:49:27 -0700107 private final BluetoothManager mBluetoothManager;
108
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700109 /**
110 * Initialize the singleton CallNotifier instance.
111 * This is only done once, at startup, from PhoneApp.onCreate().
112 */
Santos Cordon5422a8d2014-09-12 04:20:56 -0700113 /* package */ static CallNotifier init(PhoneGlobals app, Phone phone,
Santos Cordon27a3c1f2013-08-06 07:49:27 -0700114 CallLogger callLogger, CallStateMonitor callStateMonitor,
Sailesh Nepal23d9ed72014-07-03 09:40:26 -0700115 BluetoothManager bluetoothManager) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700116 synchronized (CallNotifier.class) {
117 if (sInstance == null) {
Santos Cordon5422a8d2014-09-12 04:20:56 -0700118 sInstance = new CallNotifier(app, phone, callLogger, callStateMonitor,
Sailesh Nepal23d9ed72014-07-03 09:40:26 -0700119 bluetoothManager);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700120 } else {
121 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
122 }
123 return sInstance;
124 }
125 }
126
127 /** Private constructor; @see init() */
Santos Cordon5422a8d2014-09-12 04:20:56 -0700128 private CallNotifier(PhoneGlobals app, Phone phone, CallLogger callLogger,
Sailesh Nepal23d9ed72014-07-03 09:40:26 -0700129 CallStateMonitor callStateMonitor, BluetoothManager bluetoothManager) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700130 mApplication = app;
131 mCM = app.mCM;
132 mCallLogger = callLogger;
Santos Cordon27a3c1f2013-08-06 07:49:27 -0700133 mBluetoothManager = bluetoothManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700134
135 mAudioManager = (AudioManager) mApplication.getSystemService(Context.AUDIO_SERVICE);
136
137 callStateMonitor.addListener(this);
138
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700139 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
140 if (adapter != null) {
141 adapter.getProfileProxy(mApplication.getApplicationContext(),
142 mBluetoothProfileServiceListener,
143 BluetoothProfile.HEADSET);
144 }
145
Andrew Leea82b8202014-11-21 16:18:28 -0800146 TelephonyManager telephonyManager = (TelephonyManager) app.getSystemService(
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700147 Context.TELEPHONY_SERVICE);
148 telephonyManager.listen(mPhoneStateListener,
149 PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
150 | PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR);
151 }
152
153 private void createSignalInfoToneGenerator() {
154 // Instantiate the ToneGenerator for SignalInfo and CallWaiting
155 // TODO: We probably don't need the mSignalInfoToneGenerator instance
156 // around forever. Need to change it so as to create a ToneGenerator instance only
157 // when a tone is being played and releases it after its done playing.
158 if (mSignalInfoToneGenerator == null) {
159 try {
160 mSignalInfoToneGenerator = new ToneGenerator(AudioManager.STREAM_VOICE_CALL,
161 TONE_RELATIVE_VOLUME_SIGNALINFO);
162 Log.d(LOG_TAG, "CallNotifier: mSignalInfoToneGenerator created when toneplay");
163 } catch (RuntimeException e) {
164 Log.w(LOG_TAG, "CallNotifier: Exception caught while creating " +
165 "mSignalInfoToneGenerator: " + e);
166 mSignalInfoToneGenerator = null;
167 }
168 } else {
169 Log.d(LOG_TAG, "mSignalInfoToneGenerator created already, hence skipping");
170 }
171 }
172
173 @Override
174 public void handleMessage(Message msg) {
175 switch (msg.what) {
176 case CallStateMonitor.PHONE_NEW_RINGING_CONNECTION:
177 log("RINGING... (new)");
178 onNewRingingConnection((AsyncResult) msg.obj);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700179 break;
180
181 case CallStateMonitor.PHONE_STATE_CHANGED:
182 onPhoneStateChanged((AsyncResult) msg.obj);
183 break;
184
185 case CallStateMonitor.PHONE_DISCONNECT:
186 if (DBG) log("DISCONNECT");
187 onDisconnect((AsyncResult) msg.obj);
188 break;
189
190 case CallStateMonitor.PHONE_UNKNOWN_CONNECTION_APPEARED:
191 onUnknownConnectionAppeared((AsyncResult) msg.obj);
192 break;
193
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700194 case CallStateMonitor.PHONE_STATE_DISPLAYINFO:
195 if (DBG) log("Received PHONE_STATE_DISPLAYINFO event");
196 onDisplayInfo((AsyncResult) msg.obj);
197 break;
198
199 case CallStateMonitor.PHONE_STATE_SIGNALINFO:
200 if (DBG) log("Received PHONE_STATE_SIGNALINFO event");
201 onSignalInfo((AsyncResult) msg.obj);
202 break;
203
Anthony Leee9468532014-11-15 15:21:00 -0800204 case CallStateMonitor.INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE:
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700205 if (DBG) log("Received Display Info notification done event ...");
Anthony Leee9468532014-11-15 15:21:00 -0800206 PhoneDisplayMessage.dismissMessage();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700207 break;
208
209 case CallStateMonitor.EVENT_OTA_PROVISION_CHANGE:
210 if (DBG) log("EVENT_OTA_PROVISION_CHANGE...");
211 mApplication.handleOtaspEvent(msg);
212 break;
213
214 case CallStateMonitor.PHONE_ENHANCED_VP_ON:
215 if (DBG) log("PHONE_ENHANCED_VP_ON...");
216 if (!mVoicePrivacyState) {
217 int toneToPlay = InCallTonePlayer.TONE_VOICE_PRIVACY;
218 new InCallTonePlayer(toneToPlay).start();
219 mVoicePrivacyState = true;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700220 }
221 break;
222
223 case CallStateMonitor.PHONE_ENHANCED_VP_OFF:
224 if (DBG) log("PHONE_ENHANCED_VP_OFF...");
225 if (mVoicePrivacyState) {
226 int toneToPlay = InCallTonePlayer.TONE_VOICE_PRIVACY;
227 new InCallTonePlayer(toneToPlay).start();
228 mVoicePrivacyState = false;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700229 }
230 break;
231
Anthony Leee9468532014-11-15 15:21:00 -0800232 case CallStateMonitor.PHONE_SUPP_SERVICE_FAILED:
233 if (DBG) log("PHONE_SUPP_SERVICE_FAILED...");
234 onSuppServiceFailed((AsyncResult) msg.obj);
235 break;
236
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700237 default:
238 // super.handleMessage(msg);
239 }
240 }
241
242 PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
243 @Override
Andrew Leea82b8202014-11-21 16:18:28 -0800244 public void onMessageWaitingIndicatorChanged(boolean visible) {
245 if (VDBG) log("onMessageWaitingIndicatorChanged(): " + visible);
246 mApplication.notificationMgr.updateMwi(visible);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700247 }
248
249 @Override
Andrew Leea82b8202014-11-21 16:18:28 -0800250 public void onCallForwardingIndicatorChanged(boolean visible) {
251 if (VDBG) log("onCallForwardingIndicatorChanged(): " + visible);
252 mApplication.notificationMgr.updateCfi(visible);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700253 }
254 };
255
256 /**
257 * Handles a "new ringing connection" event from the telephony layer.
258 */
259 private void onNewRingingConnection(AsyncResult r) {
260 Connection c = (Connection) r.result;
261 log("onNewRingingConnection(): state = " + mCM.getState() + ", conn = { " + c + " }");
262 Call ringing = c.getCall();
263 Phone phone = ringing.getPhone();
264
265 // Check for a few cases where we totally ignore incoming calls.
266 if (ignoreAllIncomingCalls(phone)) {
267 // Immediately reject the call, without even indicating to the user
268 // that an incoming call occurred. (This will generally send the
269 // caller straight to voicemail, just as if we *had* shown the
270 // incoming-call UI and the user had declined the call.)
271 PhoneUtils.hangupRingingCall(ringing);
272 return;
273 }
274
275 if (!c.isRinging()) {
276 Log.i(LOG_TAG, "CallNotifier.onNewRingingConnection(): connection not ringing!");
277 // This is a very strange case: an incoming call that stopped
278 // ringing almost instantly after the onNewRingingConnection()
279 // event. There's nothing we can do here, so just bail out
280 // without doing anything. (But presumably we'll log it in
281 // the call log when the disconnect event comes in...)
282 return;
283 }
284
285 // Stop any signalInfo tone being played on receiving a Call
286 stopSignalInfoTone();
287
288 Call.State state = c.getState();
289 // State will be either INCOMING or WAITING.
290 if (VDBG) log("- connection is ringing! state = " + state);
291 // if (DBG) PhoneUtils.dumpCallState(mPhone);
292
293 // No need to do any service state checks here (like for
294 // "emergency mode"), since in those states the SIM won't let
295 // us get incoming connections in the first place.
296
297 // TODO: Consider sending out a serialized broadcast Intent here
298 // (maybe "ACTION_NEW_INCOMING_CALL"), *before* starting the
299 // ringer and going to the in-call UI. The intent should contain
300 // the caller-id info for the current connection, and say whether
301 // it would be a "call waiting" call or a regular ringing call.
302 // If anybody consumed the broadcast, we'd bail out without
303 // ringing or bringing up the in-call UI.
304 //
305 // This would give 3rd party apps a chance to listen for (and
306 // intercept) new ringing connections. An app could reject the
307 // incoming call by consuming the broadcast and doing nothing, or
308 // it could "pick up" the call (without any action by the user!)
309 // via some future TelephonyManager API.
310 //
311 // See bug 1312336 for more details.
312 // We'd need to protect this with a new "intercept incoming calls"
313 // system permission.
314
315 // Obtain a partial wake lock to make sure the CPU doesn't go to
316 // sleep before we finish bringing up the InCallScreen.
317 // (This will be upgraded soon to a full wake lock; see
318 // showIncomingCall().)
319 if (VDBG) log("Holding wake lock on new incoming connection.");
320 mApplication.requestWakeState(PhoneGlobals.WakeState.PARTIAL);
321
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700322 // Note we *don't* post a status bar notification here, since
323 // we're not necessarily ready to actually show the incoming call
324 // to the user. (For calls in the INCOMING state, at least, we
325 // still need to run a caller-id query, and we may not even ring
326 // at all if the "send directly to voicemail" flag is set.)
327 //
328 // Instead, we update the notification (and potentially launch the
329 // InCallScreen) from the showIncomingCall() method, which runs
330 // when the caller-id query completes or times out.
331
332 if (VDBG) log("- onNewRingingConnection() done.");
333 }
334
335 /**
336 * Determines whether or not we're allowed to present incoming calls to the
337 * user, based on the capabilities and/or current state of the device.
338 *
339 * If this method returns true, that means we should immediately reject the
340 * current incoming call, without even indicating to the user that an
341 * incoming call occurred.
342 *
343 * (We only reject incoming calls in a few cases, like during an OTASP call
344 * when we can't interrupt the user, or if the device hasn't completed the
345 * SetupWizard yet. We also don't allow incoming calls on non-voice-capable
346 * devices. But note that we *always* allow incoming calls while in ECM.)
347 *
348 * @return true if we're *not* allowed to present an incoming call to
349 * the user.
350 */
351 private boolean ignoreAllIncomingCalls(Phone phone) {
352 // Incoming calls are totally ignored on non-voice-capable devices.
353 if (!PhoneGlobals.sVoiceCapable) {
354 // ...but still log a warning, since we shouldn't have gotten this
355 // event in the first place! (Incoming calls *should* be blocked at
356 // the telephony layer on non-voice-capable capable devices.)
357 Log.w(LOG_TAG, "Got onNewRingingConnection() on non-voice-capable device! Ignoring...");
358 return true;
359 }
360
361 // In ECM (emergency callback mode), we ALWAYS allow incoming calls
362 // to get through to the user. (Note that ECM is applicable only to
363 // voice-capable CDMA devices).
364 if (PhoneUtils.isPhoneInEcm(phone)) {
365 if (DBG) log("Incoming call while in ECM: always allow...");
366 return false;
367 }
368
369 // Incoming calls are totally ignored if the device isn't provisioned yet.
370 boolean provisioned = Settings.Global.getInt(mApplication.getContentResolver(),
371 Settings.Global.DEVICE_PROVISIONED, 0) != 0;
372 if (!provisioned) {
373 Log.i(LOG_TAG, "Ignoring incoming call: not provisioned");
374 return true;
375 }
376
377 // Incoming calls are totally ignored if an OTASP call is active.
378 if (TelephonyCapabilities.supportsOtasp(phone)) {
379 boolean activateState = (mApplication.cdmaOtaScreenState.otaScreenState
380 == OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION);
381 boolean dialogState = (mApplication.cdmaOtaScreenState.otaScreenState
382 == OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_SUCCESS_FAILURE_DLG);
383 boolean spcState = mApplication.cdmaOtaProvisionData.inOtaSpcState;
384
385 if (spcState) {
386 Log.i(LOG_TAG, "Ignoring incoming call: OTA call is active");
387 return true;
388 } else if (activateState || dialogState) {
389 // We *are* allowed to receive incoming calls at this point.
390 // But clear out any residual OTASP UI first.
391 // TODO: It's an MVC violation to twiddle the OTA UI state here;
392 // we should instead provide a higher-level API via OtaUtils.
393 if (dialogState) mApplication.dismissOtaDialogs();
394 mApplication.clearOtaState();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700395 return false;
396 }
397 }
398
399 // Normal case: allow this call to be presented to the user.
400 return false;
401 }
402
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700403 private void onUnknownConnectionAppeared(AsyncResult r) {
404 PhoneConstants.State state = mCM.getState();
405
406 if (state == PhoneConstants.State.OFFHOOK) {
Santos Cordon54fdb592013-09-19 05:16:18 -0700407 if (DBG) log("unknown connection appeared...");
Chiao Cheng312b9c92013-09-16 15:40:53 -0700408
Santos Cordon54fdb592013-09-19 05:16:18 -0700409 onPhoneStateChanged(r);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700410 }
411 }
412
Christine Chenb5e4b652013-09-19 11:20:18 -0700413 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700414 * Updates the phone UI in response to phone state changes.
415 *
416 * Watch out: certain state changes are actually handled by their own
417 * specific methods:
418 * - see onNewRingingConnection() for new incoming calls
419 * - see onDisconnect() for calls being hung up or disconnected
420 */
421 private void onPhoneStateChanged(AsyncResult r) {
422 PhoneConstants.State state = mCM.getState();
423 if (VDBG) log("onPhoneStateChanged: state = " + state);
424
425 // Turn status bar notifications on or off depending upon the state
426 // of the phone. Notification Alerts (audible or vibrating) should
427 // be on if and only if the phone is IDLE.
428 mApplication.notificationMgr.statusBarHelper
429 .enableNotificationAlerts(state == PhoneConstants.State.IDLE);
430
431 Phone fgPhone = mCM.getFgPhone();
432 if (fgPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
433 if ((fgPhone.getForegroundCall().getState() == Call.State.ACTIVE)
434 && ((mPreviousCdmaCallState == Call.State.DIALING)
435 || (mPreviousCdmaCallState == Call.State.ALERTING))) {
436 if (mIsCdmaRedialCall) {
437 int toneToPlay = InCallTonePlayer.TONE_REDIAL;
438 new InCallTonePlayer(toneToPlay).start();
439 }
440 // Stop any signal info tone when call moves to ACTIVE state
441 stopSignalInfoTone();
442 }
443 mPreviousCdmaCallState = fgPhone.getForegroundCall().getState();
444 }
445
446 // Have the PhoneApp recompute its mShowBluetoothIndication
447 // flag based on the (new) telephony state.
448 // There's no need to force a UI update since we update the
449 // in-call notification ourselves (below), and the InCallScreen
450 // listens for phone state changes itself.
Santos Cordon27a3c1f2013-08-06 07:49:27 -0700451 mBluetoothManager.updateBluetoothIndication();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700452
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700453 // Update the phone state and other sensor/lock.
454 mApplication.updatePhoneState(state);
455
456 if (state == PhoneConstants.State.OFFHOOK) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700457
458 if (VDBG) log("onPhoneStateChanged: OFF HOOK");
459 // make sure audio is in in-call mode now
460 PhoneUtils.setAudioMode(mCM);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700461 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700462 }
463
464 void updateCallNotifierRegistrationsAfterRadioTechnologyChange() {
465 if (DBG) Log.d(LOG_TAG, "updateCallNotifierRegistrationsAfterRadioTechnologyChange...");
466
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700467 // Instantiate mSignalInfoToneGenerator
468 createSignalInfoToneGenerator();
469 }
470
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700471 private void onDisconnect(AsyncResult r) {
472 if (VDBG) log("onDisconnect()... CallManager state: " + mCM.getState());
473
474 mVoicePrivacyState = false;
475 Connection c = (Connection) r.result;
476 if (c != null) {
Anders Kristensen0b35f042014-02-27 14:31:07 -0800477 log("onDisconnect: cause = " + DisconnectCause.toString(c.getDisconnectCause())
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700478 + ", incoming = " + c.isIncoming()
479 + ", date = " + c.getCreateTime());
480 } else {
481 Log.w(LOG_TAG, "onDisconnect: null connection");
482 }
483
484 int autoretrySetting = 0;
Anthony Leee9468532014-11-15 15:21:00 -0800485 if ((c != null) &&
486 (c.getCall().getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA)) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700487 autoretrySetting = android.provider.Settings.Global.getInt(mApplication.
488 getContentResolver(),android.provider.Settings.Global.CALL_AUTO_RETRY, 0);
489 }
490
491 // Stop any signalInfo tone being played when a call gets ended
492 stopSignalInfoTone();
493
Anthony Leee9468532014-11-15 15:21:00 -0800494 if ((c != null) &&
495 (c.getCall().getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA)) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700496 // Resetting the CdmaPhoneCallState members
497 mApplication.cdmaPhoneCallState.resetCdmaPhoneCallState();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700498 }
499
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700500 // If this is the end of an OTASP call, pass it on to the PhoneApp.
501 if (c != null && TelephonyCapabilities.supportsOtasp(c.getCall().getPhone())) {
502 final String number = c.getAddress();
503 if (c.getCall().getPhone().isOtaSpNumber(number)) {
504 if (DBG) log("onDisconnect: this was an OTASP call!");
505 mApplication.handleOtaspDisconnect();
506 }
507 }
508
509 // Check for the various tones we might need to play (thru the
510 // earpiece) after a call disconnects.
511 int toneToPlay = InCallTonePlayer.TONE_NONE;
512
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700513 // If we don't need to play BUSY or CONGESTION, then play the
514 // "call ended" tone if this was a "regular disconnect" (i.e. a
515 // normal call where one end or the other hung up) *and* this
516 // disconnect event caused the phone to become idle. (In other
517 // words, we *don't* play the sound if one call hangs up but
518 // there's still an active call on the other line.)
519 // TODO: We may eventually want to disable this via a preference.
520 if ((toneToPlay == InCallTonePlayer.TONE_NONE)
521 && (mCM.getState() == PhoneConstants.State.IDLE)
522 && (c != null)) {
Anders Kristensen0b35f042014-02-27 14:31:07 -0800523 int cause = c.getDisconnectCause();
524 if ((cause == DisconnectCause.NORMAL) // remote hangup
525 || (cause == DisconnectCause.LOCAL)) { // local hangup
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700526 if (VDBG) log("- need to play CALL_ENDED tone!");
527 toneToPlay = InCallTonePlayer.TONE_CALL_ENDED;
528 mIsCdmaRedialCall = false;
529 }
530 }
531
532 // All phone calls are disconnected.
533 if (mCM.getState() == PhoneConstants.State.IDLE) {
534 // Don't reset the audio mode or bluetooth/speakerphone state
535 // if we still need to let the user hear a tone through the earpiece.
536 if (toneToPlay == InCallTonePlayer.TONE_NONE) {
537 resetAudioStateAfterDisconnect();
538 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700539 }
540
541 if (c != null) {
542 mCallLogger.logCall(c);
543
544 final String number = c.getAddress();
545 final Phone phone = c.getCall().getPhone();
546 final boolean isEmergencyNumber =
Yorke Lee36bb2542014-06-05 08:09:52 -0700547 PhoneNumberUtils.isLocalEmergencyNumber(mApplication, number);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700548
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700549 // Possibly play a "post-disconnect tone" thru the earpiece.
550 // We do this here, rather than from the InCallScreen
551 // activity, since we need to do this even if you're not in
552 // the Phone UI at the moment the connection ends.
553 if (toneToPlay != InCallTonePlayer.TONE_NONE) {
554 if (VDBG) log("- starting post-disconnect tone (" + toneToPlay + ")...");
555 new InCallTonePlayer(toneToPlay).start();
556
557 // TODO: alternatively, we could start an InCallTonePlayer
558 // here with an "unlimited" tone length,
559 // and manually stop it later when this connection truly goes
560 // away. (The real connection over the network was closed as soon
561 // as we got the BUSY message. But our telephony layer keeps the
562 // connection open for a few extra seconds so we can show the
563 // "busy" indication to the user. We could stop the busy tone
564 // when *that* connection's "disconnect" event comes in.)
565 }
566
Santos Cordonf68db2e2014-07-02 14:40:44 -0700567 final int cause = c.getDisconnectCause();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700568 if (((mPreviousCdmaCallState == Call.State.DIALING)
569 || (mPreviousCdmaCallState == Call.State.ALERTING))
570 && (!isEmergencyNumber)
Anders Kristensen0b35f042014-02-27 14:31:07 -0800571 && (cause != DisconnectCause.INCOMING_MISSED )
572 && (cause != DisconnectCause.NORMAL)
573 && (cause != DisconnectCause.LOCAL)
574 && (cause != DisconnectCause.INCOMING_REJECTED)) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700575 if (!mIsCdmaRedialCall) {
576 if (autoretrySetting == InCallScreen.AUTO_RETRY_ON) {
577 // TODO: (Moto): The contact reference data may need to be stored and use
578 // here when redialing a call. For now, pass in NULL as the URI parameter.
Santos Cordonce02f3a2013-09-19 01:58:42 -0700579 final int status =
580 PhoneUtils.placeCall(mApplication, phone, number, null, false);
581 if (status != PhoneUtils.CALL_STATUS_FAILED) {
582 mIsCdmaRedialCall = true;
583 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700584 } else {
585 mIsCdmaRedialCall = false;
586 }
587 } else {
588 mIsCdmaRedialCall = false;
589 }
590 }
591 }
592 }
593
594 /**
595 * Resets the audio mode and speaker state when a call ends.
596 */
597 private void resetAudioStateAfterDisconnect() {
598 if (VDBG) log("resetAudioStateAfterDisconnect()...");
599
600 if (mBluetoothHeadset != null) {
601 mBluetoothHeadset.disconnectAudio();
602 }
603
604 // call turnOnSpeaker() with state=false and store=true even if speaker
605 // is already off to reset user requested speaker state.
606 PhoneUtils.turnOnSpeaker(mApplication, false, true);
607
608 PhoneUtils.setAudioMode(mCM);
609 }
610
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700611 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700612 * Helper class to play tones through the earpiece (or speaker / BT)
613 * during a call, using the ToneGenerator.
614 *
615 * To use, just instantiate a new InCallTonePlayer
616 * (passing in the TONE_* constant for the tone you want)
617 * and start() it.
618 *
619 * When we're done playing the tone, if the phone is idle at that
620 * point, we'll reset the audio routing and speaker state.
621 * (That means that for tones that get played *after* a call
622 * disconnects, like "busy" or "congestion" or "call ended", you
623 * should NOT call resetAudioStateAfterDisconnect() yourself.
624 * Instead, just start the InCallTonePlayer, which will automatically
625 * defer the resetAudioStateAfterDisconnect() call until the tone
626 * finishes playing.)
627 */
628 private class InCallTonePlayer extends Thread {
629 private int mToneId;
630 private int mState;
631 // The possible tones we can play.
632 public static final int TONE_NONE = 0;
633 public static final int TONE_CALL_WAITING = 1;
634 public static final int TONE_BUSY = 2;
635 public static final int TONE_CONGESTION = 3;
636 public static final int TONE_CALL_ENDED = 4;
637 public static final int TONE_VOICE_PRIVACY = 5;
638 public static final int TONE_REORDER = 6;
639 public static final int TONE_INTERCEPT = 7;
640 public static final int TONE_CDMA_DROP = 8;
641 public static final int TONE_OUT_OF_SERVICE = 9;
642 public static final int TONE_REDIAL = 10;
643 public static final int TONE_OTA_CALL_END = 11;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700644 public static final int TONE_UNOBTAINABLE_NUMBER = 13;
645
646 // The tone volume relative to other sounds in the stream
647 static final int TONE_RELATIVE_VOLUME_EMERGENCY = 100;
648 static final int TONE_RELATIVE_VOLUME_HIPRI = 80;
649 static final int TONE_RELATIVE_VOLUME_LOPRI = 50;
650
651 // Buffer time (in msec) to add on to tone timeout value.
652 // Needed mainly when the timeout value for a tone is the
653 // exact duration of the tone itself.
654 static final int TONE_TIMEOUT_BUFFER = 20;
655
656 // The tone state
657 static final int TONE_OFF = 0;
658 static final int TONE_ON = 1;
659 static final int TONE_STOPPED = 2;
660
661 InCallTonePlayer(int toneId) {
662 super();
663 mToneId = toneId;
664 mState = TONE_OFF;
665 }
666
667 @Override
668 public void run() {
669 log("InCallTonePlayer.run(toneId = " + mToneId + ")...");
670
671 int toneType = 0; // passed to ToneGenerator.startTone()
672 int toneVolume; // passed to the ToneGenerator constructor
673 int toneLengthMillis;
674 int phoneType = mCM.getFgPhone().getPhoneType();
675
676 switch (mToneId) {
677 case TONE_CALL_WAITING:
678 toneType = ToneGenerator.TONE_SUP_CALL_WAITING;
679 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
680 // Call waiting tone is stopped by stopTone() method
681 toneLengthMillis = Integer.MAX_VALUE - TONE_TIMEOUT_BUFFER;
682 break;
683 case TONE_BUSY:
684 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
685 toneType = ToneGenerator.TONE_CDMA_NETWORK_BUSY_ONE_SHOT;
686 toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
687 toneLengthMillis = 1000;
Sailesh Nepalcc0375f2013-11-13 09:15:18 -0800688 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM
689 || phoneType == PhoneConstants.PHONE_TYPE_SIP
Etan Cohen0ca1c802014-07-07 15:35:48 -0700690 || phoneType == PhoneConstants.PHONE_TYPE_IMS
Sailesh Nepalcc0375f2013-11-13 09:15:18 -0800691 || phoneType == PhoneConstants.PHONE_TYPE_THIRD_PARTY) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700692 toneType = ToneGenerator.TONE_SUP_BUSY;
693 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
694 toneLengthMillis = 4000;
695 } else {
696 throw new IllegalStateException("Unexpected phone type: " + phoneType);
697 }
698 break;
699 case TONE_CONGESTION:
700 toneType = ToneGenerator.TONE_SUP_CONGESTION;
701 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
702 toneLengthMillis = 4000;
703 break;
704
705 case TONE_CALL_ENDED:
706 toneType = ToneGenerator.TONE_PROP_PROMPT;
707 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
708 toneLengthMillis = 200;
709 break;
710 case TONE_OTA_CALL_END:
711 if (mApplication.cdmaOtaConfigData.otaPlaySuccessFailureTone ==
712 OtaUtils.OTA_PLAY_SUCCESS_FAILURE_TONE_ON) {
713 toneType = ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD;
714 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
715 toneLengthMillis = 750;
716 } else {
717 toneType = ToneGenerator.TONE_PROP_PROMPT;
718 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
719 toneLengthMillis = 200;
720 }
721 break;
722 case TONE_VOICE_PRIVACY:
723 toneType = ToneGenerator.TONE_CDMA_ALERT_NETWORK_LITE;
724 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
725 toneLengthMillis = 5000;
726 break;
727 case TONE_REORDER:
728 toneType = ToneGenerator.TONE_CDMA_REORDER;
729 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
730 toneLengthMillis = 4000;
731 break;
732 case TONE_INTERCEPT:
733 toneType = ToneGenerator.TONE_CDMA_ABBR_INTERCEPT;
734 toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
735 toneLengthMillis = 500;
736 break;
737 case TONE_CDMA_DROP:
738 case TONE_OUT_OF_SERVICE:
739 toneType = ToneGenerator.TONE_CDMA_CALLDROP_LITE;
740 toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
741 toneLengthMillis = 375;
742 break;
743 case TONE_REDIAL:
744 toneType = ToneGenerator.TONE_CDMA_ALERT_AUTOREDIAL_LITE;
745 toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
746 toneLengthMillis = 5000;
747 break;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700748 case TONE_UNOBTAINABLE_NUMBER:
749 toneType = ToneGenerator.TONE_SUP_ERROR;
750 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
751 toneLengthMillis = 4000;
752 break;
753 default:
754 throw new IllegalArgumentException("Bad toneId: " + mToneId);
755 }
756
757 // If the mToneGenerator creation fails, just continue without it. It is
758 // a local audio signal, and is not as important.
759 ToneGenerator toneGenerator;
760 try {
761 int stream;
762 if (mBluetoothHeadset != null) {
763 stream = mBluetoothHeadset.isAudioOn() ? AudioManager.STREAM_BLUETOOTH_SCO:
764 AudioManager.STREAM_VOICE_CALL;
765 } else {
766 stream = AudioManager.STREAM_VOICE_CALL;
767 }
768 toneGenerator = new ToneGenerator(stream, toneVolume);
769 // if (DBG) log("- created toneGenerator: " + toneGenerator);
770 } catch (RuntimeException e) {
771 Log.w(LOG_TAG,
772 "InCallTonePlayer: Exception caught while creating ToneGenerator: " + e);
773 toneGenerator = null;
774 }
775
776 // Using the ToneGenerator (with the CALL_WAITING / BUSY /
777 // CONGESTION tones at least), the ToneGenerator itself knows
778 // the right pattern of tones to play; we do NOT need to
779 // manually start/stop each individual tone, or manually
780 // insert the correct delay between tones. (We just start it
781 // and let it run for however long we want the tone pattern to
782 // continue.)
783 //
784 // TODO: When we stop the ToneGenerator in the middle of a
785 // "tone pattern", it sounds bad if we cut if off while the
786 // tone is actually playing. Consider adding API to the
787 // ToneGenerator to say "stop at the next silent part of the
788 // pattern", or simply "play the pattern N times and then
789 // stop."
790 boolean needToStopTone = true;
791 boolean okToPlayTone = false;
792
793 if (toneGenerator != null) {
794 int ringerMode = mAudioManager.getRingerMode();
795 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
796 if (toneType == ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD) {
797 if ((ringerMode != AudioManager.RINGER_MODE_SILENT) &&
798 (ringerMode != AudioManager.RINGER_MODE_VIBRATE)) {
799 if (DBG) log("- InCallTonePlayer: start playing call tone=" + toneType);
800 okToPlayTone = true;
801 needToStopTone = false;
802 }
803 } else if ((toneType == ToneGenerator.TONE_CDMA_NETWORK_BUSY_ONE_SHOT) ||
804 (toneType == ToneGenerator.TONE_CDMA_REORDER) ||
805 (toneType == ToneGenerator.TONE_CDMA_ABBR_REORDER) ||
806 (toneType == ToneGenerator.TONE_CDMA_ABBR_INTERCEPT) ||
807 (toneType == ToneGenerator.TONE_CDMA_CALLDROP_LITE)) {
808 if (ringerMode != AudioManager.RINGER_MODE_SILENT) {
809 if (DBG) log("InCallTonePlayer:playing call fail tone:" + toneType);
810 okToPlayTone = true;
811 needToStopTone = false;
812 }
813 } else if ((toneType == ToneGenerator.TONE_CDMA_ALERT_AUTOREDIAL_LITE) ||
814 (toneType == ToneGenerator.TONE_CDMA_ALERT_NETWORK_LITE)) {
815 if ((ringerMode != AudioManager.RINGER_MODE_SILENT) &&
816 (ringerMode != AudioManager.RINGER_MODE_VIBRATE)) {
817 if (DBG) log("InCallTonePlayer:playing tone for toneType=" + toneType);
818 okToPlayTone = true;
819 needToStopTone = false;
820 }
821 } else { // For the rest of the tones, always OK to play.
822 okToPlayTone = true;
823 }
824 } else { // Not "CDMA"
825 okToPlayTone = true;
826 }
827
828 synchronized (this) {
829 if (okToPlayTone && mState != TONE_STOPPED) {
830 mState = TONE_ON;
831 toneGenerator.startTone(toneType);
832 try {
833 wait(toneLengthMillis + TONE_TIMEOUT_BUFFER);
834 } catch (InterruptedException e) {
835 Log.w(LOG_TAG,
836 "InCallTonePlayer stopped: " + e);
837 }
838 if (needToStopTone) {
839 toneGenerator.stopTone();
840 }
841 }
842 // if (DBG) log("- InCallTonePlayer: done playing.");
843 toneGenerator.release();
844 mState = TONE_OFF;
845 }
846 }
847
848 // Finally, do the same cleanup we otherwise would have done
849 // in onDisconnect().
850 //
851 // (But watch out: do NOT do this if the phone is in use,
852 // since some of our tones get played *during* a call (like
853 // CALL_WAITING) and we definitely *don't*
854 // want to reset the audio mode / speaker / bluetooth after
855 // playing those!
856 // This call is really here for use with tones that get played
857 // *after* a call disconnects, like "busy" or "congestion" or
858 // "call ended", where the phone has already become idle but
859 // we need to defer the resetAudioStateAfterDisconnect() call
860 // till the tone finishes playing.)
861 if (mCM.getState() == PhoneConstants.State.IDLE) {
862 resetAudioStateAfterDisconnect();
863 }
864 }
865
866 public void stopTone() {
867 synchronized (this) {
868 if (mState == TONE_ON) {
869 notify();
870 }
871 mState = TONE_STOPPED;
872 }
873 }
874 }
875
876 /**
877 * Displays a notification when the phone receives a DisplayInfo record.
878 */
879 private void onDisplayInfo(AsyncResult r) {
880 // Extract the DisplayInfo String from the message
881 CdmaDisplayInfoRec displayInfoRec = (CdmaDisplayInfoRec)(r.result);
882
883 if (displayInfoRec != null) {
884 String displayInfo = displayInfoRec.alpha;
885 if (DBG) log("onDisplayInfo: displayInfo=" + displayInfo);
Anthony Leee9468532014-11-15 15:21:00 -0800886 PhoneDisplayMessage.displayNetworkMessage(mApplication, displayInfo);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700887
Anthony Leee9468532014-11-15 15:21:00 -0800888 // start a timer that kills the dialog
889 sendEmptyMessageDelayed(CallStateMonitor.INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE,
890 SHOW_MESSAGE_NOTIFICATION_TIME);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700891 }
892 }
893
894 /**
Anthony Leee9468532014-11-15 15:21:00 -0800895 * Displays a notification when the phone receives a notice that a supplemental
896 * service has failed.
Tyler Gunn9dd07d02014-12-08 10:52:57 -0800897 * TODO: This is a NOOP if it isn't for conferences or resuming call failures right now.
Anthony Leee9468532014-11-15 15:21:00 -0800898 */
899 private void onSuppServiceFailed(AsyncResult r) {
Tyler Gunn9dd07d02014-12-08 10:52:57 -0800900 if (r.result != Phone.SuppService.CONFERENCE && r.result != Phone.SuppService.RESUME) {
901 if (DBG) log("onSuppServiceFailed: not a merge or resume failure event");
Anthony Leee9468532014-11-15 15:21:00 -0800902 return;
903 }
904
Tyler Gunn9dd07d02014-12-08 10:52:57 -0800905 String mergeFailedString = "";
906 if (r.result == Phone.SuppService.CONFERENCE) {
907 if (DBG) log("onSuppServiceFailed: displaying merge failure message");
908 mergeFailedString = mApplication.getResources().getString(
909 R.string.incall_error_supp_service_conference);
910 } else if (r.result == Phone.SuppService.RESUME) {
911 if (DBG) log("onSuppServiceFailed: displaying merge failure message");
912 mergeFailedString = mApplication.getResources().getString(
913 R.string.incall_error_supp_service_switch);
914 }
Anthony Leee9468532014-11-15 15:21:00 -0800915 PhoneDisplayMessage.displayErrorMessage(mApplication, mergeFailedString);
916
917 // start a timer that kills the dialog
918 sendEmptyMessageDelayed(CallStateMonitor.INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE,
919 SHOW_MESSAGE_NOTIFICATION_TIME);
920 }
921
922 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700923 * Helper class to play SignalInfo tones using the ToneGenerator.
924 *
925 * To use, just instantiate a new SignalInfoTonePlayer
926 * (passing in the ToneID constant for the tone you want)
927 * and start() it.
928 */
929 private class SignalInfoTonePlayer extends Thread {
930 private int mToneId;
931
932 SignalInfoTonePlayer(int toneId) {
933 super();
934 mToneId = toneId;
935 }
936
937 @Override
938 public void run() {
939 log("SignalInfoTonePlayer.run(toneId = " + mToneId + ")...");
Yorke Lee65cbd162014-10-08 11:26:02 -0700940 createSignalInfoToneGenerator();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700941 if (mSignalInfoToneGenerator != null) {
942 //First stop any ongoing SignalInfo tone
943 mSignalInfoToneGenerator.stopTone();
944
945 //Start playing the new tone if its a valid tone
946 mSignalInfoToneGenerator.startTone(mToneId);
947 }
948 }
949 }
950
951 /**
952 * Plays a tone when the phone receives a SignalInfo record.
953 */
954 private void onSignalInfo(AsyncResult r) {
955 // Signal Info are totally ignored on non-voice-capable devices.
956 if (!PhoneGlobals.sVoiceCapable) {
957 Log.w(LOG_TAG, "Got onSignalInfo() on non-voice-capable device! Ignoring...");
958 return;
959 }
960
961 if (PhoneUtils.isRealIncomingCall(mCM.getFirstActiveRingingCall().getState())) {
962 // Do not start any new SignalInfo tone when Call state is INCOMING
963 // and stop any previous SignalInfo tone which is being played
964 stopSignalInfoTone();
965 } else {
966 // Extract the SignalInfo String from the message
967 CdmaSignalInfoRec signalInfoRec = (CdmaSignalInfoRec)(r.result);
968 // Only proceed if a Signal info is present.
969 if (signalInfoRec != null) {
970 boolean isPresent = signalInfoRec.isPresent;
971 if (DBG) log("onSignalInfo: isPresent=" + isPresent);
972 if (isPresent) {// if tone is valid
973 int uSignalType = signalInfoRec.signalType;
974 int uAlertPitch = signalInfoRec.alertPitch;
975 int uSignal = signalInfoRec.signal;
976
977 if (DBG) log("onSignalInfo: uSignalType=" + uSignalType + ", uAlertPitch=" +
978 uAlertPitch + ", uSignal=" + uSignal);
979 //Map the Signal to a ToneGenerator ToneID only if Signal info is present
980 int toneID = SignalToneUtil.getAudioToneFromSignalInfo
981 (uSignalType, uAlertPitch, uSignal);
982
983 //Create the SignalInfo tone player and pass the ToneID
984 new SignalInfoTonePlayer(toneID).start();
985 }
986 }
987 }
988 }
989
990 /**
991 * Stops a SignalInfo tone in the following condition
992 * 1 - On receiving a New Ringing Call
993 * 2 - On disconnecting a call
994 * 3 - On answering a Call Waiting Call
995 */
996 /* package */ void stopSignalInfoTone() {
997 if (DBG) log("stopSignalInfoTone: Stopping SignalInfo tone player");
998 new SignalInfoTonePlayer(ToneGenerator.TONE_CDMA_SIGNAL_OFF).start();
999 }
1000
1001 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001002 * Return the private variable mPreviousCdmaCallState.
1003 */
1004 /* package */ Call.State getPreviousCdmaCallState() {
1005 return mPreviousCdmaCallState;
1006 }
1007
1008 /**
1009 * Return the private variable mVoicePrivacyState.
1010 */
1011 /* package */ boolean getVoicePrivacyState() {
1012 return mVoicePrivacyState;
1013 }
1014
1015 /**
1016 * Return the private variable mIsCdmaRedialCall.
1017 */
1018 /* package */ boolean getIsCdmaRedialCall() {
1019 return mIsCdmaRedialCall;
1020 }
1021
Santos Cordon5c046722014-09-18 15:41:13 -07001022 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001023 new BluetoothProfile.ServiceListener() {
1024 public void onServiceConnected(int profile, BluetoothProfile proxy) {
1025 mBluetoothHeadset = (BluetoothHeadset) proxy;
1026 if (VDBG) log("- Got BluetoothHeadset: " + mBluetoothHeadset);
1027 }
1028
1029 public void onServiceDisconnected(int profile) {
1030 mBluetoothHeadset = null;
1031 }
1032 };
1033
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001034 private void log(String msg) {
1035 Log.d(LOG_TAG, msg);
1036 }
1037}