script-astra/Android/Sdk/sources/android-35/com/android/internal/telephony/RadioConfig.java
localadmin 4380f00a78 init
2025-01-20 18:15:20 +03:00

678 lines
25 KiB
Java

/*
* Copyright (C) 2018 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 static android.telephony.PhoneCapability.DEVICE_NR_CAPABILITY_NSA;
import static android.telephony.PhoneCapability.DEVICE_NR_CAPABILITY_SA;
import static com.android.internal.telephony.RILConstants.RADIO_NOT_AVAILABLE;
import static com.android.internal.telephony.RILConstants.REQUEST_NOT_SUPPORTED;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_HAL_DEVICE_CAPABILITIES;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_PHONE_CAPABILITY;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SIMULTANEOUS_CALLING_SUPPORT;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SLOT_STATUS;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SET_PREFERRED_DATA_MODEM;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SWITCH_DUAL_SIM_CONFIG;
import android.content.Context;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Registrant;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.Trace;
import android.os.WorkSource;
import android.telephony.TelephonyManager;
import android.telephony.UiccSlotMapping;
import android.util.SparseArray;
import com.android.telephony.Rlog;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicLong;
/**
* This class provides wrapper APIs for IRadioConfig interface.
*/
public class RadioConfig extends Handler {
private static final String TAG = "RadioConfig";
private static final boolean DBG = true;
private static final boolean VDBG = false; //STOPSHIP if true
private static final Object sLock = new Object();
static final int EVENT_HIDL_SERVICE_DEAD = 1;
static final int EVENT_AIDL_SERVICE_DEAD = 2;
private final boolean mIsMobileNetworkSupported;
private final SparseArray<RILRequest> mRequestList = new SparseArray<>();
/* default work source which will blame phone process */
private final WorkSource mDefaultWorkSource;
private final int[] mDeviceNrCapabilities;
private final AtomicLong mRadioConfigProxyCookie = new AtomicLong(0);
private final RadioConfigProxy mRadioConfigProxy;
private MockModem mMockModem;
private static Context sContext;
private static RadioConfig sRadioConfig;
protected Registrant mSimSlotStatusRegistrant;
protected Registrant mSimultaneousCallingSupportStatusRegistrant;
private boolean isMobileDataCapable(Context context) {
final TelephonyManager tm = context.getSystemService(TelephonyManager.class);
return tm != null && tm.isDataCapable();
}
private RadioConfig(Context context, HalVersion radioHalVersion) {
mIsMobileNetworkSupported = isMobileDataCapable(context);
mRadioConfigProxy = new RadioConfigProxy(this, radioHalVersion);
mDefaultWorkSource = new WorkSource(context.getApplicationInfo().uid,
context.getPackageName());
boolean is5gStandalone = context.getResources().getBoolean(
com.android.internal.R.bool.config_telephony5gStandalone);
boolean is5gNonStandalone = context.getResources().getBoolean(
com.android.internal.R.bool.config_telephony5gNonStandalone);
if (!is5gStandalone && !is5gNonStandalone) {
mDeviceNrCapabilities = new int[0];
} else {
List<Integer> list = new ArrayList<>();
if (is5gNonStandalone) {
list.add(DEVICE_NR_CAPABILITY_NSA);
}
if (is5gStandalone) {
list.add(DEVICE_NR_CAPABILITY_SA);
}
mDeviceNrCapabilities = list.stream().mapToInt(Integer::valueOf).toArray();
}
}
/**
* Returns the singleton static instance of RadioConfig
*/
public static RadioConfig getInstance() {
synchronized (sLock) {
if (sRadioConfig == null) {
throw new RuntimeException(
"RadioConfig.getInstance can't be called before make()");
}
return sRadioConfig;
}
}
/**
* Makes the radio config based on the context and the radio hal version passed in
*/
public static RadioConfig make(Context c, HalVersion radioHalVersion) {
synchronized (sLock) {
if (sRadioConfig != null) {
throw new RuntimeException("RadioConfig.make() should only be called once");
}
sContext = c;
sRadioConfig = new RadioConfig(c, radioHalVersion);
return sRadioConfig;
}
}
@Override
public void handleMessage(Message message) {
if (message.what == EVENT_HIDL_SERVICE_DEAD) {
logd("handleMessage: EVENT_HIDL_SERVICE_DEAD cookie = " + message.obj
+ " mRadioConfigProxyCookie = " + mRadioConfigProxyCookie.get());
if ((long) message.obj == mRadioConfigProxyCookie.get()) {
resetProxyAndRequestList("EVENT_HIDL_SERVICE_DEAD", null);
}
} else if (message.what == EVENT_AIDL_SERVICE_DEAD) {
logd("handleMessage: EVENT_AIDL_SERVICE_DEAD mRadioConfigProxyCookie = "
+ mRadioConfigProxyCookie.get());
resetProxyAndRequestList("EVENT_AIDL_SERVICE_DEAD", null);
}
}
/**
* Release each request in mRequestList then clear the list
* @param error is the RIL_Errno sent back
* @param loggable true means to print all requests in mRequestList
*/
private void clearRequestList(int error, boolean loggable) {
RILRequest rr;
synchronized (mRequestList) {
int count = mRequestList.size();
if (DBG && loggable) {
logd("clearRequestList: mRequestList=" + count);
}
for (int i = 0; i < count; i++) {
rr = mRequestList.valueAt(i);
if (DBG && loggable) {
logd(i + ": [" + rr.mSerial + "] " + RILUtils.requestToString(rr.mRequest));
}
rr.onError(error, null);
rr.release();
}
mRequestList.clear();
}
}
private void resetProxyAndRequestList(String caller, Exception e) {
loge(caller + ": " + e);
mRadioConfigProxy.clear();
// increment the cookie so that death notification can be ignored
mRadioConfigProxyCookie.incrementAndGet();
RILRequest.resetSerial();
// Clear request list on close
clearRequestList(RADIO_NOT_AVAILABLE, false);
getRadioConfigProxy(null);
}
/**
* Returns a holder that has either:
* - getV1() -> {@link android.hardware.radio.config.V1_0.IRadioConfig}
* - getV2() -> {@link android.hardware.radio.config.IRadioConfig}
* that returns corresponding hal implementation
*/
public RadioConfigProxy getRadioConfigProxy(Message result) {
if (!mIsMobileNetworkSupported) {
if (VDBG) logd("getRadioConfigProxy: Not calling getService(): wifi-only");
if (result != null) {
AsyncResult.forMessage(result, null,
CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
result.sendToTarget();
}
mRadioConfigProxy.clear();
return mRadioConfigProxy;
}
if (!mRadioConfigProxy.isEmpty()) {
return mRadioConfigProxy;
}
updateRadioConfigProxy();
if (mRadioConfigProxy.isEmpty() && result != null) {
AsyncResult.forMessage(
result, null, CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
result.sendToTarget();
}
return mRadioConfigProxy;
}
/**
* Request to enable/disable the mock modem service.
* This is invoked from shell commands during CTS testing only.
*
* @param serviceName the service name we want to bind to
*/
public boolean setModemService(String serviceName) {
boolean serviceBound = true;
if (serviceName != null) {
logd("Overriding connected service to MockModemService");
mMockModem = null;
mMockModem = new MockModem(sContext, serviceName);
if (mMockModem == null) {
loge("MockModem creation failed.");
return false;
}
mMockModem.bindToMockModemService(MockModem.RADIOCONFIG_SERVICE);
int retryCount = 0;
IBinder binder;
do {
binder = mMockModem.getServiceBinder(MockModem.RADIOCONFIG_SERVICE);
retryCount++;
if (binder == null) {
logd("Retry(" + retryCount + ") Mock RadioConfig");
try {
Thread.sleep(MockModem.BINDER_RETRY_MILLIS);
} catch (InterruptedException e) {
}
}
} while ((binder == null) && (retryCount < MockModem.BINDER_MAX_RETRY));
if (binder == null) {
loge("Mock RadioConfig bind fail");
serviceBound = false;
}
if (serviceBound) resetProxyAndRequestList("EVENT_HIDL_SERVICE_DEAD", null);
}
if ((serviceName == null) || (!serviceBound)) {
if (serviceBound) logd("Unbinding to mock RadioConfig service");
if (mMockModem != null) {
mMockModem = null;
resetProxyAndRequestList("EVENT_AIDL_SERVICE_DEAD", null);
}
}
return serviceBound;
}
private void updateRadioConfigProxy() {
IBinder service;
if (mMockModem == null) {
service = ServiceManager.waitForDeclaredService(
android.hardware.radio.config.IRadioConfig.DESCRIPTOR + "/default");
} else {
// Binds to Mock RadioConfig Service
service = mMockModem.getServiceBinder(MockModem.RADIOCONFIG_SERVICE);
}
if (service != null) {
mRadioConfigProxy.setAidl(
android.hardware.radio.config.IRadioConfig.Stub.asInterface(service));
}
if (mRadioConfigProxy.isEmpty()) {
try {
mRadioConfigProxy.setHidl(RIL.RADIO_HAL_VERSION_1_3,
android.hardware.radio.config.V1_3.IRadioConfig.getService(true));
} catch (RemoteException | NoSuchElementException e) {
mRadioConfigProxy.clear();
loge("getHidlRadioConfigProxy1_3: RadioConfigProxy getService: " + e);
}
}
if (mRadioConfigProxy.isEmpty()) {
try {
mRadioConfigProxy.setHidl(RIL.RADIO_HAL_VERSION_1_1,
android.hardware.radio.config.V1_1.IRadioConfig.getService(true));
} catch (RemoteException | NoSuchElementException e) {
mRadioConfigProxy.clear();
loge("getHidlRadioConfigProxy1_1: RadioConfigProxy getService | linkToDeath: " + e);
}
}
if (mRadioConfigProxy.isEmpty()) {
loge("IRadioConfig <1.1 is no longer supported.");
}
if (!mRadioConfigProxy.isEmpty()) {
try {
mRadioConfigProxy.linkToDeath(mRadioConfigProxyCookie.incrementAndGet());
mRadioConfigProxy.setResponseFunctions(this);
return;
} catch (RemoteException e) {
mRadioConfigProxy.clear();
loge("RadioConfigProxy: failed to linkToDeath() or setResponseFunction()");
}
}
loge("getRadioConfigProxy: mRadioConfigProxy == null");
}
private RILRequest obtainRequest(int request, Message result, WorkSource workSource) {
RILRequest rr = RILRequest.obtain(request, result, workSource);
Trace.asyncTraceForTrackBegin(
Trace.TRACE_TAG_NETWORK, "RIL", RILUtils.requestToString(rr.mRequest), rr.mSerial);
synchronized (mRequestList) {
mRequestList.append(rr.mSerial, rr);
}
return rr;
}
private RILRequest findAndRemoveRequestFromList(int serial) {
RILRequest rr;
synchronized (mRequestList) {
rr = mRequestList.get(serial);
if (rr != null) {
Trace.asyncTraceForTrackEnd(
Trace.TRACE_TAG_NETWORK, "RIL", rr.mSerial);
mRequestList.remove(serial);
}
}
return rr;
}
/**
* This is a helper function to be called when a RadioConfigResponse callback is called.
* It finds and returns RILRequest corresponding to the response if one is found.
* @param responseInfo RadioResponseInfo received in response callback
* @return RILRequest corresponding to the response
*/
public RILRequest processResponse(android.hardware.radio.RadioResponseInfo responseInfo) {
int serial = responseInfo.serial;
int error = responseInfo.error;
int type = responseInfo.type;
if (type != android.hardware.radio.RadioResponseType.SOLICITED) {
loge("processResponse: Unexpected response type " + type);
}
RILRequest rr = findAndRemoveRequestFromList(serial);
if (rr == null) {
loge("processResponse: Unexpected response! serial: " + serial + " error: " + error);
return null;
}
return rr;
}
/**
* This is a helper function to be called when a RadioConfigResponse callback is called.
* It finds and returns RILRequest corresponding to the response if one is found.
* @param responseInfo RadioResponseInfo received in response callback
* @return RILRequest corresponding to the response
*/
public RILRequest processResponse(android.hardware.radio.V1_0.RadioResponseInfo responseInfo) {
int serial = responseInfo.serial;
int error = responseInfo.error;
int type = responseInfo.type;
if (type != android.hardware.radio.RadioResponseType.SOLICITED) {
loge("processResponse: Unexpected response type " + type);
}
RILRequest rr = findAndRemoveRequestFromList(serial);
if (rr == null) {
loge("processResponse: Unexpected response! serial: " + serial + " error: " + error);
return null;
}
return rr;
}
/**
* This is a helper function to be called when a RadioConfigResponse callback is called.
* It finds and returns RILRequest corresponding to the response if one is found.
* @param responseInfo RadioResponseInfo received in response callback
* @return RILRequest corresponding to the response
*/
public RILRequest processResponse_1_6(
android.hardware.radio.V1_6.RadioResponseInfo responseInfo) {
int serial = responseInfo.serial;
int error = responseInfo.error;
int type = responseInfo.type;
if (type != android.hardware.radio.RadioResponseType.SOLICITED) {
loge("processResponse: Unexpected response type " + type);
}
RILRequest rr = findAndRemoveRequestFromList(serial);
if (rr == null) {
loge("processResponse: Unexpected response! serial: " + serial + " error: " + error);
return null;
}
return rr;
}
/**
* Wrapper function for IRadioConfig.getSimSlotsStatus().
*/
public void getSimSlotsStatus(Message result) {
RadioConfigProxy proxy = getRadioConfigProxy(result);
if (proxy.isEmpty()) return;
RILRequest rr = obtainRequest(RIL_REQUEST_GET_SLOT_STATUS, result, mDefaultWorkSource);
if (DBG) {
logd(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
}
try {
proxy.getSimSlotStatus(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
resetProxyAndRequestList("getSimSlotsStatus", e);
}
}
/**
* Wrapper function for IRadioConfig.setPreferredDataModem(int modemId).
*/
public void setPreferredDataModem(int modemId, Message result) {
RadioConfigProxy proxy = getRadioConfigProxy(null);
if (proxy.isEmpty()) return;
if (!isSetPreferredDataCommandSupported()) {
if (result != null) {
AsyncResult.forMessage(result, null,
CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
result.sendToTarget();
}
return;
}
RILRequest rr = obtainRequest(RIL_REQUEST_SET_PREFERRED_DATA_MODEM,
result, mDefaultWorkSource);
if (DBG) {
logd(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
}
try {
proxy.setPreferredDataModem(rr.mSerial, modemId);
} catch (RemoteException | RuntimeException e) {
resetProxyAndRequestList("setPreferredDataModem", e);
}
}
/**
* Wrapper function for IRadioConfig.getSimultaneousCallingSupport().
*/
public void updateSimultaneousCallingSupport(Message result) {
RadioConfigProxy proxy = getRadioConfigProxy(null);
if (proxy.isEmpty()) return;
if (proxy.getVersion().less(RIL.RADIO_HAL_VERSION_2_2)) {
if (result != null) {
AsyncResult.forMessage(result, null,
CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
result.sendToTarget();
}
return;
}
RILRequest rr = obtainRequest(RIL_REQUEST_GET_SIMULTANEOUS_CALLING_SUPPORT, result,
mDefaultWorkSource);
if (DBG) {
logd(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
}
try {
proxy.updateSimultaneousCallingSupport(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
resetProxyAndRequestList("updateSimultaneousCallingSupport", e);
}
}
/**
* Wrapper function for IRadioConfig.getPhoneCapability().
*/
public void getPhoneCapability(Message result) {
RadioConfigProxy proxy = getRadioConfigProxy(null);
if (proxy.isEmpty()) return;
if (proxy.getVersion().less(RIL.RADIO_HAL_VERSION_1_1)) {
if (result != null) {
AsyncResult.forMessage(result, null,
CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
result.sendToTarget();
}
return;
}
RILRequest rr = obtainRequest(RIL_REQUEST_GET_PHONE_CAPABILITY, result, mDefaultWorkSource);
if (DBG) {
logd(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
}
try {
proxy.getPhoneCapability(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
resetProxyAndRequestList("getPhoneCapability", e);
}
}
/**
* @return whether current radio config version supports SET_PREFERRED_DATA_MODEM command.
* If yes, we'll use RIL_REQUEST_SET_PREFERRED_DATA_MODEM to indicate which modem is preferred.
* If not, we shall use RIL_REQUEST_ALLOW_DATA for on-demand PS attach / detach.
* See PhoneSwitcher for more details.
*/
public boolean isSetPreferredDataCommandSupported() {
RadioConfigProxy proxy = getRadioConfigProxy(null);
return !proxy.isEmpty() && proxy.getVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_1);
}
/**
* Wrapper function for IRadioConfig.setSimSlotsMapping(int32_t serial, vec<uint32_t> slotMap).
*/
public void setSimSlotsMapping(List<UiccSlotMapping> slotMapping, Message result) {
RadioConfigProxy proxy = getRadioConfigProxy(result);
if (proxy.isEmpty()) return;
RILRequest rr = obtainRequest(RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING, result,
mDefaultWorkSource);
if (DBG) {
logd(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest) + " "
+ slotMapping);
}
try {
proxy.setSimSlotsMapping(rr.mSerial, slotMapping);
} catch (RemoteException | RuntimeException e) {
resetProxyAndRequestList("setSimSlotsMapping", e);
}
}
/**
* Wrapper function for using IRadioConfig.setNumOfLiveModems(int32_t serial,
* byte numOfLiveModems) to switch between single-sim and multi-sim.
*/
public void setNumOfLiveModems(int numOfLiveModems, Message result) {
RadioConfigProxy proxy = getRadioConfigProxy(result);
if (proxy.isEmpty()) return;
if (proxy.getVersion().less(RIL.RADIO_HAL_VERSION_1_1)) {
if (result != null) {
AsyncResult.forMessage(
result, null, CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
result.sendToTarget();
}
return;
}
RILRequest rr = obtainRequest(RIL_REQUEST_SWITCH_DUAL_SIM_CONFIG,
result, mDefaultWorkSource);
if (DBG) {
logd(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+ ", numOfLiveModems = " + numOfLiveModems);
}
try {
proxy.setNumOfLiveModems(rr.mSerial, numOfLiveModems);
} catch (RemoteException | RuntimeException e) {
resetProxyAndRequestList("setNumOfLiveModems", e);
}
}
/**
* Register a handler to get SIM slots that support simultaneous calling changed notifications.
*/
public void registerForSimultaneousCallingSupportStatusChanged(Handler h, int what,
Object obj) {
mSimultaneousCallingSupportStatusRegistrant = new Registrant(h, what, obj);
}
/**
* Register a handler to get SIM slot status changed notifications.
*/
public void registerForSimSlotStatusChanged(Handler h, int what, Object obj) {
mSimSlotStatusRegistrant = new Registrant(h, what, obj);
}
/**
* Unregister corresponding to registerForSimSlotStatusChanged().
*/
public void unregisterForSimSlotStatusChanged(Handler h) {
if (mSimSlotStatusRegistrant != null && mSimSlotStatusRegistrant.getHandler() == h) {
mSimSlotStatusRegistrant.clear();
mSimSlotStatusRegistrant = null;
}
}
/**
* Gets the hal capabilities from the device.
*/
public void getHalDeviceCapabilities(Message result) {
RadioConfigProxy proxy = getRadioConfigProxy(Message.obtain(result));
if (proxy.isEmpty()) return;
if (proxy.getVersion().less(RIL.RADIO_HAL_VERSION_1_3)) {
if (result != null) {
if (DBG) {
logd("RIL_REQUEST_GET_HAL_DEVICE_CAPABILITIES > REQUEST_NOT_SUPPORTED");
}
AsyncResult.forMessage(result,
/* Send response such that all capabilities are supported (depending on
the hal version of course.) */
proxy.getFullCapabilitySet(),
CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
result.sendToTarget();
} else {
if (DBG) {
logd("RIL_REQUEST_GET_HAL_DEVICE_CAPABILITIES > REQUEST_NOT_SUPPORTED "
+ "on complete message not set.");
}
}
return;
}
RILRequest rr = obtainRequest(RIL_REQUEST_GET_HAL_DEVICE_CAPABILITIES,
result, mDefaultWorkSource);
if (DBG) {
logd(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
}
try {
proxy.getHalDeviceCapabilities(rr.mSerial);
} catch (RemoteException | RuntimeException e) {
resetProxyAndRequestList("getHalDeviceCapabilities", e);
}
}
/**
* Returns the device's nr capability.
*/
public int[] getDeviceNrCapabilities() {
return mDeviceNrCapabilities;
}
private static void logd(String log) {
Rlog.d(TAG, log);
}
private static void loge(String log) {
Rlog.e(TAG, log);
}
@Override
public String toString() {
return "RadioConfig[" + "mRadioConfigProxy=" + mRadioConfigProxy + ']';
}
}