blob: 3bb38147bee004314bfd536b3095efb96ba2b959 [file] [log] [blame]
/*
* Copyright 2017 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.annotation.Nullable;
import android.hardware.radio.V1_0.RegState;
import android.hardware.radio.V1_4.DataRegStateResult.VopsInfo.hidl_discriminator;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.telephony.AccessNetworkConstants;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.CellIdentity;
import android.telephony.CellIdentityCdma;
import android.telephony.LteVopsSupportInfo;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.NetworkService;
import android.telephony.NetworkServiceCallback;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import com.android.telephony.Rlog;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Implementation of network services for Cellular. It's a service that handles network requests
* for Cellular. It passes the requests to inner CellularNetworkServiceProvider which has a
* handler thread for each slot.
*/
public class CellularNetworkService extends NetworkService {
private static final boolean DBG = false;
private static final String TAG = CellularNetworkService.class.getSimpleName();
private static final int GET_CS_REGISTRATION_STATE_DONE = 1;
private static final int GET_PS_REGISTRATION_STATE_DONE = 2;
private static final int NETWORK_REGISTRATION_STATE_CHANGED = 3;
// From 24.008 6.1.3.0 and 10.5.6.2 the maximum number of PDP Contexts is 16.
private static final int MAX_DATA_CALLS = 16;
private class CellularNetworkServiceProvider extends NetworkServiceProvider {
private final Map<Message, NetworkServiceCallback> mCallbackMap = new HashMap<>();
private final Handler mHandler;
private final Phone mPhone;
CellularNetworkServiceProvider(int slotId) {
super(slotId);
mPhone = PhoneFactory.getPhone(getSlotIndex());
mHandler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(Message message) {
NetworkServiceCallback callback = mCallbackMap.remove(message);
AsyncResult ar;
switch (message.what) {
case GET_CS_REGISTRATION_STATE_DONE:
case GET_PS_REGISTRATION_STATE_DONE:
if (callback == null) return;
ar = (AsyncResult) message.obj;
int domain = (message.what == GET_CS_REGISTRATION_STATE_DONE)
? NetworkRegistrationInfo.DOMAIN_CS
: NetworkRegistrationInfo.DOMAIN_PS;
NetworkRegistrationInfo netState =
getRegistrationStateFromResult(ar.result, domain);
int resultCode;
if (ar.exception != null || netState == null) {
resultCode = NetworkServiceCallback.RESULT_ERROR_FAILED;
} else {
resultCode = NetworkServiceCallback.RESULT_SUCCESS;
}
try {
if (DBG) {
log("Calling onRequestNetworkRegistrationInfoComplete."
+ "resultCode = " + resultCode
+ ", netState = " + netState);
}
callback.onRequestNetworkRegistrationInfoComplete(
resultCode, netState);
} catch (Exception e) {
loge("Exception: " + e);
}
break;
case NETWORK_REGISTRATION_STATE_CHANGED:
notifyNetworkRegistrationInfoChanged();
break;
default:
return;
}
}
};
mPhone.mCi.registerForNetworkStateChanged(
mHandler, NETWORK_REGISTRATION_STATE_CHANGED, null);
}
private int getRegStateFromHalRegState(int halRegState) {
switch (halRegState) {
case RegState.NOT_REG_MT_NOT_SEARCHING_OP:
case RegState.NOT_REG_MT_NOT_SEARCHING_OP_EM:
return NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
case RegState.REG_HOME:
return NetworkRegistrationInfo.REGISTRATION_STATE_HOME;
case RegState.NOT_REG_MT_SEARCHING_OP:
case RegState.NOT_REG_MT_SEARCHING_OP_EM:
return NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING;
case RegState.REG_DENIED:
case RegState.REG_DENIED_EM:
return NetworkRegistrationInfo.REGISTRATION_STATE_DENIED;
case RegState.UNKNOWN:
case RegState.UNKNOWN_EM:
return NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN;
case RegState.REG_ROAMING:
return NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING;
default:
return NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
}
}
private boolean isEmergencyOnly(int halRegState) {
switch (halRegState) {
case RegState.NOT_REG_MT_NOT_SEARCHING_OP_EM:
case RegState.NOT_REG_MT_SEARCHING_OP_EM:
case RegState.REG_DENIED_EM:
case RegState.UNKNOWN_EM:
return true;
case RegState.NOT_REG_MT_NOT_SEARCHING_OP:
case RegState.REG_HOME:
case RegState.NOT_REG_MT_SEARCHING_OP:
case RegState.REG_DENIED:
case RegState.UNKNOWN:
case RegState.REG_ROAMING:
default:
return false;
}
}
private List<Integer> getAvailableServices(int regState, int domain,
boolean emergencyOnly) {
List<Integer> availableServices = new ArrayList<>();
// In emergency only states, only SERVICE_TYPE_EMERGENCY is available.
// Otherwise, certain services are available only if it's registered on home or roaming
// network.
if (emergencyOnly) {
availableServices.add(NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY);
} else if (regState == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING
|| regState == NetworkRegistrationInfo.REGISTRATION_STATE_HOME) {
if (domain == NetworkRegistrationInfo.DOMAIN_PS) {
availableServices.add(NetworkRegistrationInfo.SERVICE_TYPE_DATA);
} else if (domain == NetworkRegistrationInfo.DOMAIN_CS) {
availableServices.add(NetworkRegistrationInfo.SERVICE_TYPE_VOICE);
availableServices.add(NetworkRegistrationInfo.SERVICE_TYPE_SMS);
availableServices.add(NetworkRegistrationInfo.SERVICE_TYPE_VIDEO);
}
}
return availableServices;
}
private NetworkRegistrationInfo getRegistrationStateFromResult(Object result, int domain) {
if (result == null) {
return null;
}
// TODO: unify when voiceRegStateResult and DataRegStateResult are unified.
if (domain == NetworkRegistrationInfo.DOMAIN_CS) {
return createRegistrationStateFromVoiceRegState(result);
} else if (domain == NetworkRegistrationInfo.DOMAIN_PS) {
return createRegistrationStateFromDataRegState(result);
} else {
return null;
}
}
private @NonNull String getPlmnFromCellIdentity(@Nullable final CellIdentity ci) {
if (ci == null || ci instanceof CellIdentityCdma) return "";
final String mcc = ci.getMccString();
final String mnc = ci.getMncString();
if (TextUtils.isEmpty(mcc) || TextUtils.isEmpty(mnc)) return "";
return mcc + mnc;
}
private NetworkRegistrationInfo createRegistrationStateFromVoiceRegState(Object result) {
final int transportType = AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
final int domain = NetworkRegistrationInfo.DOMAIN_CS;
// 1.5 at the top so that we can do an "early exit" from the method
if (result instanceof android.hardware.radio.V1_5.RegStateResult) {
return getNetworkRegistrationInfo(
domain,
transportType,
(android.hardware.radio.V1_5.RegStateResult) result);
} else if (result instanceof android.hardware.radio.V1_0.VoiceRegStateResult) {
android.hardware.radio.V1_0.VoiceRegStateResult voiceRegState =
(android.hardware.radio.V1_0.VoiceRegStateResult) result;
int regState = getRegStateFromHalRegState(voiceRegState.regState);
int networkType = ServiceState.rilRadioTechnologyToNetworkType(voiceRegState.rat);
if (networkType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
networkType = TelephonyManager.NETWORK_TYPE_LTE;
}
int reasonForDenial = voiceRegState.reasonForDenial;
boolean emergencyOnly = isEmergencyOnly(voiceRegState.regState);
boolean cssSupported = voiceRegState.cssSupported;
int roamingIndicator = voiceRegState.roamingIndicator;
int systemIsInPrl = voiceRegState.systemIsInPrl;
int defaultRoamingIndicator = voiceRegState.defaultRoamingIndicator;
List<Integer> availableServices = getAvailableServices(
regState, domain, emergencyOnly);
CellIdentity cellIdentity = CellIdentity.create(voiceRegState.cellIdentity);
final String rplmn = getPlmnFromCellIdentity(cellIdentity);
return new NetworkRegistrationInfo(domain, transportType, regState,
networkType, reasonForDenial, emergencyOnly, availableServices,
cellIdentity, rplmn, cssSupported, roamingIndicator, systemIsInPrl,
defaultRoamingIndicator);
} else if (result instanceof android.hardware.radio.V1_2.VoiceRegStateResult) {
android.hardware.radio.V1_2.VoiceRegStateResult voiceRegState =
(android.hardware.radio.V1_2.VoiceRegStateResult) result;
int regState = getRegStateFromHalRegState(voiceRegState.regState);
int networkType = ServiceState.rilRadioTechnologyToNetworkType(voiceRegState.rat);
if (networkType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
networkType = TelephonyManager.NETWORK_TYPE_LTE;
}
int reasonForDenial = voiceRegState.reasonForDenial;
boolean emergencyOnly = isEmergencyOnly(voiceRegState.regState);
boolean cssSupported = voiceRegState.cssSupported;
int roamingIndicator = voiceRegState.roamingIndicator;
int systemIsInPrl = voiceRegState.systemIsInPrl;
int defaultRoamingIndicator = voiceRegState.defaultRoamingIndicator;
List<Integer> availableServices = getAvailableServices(
regState, domain, emergencyOnly);
CellIdentity cellIdentity = CellIdentity.create(voiceRegState.cellIdentity);
final String rplmn = getPlmnFromCellIdentity(cellIdentity);
return new NetworkRegistrationInfo(domain, transportType, regState,
networkType, reasonForDenial, emergencyOnly, availableServices,
cellIdentity, rplmn, cssSupported, roamingIndicator, systemIsInPrl,
defaultRoamingIndicator);
}
return null;
}
private NetworkRegistrationInfo createRegistrationStateFromDataRegState(Object result) {
final int domain = NetworkRegistrationInfo.DOMAIN_PS;
final int transportType = AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
int regState = NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN;
int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
int reasonForDenial = 0;
boolean isUsingCarrierAggregation = false;
boolean emergencyOnly = false;
int maxDataCalls = 0;
CellIdentity cellIdentity;
boolean isEndcAvailable = false;
boolean isNrAvailable = false;
boolean isDcNrRestricted = false;
LteVopsSupportInfo lteVopsSupportInfo =
new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE,
LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE);
// 1.5 at the top so that we can do an "early exit" from the method
if (result instanceof android.hardware.radio.V1_5.RegStateResult) {
return getNetworkRegistrationInfo(
domain,
transportType,
(android.hardware.radio.V1_5.RegStateResult) result);
} else if (result instanceof android.hardware.radio.V1_0.DataRegStateResult) {
android.hardware.radio.V1_0.DataRegStateResult dataRegState =
(android.hardware.radio.V1_0.DataRegStateResult) result;
regState = getRegStateFromHalRegState(dataRegState.regState);
networkType = ServiceState.rilRadioTechnologyToNetworkType(dataRegState.rat);
reasonForDenial = dataRegState.reasonDataDenied;
emergencyOnly = isEmergencyOnly(dataRegState.regState);
maxDataCalls = dataRegState.maxDataCalls;
cellIdentity = CellIdentity.create(dataRegState.cellIdentity);
} else if (result instanceof android.hardware.radio.V1_2.DataRegStateResult) {
android.hardware.radio.V1_2.DataRegStateResult dataRegState =
(android.hardware.radio.V1_2.DataRegStateResult) result;
regState = getRegStateFromHalRegState(dataRegState.regState);
networkType = ServiceState.rilRadioTechnologyToNetworkType(dataRegState.rat);
reasonForDenial = dataRegState.reasonDataDenied;
emergencyOnly = isEmergencyOnly(dataRegState.regState);
maxDataCalls = dataRegState.maxDataCalls;
cellIdentity = CellIdentity.create(dataRegState.cellIdentity);
} else if (result instanceof android.hardware.radio.V1_4.DataRegStateResult) {
android.hardware.radio.V1_4.DataRegStateResult dataRegState =
(android.hardware.radio.V1_4.DataRegStateResult) result;
regState = getRegStateFromHalRegState(dataRegState.base.regState);
networkType = ServiceState.rilRadioTechnologyToNetworkType(dataRegState.base.rat);
reasonForDenial = dataRegState.base.reasonDataDenied;
emergencyOnly = isEmergencyOnly(dataRegState.base.regState);
maxDataCalls = dataRegState.base.maxDataCalls;
cellIdentity = CellIdentity.create(dataRegState.base.cellIdentity);
android.hardware.radio.V1_4.NrIndicators nrIndicators = dataRegState.nrIndicators;
// Check for lteVopsInfo only if its initialized and RAT is EUTRAN
if (dataRegState.vopsInfo.getDiscriminator() == hidl_discriminator.lteVopsInfo
&& ServiceState.rilRadioTechnologyToAccessNetworkType(dataRegState.base.rat)
== AccessNetworkType.EUTRAN) {
android.hardware.radio.V1_4.LteVopsInfo vopsSupport =
dataRegState.vopsInfo.lteVopsInfo();
lteVopsSupportInfo = convertHalLteVopsSupportInfo(vopsSupport.isVopsSupported,
vopsSupport.isEmcBearerSupported);
} else {
lteVopsSupportInfo =
new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE,
LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE);
}
isEndcAvailable = nrIndicators.isEndcAvailable;
isNrAvailable = nrIndicators.isNrAvailable;
isDcNrRestricted = nrIndicators.isDcNrRestricted;
} else {
loge("Unknown type of DataRegStateResult " + result);
return null;
}
String rplmn = getPlmnFromCellIdentity(cellIdentity);
List<Integer> availableServices = getAvailableServices(
regState, domain, emergencyOnly);
if (networkType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
isUsingCarrierAggregation = true;
networkType = TelephonyManager.NETWORK_TYPE_LTE;
}
return new NetworkRegistrationInfo(domain, transportType, regState, networkType,
reasonForDenial, emergencyOnly, availableServices, cellIdentity, rplmn,
maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable,
lteVopsSupportInfo, isUsingCarrierAggregation);
}
private @NonNull NetworkRegistrationInfo getNetworkRegistrationInfo(
int domain, int transportType,
android.hardware.radio.V1_5.RegStateResult regResult) {
// Perform common conversions that aren't domain specific
final int regState = getRegStateFromHalRegState(regResult.regState);
final boolean isEmergencyOnly = isEmergencyOnly(regResult.regState);
final List<Integer> availableServices = getAvailableServices(
regState, domain, isEmergencyOnly);
final int rejectCause = regResult.reasonForDenial;
final CellIdentity cellIdentity = CellIdentity.create(regResult.cellIdentity);
final String rplmn = regResult.registeredPlmn;
final int reasonForDenial = regResult.reasonForDenial;
// Network Type fixup for carrier aggregation
int networkType = ServiceState.rilRadioTechnologyToNetworkType(regResult.rat);
// In earlier versions of the HAL, LTE_CA was allowed to indicate that the device
// is on CA; however, that has been superseded by the PHYSICAL_CHANNEL_CONFIG signal.
// Because some vendors provide both NETWORK_TYPE_LTE_CA *and* PHYSICAL_CHANNEL_CONFIG,
// this tweak is left for compatibility; however, the network type is no longer allowed
// to be used to declare that carrier aggregation is in effect, because the other
// signal provides a much richer information set, and we want to mitigate confusion in
// how CA information is being provided.
if (networkType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
networkType = TelephonyManager.NETWORK_TYPE_LTE;
}
// Conditional parameters for specific RANs
boolean cssSupported = false;
int roamingIndicator = 0;
int systemIsInPrl = 0;
int defaultRoamingIndicator = 0;
boolean isEndcAvailable = false;
boolean isNrAvailable = false;
boolean isDcNrRestricted = false;
LteVopsSupportInfo vopsInfo = new LteVopsSupportInfo(
LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE,
LteVopsSupportInfo.LTE_STATUS_NOT_AVAILABLE);
switch (regResult.accessTechnologySpecificInfo.getDiscriminator()) {
case android.hardware.radio.V1_5.RegStateResult
.AccessTechnologySpecificInfo.hidl_discriminator.cdmaInfo:
android.hardware.radio.V1_5.RegStateResult
.AccessTechnologySpecificInfo.Cdma2000RegistrationInfo cdmaInfo =
regResult.accessTechnologySpecificInfo.cdmaInfo();
cssSupported = cdmaInfo.cssSupported;
roamingIndicator = cdmaInfo.roamingIndicator;
systemIsInPrl = cdmaInfo.systemIsInPrl;
defaultRoamingIndicator = cdmaInfo.defaultRoamingIndicator;
break;
case android.hardware.radio.V1_5.RegStateResult
.AccessTechnologySpecificInfo.hidl_discriminator.eutranInfo:
android.hardware.radio.V1_5.RegStateResult
.AccessTechnologySpecificInfo.EutranRegistrationInfo eutranInfo =
regResult.accessTechnologySpecificInfo.eutranInfo();
isDcNrRestricted = eutranInfo.nrIndicators.isDcNrRestricted;
isNrAvailable = eutranInfo.nrIndicators.isNrAvailable;
isEndcAvailable = eutranInfo.nrIndicators.isEndcAvailable;
vopsInfo = convertHalLteVopsSupportInfo(
eutranInfo.lteVopsInfo.isVopsSupported,
eutranInfo.lteVopsInfo.isEmcBearerSupported);
break;
default:
log("No access tech specific info passes for RegStateResult");
break;
}
// build the result based on the domain for the request
switch(domain) {
case NetworkRegistrationInfo.DOMAIN_CS:
return new NetworkRegistrationInfo(domain, transportType, regState,
networkType, reasonForDenial, isEmergencyOnly, availableServices,
cellIdentity, rplmn, cssSupported, roamingIndicator, systemIsInPrl,
defaultRoamingIndicator);
default:
loge("Unknown domain passed to CellularNetworkService= " + domain);
// fall through
case NetworkRegistrationInfo.DOMAIN_PS:
return new NetworkRegistrationInfo(domain, transportType, regState, networkType,
reasonForDenial, isEmergencyOnly, availableServices, cellIdentity,
rplmn, MAX_DATA_CALLS, isDcNrRestricted, isNrAvailable, isEndcAvailable,
vopsInfo, false /* isUsingCarrierAggregation */);
}
}
private LteVopsSupportInfo convertHalLteVopsSupportInfo(
boolean vopsSupport, boolean emcBearerSupport) {
int vops = LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED;
int emergency = LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED;
if (vopsSupport) {
vops = LteVopsSupportInfo.LTE_STATUS_SUPPORTED;
}
if (emcBearerSupport) {
emergency = LteVopsSupportInfo.LTE_STATUS_SUPPORTED;
}
return new LteVopsSupportInfo(vops, emergency);
}
@Override
public void requestNetworkRegistrationInfo(int domain, NetworkServiceCallback callback) {
if (DBG) log("requestNetworkRegistrationInfo for domain " + domain);
Message message = null;
if (domain == NetworkRegistrationInfo.DOMAIN_CS) {
message = Message.obtain(mHandler, GET_CS_REGISTRATION_STATE_DONE);
mCallbackMap.put(message, callback);
mPhone.mCi.getVoiceRegistrationState(message);
} else if (domain == NetworkRegistrationInfo.DOMAIN_PS) {
message = Message.obtain(mHandler, GET_PS_REGISTRATION_STATE_DONE);
mCallbackMap.put(message, callback);
mPhone.mCi.getDataRegistrationState(message);
} else {
loge("requestNetworkRegistrationInfo invalid domain " + domain);
callback.onRequestNetworkRegistrationInfoComplete(
NetworkServiceCallback.RESULT_ERROR_INVALID_ARG, null);
}
}
@Override
public void close() {
mCallbackMap.clear();
mPhone.mCi.unregisterForNetworkStateChanged(mHandler);
}
}
@Override
public NetworkServiceProvider onCreateNetworkServiceProvider(int slotIndex) {
if (DBG) log("Cellular network service created for slot " + slotIndex);
if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
loge("Tried to Cellular network service with invalid slotId " + slotIndex);
return null;
}
return new CellularNetworkServiceProvider(slotIndex);
}
private void log(String s) {
Rlog.d(TAG, s);
}
private void loge(String s) {
Rlog.e(TAG, s);
}
}