blob: 4c38d98d4594fec1c9704f7c218e08d5953b9f85 [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;
Zoey Chen5719bac2021-01-28 15:05:49 +080028import android.os.HandlerExecutor;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070029import android.os.Message;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070030import android.os.SystemProperties;
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -080031import android.telecom.TelecomManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070032import android.telephony.PhoneStateListener;
Andrew Lee2fcb6c32014-12-04 14:52:35 -080033import android.telephony.SubscriptionInfo;
34import android.telephony.SubscriptionManager;
35import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070036import android.telephony.TelephonyManager;
Andrew Lee2fcb6c32014-12-04 14:52:35 -080037import android.util.ArrayMap;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070038import android.util.Log;
39
Aravind Sreekumarafc08c52018-04-10 15:34:32 -070040import com.android.internal.telephony.CallManager;
41import com.android.internal.telephony.Phone;
42import com.android.internal.telephony.PhoneConstants;
43import com.android.internal.telephony.SubscriptionController;
44import com.android.internal.telephony.cdma.CdmaInformationRecords.CdmaDisplayInfoRec;
45import com.android.internal.telephony.cdma.CdmaInformationRecords.CdmaSignalInfoRec;
46import com.android.internal.telephony.cdma.SignalToneUtil;
47
Kazuya Ohshiro263737d2017-10-06 19:42:03 +090048import java.util.ArrayList;
49import java.util.Collections;
50import java.util.Comparator;
Andrew Lee2fcb6c32014-12-04 14:52:35 -080051import java.util.List;
52import java.util.Map;
53
Santos Cordon7d4ddf62013-07-10 11:58:08 -070054/**
55 * Phone app module that listens for phone state changes and various other
56 * events from the telephony layer, and triggers any resulting UI behavior
Santos Cordon5422a8d2014-09-12 04:20:56 -070057 * (like starting the Incoming Call UI, playing in-call tones,
Santos Cordon7d4ddf62013-07-10 11:58:08 -070058 * updating notifications, writing call log entries, etc.)
59 */
Santos Cordon5422a8d2014-09-12 04:20:56 -070060public class CallNotifier extends Handler {
Santos Cordon7d4ddf62013-07-10 11:58:08 -070061 private static final String LOG_TAG = "CallNotifier";
62 private static final boolean DBG =
63 (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
64 private static final boolean VDBG = (PhoneGlobals.DBG_LEVEL >= 2);
65
Anthony Leee9468532014-11-15 15:21:00 -080066 // Time to display the message from the underlying phone layers.
67 private static final int SHOW_MESSAGE_NOTIFICATION_TIME = 3000; // msec
Santos Cordon7d4ddf62013-07-10 11:58:08 -070068
69 /** The singleton instance. */
70 private static CallNotifier sInstance;
71
Andrew Lee2fcb6c32014-12-04 14:52:35 -080072 private Map<Integer, CallNotifierPhoneStateListener> mPhoneStateListeners =
73 new ArrayMap<Integer, CallNotifierPhoneStateListener>();
Kazuya Ohshiro263737d2017-10-06 19:42:03 +090074 private Map<Integer, Boolean> mCFIStatus = new ArrayMap<Integer, Boolean>();
75 private Map<Integer, Boolean> mMWIStatus = new ArrayMap<Integer, Boolean>();
Santos Cordon7d4ddf62013-07-10 11:58:08 -070076 private PhoneGlobals mApplication;
77 private CallManager mCM;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070078 private BluetoothHeadset mBluetoothHeadset;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070079
80 // ToneGenerator instance for playing SignalInfo tones
81 private ToneGenerator mSignalInfoToneGenerator;
82
83 // The tone volume relative to other sounds in the stream SignalInfo
84 private static final int TONE_RELATIVE_VOLUME_SIGNALINFO = 80;
85
Santos Cordon7d4ddf62013-07-10 11:58:08 -070086 private boolean mVoicePrivacyState = false;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070087
Santos Cordon7d4ddf62013-07-10 11:58:08 -070088 // Cached AudioManager
89 private AudioManager mAudioManager;
Andrew Lee2fcb6c32014-12-04 14:52:35 -080090 private SubscriptionManager mSubscriptionManager;
91 private TelephonyManager mTelephonyManager;
Santos Cordon27a3c1f2013-08-06 07:49:27 -070092
Brad Ebingera9c6b6d2016-01-07 17:24:16 -080093 // Events from the Phone object:
94 public static final int PHONE_DISCONNECT = 3;
95 public static final int PHONE_STATE_DISPLAYINFO = 6;
96 public static final int PHONE_STATE_SIGNALINFO = 7;
97 public static final int PHONE_ENHANCED_VP_ON = 9;
98 public static final int PHONE_ENHANCED_VP_OFF = 10;
99 public static final int PHONE_SUPP_SERVICE_FAILED = 14;
100 public static final int PHONE_TTY_MODE_RECEIVED = 15;
101 // Events generated internally.
102 // We should store all the possible event type values in one place to make sure that
103 // they don't step on each others' toes.
104 public static final int INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE = 22;
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800105
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530106 public static final int UPDATE_TYPE_MWI = 0;
107 public static final int UPDATE_TYPE_CFI = 1;
108 public static final int UPDATE_TYPE_MWI_CFI = 2;
109
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700110 /**
111 * Initialize the singleton CallNotifier instance.
112 * This is only done once, at startup, from PhoneApp.onCreate().
113 */
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800114 /* package */ static CallNotifier init(
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800115 PhoneGlobals app) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700116 synchronized (CallNotifier.class) {
117 if (sInstance == null) {
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800118 sInstance = new CallNotifier(app);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700119 } else {
120 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
121 }
122 return sInstance;
123 }
124 }
125
126 /** Private constructor; @see init() */
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800127 private CallNotifier(
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800128 PhoneGlobals app) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700129 mApplication = app;
130 mCM = app.mCM;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700131
132 mAudioManager = (AudioManager) mApplication.getSystemService(Context.AUDIO_SERVICE);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800133 mTelephonyManager =
134 (TelephonyManager) mApplication.getSystemService(Context.TELEPHONY_SERVICE);
135 mSubscriptionManager = (SubscriptionManager) mApplication.getSystemService(
136 Context.TELEPHONY_SUBSCRIPTION_SERVICE);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700137
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800138 registerForNotifications();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700139
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700140 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
141 if (adapter != null) {
142 adapter.getProfileProxy(mApplication.getApplicationContext(),
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800143 mBluetoothProfileServiceListener,
144 BluetoothProfile.HEADSET);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700145 }
146
Wink Savillef67832f2015-01-12 16:51:50 -0800147 mSubscriptionManager.addOnSubscriptionsChangedListener(
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800148 new OnSubscriptionsChangedListener() {
149 @Override
150 public void onSubscriptionsChanged() {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900151 updatePhoneStateListeners(true);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800152 }
153 });
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700154 }
155
156 private void createSignalInfoToneGenerator() {
157 // Instantiate the ToneGenerator for SignalInfo and CallWaiting
158 // TODO: We probably don't need the mSignalInfoToneGenerator instance
159 // around forever. Need to change it so as to create a ToneGenerator instance only
160 // when a tone is being played and releases it after its done playing.
161 if (mSignalInfoToneGenerator == null) {
162 try {
163 mSignalInfoToneGenerator = new ToneGenerator(AudioManager.STREAM_VOICE_CALL,
164 TONE_RELATIVE_VOLUME_SIGNALINFO);
165 Log.d(LOG_TAG, "CallNotifier: mSignalInfoToneGenerator created when toneplay");
166 } catch (RuntimeException e) {
167 Log.w(LOG_TAG, "CallNotifier: Exception caught while creating " +
168 "mSignalInfoToneGenerator: " + e);
169 mSignalInfoToneGenerator = null;
170 }
171 } else {
172 Log.d(LOG_TAG, "mSignalInfoToneGenerator created already, hence skipping");
173 }
174 }
175
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800176 /**
177 * Register for call state notifications with the CallManager.
178 */
179 private void registerForNotifications() {
180 mCM.registerForDisconnect(this, PHONE_DISCONNECT, null);
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800181 mCM.registerForDisplayInfo(this, PHONE_STATE_DISPLAYINFO, null);
182 mCM.registerForSignalInfo(this, PHONE_STATE_SIGNALINFO, null);
183 mCM.registerForInCallVoicePrivacyOn(this, PHONE_ENHANCED_VP_ON, null);
184 mCM.registerForInCallVoicePrivacyOff(this, PHONE_ENHANCED_VP_OFF, null);
185 mCM.registerForSuppServiceFailed(this, PHONE_SUPP_SERVICE_FAILED, null);
186 mCM.registerForTtyModeReceived(this, PHONE_TTY_MODE_RECEIVED, null);
187 }
188
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700189 @Override
190 public void handleMessage(Message msg) {
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800191 if (DBG) {
192 Log.d(LOG_TAG, "handleMessage(" + msg.what + ")");
193 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700194 switch (msg.what) {
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800195 case PHONE_DISCONNECT:
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700196 if (DBG) log("DISCONNECT");
Roshan Pius19f39cf2015-08-12 10:44:38 -0700197 // Stop any signalInfo tone being played when a call gets ended, the rest of the
198 // disconnect functionality in onDisconnect() is handled in ConnectionService.
199 stopSignalInfoTone();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700200 break;
201
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800202 case PHONE_STATE_DISPLAYINFO:
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700203 if (DBG) log("Received PHONE_STATE_DISPLAYINFO event");
204 onDisplayInfo((AsyncResult) msg.obj);
205 break;
206
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800207 case PHONE_STATE_SIGNALINFO:
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700208 if (DBG) log("Received PHONE_STATE_SIGNALINFO event");
209 onSignalInfo((AsyncResult) msg.obj);
210 break;
211
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800212 case INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE:
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700213 if (DBG) log("Received Display Info notification done event ...");
Anthony Leee9468532014-11-15 15:21:00 -0800214 PhoneDisplayMessage.dismissMessage();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700215 break;
216
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800217 case PHONE_ENHANCED_VP_ON:
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700218 if (DBG) log("PHONE_ENHANCED_VP_ON...");
219 if (!mVoicePrivacyState) {
220 int toneToPlay = InCallTonePlayer.TONE_VOICE_PRIVACY;
221 new InCallTonePlayer(toneToPlay).start();
222 mVoicePrivacyState = true;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700223 }
224 break;
225
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800226 case PHONE_ENHANCED_VP_OFF:
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700227 if (DBG) log("PHONE_ENHANCED_VP_OFF...");
228 if (mVoicePrivacyState) {
229 int toneToPlay = InCallTonePlayer.TONE_VOICE_PRIVACY;
230 new InCallTonePlayer(toneToPlay).start();
231 mVoicePrivacyState = false;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700232 }
233 break;
234
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800235 case PHONE_SUPP_SERVICE_FAILED:
Anthony Leee9468532014-11-15 15:21:00 -0800236 if (DBG) log("PHONE_SUPP_SERVICE_FAILED...");
237 onSuppServiceFailed((AsyncResult) msg.obj);
238 break;
239
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800240 case PHONE_TTY_MODE_RECEIVED:
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800241 if (DBG) log("Received PHONE_TTY_MODE_RECEIVED event");
242 onTtyModeReceived((AsyncResult) msg.obj);
243 break;
244
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700245 default:
246 // super.handleMessage(msg);
247 }
248 }
249
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700250 void updateCallNotifierRegistrationsAfterRadioTechnologyChange() {
251 if (DBG) Log.d(LOG_TAG, "updateCallNotifierRegistrationsAfterRadioTechnologyChange...");
252
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700253 // Instantiate mSignalInfoToneGenerator
254 createSignalInfoToneGenerator();
255 }
256
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700257 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700258 * Helper class to play tones through the earpiece (or speaker / BT)
259 * during a call, using the ToneGenerator.
260 *
261 * To use, just instantiate a new InCallTonePlayer
262 * (passing in the TONE_* constant for the tone you want)
263 * and start() it.
264 *
265 * When we're done playing the tone, if the phone is idle at that
266 * point, we'll reset the audio routing and speaker state.
267 * (That means that for tones that get played *after* a call
268 * disconnects, like "busy" or "congestion" or "call ended", you
269 * should NOT call resetAudioStateAfterDisconnect() yourself.
270 * Instead, just start the InCallTonePlayer, which will automatically
271 * defer the resetAudioStateAfterDisconnect() call until the tone
272 * finishes playing.)
273 */
274 private class InCallTonePlayer extends Thread {
275 private int mToneId;
276 private int mState;
277 // The possible tones we can play.
278 public static final int TONE_NONE = 0;
279 public static final int TONE_CALL_WAITING = 1;
280 public static final int TONE_BUSY = 2;
281 public static final int TONE_CONGESTION = 3;
282 public static final int TONE_CALL_ENDED = 4;
283 public static final int TONE_VOICE_PRIVACY = 5;
284 public static final int TONE_REORDER = 6;
285 public static final int TONE_INTERCEPT = 7;
286 public static final int TONE_CDMA_DROP = 8;
287 public static final int TONE_OUT_OF_SERVICE = 9;
288 public static final int TONE_REDIAL = 10;
289 public static final int TONE_OTA_CALL_END = 11;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700290 public static final int TONE_UNOBTAINABLE_NUMBER = 13;
291
292 // The tone volume relative to other sounds in the stream
293 static final int TONE_RELATIVE_VOLUME_EMERGENCY = 100;
294 static final int TONE_RELATIVE_VOLUME_HIPRI = 80;
295 static final int TONE_RELATIVE_VOLUME_LOPRI = 50;
296
297 // Buffer time (in msec) to add on to tone timeout value.
298 // Needed mainly when the timeout value for a tone is the
299 // exact duration of the tone itself.
300 static final int TONE_TIMEOUT_BUFFER = 20;
301
302 // The tone state
303 static final int TONE_OFF = 0;
304 static final int TONE_ON = 1;
305 static final int TONE_STOPPED = 2;
306
307 InCallTonePlayer(int toneId) {
308 super();
309 mToneId = toneId;
310 mState = TONE_OFF;
311 }
312
313 @Override
314 public void run() {
315 log("InCallTonePlayer.run(toneId = " + mToneId + ")...");
316
317 int toneType = 0; // passed to ToneGenerator.startTone()
318 int toneVolume; // passed to the ToneGenerator constructor
319 int toneLengthMillis;
320 int phoneType = mCM.getFgPhone().getPhoneType();
321
322 switch (mToneId) {
323 case TONE_CALL_WAITING:
324 toneType = ToneGenerator.TONE_SUP_CALL_WAITING;
325 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
326 // Call waiting tone is stopped by stopTone() method
327 toneLengthMillis = Integer.MAX_VALUE - TONE_TIMEOUT_BUFFER;
328 break;
329 case TONE_BUSY:
330 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
331 toneType = ToneGenerator.TONE_CDMA_NETWORK_BUSY_ONE_SHOT;
332 toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
333 toneLengthMillis = 1000;
Sailesh Nepalcc0375f2013-11-13 09:15:18 -0800334 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM
335 || phoneType == PhoneConstants.PHONE_TYPE_SIP
Etan Cohen0ca1c802014-07-07 15:35:48 -0700336 || phoneType == PhoneConstants.PHONE_TYPE_IMS
Sailesh Nepalcc0375f2013-11-13 09:15:18 -0800337 || phoneType == PhoneConstants.PHONE_TYPE_THIRD_PARTY) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700338 toneType = ToneGenerator.TONE_SUP_BUSY;
339 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
340 toneLengthMillis = 4000;
341 } else {
342 throw new IllegalStateException("Unexpected phone type: " + phoneType);
343 }
344 break;
345 case TONE_CONGESTION:
346 toneType = ToneGenerator.TONE_SUP_CONGESTION;
347 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
348 toneLengthMillis = 4000;
349 break;
350
351 case TONE_CALL_ENDED:
352 toneType = ToneGenerator.TONE_PROP_PROMPT;
353 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
354 toneLengthMillis = 200;
355 break;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700356 case TONE_VOICE_PRIVACY:
357 toneType = ToneGenerator.TONE_CDMA_ALERT_NETWORK_LITE;
358 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
359 toneLengthMillis = 5000;
360 break;
361 case TONE_REORDER:
362 toneType = ToneGenerator.TONE_CDMA_REORDER;
363 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
364 toneLengthMillis = 4000;
365 break;
366 case TONE_INTERCEPT:
367 toneType = ToneGenerator.TONE_CDMA_ABBR_INTERCEPT;
368 toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
369 toneLengthMillis = 500;
370 break;
371 case TONE_CDMA_DROP:
372 case TONE_OUT_OF_SERVICE:
373 toneType = ToneGenerator.TONE_CDMA_CALLDROP_LITE;
374 toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
375 toneLengthMillis = 375;
376 break;
377 case TONE_REDIAL:
378 toneType = ToneGenerator.TONE_CDMA_ALERT_AUTOREDIAL_LITE;
379 toneVolume = TONE_RELATIVE_VOLUME_LOPRI;
380 toneLengthMillis = 5000;
381 break;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700382 case TONE_UNOBTAINABLE_NUMBER:
383 toneType = ToneGenerator.TONE_SUP_ERROR;
384 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
385 toneLengthMillis = 4000;
386 break;
387 default:
388 throw new IllegalArgumentException("Bad toneId: " + mToneId);
389 }
390
391 // If the mToneGenerator creation fails, just continue without it. It is
392 // a local audio signal, and is not as important.
393 ToneGenerator toneGenerator;
394 try {
395 int stream;
396 if (mBluetoothHeadset != null) {
Amit Mahajanb8f13202020-01-27 18:16:07 -0800397 stream = isBluetoothAudioOn() ? AudioSystem.STREAM_BLUETOOTH_SCO :
398 AudioSystem.STREAM_VOICE_CALL;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700399 } else {
Amit Mahajanb8f13202020-01-27 18:16:07 -0800400 stream = AudioSystem.STREAM_VOICE_CALL;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700401 }
402 toneGenerator = new ToneGenerator(stream, toneVolume);
403 // if (DBG) log("- created toneGenerator: " + toneGenerator);
404 } catch (RuntimeException e) {
405 Log.w(LOG_TAG,
406 "InCallTonePlayer: Exception caught while creating ToneGenerator: " + e);
407 toneGenerator = null;
408 }
409
410 // Using the ToneGenerator (with the CALL_WAITING / BUSY /
411 // CONGESTION tones at least), the ToneGenerator itself knows
412 // the right pattern of tones to play; we do NOT need to
413 // manually start/stop each individual tone, or manually
414 // insert the correct delay between tones. (We just start it
415 // and let it run for however long we want the tone pattern to
416 // continue.)
417 //
418 // TODO: When we stop the ToneGenerator in the middle of a
419 // "tone pattern", it sounds bad if we cut if off while the
420 // tone is actually playing. Consider adding API to the
421 // ToneGenerator to say "stop at the next silent part of the
422 // pattern", or simply "play the pattern N times and then
423 // stop."
424 boolean needToStopTone = true;
425 boolean okToPlayTone = false;
426
427 if (toneGenerator != null) {
428 int ringerMode = mAudioManager.getRingerMode();
429 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
430 if (toneType == ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD) {
431 if ((ringerMode != AudioManager.RINGER_MODE_SILENT) &&
432 (ringerMode != AudioManager.RINGER_MODE_VIBRATE)) {
433 if (DBG) log("- InCallTonePlayer: start playing call tone=" + toneType);
434 okToPlayTone = true;
435 needToStopTone = false;
436 }
437 } else if ((toneType == ToneGenerator.TONE_CDMA_NETWORK_BUSY_ONE_SHOT) ||
438 (toneType == ToneGenerator.TONE_CDMA_REORDER) ||
439 (toneType == ToneGenerator.TONE_CDMA_ABBR_REORDER) ||
440 (toneType == ToneGenerator.TONE_CDMA_ABBR_INTERCEPT) ||
441 (toneType == ToneGenerator.TONE_CDMA_CALLDROP_LITE)) {
442 if (ringerMode != AudioManager.RINGER_MODE_SILENT) {
443 if (DBG) log("InCallTonePlayer:playing call fail tone:" + toneType);
444 okToPlayTone = true;
445 needToStopTone = false;
446 }
447 } else if ((toneType == ToneGenerator.TONE_CDMA_ALERT_AUTOREDIAL_LITE) ||
448 (toneType == ToneGenerator.TONE_CDMA_ALERT_NETWORK_LITE)) {
449 if ((ringerMode != AudioManager.RINGER_MODE_SILENT) &&
450 (ringerMode != AudioManager.RINGER_MODE_VIBRATE)) {
451 if (DBG) log("InCallTonePlayer:playing tone for toneType=" + toneType);
452 okToPlayTone = true;
453 needToStopTone = false;
454 }
455 } else { // For the rest of the tones, always OK to play.
456 okToPlayTone = true;
457 }
458 } else { // Not "CDMA"
459 okToPlayTone = true;
460 }
461
462 synchronized (this) {
463 if (okToPlayTone && mState != TONE_STOPPED) {
464 mState = TONE_ON;
465 toneGenerator.startTone(toneType);
466 try {
467 wait(toneLengthMillis + TONE_TIMEOUT_BUFFER);
468 } catch (InterruptedException e) {
469 Log.w(LOG_TAG,
470 "InCallTonePlayer stopped: " + e);
471 }
472 if (needToStopTone) {
473 toneGenerator.stopTone();
474 }
475 }
476 // if (DBG) log("- InCallTonePlayer: done playing.");
477 toneGenerator.release();
478 mState = TONE_OFF;
479 }
480 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700481 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700482 }
483
Jordan Liu1c85e772019-08-06 16:06:19 -0700484 // Returns whether there are any connected Bluetooth audio devices
485 private boolean isBluetoothAudioOn() {
486 return mBluetoothHeadset.getConnectedDevices().size() > 0;
487 }
488
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700489 /**
490 * Displays a notification when the phone receives a DisplayInfo record.
491 */
492 private void onDisplayInfo(AsyncResult r) {
493 // Extract the DisplayInfo String from the message
494 CdmaDisplayInfoRec displayInfoRec = (CdmaDisplayInfoRec)(r.result);
495
496 if (displayInfoRec != null) {
497 String displayInfo = displayInfoRec.alpha;
498 if (DBG) log("onDisplayInfo: displayInfo=" + displayInfo);
Anthony Leee9468532014-11-15 15:21:00 -0800499 PhoneDisplayMessage.displayNetworkMessage(mApplication, displayInfo);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700500
Anthony Leee9468532014-11-15 15:21:00 -0800501 // start a timer that kills the dialog
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800502 sendEmptyMessageDelayed(INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE,
Anthony Leee9468532014-11-15 15:21:00 -0800503 SHOW_MESSAGE_NOTIFICATION_TIME);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700504 }
505 }
506
507 /**
Anthony Leee9468532014-11-15 15:21:00 -0800508 * Displays a notification when the phone receives a notice that a supplemental
509 * service has failed.
Anthony Leee9468532014-11-15 15:21:00 -0800510 */
511 private void onSuppServiceFailed(AsyncResult r) {
Tyler Gunn9dd07d02014-12-08 10:52:57 -0800512 String mergeFailedString = "";
513 if (r.result == Phone.SuppService.CONFERENCE) {
514 if (DBG) log("onSuppServiceFailed: displaying merge failure message");
515 mergeFailedString = mApplication.getResources().getString(
516 R.string.incall_error_supp_service_conference);
517 } else if (r.result == Phone.SuppService.RESUME) {
Tyler Gunn93f9ff52018-04-11 13:53:04 -0700518 if (DBG) log("onSuppServiceFailed: displaying resume failure message");
Tyler Gunn9dd07d02014-12-08 10:52:57 -0800519 mergeFailedString = mApplication.getResources().getString(
Tyler Gunn93f9ff52018-04-11 13:53:04 -0700520 R.string.incall_error_supp_service_resume);
Anju Mathapati4effeb22016-01-25 22:25:09 -0800521 } else if (r.result == Phone.SuppService.HOLD) {
Tyler Gunna7de7d32017-06-09 16:21:00 -0700522 if (DBG) log("onSuppServiceFailed: displaying hold failure message");
Anju Mathapati4effeb22016-01-25 22:25:09 -0800523 mergeFailedString = mApplication.getResources().getString(
524 R.string.incall_error_supp_service_hold);
Tyler Gunna7de7d32017-06-09 16:21:00 -0700525 } else if (r.result == Phone.SuppService.TRANSFER) {
526 if (DBG) log("onSuppServiceFailed: displaying transfer failure message");
527 mergeFailedString = mApplication.getResources().getString(
528 R.string.incall_error_supp_service_transfer);
529 } else if (r.result == Phone.SuppService.SEPARATE) {
530 if (DBG) log("onSuppServiceFailed: displaying separate failure message");
531 mergeFailedString = mApplication.getResources().getString(
532 R.string.incall_error_supp_service_separate);
533 } else if (r.result == Phone.SuppService.SWITCH) {
534 if (DBG) log("onSuppServiceFailed: displaying switch failure message");
Shashidhar Vithalrao Kulkarnia557d622018-05-10 13:41:42 +0530535 mergeFailedString = mApplication.getResources().getString(
Tyler Gunna7de7d32017-06-09 16:21:00 -0700536 R.string.incall_error_supp_service_switch);
537 } else if (r.result == Phone.SuppService.REJECT) {
538 if (DBG) log("onSuppServiceFailed: displaying reject failure message");
Shashidhar Vithalrao Kulkarnia557d622018-05-10 13:41:42 +0530539 mergeFailedString = mApplication.getResources().getString(
Tyler Gunna7de7d32017-06-09 16:21:00 -0700540 R.string.incall_error_supp_service_reject);
Shashidhar Vithalrao Kulkarnia557d622018-05-10 13:41:42 +0530541 } else if (r.result == Phone.SuppService.HANGUP) {
542 mergeFailedString = mApplication.getResources().getString(
543 R.string.incall_error_supp_service_hangup);
544 } else {
Tyler Gunna7de7d32017-06-09 16:21:00 -0700545 if (DBG) log("onSuppServiceFailed: unknown failure");
546 return;
Tyler Gunn9dd07d02014-12-08 10:52:57 -0800547 }
Tyler Gunna7de7d32017-06-09 16:21:00 -0700548
Anthony Leee9468532014-11-15 15:21:00 -0800549 PhoneDisplayMessage.displayErrorMessage(mApplication, mergeFailedString);
550
551 // start a timer that kills the dialog
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800552 sendEmptyMessageDelayed(INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE,
Anthony Leee9468532014-11-15 15:21:00 -0800553 SHOW_MESSAGE_NOTIFICATION_TIME);
554 }
555
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900556 public void updatePhoneStateListeners(boolean isRefresh) {
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530557 updatePhoneStateListeners(isRefresh, UPDATE_TYPE_MWI_CFI,
558 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
559 }
560
561 public void updatePhoneStateListeners(boolean isRefresh, int updateType, int subIdToUpdate) {
Malcolm Chena336e2a2019-02-12 18:51:37 -0800562 List<SubscriptionInfo> subInfos = SubscriptionController.getInstance()
Philip P. Moltmann700a9592019-10-03 11:53:50 -0700563 .getActiveSubscriptionInfoList(mApplication.getOpPackageName(),
Philip P. Moltmanne6bb1cd2020-03-05 16:24:02 -0800564 mApplication.getAttributionTag());
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800565
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900566 // Sort sub id list based on slot id, so that CFI/MWI notifications will be updated for
567 // slot 0 first then slot 1. This is needed to ensure that when CFI or MWI is enabled for
568 // both slots, user always sees icon related to slot 0 on left side followed by that of
569 // slot 1.
570 List<Integer> subIdList = new ArrayList<Integer>(mPhoneStateListeners.keySet());
571 Collections.sort(subIdList, new Comparator<Integer>() {
572 public int compare(Integer sub1, Integer sub2) {
573 int slotId1 = SubscriptionController.getInstance().getSlotIndex(sub1);
574 int slotId2 = SubscriptionController.getInstance().getSlotIndex(sub2);
575 return slotId1 > slotId2 ? 0 : -1;
576 }
577 });
578
579 for (int subIdCounter = (subIdList.size() - 1); subIdCounter >= 0; subIdCounter--) {
580 int subId = subIdList.get(subIdCounter);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800581 if (subInfos == null || !containsSubId(subInfos, subId)) {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900582 Log.d(LOG_TAG, "updatePhoneStateListeners: Hide the outstanding notifications.");
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800583 // Hide the outstanding notifications.
584 mApplication.notificationMgr.updateMwi(subId, false);
585 mApplication.notificationMgr.updateCfi(subId, false);
586
Zoey Chen5719bac2021-01-28 15:05:49 +0800587 // Unregister the listener.
588 mTelephonyManager.unregisterPhoneStateListener(mPhoneStateListeners.get(subId));
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);
Zoey Chen5719bac2021-01-28 15:05:49 +0800621 mTelephonyManager.createForSubscriptionId(subId).registerPhoneStateListener(
622 new HandlerExecutor(this), listener);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800623 mPhoneStateListeners.put(subId, listener);
624 }
625 }
626 }
627
628 /**
629 * @return {@code true} if the list contains SubscriptionInfo with the given subscription id.
630 */
631 private boolean containsSubId(List<SubscriptionInfo> subInfos, int subId) {
632 if (subInfos == null) {
633 return false;
634 }
635
636 for (int i = 0; i < subInfos.size(); i++) {
637 if (subInfos.get(i).getSubscriptionId() == subId) {
638 return true;
639 }
640 }
641 return false;
642 }
643
Anthony Leee9468532014-11-15 15:21:00 -0800644 /**
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800645 * Displays a notification when the phone receives a notice that TTY mode
646 * has changed on remote end.
647 */
648 private void onTtyModeReceived(AsyncResult r) {
649 if (DBG) log("TtyModeReceived: displaying notification message");
650
651 int resId = 0;
652 switch (((Integer)r.result).intValue()) {
653 case TelecomManager.TTY_MODE_FULL:
Daniel Brightebb4eb72020-02-18 15:16:33 -0800654 resId = com.android.internal.R.string.peerTtyModeFull;
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800655 break;
656 case TelecomManager.TTY_MODE_HCO:
Daniel Brightebb4eb72020-02-18 15:16:33 -0800657 resId = com.android.internal.R.string.peerTtyModeHco;
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800658 break;
659 case TelecomManager.TTY_MODE_VCO:
Daniel Brightebb4eb72020-02-18 15:16:33 -0800660 resId = com.android.internal.R.string.peerTtyModeVco;
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800661 break;
662 case TelecomManager.TTY_MODE_OFF:
Daniel Brightebb4eb72020-02-18 15:16:33 -0800663 resId = com.android.internal.R.string.peerTtyModeOff;
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800664 break;
665 default:
666 Log.e(LOG_TAG, "Unsupported TTY mode: " + r.result);
667 break;
668 }
669 if (resId != 0) {
670 PhoneDisplayMessage.displayNetworkMessage(mApplication,
Daniel Brightebb4eb72020-02-18 15:16:33 -0800671 mApplication.getResources().getString(resId));
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800672
673 // start a timer that kills the dialog
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800674 sendEmptyMessageDelayed(INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE,
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800675 SHOW_MESSAGE_NOTIFICATION_TIME);
676 }
677 }
678
679 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700680 * Helper class to play SignalInfo tones using the ToneGenerator.
681 *
682 * To use, just instantiate a new SignalInfoTonePlayer
683 * (passing in the ToneID constant for the tone you want)
684 * and start() it.
685 */
686 private class SignalInfoTonePlayer extends Thread {
687 private int mToneId;
688
689 SignalInfoTonePlayer(int toneId) {
690 super();
691 mToneId = toneId;
692 }
693
694 @Override
695 public void run() {
696 log("SignalInfoTonePlayer.run(toneId = " + mToneId + ")...");
Yorke Lee65cbd162014-10-08 11:26:02 -0700697 createSignalInfoToneGenerator();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700698 if (mSignalInfoToneGenerator != null) {
699 //First stop any ongoing SignalInfo tone
700 mSignalInfoToneGenerator.stopTone();
701
702 //Start playing the new tone if its a valid tone
703 mSignalInfoToneGenerator.startTone(mToneId);
704 }
705 }
706 }
707
708 /**
709 * Plays a tone when the phone receives a SignalInfo record.
710 */
711 private void onSignalInfo(AsyncResult r) {
712 // Signal Info are totally ignored on non-voice-capable devices.
713 if (!PhoneGlobals.sVoiceCapable) {
714 Log.w(LOG_TAG, "Got onSignalInfo() on non-voice-capable device! Ignoring...");
715 return;
716 }
717
718 if (PhoneUtils.isRealIncomingCall(mCM.getFirstActiveRingingCall().getState())) {
719 // Do not start any new SignalInfo tone when Call state is INCOMING
720 // and stop any previous SignalInfo tone which is being played
721 stopSignalInfoTone();
722 } else {
723 // Extract the SignalInfo String from the message
724 CdmaSignalInfoRec signalInfoRec = (CdmaSignalInfoRec)(r.result);
725 // Only proceed if a Signal info is present.
726 if (signalInfoRec != null) {
727 boolean isPresent = signalInfoRec.isPresent;
728 if (DBG) log("onSignalInfo: isPresent=" + isPresent);
729 if (isPresent) {// if tone is valid
730 int uSignalType = signalInfoRec.signalType;
731 int uAlertPitch = signalInfoRec.alertPitch;
732 int uSignal = signalInfoRec.signal;
733
734 if (DBG) log("onSignalInfo: uSignalType=" + uSignalType + ", uAlertPitch=" +
735 uAlertPitch + ", uSignal=" + uSignal);
736 //Map the Signal to a ToneGenerator ToneID only if Signal info is present
737 int toneID = SignalToneUtil.getAudioToneFromSignalInfo
738 (uSignalType, uAlertPitch, uSignal);
739
740 //Create the SignalInfo tone player and pass the ToneID
741 new SignalInfoTonePlayer(toneID).start();
742 }
743 }
744 }
745 }
746
747 /**
748 * Stops a SignalInfo tone in the following condition
749 * 1 - On receiving a New Ringing Call
750 * 2 - On disconnecting a call
751 * 3 - On answering a Call Waiting Call
752 */
753 /* package */ void stopSignalInfoTone() {
754 if (DBG) log("stopSignalInfoTone: Stopping SignalInfo tone player");
755 new SignalInfoTonePlayer(ToneGenerator.TONE_CDMA_SIGNAL_OFF).start();
756 }
757
Santos Cordon5c046722014-09-18 15:41:13 -0700758 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800759 new BluetoothProfile.ServiceListener() {
760 public void onServiceConnected(int profile, BluetoothProfile proxy) {
761 mBluetoothHeadset = (BluetoothHeadset) proxy;
762 if (VDBG) log("- Got BluetoothHeadset: " + mBluetoothHeadset);
763 }
764
765 public void onServiceDisconnected(int profile) {
766 mBluetoothHeadset = null;
767 }
768 };
769
Zoey Chen5719bac2021-01-28 15:05:49 +0800770 private class CallNotifierPhoneStateListener extends PhoneStateListener implements
771 PhoneStateListener.MessageWaitingIndicatorChangedListener,
772 PhoneStateListener.CallForwardingIndicatorChangedListener {
773
Meng Wang7f34c9c2020-01-14 14:57:57 -0800774 private final int mSubId;
775
776 CallNotifierPhoneStateListener(int subId) {
chen xu06946472019-03-20 14:55:59 -0700777 super();
Meng Wang7f34c9c2020-01-14 14:57:57 -0800778 this.mSubId = subId;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700779 }
780
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800781 @Override
782 public void onMessageWaitingIndicatorChanged(boolean visible) {
783 if (VDBG) log("onMessageWaitingIndicatorChanged(): " + this.mSubId + " " + visible);
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900784 mMWIStatus.put(this.mSubId, visible);
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530785 updatePhoneStateListeners(false, UPDATE_TYPE_MWI, this.mSubId);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800786 }
787
788 @Override
789 public void onCallForwardingIndicatorChanged(boolean visible) {
Tyler Gunn17bffd02017-09-19 11:40:12 -0700790 Log.i(LOG_TAG, "onCallForwardingIndicatorChanged(): subId=" + this.mSubId
791 + ", visible=" + (visible ? "Y" : "N"));
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900792 mCFIStatus.put(this.mSubId, visible);
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530793 updatePhoneStateListeners(false, UPDATE_TYPE_CFI, this.mSubId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700794 }
Zoey Chen5719bac2021-01-28 15:05:49 +0800795 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700796
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700797 private void log(String msg) {
798 Log.d(LOG_TAG, msg);
799 }
800}