518 lines
23 KiB
Java
518 lines
23 KiB
Java
/*
|
|
* Copyright (C) 2024 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.internal.telephony;
|
|
|
|
import android.annotation.NonNull;
|
|
import android.content.Context;
|
|
import android.os.AsyncResult;
|
|
import android.os.Handler;
|
|
import android.os.HandlerExecutor;
|
|
import android.os.Message;
|
|
import android.telephony.AccessNetworkConstants;
|
|
import android.telephony.SubscriptionInfo;
|
|
import android.telephony.SubscriptionManager;
|
|
import android.telephony.TelephonyRegistryManager;
|
|
import android.util.Log;
|
|
|
|
import com.android.internal.annotations.VisibleForTesting;
|
|
import com.android.internal.telephony.flags.FeatureFlags;
|
|
import com.android.internal.telephony.imsphone.ImsPhone;
|
|
import com.android.internal.telephony.subscription.SubscriptionManagerService;
|
|
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
import java.util.Set;
|
|
import java.util.concurrent.CopyOnWriteArraySet;
|
|
import java.util.stream.Collectors;
|
|
|
|
public class SimultaneousCallingTracker {
|
|
private static SimultaneousCallingTracker sInstance = null;
|
|
private final Context mContext;
|
|
|
|
/**
|
|
* A dynamic map of all voice capable {@link Phone} objects mapped to the set of {@link Phone}
|
|
* objects each {@link Phone} has a compatible user association with. To be considered
|
|
* compatible based on user association, both must be associated with the same
|
|
* {@link android.os.UserHandle} or both must be unassociated.
|
|
*/
|
|
private Map<Phone, Set<Phone>> mVoiceCapablePhoneMap = new HashMap<>();
|
|
|
|
@VisibleForTesting
|
|
public boolean isDeviceSimultaneousCallingCapable = false;
|
|
public Set<Listener> mListeners = new CopyOnWriteArraySet<>();
|
|
private final PhoneConfigurationManager mPhoneConfigurationManager;
|
|
private final Handler mHandler;
|
|
|
|
/**
|
|
* A dynamic map of all the Phone IDs mapped to the set of {@link Phone} objects each
|
|
* {@link Phone} supports simultaneous calling (DSDA) with.
|
|
*/
|
|
private Map<Integer, Set<Phone>> mSimultaneousCallPhoneSupportMap = new HashMap<>();
|
|
private static final String LOG_TAG = "SimultaneousCallingTracker";
|
|
protected static final int EVENT_SUBSCRIPTION_CHANGED = 101;
|
|
protected static final int EVENT_PHONE_CAPABILITY_CHANGED = 102;
|
|
protected static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 103;
|
|
protected static final int EVENT_DEVICE_CONFIG_CHANGED = 104;
|
|
protected static final int EVENT_IMS_REGISTRATION_CHANGED = 105;
|
|
|
|
/** Feature flags */
|
|
@NonNull
|
|
private final FeatureFlags mFeatureFlags;
|
|
|
|
/**
|
|
* Init method to instantiate the object
|
|
* Should only be called once.
|
|
*/
|
|
public static SimultaneousCallingTracker init(Context context,
|
|
@NonNull FeatureFlags featureFlags) {
|
|
if (sInstance == null) {
|
|
sInstance = new SimultaneousCallingTracker(context, featureFlags);
|
|
} else {
|
|
Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
|
|
}
|
|
return sInstance;
|
|
}
|
|
|
|
/**
|
|
* Constructor.
|
|
* @param context context needed to send broadcast.
|
|
*/
|
|
private SimultaneousCallingTracker(Context context, @NonNull FeatureFlags featureFlags) {
|
|
mContext = context;
|
|
mFeatureFlags = featureFlags;
|
|
mHandler = new ConfigManagerHandler();
|
|
mPhoneConfigurationManager = PhoneConfigurationManager.getInstance();
|
|
mPhoneConfigurationManager.addListener(mPhoneConfigurationManagerListener);
|
|
PhoneConfigurationManager.registerForMultiSimConfigChange(mHandler,
|
|
EVENT_MULTI_SIM_CONFIG_CHANGED, null);
|
|
TelephonyRegistryManager telephonyRegistryManager = (TelephonyRegistryManager)
|
|
context.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
|
|
telephonyRegistryManager.addOnSubscriptionsChangedListener(
|
|
mSubscriptionsChangedListener, new HandlerExecutor(mHandler));
|
|
}
|
|
|
|
/**
|
|
* Static method to get instance.
|
|
*/
|
|
public static SimultaneousCallingTracker getInstance() {
|
|
if (sInstance == null) {
|
|
Log.wtf(LOG_TAG, "getInstance null");
|
|
}
|
|
|
|
return sInstance;
|
|
}
|
|
|
|
/**
|
|
* Handler class to handle callbacks
|
|
*/
|
|
private final class ConfigManagerHandler extends Handler {
|
|
@Override
|
|
public void handleMessage(Message msg) {
|
|
if (!mFeatureFlags.simultaneousCallingIndications()) { return; }
|
|
Log.v(LOG_TAG, "Received EVENT " + msg.what);
|
|
switch (msg.what) {
|
|
case EVENT_PHONE_CAPABILITY_CHANGED -> {
|
|
checkSimultaneousCallingDeviceCapability();
|
|
}
|
|
case EVENT_SUBSCRIPTION_CHANGED -> {
|
|
updatePhoneMapAndSimultaneousCallSupportMap();
|
|
}
|
|
case EVENT_MULTI_SIM_CONFIG_CHANGED -> {
|
|
int activeModemCount = (int) ((AsyncResult) msg.obj).result;
|
|
if (activeModemCount > 1) {
|
|
// SSIM --> MSIM: recalculate simultaneous calling supported combinations
|
|
updatePhoneMapAndSimultaneousCallSupportMap();
|
|
} else {
|
|
// MSIM --> SSIM: remove all simultaneous calling supported combinations
|
|
disableSimultaneousCallingSupport();
|
|
handleSimultaneousCallingSupportChanged();
|
|
}
|
|
}
|
|
case EVENT_DEVICE_CONFIG_CHANGED, EVENT_IMS_REGISTRATION_CHANGED -> {
|
|
updateSimultaneousCallSupportMap();
|
|
}
|
|
default -> Log.i(LOG_TAG, "Received unknown event: " + msg.what);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Listener interface for events related to the {@link SimultaneousCallingTracker}.
|
|
*/
|
|
public interface Listener {
|
|
/**
|
|
* Inform Telecom that the simultaneous calling subscription support map may have changed.
|
|
*
|
|
* @param simultaneousCallSubSupportMap Map of all voice capable subscription IDs mapped to
|
|
* a set containing the subscription IDs which that
|
|
* subscription is DSDA compatible with.
|
|
*/
|
|
public void onSimultaneousCallingSupportChanged(Map<Integer,
|
|
Set<Integer>> simultaneousCallSubSupportMap);
|
|
}
|
|
|
|
/**
|
|
* Base listener implementation.
|
|
*/
|
|
public abstract static class ListenerBase implements SimultaneousCallingTracker.Listener {
|
|
@Override
|
|
public void onSimultaneousCallingSupportChanged(Map<Integer,
|
|
Set<Integer>> simultaneousCallSubSupportMap) {}
|
|
}
|
|
|
|
/**
|
|
* Assign a listener to be notified of state changes.
|
|
*
|
|
* @param listener A listener.
|
|
*/
|
|
public void addListener(Listener listener) {
|
|
if (mFeatureFlags.simultaneousCallingIndications()) {
|
|
mListeners.add(listener);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes a listener.
|
|
*
|
|
* @param listener A listener.
|
|
*/
|
|
public final void removeListener(Listener listener) {
|
|
if (mFeatureFlags.simultaneousCallingIndications()) {
|
|
mListeners.remove(listener);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Listener for listening to events in the {@link android.telephony.TelephonyRegistryManager}
|
|
*/
|
|
private final SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionsChangedListener =
|
|
new SubscriptionManager.OnSubscriptionsChangedListener() {
|
|
@Override
|
|
public void onSubscriptionsChanged() {
|
|
if (!mHandler.hasMessages(EVENT_SUBSCRIPTION_CHANGED)) {
|
|
mHandler.sendEmptyMessage(EVENT_SUBSCRIPTION_CHANGED);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Listener for listening to events in the {@link PhoneConfigurationManager}.
|
|
*/
|
|
private final PhoneConfigurationManager.Listener mPhoneConfigurationManagerListener =
|
|
new PhoneConfigurationManager.Listener() {
|
|
@Override
|
|
public void onPhoneCapabilityChanged() {
|
|
if (!mHandler.hasMessages(EVENT_PHONE_CAPABILITY_CHANGED)) {
|
|
mHandler.sendEmptyMessage(EVENT_PHONE_CAPABILITY_CHANGED);
|
|
}
|
|
}
|
|
@Override
|
|
public void onDeviceConfigChanged() {
|
|
if (!mHandler.hasMessages(EVENT_DEVICE_CONFIG_CHANGED)) {
|
|
mHandler.sendEmptyMessage(EVENT_DEVICE_CONFIG_CHANGED);
|
|
}
|
|
}
|
|
};
|
|
|
|
private void checkSimultaneousCallingDeviceCapability() {
|
|
if (mPhoneConfigurationManager.getNumberOfModemsWithSimultaneousVoiceConnections() > 1) {
|
|
isDeviceSimultaneousCallingCapable = true;
|
|
mPhoneConfigurationManager.registerForSimultaneousCellularCallingSlotsChanged(
|
|
this::onSimultaneousCellularCallingSlotsChanged);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param subId to get the slots supporting simultaneous calling with
|
|
* @return the set of subId's that support simultaneous calling with the param subId
|
|
*/
|
|
public Set<Integer> getSubIdsSupportingSimultaneousCalling(int subId) {
|
|
if (!isDeviceSimultaneousCallingCapable) {
|
|
Log.v(LOG_TAG, "Device is not simultaneous calling capable");
|
|
return Collections.emptySet();
|
|
}
|
|
for (int phoneId : mSimultaneousCallPhoneSupportMap.keySet()) {
|
|
if (PhoneFactory.getPhone(phoneId).getSubId() == subId) {
|
|
Set<Integer> subIdsSupportingSimultaneousCalling = new HashSet<>();
|
|
for (Phone phone : mSimultaneousCallPhoneSupportMap.get(phoneId)) {
|
|
subIdsSupportingSimultaneousCalling.add(phone.getSubId());
|
|
}
|
|
Log.d(LOG_TAG, "getSlotsSupportingSimultaneousCalling for subId=" + subId +
|
|
"; subIdsSupportingSimultaneousCalling=[" +
|
|
getStringFromSet(subIdsSupportingSimultaneousCalling) + "].");
|
|
return subIdsSupportingSimultaneousCalling;
|
|
}
|
|
}
|
|
Log.e(LOG_TAG, "getSlotsSupportingSimultaneousCalling: Subscription ID not found in"
|
|
+ " the map of voice capable phones.");
|
|
return Collections.emptySet();
|
|
}
|
|
|
|
private void updatePhoneMapAndSimultaneousCallSupportMap() {
|
|
if (!isDeviceSimultaneousCallingCapable) {
|
|
Log.d(LOG_TAG, "Ignoring updatePhoneMapAndSimultaneousCallSupportMap since device "
|
|
+ "is not DSDA capable.");
|
|
return;
|
|
}
|
|
unregisterForImsRegistrationChanges(mVoiceCapablePhoneMap);
|
|
mVoiceCapablePhoneMap = generateVoiceCapablePhoneMapBasedOnUserAssociation();
|
|
Log.i(LOG_TAG, "updatePhoneMapAndSimultaneousCallSupportMap: mVoiceCapablePhoneMap.size = "
|
|
+ mVoiceCapablePhoneMap.size());
|
|
registerForImsRegistrationChanges(mVoiceCapablePhoneMap);
|
|
updateSimultaneousCallSupportMap();
|
|
}
|
|
|
|
private void updateSimultaneousCallSupportMap() {
|
|
if (!isDeviceSimultaneousCallingCapable) {
|
|
Log.d(LOG_TAG, "Ignoring updateSimultaneousCallSupportMap since device is not DSDA"
|
|
+ "capable.");
|
|
return;
|
|
}
|
|
mSimultaneousCallPhoneSupportMap =
|
|
generateSimultaneousCallSupportMap(mVoiceCapablePhoneMap);
|
|
handleSimultaneousCallingSupportChanged();
|
|
}
|
|
|
|
/**
|
|
* The simultaneous cellular calling slots have changed.
|
|
* @param slotIds The Set of slotIds that have simultaneous cellular calling.
|
|
*/
|
|
private void onSimultaneousCellularCallingSlotsChanged(Set<Integer> slotIds) {
|
|
//Cellular calling slots have changed - regenerate simultaneous calling support map:
|
|
updateSimultaneousCallSupportMap();
|
|
}
|
|
|
|
private void disableSimultaneousCallingSupport() {
|
|
if (!isDeviceSimultaneousCallingCapable) {
|
|
Log.d(LOG_TAG, "Ignoring updateSimultaneousCallSupportMap since device is not DSDA"
|
|
+ "capable.");
|
|
return;
|
|
}
|
|
unregisterForImsRegistrationChanges(mVoiceCapablePhoneMap);
|
|
|
|
// In Single-SIM mode, simultaneous calling is not supported at all:
|
|
mSimultaneousCallPhoneSupportMap.clear();
|
|
mVoiceCapablePhoneMap.clear();
|
|
}
|
|
|
|
/**
|
|
* Registers a listener to receive IMS registration changes for all phones in the phoneMap.
|
|
*
|
|
* @param phoneMap Map of voice capable phones mapped to the set of phones each has a compatible
|
|
* user association with.
|
|
*/
|
|
private void registerForImsRegistrationChanges(Map<Phone, Set<Phone>> phoneMap) {
|
|
for (Phone phone : phoneMap.keySet()) {
|
|
ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
|
|
if (imsPhone != null) {
|
|
Log.v(LOG_TAG, "registerForImsRegistrationChanges: registering phoneId = " +
|
|
phone.getPhoneId());
|
|
imsPhone.registerForImsRegistrationChanges(mHandler,
|
|
EVENT_IMS_REGISTRATION_CHANGED, null);
|
|
} else {
|
|
Log.v(LOG_TAG, "registerForImsRegistrationChanges: phone not recognized as "
|
|
+ "ImsPhone: phoneId = " + phone.getPhoneId());
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unregisters the listener to stop receiving IMS registration changes for all phones in the
|
|
* phoneMap.
|
|
*
|
|
* @param phoneMap Map of voice capable phones mapped to the set of phones each has a compatible
|
|
* user association with.
|
|
*/
|
|
private void unregisterForImsRegistrationChanges(Map<Phone, Set<Phone>> phoneMap) {
|
|
for (Phone phone : phoneMap.keySet()) {
|
|
ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
|
|
if (imsPhone != null) {
|
|
imsPhone.unregisterForImsRegistrationChanges(mHandler);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates mVoiceCapablePhoneMap by iterating through {@link PhoneFactory#getPhones()} and
|
|
* checking whether each {@link Phone} corresponds to a valid and voice capable subscription.
|
|
* Maps the voice capable phones to the other voice capable phones that have compatible user
|
|
* associations
|
|
*/
|
|
private Map<Phone, Set<Phone>> generateVoiceCapablePhoneMapBasedOnUserAssociation() {
|
|
Map<Phone, Set<Phone>> voiceCapablePhoneMap = new HashMap<>(3);
|
|
|
|
// Generate a map of phone slots that corresponds to valid and voice capable subscriptions:
|
|
Phone[] allPhones = PhoneFactory.getPhones();
|
|
for (Phone phone : allPhones) {
|
|
int subId = phone.getSubId();
|
|
SubscriptionInfo subInfo =
|
|
SubscriptionManagerService.getInstance().getSubscriptionInfo(subId);
|
|
|
|
if (mFeatureFlags.dataOnlyCellularService() &&
|
|
subId > SubscriptionManager.INVALID_SUBSCRIPTION_ID && subInfo != null &&
|
|
subInfo.getServiceCapabilities()
|
|
.contains(SubscriptionManager.SERVICE_CAPABILITY_VOICE)) {
|
|
Log.v(LOG_TAG, "generateVoiceCapablePhoneMapBasedOnUserAssociation: adding "
|
|
+ "phoneId = " + phone.getPhoneId());
|
|
voiceCapablePhoneMap.put(phone, new HashSet<>(3));
|
|
}
|
|
}
|
|
|
|
Map<Phone, Set<Phone>> userAssociationPhoneMap = new HashMap<>(3);
|
|
// Map the voice capable phones to the others that have compatible user associations:
|
|
for (Phone phone1 : voiceCapablePhoneMap.keySet()) {
|
|
Set<Phone> phone1UserAssociationCompatiblePhones = new HashSet<>(3);
|
|
for (Phone phone2 : voiceCapablePhoneMap.keySet()) {
|
|
if (phone1.getPhoneId() == phone2.getPhoneId()) { continue; }
|
|
if (phonesHaveSameUserAssociation(phone1, phone2)) {
|
|
phone1UserAssociationCompatiblePhones.add(phone2);
|
|
}
|
|
}
|
|
userAssociationPhoneMap.put(phone1, phone1UserAssociationCompatiblePhones);
|
|
}
|
|
|
|
return userAssociationPhoneMap;
|
|
}
|
|
|
|
private Map<Integer, Set<Phone>> generateSimultaneousCallSupportMap(
|
|
Map<Phone, Set<Phone>> phoneMap) {
|
|
Map<Integer, Set<Phone>> simultaneousCallSubSupportMap = new HashMap<>(3);
|
|
|
|
// Initially populate simultaneousCallSubSupportMap based on the passed in phoneMap:
|
|
for (Phone phone : phoneMap.keySet()) {
|
|
simultaneousCallSubSupportMap.put(phone.getPhoneId(),
|
|
new HashSet<>(phoneMap.get(phone)));
|
|
}
|
|
|
|
// Remove phone combinations that don't support simultaneous calling from the support map:
|
|
for (Phone phone : phoneMap.keySet()) {
|
|
if (phone.isImsRegistered()) {
|
|
if (mPhoneConfigurationManager.isVirtualDsdaEnabled() ||
|
|
phone.isImsServiceSimultaneousCallingSupportCapable(mContext)) {
|
|
// Check if the transport types of each phone support simultaneous IMS calling:
|
|
int phone1TransportType = ((ImsPhone) phone.getImsPhone()).getTransportType();
|
|
if (phone1TransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
|
|
// The transport type of this phone is WLAN so all combos are supported:
|
|
continue;
|
|
}
|
|
for (Phone phone2 : phoneMap.keySet()) {
|
|
if (phone.getPhoneId() == phone2.getPhoneId()) { continue; }
|
|
if (!phonesSupportSimultaneousCallingViaCellularOrWlan(phone, phone2)) {
|
|
simultaneousCallSubSupportMap.get(phone.getPhoneId()).remove(phone2);
|
|
}
|
|
}
|
|
} else {
|
|
// IMS is registered, vDSDA is disabled, but IMS is not DSDA capable so
|
|
// clear the map for this phone:
|
|
simultaneousCallSubSupportMap.get(phone.getPhoneId()).clear();
|
|
}
|
|
} else {
|
|
// Check if this phone supports simultaneous cellular calling with other phones:
|
|
for (Phone phone2 : phoneMap.keySet()) {
|
|
if (phone.getPhoneId() == phone2.getPhoneId()) { continue; }
|
|
if (!phonesSupportSimultaneousCallingViaCellularOrWlan(phone, phone2)) {
|
|
simultaneousCallSubSupportMap.get(phone.getPhoneId()).remove(phone2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Log.v(LOG_TAG, "generateSimultaneousCallSupportMap: returning "
|
|
+ "simultaneousCallSubSupportMap = " +
|
|
getStringFromMap(simultaneousCallSubSupportMap));
|
|
return simultaneousCallSubSupportMap;
|
|
}
|
|
|
|
/**
|
|
* Determines whether the {@link Phone} instances have compatible user associations. To be
|
|
* considered compatible based on user association, both must be associated with the same
|
|
* {@link android.os.UserHandle} or both must be unassociated.
|
|
*/
|
|
private boolean phonesHaveSameUserAssociation(Phone phone1, Phone phone2) {
|
|
return Objects.equals(phone1.getUserHandle(), phone2.getUserHandle());
|
|
}
|
|
|
|
private boolean phonesSupportCellularSimultaneousCalling(Phone phone1, Phone phone2) {
|
|
Set<Integer> slotsSupportingSimultaneousCellularCalls =
|
|
mPhoneConfigurationManager.getSlotsSupportingSimultaneousCellularCalls();
|
|
Log.v(LOG_TAG, "phonesSupportCellularSimultaneousCalling: modem returned slots = " +
|
|
getStringFromSet(slotsSupportingSimultaneousCellularCalls));
|
|
if (slotsSupportingSimultaneousCellularCalls.contains(phone1.getPhoneId()) &&
|
|
slotsSupportingSimultaneousCellularCalls.contains(phone2.getPhoneId())) {
|
|
return true;
|
|
};
|
|
return false;
|
|
}
|
|
|
|
private boolean phonesSupportSimultaneousCallingViaCellularOrWlan(Phone phone1, Phone phone2) {
|
|
int phone2TransportType =
|
|
((ImsPhone) phone2.getImsPhone()).getTransportType();
|
|
return phone2TransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN ||
|
|
phonesSupportCellularSimultaneousCalling(phone1, phone2);
|
|
}
|
|
|
|
private void handleSimultaneousCallingSupportChanged() {
|
|
try {
|
|
Log.v(LOG_TAG, "handleSimultaneousCallingSupportChanged");
|
|
// Convert mSimultaneousCallPhoneSupportMap to a map of each subId to a set of the
|
|
// subIds it supports simultaneous calling with:
|
|
Map<Integer, Set<Integer>> simultaneousCallSubscriptionIdMap = new HashMap<>();
|
|
for (Integer phoneId : mSimultaneousCallPhoneSupportMap.keySet()) {
|
|
Phone phone = PhoneFactory.getPhone(phoneId);
|
|
if (phone == null) {
|
|
Log.wtf(LOG_TAG, "handleSimultaneousCallingSupportChanged: phoneId=" +
|
|
phoneId + " not found.");
|
|
return;
|
|
}
|
|
int subId = phone.getSubId();
|
|
Set<Integer> supportedSubscriptionIds = new HashSet<>(3);
|
|
for (Phone p : mSimultaneousCallPhoneSupportMap.get(phoneId)) {
|
|
supportedSubscriptionIds.add(p.getSubId());
|
|
}
|
|
simultaneousCallSubscriptionIdMap.put(subId, supportedSubscriptionIds);
|
|
}
|
|
|
|
// Notify listeners that simultaneous calling support has changed:
|
|
for (Listener l : mListeners) {
|
|
l.onSimultaneousCallingSupportChanged(simultaneousCallSubscriptionIdMap);
|
|
}
|
|
} catch (Exception e) {
|
|
Log.w(LOG_TAG, "handleSimultaneousCallingSupportChanged: Exception = " + e);
|
|
}
|
|
}
|
|
|
|
private String getStringFromMap(Map<Integer, Set<Phone>> phoneMap) {
|
|
StringBuilder sb = new StringBuilder();
|
|
for (Map.Entry<Integer, Set<Phone>> entry : phoneMap.entrySet()) {
|
|
sb.append("Phone ID=");
|
|
sb.append(entry.getKey());
|
|
sb.append(" - Simultaneous calling compatible phone IDs=[");
|
|
sb.append(entry.getValue().stream().map(Phone::getPhoneId).map(String::valueOf)
|
|
.collect(Collectors.joining(", ")));
|
|
sb.append("]; ");
|
|
}
|
|
return sb.toString();
|
|
}
|
|
|
|
private String getStringFromSet(Set<Integer> integerSet) {
|
|
return integerSet.stream().map(String::valueOf).collect(Collectors.joining(","));
|
|
}
|
|
}
|