| /* |
| * 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.hardware.radio.V1_0.CellInfoType; |
| 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.HandlerThread; |
| 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.CellIdentityGsm; |
| import android.telephony.CellIdentityLte; |
| import android.telephony.CellIdentityTdscdma; |
| import android.telephony.CellIdentityWcdma; |
| import android.telephony.LteVopsSupportInfo; |
| import android.telephony.NetworkRegistrationInfo; |
| import android.telephony.NetworkService; |
| import android.telephony.NetworkServiceCallback; |
| import android.telephony.Rlog; |
| import android.telephony.ServiceState; |
| import android.telephony.SubscriptionManager; |
| import android.telephony.TelephonyManager; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| /** |
| * 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; |
| |
| private class CellularNetworkServiceProvider extends NetworkServiceProvider { |
| |
| private final ConcurrentHashMap<Message, NetworkServiceCallback> mCallbackMap = |
| new ConcurrentHashMap<>(); |
| |
| private final Looper mLooper; |
| |
| private final HandlerThread mHandlerThread; |
| |
| private final Handler mHandler; |
| |
| private final Phone mPhone; |
| |
| CellularNetworkServiceProvider(int slotId) { |
| super(slotId); |
| |
| mPhone = PhoneFactory.getPhone(getSlotIndex()); |
| |
| mHandlerThread = new HandlerThread(CellularNetworkService.class.getSimpleName()); |
| mHandlerThread.start(); |
| mLooper = mHandlerThread.getLooper(); |
| mHandler = new Handler(mLooper) { |
| @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 NetworkRegistrationInfo createRegistrationStateFromVoiceRegState(Object result) { |
| int transportType = AccessNetworkConstants.TRANSPORT_TYPE_WWAN; |
| int domain = NetworkRegistrationInfo.DOMAIN_CS; |
| |
| 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 = |
| convertHalCellIdentityToCellIdentity(voiceRegState.cellIdentity); |
| |
| return new NetworkRegistrationInfo(domain, transportType, regState, |
| networkType, reasonForDenial, emergencyOnly, availableServices, |
| cellIdentity, 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 = |
| convertHalCellIdentityToCellIdentity(voiceRegState.cellIdentity); |
| |
| return new NetworkRegistrationInfo(domain, transportType, regState, |
| networkType, reasonForDenial, emergencyOnly, availableServices, |
| cellIdentity, cssSupported, roamingIndicator, systemIsInPrl, |
| defaultRoamingIndicator); |
| } |
| |
| return null; |
| } |
| |
| private NetworkRegistrationInfo createRegistrationStateFromDataRegState(Object result) { |
| int domain = NetworkRegistrationInfo.DOMAIN_PS; |
| int regState = NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN; |
| int transportType = AccessNetworkConstants.TRANSPORT_TYPE_WWAN; |
| 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); |
| |
| 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 = convertHalCellIdentityToCellIdentity(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 = convertHalCellIdentityToCellIdentity(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 = convertHalCellIdentityToCellIdentity(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; |
| } |
| |
| 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, maxDataCalls, |
| isDcNrRestricted, isNrAvailable, isEndcAvailable, lteVopsSupportInfo, |
| 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); |
| } |
| |
| private CellIdentity convertHalCellIdentityToCellIdentity( |
| android.hardware.radio.V1_0.CellIdentity cellIdentity) { |
| if (cellIdentity == null) { |
| return null; |
| } |
| |
| CellIdentity result = null; |
| switch(cellIdentity.cellInfoType) { |
| case CellInfoType.GSM: { |
| if (cellIdentity.cellIdentityGsm.size() == 1) { |
| android.hardware.radio.V1_0.CellIdentityGsm cellIdentityGsm = |
| cellIdentity.cellIdentityGsm.get(0); |
| result = new CellIdentityGsm(cellIdentityGsm); |
| } |
| break; |
| } |
| case CellInfoType.WCDMA: { |
| if (cellIdentity.cellIdentityWcdma.size() == 1) { |
| android.hardware.radio.V1_0.CellIdentityWcdma cellIdentityWcdma = |
| cellIdentity.cellIdentityWcdma.get(0); |
| result = new CellIdentityWcdma(cellIdentityWcdma); |
| } |
| break; |
| } |
| case CellInfoType.TD_SCDMA: { |
| if (cellIdentity.cellIdentityTdscdma.size() == 1) { |
| android.hardware.radio.V1_0.CellIdentityTdscdma cellIdentityTdscdma = |
| cellIdentity.cellIdentityTdscdma.get(0); |
| result = new CellIdentityTdscdma(cellIdentityTdscdma); |
| } |
| break; |
| } |
| case CellInfoType.LTE: { |
| if (cellIdentity.cellIdentityLte.size() == 1) { |
| android.hardware.radio.V1_0.CellIdentityLte cellIdentityLte = |
| cellIdentity.cellIdentityLte.get(0); |
| result = new CellIdentityLte(cellIdentityLte); |
| } |
| break; |
| } |
| case CellInfoType.CDMA: { |
| if (cellIdentity.cellIdentityCdma.size() == 1) { |
| android.hardware.radio.V1_0.CellIdentityCdma cellIdentityCdma = |
| cellIdentity.cellIdentityCdma.get(0); |
| result = new CellIdentityCdma(cellIdentityCdma); |
| } |
| break; |
| } |
| case CellInfoType.NONE: |
| default: |
| break; |
| } |
| |
| return result; |
| } |
| |
| private CellIdentity convertHalCellIdentityToCellIdentity( |
| android.hardware.radio.V1_2.CellIdentity cellIdentity) { |
| if (cellIdentity == null) { |
| return null; |
| } |
| |
| CellIdentity result = null; |
| switch(cellIdentity.cellInfoType) { |
| case CellInfoType.GSM: { |
| if (cellIdentity.cellIdentityGsm.size() == 1) { |
| android.hardware.radio.V1_2.CellIdentityGsm cellIdentityGsm = |
| cellIdentity.cellIdentityGsm.get(0); |
| result = new CellIdentityGsm(cellIdentityGsm); |
| } |
| break; |
| } |
| case CellInfoType.WCDMA: { |
| if (cellIdentity.cellIdentityWcdma.size() == 1) { |
| android.hardware.radio.V1_2.CellIdentityWcdma cellIdentityWcdma = |
| cellIdentity.cellIdentityWcdma.get(0); |
| result = new CellIdentityWcdma(cellIdentityWcdma); |
| } |
| break; |
| } |
| case CellInfoType.TD_SCDMA: { |
| if (cellIdentity.cellIdentityTdscdma.size() == 1) { |
| android.hardware.radio.V1_2.CellIdentityTdscdma cellIdentityTdscdma = |
| cellIdentity.cellIdentityTdscdma.get(0); |
| result = new CellIdentityTdscdma(cellIdentityTdscdma); |
| } |
| break; |
| } |
| case CellInfoType.LTE: { |
| if (cellIdentity.cellIdentityLte.size() == 1) { |
| android.hardware.radio.V1_2.CellIdentityLte cellIdentityLte = |
| cellIdentity.cellIdentityLte.get(0); |
| result = new CellIdentityLte(cellIdentityLte); |
| } |
| break; |
| } |
| case CellInfoType.CDMA: { |
| if (cellIdentity.cellIdentityCdma.size() == 1) { |
| android.hardware.radio.V1_2.CellIdentityCdma cellIdentityCdma = |
| cellIdentity.cellIdentityCdma.get(0); |
| result = new CellIdentityCdma(cellIdentityCdma); |
| } |
| break; |
| } |
| case CellInfoType.NONE: |
| default: |
| break; |
| } |
| |
| return result; |
| } |
| |
| @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(); |
| mHandlerThread.quit(); |
| 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); |
| } |
| } |