| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 1 | /* | 
|  | 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 |  | 
|  | 17 | package com.android.phone; | 
|  | 18 |  | 
|  | 19 | import android.app.AlertDialog; | 
|  | 20 | import android.app.Dialog; | 
|  | 21 | import android.app.ProgressDialog; | 
| Sailesh Nepal | bfb6832 | 2013-11-07 14:07:41 -0800 | [diff] [blame] | 22 | import android.content.ComponentName; | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 23 | import android.content.Context; | 
|  | 24 | import android.content.DialogInterface; | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 25 | import android.net.Uri; | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 26 | import android.os.Handler; | 
|  | 27 | import android.os.Message; | 
| SongFerngWang | 5ae4f6d | 2018-10-31 22:11:32 +0800 | [diff] [blame] | 28 | import android.os.PersistableBundle; | 
| Tyler Gunn | 4d45d1c | 2014-09-12 22:17:53 -0700 | [diff] [blame] | 29 | import android.telecom.PhoneAccount; | 
| Andrew Lee | d5165b0 | 2014-12-05 15:53:58 -0800 | [diff] [blame] | 30 | import android.telecom.PhoneAccountHandle; | 
| Tyler Gunn | 4d45d1c | 2014-09-12 22:17:53 -0700 | [diff] [blame] | 31 | import android.telecom.VideoProfile; | 
| SongFerngWang | 5ae4f6d | 2018-10-31 22:11:32 +0800 | [diff] [blame] | 32 | import android.telephony.CarrierConfigManager; | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 33 | import android.telephony.PhoneNumberUtils; | 
| Santos Cordon | f69df96 | 2015-02-03 11:15:19 -0800 | [diff] [blame] | 34 | import android.telephony.SubscriptionManager; | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 35 | import android.text.TextUtils; | 
|  | 36 | import android.util.Log; | 
| Andrew Lee | b3ee4dc | 2015-01-23 17:35:00 -0800 | [diff] [blame] | 37 | import android.view.ContextThemeWrapper; | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 38 | import android.view.KeyEvent; | 
|  | 39 | import android.view.LayoutInflater; | 
|  | 40 | import android.view.View; | 
|  | 41 | import android.view.WindowManager; | 
|  | 42 | import android.widget.EditText; | 
|  | 43 | import android.widget.Toast; | 
|  | 44 |  | 
|  | 45 | import com.android.internal.telephony.Call; | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 46 | import com.android.internal.telephony.CallStateException; | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 47 | import com.android.internal.telephony.Connection; | 
| Stuart Scott | dcf40a9 | 2014-12-09 10:45:01 -0800 | [diff] [blame] | 48 | import com.android.internal.telephony.IccCard; | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 49 | import com.android.internal.telephony.MmiCode; | 
|  | 50 | import com.android.internal.telephony.Phone; | 
|  | 51 | import com.android.internal.telephony.PhoneConstants; | 
| Stuart Scott | dcf40a9 | 2014-12-09 10:45:01 -0800 | [diff] [blame] | 52 | import com.android.internal.telephony.PhoneFactory; | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 53 | import com.android.internal.telephony.TelephonyCapabilities; | 
| SongFerngWang | 5ae4f6d | 2018-10-31 22:11:32 +0800 | [diff] [blame] | 54 | import com.android.phone.settings.SuppServicesUiUtil; | 
| Daniel Bright | 9a8791d | 2020-02-11 18:19:36 -0800 | [diff] [blame] | 55 | import com.android.telephony.Rlog; | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 56 |  | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 57 | import java.util.List; | 
|  | 58 |  | 
|  | 59 | /** | 
|  | 60 | * Misc utilities for the Phone app. | 
|  | 61 | */ | 
|  | 62 | public class PhoneUtils { | 
| Brad Ebinger | d3edc22 | 2016-03-23 19:44:38 -0700 | [diff] [blame] | 63 | public static final String EMERGENCY_ACCOUNT_HANDLE_ID = "E"; | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 64 | private static final String LOG_TAG = "PhoneUtils"; | 
|  | 65 | private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2); | 
|  | 66 |  | 
|  | 67 | // Do not check in with VDBG = true, since that may write PII to the system log. | 
|  | 68 | private static final boolean VDBG = false; | 
|  | 69 |  | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 70 | // Return codes from placeCall() | 
| fionaxu | f788514 | 2016-11-27 16:08:39 -0800 | [diff] [blame] | 71 | public static final int CALL_STATUS_DIALED = 0;  // The number was successfully dialed | 
|  | 72 | public static final int CALL_STATUS_DIALED_MMI = 1;  // The specified number was an MMI code | 
|  | 73 | public static final int CALL_STATUS_FAILED = 2;  // The call failed | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 74 |  | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 75 | // USSD string length for MMI operations | 
|  | 76 | static final int MIN_USSD_LEN = 1; | 
|  | 77 | static final int MAX_USSD_LEN = 160; | 
|  | 78 |  | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 79 | /** Define for not a special CNAP string */ | 
|  | 80 | private static final int CNAP_SPECIAL_CASE_NO = -1; | 
|  | 81 |  | 
| Yorke Lee | e0f2b79 | 2014-10-15 12:30:44 -0700 | [diff] [blame] | 82 | /** | 
|  | 83 | * Theme to use for dialogs displayed by utility methods in this class. This is needed | 
|  | 84 | * because these dialogs are displayed using the application context, which does not resolve | 
|  | 85 | * the dialog theme correctly. | 
|  | 86 | */ | 
| Zheng Zhang | d889c77 | 2017-11-02 15:49:37 -0700 | [diff] [blame] | 87 | private static final int THEME = com.android.internal.R.style.Theme_DeviceDefault_Dialog_Alert; | 
| Yorke Lee | e0f2b79 | 2014-10-15 12:30:44 -0700 | [diff] [blame] | 88 |  | 
| Etan Cohen | 37abbab | 2014-03-07 16:57:03 -0800 | [diff] [blame] | 89 | /** USSD information used to aggregate all USSD messages */ | 
| Etan Cohen | 37abbab | 2014-03-07 16:57:03 -0800 | [diff] [blame] | 90 | private static StringBuilder sUssdMsg = new StringBuilder(); | 
|  | 91 |  | 
| Tony Mak | 4c8f3c6 | 2015-12-07 02:30:37 +0000 | [diff] [blame] | 92 | private static final ComponentName PSTN_CONNECTION_SERVICE_COMPONENT = | 
|  | 93 | new ComponentName("com.android.phone", | 
|  | 94 | "com.android.services.telephony.TelephonyConnectionService"); | 
|  | 95 |  | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 96 | /** This class is never instantiated. */ | 
|  | 97 | private PhoneUtils() { | 
|  | 98 | } | 
|  | 99 |  | 
|  | 100 | /** | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 101 | * For a CDMA phone, advance the call state upon making a new | 
|  | 102 | * outgoing call. | 
|  | 103 | * | 
|  | 104 | * <pre> | 
|  | 105 | *   IDLE -> SINGLE_ACTIVE | 
|  | 106 | * or | 
|  | 107 | *   SINGLE_ACTIVE -> THRWAY_ACTIVE | 
|  | 108 | * </pre> | 
|  | 109 | * @param app The phone instance. | 
|  | 110 | */ | 
| Santos Cordon | ad1ed6d | 2013-09-16 03:04:23 -0700 | [diff] [blame] | 111 | private static void updateCdmaCallStateOnNewOutgoingCall(PhoneGlobals app, | 
|  | 112 | Connection connection) { | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 113 | if (app.cdmaPhoneCallState.getCurrentCallState() == | 
|  | 114 | CdmaPhoneCallState.PhoneCallState.IDLE) { | 
|  | 115 | // This is the first outgoing call. Set the Phone Call State to ACTIVE | 
|  | 116 | app.cdmaPhoneCallState.setCurrentCallState( | 
|  | 117 | CdmaPhoneCallState.PhoneCallState.SINGLE_ACTIVE); | 
|  | 118 | } else { | 
|  | 119 | // This is the second outgoing call. Set the Phone Call State to 3WAY | 
|  | 120 | app.cdmaPhoneCallState.setCurrentCallState( | 
|  | 121 | CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE); | 
| Santos Cordon | ad1ed6d | 2013-09-16 03:04:23 -0700 | [diff] [blame] | 122 |  | 
| Santos Cordon | da120f4 | 2014-08-06 04:44:34 -0700 | [diff] [blame] | 123 | // TODO: Remove this code. | 
| Sailesh Nepal | 23d9ed7 | 2014-07-03 09:40:26 -0700 | [diff] [blame] | 124 | //app.getCallModeler().setCdmaOutgoing3WayCall(connection); | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 125 | } | 
|  | 126 | } | 
|  | 127 |  | 
|  | 128 | /** | 
|  | 129 | * Dial the number using the phone passed in. | 
|  | 130 | * | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 131 | * @param context To perform the CallerInfo query. | 
|  | 132 | * @param phone the Phone object. | 
|  | 133 | * @param number to be dialed as requested by the user. This is | 
|  | 134 | * NOT the phone number to connect to. It is used only to build the | 
|  | 135 | * call card and to update the call log. See above for restrictions. | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 136 | * | 
|  | 137 | * @return either CALL_STATUS_DIALED or CALL_STATUS_FAILED | 
|  | 138 | */ | 
| Hall Liu | 0587e3f | 2019-10-31 15:16:38 -0700 | [diff] [blame] | 139 | public static int placeOtaspCall(Context context, Phone phone, String number) { | 
|  | 140 | final Uri gatewayUri = null; | 
| Santos Cordon | 69a6919 | 2013-08-22 14:25:42 -0700 | [diff] [blame] | 141 |  | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 142 | if (VDBG) { | 
|  | 143 | log("placeCall()... number: '" + number + "'" | 
| Hall Liu | 0587e3f | 2019-10-31 15:16:38 -0700 | [diff] [blame] | 144 | + ", GW:'" + gatewayUri + "'"); | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 145 | } else { | 
|  | 146 | log("placeCall()... number: " + toLogSafePhoneNumber(number) | 
| Hall Liu | 0587e3f | 2019-10-31 15:16:38 -0700 | [diff] [blame] | 147 | + ", GW: " + (gatewayUri != null ? "non-null" : "null")); | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 148 | } | 
|  | 149 | final PhoneGlobals app = PhoneGlobals.getInstance(); | 
|  | 150 |  | 
|  | 151 | boolean useGateway = false; | 
| Hall Liu | 0587e3f | 2019-10-31 15:16:38 -0700 | [diff] [blame] | 152 | Uri contactRef = null; | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 153 |  | 
|  | 154 | int status = CALL_STATUS_DIALED; | 
|  | 155 | Connection connection; | 
|  | 156 | String numberToDial; | 
| Hall Liu | 0587e3f | 2019-10-31 15:16:38 -0700 | [diff] [blame] | 157 | numberToDial = number; | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 158 |  | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 159 | try { | 
| Tyler Gunn | c651435 | 2015-06-02 21:46:34 -0700 | [diff] [blame] | 160 | connection = app.mCM.dial(phone, numberToDial, VideoProfile.STATE_AUDIO_ONLY); | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 161 | } catch (CallStateException ex) { | 
|  | 162 | // CallStateException means a new outgoing call is not currently | 
|  | 163 | // possible: either no more call slots exist, or there's another | 
|  | 164 | // call already in the process of dialing or ringing. | 
|  | 165 | Log.w(LOG_TAG, "Exception from app.mCM.dial()", ex); | 
|  | 166 | return CALL_STATUS_FAILED; | 
|  | 167 |  | 
|  | 168 | // Note that it's possible for CallManager.dial() to return | 
|  | 169 | // null *without* throwing an exception; that indicates that | 
|  | 170 | // we dialed an MMI (see below). | 
|  | 171 | } | 
|  | 172 |  | 
|  | 173 | int phoneType = phone.getPhoneType(); | 
|  | 174 |  | 
|  | 175 | // On GSM phones, null is returned for MMI codes | 
|  | 176 | if (null == connection) { | 
| Santos Cordon | f77a339 | 2014-07-21 19:48:10 -0700 | [diff] [blame] | 177 | status = CALL_STATUS_FAILED; | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 178 | } else { | 
|  | 179 | if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) { | 
| Santos Cordon | ad1ed6d | 2013-09-16 03:04:23 -0700 | [diff] [blame] | 180 | updateCdmaCallStateOnNewOutgoingCall(app, connection); | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 181 | } | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 182 | } | 
|  | 183 |  | 
|  | 184 | return status; | 
|  | 185 | } | 
|  | 186 |  | 
|  | 187 | /* package */ static String toLogSafePhoneNumber(String number) { | 
|  | 188 | // For unknown number, log empty string. | 
|  | 189 | if (number == null) { | 
|  | 190 | return ""; | 
|  | 191 | } | 
|  | 192 |  | 
|  | 193 | if (VDBG) { | 
|  | 194 | // When VDBG is true we emit PII. | 
|  | 195 | return number; | 
|  | 196 | } | 
|  | 197 |  | 
|  | 198 | // Do exactly same thing as Uri#toSafeString() does, which will enable us to compare | 
|  | 199 | // sanitized phone numbers. | 
|  | 200 | StringBuilder builder = new StringBuilder(); | 
|  | 201 | for (int i = 0; i < number.length(); i++) { | 
|  | 202 | char c = number.charAt(i); | 
|  | 203 | if (c == '-' || c == '@' || c == '.') { | 
|  | 204 | builder.append(c); | 
|  | 205 | } else { | 
|  | 206 | builder.append('x'); | 
|  | 207 | } | 
|  | 208 | } | 
|  | 209 | return builder.toString(); | 
|  | 210 | } | 
|  | 211 |  | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 212 | /** | 
|  | 213 | * Handle the MMIInitiate message and put up an alert that lets | 
|  | 214 | * the user cancel the operation, if applicable. | 
|  | 215 | * | 
|  | 216 | * @param context context to get strings. | 
|  | 217 | * @param mmiCode the MmiCode object being started. | 
|  | 218 | * @param buttonCallbackMessage message to post when button is clicked. | 
|  | 219 | * @param previousAlert a previous alert used in this activity. | 
|  | 220 | * @return the dialog handle | 
|  | 221 | */ | 
|  | 222 | static Dialog displayMMIInitiate(Context context, | 
|  | 223 | MmiCode mmiCode, | 
|  | 224 | Message buttonCallbackMessage, | 
|  | 225 | Dialog previousAlert) { | 
| Tyler Gunn | 5ddfdc9 | 2019-10-31 13:08:23 -0700 | [diff] [blame] | 226 | log("displayMMIInitiate: " + Rlog.pii(LOG_TAG, mmiCode.toString())); | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 227 | if (previousAlert != null) { | 
|  | 228 | previousAlert.dismiss(); | 
|  | 229 | } | 
|  | 230 |  | 
|  | 231 | // The UI paradigm we are using now requests that all dialogs have | 
|  | 232 | // user interaction, and that any other messages to the user should | 
|  | 233 | // be by way of Toasts. | 
|  | 234 | // | 
|  | 235 | // In adhering to this request, all MMI initiating "OK" dialogs | 
|  | 236 | // (non-cancelable MMIs) that end up being closed when the MMI | 
|  | 237 | // completes (thereby showing a completion dialog) are being | 
|  | 238 | // replaced with Toasts. | 
|  | 239 | // | 
|  | 240 | // As a side effect, moving to Toasts for the non-cancelable MMIs | 
|  | 241 | // also means that buttonCallbackMessage (which was tied into "OK") | 
|  | 242 | // is no longer invokable for these dialogs.  This is not a problem | 
|  | 243 | // since the only callback messages we supported were for cancelable | 
|  | 244 | // MMIs anyway. | 
|  | 245 | // | 
|  | 246 | // A cancelable MMI is really just a USSD request. The term | 
|  | 247 | // "cancelable" here means that we can cancel the request when the | 
|  | 248 | // system prompts us for a response, NOT while the network is | 
|  | 249 | // processing the MMI request.  Any request to cancel a USSD while | 
|  | 250 | // the network is NOT ready for a response may be ignored. | 
|  | 251 | // | 
|  | 252 | // With this in mind, we replace the cancelable alert dialog with | 
|  | 253 | // a progress dialog, displayed until we receive a request from | 
|  | 254 | // the the network.  For more information, please see the comments | 
|  | 255 | // in the displayMMIComplete() method below. | 
|  | 256 | // | 
|  | 257 | // Anything that is NOT a USSD request is a normal MMI request, | 
|  | 258 | // which will bring up a toast (desribed above). | 
|  | 259 |  | 
|  | 260 | boolean isCancelable = (mmiCode != null) && mmiCode.isCancelable(); | 
|  | 261 |  | 
|  | 262 | if (!isCancelable) { | 
| Tyler Gunn | 13fe249 | 2017-05-22 14:14:20 -0700 | [diff] [blame] | 263 | log("displayMMIInitiate: not a USSD code, displaying status toast."); | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 264 | CharSequence text = context.getText(R.string.mmiStarted); | 
|  | 265 | Toast.makeText(context, text, Toast.LENGTH_SHORT) | 
|  | 266 | .show(); | 
|  | 267 | return null; | 
|  | 268 | } else { | 
| Tyler Gunn | 13fe249 | 2017-05-22 14:14:20 -0700 | [diff] [blame] | 269 | log("displayMMIInitiate: running USSD code, displaying intermediate progress."); | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 270 |  | 
|  | 271 | // create the indeterminate progress dialog and display it. | 
| Alex Hills | 779841a | 2015-10-22 13:23:28 -0400 | [diff] [blame] | 272 | ProgressDialog pd = new ProgressDialog(context, THEME); | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 273 | pd.setMessage(context.getText(R.string.ussdRunning)); | 
|  | 274 | pd.setCancelable(false); | 
|  | 275 | pd.setIndeterminate(true); | 
|  | 276 | pd.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); | 
|  | 277 |  | 
|  | 278 | pd.show(); | 
|  | 279 |  | 
|  | 280 | return pd; | 
|  | 281 | } | 
|  | 282 |  | 
|  | 283 | } | 
|  | 284 |  | 
|  | 285 | /** | 
|  | 286 | * Handle the MMIComplete message and fire off an intent to display | 
|  | 287 | * the message. | 
|  | 288 | * | 
|  | 289 | * @param context context to get strings. | 
|  | 290 | * @param mmiCode MMI result. | 
|  | 291 | * @param previousAlert a previous alert used in this activity. | 
|  | 292 | */ | 
|  | 293 | static void displayMMIComplete(final Phone phone, Context context, final MmiCode mmiCode, | 
|  | 294 | Message dismissCallbackMessage, | 
|  | 295 | AlertDialog previousAlert) { | 
|  | 296 | final PhoneGlobals app = PhoneGlobals.getInstance(); | 
|  | 297 | CharSequence text; | 
| Daniel Bright | ebb4eb7 | 2020-02-18 15:16:33 -0800 | [diff] [blame] | 298 | int title = 0;  // title for the progress dialog, if needed. | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 299 | MmiCode.State state = mmiCode.getState(); | 
|  | 300 |  | 
| Tyler Gunn | 13fe249 | 2017-05-22 14:14:20 -0700 | [diff] [blame] | 301 | log("displayMMIComplete: state=" + state); | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 302 |  | 
|  | 303 | switch (state) { | 
|  | 304 | case PENDING: | 
|  | 305 | // USSD code asking for feedback from user. | 
|  | 306 | text = mmiCode.getMessage(); | 
| Tyler Gunn | 13fe249 | 2017-05-22 14:14:20 -0700 | [diff] [blame] | 307 | log("displayMMIComplete: using text from PENDING MMI message: '" + text + "'"); | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 308 | break; | 
|  | 309 | case CANCELLED: | 
|  | 310 | text = null; | 
|  | 311 | break; | 
|  | 312 | case COMPLETE: | 
| SongFerngWang | 5ae4f6d | 2018-10-31 22:11:32 +0800 | [diff] [blame] | 313 | PersistableBundle b = null; | 
|  | 314 | if (SubscriptionManager.isValidSubscriptionId(phone.getSubId())) { | 
|  | 315 | b = app.getCarrierConfigForSubId( | 
|  | 316 | phone.getSubId()); | 
|  | 317 | } else { | 
|  | 318 | b = app.getCarrierConfig(); | 
|  | 319 | } | 
|  | 320 |  | 
|  | 321 | if (b.getBoolean(CarrierConfigManager.KEY_USE_CALLER_ID_USSD_BOOL)) { | 
|  | 322 | text = SuppServicesUiUtil.handleCallerIdUssdResponse(app, context, phone, | 
|  | 323 | mmiCode); | 
|  | 324 | if (mmiCode.getMessage() != null && !text.equals(mmiCode.getMessage())) { | 
|  | 325 | break; | 
|  | 326 | } | 
|  | 327 | } | 
|  | 328 |  | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 329 | if (app.getPUKEntryActivity() != null) { | 
|  | 330 | // if an attempt to unPUK the device was made, we specify | 
|  | 331 | // the title and the message here. | 
| Daniel Bright | ebb4eb7 | 2020-02-18 15:16:33 -0800 | [diff] [blame] | 332 | title = com.android.internal.R.string.PinMmi; | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 333 | text = context.getText(R.string.puk_unlocked); | 
|  | 334 | break; | 
|  | 335 | } | 
|  | 336 | // All other conditions for the COMPLETE mmi state will cause | 
|  | 337 | // the case to fall through to message logic in common with | 
|  | 338 | // the FAILED case. | 
|  | 339 |  | 
|  | 340 | case FAILED: | 
|  | 341 | text = mmiCode.getMessage(); | 
| Tyler Gunn | 13fe249 | 2017-05-22 14:14:20 -0700 | [diff] [blame] | 342 | log("displayMMIComplete (failed): using text from MMI message: '" + text + "'"); | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 343 | break; | 
|  | 344 | default: | 
|  | 345 | throw new IllegalStateException("Unexpected MmiCode state: " + state); | 
|  | 346 | } | 
|  | 347 |  | 
|  | 348 | if (previousAlert != null) { | 
|  | 349 | previousAlert.dismiss(); | 
|  | 350 | } | 
|  | 351 |  | 
|  | 352 | // Check to see if a UI exists for the PUK activation.  If it does | 
|  | 353 | // exist, then it indicates that we're trying to unblock the PUK. | 
|  | 354 | if ((app.getPUKEntryActivity() != null) && (state == MmiCode.State.COMPLETE)) { | 
|  | 355 | if (DBG) log("displaying PUK unblocking progress dialog."); | 
|  | 356 |  | 
|  | 357 | // create the progress dialog, make sure the flags and type are | 
|  | 358 | // set correctly. | 
| Alex Hills | 779841a | 2015-10-22 13:23:28 -0400 | [diff] [blame] | 359 | ProgressDialog pd = new ProgressDialog(app, THEME); | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 360 | pd.setTitle(title); | 
|  | 361 | pd.setMessage(text); | 
|  | 362 | pd.setCancelable(false); | 
|  | 363 | pd.setIndeterminate(true); | 
|  | 364 | pd.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); | 
|  | 365 | pd.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); | 
|  | 366 |  | 
|  | 367 | // display the dialog | 
|  | 368 | pd.show(); | 
|  | 369 |  | 
|  | 370 | // indicate to the Phone app that the progress dialog has | 
|  | 371 | // been assigned for the PUK unlock / SIM READY process. | 
|  | 372 | app.setPukEntryProgressDialog(pd); | 
|  | 373 |  | 
| SongFerngWang | dfa107a | 2018-10-29 20:31:58 +0800 | [diff] [blame] | 374 | } else if ((app.getPUKEntryActivity() != null) && (state == MmiCode.State.FAILED)) { | 
| Youming Ye | eb117bd | 2019-04-15 14:05:02 -0700 | [diff] [blame] | 375 | createUssdDialog(app, context, text, phone, | 
| SongFerngWang | dfa107a | 2018-10-29 20:31:58 +0800 | [diff] [blame] | 376 | WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); | 
|  | 377 | // In case of failure to unlock, we'll need to reset the | 
|  | 378 | // PUK unlock activity, so that the user may try again. | 
|  | 379 | app.setPukEntryActivity(null); | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 380 | } else { | 
|  | 381 | // In case of failure to unlock, we'll need to reset the | 
|  | 382 | // PUK unlock activity, so that the user may try again. | 
|  | 383 | if (app.getPUKEntryActivity() != null) { | 
|  | 384 | app.setPukEntryActivity(null); | 
|  | 385 | } | 
|  | 386 |  | 
|  | 387 | // A USSD in a pending state means that it is still | 
|  | 388 | // interacting with the user. | 
|  | 389 | if (state != MmiCode.State.PENDING) { | 
| Youming Ye | eb117bd | 2019-04-15 14:05:02 -0700 | [diff] [blame] | 390 | createUssdDialog(app, context, text, phone, | 
| SongFerngWang | dfa107a | 2018-10-29 20:31:58 +0800 | [diff] [blame] | 391 | WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 392 | } else { | 
| Tyler Gunn | 13fe249 | 2017-05-22 14:14:20 -0700 | [diff] [blame] | 393 | log("displayMMIComplete: USSD code has requested user input. Constructing input " | 
|  | 394 | + "dialog."); | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 395 |  | 
|  | 396 | // USSD MMI code that is interacting with the user.  The | 
|  | 397 | // basic set of steps is this: | 
|  | 398 | //   1. User enters a USSD request | 
|  | 399 | //   2. We recognize the request and displayMMIInitiate | 
|  | 400 | //      (above) creates a progress dialog. | 
|  | 401 | //   3. Request returns and we get a PENDING or COMPLETE | 
|  | 402 | //      message. | 
|  | 403 | //   4. These MMI messages are caught in the PhoneApp | 
|  | 404 | //      (onMMIComplete) and the InCallScreen | 
|  | 405 | //      (mHandler.handleMessage) which bring up this dialog | 
|  | 406 | //      and closes the original progress dialog, | 
|  | 407 | //      respectively. | 
|  | 408 | //   5. If the message is anything other than PENDING, | 
|  | 409 | //      we are done, and the alert dialog (directly above) | 
|  | 410 | //      displays the outcome. | 
|  | 411 | //   6. If the network is requesting more information from | 
|  | 412 | //      the user, the MMI will be in a PENDING state, and | 
|  | 413 | //      we display this dialog with the message. | 
|  | 414 | //   7. User input, or cancel requests result in a return | 
|  | 415 | //      to step 1.  Keep in mind that this is the only | 
|  | 416 | //      time that a USSD should be canceled. | 
|  | 417 |  | 
|  | 418 | // inflate the layout with the scrolling text area for the dialog. | 
| Andrew Lee | b3ee4dc | 2015-01-23 17:35:00 -0800 | [diff] [blame] | 419 | ContextThemeWrapper contextThemeWrapper = | 
|  | 420 | new ContextThemeWrapper(context, R.style.DialerAlertDialogTheme); | 
|  | 421 | LayoutInflater inflater = (LayoutInflater) contextThemeWrapper.getSystemService( | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 422 | Context.LAYOUT_INFLATER_SERVICE); | 
|  | 423 | View dialogView = inflater.inflate(R.layout.dialog_ussd_response, null); | 
|  | 424 |  | 
|  | 425 | // get the input field. | 
|  | 426 | final EditText inputText = (EditText) dialogView.findViewById(R.id.input_field); | 
|  | 427 |  | 
|  | 428 | // specify the dialog's click listener, with SEND and CANCEL logic. | 
|  | 429 | final DialogInterface.OnClickListener mUSSDDialogListener = | 
|  | 430 | new DialogInterface.OnClickListener() { | 
|  | 431 | public void onClick(DialogInterface dialog, int whichButton) { | 
|  | 432 | switch (whichButton) { | 
|  | 433 | case DialogInterface.BUTTON_POSITIVE: | 
|  | 434 | // As per spec 24.080, valid length of ussd string | 
|  | 435 | // is 1 - 160. If length is out of the range then | 
|  | 436 | // display toast message & Cancel MMI operation. | 
|  | 437 | if (inputText.length() < MIN_USSD_LEN | 
|  | 438 | || inputText.length() > MAX_USSD_LEN) { | 
|  | 439 | Toast.makeText(app, | 
|  | 440 | app.getResources().getString(R.string.enter_input, | 
|  | 441 | MIN_USSD_LEN, MAX_USSD_LEN), | 
|  | 442 | Toast.LENGTH_LONG).show(); | 
|  | 443 | if (mmiCode.isCancelable()) { | 
|  | 444 | mmiCode.cancel(); | 
|  | 445 | } | 
|  | 446 | } else { | 
|  | 447 | phone.sendUssdResponse(inputText.getText().toString()); | 
|  | 448 | } | 
|  | 449 | break; | 
|  | 450 | case DialogInterface.BUTTON_NEGATIVE: | 
|  | 451 | if (mmiCode.isCancelable()) { | 
|  | 452 | mmiCode.cancel(); | 
|  | 453 | } | 
|  | 454 | break; | 
|  | 455 | } | 
|  | 456 | } | 
|  | 457 | }; | 
|  | 458 |  | 
|  | 459 | // build the dialog | 
| Andrew Lee | b3ee4dc | 2015-01-23 17:35:00 -0800 | [diff] [blame] | 460 | final AlertDialog newDialog = new AlertDialog.Builder(contextThemeWrapper) | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 461 | .setMessage(text) | 
|  | 462 | .setView(dialogView) | 
|  | 463 | .setPositiveButton(R.string.send_button, mUSSDDialogListener) | 
|  | 464 | .setNegativeButton(R.string.cancel, mUSSDDialogListener) | 
|  | 465 | .setCancelable(false) | 
|  | 466 | .create(); | 
|  | 467 |  | 
|  | 468 | // attach the key listener to the dialog's input field and make | 
|  | 469 | // sure focus is set. | 
|  | 470 | final View.OnKeyListener mUSSDDialogInputListener = | 
|  | 471 | new View.OnKeyListener() { | 
|  | 472 | public boolean onKey(View v, int keyCode, KeyEvent event) { | 
|  | 473 | switch (keyCode) { | 
|  | 474 | case KeyEvent.KEYCODE_CALL: | 
|  | 475 | case KeyEvent.KEYCODE_ENTER: | 
|  | 476 | if(event.getAction() == KeyEvent.ACTION_DOWN) { | 
|  | 477 | phone.sendUssdResponse(inputText.getText().toString()); | 
|  | 478 | newDialog.dismiss(); | 
|  | 479 | } | 
|  | 480 | return true; | 
|  | 481 | } | 
|  | 482 | return false; | 
|  | 483 | } | 
|  | 484 | }; | 
|  | 485 | inputText.setOnKeyListener(mUSSDDialogInputListener); | 
|  | 486 | inputText.requestFocus(); | 
|  | 487 |  | 
|  | 488 | // set the window properties of the dialog | 
|  | 489 | newDialog.getWindow().setType( | 
|  | 490 | WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); | 
|  | 491 | newDialog.getWindow().addFlags( | 
|  | 492 | WindowManager.LayoutParams.FLAG_DIM_BEHIND); | 
|  | 493 |  | 
|  | 494 | // now show the dialog! | 
|  | 495 | newDialog.show(); | 
| Andrew Lee | b3ee4dc | 2015-01-23 17:35:00 -0800 | [diff] [blame] | 496 |  | 
|  | 497 | newDialog.getButton(DialogInterface.BUTTON_POSITIVE) | 
|  | 498 | .setTextColor(context.getResources().getColor(R.color.dialer_theme_color)); | 
|  | 499 | newDialog.getButton(DialogInterface.BUTTON_NEGATIVE) | 
|  | 500 | .setTextColor(context.getResources().getColor(R.color.dialer_theme_color)); | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 501 | } | 
|  | 502 | } | 
|  | 503 | } | 
|  | 504 |  | 
| SongFerngWang | 5ae4f6d | 2018-10-31 22:11:32 +0800 | [diff] [blame] | 505 | /** | 
|  | 506 | * It displays the message dialog for user about the mmi code result message. | 
|  | 507 | * | 
|  | 508 | * @param app This is {@link PhoneGlobals} | 
|  | 509 | * @param context Context to get strings. | 
|  | 510 | * @param text This is message's result. | 
| Youming Ye | eb117bd | 2019-04-15 14:05:02 -0700 | [diff] [blame] | 511 | * @param phone This is phone to create sssd dialog. | 
| SongFerngWang | 5ae4f6d | 2018-10-31 22:11:32 +0800 | [diff] [blame] | 512 | * @param windowType The new window type. {@link WindowManager.LayoutParams}. | 
|  | 513 | */ | 
|  | 514 | public static void createUssdDialog(PhoneGlobals app, Context context, CharSequence text, | 
| Youming Ye | eb117bd | 2019-04-15 14:05:02 -0700 | [diff] [blame] | 515 | Phone phone, int windowType) { | 
| SongFerngWang | dfa107a | 2018-10-29 20:31:58 +0800 | [diff] [blame] | 516 | log("displayMMIComplete: MMI code has finished running."); | 
|  | 517 |  | 
|  | 518 | log("displayMMIComplete: Extended NW displayMMIInitiate (" + text + ")"); | 
|  | 519 | if (text == null || text.length() == 0) { | 
|  | 520 | return; | 
|  | 521 | } | 
|  | 522 |  | 
|  | 523 | // displaying system alert dialog on the screen instead of | 
|  | 524 | // using another activity to display the message.  This | 
|  | 525 | // places the message at the forefront of the UI. | 
| Jack Yu | 0766600 | 2020-03-27 19:39:53 -0700 | [diff] [blame] | 526 | AlertDialog ussdDialog = new AlertDialog.Builder(context, THEME) | 
|  | 527 | .setPositiveButton(R.string.ok, null) | 
|  | 528 | .setCancelable(true) | 
|  | 529 | .setOnDismissListener(new DialogInterface.OnDismissListener() { | 
|  | 530 | @Override | 
|  | 531 | public void onDismiss(DialogInterface dialog) { | 
|  | 532 | sUssdMsg.setLength(0); | 
|  | 533 | } | 
|  | 534 | }) | 
|  | 535 | .create(); | 
| SongFerngWang | dfa107a | 2018-10-29 20:31:58 +0800 | [diff] [blame] | 536 |  | 
| Jack Yu | 0766600 | 2020-03-27 19:39:53 -0700 | [diff] [blame] | 537 | ussdDialog.getWindow().setType(windowType); | 
|  | 538 | ussdDialog.getWindow().addFlags( | 
|  | 539 | WindowManager.LayoutParams.FLAG_DIM_BEHIND); | 
| SongFerngWang | dfa107a | 2018-10-29 20:31:58 +0800 | [diff] [blame] | 540 |  | 
| SongFerngWang | dfa107a | 2018-10-29 20:31:58 +0800 | [diff] [blame] | 541 | if (sUssdMsg.length() != 0) { | 
| Jack Yu | 0766600 | 2020-03-27 19:39:53 -0700 | [diff] [blame] | 542 | sUssdMsg.insert(0, "\n") | 
| SongFerngWang | dfa107a | 2018-10-29 20:31:58 +0800 | [diff] [blame] | 543 | .insert(0, app.getResources().getString(R.string.ussd_dialog_sep)) | 
|  | 544 | .insert(0, "\n"); | 
|  | 545 | } | 
| Youming Ye | eb117bd | 2019-04-15 14:05:02 -0700 | [diff] [blame] | 546 | if (phone != null && phone.getCarrierName() != null) { | 
| Jack Yu | 0766600 | 2020-03-27 19:39:53 -0700 | [diff] [blame] | 547 | ussdDialog.setTitle(app.getResources().getString(R.string.carrier_mmi_msg_title, | 
| Youming Ye | eb117bd | 2019-04-15 14:05:02 -0700 | [diff] [blame] | 548 | phone.getCarrierName())); | 
|  | 549 | } else { | 
| Jack Yu | 0766600 | 2020-03-27 19:39:53 -0700 | [diff] [blame] | 550 | ussdDialog | 
| Youming Ye | eb117bd | 2019-04-15 14:05:02 -0700 | [diff] [blame] | 551 | .setTitle(app.getResources().getString(R.string.default_carrier_mmi_msg_title)); | 
|  | 552 | } | 
| SongFerngWang | dfa107a | 2018-10-29 20:31:58 +0800 | [diff] [blame] | 553 | sUssdMsg.insert(0, text); | 
| Jack Yu | 0766600 | 2020-03-27 19:39:53 -0700 | [diff] [blame] | 554 | ussdDialog.setMessage(sUssdMsg.toString()); | 
|  | 555 | ussdDialog.show(); | 
| SongFerngWang | dfa107a | 2018-10-29 20:31:58 +0800 | [diff] [blame] | 556 | } | 
|  | 557 |  | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 558 | /** | 
|  | 559 | * Cancels the current pending MMI operation, if applicable. | 
|  | 560 | * @return true if we canceled an MMI operation, or false | 
|  | 561 | *         if the current pending MMI wasn't cancelable | 
|  | 562 | *         or if there was no current pending MMI at all. | 
|  | 563 | * | 
|  | 564 | * @see displayMMIInitiate | 
|  | 565 | */ | 
|  | 566 | static boolean cancelMmiCode(Phone phone) { | 
|  | 567 | List<? extends MmiCode> pendingMmis = phone.getPendingMmiCodes(); | 
|  | 568 | int count = pendingMmis.size(); | 
|  | 569 | if (DBG) log("cancelMmiCode: num pending MMIs = " + count); | 
|  | 570 |  | 
|  | 571 | boolean canceled = false; | 
|  | 572 | if (count > 0) { | 
|  | 573 | // assume that we only have one pending MMI operation active at a time. | 
|  | 574 | // I don't think it's possible to enter multiple MMI codes concurrently | 
|  | 575 | // in the phone UI, because during the MMI operation, an Alert panel | 
|  | 576 | // is displayed, which prevents more MMI code from being entered. | 
|  | 577 | MmiCode mmiCode = pendingMmis.get(0); | 
|  | 578 | if (mmiCode.isCancelable()) { | 
|  | 579 | mmiCode.cancel(); | 
|  | 580 | canceled = true; | 
|  | 581 | } | 
|  | 582 | } | 
|  | 583 | return canceled; | 
|  | 584 | } | 
|  | 585 |  | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 586 |  | 
|  | 587 | // | 
|  | 588 | // Misc UI policy helper functions | 
|  | 589 | // | 
|  | 590 |  | 
|  | 591 | /** | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 592 | * Check if a phone number can be route through a 3rd party | 
|  | 593 | * gateway. The number must be a global phone number in numerical | 
|  | 594 | * form (1-800-666-SEXY won't work). | 
|  | 595 | * | 
|  | 596 | * MMI codes and the like cannot be used as a dial number for the | 
|  | 597 | * gateway either. | 
|  | 598 | * | 
|  | 599 | * @param number To be dialed via a 3rd party gateway. | 
|  | 600 | * @return true If the number can be routed through the 3rd party network. | 
|  | 601 | */ | 
| Santos Cordon | 69a6919 | 2013-08-22 14:25:42 -0700 | [diff] [blame] | 602 | private static boolean isRoutableViaGateway(String number) { | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 603 | if (TextUtils.isEmpty(number)) { | 
|  | 604 | return false; | 
|  | 605 | } | 
|  | 606 | number = PhoneNumberUtils.stripSeparators(number); | 
|  | 607 | if (!number.equals(PhoneNumberUtils.convertKeypadLettersToDigits(number))) { | 
|  | 608 | return false; | 
|  | 609 | } | 
|  | 610 | number = PhoneNumberUtils.extractNetworkPortion(number); | 
|  | 611 | return PhoneNumberUtils.isGlobalPhoneNumber(number); | 
|  | 612 | } | 
|  | 613 |  | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 614 | /** | 
|  | 615 | * Returns whether the phone is in ECM ("Emergency Callback Mode") or not. | 
|  | 616 | */ | 
|  | 617 | /* package */ static boolean isPhoneInEcm(Phone phone) { | 
|  | 618 | if ((phone != null) && TelephonyCapabilities.supportsEcm(phone)) { | 
| Shuo Qian | dd21031 | 2017-04-12 22:11:33 +0000 | [diff] [blame] | 619 | return phone.isInEcm(); | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 620 | } | 
|  | 621 | return false; | 
|  | 622 | } | 
|  | 623 |  | 
|  | 624 | /** | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 625 | * Returns true when the given call is in INCOMING state and there's no foreground phone call, | 
|  | 626 | * meaning the call is the first real incoming call the phone is having. | 
|  | 627 | */ | 
|  | 628 | public static boolean isRealIncomingCall(Call.State state) { | 
|  | 629 | return (state == Call.State.INCOMING && !PhoneGlobals.getInstance().mCM.hasActiveFgCall()); | 
|  | 630 | } | 
|  | 631 |  | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 632 | // | 
|  | 633 | // General phone and call state debugging/testing code | 
|  | 634 | // | 
|  | 635 |  | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 636 | private static void log(String msg) { | 
|  | 637 | Log.d(LOG_TAG, msg); | 
|  | 638 | } | 
|  | 639 |  | 
| Nancy Chen | e53a064 | 2015-03-12 11:20:12 -0700 | [diff] [blame] | 640 | public static PhoneAccountHandle makePstnPhoneAccountHandle(String id) { | 
|  | 641 | return makePstnPhoneAccountHandleWithPrefix(id, "", false); | 
|  | 642 | } | 
|  | 643 |  | 
| Nancy Chen | 32031f4 | 2015-03-04 21:00:07 -0800 | [diff] [blame] | 644 | public static PhoneAccountHandle makePstnPhoneAccountHandle(int phoneId) { | 
|  | 645 | return makePstnPhoneAccountHandle(PhoneFactory.getPhone(phoneId)); | 
|  | 646 | } | 
|  | 647 |  | 
| Andrew Lee | d5165b0 | 2014-12-05 15:53:58 -0800 | [diff] [blame] | 648 | public static PhoneAccountHandle makePstnPhoneAccountHandle(Phone phone) { | 
|  | 649 | return makePstnPhoneAccountHandleWithPrefix(phone, "", false); | 
|  | 650 | } | 
|  | 651 |  | 
|  | 652 | public static PhoneAccountHandle makePstnPhoneAccountHandleWithPrefix( | 
|  | 653 | Phone phone, String prefix, boolean isEmergency) { | 
| Andrew Lee | d5165b0 | 2014-12-05 15:53:58 -0800 | [diff] [blame] | 654 | // TODO: Should use some sort of special hidden flag to decorate this account as | 
|  | 655 | // an emergency-only account | 
| Brad Ebinger | d3edc22 | 2016-03-23 19:44:38 -0700 | [diff] [blame] | 656 | String id = isEmergency ? EMERGENCY_ACCOUNT_HANDLE_ID : prefix + | 
| Brad Ebinger | 4fe4811 | 2016-04-13 11:21:53 -0700 | [diff] [blame] | 657 | String.valueOf(phone.getFullIccSerialNumber()); | 
| Nancy Chen | e53a064 | 2015-03-12 11:20:12 -0700 | [diff] [blame] | 658 | return makePstnPhoneAccountHandleWithPrefix(id, prefix, isEmergency); | 
|  | 659 | } | 
|  | 660 |  | 
|  | 661 | public static PhoneAccountHandle makePstnPhoneAccountHandleWithPrefix( | 
|  | 662 | String id, String prefix, boolean isEmergency) { | 
|  | 663 | ComponentName pstnConnectionServiceName = getPstnConnectionServiceName(); | 
| Andrew Lee | d5165b0 | 2014-12-05 15:53:58 -0800 | [diff] [blame] | 664 | return new PhoneAccountHandle(pstnConnectionServiceName, id); | 
|  | 665 | } | 
| Stuart Scott | dcf40a9 | 2014-12-09 10:45:01 -0800 | [diff] [blame] | 666 |  | 
| Santos Cordon | f69df96 | 2015-02-03 11:15:19 -0800 | [diff] [blame] | 667 | public static int getSubIdForPhoneAccount(PhoneAccount phoneAccount) { | 
| Santos Cordon | 21e78ac | 2015-02-06 05:19:29 -0800 | [diff] [blame] | 668 | if (phoneAccount != null | 
|  | 669 | && phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) { | 
|  | 670 | return getSubIdForPhoneAccountHandle(phoneAccount.getAccountHandle()); | 
|  | 671 | } | 
|  | 672 | return SubscriptionManager.INVALID_SUBSCRIPTION_ID; | 
|  | 673 | } | 
|  | 674 |  | 
|  | 675 | public static int getSubIdForPhoneAccountHandle(PhoneAccountHandle handle) { | 
| Nancy Chen | 31f9ba1 | 2016-01-06 11:42:12 -0800 | [diff] [blame] | 676 | Phone phone = getPhoneForPhoneAccountHandle(handle); | 
|  | 677 | if (phone != null) { | 
|  | 678 | return phone.getSubId(); | 
| Santos Cordon | f69df96 | 2015-02-03 11:15:19 -0800 | [diff] [blame] | 679 | } | 
|  | 680 | return SubscriptionManager.INVALID_SUBSCRIPTION_ID; | 
|  | 681 | } | 
|  | 682 |  | 
| Ta-wei Yen | 845f699 | 2016-06-16 12:53:46 -0700 | [diff] [blame] | 683 | public static Phone getPhoneForPhoneAccountHandle(PhoneAccountHandle handle) { | 
| Nancy Chen | 31f9ba1 | 2016-01-06 11:42:12 -0800 | [diff] [blame] | 684 | if (handle != null && handle.getComponentName().equals(getPstnConnectionServiceName())) { | 
|  | 685 | return getPhoneFromIccId(handle.getId()); | 
|  | 686 | } | 
|  | 687 | return null; | 
|  | 688 | } | 
|  | 689 |  | 
| Nancy Chen | e53a064 | 2015-03-12 11:20:12 -0700 | [diff] [blame] | 690 | /** | 
|  | 691 | * Determine if a given phone account corresponds to an active SIM | 
|  | 692 | * | 
|  | 693 | * @param sm An instance of the subscription manager so it is not recreated for each calling of | 
|  | 694 | * this method. | 
|  | 695 | * @param handle The handle for the phone account to check | 
|  | 696 | * @return {@code true} If there is an active SIM for this phone account, | 
|  | 697 | * {@code false} otherwise. | 
|  | 698 | */ | 
|  | 699 | public static boolean isPhoneAccountActive(SubscriptionManager sm, PhoneAccountHandle handle) { | 
| Shuo Qian | dfc8588 | 2020-01-06 15:59:12 -0800 | [diff] [blame] | 700 | return sm.getActiveSubscriptionInfoForIcc(handle.getId()) != null; | 
| Nancy Chen | e53a064 | 2015-03-12 11:20:12 -0700 | [diff] [blame] | 701 | } | 
|  | 702 |  | 
| Santos Cordon | f69df96 | 2015-02-03 11:15:19 -0800 | [diff] [blame] | 703 | private static ComponentName getPstnConnectionServiceName() { | 
| Tony Mak | 4c8f3c6 | 2015-12-07 02:30:37 +0000 | [diff] [blame] | 704 | return PSTN_CONNECTION_SERVICE_COMPONENT; | 
| Santos Cordon | f69df96 | 2015-02-03 11:15:19 -0800 | [diff] [blame] | 705 | } | 
|  | 706 |  | 
| Santos Cordon | 80855d5 | 2015-02-10 04:33:36 -0800 | [diff] [blame] | 707 | private static Phone getPhoneFromIccId(String iccId) { | 
|  | 708 | if (!TextUtils.isEmpty(iccId)) { | 
|  | 709 | for (Phone phone : PhoneFactory.getPhones()) { | 
| Brad Ebinger | 4fe4811 | 2016-04-13 11:21:53 -0700 | [diff] [blame] | 710 | String phoneIccId = phone.getFullIccSerialNumber(); | 
| Santos Cordon | 80855d5 | 2015-02-10 04:33:36 -0800 | [diff] [blame] | 711 | if (iccId.equals(phoneIccId)) { | 
|  | 712 | return phone; | 
|  | 713 | } | 
|  | 714 | } | 
|  | 715 | } | 
| Santos Cordon | 80855d5 | 2015-02-10 04:33:36 -0800 | [diff] [blame] | 716 | return null; | 
|  | 717 | } | 
|  | 718 |  | 
| Stuart Scott | dcf40a9 | 2014-12-09 10:45:01 -0800 | [diff] [blame] | 719 | /** | 
|  | 720 | * Register ICC status for all phones. | 
|  | 721 | */ | 
|  | 722 | static final void registerIccStatus(Handler handler, int event) { | 
|  | 723 | for (Phone phone : PhoneFactory.getPhones()) { | 
|  | 724 | IccCard sim = phone.getIccCard(); | 
|  | 725 | if (sim != null) { | 
|  | 726 | if (VDBG) Log.v(LOG_TAG, "register for ICC status, phone " + phone.getPhoneId()); | 
|  | 727 | sim.registerForNetworkLocked(handler, event, phone); | 
|  | 728 | } | 
|  | 729 | } | 
|  | 730 | } | 
|  | 731 |  | 
|  | 732 | /** | 
| Amit Mahajan | f5d92c8 | 2018-11-02 17:44:40 -0700 | [diff] [blame] | 733 | * Register ICC status for all phones. | 
|  | 734 | */ | 
|  | 735 | static final void registerIccStatus(Handler handler, int event, int phoneId) { | 
|  | 736 | Phone[] phones = PhoneFactory.getPhones(); | 
|  | 737 | IccCard sim = phones[phoneId].getIccCard(); | 
|  | 738 | if (sim != null) { | 
|  | 739 | if (VDBG) { | 
|  | 740 | Log.v(LOG_TAG, "register for ICC status, phone " + phones[phoneId].getPhoneId()); | 
|  | 741 | } | 
|  | 742 | sim.registerForNetworkLocked(handler, event, phones[phoneId]); | 
|  | 743 | } | 
|  | 744 | } | 
|  | 745 |  | 
|  | 746 | /** | 
|  | 747 | * Unregister ICC status for a specific phone. | 
|  | 748 | */ | 
|  | 749 | static final void unregisterIccStatus(Handler handler, int phoneId) { | 
|  | 750 | Phone[] phones = PhoneFactory.getPhones(); | 
|  | 751 | IccCard sim = phones[phoneId].getIccCard(); | 
|  | 752 | if (sim != null) { | 
|  | 753 | if (VDBG) { | 
|  | 754 | Log.v(LOG_TAG, "unregister for ICC status, phone " + phones[phoneId].getPhoneId()); | 
|  | 755 | } | 
|  | 756 | sim.unregisterForNetworkLocked(handler); | 
|  | 757 | } | 
|  | 758 | } | 
|  | 759 |  | 
|  | 760 | /** | 
| Stuart Scott | dcf40a9 | 2014-12-09 10:45:01 -0800 | [diff] [blame] | 761 | * Set the radio power on/off state for all phones. | 
|  | 762 | * | 
|  | 763 | * @param enabled true means on, false means off. | 
|  | 764 | */ | 
|  | 765 | static final void setRadioPower(boolean enabled) { | 
|  | 766 | for (Phone phone : PhoneFactory.getPhones()) { | 
|  | 767 | phone.setRadioPower(enabled); | 
|  | 768 | } | 
|  | 769 | } | 
| Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 770 | } |