Merge "MMI code: Handling MMI code on CDMA VoLTE"
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index b969ce4..997f06c 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -42,6 +42,7 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.preference.PreferenceManager;
import android.service.carrier.CarrierIdentifier;
import android.service.carrier.CarrierService;
@@ -102,8 +103,10 @@
private CarrierServiceConnection[] mServiceConnection;
// Whether we are bound to a service for each phone
private boolean[] mServiceBound;
- // Whether we have sent config change bcast for each phone id.
+ // Whether we have sent config change broadcast for each phone id.
private boolean[] mHasSentConfigChange;
+ // Whether the broadcast was sent from EVENT_SYSTEM_UNLOCKED, to track rebroadcasts
+ private boolean[] mFromSystemUnlocked;
// SubscriptionInfoUpdater
private final SubscriptionInfoUpdater mSubscriptionInfoUpdater;
@@ -199,11 +202,13 @@
case EVENT_SYSTEM_UNLOCKED: {
for (int i = 0; i < TelephonyManager.from(mContext).getActiveModemCount();
++i) {
- // When user unlock device, we should only try to send broadcast again if we
- // have sent it before unlock. This will avoid we try to load carrier config
- // when SIM is still loading when unlock happens.
+ // When the user unlocks the device, send the broadcast again (with a
+ // rebroadcast extra) if we have sent it before unlock. This will avoid
+ // trying to load the carrier config when the SIM is still loading when the
+ // unlock happens.
if (mHasSentConfigChange[i]) {
logdWithLocalLog("System unlocked");
+ mFromSystemUnlocked[i] = true;
updateConfigForPhoneId(i);
}
}
@@ -537,6 +542,7 @@
mServiceConnection = new CarrierServiceConnection[numPhones];
mServiceBound = new boolean[numPhones];
mHasSentConfigChange = new boolean[numPhones];
+ mFromSystemUnlocked = new boolean[numPhones];
// Make this service available through ServiceManager.
ServiceManager.addService(Context.CARRIER_CONFIG_SERVICE, this);
logd("CarrierConfigLoader has started");
@@ -634,9 +640,17 @@
}
}
intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, phoneId);
- logd("Broadcast CARRIER_CONFIG_CHANGED for phone " + phoneId);
- mContext.sendBroadcast(intent);
+ intent.putExtra(CarrierConfigManager.EXTRA_REBROADCAST_ON_UNLOCK,
+ mFromSystemUnlocked[phoneId]);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ int[] subIds = SubscriptionManager.getSubId(phoneId);
+ if (subIds != null && subIds.length > 0) {
+ logd("Broadcast CARRIER_CONFIG_CHANGED for phone " + phoneId + ", subId=" + subIds[0]);
+ } else {
+ logd("Broadcast CARRIER_CONFIG_CHANGED for phone " + phoneId);
+ }
mHasSentConfigChange[phoneId] = true;
+ mFromSystemUnlocked[phoneId] = false;
}
/** Binds to the default or carrier config app. */
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index 20a818e..c567806 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -87,7 +87,6 @@
private static final int THEME = com.android.internal.R.style.Theme_DeviceDefault_Dialog_Alert;
/** USSD information used to aggregate all USSD messages */
- private static AlertDialog sUssdDialog = null;
private static StringBuilder sUssdMsg = new StringBuilder();
private static final ComponentName PSTN_CONNECTION_SERVICE_COMPONENT =
@@ -524,39 +523,36 @@
// displaying system alert dialog on the screen instead of
// using another activity to display the message. This
// places the message at the forefront of the UI.
+ AlertDialog ussdDialog = new AlertDialog.Builder(context, THEME)
+ .setPositiveButton(R.string.ok, null)
+ .setCancelable(true)
+ .setOnDismissListener(new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ sUssdMsg.setLength(0);
+ }
+ })
+ .create();
- if (sUssdDialog == null) {
- sUssdDialog = new AlertDialog.Builder(context, THEME)
- .setPositiveButton(R.string.ok, null)
- .setCancelable(true)
- .setOnDismissListener(new DialogInterface.OnDismissListener() {
- @Override
- public void onDismiss(DialogInterface dialog) {
- sUssdMsg.setLength(0);
- }
- })
- .create();
+ ussdDialog.getWindow().setType(windowType);
+ ussdDialog.getWindow().addFlags(
+ WindowManager.LayoutParams.FLAG_DIM_BEHIND);
- sUssdDialog.getWindow().setType(windowType);
- sUssdDialog.getWindow().addFlags(
- WindowManager.LayoutParams.FLAG_DIM_BEHIND);
- }
if (sUssdMsg.length() != 0) {
- sUssdMsg
- .insert(0, "\n")
+ sUssdMsg.insert(0, "\n")
.insert(0, app.getResources().getString(R.string.ussd_dialog_sep))
.insert(0, "\n");
}
if (phone != null && phone.getCarrierName() != null) {
- sUssdDialog.setTitle(app.getResources().getString(R.string.carrier_mmi_msg_title,
+ ussdDialog.setTitle(app.getResources().getString(R.string.carrier_mmi_msg_title,
phone.getCarrierName()));
} else {
- sUssdDialog
+ ussdDialog
.setTitle(app.getResources().getString(R.string.default_carrier_mmi_msg_title));
}
sUssdMsg.insert(0, text);
- sUssdDialog.setMessage(sUssdMsg.toString());
- sUssdDialog.show();
+ ussdDialog.setMessage(sUssdMsg.toString());
+ ussdDialog.show();
}
/**
diff --git a/src/com/android/services/telephony/CdmaConferenceController.java b/src/com/android/services/telephony/CdmaConferenceController.java
index 9afcd0a..8523a5f 100644
--- a/src/com/android/services/telephony/CdmaConferenceController.java
+++ b/src/com/android/services/telephony/CdmaConferenceController.java
@@ -97,7 +97,7 @@
return;
}
- if (!mCdmaConnections.isEmpty() && connection.isOutgoing()) {
+ if (!mCdmaConnections.isEmpty() && connection.isOutgoingCall()) {
// There already exists a connection, so this will probably result in a conference once
// it is added. For outgoing connections which are added while another connection
// exists, we mark them as "dialing" for a set amount of time to give the user time to
@@ -183,7 +183,7 @@
isNewlyCreated = true;
}
- if (newConnection.isOutgoing()) {
+ if (newConnection.isOutgoingCall()) {
// Only an outgoing call can be merged with an ongoing call.
mConference.updateCapabilities(Connection.CAPABILITY_MERGE_CONFERENCE);
} else {
diff --git a/src/com/android/services/telephony/CdmaConnection.java b/src/com/android/services/telephony/CdmaConnection.java
index 36618c5..90e7663 100644
--- a/src/com/android/services/telephony/CdmaConnection.java
+++ b/src/com/android/services/telephony/CdmaConnection.java
@@ -81,9 +81,9 @@
Connection connection,
EmergencyTonePlayer emergencyTonePlayer,
boolean allowMute,
- boolean isOutgoing,
+ int callDirection,
String telecomCallId) {
- super(connection, telecomCallId, isOutgoing);
+ super(connection, telecomCallId, callDirection);
mEmergencyTonePlayer = emergencyTonePlayer;
mAllowMute = allowMute;
mIsCallWaiting = connection != null && connection.getState() == Call.State.WAITING;
@@ -151,7 +151,7 @@
@Override
public TelephonyConnection cloneConnection() {
CdmaConnection cdmaConnection = new CdmaConnection(getOriginalConnection(),
- mEmergencyTonePlayer, mAllowMute, mIsOutgoing, getTelecomCallId());
+ mEmergencyTonePlayer, mAllowMute, getCallDirection(), getTelecomCallId());
return cdmaConnection;
}
@@ -202,10 +202,6 @@
}
}
- boolean isOutgoing() {
- return mIsOutgoing;
- }
-
boolean isCallWaiting() {
return mIsCallWaiting;
}
@@ -305,7 +301,7 @@
private void handleCdmaConnectionTimeReset() {
boolean isImsCall = getOriginalConnection() instanceof ImsPhoneConnection;
- if (!isImsCall && !mIsConnectionTimeReset && mIsOutgoing
+ if (!isImsCall && !mIsConnectionTimeReset && isOutgoingCall()
&& getOriginalConnection() != null
&& getOriginalConnection().getState() == Call.State.ACTIVE
&& getOriginalConnection().getDurationMillis() > 0) {
diff --git a/src/com/android/services/telephony/GsmConnection.java b/src/com/android/services/telephony/GsmConnection.java
index 47434c0..769ec22 100644
--- a/src/com/android/services/telephony/GsmConnection.java
+++ b/src/com/android/services/telephony/GsmConnection.java
@@ -22,8 +22,8 @@
* Manages a single phone call handled by GSM.
*/
final class GsmConnection extends TelephonyConnection {
- GsmConnection(Connection connection, String telecomCallId, boolean isOutgoing) {
- super(connection, telecomCallId, isOutgoing);
+ GsmConnection(Connection connection, String telecomCallId, int callDirection) {
+ super(connection, telecomCallId, callDirection);
}
/**
@@ -35,9 +35,7 @@
*/
@Override
public TelephonyConnection cloneConnection() {
- GsmConnection gsmConnection = new GsmConnection(getOriginalConnection(),
- getTelecomCallId(), mIsOutgoing);
- return gsmConnection;
+ return new GsmConnection(getOriginalConnection(), getTelecomCallId(), getCallDirection());
}
/** {@inheritDoc} */
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index c11a1ca..f7fcf64 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -20,7 +20,6 @@
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
-import android.os.PersistableBundle;
import android.telecom.Connection;
import android.telecom.Connection.VideoProvider;
import android.telecom.DisconnectCause;
@@ -28,7 +27,6 @@
import android.telecom.StatusHints;
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
-import android.telephony.CarrierConfigManager;
import android.telephony.PhoneNumberUtils;
import android.util.Pair;
@@ -38,7 +36,6 @@
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
-import com.android.phone.PhoneGlobals;
import com.android.phone.PhoneUtils;
import com.android.phone.R;
import com.android.telephony.Rlog;
@@ -79,6 +76,126 @@
}
/**
+ * Abstracts out carrier configuration items specific to the conference.
+ */
+ public static class CarrierConfiguration {
+ /**
+ * Builds and instance of {@link CarrierConfiguration}.
+ */
+ public static class Builder {
+ private boolean mIsMaximumConferenceSizeEnforced = false;
+ private int mMaximumConferenceSize = 5;
+ private boolean mShouldLocalDisconnectEmptyConference = false;
+ private boolean mIsHoldAllowed = false;
+
+ /**
+ * Sets whether the maximum size of the conference is enforced.
+ * @param isMaximumConferenceSizeEnforced {@code true} if conference size enforced.
+ * @return builder instance.
+ */
+ public Builder setIsMaximumConferenceSizeEnforced(
+ boolean isMaximumConferenceSizeEnforced) {
+ mIsMaximumConferenceSizeEnforced = isMaximumConferenceSizeEnforced;
+ return this;
+ }
+
+ /**
+ * Sets the maximum size of an IMS conference.
+ * @param maximumConferenceSize Max conference size.
+ * @return builder instance.
+ */
+ public Builder setMaximumConferenceSize(int maximumConferenceSize) {
+ mMaximumConferenceSize = maximumConferenceSize;
+ return this;
+ }
+
+ /**
+ * Sets whether an empty conference should be locally disconnected.
+ * @param shouldLocalDisconnectEmptyConference {@code true} if conference should be
+ * locally disconnected if empty.
+ * @return builder instance.
+ */
+ public Builder setShouldLocalDisconnectEmptyConference(
+ boolean shouldLocalDisconnectEmptyConference) {
+ mShouldLocalDisconnectEmptyConference = shouldLocalDisconnectEmptyConference;
+ return this;
+ }
+
+ /**
+ * Sets whether holding the conference is allowed.
+ * @param isHoldAllowed {@code true} if holding is allowed.
+ * @return builder instance.
+ */
+ public Builder setIsHoldAllowed(boolean isHoldAllowed) {
+ mIsHoldAllowed = isHoldAllowed;
+ return this;
+ }
+
+ /**
+ * Build instance of {@link CarrierConfiguration}.
+ * @return carrier config instance.
+ */
+ public ImsConference.CarrierConfiguration build() {
+ return new ImsConference.CarrierConfiguration(mIsMaximumConferenceSizeEnforced,
+ mMaximumConferenceSize, mShouldLocalDisconnectEmptyConference,
+ mIsHoldAllowed);
+ }
+ }
+
+ private CarrierConfiguration(boolean isMaximumConferenceSizeEnforced,
+ int maximumConferenceSize, boolean shouldLocalDisconnectEmptyConference,
+ boolean isHoldAllowed) {
+ mIsMaximumConferenceSizeEnforced = isMaximumConferenceSizeEnforced;
+ mMaximumConferenceSize = maximumConferenceSize;
+ mShouldLocalDisconnectEmptyConference = shouldLocalDisconnectEmptyConference;
+ mIsHoldAllowed = isHoldAllowed;
+ }
+
+ private boolean mIsMaximumConferenceSizeEnforced;
+
+ private int mMaximumConferenceSize;
+
+ private boolean mShouldLocalDisconnectEmptyConference;
+
+ private boolean mIsHoldAllowed;
+
+ /**
+ * Determines whether the {@link ImsConference} should enforce a size limit based on
+ * {@link #getMaximumConferenceSize()}.
+ * {@code true} if maximum size limit should be enforced, {@code false} otherwise.
+ */
+ public boolean isMaximumConferenceSizeEnforced() {
+ return mIsMaximumConferenceSizeEnforced;
+ }
+
+ /**
+ * Determines the maximum number of participants (not including the host) in a conference
+ * which is enforced when {@link #isMaximumConferenceSizeEnforced()} is {@code true}.
+ */
+ public int getMaximumConferenceSize() {
+ return mMaximumConferenceSize;
+ }
+
+ /**
+ * Determines whether this {@link ImsConference} should locally disconnect itself when the
+ * number of participants in the conference drops to zero.
+ * {@code true} if empty conference should be locally disconnected, {@code false}
+ * otherwise.
+ */
+ public boolean shouldLocalDisconnectEmptyConference() {
+ return mShouldLocalDisconnectEmptyConference;
+ }
+
+ /**
+ * Determines whether holding the conference is permitted or not.
+ * {@code true} if hold is permitted, {@code false} otherwise.
+ */
+ public boolean isHoldAllowed() {
+ return mIsHoldAllowed;
+ }
+ }
+
+ /**
* Listener used to respond to changes to the underlying radio connection for the conference
* host connection. Used to respond to SRVCC changes.
*/
@@ -260,6 +377,7 @@
private boolean mIsHoldable;
private boolean mCouldManageConference;
private FeatureFlagProxy mFeatureFlagProxy;
+ private final CarrierConfiguration mCarrierConfig;
private boolean mIsEmulatingSinglePartyCall = false;
private boolean mIsUsingSimCallManager = false;
@@ -301,12 +419,13 @@
public ImsConference(TelecomAccountRegistry telecomAccountRegistry,
TelephonyConnectionServiceProxy telephonyConnectionService,
TelephonyConnection conferenceHost, PhoneAccountHandle phoneAccountHandle,
- FeatureFlagProxy featureFlagProxy) {
+ FeatureFlagProxy featureFlagProxy, CarrierConfiguration carrierConfig) {
super(phoneAccountHandle);
mTelecomAccountRegistry = telecomAccountRegistry;
mFeatureFlagProxy = featureFlagProxy;
+ mCarrierConfig = carrierConfig;
// Specify the connection time of the conference to be the connection time of the original
// connection.
@@ -323,7 +442,7 @@
int capabilities = Connection.CAPABILITY_MUTE |
Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN;
- if (canHoldImsCalls()) {
+ if (mCarrierConfig.isHoldAllowed()) {
capabilities |= Connection.CAPABILITY_SUPPORT_HOLD | Connection.CAPABILITY_HOLD;
mIsHoldable = true;
}
@@ -491,6 +610,8 @@
} catch (CallStateException e) {
Log.e(this, e, "Exception thrown trying to hangup conference");
}
+ } else {
+ Log.w(this, "onDisconnect - null call");
}
}
@@ -935,6 +1056,14 @@
if (newParticipantsAdded || oldParticipantsRemoved) {
updateManageConference();
}
+
+ // If the conference is empty and we're supposed to do a local disconnect, do so now.
+ if (mCarrierConfig.shouldLocalDisconnectEmptyConference()
+ && oldParticipantCount > 0 && newParticipantCount == 0) {
+ Log.i(this, "handleConferenceParticipantsUpdate: empty conference; "
+ + "local disconnect.");
+ onDisconnect();
+ }
}
}
@@ -1021,6 +1150,7 @@
setConnectionStartElapsedRealtimeMillis(
entry.getConnectionStartElapsedRealtimeMillis());
setConnectionTime(entry.getConnectTimeMillis());
+ setCallDirection(entry.getCallDirection());
mLoneParticipantIdentity = new Pair<>(entry.getUserEntity(), entry.getEndpoint());
// Remove the participant from Telecom. It'll get picked up in a future CEP update
@@ -1225,7 +1355,7 @@
if (mConferenceHost.getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
Log.i(this,"handleOriginalConnectionChange : SRVCC to GSM");
GsmConnection c = new GsmConnection(originalConnection, getTelecomCallId(),
- mConferenceHost.isOutgoingCall());
+ mConferenceHost.getCallDirection());
// This is a newly created conference connection as a result of SRVCC
c.setConferenceSupported(true);
c.setTelephonyConnectionProperties(
@@ -1346,51 +1476,6 @@
return sb.toString();
}
- private boolean canHoldImsCalls() {
- PersistableBundle b = getCarrierConfig();
- // Return true if the CarrierConfig is unavailable
- return b == null || b.getBoolean(CarrierConfigManager.KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL);
- }
-
- private PersistableBundle getCarrierConfig() {
- if (mConferenceHost == null) {
- return null;
- }
-
- Phone phone = mConferenceHost.getPhone();
- if (phone == null) {
- return null;
- }
- return PhoneGlobals.getInstance().getCarrierConfigForSubId(phone.getSubId());
- }
-
- /**
- * @return {@code true} if the carrier associated with the conference requires that the maximum
- * size of the conference is enforced, {@code false} otherwise.
- */
- public boolean isMaximumConferenceSizeEnforced() {
- PersistableBundle b = getCarrierConfig();
- // Return false if the CarrierConfig is unavailable
- return b != null && b.getBoolean(
- CarrierConfigManager.KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL);
- }
-
- /**
- * @return The maximum size of a conference call where
- * {@link #isMaximumConferenceSizeEnforced()} is true.
- */
- public int getMaximumConferenceSize() {
- PersistableBundle b = getCarrierConfig();
-
- // If there is no carrier config its really a problem, but we'll still define a sane limit
- // of 5 so that we can still make a conference.
- if (b == null) {
- Log.w(this, "getMaximumConferenceSize - failed to get conference size");
- return 5;
- }
- return b.getInt(CarrierConfigManager.KEY_IMS_CONFERENCE_SIZE_LIMIT_INT);
- }
-
/**
* @return The number of participants in the conference.
*/
@@ -1403,8 +1488,8 @@
* participants in the conference has reached the limit, {@code false} otherwise.
*/
public boolean isFullConference() {
- return isMaximumConferenceSizeEnforced()
- && getNumberOfParticipants() >= getMaximumConferenceSize();
+ return mCarrierConfig.isMaximumConferenceSizeEnforced()
+ && getNumberOfParticipants() >= mCarrierConfig.getMaximumConferenceSize();
}
/**
diff --git a/src/com/android/services/telephony/ImsConferenceController.java b/src/com/android/services/telephony/ImsConferenceController.java
index 8789ba8..6d3d4c2 100644
--- a/src/com/android/services/telephony/ImsConferenceController.java
+++ b/src/com/android/services/telephony/ImsConferenceController.java
@@ -16,12 +16,16 @@
package com.android.services.telephony;
+import android.content.Context;
+import android.os.PersistableBundle;
import android.telecom.Conference;
import android.telecom.Conferenceable;
import android.telecom.Connection;
import android.telecom.ConnectionService;
import android.telecom.DisconnectCause;
import android.telecom.PhoneAccountHandle;
+import android.telephony.CarrierConfigManager;
+
import com.android.telephony.Rlog;
import com.android.internal.telephony.Phone;
@@ -403,6 +407,7 @@
PhoneAccountHandle phoneAccountHandle = null;
// Attempt to determine the phone account associated with the conference host connection.
+ ImsConference.CarrierConfiguration carrierConfig = null;
if (connection.getPhone() != null &&
connection.getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
Phone imsPhone = connection.getPhone();
@@ -410,11 +415,13 @@
// base GSM or CDMA phone, not on the ImsPhone itself).
phoneAccountHandle =
PhoneUtils.makePstnPhoneAccountHandle(imsPhone.getDefaultPhone());
+ carrierConfig = getCarrierConfig(imsPhone);
}
ImsConference conference = new ImsConference(mTelecomAccountRegistry, mConnectionService,
- conferenceHostConnection, phoneAccountHandle, mFeatureFlagProxy);
+ conferenceHostConnection, phoneAccountHandle, mFeatureFlagProxy, carrierConfig);
conference.setState(conferenceHostConnection.getState());
+ conference.setCallDirection(conferenceHostConnection.getCallDirection());
conference.addTelephonyConferenceListener(mConferenceListener);
conference.updateConferenceParticipantsAfterCreation();
mConnectionService.addConference(conference);
@@ -433,4 +440,33 @@
// conferenceable connections for the conference to show merge calls option.
recalculateConferenceable();
}
+
+ public static ImsConference.CarrierConfiguration getCarrierConfig(Phone phone) {
+ ImsConference.CarrierConfiguration.Builder config =
+ new ImsConference.CarrierConfiguration.Builder();
+ if (phone == null) {
+ return config.build();
+ }
+
+ CarrierConfigManager cfgManager = (CarrierConfigManager)
+ phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (cfgManager != null) {
+ PersistableBundle bundle = cfgManager.getConfigForSubId(phone.getSubId());
+ boolean isMaximumConferenceSizeEnforced = bundle.getBoolean(
+ CarrierConfigManager.KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL);
+ int maximumConferenceSize = bundle.getInt(
+ CarrierConfigManager.KEY_IMS_CONFERENCE_SIZE_LIMIT_INT);
+ boolean isHoldAllowed = bundle.getBoolean(
+ CarrierConfigManager.KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL);
+ boolean shouldLocalDisconnectOnEmptyConference = bundle.getBoolean(
+ CarrierConfigManager.KEY_LOCAL_DISCONNECT_EMPTY_IMS_CONFERENCE_BOOL);
+
+ config.setIsMaximumConferenceSizeEnforced(isMaximumConferenceSizeEnforced)
+ .setMaximumConferenceSize(maximumConferenceSize)
+ .setIsHoldAllowed(isHoldAllowed)
+ .setShouldLocalDisconnectEmptyConference(
+ shouldLocalDisconnectOnEmptyConference);
+ }
+ return config.build();
+ }
}
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 9e880a5..c38923f 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -32,6 +32,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.PersistableBundle;
import android.os.UserHandle;
@@ -57,6 +58,7 @@
import android.text.TextUtils;
import com.android.ims.ImsManager;
+import com.android.internal.telephony.ExponentialBackoff;
import com.android.internal.telephony.LocaleTracker;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
@@ -85,6 +87,26 @@
private final static int DEFAULT_SIM_ICON = R.drawable.ic_multi_sim;
private final static String GROUP_PREFIX = "group_";
+ private static final int REGISTER_START_DELAY_MS = 1 * 1000; // 1 second
+ private static final int REGISTER_MAXIMUM_DELAY_MS = 60 * 1000; // 1 minute
+
+ /**
+ * Indicates the {@link SubscriptionManager.OnSubscriptionsChangedListener} has not yet been
+ * registered.
+ */
+ private static final int LISTENER_STATE_UNREGISTERED = 0;
+
+ /**
+ * Indicates the first {@link SubscriptionManager.OnSubscriptionsChangedListener} registration
+ * attempt failed and we are performing backoff registration.
+ */
+ private static final int LISTENER_STATE_PERFORMING_BACKOFF = 2;
+
+ /**
+ * Indicates the {@link SubscriptionManager.OnSubscriptionsChangedListener} has been registered.
+ */
+ private static final int LISTENER_STATE_REGISTERED = 3;
+
final class AccountEntry implements PstnPhoneCapabilitiesNotifier.Listener {
private final Phone mPhone;
private PhoneAccount mAccount;
@@ -952,18 +974,46 @@
new OnSubscriptionsChangedListener() {
@Override
public void onSubscriptionsChanged() {
- // Any time the SubscriptionInfo changes...rerun the setup
- Log.i(this, "onSubscriptionsChanged - update accounts");
+ if (mSubscriptionListenerState != LISTENER_STATE_REGISTERED) {
+ mRegisterSubscriptionListenerBackoff.stop();
+ mHandlerThread.quitSafely();
+ }
+ mSubscriptionListenerState = LISTENER_STATE_REGISTERED;
+
+ // Any time the SubscriptionInfo changes rerun the setup
+ Log.i(this, "TelecomAccountRegistry: onSubscriptionsChanged - update accounts");
tearDownAccounts();
setupAccounts();
}
+
+ @Override
+ public void onAddListenerFailed() {
+ // Woe! Failed to add the listener!
+ Log.w(this, "TelecomAccountRegistry: onAddListenerFailed - failed to register "
+ + "OnSubscriptionsChangedListener");
+
+ // Even though registering the listener failed, we will still try to setup the phone
+ // accounts now; the phone instances should already be present and ready, so even if
+ // telephony registry is poking along we can still try to setup the phone account.
+ tearDownAccounts();
+ setupAccounts();
+
+ if (mSubscriptionListenerState == LISTENER_STATE_UNREGISTERED) {
+ // Initial registration attempt failed; start exponential backoff.
+ mSubscriptionListenerState = LISTENER_STATE_PERFORMING_BACKOFF;
+ mRegisterSubscriptionListenerBackoff.start();
+ } else {
+ // We're already doing exponential backoff and a registration failed.
+ mRegisterSubscriptionListenerBackoff.notifyFailed();
+ }
+ }
};
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
- Log.i(this, "User changed, re-registering phone accounts.");
+ Log.i(this, "TelecomAccountRegistry: User changed, re-registering phone accounts.");
int userHandleId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
UserHandle currentUserHandle = UserHandle.of(userHandleId);
@@ -1027,20 +1077,42 @@
private final SubscriptionManager mSubscriptionManager;
private List<AccountEntry> mAccounts = new LinkedList<AccountEntry>();
private final Object mAccountsLock = new Object();
+ private int mSubscriptionListenerState = LISTENER_STATE_UNREGISTERED;
private int mServiceState = ServiceState.STATE_POWER_OFF;
private int mActiveDataSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private boolean mIsPrimaryUser = true;
+ private ExponentialBackoff mRegisterSubscriptionListenerBackoff;
+ private final HandlerThread mHandlerThread = new HandlerThread("TelecomAccountRegistry");
// TODO: Remove back-pointer from app singleton to Service, since this is not a preferred
// pattern; redesign. This was added to fix a late release bug.
private TelephonyConnectionService mTelephonyConnectionService;
+ // Used to register subscription changed listener when initial attempts fail.
+ private Runnable mRegisterOnSubscriptionsChangedListenerRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mSubscriptionListenerState != LISTENER_STATE_REGISTERED) {
+ Log.i(this, "TelecomAccountRegistry: performing delayed register.");
+ SubscriptionManager.from(mContext).addOnSubscriptionsChangedListener(
+ mOnSubscriptionsChangedListener);
+ }
+ }
+ };
+
TelecomAccountRegistry(Context context) {
mContext = context;
mTelecomManager = context.getSystemService(TelecomManager.class);
mImsManager = context.getSystemService(android.telephony.ims.ImsManager.class);
mTelephonyManager = TelephonyManager.from(context);
mSubscriptionManager = SubscriptionManager.from(context);
+ mHandlerThread.start();
+ mRegisterSubscriptionListenerBackoff = new ExponentialBackoff(
+ REGISTER_START_DELAY_MS,
+ REGISTER_MAXIMUM_DELAY_MS,
+ 2, /* multiplier */
+ mHandlerThread.getLooper(),
+ mRegisterOnSubscriptionsChangedListenerRunnable);
}
/**
@@ -1256,6 +1328,7 @@
// Register for SubscriptionInfo list changes which is guaranteed
// to invoke onSubscriptionsChanged the first time.
+ Log.i(this, "TelecomAccountRegistry: setupOnBoot - register subscription listener");
SubscriptionManager.from(mContext).addOnSubscriptionsChangedListener(
mOnSubscriptionsChangedListener);
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index d5ed409..19afe24 100755
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -772,11 +772,6 @@
private boolean mIsCdmaVoicePrivacyEnabled;
/**
- * Indicates whether this call is an outgoing call.
- */
- protected final boolean mIsOutgoing;
-
- /**
* Indicates whether the connection can be held. This filed combined with the state of the
* connection can determine whether {@link Connection#CAPABILITY_HOLD} should be added to the
* connection.
@@ -810,8 +805,8 @@
new ConcurrentHashMap<TelephonyConnectionListener, Boolean>(8, 0.9f, 1));
protected TelephonyConnection(com.android.internal.telephony.Connection originalConnection,
- String callId, boolean isOutgoingCall) {
- mIsOutgoing = isOutgoingCall;
+ String callId, @android.telecom.Call.Details.CallDirection int callDirection) {
+ setCallDirection(callDirection);
setTelecomCallId(callId);
if (originalConnection != null) {
setOriginalConnection(originalConnection);
@@ -2434,8 +2429,8 @@
/**
* @return {@code true} if this is an outgoing call, {@code false} otherwise.
*/
- boolean isOutgoingCall() {
- return mIsOutgoing;
+ public boolean isOutgoingCall() {
+ return getCallDirection() == android.telecom.Call.Details.DIRECTION_OUTGOING;
}
/**
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index a703cb6..bd10aea 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -585,9 +585,11 @@
}
TelephonyConnection connection = (TelephonyConnection)conn;
+
ImsConference conference = new ImsConference(TelecomAccountRegistry.getInstance(this),
mTelephonyConnectionServiceProxy, connection,
- phoneAccountHandle, () -> true);
+ phoneAccountHandle, () -> true,
+ ImsConferenceController.getCarrierConfig(connection.getPhone()));
mImsConferenceController.addConference(conference);
conference.setVideoState(connection,
connection.getVideoState());
@@ -1821,12 +1823,14 @@
boolean isAdhocConference) {
TelephonyConnection returnConnection = null;
int phoneType = phone.getPhoneType();
+ int callDirection = isOutgoing ? android.telecom.Call.Details.DIRECTION_OUTGOING
+ : android.telecom.Call.Details.DIRECTION_INCOMING;
if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
- returnConnection = new GsmConnection(originalConnection, telecomCallId, isOutgoing);
+ returnConnection = new GsmConnection(originalConnection, telecomCallId, callDirection);
} else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) {
boolean allowsMute = allowsMute(phone);
returnConnection = new CdmaConnection(originalConnection, mEmergencyTonePlayer,
- allowsMute, isOutgoing, telecomCallId);
+ allowsMute, callDirection, telecomCallId);
}
if (returnConnection != null) {
if (!isAdhocConference) {
diff --git a/src/com/android/services/telephony/rcs/UserCapabilityExchangeImpl.java b/src/com/android/services/telephony/rcs/UserCapabilityExchangeImpl.java
index 0606d32..9b0a303 100644
--- a/src/com/android/services/telephony/rcs/UserCapabilityExchangeImpl.java
+++ b/src/com/android/services/telephony/rcs/UserCapabilityExchangeImpl.java
@@ -643,13 +643,22 @@
// Unregister IMS callback
ImsMmTelManager imsMmtelManager = getImsMmTelManager(subId);
if (imsMmtelManager != null) {
- imsMmtelManager.unregisterImsRegistrationCallback(mImsRegistrationCallback);
- imsMmtelManager.unregisterMmTelCapabilityCallback(mCapabilityCallback);
+ try {
+ imsMmtelManager.unregisterImsRegistrationCallback(mImsRegistrationCallback);
+ imsMmtelManager.unregisterMmTelCapabilityCallback(mCapabilityCallback);
+ } catch (RuntimeException e) {
+ logw("unregister IMS callback error: " + e.getMessage());
+ }
}
+
// Unregister provision changed callback
ProvisioningManager provisioningManager =
ProvisioningManager.createForSubscriptionId(subId);
- provisioningManager.unregisterProvisioningChangedCallback(mProvisioningChangedCallback);
+ try {
+ provisioningManager.unregisterProvisioningChangedCallback(mProvisioningChangedCallback);
+ } catch (RuntimeException e) {
+ logw("unregister provisioning callback error: " + e.getMessage());
+ }
// Remove all publish state callbacks
clearPublishStateCallbacks();
@@ -718,10 +727,17 @@
if (mSubId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
return;
}
+
ImsMmTelManager ims = getImsMmTelManager(mSubId);
- boolean isEnabled = ims.isVtSettingEnabled();
- logi("SimInfo changed: VT setting=" + isEnabled);
- mPresencePublication.onVtEnabled(isEnabled);
+ if (ims == null) return;
+
+ try {
+ boolean isEnabled = ims.isVtSettingEnabled();
+ logi("SimInfo changed: VT setting=" + isEnabled);
+ mPresencePublication.onVtEnabled(isEnabled);
+ } catch (RuntimeException e) {
+ logw("SimInfo changed error: " + e);
+ }
}
};
@@ -790,6 +806,7 @@
Context.TELEPHONY_IMS_SERVICE);
return (imsManager == null) ? null : imsManager.getImsMmTelManager(subId);
} catch (IllegalArgumentException e) {
+ logw("getImsMmTelManager error: " + e.getMessage());
return null;
}
}
diff --git a/tests/src/com/android/services/telephony/ImsConferenceTest.java b/tests/src/com/android/services/telephony/ImsConferenceTest.java
index 7251402..b84aca1 100644
--- a/tests/src/com/android/services/telephony/ImsConferenceTest.java
+++ b/tests/src/com/android/services/telephony/ImsConferenceTest.java
@@ -43,6 +43,7 @@
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
+import java.util.Collections;
public class ImsConferenceTest {
@Mock
@@ -73,14 +74,15 @@
ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
mMockTelephonyConnectionServiceProxy, mConferenceHost,
- null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+ null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+ new ImsConference.CarrierConfiguration.Builder().build());
ConferenceParticipant participant1 = new ConferenceParticipant(
Uri.parse("tel:6505551212"),
"A",
Uri.parse("sip:6505551212@testims.com"),
Connection.STATE_ACTIVE,
- Call.Details.DIRECTION_INCOMING);
+ Call.Details.DIRECTION_OUTGOING);
ConferenceParticipant participant2 = new ConferenceParticipant(
Uri.parse("tel:6505551213"),
"A",
@@ -98,6 +100,8 @@
imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
Arrays.asList(participant1));
assertEquals(0, imsConference.getNumberOfParticipants());
+ // Ensure the call direction is set correctly during emulation
+ assertEquals(Call.Details.DIRECTION_OUTGOING, imsConference.getCallDirection());
reset(mMockTelephonyConnectionServiceProxy);
// Back to 2!
@@ -120,7 +124,8 @@
ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
mMockTelephonyConnectionServiceProxy, mConferenceHost,
- null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+ null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+ new ImsConference.CarrierConfiguration.Builder().build());
// Start off with 3 participants.
ConferenceParticipant participant1 = new ConferenceParticipant(
@@ -183,7 +188,8 @@
ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
mMockTelephonyConnectionServiceProxy, mConferenceHost,
- null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+ null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+ new ImsConference.CarrierConfiguration.Builder().build());
// Start off with 3 participants.
ConferenceParticipant participant1 = new ConferenceParticipant(
@@ -240,7 +246,8 @@
ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
mMockTelephonyConnectionServiceProxy, mConferenceHost,
- null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+ null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+ new ImsConference.CarrierConfiguration.Builder().build());
// Setup the initial conference state with 2 participants.
ConferenceParticipant participant1 = new ConferenceParticipant(
@@ -298,7 +305,8 @@
ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
mMockTelephonyConnectionServiceProxy, mConferenceHost,
- null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+ null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+ new ImsConference.CarrierConfiguration.Builder().build());
final boolean[] isConferenceState = new boolean[1];
TelephonyConferenceBase.TelephonyConferenceListener conferenceListener =
@@ -354,7 +362,8 @@
ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
mMockTelephonyConnectionServiceProxy, mConferenceHost,
- null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+ null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+ new ImsConference.CarrierConfiguration.Builder().build());
ConferenceParticipant participant1 = new ConferenceParticipant(
Uri.parse("tel:6505551212"),
@@ -379,7 +388,8 @@
ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
mMockTelephonyConnectionServiceProxy, mConferenceHost,
- null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+ null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+ new ImsConference.CarrierConfiguration.Builder().build());
ConferenceParticipant participant1 = new ConferenceParticipant(
Uri.parse("tel:6505551212"),
@@ -410,13 +420,14 @@
@Test
@SmallTest
- public void testNormalConference() {
+ public void testNormalConference() throws Exception {
when(mMockTelecomAccountRegistry.isUsingSimCallManager(any(PhoneAccountHandle.class)))
.thenReturn(false);
ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
mMockTelephonyConnectionServiceProxy, mConferenceHost,
- null /* phoneAccountHandle */, () -> false /* featureFlagProxy */);
+ null /* phoneAccountHandle */, () -> false /* featureFlagProxy */,
+ new ImsConference.CarrierConfiguration.Builder().build());
ConferenceParticipant participant1 = new ConferenceParticipant(
Uri.parse("tel:6505551212"),
@@ -438,5 +449,45 @@
imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
Arrays.asList(participant1));
assertEquals(1, imsConference.getNumberOfParticipants());
+
+ // Drop to 0 participants; should not hangup the conf now
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost, Collections.emptyList());
+ assertEquals(0, imsConference.getNumberOfParticipants());
+ verify(mConferenceHost.mMockCall, never()).hangup();
+ }
+
+ @Test
+ @SmallTest
+ public void testLocalDisconnectOnEmptyConference() throws Exception {
+ when(mMockTelecomAccountRegistry.isUsingSimCallManager(any(PhoneAccountHandle.class)))
+ .thenReturn(false);
+
+ ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
+ mMockTelephonyConnectionServiceProxy, mConferenceHost,
+ null /* phoneAccountHandle */, () -> false /* featureFlagProxy */,
+ new ImsConference.CarrierConfiguration.Builder()
+ .setShouldLocalDisconnectEmptyConference(true)
+ .build());
+
+ ConferenceParticipant participant1 = new ConferenceParticipant(
+ Uri.parse("tel:6505551212"),
+ "A",
+ Uri.parse("sip:6505551212@testims.com"),
+ Connection.STATE_ACTIVE,
+ Call.Details.DIRECTION_INCOMING);
+ ConferenceParticipant participant2 = new ConferenceParticipant(
+ Uri.parse("tel:6505551213"),
+ "A",
+ Uri.parse("sip:6505551213@testims.com"),
+ Connection.STATE_ACTIVE,
+ Call.Details.DIRECTION_INCOMING);
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+ Arrays.asList(participant1, participant2));
+ assertEquals(2, imsConference.getNumberOfParticipants());
+
+ // Drop to 0 participants; should have a hangup request.
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost, Collections.emptyList());
+ assertEquals(0, imsConference.getNumberOfParticipants());
+ verify(mConferenceHost.mMockCall).hangup();
}
}
diff --git a/tests/src/com/android/services/telephony/TestTelephonyConnection.java b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
index 5b31c0f..c36c405 100644
--- a/tests/src/com/android/services/telephony/TestTelephonyConnection.java
+++ b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
@@ -30,6 +30,7 @@
import static org.mockito.Mockito.when;
import com.android.internal.telephony.Call;
+import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
@@ -72,8 +73,13 @@
return mMockRadioConnection;
}
+ @Override
+ protected Call getCall() {
+ return mMockCall;
+ }
+
public TestTelephonyConnection() {
- super(null, null, false);
+ super(null, null, android.telecom.Call.Details.DIRECTION_INCOMING);
MockitoAnnotations.initMocks(this);
mMockPhone = mock(Phone.class);
@@ -100,6 +106,11 @@
when(mMockPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_IMS);
when(mMockCall.getState()).thenReturn(Call.State.ACTIVE);
when(mMockCall.getPhone()).thenReturn(mMockPhone);
+ try {
+ doNothing().when(mMockCall).hangup();
+ } catch (CallStateException e) {
+ e.printStackTrace();
+ }
}
@Override