Merge "Solve flakiness of RadioOnStateListenerTest"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9e56ef8..16ad93a 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -310,6 +310,15 @@
</intent-filter>
</activity>
+ <activity android:name="CdmaCallForwardOptions"
+ android:label="@string/labelCF"
+ android:configChanges="orientation|screenSize|keyboardHidden"
+ android:theme="@style/DialerSettingsLight">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
+
<activity android:name="GsmUmtsCallBarringOptions"
android:label="@string/labelCallBarring"
android:configChanges="orientation|screenSize|keyboardHidden"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a19f3df..b74b2ff 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -275,6 +275,14 @@
<string name="disable_cfnrc_forbidden">Your carrier doesn\'t support disabling call forwarding when your phone is unreachable.</string>
<string name="registration_cf_forbidden">Your carrier doesn\'t support call forwarding.</string>
+ <!-- Cdma Call waiting settings screen, setting option name -->
+ <string name="cdma_call_waiting">Turn on call waiting?</string>
+ <string name="enable_cdma_call_waiting_setting">During a call, you\'ll be notified about incoming calls</string>
+ <string name="enable_cdma_cw">Turn on</string>
+ <string name="disable_cdma_cw">Cancel</string>
+ <string name="cdma_call_waiting_in_ims_on">CDMA Call Waiting under IMS On</string>
+ <string name="cdma_call_waiting_in_ims_off">CDMA Call Waiting under IMS Off</string>
+
<!-- Title of the progress dialog displayed while updating Call settings -->
<string name="updating_title">Call settings</string>
<!-- Toast in Call settings when asked to launch settings for a secondary user -->
@@ -2154,5 +2162,4 @@
<string name="carrier_provisioning">Carrier Provisioning Info</string>
<!-- Trigger Carrier Provisioning [CHAR LIMIT=NONE] -->
<string name="trigger_carrier_provisioning">Trigger Carrier Provisioning</string>
-
</resources>
diff --git a/res/xml/cdma_call_privacy.xml b/res/xml/cdma_call_privacy.xml
index 1aeeefe..a16a504 100644
--- a/res/xml/cdma_call_privacy.xml
+++ b/res/xml/cdma_call_privacy.xml
@@ -7,4 +7,14 @@
android:title="@string/voice_privacy"
android:persistent="false"
android:summary="@string/voice_privacy_summary"/>
+
+ <PreferenceScreen
+ android:key="call_forwarding_key"
+ android:title="@string/labelCF"
+ android:persistent="false" />
+
+ <com.android.phone.CdmaCallWaitingPreference
+ android:key="call_waiting_key"
+ android:title="@string/labelCW"
+ android:persistent="false" />
</PreferenceScreen>
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index 3f57cae..9faffa1 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -396,11 +396,7 @@
} else {
if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
prefSet.removePreference(fdnButton);
-
- if (!carrierConfig.getBoolean(
- CarrierConfigManager.KEY_VOICE_PRIVACY_DISABLE_UI_BOOL)) {
- addPreferencesFromResource(R.xml.cdma_call_privacy);
- }
+ addPreferencesFromResource(R.xml.cdma_call_privacy);
} else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
if (mPhone.getIccCard() == null || !mPhone.getIccCard().getIccFdnAvailable()) {
prefSet.removePreference(fdnButton);
diff --git a/src/com/android/phone/CallForwardEditPreference.java b/src/com/android/phone/CallForwardEditPreference.java
index 24dc020..8e0b685 100644
--- a/src/com/android/phone/CallForwardEditPreference.java
+++ b/src/com/android/phone/CallForwardEditPreference.java
@@ -122,7 +122,27 @@
Log.d(LOG_TAG, "mButtonClicked=" + mButtonClicked + ", positiveResult=" + positiveResult);
// Ignore this event if the user clicked the cancel button, or if the dialog is dismissed
// without any button being pressed (back button press or click event outside the dialog).
- if (this.mButtonClicked != DialogInterface.BUTTON_NEGATIVE) {
+ if (isUnknownStatus() && this.mButtonClicked != DialogInterface.BUTTON_NEGATIVE) {
+ int action = (mButtonClicked == DialogInterface.BUTTON_POSITIVE) ?
+ CommandsInterface.CF_ACTION_REGISTRATION :
+ CommandsInterface.CF_ACTION_DISABLE;
+ final String number = (action == CommandsInterface.CF_ACTION_DISABLE) ?
+ "" : getPhoneNumber();
+
+ Log.d(LOG_TAG, "reason=" + reason + ", action=" + action + ", number=" + number);
+
+ // Display no forwarding number while we're waiting for confirmation.
+ setSummaryOff("");
+
+ mPhone.setCallForwardingOption(action,
+ reason,
+ number,
+ mServiceClass,
+ 0,
+ mHandler.obtainMessage(MyHandler.MESSAGE_SET_CF,
+ action,
+ MyHandler.MESSAGE_SET_CF));
+ } else if (this.mButtonClicked != DialogInterface.BUTTON_NEGATIVE) {
int action = (isToggled() || (mButtonClicked == DialogInterface.BUTTON_POSITIVE)) ?
CommandsInterface.CF_ACTION_REGISTRATION :
CommandsInterface.CF_ACTION_DISABLE;
@@ -194,6 +214,7 @@
Log.i(LOG_TAG, "handleGetCFResponse: Overridding CF number");
}
+ setUnknownStatus(callForwardInfo.status == CommandsInterface.SS_STATUS_UNKNOWN);
setToggled(callForwardInfo.status == 1);
boolean displayVoicemailNumber = false;
if (TextUtils.isEmpty(callForwardInfo.number)) {
@@ -343,6 +364,7 @@
AsyncResult ar = (AsyncResult) msg.obj;
callForwardInfo = null;
+ boolean summaryOff = false;
if (ar.exception != null) {
Log.d(LOG_TAG, "handleGetCFResponse: ar.exception=" + ar.exception);
if (ar.exception instanceof CommandException) {
@@ -372,6 +394,8 @@
CallForwardInfo info = cfInfoArray[i];
handleCallForwardResult(info);
+ summaryOff = (info.status == CommandsInterface.SS_STATUS_UNKNOWN);
+
if (ar.userObj instanceof Throwable) {
Log.d(LOG_TAG, "Skipped duplicated error dialog");
continue;
@@ -430,7 +454,15 @@
// Now whether or not we got a new number, reset our enabled
// summary text since it may have been replaced by an empty
// placeholder.
- updateSummaryText();
+ // for CDMA, doesn't display summary.
+ if (summaryOff) {
+ setSummaryOff("");
+ } else {
+ // Now whether or not we got a new number, reset our enabled
+ // summary text since it may have been replaced by an empty
+ // placeholder.
+ updateSummaryText();
+ }
}
private void handleSetCFResponse(Message msg) {
@@ -439,6 +471,16 @@
Log.d(LOG_TAG, "handleSetCFResponse: ar.exception=" + ar.exception);
// setEnabled(false);
}
+
+ if (ar.result != null) {
+ int arr = (int)ar.result;
+ if (arr == CommandsInterface.SS_STATUS_UNKNOWN) {
+ Log.d(LOG_TAG, "handleSetCFResponse: no need to re get in CDMA");
+ mTcpListener.onFinished(CallForwardEditPreference.this, false);
+ return;
+ }
+ }
+
Log.d(LOG_TAG, "handleSetCFResponse: re get");
if (!mCallForwardByUssd) {
mPhone.getCallForwardingOption(reason, mServiceClass,
diff --git a/src/com/android/phone/CdmaCallForwardOptions.java b/src/com/android/phone/CdmaCallForwardOptions.java
new file mode 100644
index 0000000..a8d2e93
--- /dev/null
+++ b/src/com/android/phone/CdmaCallForwardOptions.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone;
+
+import android.app.ActionBar;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.telephony.CarrierConfigManager;
+import android.util.Log;
+import android.view.MenuItem;
+
+import com.android.internal.telephony.CallForwardInfo;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.Phone;
+
+import java.util.ArrayList;
+
+public class CdmaCallForwardOptions extends TimeConsumingPreferenceActivity {
+ private static final String LOG_TAG = "CdmaCallForwardOptions";
+
+ private static final String NUM_PROJECTION[] = {
+ android.provider.ContactsContract.CommonDataKinds.Phone.NUMBER
+ };
+
+ private static final String BUTTON_CFU_KEY = "button_cfu_key";
+ private static final String BUTTON_CFB_KEY = "button_cfb_key";
+ private static final String BUTTON_CFNRY_KEY = "button_cfnry_key";
+ private static final String BUTTON_CFNRC_KEY = "button_cfnrc_key";
+
+ private static final String KEY_TOGGLE = "toggle";
+ private static final String KEY_STATUS = "status";
+ private static final String KEY_NUMBER = "number";
+ private static final String KEY_ENABLE = "enable";
+
+ private CallForwardEditPreference mButtonCFU;
+ private CallForwardEditPreference mButtonCFB;
+ private CallForwardEditPreference mButtonCFNRy;
+ private CallForwardEditPreference mButtonCFNRc;
+
+ private final ArrayList<CallForwardEditPreference> mPreferences =
+ new ArrayList<CallForwardEditPreference> ();
+ private int mInitIndex= 0;
+
+ private boolean mFirstResume;
+ private Bundle mIcicle;
+ private Phone mPhone;
+ private SubscriptionInfoHelper mSubscriptionInfoHelper;
+ private boolean mReplaceInvalidCFNumbers;
+ private boolean mCallForwardByUssd;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ addPreferencesFromResource(R.xml.callforward_options);
+
+ mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, getIntent());
+ mSubscriptionInfoHelper.setActionBarTitle(
+ getActionBar(), getResources(), R.string.call_forwarding_settings_with_label);
+ mPhone = mSubscriptionInfoHelper.getPhone();
+
+ PersistableBundle b = null;
+ boolean supportCFNRc = true;
+ if (mSubscriptionInfoHelper.hasSubId()) {
+ b = PhoneGlobals.getInstance().getCarrierConfigForSubId(
+ mSubscriptionInfoHelper.getSubId());
+ } else {
+ b = PhoneGlobals.getInstance().getCarrierConfig();
+ }
+ if (b != null) {
+ mReplaceInvalidCFNumbers = b.getBoolean(
+ CarrierConfigManager.KEY_CALL_FORWARDING_MAP_NON_NUMBER_TO_VOICEMAIL_BOOL);
+ mCallForwardByUssd = b.getBoolean(
+ CarrierConfigManager.KEY_USE_CALL_FORWARDING_USSD_BOOL);
+ supportCFNRc = b.getBoolean(
+ CarrierConfigManager.KEY_CALL_FORWARDING_WHEN_UNREACHABLE_SUPPORTED_BOOL);
+ }
+
+ PreferenceScreen prefSet = getPreferenceScreen();
+ mButtonCFU = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFU_KEY);
+ mButtonCFB = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFB_KEY);
+ mButtonCFNRy = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFNRY_KEY);
+ mButtonCFNRc = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFNRC_KEY);
+
+ mButtonCFU.setParentActivity(this, mButtonCFU.reason);
+ mButtonCFB.setParentActivity(this, mButtonCFB.reason);
+ mButtonCFNRy.setParentActivity(this, mButtonCFNRy.reason);
+ mButtonCFNRc.setParentActivity(this, mButtonCFNRc.reason);
+
+ mPreferences.add(mButtonCFU);
+ mPreferences.add(mButtonCFB);
+ mPreferences.add(mButtonCFNRy);
+
+ if (supportCFNRc) {
+ mPreferences.add(mButtonCFNRc);
+ } else {
+ // When CFNRc is not supported, mButtonCFNRc is grayed out from the menu.
+ // Default state for the preferences in this PreferenceScreen is disabled.
+ // Only preferences listed in the ArrayList mPreferences will be enabled.
+ // By not adding mButtonCFNRc to mPreferences it will be kept disabled.
+ Log.d(LOG_TAG, "onCreate: CFNRc is not supported, grey out the item.");
+ }
+
+ if (mCallForwardByUssd) {
+ //the call forwarding ussd command's behavior is similar to the call forwarding when
+ //unanswered,so only display the call forwarding when unanswered item.
+ prefSet.removePreference(mButtonCFU);
+ prefSet.removePreference(mButtonCFB);
+ prefSet.removePreference(mButtonCFNRc);
+ mPreferences.remove(mButtonCFU);
+ mPreferences.remove(mButtonCFB);
+ mPreferences.remove(mButtonCFNRc);
+ mButtonCFNRy.setDependency(null);
+ }
+
+ // we wait to do the initialization until onResume so that the
+ // TimeConsumingPreferenceActivity dialog can display as it
+ // relies on onResume / onPause to maintain its foreground state.
+
+ mFirstResume = true;
+ mIcicle = icicle;
+
+ ActionBar actionBar = getActionBar();
+ if (actionBar != null) {
+ // android.R.id.home will be triggered in onOptionsItemSelected()
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (mFirstResume) {
+ if (mIcicle == null) {
+ Log.d(LOG_TAG, "start to init ");
+ CallForwardEditPreference pref = mPreferences.get(mInitIndex);
+ pref.init(this, mPhone, mReplaceInvalidCFNumbers, mCallForwardByUssd);
+ pref.startCallForwardOptionsQuery();
+
+ } else {
+ mInitIndex = mPreferences.size();
+
+ for (CallForwardEditPreference pref : mPreferences) {
+ Bundle bundle = mIcicle.getParcelable(pref.getKey());
+ pref.setToggled(bundle.getBoolean(KEY_TOGGLE));
+ pref.setEnabled(bundle.getBoolean(KEY_ENABLE));
+ CallForwardInfo cf = new CallForwardInfo();
+ cf.number = bundle.getString(KEY_NUMBER);
+ cf.status = bundle.getInt(KEY_STATUS);
+ pref.init(this, mPhone, mReplaceInvalidCFNumbers, mCallForwardByUssd);
+ pref.restoreCallForwardInfo(cf);
+ }
+ }
+ mFirstResume = false;
+ mIcicle = null;
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ for (CallForwardEditPreference pref : mPreferences) {
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(KEY_TOGGLE, pref.isToggled());
+ bundle.putBoolean(KEY_ENABLE, pref.isEnabled());
+ if (pref.callForwardInfo != null) {
+ bundle.putString(KEY_NUMBER, pref.callForwardInfo.number);
+ bundle.putInt(KEY_STATUS, pref.callForwardInfo.status);
+ }
+ outState.putParcelable(pref.getKey(), bundle);
+ }
+ }
+
+ @Override
+ public void onFinished(Preference preference, boolean reading) {
+ if (mInitIndex < mPreferences.size()-1 && !isFinishing()) {
+ mInitIndex++;
+ CallForwardEditPreference pref = mPreferences.get(mInitIndex);
+ pref.init(this, mPhone, mReplaceInvalidCFNumbers, mCallForwardByUssd);
+ pref.startCallForwardOptionsQuery();
+ }
+
+ super.onFinished(preference, reading);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ Log.d(LOG_TAG, "onActivityResult: done");
+ if (resultCode != RESULT_OK) {
+ Log.d(LOG_TAG, "onActivityResult: contact picker result not OK.");
+ return;
+ }
+ Cursor cursor = null;
+ try {
+ cursor = getContentResolver().query(data.getData(),
+ NUM_PROJECTION, null, null, null);
+ if ((cursor == null) || (!cursor.moveToFirst())) {
+ Log.d(LOG_TAG, "onActivityResult: bad contact data, no results found.");
+ return;
+ }
+
+ switch (requestCode) {
+ case CommandsInterface.CF_REASON_UNCONDITIONAL:
+ mButtonCFU.onPickActivityResult(cursor.getString(0));
+ break;
+ case CommandsInterface.CF_REASON_BUSY:
+ mButtonCFB.onPickActivityResult(cursor.getString(0));
+ break;
+ case CommandsInterface.CF_REASON_NO_REPLY:
+ mButtonCFNRy.onPickActivityResult(cursor.getString(0));
+ break;
+ case CommandsInterface.CF_REASON_NOT_REACHABLE:
+ mButtonCFNRc.onPickActivityResult(cursor.getString(0));
+ break;
+ default:
+ // TODO: may need exception here.
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ final int itemId = item.getItemId();
+ if (itemId == android.R.id.home) { // See ActionBar#setDisplayHomeAsUpEnabled()
+ CallFeaturesSetting.goUpToTopLevelSetting(this, mSubscriptionInfoHelper);
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/src/com/android/phone/CdmaCallOptions.java b/src/com/android/phone/CdmaCallOptions.java
index acfa496..8f4fa67 100644
--- a/src/com/android/phone/CdmaCallOptions.java
+++ b/src/com/android/phone/CdmaCallOptions.java
@@ -27,11 +27,13 @@
import com.android.internal.telephony.PhoneConstants;
-public class CdmaCallOptions extends PreferenceActivity {
+public class CdmaCallOptions extends TimeConsumingPreferenceActivity {
private static final String LOG_TAG = "CdmaCallOptions";
private final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
private static final String BUTTON_VP_KEY = "button_voice_privacy_key";
+ private static final String CALL_FORWARDING_KEY = "call_forwarding_key";
+ private static final String CALL_WAITING_KEY = "call_waiting_key";
private SwitchPreference mButtonVoicePrivacy;
@Override
@@ -55,8 +57,15 @@
if (subInfoHelper.getPhone().getPhoneType() != PhoneConstants.PHONE_TYPE_CDMA
|| carrierConfig.getBoolean(CarrierConfigManager.KEY_VOICE_PRIVACY_DISABLE_UI_BOOL)) {
// disable the entire screen
- getPreferenceScreen().setEnabled(false);
+ mButtonVoicePrivacy.setEnabled(false);
}
+
+ Preference callForwardingPref = getPreferenceScreen().findPreference(CALL_FORWARDING_KEY);
+ callForwardingPref.setIntent(subInfoHelper.getIntent(CdmaCallForwardOptions.class));
+
+ CdmaCallWaitingPreference callWaitingPref = (CdmaCallWaitingPreference)getPreferenceScreen()
+ .findPreference(CALL_WAITING_KEY);
+ callWaitingPref.init(this, subInfoHelper.getPhone());
}
@Override
@@ -76,5 +85,4 @@
}
return false;
}
-
}
diff --git a/src/com/android/phone/CdmaCallWaitingPreference.java b/src/com/android/phone/CdmaCallWaitingPreference.java
new file mode 100644
index 0000000..4cda7ba
--- /dev/null
+++ b/src/com/android/phone/CdmaCallWaitingPreference.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone;
+
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.Phone;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.TypedArray;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.util.AttributeSet;
+import android.util.Log;
+
+public class CdmaCallWaitingPreference extends Preference {
+ private static final String LOG_TAG = "CdmaCallWaitingPreference";
+ private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
+
+ private int mButtonClicked;
+ private Context mContext;
+ private Phone mPhone;
+ private SubscriptionInfoHelper mSubscriptionInfoHelper;
+ private TimeConsumingPreferenceListener mTcpListener;
+ private MyHandler mHandler = new MyHandler();
+
+ public CdmaCallWaitingPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mContext = context;
+ }
+
+ public CdmaCallWaitingPreference(Context context, AttributeSet attrs) {
+ this(context, attrs, com.android.internal.R.attr.preferenceStyle);
+ }
+
+ public CdmaCallWaitingPreference(Context context) {
+ this(context, null);
+ }
+
+ public void init(TimeConsumingPreferenceListener listener, Phone phone) {
+ mPhone = phone;
+ mTcpListener = listener;
+ Log.d(LOG_TAG, "phone id= " + mPhone.getPhoneId());
+ mPhone.getCallWaiting(mHandler.obtainMessage(MyHandler.MESSAGE_GET_CALL_WAITING,
+ MyHandler.MESSAGE_GET_CALL_WAITING, MyHandler.MESSAGE_GET_CALL_WAITING));
+ if (mTcpListener != null) {
+ mTcpListener.onStarted(this, true);
+ }
+ }
+
+ @Override
+ public void onClick() {
+ super.onClick();
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
+ builder.setTitle(mContext.getText(R.string.cdma_call_waiting));
+ builder.setMessage(mContext.getText(R.string.enable_cdma_call_waiting_setting));
+ builder.setPositiveButton(R.string.enable_cdma_cw, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ mPhone.setCallWaiting(true,
+ mHandler.obtainMessage(MyHandler.MESSAGE_SET_CALL_WAITING));
+ if (mTcpListener != null) {
+ mTcpListener.onStarted(CdmaCallWaitingPreference.this, false);
+ }
+ }
+ });
+ builder.setNegativeButton(R.string.disable_cdma_cw, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ mPhone.setCallWaiting(false,
+ mHandler.obtainMessage(MyHandler.MESSAGE_SET_CALL_WAITING));
+ if (mTcpListener != null) {
+ mTcpListener.onStarted(CdmaCallWaitingPreference.this, false);
+ }
+ }
+ });
+ builder.create().show();
+ }
+
+ private class MyHandler extends Handler {
+ static final int MESSAGE_GET_CALL_WAITING = 0;
+ static final int MESSAGE_SET_CALL_WAITING = 1;
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_GET_CALL_WAITING:
+ handleGetCallWaitingResponse(msg);
+ break;
+ case MESSAGE_SET_CALL_WAITING:
+ handleSetCallWaitingResponse(msg);
+ break;
+ }
+ }
+
+ private void handleGetCallWaitingResponse(Message msg) {
+ AsyncResult ar = (AsyncResult) msg.obj;
+
+ if (mTcpListener != null) {
+ if (msg.arg2 == MESSAGE_SET_CALL_WAITING) {
+ mTcpListener.onFinished(CdmaCallWaitingPreference.this, false);
+ } else {
+ mTcpListener.onFinished(CdmaCallWaitingPreference.this, true);
+ }
+ }
+
+ if (ar.exception instanceof CommandException) {
+ if (DBG) {
+ Log.d(LOG_TAG, "handleGetCallWaitingResponse: CommandException=" +
+ ar.exception);
+ }
+ if (mTcpListener != null) {
+ mTcpListener.onException(CdmaCallWaitingPreference.this,
+ (CommandException)ar.exception);
+ }
+ } else if (ar.userObj instanceof Throwable || ar.exception != null) {
+ if (DBG) {
+ Log.d(LOG_TAG, "handleGetCallWaitingResponse: Exception" + ar.exception);
+ }
+ if (mTcpListener != null) {
+ mTcpListener.onError(CdmaCallWaitingPreference.this,
+ TimeConsumingPreferenceActivity.RESPONSE_ERROR);
+ }
+ } else {
+ if (DBG) {
+ Log.d(LOG_TAG, "handleGetCallWaitingResponse: CW state successfully queried.");
+ }
+ int[] cwArray = (int[])ar.result;
+ if (cwArray == null) {
+ if (mTcpListener != null) {
+ mTcpListener.onError(CdmaCallWaitingPreference.this,
+ TimeConsumingPreferenceActivity.RESPONSE_ERROR);
+ }
+ return;
+ }
+
+ try {
+ if (cwArray[0] == CommandsInterface.SS_STATUS_UNKNOWN) {
+ setSummary("");
+ } else if(cwArray[0] == 1) {
+ setSummary(mContext.getString(R.string.cdma_call_waiting_in_ims_on));
+ } else if(cwArray[0] == 0) {
+ setSummary(mContext.getString(R.string.cdma_call_waiting_in_ims_off));
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ setSummary("");
+ Log.e(LOG_TAG, "handleGetCallWaitingResponse: improper result: err ="
+ + e.getMessage());
+ }
+ }
+ }
+
+ private void handleSetCallWaitingResponse(Message msg) {
+ AsyncResult ar = (AsyncResult) msg.obj;
+
+ if (ar.exception != null) {
+ if (DBG) {
+ Log.d(LOG_TAG, "handleSetCallWaitingResponse: ar.exception=" + ar.exception);
+ }
+ }
+
+ if (ar.result != null) {
+ int arr = (int)ar.result;
+ if (arr == CommandsInterface.SS_STATUS_UNKNOWN) {
+ Log.d(LOG_TAG, "handleSetCallWaitingResponse: no need to re get in CDMA");
+ mTcpListener.onFinished(CdmaCallWaitingPreference.this, false);
+ return;
+ }
+ }
+
+ if (DBG) Log.d(LOG_TAG, "handleSetCallWaitingResponse: re get");
+ mPhone.getCallWaiting(obtainMessage(MESSAGE_GET_CALL_WAITING,
+ MESSAGE_SET_CALL_WAITING, MESSAGE_SET_CALL_WAITING, ar.exception));
+ }
+ }
+}
diff --git a/src/com/android/phone/EditPhoneNumberPreference.java b/src/com/android/phone/EditPhoneNumberPreference.java
index 74b8a45..505c284 100644
--- a/src/com/android/phone/EditPhoneNumberPreference.java
+++ b/src/com/android/phone/EditPhoneNumberPreference.java
@@ -16,6 +16,9 @@
package com.android.phone;
+import static android.view.View.LAYOUT_DIRECTION_LOCALE;
+import static android.view.View.TEXT_DIRECTION_LOCALE;
+
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
@@ -37,6 +40,8 @@
import android.widget.ImageButton;
import android.widget.TextView;
+import com.android.internal.telephony.CommandsInterface;
+
public class EditPhoneNumberPreference extends EditTextPreference {
//allowed modes for this preference.
@@ -90,6 +95,7 @@
private String mPhoneNumber;
private boolean mChecked;
+ private boolean mIsUnknownStatus;
/**
* Interface for the dialog closed listener, related to
@@ -209,7 +215,9 @@
}
}
editText.setText(BidiFormatter.getInstance().unicodeWrap(
- mPhoneNumber, TextDirectionHeuristics.LTR));
+ mPhoneNumber, TextDirectionHeuristics.LOCALE));
+ editText.setTextDirection(TEXT_DIRECTION_LOCALE);
+ editText.setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
editText.setMovementMethod(ArrowKeyMovementMethod.getInstance());
editText.setKeyListener(DialerKeyListener.getInstance());
editText.setOnFocusChangeListener(mDialogFocusChangeListener);
@@ -254,7 +262,13 @@
// displayed, since there is no need to hide the edittext
// field anymore.
if (mConfirmationMode == CM_ACTIVATION) {
- if (mChecked) {
+ if (mIsUnknownStatus) {
+ builder.setPositiveButton(mEnableText, this);
+ builder.setNeutralButton(mDisableText, this);
+ if (mPrefId == CommandsInterface.CF_REASON_ALL) {
+ builder.setPositiveButton(null, null);
+ }
+ } else if (mChecked) {
builder.setPositiveButton(mChangeNumberText, this);
builder.setNeutralButton(mDisableText, this);
} else {
@@ -310,7 +324,8 @@
@Override
public void onClick(DialogInterface dialog, int which) {
// The neutral button (button3) is always the toggle.
- if ((mConfirmationMode == CM_ACTIVATION) && (which == DialogInterface.BUTTON_NEUTRAL)) {
+ if ((mConfirmationMode == CM_ACTIVATION) && (which == DialogInterface.BUTTON_NEUTRAL)
+ && !mIsUnknownStatus) {
//flip the toggle if we are in the correct mode.
setToggled(!isToggled());
}
@@ -499,4 +514,12 @@
public void showPhoneNumberDialog() {
showDialog(null);
}
+
+ public void setUnknownStatus(boolean isUnknown) {
+ mIsUnknownStatus = isUnknown;
+ }
+
+ public boolean isUnknownStatus() {
+ return mIsUnknownStatus;
+ }
}
diff --git a/src/com/android/phone/EmergencyCallbackModeService.java b/src/com/android/phone/EmergencyCallbackModeService.java
index c7e4cc7..6b247bd 100644
--- a/src/com/android/phone/EmergencyCallbackModeService.java
+++ b/src/com/android/phone/EmergencyCallbackModeService.java
@@ -30,6 +30,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
+import android.os.UserHandle;
import android.sysprop.TelephonyProperties;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -106,7 +107,8 @@
mPhone.unregisterForEcmTimerReset(mHandler);
// Cancel the notification and timer
- mNotificationManager.cancel(R.string.phone_in_ecm_notification_title);
+ mNotificationManager.cancelAsUser(null, R.string.phone_in_ecm_notification_title,
+ UserHandle.ALL);
mTimer.cancel();
}
}
@@ -221,7 +223,8 @@
builder.setChannelId(NotificationChannelController.CHANNEL_ID_ALERT);
// Show notification
- mNotificationManager.notify(R.string.phone_in_ecm_notification_title, builder.build());
+ mNotificationManager.notifyAsUser(null, R.string.phone_in_ecm_notification_title,
+ builder.build(), UserHandle.ALL);
}
/**
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index fa996a6..59a05c7 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -2285,16 +2285,7 @@
}
@Override
- public String getNetworkCountryIsoForPhone(int phoneId, String callingPackage,
- String callingFeatureId) {
- if (!TextUtils.isEmpty(callingPackage)) {
- final int subId = mSubscriptionController.getSubIdUsingPhoneId(phoneId);
- if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, subId, callingPackage,
- callingFeatureId, "getNetworkCountryIsoForPhone")) {
- return "";
- }
- }
-
+ public String getNetworkCountryIsoForPhone(int phoneId) {
// Reporting the correct network country is ambiguous when IWLAN could conflict with
// registered cell info, so return a NULL country instead.
final long identity = Binder.clearCallingIdentity();
@@ -3053,14 +3044,15 @@
}
@Override
- public void sendVisualVoicemailSmsForSubscriber(String callingPackage, int subId,
- String number, int port, String text, PendingIntent sentIntent) {
+ public void sendVisualVoicemailSmsForSubscriber(String callingPackage,
+ String callingAttributionTag, int subId, String number, int port, String text,
+ PendingIntent sentIntent) {
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
enforceVisualVoicemailPackage(callingPackage, subId);
enforceSendSmsPermission();
SmsController smsController = PhoneFactory.getSmsController();
- smsController.sendVisualVoicemailSmsForSubscriber(callingPackage, subId, number, port, text,
- sentIntent);
+ smsController.sendVisualVoicemailSmsForSubscriber(callingPackage, callingAttributionTag,
+ subId, number, port, text, sentIntent);
}
/**
diff --git a/src/com/android/phone/settings/SuppServicesUiUtil.java b/src/com/android/phone/settings/SuppServicesUiUtil.java
index 4e9841f..4f1a79f 100644
--- a/src/com/android/phone/settings/SuppServicesUiUtil.java
+++ b/src/com/android/phone/settings/SuppServicesUiUtil.java
@@ -84,7 +84,7 @@
.create();
}
- private static String makeMessage(Context context, String preferenceKey, Phone phone) {
+ public static String makeMessage(Context context, String preferenceKey, Phone phone) {
String message = "";
int simSlot = (phone.getPhoneId() == 0) ? 1 : 2;
String suppServiceName = getSuppServiceName(context, preferenceKey);
diff --git a/src/com/android/services/telephony/MmiCodeUtil.java b/src/com/android/services/telephony/MmiCodeUtil.java
new file mode 100644
index 0000000..d208ec3
--- /dev/null
+++ b/src/com/android/services/telephony/MmiCodeUtil.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.services.telephony;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class MmiCodeUtil {
+ //***** Constants
+
+ // Supp Service codes from TS 22.030 Annex B
+
+ //Called line presentation
+ static final String SC_CLIP = "30";
+ static final String SC_CLIR = "31";
+
+ // Call Forwarding
+ static final String SC_CFU = "21";
+ static final String SC_CFB = "67";
+ static final String SC_CFNRy = "61";
+ static final String SC_CFNR = "62";
+
+ static final String SC_CF_All = "002";
+ static final String SC_CF_All_Conditional = "004";
+
+ // Call Waiting
+ static final String SC_WAIT = "43";
+
+ // Call Barring
+ static final String SC_BAOC = "33";
+ static final String SC_BAOIC = "331";
+ static final String SC_BAOICxH = "332";
+ static final String SC_BAIC = "35";
+ static final String SC_BAICr = "351";
+
+ static final String SC_BA_ALL = "330";
+ static final String SC_BA_MO = "333";
+ static final String SC_BA_MT = "353";
+
+ // Supp Service Password registration
+ static final String SC_PWD = "03";
+
+ // PIN/PIN2/PUK/PUK2
+ static final String SC_PIN = "04";
+ static final String SC_PIN2 = "042";
+ static final String SC_PUK = "05";
+ static final String SC_PUK2 = "052";
+
+ // See TS 22.030 6.5.2 "Structure of the MMI"
+
+ static Pattern sPatternSuppService = Pattern.compile(
+ "((\\*|#|\\*#|\\*\\*|##)(\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?#)(.*)");
+/* 1 2 3 4 5 6 7 8 9 10 11 12
+
+ 1 = Full string up to and including #
+ 2 = action (activation/interrogation/registration/erasure)
+ 3 = service code
+ 5 = SIA
+ 7 = SIB
+ 9 = SIC
+ 10 = dialing number
+*/
+
+ static final int MATCH_GROUP_SERVICE_CODE = 3;
+
+ public static final String BUTTON_CLIR_KEY = "button_clir_key";
+ public static final String BUTTON_CW_KEY = "button_cw_key";
+ public static final String CALL_FORWARDING_KEY = "call_forwarding_key";
+ public static final String CALL_BARRING_KEY = "call_barring_key";
+
+ //***** Public Class methods
+ public static String getMmiServiceCode(String dialString) {
+ Matcher m;
+ String ret = null;
+
+ m = sPatternSuppService.matcher(dialString);
+
+ if (m.matches()) {
+ ret = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE));
+ }
+
+ return ret;
+ }
+
+ private static String makeEmptyNull(String s) {
+ if (s != null && s.length() == 0) return null;
+
+ return s;
+ }
+
+ static boolean isServiceCodeCallForwarding(String sc) {
+ return sc != null &&
+ (sc.equals(SC_CFU)
+ || sc.equals(SC_CFB) || sc.equals(SC_CFNRy)
+ || sc.equals(SC_CFNR) || sc.equals(SC_CF_All)
+ || sc.equals(SC_CF_All_Conditional));
+ }
+
+ static boolean isServiceCodeCallBarring(String sc) {
+ return sc != null &&
+ (sc.equals(SC_BAOC)
+ || sc.equals(SC_BAOIC) || sc.equals(SC_BAOICxH)
+ || sc.equals(SC_BAIC) || sc.equals(SC_BAICr)
+ || sc.equals(SC_BA_ALL) || sc.equals(SC_BA_MO)
+ || sc.equals(SC_BA_MT));
+ }
+
+ static boolean isPinPukCommand(String sc) {
+ return sc != null && (sc.equals(SC_PIN) || sc.equals(SC_PIN2)
+ || sc.equals(SC_PUK) || sc.equals(SC_PUK2));
+ }
+
+ public static String getSuppServiceKey(String dialString) {
+ String sc = getMmiServiceCode(dialString);
+ if (sc != null && sc.equals(SC_CLIP)) {
+ return "";
+ } else if (sc != null && sc.equals(SC_CLIR)) {
+ return BUTTON_CLIR_KEY;
+ } else if (isServiceCodeCallForwarding(sc)) {
+ return CALL_FORWARDING_KEY;
+ } else if (isServiceCodeCallBarring(sc)) {
+ return CALL_BARRING_KEY;
+ } else if (sc != null && sc.equals(SC_PWD)) {
+ return "";
+ } else if (sc != null && sc.equals(SC_WAIT)) {
+ return BUTTON_CW_KEY;
+ } else if (isPinPukCommand(sc)) {
+ return "";
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/src/com/android/services/telephony/RadioOnHelper.java b/src/com/android/services/telephony/RadioOnHelper.java
index 116c61d..25ac220 100644
--- a/src/com/android/services/telephony/RadioOnHelper.java
+++ b/src/com/android/services/telephony/RadioOnHelper.java
@@ -77,7 +77,8 @@
* RadioOnHelper's handler (thus ensuring that the rest of the sequence is entirely
* serialized, and runs on the main looper.)
*/
- public void triggerRadioOnAndListen(RadioOnStateListener.Callback callback) {
+ public void triggerRadioOnAndListen(RadioOnStateListener.Callback callback,
+ boolean forEmergencyCall, Phone phoneForEmergencyCall) {
setupListeners();
mCallback = callback;
mInProgressListeners.clear();
@@ -89,16 +90,17 @@
}
mInProgressListeners.add(mListeners.get(i));
- mListeners.get(i).waitForRadioOn(phone, this);
+ mListeners.get(i).waitForRadioOn(phone, this, forEmergencyCall,
+ forEmergencyCall && phone == phoneForEmergencyCall);
}
- powerOnRadio();
+ powerOnRadio(forEmergencyCall, phoneForEmergencyCall);
}
/**
* Attempt to power on the radio (i.e. take the device out of airplane mode). We'll eventually
* get an onServiceStateChanged() callback when the radio successfully comes up.
*/
- private void powerOnRadio() {
+ private void powerOnRadio(boolean forEmergencyCall, Phone phoneForEmergencyCall) {
// If airplane mode is on, we turn it off the same way that the Settings activity turns it
// off.
@@ -112,7 +114,7 @@
for (Phone phone : PhoneFactory.getPhones()) {
Log.d(this, "powerOnRadio, enabling Radio");
- phone.setRadioPower(true);
+ phone.setRadioPower(true, forEmergencyCall, phone == phoneForEmergencyCall, false);
}
// Post the broadcast intend for change in airplane mode
diff --git a/src/com/android/services/telephony/RadioOnStateListener.java b/src/com/android/services/telephony/RadioOnStateListener.java
index 53f97f0..93e1e3c 100644
--- a/src/com/android/services/telephony/RadioOnStateListener.java
+++ b/src/com/android/services/telephony/RadioOnStateListener.java
@@ -70,7 +70,10 @@
Phone phone = (Phone) args.arg1;
RadioOnStateListener.Callback callback =
(RadioOnStateListener.Callback) args.arg2;
- startSequenceInternal(phone, callback);
+ boolean forEmergencyCall = (boolean) args.arg3;
+ boolean isSelectedPhoneForEmergencyCall = (boolean) args.arg4;
+ startSequenceInternal(phone, callback, forEmergencyCall,
+ isSelectedPhoneForEmergencyCall);
} finally {
args.recycle();
}
@@ -97,6 +100,10 @@
private Callback mCallback; // The callback to notify upon completion.
private Phone mPhone; // The phone that will attempt to place the call.
+ private boolean mForEmergencyCall; // Whether radio is being turned on for emergency call.
+ // Whether this phone is selected to place emergency call. Can be true only if
+ // mForEmergencyCall is true.
+ private boolean mSelectedPhoneForEmergencyCall;
private int mNumRetriesSoFar;
/**
@@ -113,7 +120,8 @@
* RadioOnStateListener's handler (thus ensuring that the rest of the sequence is entirely
* serialized, and runs only on the handler thread.)
*/
- public void waitForRadioOn(Phone phone, Callback callback) {
+ public void waitForRadioOn(Phone phone, Callback callback,
+ boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall) {
Log.d(this, "waitForRadioOn: Phone " + phone.getPhoneId());
if (mPhone != null) {
@@ -124,6 +132,8 @@
SomeArgs args = SomeArgs.obtain();
args.arg1 = phone;
args.arg2 = callback;
+ args.arg3 = forEmergencyCall;
+ args.arg4 = isSelectedPhoneForEmergencyCall;
mHandler.obtainMessage(MSG_START_SEQUENCE, args).sendToTarget();
}
@@ -132,7 +142,8 @@
*
* @see #waitForRadioOn
*/
- private void startSequenceInternal(Phone phone, Callback callback) {
+ private void startSequenceInternal(Phone phone, Callback callback,
+ boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall) {
Log.d(this, "startSequenceInternal: Phone " + phone.getPhoneId());
// First of all, clean up any state left over from a prior RadioOn call sequence. This
@@ -142,6 +153,8 @@
mPhone = phone;
mCallback = callback;
+ mForEmergencyCall = forEmergencyCall;
+ mSelectedPhoneForEmergencyCall = isSelectedPhoneForEmergencyCall;
registerForServiceStateChanged();
// Register for RADIO_OFF to handle cases where emergency call is dialed before
@@ -234,7 +247,8 @@
cleanup();
} else {
Log.d(this, "Trying (again) to turn on the radio.");
- mPhone.setRadioPower(true);
+ mPhone.setRadioPower(true, mForEmergencyCall, mSelectedPhoneForEmergencyCall,
+ false);
startRetryTimer();
}
}
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 854d935..665bf22 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -17,10 +17,13 @@
package com.android.services.telephony;
import android.annotation.NonNull;
+import android.app.AlertDialog;
+import android.app.Dialog;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
@@ -46,6 +49,7 @@
import android.telephony.emergency.EmergencyNumber;
import android.text.TextUtils;
import android.util.Pair;
+import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Call;
@@ -65,6 +69,7 @@
import com.android.phone.MMIDialogActivity;
import com.android.phone.PhoneUtils;
import com.android.phone.R;
+import com.android.phone.settings.SuppServicesUiUtil;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -430,6 +435,14 @@
}
/**
+ * Overrides radioOnHelper for testing.
+ */
+ @VisibleForTesting
+ public void setRadioOnHelper(RadioOnHelper radioOnHelper) {
+ mRadioOnHelper = radioOnHelper;
+ }
+
+ /**
* Overrides PhoneSwitcher dependencies for testing.
*/
@VisibleForTesting
@@ -772,13 +785,16 @@
boolean needToTurnOnRadio = (isEmergencyNumber && (!isRadioOn() || isAirplaneModeOn))
|| isRadioPowerDownOnBluetooth();
+ // Get the right phone object from the account data passed in.
+ final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber,
+ /* Note: when not an emergency, handle can be null for unknown callers */
+ handle == null ? null : handle.getSchemeSpecificPart());
+
if (needToTurnOnRadio) {
final Uri resultHandle = handle;
- // By default, Connection based on the default Phone, since we need to return to Telecom
- // now.
- final int originalPhoneType = mPhoneFactoryProxy.getDefaultPhone().getPhoneType();
+ final int originalPhoneType = phone.getPhoneType();
final Connection resultConnection = getTelephonyConnection(request, numberToDial,
- isEmergencyNumber, resultHandle, PhoneFactory.getDefaultPhone());
+ isEmergencyNumber, resultHandle, phone);
if (mRadioOnHelper == null) {
mRadioOnHelper = new RadioOnHelper(this);
}
@@ -786,7 +802,7 @@
@Override
public void onComplete(RadioOnStateListener listener, boolean isRadioReady) {
handleOnComplete(isRadioReady, isEmergencyNumber, resultConnection, request,
- numberToDial, resultHandle, originalPhoneType);
+ numberToDial, resultHandle, originalPhoneType, phone);
}
@Override
@@ -815,7 +831,7 @@
|| serviceState == ServiceState.STATE_IN_SERVICE;
}
}
- });
+ }, isEmergencyNumber && !isTestEmergencyNumber, phone);
// Return the still unconnected GsmConnection and wait for the Radios to boot before
// connecting it to the underlying Phone.
return resultConnection;
@@ -831,10 +847,6 @@
"Add call restricted due to ongoing video call"));
}
- // Get the right phone object from the account data passed in.
- final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber,
- /* Note: when not an emergency, handle can be null for unknown callers */
- handle == null ? null : handle.getSchemeSpecificPart());
if (!isEmergencyNumber) {
final Connection resultConnection = getTelephonyConnection(request, numberToDial,
false, handle, phone);
@@ -911,18 +923,21 @@
*/
private void handleOnComplete(boolean isRadioReady, boolean isEmergencyNumber,
Connection originalConnection, ConnectionRequest request, String numberToDial,
- Uri handle, int originalPhoneType) {
+ Uri handle, int originalPhoneType, Phone phone) {
// Make sure the Call has not already been canceled by the user.
if (originalConnection.getState() == Connection.STATE_DISCONNECTED) {
Log.i(this, "Call disconnected before the outgoing call was placed. Skipping call "
+ "placement.");
+ if (isEmergencyNumber) {
+ // If call is already canceled by the user, notify modem to exit emergency call
+ // mode by sending radio on with forEmergencyCall=false.
+ for (Phone curPhone : mPhoneFactoryProxy.getPhones()) {
+ curPhone.setRadioPower(true, false, false, true);
+ }
+ }
return;
}
- // Get the right phone object since the radio has been turned on successfully.
if (isRadioReady) {
- final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber,
- /* Note: when not an emergency, handle can be null for unknown callers */
- handle == null ? null : handle.getSchemeSpecificPart());
if (!isEmergencyNumber) {
adjustAndPlaceOutgoingConnection(phone, originalConnection, request, numberToDial,
handle, originalPhoneType, false);
@@ -1640,6 +1655,12 @@
TelephonyConnection connection, Phone phone, int videoState, Bundle extras) {
String number = connection.getAddress().getSchemeSpecificPart();
+ if (showDataDialog(phone, number)) {
+ connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
+ android.telephony.DisconnectCause.DIALED_MMI, "UT is not available"));
+ return;
+ }
+
com.android.internal.telephony.Connection originalConnection = null;
try {
if (phone != null) {
@@ -2347,6 +2368,78 @@
}
}
+ private boolean showDataDialog(Phone phone, String number) {
+ boolean ret = false;
+ final Context context = getApplicationContext();
+ String suppKey = MmiCodeUtil.getSuppServiceKey(number);
+ if (suppKey != null) {
+ boolean clirOverUtPrecautions = false;
+ boolean cfOverUtPrecautions = false;
+ boolean cbOverUtPrecautions = false;
+ boolean cwOverUtPrecautions = false;
+
+ CarrierConfigManager cfgManager = (CarrierConfigManager)
+ phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (cfgManager != null) {
+ clirOverUtPrecautions = cfgManager.getConfigForSubId(phone.getSubId())
+ .getBoolean(CarrierConfigManager.KEY_CALLER_ID_OVER_UT_WARNING_BOOL);
+ cfOverUtPrecautions = cfgManager.getConfigForSubId(phone.getSubId())
+ .getBoolean(CarrierConfigManager.KEY_CALL_FORWARDING_OVER_UT_WARNING_BOOL);
+ cbOverUtPrecautions = cfgManager.getConfigForSubId(phone.getSubId())
+ .getBoolean(CarrierConfigManager.KEY_CALL_BARRING_OVER_UT_WARNING_BOOL);
+ cwOverUtPrecautions = cfgManager.getConfigForSubId(phone.getSubId())
+ .getBoolean(CarrierConfigManager.KEY_CALL_WAITING_OVER_UT_WARNING_BOOL);
+ }
+
+ boolean isSsOverUtPrecautions = SuppServicesUiUtil
+ .isSsOverUtPrecautions(context, phone);
+ if (isSsOverUtPrecautions) {
+ boolean showDialog = false;
+ if (suppKey == MmiCodeUtil.BUTTON_CLIR_KEY && clirOverUtPrecautions) {
+ showDialog = true;
+ } else if (suppKey == MmiCodeUtil.CALL_FORWARDING_KEY && cfOverUtPrecautions) {
+ showDialog = true;
+ } else if (suppKey == MmiCodeUtil.CALL_BARRING_KEY && cbOverUtPrecautions) {
+ showDialog = true;
+ } else if (suppKey == MmiCodeUtil.BUTTON_CW_KEY && cwOverUtPrecautions) {
+ showDialog = true;
+ }
+
+ if (showDialog) {
+ Log.d(this, "Creating UT Data enable dialog");
+ String message = SuppServicesUiUtil.makeMessage(context, suppKey, phone);
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ DialogInterface.OnClickListener networkSettingsClickListener =
+ new Dialog.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ ComponentName mobileNetworkSettingsComponent
+ = new ComponentName(
+ context.getString(
+ R.string.mobile_network_settings_package),
+ context.getString(
+ R.string.mobile_network_settings_class));
+ intent.setComponent(mobileNetworkSettingsComponent);
+ context.startActivity(intent);
+ }
+ };
+ Dialog dialog = builder.setMessage(message)
+ .setNeutralButton(context.getResources().getString(
+ R.string.settings_label),
+ networkSettingsClickListener)
+ .setPositiveButton(context.getResources().getString(
+ R.string.supp_service_over_ut_precautions_dialog_dismiss), null)
+ .create();
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ dialog.show();
+ ret = true;
+ }
+ }
+ }
+ return ret;
+ }
+
/**
* Adds a {@link Conference} to the telephony ConnectionService and registers a listener for
* changes to the conference. Should be used instead of {@link #addConference(Conference)}.
diff --git a/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java b/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
index 073aa2c..25df848 100644
--- a/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
+++ b/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
@@ -83,7 +83,7 @@
@SmallTest
public void testRegisterForCallback() {
mMockPhone.mCi = mMockCi;
- mListener.waitForRadioOn(mMockPhone, mCallback);
+ mListener.waitForRadioOn(mMockPhone, mCallback, false, false);
waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
@@ -105,10 +105,11 @@
public void testPhoneChangeState_OkToCallTrue() {
ServiceState state = new ServiceState();
state.setState(ServiceState.STATE_IN_SERVICE);
+ when(mMockPhone.getServiceState()).thenReturn(state);
when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
when(mCallback.isOkToCall(eq(mMockPhone), anyInt())).thenReturn(true);
mMockPhone.mCi = mMockCi;
- mListener.waitForRadioOn(mMockPhone, mCallback);
+ mListener.waitForRadioOn(mMockPhone, mCallback, false, false);
waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
mListener.getHandler().obtainMessage(RadioOnStateListener.MSG_SERVICE_STATE_CHANGED,
@@ -132,7 +133,7 @@
when(mCallback.isOkToCall(eq(mMockPhone), anyInt())).thenReturn(false);
when(mMockPhone.getServiceState()).thenReturn(state);
mMockPhone.mCi = mMockCi;
- mListener.waitForRadioOn(mMockPhone, mCallback);
+ mListener.waitForRadioOn(mMockPhone, mCallback, false, false);
waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
mListener.getHandler().obtainMessage(RadioOnStateListener.MSG_SERVICE_STATE_CHANGED,
@@ -161,10 +162,32 @@
// Wait for the timer to expire and check state manually in onRetryTimeout
mMockPhone.mCi = mMockCi;
- mListener.waitForRadioOn(mMockPhone, mCallback);
+ mListener.waitForRadioOn(mMockPhone, mCallback, false, false);
waitForHandlerActionDelayed(mListener.getHandler(), TIMEOUT_MS, TIMEOUT_MS /*delay*/);
verify(mCallback).onComplete(eq(mListener), eq(false));
- verify(mMockPhone, times(2)).setRadioPower(eq(true));
+ verify(mMockPhone, times(2)).setRadioPower(eq(true),
+ eq(false), eq(false), eq(false));
+ }
+
+ @Test
+ @SmallTest
+ public void testTimeout_RetryFailure_ForEmergency() {
+ ServiceState state = new ServiceState();
+ state.setState(ServiceState.STATE_POWER_OFF);
+ when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
+ when(mMockPhone.getServiceState()).thenReturn(state);
+ when(mCallback.isOkToCall(eq(mMockPhone), anyInt())).thenReturn(false);
+ mListener.setTimeBetweenRetriesMillis(0/*ms*/);
+ mListener.setMaxNumRetries(2);
+
+ // Wait for the timer to expire and check state manually in onRetryTimeout
+ mMockPhone.mCi = mMockCi;
+ mListener.waitForRadioOn(mMockPhone, mCallback, true, true);
+ waitForHandlerActionDelayed(mListener.getHandler(), TIMEOUT_MS, TIMEOUT_MS /*delay*/);
+
+ verify(mCallback).onComplete(eq(mListener), eq(false));
+ verify(mMockPhone, times(2)).setRadioPower(eq(true),
+ eq(true), eq(true), eq(false));
}
}
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 6e11e51..2060e6f 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -16,7 +16,10 @@
package com.android.services.telephony;
+import static com.android.internal.telephony.RILConstants.GSM_PHONE;
+
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
@@ -26,6 +29,7 @@
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -58,6 +62,7 @@
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneSwitcher;
+import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import com.android.internal.telephony.gsm.SuppServiceNotification;
@@ -93,6 +98,7 @@
private static final PhoneAccountHandle PHONE_ACCOUNT_HANDLE_2 = new PhoneAccountHandle(
TEST_COMPONENT_NAME, TEST_ACCOUNT_ID2);
private static final Uri TEST_ADDRESS = Uri.parse("tel:+16505551212");
+ private android.telecom.Connection mConnection;
@Mock TelephonyConnectionService.TelephonyManagerProxy mTelephonyManagerProxy;
@Mock TelephonyConnectionService.SubscriptionManagerProxy mSubscriptionManagerProxy;
@@ -106,6 +112,8 @@
@Mock Handler mMockHandler;
@Mock EmergencyNumberTracker mEmergencyNumberTracker;
@Mock PhoneSwitcher mPhoneSwitcher;
+ @Mock RadioOnHelper mRadioOnHelper;
+ @Mock ServiceStateTracker mSST;
private static class TestTelephonyConnectionService extends TelephonyConnectionService {
@@ -135,6 +143,8 @@
doReturn(false).when(mDeviceState).shouldCheckSimStateBeforeOutgoingCall(any());
mTestConnectionService.setPhoneSwitcherProxy(mPhoneSwitcherProxy);
doReturn(mPhoneSwitcher).when(mPhoneSwitcherProxy).getPhoneSwitcher();
+ when(mPhoneNumberUtilsProxy.convertToEmergencyNumber(any(), anyString()))
+ .thenAnswer(invocation -> invocation.getArgument(1));
mTestConnectionService.setPhoneNumberUtilsProxy(mPhoneNumberUtilsProxy);
mTestConnectionService.setPhoneUtilsProxy(mPhoneUtilsProxy);
HandlerThread mockHandlerThread = mock(HandlerThread.class);
@@ -143,6 +153,7 @@
doReturn(mMockHandler).when(mHandlerFactory).createHandler(any());
mTestConnectionService.setHandlerFactory(mHandlerFactory);
mTestConnectionService.setDeviceState(mDeviceState);
+ mTestConnectionService.setRadioOnHelper(mRadioOnHelper);
doReturn(new DisconnectCause(DisconnectCause.UNKNOWN)).when(mDisconnectCauseFactory)
.toTelecomDisconnectCause(anyInt(), any());
doReturn(new DisconnectCause(DisconnectCause.UNKNOWN)).when(mDisconnectCauseFactory)
@@ -941,6 +952,64 @@
}
/**
+ * Test that the TelephonyConnectionService successfully turns radio on before placing the
+ * emergency call.
+ */
+ @Test
+ @SmallTest
+ public void testCreateOutgoingEmerge_exitingApm_disconnected() {
+ when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
+ Phone testPhone = setupConnectionServiceInApm();
+
+ ArgumentCaptor<RadioOnStateListener.Callback> callback =
+ ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
+ verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
+ eq(testPhone));
+
+ assertFalse(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+ when(mSST.isRadioOn()).thenReturn(true);
+ assertTrue(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+
+ mConnection.setDisconnected(null);
+ callback.getValue().onComplete(null, true);
+ for (Phone phone : mPhoneFactoryProxy.getPhones()) {
+ verify(phone).setRadioPower(true, false, false, true);
+ }
+ }
+
+ /**
+ * Test that the TelephonyConnectionService successfully turns radio on before placing the
+ * emergency call.
+ */
+ @Test
+ @SmallTest
+ public void testCreateOutgoingEmergencyConnection_exitingApm_placeCall() {
+ when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
+ Phone testPhone = setupConnectionServiceInApm();
+
+ ArgumentCaptor<RadioOnStateListener.Callback> callback =
+ ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
+ verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
+ eq(testPhone));
+
+ assertFalse(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+ when(mSST.isRadioOn()).thenReturn(true);
+ assertTrue(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+
+ callback.getValue().onComplete(null, true);
+ Runnable delayDialRunnable = verifyRunnablePosted();
+
+ try {
+ doAnswer(invocation -> null).when(mContext).startActivity(any());
+ delayDialRunnable.run();
+ verify(testPhone).dial(anyString(), any());
+ } catch (CallStateException e) {
+ // This shouldn't happen
+ fail();
+ }
+ }
+
+ /**
* Test that the TelephonyConnectionService does not perform a DDS switch when the carrier
* supports control-plane fallback.
*/
@@ -1115,9 +1184,49 @@
doReturn(emergencyNumbers).when(mTelephonyManagerProxy).getCurrentEmergencyNumberList();
doReturn(2).when(mTelephonyManagerProxy).getPhoneCount();
- android.telecom.Connection testConnection = mTestConnectionService
- .onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1, connectionRequest);
- assertNotNull("test connection was not set up correctly.", testConnection);
+ mConnection = mTestConnectionService.onCreateOutgoingConnection(
+ PHONE_ACCOUNT_HANDLE_1, connectionRequest);
+ assertNotNull("test connection was not set up correctly.", mConnection);
+
+ return testPhone0;
+ }
+
+
+ /**
+ * Set up a mock MSIM device with TEST_ADDRESS set as an emergency number in airplane mode.
+ * @return the Phone associated with slot 0.
+ */
+ private Phone setupConnectionServiceInApm() {
+ ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
+ .setAccountHandle(PHONE_ACCOUNT_HANDLE_1)
+ .setAddress(TEST_ADDRESS)
+ .build();
+ Phone testPhone0 = makeTestPhone(0 /*phoneId*/, ServiceState.STATE_POWER_OFF,
+ false /*isEmergencyOnly*/);
+ Phone testPhone1 = makeTestPhone(1 /*phoneId*/, ServiceState.STATE_POWER_OFF,
+ false /*isEmergencyOnly*/);
+ doReturn(GSM_PHONE).when(testPhone0).getPhoneType();
+ doReturn(GSM_PHONE).when(testPhone1).getPhoneType();
+ List<Phone> phones = new ArrayList<>(2);
+ doReturn(false).when(testPhone0).isRadioOn();
+ doReturn(false).when(testPhone1).isRadioOn();
+ phones.add(testPhone0);
+ phones.add(testPhone1);
+ setPhones(phones);
+ setupHandleToPhoneMap(PHONE_ACCOUNT_HANDLE_1, testPhone0);
+ setupDeviceConfig(testPhone0, testPhone1, 0);
+ doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(
+ TEST_ADDRESS.getSchemeSpecificPart());
+ HashMap<Integer, List<EmergencyNumber>> emergencyNumbers = new HashMap<>(1);
+ List<EmergencyNumber> numbers = new ArrayList<>();
+ numbers.add(setupEmergencyNumber(TEST_ADDRESS));
+ emergencyNumbers.put(0 /*subId*/, numbers);
+ doReturn(emergencyNumbers).when(mTelephonyManagerProxy).getCurrentEmergencyNumberList();
+ doReturn(2).when(mTelephonyManagerProxy).getPhoneCount();
+
+ mConnection = mTestConnectionService.onCreateOutgoingConnection(
+ PHONE_ACCOUNT_HANDLE_1, connectionRequest);
+ assertNotNull("test connection was not set up correctly.", mConnection);
return testPhone0;
}
@@ -1163,6 +1272,7 @@
when(phone.getPhoneId()).thenReturn(phoneId);
when(phone.getDefaultPhone()).thenReturn(phone);
when(phone.getEmergencyNumberTracker()).thenReturn(mEmergencyNumberTracker);
+ when(phone.getServiceStateTracker()).thenReturn(mSST);
when(mEmergencyNumberTracker.getEmergencyNumber(anyString())).thenReturn(null);
return phone;
}
@@ -1193,6 +1303,7 @@
private void setPhones(List<Phone> phones) {
when(mPhoneFactoryProxy.getPhones()).thenReturn(phones.toArray(new Phone[phones.size()]));
+ when(mPhoneFactoryProxy.getDefaultPhone()).thenReturn(phones.get(0));
}
private void setPhonesDialConnection(Phone phone, Connection c) {