blob: 14db9303862cef590886a6e6ecc75af5590f2dba [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;
Andrew Lee2fcb6c32014-12-04 14:52:35 -080032import android.telephony.SubscriptionInfo;
33import android.telephony.SubscriptionManager;
34import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
Zoey Chen5ec326b2021-02-22 21:21:28 +080035import android.telephony.TelephonyCallback;
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;
Jack Yu285100e2022-12-02 22:48:35 -080043import com.android.internal.telephony.PhoneFactory;
Aravind Sreekumarafc08c52018-04-10 15:34:32 -070044import com.android.internal.telephony.SubscriptionController;
45import com.android.internal.telephony.cdma.CdmaInformationRecords.CdmaDisplayInfoRec;
46import com.android.internal.telephony.cdma.CdmaInformationRecords.CdmaSignalInfoRec;
47import com.android.internal.telephony.cdma.SignalToneUtil;
Jack Yu285100e2022-12-02 22:48:35 -080048import com.android.internal.telephony.subscription.SubscriptionManagerService;
Aravind Sreekumarafc08c52018-04-10 15:34:32 -070049
Kazuya Ohshiro263737d2017-10-06 19:42:03 +090050import java.util.ArrayList;
51import java.util.Collections;
52import java.util.Comparator;
Andrew Lee2fcb6c32014-12-04 14:52:35 -080053import java.util.List;
54import java.util.Map;
55
Santos Cordon7d4ddf62013-07-10 11:58:08 -070056/**
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
71 /** The singleton instance. */
72 private static CallNotifier sInstance;
73
Zoey Chen5ec326b2021-02-22 21:21:28 +080074 private Map<Integer, CallNotifierTelephonyCallback> mTelephonyCallback =
75 new ArrayMap<Integer, CallNotifierTelephonyCallback>();
Kazuya Ohshiro263737d2017-10-06 19:42:03 +090076 private Map<Integer, Boolean> mCFIStatus = new ArrayMap<Integer, Boolean>();
77 private Map<Integer, Boolean> mMWIStatus = new ArrayMap<Integer, Boolean>();
Santos Cordon7d4ddf62013-07-10 11:58:08 -070078 private PhoneGlobals mApplication;
79 private CallManager mCM;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070080 private BluetoothHeadset mBluetoothHeadset;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070081
82 // ToneGenerator instance for playing SignalInfo tones
83 private ToneGenerator mSignalInfoToneGenerator;
84
85 // The tone volume relative to other sounds in the stream SignalInfo
86 private static final int TONE_RELATIVE_VOLUME_SIGNALINFO = 80;
87
Santos Cordon7d4ddf62013-07-10 11:58:08 -070088 private boolean mVoicePrivacyState = false;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070089
Santos Cordon7d4ddf62013-07-10 11:58:08 -070090 // Cached AudioManager
91 private AudioManager mAudioManager;
Andrew Lee2fcb6c32014-12-04 14:52:35 -080092 private SubscriptionManager mSubscriptionManager;
93 private TelephonyManager mTelephonyManager;
Santos Cordon27a3c1f2013-08-06 07:49:27 -070094
Brad Ebingera9c6b6d2016-01-07 17:24:16 -080095 // Events from the Phone object:
96 public static final int PHONE_DISCONNECT = 3;
97 public static final int PHONE_STATE_DISPLAYINFO = 6;
98 public static final int PHONE_STATE_SIGNALINFO = 7;
99 public static final int PHONE_ENHANCED_VP_ON = 9;
100 public static final int PHONE_ENHANCED_VP_OFF = 10;
101 public static final int PHONE_SUPP_SERVICE_FAILED = 14;
102 public static final int PHONE_TTY_MODE_RECEIVED = 15;
103 // Events generated internally.
104 // We should store all the possible event type values in one place to make sure that
105 // they don't step on each others' toes.
106 public static final int INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE = 22;
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800107
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530108 public static final int UPDATE_TYPE_MWI = 0;
109 public static final int UPDATE_TYPE_CFI = 1;
110 public static final int UPDATE_TYPE_MWI_CFI = 2;
111
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700112 /**
113 * Initialize the singleton CallNotifier instance.
114 * This is only done once, at startup, from PhoneApp.onCreate().
115 */
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800116 /* package */ static CallNotifier init(
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800117 PhoneGlobals app) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700118 synchronized (CallNotifier.class) {
119 if (sInstance == null) {
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800120 sInstance = new CallNotifier(app);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700121 } else {
122 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
123 }
124 return sInstance;
125 }
126 }
127
128 /** Private constructor; @see init() */
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800129 private CallNotifier(
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800130 PhoneGlobals app) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700131 mApplication = app;
132 mCM = app.mCM;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700133
134 mAudioManager = (AudioManager) mApplication.getSystemService(Context.AUDIO_SERVICE);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800135 mTelephonyManager =
136 (TelephonyManager) mApplication.getSystemService(Context.TELEPHONY_SERVICE);
137 mSubscriptionManager = (SubscriptionManager) mApplication.getSystemService(
138 Context.TELEPHONY_SUBSCRIPTION_SERVICE);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700139
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800140 registerForNotifications();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700141
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700142 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
143 if (adapter != null) {
144 adapter.getProfileProxy(mApplication.getApplicationContext(),
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800145 mBluetoothProfileServiceListener,
146 BluetoothProfile.HEADSET);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700147 }
148
Wink Savillef67832f2015-01-12 16:51:50 -0800149 mSubscriptionManager.addOnSubscriptionsChangedListener(
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800150 new OnSubscriptionsChangedListener() {
151 @Override
152 public void onSubscriptionsChanged() {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900153 updatePhoneStateListeners(true);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800154 }
155 });
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700156 }
157
158 private void createSignalInfoToneGenerator() {
159 // Instantiate the ToneGenerator for SignalInfo and CallWaiting
160 // TODO: We probably don't need the mSignalInfoToneGenerator instance
161 // around forever. Need to change it so as to create a ToneGenerator instance only
162 // when a tone is being played and releases it after its done playing.
163 if (mSignalInfoToneGenerator == null) {
164 try {
165 mSignalInfoToneGenerator = new ToneGenerator(AudioManager.STREAM_VOICE_CALL,
166 TONE_RELATIVE_VOLUME_SIGNALINFO);
167 Log.d(LOG_TAG, "CallNotifier: mSignalInfoToneGenerator created when toneplay");
168 } catch (RuntimeException e) {
169 Log.w(LOG_TAG, "CallNotifier: Exception caught while creating " +
170 "mSignalInfoToneGenerator: " + e);
171 mSignalInfoToneGenerator = null;
172 }
173 } else {
174 Log.d(LOG_TAG, "mSignalInfoToneGenerator created already, hence skipping");
175 }
176 }
177
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800178 /**
179 * Register for call state notifications with the CallManager.
180 */
181 private void registerForNotifications() {
182 mCM.registerForDisconnect(this, PHONE_DISCONNECT, null);
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800183 mCM.registerForDisplayInfo(this, PHONE_STATE_DISPLAYINFO, null);
184 mCM.registerForSignalInfo(this, PHONE_STATE_SIGNALINFO, null);
185 mCM.registerForInCallVoicePrivacyOn(this, PHONE_ENHANCED_VP_ON, null);
186 mCM.registerForInCallVoicePrivacyOff(this, PHONE_ENHANCED_VP_OFF, null);
187 mCM.registerForSuppServiceFailed(this, PHONE_SUPP_SERVICE_FAILED, null);
188 mCM.registerForTtyModeReceived(this, PHONE_TTY_MODE_RECEIVED, null);
189 }
190
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700191 @Override
192 public void handleMessage(Message msg) {
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800193 if (DBG) {
194 Log.d(LOG_TAG, "handleMessage(" + msg.what + ")");
195 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700196 switch (msg.what) {
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800197 case PHONE_DISCONNECT:
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700198 if (DBG) log("DISCONNECT");
Roshan Pius19f39cf2015-08-12 10:44:38 -0700199 // Stop any signalInfo tone being played when a call gets ended, the rest of the
200 // disconnect functionality in onDisconnect() is handled in ConnectionService.
201 stopSignalInfoTone();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700202 break;
203
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800204 case PHONE_STATE_DISPLAYINFO:
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700205 if (DBG) log("Received PHONE_STATE_DISPLAYINFO event");
206 onDisplayInfo((AsyncResult) msg.obj);
207 break;
208
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800209 case PHONE_STATE_SIGNALINFO:
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700210 if (DBG) log("Received PHONE_STATE_SIGNALINFO event");
211 onSignalInfo((AsyncResult) msg.obj);
212 break;
213
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800214 case INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE:
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700215 if (DBG) log("Received Display Info notification done event ...");
Anthony Leee9468532014-11-15 15:21:00 -0800216 PhoneDisplayMessage.dismissMessage();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700217 break;
218
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800219 case PHONE_ENHANCED_VP_ON:
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700220 if (DBG) log("PHONE_ENHANCED_VP_ON...");
221 if (!mVoicePrivacyState) {
222 int toneToPlay = InCallTonePlayer.TONE_VOICE_PRIVACY;
223 new InCallTonePlayer(toneToPlay).start();
224 mVoicePrivacyState = true;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700225 }
226 break;
227
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800228 case PHONE_ENHANCED_VP_OFF:
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700229 if (DBG) log("PHONE_ENHANCED_VP_OFF...");
230 if (mVoicePrivacyState) {
231 int toneToPlay = InCallTonePlayer.TONE_VOICE_PRIVACY;
232 new InCallTonePlayer(toneToPlay).start();
233 mVoicePrivacyState = false;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700234 }
235 break;
236
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800237 case PHONE_SUPP_SERVICE_FAILED:
Anthony Leee9468532014-11-15 15:21:00 -0800238 if (DBG) log("PHONE_SUPP_SERVICE_FAILED...");
239 onSuppServiceFailed((AsyncResult) msg.obj);
240 break;
241
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800242 case PHONE_TTY_MODE_RECEIVED:
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800243 if (DBG) log("Received PHONE_TTY_MODE_RECEIVED event");
244 onTtyModeReceived((AsyncResult) msg.obj);
245 break;
246
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700247 default:
248 // super.handleMessage(msg);
249 }
250 }
251
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700252 void updateCallNotifierRegistrationsAfterRadioTechnologyChange() {
253 if (DBG) Log.d(LOG_TAG, "updateCallNotifierRegistrationsAfterRadioTechnologyChange...");
254
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700255 // Instantiate mSignalInfoToneGenerator
256 createSignalInfoToneGenerator();
257 }
258
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700259 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700260 * Helper class to play tones through the earpiece (or speaker / BT)
261 * during a call, using the ToneGenerator.
262 *
263 * To use, just instantiate a new InCallTonePlayer
264 * (passing in the TONE_* constant for the tone you want)
265 * and start() it.
266 *
267 * When we're done playing the tone, if the phone is idle at that
268 * point, we'll reset the audio routing and speaker state.
269 * (That means that for tones that get played *after* a call
270 * disconnects, like "busy" or "congestion" or "call ended", you
271 * should NOT call resetAudioStateAfterDisconnect() yourself.
272 * Instead, just start the InCallTonePlayer, which will automatically
273 * defer the resetAudioStateAfterDisconnect() call until the tone
274 * finishes playing.)
275 */
276 private class InCallTonePlayer extends Thread {
277 private int mToneId;
278 private int mState;
279 // The possible tones we can play.
280 public static final int TONE_NONE = 0;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700281 public static final int TONE_VOICE_PRIVACY = 5;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700282
283 // The tone volume relative to other sounds in the stream
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700284 static final int TONE_RELATIVE_VOLUME_HIPRI = 80;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700285
286 // Buffer time (in msec) to add on to tone timeout value.
287 // Needed mainly when the timeout value for a tone is the
288 // exact duration of the tone itself.
289 static final int TONE_TIMEOUT_BUFFER = 20;
290
291 // The tone state
292 static final int TONE_OFF = 0;
293 static final int TONE_ON = 1;
294 static final int TONE_STOPPED = 2;
295
296 InCallTonePlayer(int toneId) {
297 super();
298 mToneId = toneId;
299 mState = TONE_OFF;
300 }
301
302 @Override
303 public void run() {
304 log("InCallTonePlayer.run(toneId = " + mToneId + ")...");
305
306 int toneType = 0; // passed to ToneGenerator.startTone()
307 int toneVolume; // passed to the ToneGenerator constructor
308 int toneLengthMillis;
309 int phoneType = mCM.getFgPhone().getPhoneType();
310
311 switch (mToneId) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700312 case TONE_VOICE_PRIVACY:
313 toneType = ToneGenerator.TONE_CDMA_ALERT_NETWORK_LITE;
314 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
315 toneLengthMillis = 5000;
316 break;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700317 default:
318 throw new IllegalArgumentException("Bad toneId: " + mToneId);
319 }
320
321 // If the mToneGenerator creation fails, just continue without it. It is
322 // a local audio signal, and is not as important.
323 ToneGenerator toneGenerator;
324 try {
325 int stream;
326 if (mBluetoothHeadset != null) {
Amit Mahajanb8f13202020-01-27 18:16:07 -0800327 stream = isBluetoothAudioOn() ? AudioSystem.STREAM_BLUETOOTH_SCO :
328 AudioSystem.STREAM_VOICE_CALL;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700329 } else {
Amit Mahajanb8f13202020-01-27 18:16:07 -0800330 stream = AudioSystem.STREAM_VOICE_CALL;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700331 }
332 toneGenerator = new ToneGenerator(stream, toneVolume);
333 // if (DBG) log("- created toneGenerator: " + toneGenerator);
334 } catch (RuntimeException e) {
335 Log.w(LOG_TAG,
336 "InCallTonePlayer: Exception caught while creating ToneGenerator: " + e);
337 toneGenerator = null;
338 }
339
340 // Using the ToneGenerator (with the CALL_WAITING / BUSY /
341 // CONGESTION tones at least), the ToneGenerator itself knows
342 // the right pattern of tones to play; we do NOT need to
343 // manually start/stop each individual tone, or manually
344 // insert the correct delay between tones. (We just start it
345 // and let it run for however long we want the tone pattern to
346 // continue.)
347 //
348 // TODO: When we stop the ToneGenerator in the middle of a
349 // "tone pattern", it sounds bad if we cut if off while the
350 // tone is actually playing. Consider adding API to the
351 // ToneGenerator to say "stop at the next silent part of the
352 // pattern", or simply "play the pattern N times and then
353 // stop."
354 boolean needToStopTone = true;
355 boolean okToPlayTone = false;
356
357 if (toneGenerator != null) {
358 int ringerMode = mAudioManager.getRingerMode();
359 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
360 if (toneType == ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD) {
361 if ((ringerMode != AudioManager.RINGER_MODE_SILENT) &&
362 (ringerMode != AudioManager.RINGER_MODE_VIBRATE)) {
363 if (DBG) log("- InCallTonePlayer: start playing call tone=" + toneType);
364 okToPlayTone = true;
365 needToStopTone = false;
366 }
367 } else if ((toneType == ToneGenerator.TONE_CDMA_NETWORK_BUSY_ONE_SHOT) ||
368 (toneType == ToneGenerator.TONE_CDMA_REORDER) ||
369 (toneType == ToneGenerator.TONE_CDMA_ABBR_REORDER) ||
370 (toneType == ToneGenerator.TONE_CDMA_ABBR_INTERCEPT) ||
371 (toneType == ToneGenerator.TONE_CDMA_CALLDROP_LITE)) {
372 if (ringerMode != AudioManager.RINGER_MODE_SILENT) {
373 if (DBG) log("InCallTonePlayer:playing call fail tone:" + toneType);
374 okToPlayTone = true;
375 needToStopTone = false;
376 }
377 } else if ((toneType == ToneGenerator.TONE_CDMA_ALERT_AUTOREDIAL_LITE) ||
378 (toneType == ToneGenerator.TONE_CDMA_ALERT_NETWORK_LITE)) {
379 if ((ringerMode != AudioManager.RINGER_MODE_SILENT) &&
380 (ringerMode != AudioManager.RINGER_MODE_VIBRATE)) {
381 if (DBG) log("InCallTonePlayer:playing tone for toneType=" + toneType);
382 okToPlayTone = true;
383 needToStopTone = false;
384 }
385 } else { // For the rest of the tones, always OK to play.
386 okToPlayTone = true;
387 }
388 } else { // Not "CDMA"
389 okToPlayTone = true;
390 }
391
392 synchronized (this) {
393 if (okToPlayTone && mState != TONE_STOPPED) {
394 mState = TONE_ON;
395 toneGenerator.startTone(toneType);
396 try {
397 wait(toneLengthMillis + TONE_TIMEOUT_BUFFER);
398 } catch (InterruptedException e) {
399 Log.w(LOG_TAG,
400 "InCallTonePlayer stopped: " + e);
401 }
402 if (needToStopTone) {
403 toneGenerator.stopTone();
404 }
405 }
406 // if (DBG) log("- InCallTonePlayer: done playing.");
407 toneGenerator.release();
408 mState = TONE_OFF;
409 }
410 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700411 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700412 }
413
Jordan Liu1c85e772019-08-06 16:06:19 -0700414 // Returns whether there are any connected Bluetooth audio devices
415 private boolean isBluetoothAudioOn() {
416 return mBluetoothHeadset.getConnectedDevices().size() > 0;
417 }
418
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700419 /**
420 * Displays a notification when the phone receives a DisplayInfo record.
421 */
422 private void onDisplayInfo(AsyncResult r) {
423 // Extract the DisplayInfo String from the message
424 CdmaDisplayInfoRec displayInfoRec = (CdmaDisplayInfoRec)(r.result);
425
426 if (displayInfoRec != null) {
427 String displayInfo = displayInfoRec.alpha;
428 if (DBG) log("onDisplayInfo: displayInfo=" + displayInfo);
Anthony Leee9468532014-11-15 15:21:00 -0800429 PhoneDisplayMessage.displayNetworkMessage(mApplication, displayInfo);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700430
Anthony Leee9468532014-11-15 15:21:00 -0800431 // start a timer that kills the dialog
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800432 sendEmptyMessageDelayed(INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE,
Anthony Leee9468532014-11-15 15:21:00 -0800433 SHOW_MESSAGE_NOTIFICATION_TIME);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700434 }
435 }
436
437 /**
Anthony Leee9468532014-11-15 15:21:00 -0800438 * Displays a notification when the phone receives a notice that a supplemental
439 * service has failed.
Anthony Leee9468532014-11-15 15:21:00 -0800440 */
441 private void onSuppServiceFailed(AsyncResult r) {
Tyler Gunn9dd07d02014-12-08 10:52:57 -0800442 String mergeFailedString = "";
443 if (r.result == Phone.SuppService.CONFERENCE) {
444 if (DBG) log("onSuppServiceFailed: displaying merge failure message");
445 mergeFailedString = mApplication.getResources().getString(
446 R.string.incall_error_supp_service_conference);
447 } else if (r.result == Phone.SuppService.RESUME) {
Tyler Gunn93f9ff52018-04-11 13:53:04 -0700448 if (DBG) log("onSuppServiceFailed: displaying resume failure message");
Tyler Gunn9dd07d02014-12-08 10:52:57 -0800449 mergeFailedString = mApplication.getResources().getString(
Tyler Gunn93f9ff52018-04-11 13:53:04 -0700450 R.string.incall_error_supp_service_resume);
Anju Mathapati4effeb22016-01-25 22:25:09 -0800451 } else if (r.result == Phone.SuppService.HOLD) {
Tyler Gunna7de7d32017-06-09 16:21:00 -0700452 if (DBG) log("onSuppServiceFailed: displaying hold failure message");
Anju Mathapati4effeb22016-01-25 22:25:09 -0800453 mergeFailedString = mApplication.getResources().getString(
454 R.string.incall_error_supp_service_hold);
Tyler Gunna7de7d32017-06-09 16:21:00 -0700455 } else if (r.result == Phone.SuppService.TRANSFER) {
456 if (DBG) log("onSuppServiceFailed: displaying transfer failure message");
457 mergeFailedString = mApplication.getResources().getString(
458 R.string.incall_error_supp_service_transfer);
459 } else if (r.result == Phone.SuppService.SEPARATE) {
460 if (DBG) log("onSuppServiceFailed: displaying separate failure message");
461 mergeFailedString = mApplication.getResources().getString(
462 R.string.incall_error_supp_service_separate);
463 } else if (r.result == Phone.SuppService.SWITCH) {
464 if (DBG) log("onSuppServiceFailed: displaying switch failure message");
Shashidhar Vithalrao Kulkarnia557d622018-05-10 13:41:42 +0530465 mergeFailedString = mApplication.getResources().getString(
Tyler Gunna7de7d32017-06-09 16:21:00 -0700466 R.string.incall_error_supp_service_switch);
467 } else if (r.result == Phone.SuppService.REJECT) {
468 if (DBG) log("onSuppServiceFailed: displaying reject failure message");
Shashidhar Vithalrao Kulkarnia557d622018-05-10 13:41:42 +0530469 mergeFailedString = mApplication.getResources().getString(
Tyler Gunna7de7d32017-06-09 16:21:00 -0700470 R.string.incall_error_supp_service_reject);
Shashidhar Vithalrao Kulkarnia557d622018-05-10 13:41:42 +0530471 } else if (r.result == Phone.SuppService.HANGUP) {
472 mergeFailedString = mApplication.getResources().getString(
473 R.string.incall_error_supp_service_hangup);
474 } else {
Tyler Gunna7de7d32017-06-09 16:21:00 -0700475 if (DBG) log("onSuppServiceFailed: unknown failure");
476 return;
Tyler Gunn9dd07d02014-12-08 10:52:57 -0800477 }
Tyler Gunna7de7d32017-06-09 16:21:00 -0700478
Anthony Leee9468532014-11-15 15:21:00 -0800479 PhoneDisplayMessage.displayErrorMessage(mApplication, mergeFailedString);
480
481 // start a timer that kills the dialog
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800482 sendEmptyMessageDelayed(INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE,
Anthony Leee9468532014-11-15 15:21:00 -0800483 SHOW_MESSAGE_NOTIFICATION_TIME);
484 }
485
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900486 public void updatePhoneStateListeners(boolean isRefresh) {
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530487 updatePhoneStateListeners(isRefresh, UPDATE_TYPE_MWI_CFI,
488 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
489 }
490
491 public void updatePhoneStateListeners(boolean isRefresh, int updateType, int subIdToUpdate) {
Jack Yu285100e2022-12-02 22:48:35 -0800492 List<SubscriptionInfo> subInfos;
493 if (PhoneFactory.isSubscriptionManagerServiceEnabled()) {
494 subInfos = SubscriptionManagerService.getInstance()
495 .getActiveSubscriptionInfoList(mApplication.getOpPackageName(),
496 mApplication.getAttributionTag());
497 } else {
498 subInfos = SubscriptionController.getInstance()
499 .getActiveSubscriptionInfoList(mApplication.getOpPackageName(),
500 mApplication.getAttributionTag());
501 }
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800502
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900503 // Sort sub id list based on slot id, so that CFI/MWI notifications will be updated for
504 // slot 0 first then slot 1. This is needed to ensure that when CFI or MWI is enabled for
505 // both slots, user always sees icon related to slot 0 on left side followed by that of
506 // slot 1.
Zoey Chen5ec326b2021-02-22 21:21:28 +0800507 List<Integer> subIdList = new ArrayList<Integer>(mTelephonyCallback.keySet());
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900508 Collections.sort(subIdList, new Comparator<Integer>() {
509 public int compare(Integer sub1, Integer sub2) {
Jack Yu285100e2022-12-02 22:48:35 -0800510 int slotId1 = SubscriptionManager.getSlotIndex(sub1);
511 int slotId2 = SubscriptionManager.getSlotIndex(sub2);
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900512 return slotId1 > slotId2 ? 0 : -1;
513 }
514 });
515
516 for (int subIdCounter = (subIdList.size() - 1); subIdCounter >= 0; subIdCounter--) {
517 int subId = subIdList.get(subIdCounter);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800518 if (subInfos == null || !containsSubId(subInfos, subId)) {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900519 Log.d(LOG_TAG, "updatePhoneStateListeners: Hide the outstanding notifications.");
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800520 // Hide the outstanding notifications.
521 mApplication.notificationMgr.updateMwi(subId, false);
522 mApplication.notificationMgr.updateCfi(subId, false);
523
Zoey Chen5719bac2021-01-28 15:05:49 +0800524 // Unregister the listener.
Zoey Chen5ec326b2021-02-22 21:21:28 +0800525 mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback.get(subId));
526 mTelephonyCallback.remove(subId);
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900527 } else {
528 Log.d(LOG_TAG, "updatePhoneStateListeners: update CF notifications.");
529
530 if (mCFIStatus.containsKey(subId)) {
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530531 if ((updateType == UPDATE_TYPE_CFI) && (subId == subIdToUpdate)) {
532 mApplication.notificationMgr.updateCfi(subId, mCFIStatus.get(subId),
533 isRefresh);
534 } else {
535 mApplication.notificationMgr.updateCfi(subId, mCFIStatus.get(subId), true);
536 }
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900537 }
538 if (mMWIStatus.containsKey(subId)) {
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530539 if ((updateType == UPDATE_TYPE_MWI) && (subId == subIdToUpdate)) {
540 mApplication.notificationMgr.updateMwi(subId, mMWIStatus.get(subId),
541 isRefresh);
542 } else {
543 mApplication.notificationMgr.updateMwi(subId, mMWIStatus.get(subId), true);
544 }
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900545 }
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800546 }
547 }
548
549 if (subInfos == null) {
550 return;
551 }
552
553 // Register new phone listeners for active subscriptions.
554 for (int i = 0; i < subInfos.size(); i++) {
555 int subId = subInfos.get(i).getSubscriptionId();
Zoey Chen5ec326b2021-02-22 21:21:28 +0800556 if (!mTelephonyCallback.containsKey(subId)) {
557 CallNotifierTelephonyCallback listener = new CallNotifierTelephonyCallback(subId);
558 mTelephonyManager.createForSubscriptionId(subId).registerTelephonyCallback(
Zoey Chen5719bac2021-01-28 15:05:49 +0800559 new HandlerExecutor(this), listener);
Zoey Chen5ec326b2021-02-22 21:21:28 +0800560 mTelephonyCallback.put(subId, listener);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800561 }
562 }
563 }
564
565 /**
566 * @return {@code true} if the list contains SubscriptionInfo with the given subscription id.
567 */
568 private boolean containsSubId(List<SubscriptionInfo> subInfos, int subId) {
569 if (subInfos == null) {
570 return false;
571 }
572
573 for (int i = 0; i < subInfos.size(); i++) {
574 if (subInfos.get(i).getSubscriptionId() == subId) {
575 return true;
576 }
577 }
578 return false;
579 }
580
Anthony Leee9468532014-11-15 15:21:00 -0800581 /**
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800582 * Displays a notification when the phone receives a notice that TTY mode
583 * has changed on remote end.
584 */
585 private void onTtyModeReceived(AsyncResult r) {
586 if (DBG) log("TtyModeReceived: displaying notification message");
587
588 int resId = 0;
589 switch (((Integer)r.result).intValue()) {
590 case TelecomManager.TTY_MODE_FULL:
Daniel Brightebb4eb72020-02-18 15:16:33 -0800591 resId = com.android.internal.R.string.peerTtyModeFull;
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800592 break;
593 case TelecomManager.TTY_MODE_HCO:
Daniel Brightebb4eb72020-02-18 15:16:33 -0800594 resId = com.android.internal.R.string.peerTtyModeHco;
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800595 break;
596 case TelecomManager.TTY_MODE_VCO:
Daniel Brightebb4eb72020-02-18 15:16:33 -0800597 resId = com.android.internal.R.string.peerTtyModeVco;
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800598 break;
599 case TelecomManager.TTY_MODE_OFF:
Daniel Brightebb4eb72020-02-18 15:16:33 -0800600 resId = com.android.internal.R.string.peerTtyModeOff;
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800601 break;
602 default:
603 Log.e(LOG_TAG, "Unsupported TTY mode: " + r.result);
604 break;
605 }
606 if (resId != 0) {
607 PhoneDisplayMessage.displayNetworkMessage(mApplication,
Daniel Brightebb4eb72020-02-18 15:16:33 -0800608 mApplication.getResources().getString(resId));
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800609
610 // start a timer that kills the dialog
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800611 sendEmptyMessageDelayed(INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE,
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800612 SHOW_MESSAGE_NOTIFICATION_TIME);
613 }
614 }
615
616 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700617 * Helper class to play SignalInfo tones using the ToneGenerator.
618 *
619 * To use, just instantiate a new SignalInfoTonePlayer
620 * (passing in the ToneID constant for the tone you want)
621 * and start() it.
622 */
623 private class SignalInfoTonePlayer extends Thread {
624 private int mToneId;
625
626 SignalInfoTonePlayer(int toneId) {
627 super();
628 mToneId = toneId;
629 }
630
631 @Override
632 public void run() {
633 log("SignalInfoTonePlayer.run(toneId = " + mToneId + ")...");
Yorke Lee65cbd162014-10-08 11:26:02 -0700634 createSignalInfoToneGenerator();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700635 if (mSignalInfoToneGenerator != null) {
636 //First stop any ongoing SignalInfo tone
637 mSignalInfoToneGenerator.stopTone();
638
639 //Start playing the new tone if its a valid tone
640 mSignalInfoToneGenerator.startTone(mToneId);
641 }
642 }
643 }
644
645 /**
646 * Plays a tone when the phone receives a SignalInfo record.
647 */
648 private void onSignalInfo(AsyncResult r) {
649 // Signal Info are totally ignored on non-voice-capable devices.
650 if (!PhoneGlobals.sVoiceCapable) {
651 Log.w(LOG_TAG, "Got onSignalInfo() on non-voice-capable device! Ignoring...");
652 return;
653 }
654
655 if (PhoneUtils.isRealIncomingCall(mCM.getFirstActiveRingingCall().getState())) {
656 // Do not start any new SignalInfo tone when Call state is INCOMING
657 // and stop any previous SignalInfo tone which is being played
658 stopSignalInfoTone();
659 } else {
660 // Extract the SignalInfo String from the message
661 CdmaSignalInfoRec signalInfoRec = (CdmaSignalInfoRec)(r.result);
662 // Only proceed if a Signal info is present.
663 if (signalInfoRec != null) {
664 boolean isPresent = signalInfoRec.isPresent;
665 if (DBG) log("onSignalInfo: isPresent=" + isPresent);
666 if (isPresent) {// if tone is valid
667 int uSignalType = signalInfoRec.signalType;
668 int uAlertPitch = signalInfoRec.alertPitch;
669 int uSignal = signalInfoRec.signal;
670
671 if (DBG) log("onSignalInfo: uSignalType=" + uSignalType + ", uAlertPitch=" +
672 uAlertPitch + ", uSignal=" + uSignal);
673 //Map the Signal to a ToneGenerator ToneID only if Signal info is present
674 int toneID = SignalToneUtil.getAudioToneFromSignalInfo
675 (uSignalType, uAlertPitch, uSignal);
676
677 //Create the SignalInfo tone player and pass the ToneID
678 new SignalInfoTonePlayer(toneID).start();
679 }
680 }
681 }
682 }
683
684 /**
685 * Stops a SignalInfo tone in the following condition
686 * 1 - On receiving a New Ringing Call
687 * 2 - On disconnecting a call
688 * 3 - On answering a Call Waiting Call
689 */
690 /* package */ void stopSignalInfoTone() {
691 if (DBG) log("stopSignalInfoTone: Stopping SignalInfo tone player");
692 new SignalInfoTonePlayer(ToneGenerator.TONE_CDMA_SIGNAL_OFF).start();
693 }
694
Santos Cordon5c046722014-09-18 15:41:13 -0700695 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800696 new BluetoothProfile.ServiceListener() {
697 public void onServiceConnected(int profile, BluetoothProfile proxy) {
698 mBluetoothHeadset = (BluetoothHeadset) proxy;
699 if (VDBG) log("- Got BluetoothHeadset: " + mBluetoothHeadset);
700 }
701
702 public void onServiceDisconnected(int profile) {
703 mBluetoothHeadset = null;
704 }
705 };
706
Zoey Chen5ec326b2021-02-22 21:21:28 +0800707 private class CallNotifierTelephonyCallback extends TelephonyCallback implements
708 TelephonyCallback.MessageWaitingIndicatorListener,
709 TelephonyCallback.CallForwardingIndicatorListener {
Zoey Chen5719bac2021-01-28 15:05:49 +0800710
Meng Wang7f34c9c2020-01-14 14:57:57 -0800711 private final int mSubId;
712
Zoey Chen5ec326b2021-02-22 21:21:28 +0800713 CallNotifierTelephonyCallback(int subId) {
chen xu06946472019-03-20 14:55:59 -0700714 super();
Meng Wang7f34c9c2020-01-14 14:57:57 -0800715 this.mSubId = subId;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700716 }
717
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800718 @Override
719 public void onMessageWaitingIndicatorChanged(boolean visible) {
720 if (VDBG) log("onMessageWaitingIndicatorChanged(): " + this.mSubId + " " + visible);
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900721 mMWIStatus.put(this.mSubId, visible);
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530722 updatePhoneStateListeners(false, UPDATE_TYPE_MWI, this.mSubId);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800723 }
724
725 @Override
726 public void onCallForwardingIndicatorChanged(boolean visible) {
Tyler Gunn17bffd02017-09-19 11:40:12 -0700727 Log.i(LOG_TAG, "onCallForwardingIndicatorChanged(): subId=" + this.mSubId
728 + ", visible=" + (visible ? "Y" : "N"));
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900729 mCFIStatus.put(this.mSubId, visible);
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530730 updatePhoneStateListeners(false, UPDATE_TYPE_CFI, this.mSubId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700731 }
Zoey Chen5719bac2021-01-28 15:05:49 +0800732 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700733
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700734 private void log(String msg) {
735 Log.d(LOG_TAG, msg);
736 }
737}