blob: 5b145919844869ee35f884f3f691b30b937f4402 [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
Santos Cordon7d4ddf62013-07-10 11:58:08 -070019import android.bluetooth.BluetoothAdapter;
20import android.bluetooth.BluetoothHeadset;
21import android.bluetooth.BluetoothProfile;
22import android.content.Context;
23import android.media.AudioManager;
Amit Mahajanb8f13202020-01-27 18:16:07 -080024import android.media.AudioSystem;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070025import android.media.ToneGenerator;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070026import android.os.AsyncResult;
27import android.os.Handler;
28import android.os.Message;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070029import android.os.SystemProperties;
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -080030import android.telecom.TelecomManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070031import android.telephony.PhoneStateListener;
Andrew Lee2fcb6c32014-12-04 14:52:35 -080032import android.telephony.SubscriptionInfo;
33import android.telephony.SubscriptionManager;
34import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070035import android.telephony.TelephonyManager;
Andrew Lee2fcb6c32014-12-04 14:52:35 -080036import android.util.ArrayMap;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070037import android.util.Log;
38
Aravind Sreekumarafc08c52018-04-10 15:34:32 -070039import com.android.internal.telephony.CallManager;
40import com.android.internal.telephony.Phone;
41import com.android.internal.telephony.PhoneConstants;
42import com.android.internal.telephony.SubscriptionController;
43import com.android.internal.telephony.cdma.CdmaInformationRecords.CdmaDisplayInfoRec;
44import com.android.internal.telephony.cdma.CdmaInformationRecords.CdmaSignalInfoRec;
45import com.android.internal.telephony.cdma.SignalToneUtil;
46
Kazuya Ohshiro263737d2017-10-06 19:42:03 +090047import java.util.ArrayList;
48import java.util.Collections;
49import java.util.Comparator;
Andrew Lee2fcb6c32014-12-04 14:52:35 -080050import java.util.List;
51import java.util.Map;
52
Santos Cordon7d4ddf62013-07-10 11:58:08 -070053/**
54 * Phone app module that listens for phone state changes and various other
55 * events from the telephony layer, and triggers any resulting UI behavior
Santos Cordon5422a8d2014-09-12 04:20:56 -070056 * (like starting the Incoming Call UI, playing in-call tones,
Santos Cordon7d4ddf62013-07-10 11:58:08 -070057 * updating notifications, writing call log entries, etc.)
58 */
Santos Cordon5422a8d2014-09-12 04:20:56 -070059public class CallNotifier extends Handler {
Santos Cordon7d4ddf62013-07-10 11:58:08 -070060 private static final String LOG_TAG = "CallNotifier";
61 private static final boolean DBG =
62 (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
63 private static final boolean VDBG = (PhoneGlobals.DBG_LEVEL >= 2);
64
Anthony Leee9468532014-11-15 15:21:00 -080065 // Time to display the message from the underlying phone layers.
66 private static final int SHOW_MESSAGE_NOTIFICATION_TIME = 3000; // msec
Santos Cordon7d4ddf62013-07-10 11:58:08 -070067
68 /** The singleton instance. */
69 private static CallNotifier sInstance;
70
Andrew Lee2fcb6c32014-12-04 14:52:35 -080071 private Map<Integer, CallNotifierPhoneStateListener> mPhoneStateListeners =
72 new ArrayMap<Integer, CallNotifierPhoneStateListener>();
Kazuya Ohshiro263737d2017-10-06 19:42:03 +090073 private Map<Integer, Boolean> mCFIStatus = new ArrayMap<Integer, Boolean>();
74 private Map<Integer, Boolean> mMWIStatus = new ArrayMap<Integer, Boolean>();
Santos Cordon7d4ddf62013-07-10 11:58:08 -070075 private PhoneGlobals mApplication;
76 private CallManager mCM;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070077 private BluetoothHeadset mBluetoothHeadset;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070078
79 // ToneGenerator instance for playing SignalInfo tones
80 private ToneGenerator mSignalInfoToneGenerator;
81
82 // The tone volume relative to other sounds in the stream SignalInfo
83 private static final int TONE_RELATIVE_VOLUME_SIGNALINFO = 80;
84
Santos Cordon7d4ddf62013-07-10 11:58:08 -070085 private boolean mVoicePrivacyState = false;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070086
Santos Cordon7d4ddf62013-07-10 11:58:08 -070087 // Cached AudioManager
88 private AudioManager mAudioManager;
Andrew Lee2fcb6c32014-12-04 14:52:35 -080089 private SubscriptionManager mSubscriptionManager;
90 private TelephonyManager mTelephonyManager;
Santos Cordon27a3c1f2013-08-06 07:49:27 -070091
Brad Ebingera9c6b6d2016-01-07 17:24:16 -080092 // Events from the Phone object:
93 public static final int PHONE_DISCONNECT = 3;
94 public static final int PHONE_STATE_DISPLAYINFO = 6;
95 public static final int PHONE_STATE_SIGNALINFO = 7;
96 public static final int PHONE_ENHANCED_VP_ON = 9;
97 public static final int PHONE_ENHANCED_VP_OFF = 10;
98 public static final int PHONE_SUPP_SERVICE_FAILED = 14;
99 public static final int PHONE_TTY_MODE_RECEIVED = 15;
100 // Events generated internally.
101 // We should store all the possible event type values in one place to make sure that
102 // they don't step on each others' toes.
103 public static final int INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE = 22;
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800104
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530105 public static final int UPDATE_TYPE_MWI = 0;
106 public static final int UPDATE_TYPE_CFI = 1;
107 public static final int UPDATE_TYPE_MWI_CFI = 2;
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 */
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800113 /* package */ static CallNotifier init(
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800114 PhoneGlobals app) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700115 synchronized (CallNotifier.class) {
116 if (sInstance == null) {
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800117 sInstance = new CallNotifier(app);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700118 } else {
119 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
120 }
121 return sInstance;
122 }
123 }
124
125 /** Private constructor; @see init() */
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800126 private CallNotifier(
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800127 PhoneGlobals app) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700128 mApplication = app;
129 mCM = app.mCM;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700130
131 mAudioManager = (AudioManager) mApplication.getSystemService(Context.AUDIO_SERVICE);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800132 mTelephonyManager =
133 (TelephonyManager) mApplication.getSystemService(Context.TELEPHONY_SERVICE);
134 mSubscriptionManager = (SubscriptionManager) mApplication.getSystemService(
135 Context.TELEPHONY_SUBSCRIPTION_SERVICE);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700136
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800137 registerForNotifications();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700138
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700139 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
140 if (adapter != null) {
141 adapter.getProfileProxy(mApplication.getApplicationContext(),
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800142 mBluetoothProfileServiceListener,
143 BluetoothProfile.HEADSET);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700144 }
145
Wink Savillef67832f2015-01-12 16:51:50 -0800146 mSubscriptionManager.addOnSubscriptionsChangedListener(
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800147 new OnSubscriptionsChangedListener() {
148 @Override
149 public void onSubscriptionsChanged() {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900150 updatePhoneStateListeners(true);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800151 }
152 });
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700153 }
154
155 private void createSignalInfoToneGenerator() {
156 // Instantiate the ToneGenerator for SignalInfo and CallWaiting
157 // TODO: We probably don't need the mSignalInfoToneGenerator instance
158 // around forever. Need to change it so as to create a ToneGenerator instance only
159 // when a tone is being played and releases it after its done playing.
160 if (mSignalInfoToneGenerator == null) {
161 try {
162 mSignalInfoToneGenerator = new ToneGenerator(AudioManager.STREAM_VOICE_CALL,
163 TONE_RELATIVE_VOLUME_SIGNALINFO);
164 Log.d(LOG_TAG, "CallNotifier: mSignalInfoToneGenerator created when toneplay");
165 } catch (RuntimeException e) {
166 Log.w(LOG_TAG, "CallNotifier: Exception caught while creating " +
167 "mSignalInfoToneGenerator: " + e);
168 mSignalInfoToneGenerator = null;
169 }
170 } else {
171 Log.d(LOG_TAG, "mSignalInfoToneGenerator created already, hence skipping");
172 }
173 }
174
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800175 /**
176 * Register for call state notifications with the CallManager.
177 */
178 private void registerForNotifications() {
179 mCM.registerForDisconnect(this, PHONE_DISCONNECT, null);
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800180 mCM.registerForDisplayInfo(this, PHONE_STATE_DISPLAYINFO, null);
181 mCM.registerForSignalInfo(this, PHONE_STATE_SIGNALINFO, null);
182 mCM.registerForInCallVoicePrivacyOn(this, PHONE_ENHANCED_VP_ON, null);
183 mCM.registerForInCallVoicePrivacyOff(this, PHONE_ENHANCED_VP_OFF, null);
184 mCM.registerForSuppServiceFailed(this, PHONE_SUPP_SERVICE_FAILED, null);
185 mCM.registerForTtyModeReceived(this, PHONE_TTY_MODE_RECEIVED, null);
186 }
187
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700188 @Override
189 public void handleMessage(Message msg) {
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800190 if (DBG) {
191 Log.d(LOG_TAG, "handleMessage(" + msg.what + ")");
192 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700193 switch (msg.what) {
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800194 case PHONE_DISCONNECT:
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700195 if (DBG) log("DISCONNECT");
Roshan Pius19f39cf2015-08-12 10:44:38 -0700196 // Stop any signalInfo tone being played when a call gets ended, the rest of the
197 // disconnect functionality in onDisconnect() is handled in ConnectionService.
198 stopSignalInfoTone();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700199 break;
200
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800201 case PHONE_STATE_DISPLAYINFO:
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700202 if (DBG) log("Received PHONE_STATE_DISPLAYINFO event");
203 onDisplayInfo((AsyncResult) msg.obj);
204 break;
205
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800206 case PHONE_STATE_SIGNALINFO:
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700207 if (DBG) log("Received PHONE_STATE_SIGNALINFO event");
208 onSignalInfo((AsyncResult) msg.obj);
209 break;
210
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800211 case INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE:
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700212 if (DBG) log("Received Display Info notification done event ...");
Anthony Leee9468532014-11-15 15:21:00 -0800213 PhoneDisplayMessage.dismissMessage();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700214 break;
215
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800216 case PHONE_ENHANCED_VP_ON:
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700217 if (DBG) log("PHONE_ENHANCED_VP_ON...");
218 if (!mVoicePrivacyState) {
219 int toneToPlay = InCallTonePlayer.TONE_VOICE_PRIVACY;
220 new InCallTonePlayer(toneToPlay).start();
221 mVoicePrivacyState = true;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700222 }
223 break;
224
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800225 case PHONE_ENHANCED_VP_OFF:
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700226 if (DBG) log("PHONE_ENHANCED_VP_OFF...");
227 if (mVoicePrivacyState) {
228 int toneToPlay = InCallTonePlayer.TONE_VOICE_PRIVACY;
229 new InCallTonePlayer(toneToPlay).start();
230 mVoicePrivacyState = false;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700231 }
232 break;
233
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800234 case PHONE_SUPP_SERVICE_FAILED:
Anthony Leee9468532014-11-15 15:21:00 -0800235 if (DBG) log("PHONE_SUPP_SERVICE_FAILED...");
236 onSuppServiceFailed((AsyncResult) msg.obj);
237 break;
238
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800239 case PHONE_TTY_MODE_RECEIVED:
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800240 if (DBG) log("Received PHONE_TTY_MODE_RECEIVED event");
241 onTtyModeReceived((AsyncResult) msg.obj);
242 break;
243
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700244 default:
245 // super.handleMessage(msg);
246 }
247 }
248
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700249 void updateCallNotifierRegistrationsAfterRadioTechnologyChange() {
250 if (DBG) Log.d(LOG_TAG, "updateCallNotifierRegistrationsAfterRadioTechnologyChange...");
251
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700252 // Instantiate mSignalInfoToneGenerator
253 createSignalInfoToneGenerator();
254 }
255
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700256 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700257 * Helper class to play tones through the earpiece (or speaker / BT)
258 * during a call, using the ToneGenerator.
259 *
260 * To use, just instantiate a new InCallTonePlayer
261 * (passing in the TONE_* constant for the tone you want)
262 * and start() it.
263 *
264 * When we're done playing the tone, if the phone is idle at that
265 * point, we'll reset the audio routing and speaker state.
266 * (That means that for tones that get played *after* a call
267 * disconnects, like "busy" or "congestion" or "call ended", you
268 * should NOT call resetAudioStateAfterDisconnect() yourself.
269 * Instead, just start the InCallTonePlayer, which will automatically
270 * defer the resetAudioStateAfterDisconnect() call until the tone
271 * finishes playing.)
272 */
273 private class InCallTonePlayer extends Thread {
274 private int mToneId;
275 private int mState;
276 // The possible tones we can play.
277 public static final int TONE_NONE = 0;
278 public static final int TONE_CALL_WAITING = 1;
279 public static final int TONE_BUSY = 2;
280 public static final int TONE_CONGESTION = 3;
281 public static final int TONE_CALL_ENDED = 4;
282 public static final int TONE_VOICE_PRIVACY = 5;
283 public static final int TONE_REORDER = 6;
284 public static final int TONE_INTERCEPT = 7;
285 public static final int TONE_CDMA_DROP = 8;
286 public static final int TONE_OUT_OF_SERVICE = 9;
287 public static final int TONE_REDIAL = 10;
288 public static final int TONE_OTA_CALL_END = 11;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700289 public static final int TONE_UNOBTAINABLE_NUMBER = 13;
290
291 // The tone volume relative to other sounds in the stream
292 static final int TONE_RELATIVE_VOLUME_EMERGENCY = 100;
293 static final int TONE_RELATIVE_VOLUME_HIPRI = 80;
294 static final int TONE_RELATIVE_VOLUME_LOPRI = 50;
295
296 // Buffer time (in msec) to add on to tone timeout value.
297 // Needed mainly when the timeout value for a tone is the
298 // exact duration of the tone itself.
299 static final int TONE_TIMEOUT_BUFFER = 20;
300
301 // The tone state
302 static final int TONE_OFF = 0;
303 static final int TONE_ON = 1;
304 static final int TONE_STOPPED = 2;
305
306 InCallTonePlayer(int toneId) {
307 super();
308 mToneId = toneId;
309 mState = TONE_OFF;
310 }
311
312 @Override
313 public void run() {
314 log("InCallTonePlayer.run(toneId = " + mToneId + ")...");
315
316 int toneType = 0; // passed to ToneGenerator.startTone()
317 int toneVolume; // passed to the ToneGenerator constructor
318 int toneLengthMillis;
319 int phoneType = mCM.getFgPhone().getPhoneType();
320
321 switch (mToneId) {
322 case TONE_CALL_WAITING:
323 toneType = ToneGenerator.TONE_SUP_CALL_WAITING;
324 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
325 // Call waiting tone is stopped by stopTone() method
326 toneLengthMillis = Integer.MAX_VALUE - TONE_TIMEOUT_BUFFER;
327 break;
328 case TONE_BUSY:
329 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
330 toneType = ToneGenerator.TONE_CDMA_NETWORK_BUSY_ONE_SHOT;
331 toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
332 toneLengthMillis = 1000;
Sailesh Nepalcc0375f2013-11-13 09:15:18 -0800333 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM
334 || phoneType == PhoneConstants.PHONE_TYPE_SIP
Etan Cohen0ca1c802014-07-07 15:35:48 -0700335 || phoneType == PhoneConstants.PHONE_TYPE_IMS
Sailesh Nepalcc0375f2013-11-13 09:15:18 -0800336 || phoneType == PhoneConstants.PHONE_TYPE_THIRD_PARTY) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700337 toneType = ToneGenerator.TONE_SUP_BUSY;
338 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
339 toneLengthMillis = 4000;
340 } else {
341 throw new IllegalStateException("Unexpected phone type: " + phoneType);
342 }
343 break;
344 case TONE_CONGESTION:
345 toneType = ToneGenerator.TONE_SUP_CONGESTION;
346 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
347 toneLengthMillis = 4000;
348 break;
349
350 case TONE_CALL_ENDED:
351 toneType = ToneGenerator.TONE_PROP_PROMPT;
352 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
353 toneLengthMillis = 200;
354 break;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700355 case TONE_VOICE_PRIVACY:
356 toneType = ToneGenerator.TONE_CDMA_ALERT_NETWORK_LITE;
357 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
358 toneLengthMillis = 5000;
359 break;
360 case TONE_REORDER:
361 toneType = ToneGenerator.TONE_CDMA_REORDER;
362 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
363 toneLengthMillis = 4000;
364 break;
365 case TONE_INTERCEPT:
366 toneType = ToneGenerator.TONE_CDMA_ABBR_INTERCEPT;
367 toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
368 toneLengthMillis = 500;
369 break;
370 case TONE_CDMA_DROP:
371 case TONE_OUT_OF_SERVICE:
372 toneType = ToneGenerator.TONE_CDMA_CALLDROP_LITE;
373 toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
374 toneLengthMillis = 375;
375 break;
376 case TONE_REDIAL:
377 toneType = ToneGenerator.TONE_CDMA_ALERT_AUTOREDIAL_LITE;
378 toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
379 toneLengthMillis = 5000;
380 break;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700381 case TONE_UNOBTAINABLE_NUMBER:
382 toneType = ToneGenerator.TONE_SUP_ERROR;
383 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
384 toneLengthMillis = 4000;
385 break;
386 default:
387 throw new IllegalArgumentException("Bad toneId: " + mToneId);
388 }
389
390 // If the mToneGenerator creation fails, just continue without it. It is
391 // a local audio signal, and is not as important.
392 ToneGenerator toneGenerator;
393 try {
394 int stream;
395 if (mBluetoothHeadset != null) {
Amit Mahajanb8f13202020-01-27 18:16:07 -0800396 stream = isBluetoothAudioOn() ? AudioSystem.STREAM_BLUETOOTH_SCO :
397 AudioSystem.STREAM_VOICE_CALL;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700398 } else {
Amit Mahajanb8f13202020-01-27 18:16:07 -0800399 stream = AudioSystem.STREAM_VOICE_CALL;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700400 }
401 toneGenerator = new ToneGenerator(stream, toneVolume);
402 // if (DBG) log("- created toneGenerator: " + toneGenerator);
403 } catch (RuntimeException e) {
404 Log.w(LOG_TAG,
405 "InCallTonePlayer: Exception caught while creating ToneGenerator: " + e);
406 toneGenerator = null;
407 }
408
409 // Using the ToneGenerator (with the CALL_WAITING / BUSY /
410 // CONGESTION tones at least), the ToneGenerator itself knows
411 // the right pattern of tones to play; we do NOT need to
412 // manually start/stop each individual tone, or manually
413 // insert the correct delay between tones. (We just start it
414 // and let it run for however long we want the tone pattern to
415 // continue.)
416 //
417 // TODO: When we stop the ToneGenerator in the middle of a
418 // "tone pattern", it sounds bad if we cut if off while the
419 // tone is actually playing. Consider adding API to the
420 // ToneGenerator to say "stop at the next silent part of the
421 // pattern", or simply "play the pattern N times and then
422 // stop."
423 boolean needToStopTone = true;
424 boolean okToPlayTone = false;
425
426 if (toneGenerator != null) {
427 int ringerMode = mAudioManager.getRingerMode();
428 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
429 if (toneType == ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD) {
430 if ((ringerMode != AudioManager.RINGER_MODE_SILENT) &&
431 (ringerMode != AudioManager.RINGER_MODE_VIBRATE)) {
432 if (DBG) log("- InCallTonePlayer: start playing call tone=" + toneType);
433 okToPlayTone = true;
434 needToStopTone = false;
435 }
436 } else if ((toneType == ToneGenerator.TONE_CDMA_NETWORK_BUSY_ONE_SHOT) ||
437 (toneType == ToneGenerator.TONE_CDMA_REORDER) ||
438 (toneType == ToneGenerator.TONE_CDMA_ABBR_REORDER) ||
439 (toneType == ToneGenerator.TONE_CDMA_ABBR_INTERCEPT) ||
440 (toneType == ToneGenerator.TONE_CDMA_CALLDROP_LITE)) {
441 if (ringerMode != AudioManager.RINGER_MODE_SILENT) {
442 if (DBG) log("InCallTonePlayer:playing call fail tone:" + toneType);
443 okToPlayTone = true;
444 needToStopTone = false;
445 }
446 } else if ((toneType == ToneGenerator.TONE_CDMA_ALERT_AUTOREDIAL_LITE) ||
447 (toneType == ToneGenerator.TONE_CDMA_ALERT_NETWORK_LITE)) {
448 if ((ringerMode != AudioManager.RINGER_MODE_SILENT) &&
449 (ringerMode != AudioManager.RINGER_MODE_VIBRATE)) {
450 if (DBG) log("InCallTonePlayer:playing tone for toneType=" + toneType);
451 okToPlayTone = true;
452 needToStopTone = false;
453 }
454 } else { // For the rest of the tones, always OK to play.
455 okToPlayTone = true;
456 }
457 } else { // Not "CDMA"
458 okToPlayTone = true;
459 }
460
461 synchronized (this) {
462 if (okToPlayTone && mState != TONE_STOPPED) {
463 mState = TONE_ON;
464 toneGenerator.startTone(toneType);
465 try {
466 wait(toneLengthMillis + TONE_TIMEOUT_BUFFER);
467 } catch (InterruptedException e) {
468 Log.w(LOG_TAG,
469 "InCallTonePlayer stopped: " + e);
470 }
471 if (needToStopTone) {
472 toneGenerator.stopTone();
473 }
474 }
475 // if (DBG) log("- InCallTonePlayer: done playing.");
476 toneGenerator.release();
477 mState = TONE_OFF;
478 }
479 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700480 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700481 }
482
Jordan Liu1c85e772019-08-06 16:06:19 -0700483 // Returns whether there are any connected Bluetooth audio devices
484 private boolean isBluetoothAudioOn() {
485 return mBluetoothHeadset.getConnectedDevices().size() > 0;
486 }
487
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700488 /**
489 * Displays a notification when the phone receives a DisplayInfo record.
490 */
491 private void onDisplayInfo(AsyncResult r) {
492 // Extract the DisplayInfo String from the message
493 CdmaDisplayInfoRec displayInfoRec = (CdmaDisplayInfoRec)(r.result);
494
495 if (displayInfoRec != null) {
496 String displayInfo = displayInfoRec.alpha;
497 if (DBG) log("onDisplayInfo: displayInfo=" + displayInfo);
Anthony Leee9468532014-11-15 15:21:00 -0800498 PhoneDisplayMessage.displayNetworkMessage(mApplication, displayInfo);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700499
Anthony Leee9468532014-11-15 15:21:00 -0800500 // start a timer that kills the dialog
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800501 sendEmptyMessageDelayed(INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE,
Anthony Leee9468532014-11-15 15:21:00 -0800502 SHOW_MESSAGE_NOTIFICATION_TIME);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700503 }
504 }
505
506 /**
Anthony Leee9468532014-11-15 15:21:00 -0800507 * Displays a notification when the phone receives a notice that a supplemental
508 * service has failed.
Anthony Leee9468532014-11-15 15:21:00 -0800509 */
510 private void onSuppServiceFailed(AsyncResult r) {
Tyler Gunn9dd07d02014-12-08 10:52:57 -0800511 String mergeFailedString = "";
512 if (r.result == Phone.SuppService.CONFERENCE) {
513 if (DBG) log("onSuppServiceFailed: displaying merge failure message");
514 mergeFailedString = mApplication.getResources().getString(
515 R.string.incall_error_supp_service_conference);
516 } else if (r.result == Phone.SuppService.RESUME) {
Tyler Gunn93f9ff52018-04-11 13:53:04 -0700517 if (DBG) log("onSuppServiceFailed: displaying resume failure message");
Tyler Gunn9dd07d02014-12-08 10:52:57 -0800518 mergeFailedString = mApplication.getResources().getString(
Tyler Gunn93f9ff52018-04-11 13:53:04 -0700519 R.string.incall_error_supp_service_resume);
Anju Mathapati4effeb22016-01-25 22:25:09 -0800520 } else if (r.result == Phone.SuppService.HOLD) {
Tyler Gunna7de7d32017-06-09 16:21:00 -0700521 if (DBG) log("onSuppServiceFailed: displaying hold failure message");
Anju Mathapati4effeb22016-01-25 22:25:09 -0800522 mergeFailedString = mApplication.getResources().getString(
523 R.string.incall_error_supp_service_hold);
Tyler Gunna7de7d32017-06-09 16:21:00 -0700524 } else if (r.result == Phone.SuppService.TRANSFER) {
525 if (DBG) log("onSuppServiceFailed: displaying transfer failure message");
526 mergeFailedString = mApplication.getResources().getString(
527 R.string.incall_error_supp_service_transfer);
528 } else if (r.result == Phone.SuppService.SEPARATE) {
529 if (DBG) log("onSuppServiceFailed: displaying separate failure message");
530 mergeFailedString = mApplication.getResources().getString(
531 R.string.incall_error_supp_service_separate);
532 } else if (r.result == Phone.SuppService.SWITCH) {
533 if (DBG) log("onSuppServiceFailed: displaying switch failure message");
Shashidhar Vithalrao Kulkarnia557d622018-05-10 13:41:42 +0530534 mergeFailedString = mApplication.getResources().getString(
Tyler Gunna7de7d32017-06-09 16:21:00 -0700535 R.string.incall_error_supp_service_switch);
536 } else if (r.result == Phone.SuppService.REJECT) {
537 if (DBG) log("onSuppServiceFailed: displaying reject failure message");
Shashidhar Vithalrao Kulkarnia557d622018-05-10 13:41:42 +0530538 mergeFailedString = mApplication.getResources().getString(
Tyler Gunna7de7d32017-06-09 16:21:00 -0700539 R.string.incall_error_supp_service_reject);
Shashidhar Vithalrao Kulkarnia557d622018-05-10 13:41:42 +0530540 } else if (r.result == Phone.SuppService.HANGUP) {
541 mergeFailedString = mApplication.getResources().getString(
542 R.string.incall_error_supp_service_hangup);
543 } else {
Tyler Gunna7de7d32017-06-09 16:21:00 -0700544 if (DBG) log("onSuppServiceFailed: unknown failure");
545 return;
Tyler Gunn9dd07d02014-12-08 10:52:57 -0800546 }
Tyler Gunna7de7d32017-06-09 16:21:00 -0700547
Anthony Leee9468532014-11-15 15:21:00 -0800548 PhoneDisplayMessage.displayErrorMessage(mApplication, mergeFailedString);
549
550 // start a timer that kills the dialog
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800551 sendEmptyMessageDelayed(INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE,
Anthony Leee9468532014-11-15 15:21:00 -0800552 SHOW_MESSAGE_NOTIFICATION_TIME);
553 }
554
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900555 public void updatePhoneStateListeners(boolean isRefresh) {
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530556 updatePhoneStateListeners(isRefresh, UPDATE_TYPE_MWI_CFI,
557 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
558 }
559
560 public void updatePhoneStateListeners(boolean isRefresh, int updateType, int subIdToUpdate) {
Malcolm Chena336e2a2019-02-12 18:51:37 -0800561 List<SubscriptionInfo> subInfos = SubscriptionController.getInstance()
Philip P. Moltmann700a9592019-10-03 11:53:50 -0700562 .getActiveSubscriptionInfoList(mApplication.getOpPackageName(),
563 mApplication.getFeatureId());
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800564
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900565 // Sort sub id list based on slot id, so that CFI/MWI notifications will be updated for
566 // slot 0 first then slot 1. This is needed to ensure that when CFI or MWI is enabled for
567 // both slots, user always sees icon related to slot 0 on left side followed by that of
568 // slot 1.
569 List<Integer> subIdList = new ArrayList<Integer>(mPhoneStateListeners.keySet());
570 Collections.sort(subIdList, new Comparator<Integer>() {
571 public int compare(Integer sub1, Integer sub2) {
572 int slotId1 = SubscriptionController.getInstance().getSlotIndex(sub1);
573 int slotId2 = SubscriptionController.getInstance().getSlotIndex(sub2);
574 return slotId1 > slotId2 ? 0 : -1;
575 }
576 });
577
578 for (int subIdCounter = (subIdList.size() - 1); subIdCounter >= 0; subIdCounter--) {
579 int subId = subIdList.get(subIdCounter);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800580 if (subInfos == null || !containsSubId(subInfos, subId)) {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900581 Log.d(LOG_TAG, "updatePhoneStateListeners: Hide the outstanding notifications.");
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800582 // Hide the outstanding notifications.
583 mApplication.notificationMgr.updateMwi(subId, false);
584 mApplication.notificationMgr.updateCfi(subId, false);
585
586 // Listening to LISTEN_NONE removes the listener.
587 mTelephonyManager.listen(
588 mPhoneStateListeners.get(subId), PhoneStateListener.LISTEN_NONE);
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900589 mPhoneStateListeners.remove(subId);
590 } else {
591 Log.d(LOG_TAG, "updatePhoneStateListeners: update CF notifications.");
592
593 if (mCFIStatus.containsKey(subId)) {
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530594 if ((updateType == UPDATE_TYPE_CFI) && (subId == subIdToUpdate)) {
595 mApplication.notificationMgr.updateCfi(subId, mCFIStatus.get(subId),
596 isRefresh);
597 } else {
598 mApplication.notificationMgr.updateCfi(subId, mCFIStatus.get(subId), true);
599 }
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900600 }
601 if (mMWIStatus.containsKey(subId)) {
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530602 if ((updateType == UPDATE_TYPE_MWI) && (subId == subIdToUpdate)) {
603 mApplication.notificationMgr.updateMwi(subId, mMWIStatus.get(subId),
604 isRefresh);
605 } else {
606 mApplication.notificationMgr.updateMwi(subId, mMWIStatus.get(subId), true);
607 }
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900608 }
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800609 }
610 }
611
612 if (subInfos == null) {
613 return;
614 }
615
616 // Register new phone listeners for active subscriptions.
617 for (int i = 0; i < subInfos.size(); i++) {
618 int subId = subInfos.get(i).getSubscriptionId();
619 if (!mPhoneStateListeners.containsKey(subId)) {
Meng Wang7f34c9c2020-01-14 14:57:57 -0800620 CallNotifierPhoneStateListener listener = new CallNotifierPhoneStateListener(subId);
chen xu06946472019-03-20 14:55:59 -0700621 mTelephonyManager.createForSubscriptionId(subId).listen(listener,
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800622 PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
623 | PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR);
624 mPhoneStateListeners.put(subId, listener);
625 }
626 }
627 }
628
629 /**
630 * @return {@code true} if the list contains SubscriptionInfo with the given subscription id.
631 */
632 private boolean containsSubId(List<SubscriptionInfo> subInfos, int subId) {
633 if (subInfos == null) {
634 return false;
635 }
636
637 for (int i = 0; i < subInfos.size(); i++) {
638 if (subInfos.get(i).getSubscriptionId() == subId) {
639 return true;
640 }
641 }
642 return false;
643 }
644
Anthony Leee9468532014-11-15 15:21:00 -0800645 /**
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800646 * Displays a notification when the phone receives a notice that TTY mode
647 * has changed on remote end.
648 */
649 private void onTtyModeReceived(AsyncResult r) {
650 if (DBG) log("TtyModeReceived: displaying notification message");
651
652 int resId = 0;
653 switch (((Integer)r.result).intValue()) {
654 case TelecomManager.TTY_MODE_FULL:
Daniel Brightebb4eb72020-02-18 15:16:33 -0800655 resId = com.android.internal.R.string.peerTtyModeFull;
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800656 break;
657 case TelecomManager.TTY_MODE_HCO:
Daniel Brightebb4eb72020-02-18 15:16:33 -0800658 resId = com.android.internal.R.string.peerTtyModeHco;
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800659 break;
660 case TelecomManager.TTY_MODE_VCO:
Daniel Brightebb4eb72020-02-18 15:16:33 -0800661 resId = com.android.internal.R.string.peerTtyModeVco;
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800662 break;
663 case TelecomManager.TTY_MODE_OFF:
Daniel Brightebb4eb72020-02-18 15:16:33 -0800664 resId = com.android.internal.R.string.peerTtyModeOff;
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800665 break;
666 default:
667 Log.e(LOG_TAG, "Unsupported TTY mode: " + r.result);
668 break;
669 }
670 if (resId != 0) {
671 PhoneDisplayMessage.displayNetworkMessage(mApplication,
Daniel Brightebb4eb72020-02-18 15:16:33 -0800672 mApplication.getResources().getString(resId));
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800673
674 // start a timer that kills the dialog
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800675 sendEmptyMessageDelayed(INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE,
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800676 SHOW_MESSAGE_NOTIFICATION_TIME);
677 }
678 }
679
680 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700681 * Helper class to play SignalInfo tones using the ToneGenerator.
682 *
683 * To use, just instantiate a new SignalInfoTonePlayer
684 * (passing in the ToneID constant for the tone you want)
685 * and start() it.
686 */
687 private class SignalInfoTonePlayer extends Thread {
688 private int mToneId;
689
690 SignalInfoTonePlayer(int toneId) {
691 super();
692 mToneId = toneId;
693 }
694
695 @Override
696 public void run() {
697 log("SignalInfoTonePlayer.run(toneId = " + mToneId + ")...");
Yorke Lee65cbd162014-10-08 11:26:02 -0700698 createSignalInfoToneGenerator();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700699 if (mSignalInfoToneGenerator != null) {
700 //First stop any ongoing SignalInfo tone
701 mSignalInfoToneGenerator.stopTone();
702
703 //Start playing the new tone if its a valid tone
704 mSignalInfoToneGenerator.startTone(mToneId);
705 }
706 }
707 }
708
709 /**
710 * Plays a tone when the phone receives a SignalInfo record.
711 */
712 private void onSignalInfo(AsyncResult r) {
713 // Signal Info are totally ignored on non-voice-capable devices.
714 if (!PhoneGlobals.sVoiceCapable) {
715 Log.w(LOG_TAG, "Got onSignalInfo() on non-voice-capable device! Ignoring...");
716 return;
717 }
718
719 if (PhoneUtils.isRealIncomingCall(mCM.getFirstActiveRingingCall().getState())) {
720 // Do not start any new SignalInfo tone when Call state is INCOMING
721 // and stop any previous SignalInfo tone which is being played
722 stopSignalInfoTone();
723 } else {
724 // Extract the SignalInfo String from the message
725 CdmaSignalInfoRec signalInfoRec = (CdmaSignalInfoRec)(r.result);
726 // Only proceed if a Signal info is present.
727 if (signalInfoRec != null) {
728 boolean isPresent = signalInfoRec.isPresent;
729 if (DBG) log("onSignalInfo: isPresent=" + isPresent);
730 if (isPresent) {// if tone is valid
731 int uSignalType = signalInfoRec.signalType;
732 int uAlertPitch = signalInfoRec.alertPitch;
733 int uSignal = signalInfoRec.signal;
734
735 if (DBG) log("onSignalInfo: uSignalType=" + uSignalType + ", uAlertPitch=" +
736 uAlertPitch + ", uSignal=" + uSignal);
737 //Map the Signal to a ToneGenerator ToneID only if Signal info is present
738 int toneID = SignalToneUtil.getAudioToneFromSignalInfo
739 (uSignalType, uAlertPitch, uSignal);
740
741 //Create the SignalInfo tone player and pass the ToneID
742 new SignalInfoTonePlayer(toneID).start();
743 }
744 }
745 }
746 }
747
748 /**
749 * Stops a SignalInfo tone in the following condition
750 * 1 - On receiving a New Ringing Call
751 * 2 - On disconnecting a call
752 * 3 - On answering a Call Waiting Call
753 */
754 /* package */ void stopSignalInfoTone() {
755 if (DBG) log("stopSignalInfoTone: Stopping SignalInfo tone player");
756 new SignalInfoTonePlayer(ToneGenerator.TONE_CDMA_SIGNAL_OFF).start();
757 }
758
Santos Cordon5c046722014-09-18 15:41:13 -0700759 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800760 new BluetoothProfile.ServiceListener() {
761 public void onServiceConnected(int profile, BluetoothProfile proxy) {
762 mBluetoothHeadset = (BluetoothHeadset) proxy;
763 if (VDBG) log("- Got BluetoothHeadset: " + mBluetoothHeadset);
764 }
765
766 public void onServiceDisconnected(int profile) {
767 mBluetoothHeadset = null;
768 }
769 };
770
771 private class CallNotifierPhoneStateListener extends PhoneStateListener {
Meng Wang7f34c9c2020-01-14 14:57:57 -0800772 private final int mSubId;
773
774 CallNotifierPhoneStateListener(int subId) {
chen xu06946472019-03-20 14:55:59 -0700775 super();
Meng Wang7f34c9c2020-01-14 14:57:57 -0800776 this.mSubId = subId;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700777 }
778
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800779 @Override
780 public void onMessageWaitingIndicatorChanged(boolean visible) {
781 if (VDBG) log("onMessageWaitingIndicatorChanged(): " + this.mSubId + " " + visible);
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900782 mMWIStatus.put(this.mSubId, visible);
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530783 updatePhoneStateListeners(false, UPDATE_TYPE_MWI, this.mSubId);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800784 }
785
786 @Override
787 public void onCallForwardingIndicatorChanged(boolean visible) {
Tyler Gunn17bffd02017-09-19 11:40:12 -0700788 Log.i(LOG_TAG, "onCallForwardingIndicatorChanged(): subId=" + this.mSubId
789 + ", visible=" + (visible ? "Y" : "N"));
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900790 mCFIStatus.put(this.mSubId, visible);
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530791 updatePhoneStateListeners(false, UPDATE_TYPE_CFI, this.mSubId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700792 }
793 };
794
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700795 private void log(String msg) {
796 Log.d(LOG_TAG, msg);
797 }
798}