blob: daf3aa23485320098b57252b91b7236fc496cbe2 [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;
Aravind Sreekumarafc08c52018-04-10 15:34:32 -070043import com.android.internal.telephony.cdma.CdmaInformationRecords.CdmaDisplayInfoRec;
44import com.android.internal.telephony.cdma.CdmaInformationRecords.CdmaSignalInfoRec;
45import com.android.internal.telephony.cdma.SignalToneUtil;
Jack Yu285100e2022-12-02 22:48:35 -080046import com.android.internal.telephony.subscription.SubscriptionManagerService;
Aravind Sreekumarafc08c52018-04-10 15:34:32 -070047
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
Zoey Chen5ec326b2021-02-22 21:21:28 +080072 private Map<Integer, CallNotifierTelephonyCallback> mTelephonyCallback =
73 new ArrayMap<Integer, CallNotifierTelephonyCallback>();
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;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700279 public static final int TONE_VOICE_PRIVACY = 5;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700280
281 // The tone volume relative to other sounds in the stream
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700282 static final int TONE_RELATIVE_VOLUME_HIPRI = 80;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700283
284 // Buffer time (in msec) to add on to tone timeout value.
285 // Needed mainly when the timeout value for a tone is the
286 // exact duration of the tone itself.
287 static final int TONE_TIMEOUT_BUFFER = 20;
288
289 // The tone state
290 static final int TONE_OFF = 0;
291 static final int TONE_ON = 1;
292 static final int TONE_STOPPED = 2;
293
294 InCallTonePlayer(int toneId) {
295 super();
296 mToneId = toneId;
297 mState = TONE_OFF;
298 }
299
300 @Override
301 public void run() {
302 log("InCallTonePlayer.run(toneId = " + mToneId + ")...");
303
304 int toneType = 0; // passed to ToneGenerator.startTone()
305 int toneVolume; // passed to the ToneGenerator constructor
306 int toneLengthMillis;
307 int phoneType = mCM.getFgPhone().getPhoneType();
308
309 switch (mToneId) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700310 case TONE_VOICE_PRIVACY:
311 toneType = ToneGenerator.TONE_CDMA_ALERT_NETWORK_LITE;
312 toneVolume = TONE_RELATIVE_VOLUME_HIPRI;
313 toneLengthMillis = 5000;
314 break;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700315 default:
316 throw new IllegalArgumentException("Bad toneId: " + mToneId);
317 }
318
319 // If the mToneGenerator creation fails, just continue without it. It is
320 // a local audio signal, and is not as important.
321 ToneGenerator toneGenerator;
322 try {
323 int stream;
324 if (mBluetoothHeadset != null) {
Amit Mahajanb8f13202020-01-27 18:16:07 -0800325 stream = isBluetoothAudioOn() ? AudioSystem.STREAM_BLUETOOTH_SCO :
326 AudioSystem.STREAM_VOICE_CALL;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700327 } else {
Amit Mahajanb8f13202020-01-27 18:16:07 -0800328 stream = AudioSystem.STREAM_VOICE_CALL;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700329 }
330 toneGenerator = new ToneGenerator(stream, toneVolume);
331 // if (DBG) log("- created toneGenerator: " + toneGenerator);
332 } catch (RuntimeException e) {
333 Log.w(LOG_TAG,
334 "InCallTonePlayer: Exception caught while creating ToneGenerator: " + e);
335 toneGenerator = null;
336 }
337
338 // Using the ToneGenerator (with the CALL_WAITING / BUSY /
339 // CONGESTION tones at least), the ToneGenerator itself knows
340 // the right pattern of tones to play; we do NOT need to
341 // manually start/stop each individual tone, or manually
342 // insert the correct delay between tones. (We just start it
343 // and let it run for however long we want the tone pattern to
344 // continue.)
345 //
346 // TODO: When we stop the ToneGenerator in the middle of a
347 // "tone pattern", it sounds bad if we cut if off while the
348 // tone is actually playing. Consider adding API to the
349 // ToneGenerator to say "stop at the next silent part of the
350 // pattern", or simply "play the pattern N times and then
351 // stop."
352 boolean needToStopTone = true;
353 boolean okToPlayTone = false;
354
355 if (toneGenerator != null) {
356 int ringerMode = mAudioManager.getRingerMode();
357 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
358 if (toneType == ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD) {
359 if ((ringerMode != AudioManager.RINGER_MODE_SILENT) &&
360 (ringerMode != AudioManager.RINGER_MODE_VIBRATE)) {
361 if (DBG) log("- InCallTonePlayer: start playing call tone=" + toneType);
362 okToPlayTone = true;
363 needToStopTone = false;
364 }
365 } else if ((toneType == ToneGenerator.TONE_CDMA_NETWORK_BUSY_ONE_SHOT) ||
366 (toneType == ToneGenerator.TONE_CDMA_REORDER) ||
367 (toneType == ToneGenerator.TONE_CDMA_ABBR_REORDER) ||
368 (toneType == ToneGenerator.TONE_CDMA_ABBR_INTERCEPT) ||
369 (toneType == ToneGenerator.TONE_CDMA_CALLDROP_LITE)) {
370 if (ringerMode != AudioManager.RINGER_MODE_SILENT) {
371 if (DBG) log("InCallTonePlayer:playing call fail tone:" + toneType);
372 okToPlayTone = true;
373 needToStopTone = false;
374 }
375 } else if ((toneType == ToneGenerator.TONE_CDMA_ALERT_AUTOREDIAL_LITE) ||
376 (toneType == ToneGenerator.TONE_CDMA_ALERT_NETWORK_LITE)) {
377 if ((ringerMode != AudioManager.RINGER_MODE_SILENT) &&
378 (ringerMode != AudioManager.RINGER_MODE_VIBRATE)) {
379 if (DBG) log("InCallTonePlayer:playing tone for toneType=" + toneType);
380 okToPlayTone = true;
381 needToStopTone = false;
382 }
383 } else { // For the rest of the tones, always OK to play.
384 okToPlayTone = true;
385 }
386 } else { // Not "CDMA"
387 okToPlayTone = true;
388 }
389
390 synchronized (this) {
391 if (okToPlayTone && mState != TONE_STOPPED) {
392 mState = TONE_ON;
393 toneGenerator.startTone(toneType);
394 try {
395 wait(toneLengthMillis + TONE_TIMEOUT_BUFFER);
396 } catch (InterruptedException e) {
397 Log.w(LOG_TAG,
398 "InCallTonePlayer stopped: " + e);
399 }
400 if (needToStopTone) {
401 toneGenerator.stopTone();
402 }
403 }
404 // if (DBG) log("- InCallTonePlayer: done playing.");
405 toneGenerator.release();
406 mState = TONE_OFF;
407 }
408 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700409 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700410 }
411
Jordan Liu1c85e772019-08-06 16:06:19 -0700412 // Returns whether there are any connected Bluetooth audio devices
413 private boolean isBluetoothAudioOn() {
414 return mBluetoothHeadset.getConnectedDevices().size() > 0;
415 }
416
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700417 /**
418 * Displays a notification when the phone receives a DisplayInfo record.
419 */
420 private void onDisplayInfo(AsyncResult r) {
421 // Extract the DisplayInfo String from the message
422 CdmaDisplayInfoRec displayInfoRec = (CdmaDisplayInfoRec)(r.result);
423
424 if (displayInfoRec != null) {
425 String displayInfo = displayInfoRec.alpha;
426 if (DBG) log("onDisplayInfo: displayInfo=" + displayInfo);
Anthony Leee9468532014-11-15 15:21:00 -0800427 PhoneDisplayMessage.displayNetworkMessage(mApplication, displayInfo);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700428
Anthony Leee9468532014-11-15 15:21:00 -0800429 // start a timer that kills the dialog
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800430 sendEmptyMessageDelayed(INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE,
Anthony Leee9468532014-11-15 15:21:00 -0800431 SHOW_MESSAGE_NOTIFICATION_TIME);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700432 }
433 }
434
435 /**
Anthony Leee9468532014-11-15 15:21:00 -0800436 * Displays a notification when the phone receives a notice that a supplemental
437 * service has failed.
Anthony Leee9468532014-11-15 15:21:00 -0800438 */
439 private void onSuppServiceFailed(AsyncResult r) {
Tyler Gunn9dd07d02014-12-08 10:52:57 -0800440 String mergeFailedString = "";
441 if (r.result == Phone.SuppService.CONFERENCE) {
442 if (DBG) log("onSuppServiceFailed: displaying merge failure message");
443 mergeFailedString = mApplication.getResources().getString(
444 R.string.incall_error_supp_service_conference);
445 } else if (r.result == Phone.SuppService.RESUME) {
Tyler Gunn93f9ff52018-04-11 13:53:04 -0700446 if (DBG) log("onSuppServiceFailed: displaying resume failure message");
Tyler Gunn9dd07d02014-12-08 10:52:57 -0800447 mergeFailedString = mApplication.getResources().getString(
Tyler Gunn93f9ff52018-04-11 13:53:04 -0700448 R.string.incall_error_supp_service_resume);
Anju Mathapati4effeb22016-01-25 22:25:09 -0800449 } else if (r.result == Phone.SuppService.HOLD) {
Tyler Gunna7de7d32017-06-09 16:21:00 -0700450 if (DBG) log("onSuppServiceFailed: displaying hold failure message");
Anju Mathapati4effeb22016-01-25 22:25:09 -0800451 mergeFailedString = mApplication.getResources().getString(
452 R.string.incall_error_supp_service_hold);
Tyler Gunna7de7d32017-06-09 16:21:00 -0700453 } else if (r.result == Phone.SuppService.TRANSFER) {
454 if (DBG) log("onSuppServiceFailed: displaying transfer failure message");
455 mergeFailedString = mApplication.getResources().getString(
456 R.string.incall_error_supp_service_transfer);
457 } else if (r.result == Phone.SuppService.SEPARATE) {
458 if (DBG) log("onSuppServiceFailed: displaying separate failure message");
459 mergeFailedString = mApplication.getResources().getString(
460 R.string.incall_error_supp_service_separate);
461 } else if (r.result == Phone.SuppService.SWITCH) {
462 if (DBG) log("onSuppServiceFailed: displaying switch failure message");
Shashidhar Vithalrao Kulkarnia557d622018-05-10 13:41:42 +0530463 mergeFailedString = mApplication.getResources().getString(
Tyler Gunna7de7d32017-06-09 16:21:00 -0700464 R.string.incall_error_supp_service_switch);
465 } else if (r.result == Phone.SuppService.REJECT) {
466 if (DBG) log("onSuppServiceFailed: displaying reject failure message");
Shashidhar Vithalrao Kulkarnia557d622018-05-10 13:41:42 +0530467 mergeFailedString = mApplication.getResources().getString(
Tyler Gunna7de7d32017-06-09 16:21:00 -0700468 R.string.incall_error_supp_service_reject);
Shashidhar Vithalrao Kulkarnia557d622018-05-10 13:41:42 +0530469 } else if (r.result == Phone.SuppService.HANGUP) {
470 mergeFailedString = mApplication.getResources().getString(
471 R.string.incall_error_supp_service_hangup);
472 } else {
Tyler Gunna7de7d32017-06-09 16:21:00 -0700473 if (DBG) log("onSuppServiceFailed: unknown failure");
474 return;
Tyler Gunn9dd07d02014-12-08 10:52:57 -0800475 }
Tyler Gunna7de7d32017-06-09 16:21:00 -0700476
Anthony Leee9468532014-11-15 15:21:00 -0800477 PhoneDisplayMessage.displayErrorMessage(mApplication, mergeFailedString);
478
479 // start a timer that kills the dialog
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800480 sendEmptyMessageDelayed(INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE,
Anthony Leee9468532014-11-15 15:21:00 -0800481 SHOW_MESSAGE_NOTIFICATION_TIME);
482 }
483
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900484 public void updatePhoneStateListeners(boolean isRefresh) {
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530485 updatePhoneStateListeners(isRefresh, UPDATE_TYPE_MWI_CFI,
486 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
487 }
488
489 public void updatePhoneStateListeners(boolean isRefresh, int updateType, int subIdToUpdate) {
Jack Yu3beaf9d2023-04-14 09:17:27 -0700490 List<SubscriptionInfo> subInfos = SubscriptionManagerService.getInstance()
491 .getActiveSubscriptionInfoList(mApplication.getOpPackageName(),
Ling Ma9fa67412023-11-13 14:13:19 -0800492 mApplication.getAttributionTag(), true/*isForAllProfile*/);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800493
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900494 // Sort sub id list based on slot id, so that CFI/MWI notifications will be updated for
495 // slot 0 first then slot 1. This is needed to ensure that when CFI or MWI is enabled for
496 // both slots, user always sees icon related to slot 0 on left side followed by that of
497 // slot 1.
Zoey Chen5ec326b2021-02-22 21:21:28 +0800498 List<Integer> subIdList = new ArrayList<Integer>(mTelephonyCallback.keySet());
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900499 Collections.sort(subIdList, new Comparator<Integer>() {
500 public int compare(Integer sub1, Integer sub2) {
Jack Yu285100e2022-12-02 22:48:35 -0800501 int slotId1 = SubscriptionManager.getSlotIndex(sub1);
502 int slotId2 = SubscriptionManager.getSlotIndex(sub2);
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900503 return slotId1 > slotId2 ? 0 : -1;
504 }
505 });
506
507 for (int subIdCounter = (subIdList.size() - 1); subIdCounter >= 0; subIdCounter--) {
508 int subId = subIdList.get(subIdCounter);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800509 if (subInfos == null || !containsSubId(subInfos, subId)) {
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900510 Log.d(LOG_TAG, "updatePhoneStateListeners: Hide the outstanding notifications.");
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800511 // Hide the outstanding notifications.
512 mApplication.notificationMgr.updateMwi(subId, false);
513 mApplication.notificationMgr.updateCfi(subId, false);
514
Zoey Chen5719bac2021-01-28 15:05:49 +0800515 // Unregister the listener.
Zoey Chen5ec326b2021-02-22 21:21:28 +0800516 mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback.get(subId));
517 mTelephonyCallback.remove(subId);
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900518 } else {
519 Log.d(LOG_TAG, "updatePhoneStateListeners: update CF notifications.");
520
521 if (mCFIStatus.containsKey(subId)) {
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530522 if ((updateType == UPDATE_TYPE_CFI) && (subId == subIdToUpdate)) {
523 mApplication.notificationMgr.updateCfi(subId, mCFIStatus.get(subId),
524 isRefresh);
525 } else {
526 mApplication.notificationMgr.updateCfi(subId, mCFIStatus.get(subId), true);
527 }
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900528 }
529 if (mMWIStatus.containsKey(subId)) {
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530530 if ((updateType == UPDATE_TYPE_MWI) && (subId == subIdToUpdate)) {
531 mApplication.notificationMgr.updateMwi(subId, mMWIStatus.get(subId),
532 isRefresh);
533 } else {
534 mApplication.notificationMgr.updateMwi(subId, mMWIStatus.get(subId), true);
535 }
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900536 }
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800537 }
538 }
539
540 if (subInfos == null) {
541 return;
542 }
543
544 // Register new phone listeners for active subscriptions.
545 for (int i = 0; i < subInfos.size(); i++) {
546 int subId = subInfos.get(i).getSubscriptionId();
Zoey Chen5ec326b2021-02-22 21:21:28 +0800547 if (!mTelephonyCallback.containsKey(subId)) {
548 CallNotifierTelephonyCallback listener = new CallNotifierTelephonyCallback(subId);
549 mTelephonyManager.createForSubscriptionId(subId).registerTelephonyCallback(
Zoey Chen5719bac2021-01-28 15:05:49 +0800550 new HandlerExecutor(this), listener);
Zoey Chen5ec326b2021-02-22 21:21:28 +0800551 mTelephonyCallback.put(subId, listener);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800552 }
553 }
554 }
555
556 /**
557 * @return {@code true} if the list contains SubscriptionInfo with the given subscription id.
558 */
559 private boolean containsSubId(List<SubscriptionInfo> subInfos, int subId) {
560 if (subInfos == null) {
561 return false;
562 }
563
564 for (int i = 0; i < subInfos.size(); i++) {
565 if (subInfos.get(i).getSubscriptionId() == subId) {
566 return true;
567 }
568 }
569 return false;
570 }
571
Anthony Leee9468532014-11-15 15:21:00 -0800572 /**
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800573 * Displays a notification when the phone receives a notice that TTY mode
574 * has changed on remote end.
575 */
576 private void onTtyModeReceived(AsyncResult r) {
577 if (DBG) log("TtyModeReceived: displaying notification message");
578
579 int resId = 0;
580 switch (((Integer)r.result).intValue()) {
581 case TelecomManager.TTY_MODE_FULL:
Daniel Brightebb4eb72020-02-18 15:16:33 -0800582 resId = com.android.internal.R.string.peerTtyModeFull;
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800583 break;
584 case TelecomManager.TTY_MODE_HCO:
Daniel Brightebb4eb72020-02-18 15:16:33 -0800585 resId = com.android.internal.R.string.peerTtyModeHco;
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800586 break;
587 case TelecomManager.TTY_MODE_VCO:
Daniel Brightebb4eb72020-02-18 15:16:33 -0800588 resId = com.android.internal.R.string.peerTtyModeVco;
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800589 break;
590 case TelecomManager.TTY_MODE_OFF:
Daniel Brightebb4eb72020-02-18 15:16:33 -0800591 resId = com.android.internal.R.string.peerTtyModeOff;
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800592 break;
593 default:
594 Log.e(LOG_TAG, "Unsupported TTY mode: " + r.result);
595 break;
596 }
597 if (resId != 0) {
598 PhoneDisplayMessage.displayNetworkMessage(mApplication,
Daniel Brightebb4eb72020-02-18 15:16:33 -0800599 mApplication.getResources().getString(resId));
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800600
601 // start a timer that kills the dialog
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800602 sendEmptyMessageDelayed(INTERNAL_SHOW_MESSAGE_NOTIFICATION_DONE,
Pavel Zhamaitsiak82256c02014-12-10 17:11:40 -0800603 SHOW_MESSAGE_NOTIFICATION_TIME);
604 }
605 }
606
607 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700608 * Helper class to play SignalInfo tones using the ToneGenerator.
609 *
610 * To use, just instantiate a new SignalInfoTonePlayer
611 * (passing in the ToneID constant for the tone you want)
612 * and start() it.
613 */
614 private class SignalInfoTonePlayer extends Thread {
615 private int mToneId;
616
617 SignalInfoTonePlayer(int toneId) {
618 super();
619 mToneId = toneId;
620 }
621
622 @Override
623 public void run() {
624 log("SignalInfoTonePlayer.run(toneId = " + mToneId + ")...");
Yorke Lee65cbd162014-10-08 11:26:02 -0700625 createSignalInfoToneGenerator();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700626 if (mSignalInfoToneGenerator != null) {
627 //First stop any ongoing SignalInfo tone
628 mSignalInfoToneGenerator.stopTone();
629
630 //Start playing the new tone if its a valid tone
631 mSignalInfoToneGenerator.startTone(mToneId);
632 }
633 }
634 }
635
636 /**
637 * Plays a tone when the phone receives a SignalInfo record.
638 */
639 private void onSignalInfo(AsyncResult r) {
640 // Signal Info are totally ignored on non-voice-capable devices.
641 if (!PhoneGlobals.sVoiceCapable) {
642 Log.w(LOG_TAG, "Got onSignalInfo() on non-voice-capable device! Ignoring...");
643 return;
644 }
645
646 if (PhoneUtils.isRealIncomingCall(mCM.getFirstActiveRingingCall().getState())) {
647 // Do not start any new SignalInfo tone when Call state is INCOMING
648 // and stop any previous SignalInfo tone which is being played
649 stopSignalInfoTone();
650 } else {
651 // Extract the SignalInfo String from the message
652 CdmaSignalInfoRec signalInfoRec = (CdmaSignalInfoRec)(r.result);
653 // Only proceed if a Signal info is present.
654 if (signalInfoRec != null) {
655 boolean isPresent = signalInfoRec.isPresent;
656 if (DBG) log("onSignalInfo: isPresent=" + isPresent);
657 if (isPresent) {// if tone is valid
658 int uSignalType = signalInfoRec.signalType;
659 int uAlertPitch = signalInfoRec.alertPitch;
660 int uSignal = signalInfoRec.signal;
661
662 if (DBG) log("onSignalInfo: uSignalType=" + uSignalType + ", uAlertPitch=" +
663 uAlertPitch + ", uSignal=" + uSignal);
664 //Map the Signal to a ToneGenerator ToneID only if Signal info is present
665 int toneID = SignalToneUtil.getAudioToneFromSignalInfo
666 (uSignalType, uAlertPitch, uSignal);
667
668 //Create the SignalInfo tone player and pass the ToneID
669 new SignalInfoTonePlayer(toneID).start();
670 }
671 }
672 }
673 }
674
675 /**
676 * Stops a SignalInfo tone in the following condition
677 * 1 - On receiving a New Ringing Call
678 * 2 - On disconnecting a call
679 * 3 - On answering a Call Waiting Call
680 */
681 /* package */ void stopSignalInfoTone() {
682 if (DBG) log("stopSignalInfoTone: Stopping SignalInfo tone player");
683 new SignalInfoTonePlayer(ToneGenerator.TONE_CDMA_SIGNAL_OFF).start();
684 }
685
Santos Cordon5c046722014-09-18 15:41:13 -0700686 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800687 new BluetoothProfile.ServiceListener() {
688 public void onServiceConnected(int profile, BluetoothProfile proxy) {
689 mBluetoothHeadset = (BluetoothHeadset) proxy;
690 if (VDBG) log("- Got BluetoothHeadset: " + mBluetoothHeadset);
691 }
692
693 public void onServiceDisconnected(int profile) {
694 mBluetoothHeadset = null;
695 }
696 };
697
Zoey Chen5ec326b2021-02-22 21:21:28 +0800698 private class CallNotifierTelephonyCallback extends TelephonyCallback implements
699 TelephonyCallback.MessageWaitingIndicatorListener,
700 TelephonyCallback.CallForwardingIndicatorListener {
Zoey Chen5719bac2021-01-28 15:05:49 +0800701
Meng Wang7f34c9c2020-01-14 14:57:57 -0800702 private final int mSubId;
703
Zoey Chen5ec326b2021-02-22 21:21:28 +0800704 CallNotifierTelephonyCallback(int subId) {
chen xu06946472019-03-20 14:55:59 -0700705 super();
Meng Wang7f34c9c2020-01-14 14:57:57 -0800706 this.mSubId = subId;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700707 }
708
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800709 @Override
710 public void onMessageWaitingIndicatorChanged(boolean visible) {
711 if (VDBG) log("onMessageWaitingIndicatorChanged(): " + this.mSubId + " " + visible);
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900712 mMWIStatus.put(this.mSubId, visible);
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530713 updatePhoneStateListeners(false, UPDATE_TYPE_MWI, this.mSubId);
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800714 }
715
716 @Override
717 public void onCallForwardingIndicatorChanged(boolean visible) {
Tyler Gunn17bffd02017-09-19 11:40:12 -0700718 Log.i(LOG_TAG, "onCallForwardingIndicatorChanged(): subId=" + this.mSubId
719 + ", visible=" + (visible ? "Y" : "N"));
Kazuya Ohshiro263737d2017-10-06 19:42:03 +0900720 mCFIStatus.put(this.mSubId, visible);
Srikanth Chintala4baf0b92017-11-14 15:52:47 +0530721 updatePhoneStateListeners(false, UPDATE_TYPE_CFI, this.mSubId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700722 }
Zoey Chen5719bac2021-01-28 15:05:49 +0800723 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700724
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700725 private void log(String msg) {
726 Log.d(LOG_TAG, msg);
727 }
728}