/* * Copyright (C) 2006 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.phone; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_CDMA; import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_GSM; import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_IMS; import static com.android.internal.telephony.PhoneConstants.SUBSCRIPTION_KEY; import static com.android.internal.telephony.TelephonyStatsLog.RCS_CLIENT_PROVISIONING_STATS__EVENT__CLIENT_PARAMS_SENT; import android.Manifest; import android.Manifest.permission; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; import android.app.PendingIntent; import android.app.compat.CompatChanges; import android.app.role.RoleManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ComponentInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.net.Uri; import android.os.AsyncResult; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Messenger; import android.os.ParcelFileDescriptor; import android.os.ParcelUuid; import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceSpecificException; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.os.WorkSource; import android.preference.PreferenceManager; import android.provider.DeviceConfig; import android.provider.Settings; import android.provider.Telephony; import android.sysprop.TelephonyProperties; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.telephony.Annotation.ApnType; import android.telephony.Annotation.ThermalMitigationResult; import android.telephony.CallForwardingInfo; import android.telephony.CarrierConfigManager; import android.telephony.CarrierRestrictionRules; import android.telephony.CellIdentity; import android.telephony.CellIdentityCdma; import android.telephony.CellIdentityGsm; import android.telephony.CellInfo; import android.telephony.CellInfoGsm; import android.telephony.CellInfoWcdma; import android.telephony.ClientRequestStats; import android.telephony.DataThrottlingRequest; import android.telephony.IBootstrapAuthenticationCallback; import android.telephony.ICellInfoCallback; import android.telephony.IccOpenLogicalChannelResponse; import android.telephony.LocationAccessPolicy; import android.telephony.ModemActivityInfo; import android.telephony.NeighboringCellInfo; import android.telephony.NetworkScanRequest; import android.telephony.PhoneCapability; import android.telephony.PhoneNumberRange; import android.telephony.RadioAccessFamily; import android.telephony.RadioAccessSpecifier; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.SignalStrengthUpdateRequest; import android.telephony.SignalThresholdInfo; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyFrameworkInitializer; import android.telephony.TelephonyHistogram; import android.telephony.TelephonyManager; import android.telephony.TelephonyScanManager; import android.telephony.ThermalMitigationRequest; import android.telephony.UiccCardInfo; import android.telephony.UiccSlotInfo; import android.telephony.UssdResponse; import android.telephony.VisualVoicemailSmsFilterSettings; import android.telephony.data.ApnSetting; import android.telephony.data.NetworkSlicingConfig; import android.telephony.emergency.EmergencyNumber; import android.telephony.gba.GbaAuthRequest; import android.telephony.gba.UaSecurityProtocolIdentifier; import android.telephony.ims.ImsException; import android.telephony.ims.ProvisioningManager; import android.telephony.ims.RcsClientConfiguration; import android.telephony.ims.RcsContactUceCapability; import android.telephony.ims.RegistrationManager; import android.telephony.ims.aidl.IImsCapabilityCallback; import android.telephony.ims.aidl.IImsConfig; import android.telephony.ims.aidl.IImsConfigCallback; import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.aidl.IImsRegistrationCallback; import android.telephony.ims.aidl.IRcsConfigCallback; import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.feature.MmTelFeature; import android.telephony.ims.feature.RcsFeature; import android.telephony.ims.stub.ImsConfigImplBase; import android.telephony.ims.stub.ImsRegistrationImplBase; import android.text.TextUtils; import android.util.ArraySet; import android.util.EventLog; import android.util.Log; import android.util.Pair; import com.android.ims.ImsManager; import com.android.ims.internal.IImsServiceFeatureCallback; import com.android.ims.rcs.uce.eab.EabUtil; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.CallForwardInfo; import com.android.internal.telephony.CallManager; import com.android.internal.telephony.CallStateException; import com.android.internal.telephony.CallTracker; import com.android.internal.telephony.CarrierResolver; import com.android.internal.telephony.CellNetworkScanResult; import com.android.internal.telephony.CommandException; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.DefaultPhoneNotifier; import com.android.internal.telephony.GbaManager; import com.android.internal.telephony.GsmCdmaPhone; import com.android.internal.telephony.HalVersion; import com.android.internal.telephony.IBooleanConsumer; import com.android.internal.telephony.ICallForwardingInfoCallback; import com.android.internal.telephony.IIntegerConsumer; import com.android.internal.telephony.INumberVerificationCallback; import com.android.internal.telephony.ITelephony; import com.android.internal.telephony.IccCard; import com.android.internal.telephony.LocaleTracker; import com.android.internal.telephony.NetworkScanRequestTracker; import com.android.internal.telephony.OperatorInfo; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneConfigurationManager; import com.android.internal.telephony.PhoneConstantConversions; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.PhoneFactory; import com.android.internal.telephony.ProxyController; import com.android.internal.telephony.RIL; import com.android.internal.telephony.RILConstants; import com.android.internal.telephony.RadioInterfaceCapabilityController; import com.android.internal.telephony.ServiceStateTracker; import com.android.internal.telephony.SmsController; import com.android.internal.telephony.SmsPermissions; import com.android.internal.telephony.SubscriptionController; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.TelephonyPermissions; import com.android.internal.telephony.dataconnection.ApnSettingUtils; import com.android.internal.telephony.emergency.EmergencyNumberTracker; import com.android.internal.telephony.euicc.EuiccConnector; import com.android.internal.telephony.ims.ImsResolver; import com.android.internal.telephony.imsphone.ImsPhone; import com.android.internal.telephony.imsphone.ImsPhoneCallTracker; import com.android.internal.telephony.metrics.RcsStats; import com.android.internal.telephony.metrics.TelephonyMetrics; import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType; import com.android.internal.telephony.uicc.IccIoResult; import com.android.internal.telephony.uicc.IccRecords; import com.android.internal.telephony.uicc.IccUtils; import com.android.internal.telephony.uicc.SIMRecords; import com.android.internal.telephony.uicc.UiccCard; import com.android.internal.telephony.uicc.UiccCardApplication; import com.android.internal.telephony.uicc.UiccController; import com.android.internal.telephony.uicc.UiccProfile; import com.android.internal.telephony.uicc.UiccSlot; import com.android.internal.telephony.util.LocaleUtils; import com.android.internal.telephony.util.VoicemailNotificationSettingsUtil; import com.android.internal.util.FunctionalUtils; import com.android.internal.util.HexDump; import com.android.phone.callcomposer.CallComposerPictureManager; import com.android.phone.callcomposer.CallComposerPictureTransfer; import com.android.phone.callcomposer.ImageData; import com.android.phone.settings.PickSmsSubscriptionActivity; import com.android.phone.vvm.PhoneAccountHandleConverter; import com.android.phone.vvm.RemoteVvmTaskManager; import com.android.phone.vvm.VisualVoicemailSettingsUtil; import com.android.phone.vvm.VisualVoicemailSmsFilterConfig; import com.android.services.telephony.TelecomAccountRegistry; import com.android.services.telephony.TelephonyConnectionService; import com.android.telephony.Rlog; import java.io.ByteArrayOutputStream; import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; /** * Implementation of the ITelephony interface. */ public class PhoneInterfaceManager extends ITelephony.Stub { private static final String LOG_TAG = "PhoneInterfaceManager"; private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2); private static final boolean DBG_LOC = false; private static final boolean DBG_MERGE = false; // Message codes used with mMainThreadHandler private static final int CMD_HANDLE_PIN_MMI = 1; private static final int CMD_TRANSMIT_APDU_LOGICAL_CHANNEL = 7; private static final int EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE = 8; private static final int CMD_OPEN_CHANNEL = 9; private static final int EVENT_OPEN_CHANNEL_DONE = 10; private static final int CMD_CLOSE_CHANNEL = 11; private static final int EVENT_CLOSE_CHANNEL_DONE = 12; private static final int CMD_NV_READ_ITEM = 13; private static final int EVENT_NV_READ_ITEM_DONE = 14; private static final int CMD_NV_WRITE_ITEM = 15; private static final int EVENT_NV_WRITE_ITEM_DONE = 16; private static final int CMD_NV_WRITE_CDMA_PRL = 17; private static final int EVENT_NV_WRITE_CDMA_PRL_DONE = 18; private static final int CMD_RESET_MODEM_CONFIG = 19; private static final int EVENT_RESET_MODEM_CONFIG_DONE = 20; private static final int CMD_GET_ALLOWED_NETWORK_TYPES_BITMASK = 21; private static final int EVENT_GET_ALLOWED_NETWORK_TYPES_BITMASK_DONE = 22; private static final int CMD_SEND_ENVELOPE = 25; private static final int EVENT_SEND_ENVELOPE_DONE = 26; private static final int CMD_INVOKE_OEM_RIL_REQUEST_RAW = 27; private static final int EVENT_INVOKE_OEM_RIL_REQUEST_RAW_DONE = 28; private static final int CMD_TRANSMIT_APDU_BASIC_CHANNEL = 29; private static final int EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE = 30; private static final int CMD_EXCHANGE_SIM_IO = 31; private static final int EVENT_EXCHANGE_SIM_IO_DONE = 32; private static final int CMD_SET_VOICEMAIL_NUMBER = 33; private static final int EVENT_SET_VOICEMAIL_NUMBER_DONE = 34; private static final int CMD_SET_NETWORK_SELECTION_MODE_AUTOMATIC = 35; private static final int EVENT_SET_NETWORK_SELECTION_MODE_AUTOMATIC_DONE = 36; private static final int CMD_GET_MODEM_ACTIVITY_INFO = 37; private static final int EVENT_GET_MODEM_ACTIVITY_INFO_DONE = 38; private static final int CMD_PERFORM_NETWORK_SCAN = 39; private static final int EVENT_PERFORM_NETWORK_SCAN_DONE = 40; private static final int CMD_SET_NETWORK_SELECTION_MODE_MANUAL = 41; private static final int EVENT_SET_NETWORK_SELECTION_MODE_MANUAL_DONE = 42; private static final int CMD_SET_ALLOWED_CARRIERS = 43; private static final int EVENT_SET_ALLOWED_CARRIERS_DONE = 44; private static final int CMD_GET_ALLOWED_CARRIERS = 45; private static final int EVENT_GET_ALLOWED_CARRIERS_DONE = 46; private static final int CMD_HANDLE_USSD_REQUEST = 47; private static final int CMD_GET_FORBIDDEN_PLMNS = 48; private static final int EVENT_GET_FORBIDDEN_PLMNS_DONE = 49; private static final int CMD_SWITCH_SLOTS = 50; private static final int EVENT_SWITCH_SLOTS_DONE = 51; private static final int CMD_GET_NETWORK_SELECTION_MODE = 52; private static final int EVENT_GET_NETWORK_SELECTION_MODE_DONE = 53; private static final int CMD_GET_CDMA_ROAMING_MODE = 54; private static final int EVENT_GET_CDMA_ROAMING_MODE_DONE = 55; private static final int CMD_SET_CDMA_ROAMING_MODE = 56; private static final int EVENT_SET_CDMA_ROAMING_MODE_DONE = 57; private static final int CMD_SET_CDMA_SUBSCRIPTION_MODE = 58; private static final int EVENT_SET_CDMA_SUBSCRIPTION_MODE_DONE = 59; private static final int CMD_GET_ALL_CELL_INFO = 60; private static final int EVENT_GET_ALL_CELL_INFO_DONE = 61; private static final int CMD_GET_CELL_LOCATION = 62; private static final int EVENT_GET_CELL_LOCATION_DONE = 63; private static final int CMD_MODEM_REBOOT = 64; private static final int EVENT_CMD_MODEM_REBOOT_DONE = 65; private static final int CMD_REQUEST_CELL_INFO_UPDATE = 66; private static final int EVENT_REQUEST_CELL_INFO_UPDATE_DONE = 67; private static final int CMD_REQUEST_ENABLE_MODEM = 68; private static final int EVENT_ENABLE_MODEM_DONE = 69; private static final int CMD_GET_MODEM_STATUS = 70; private static final int EVENT_GET_MODEM_STATUS_DONE = 71; private static final int CMD_SET_FORBIDDEN_PLMNS = 72; private static final int EVENT_SET_FORBIDDEN_PLMNS_DONE = 73; private static final int CMD_ERASE_MODEM_CONFIG = 74; private static final int EVENT_ERASE_MODEM_CONFIG_DONE = 75; private static final int CMD_CHANGE_ICC_LOCK_PASSWORD = 76; private static final int EVENT_CHANGE_ICC_LOCK_PASSWORD_DONE = 77; private static final int CMD_SET_ICC_LOCK_ENABLED = 78; private static final int EVENT_SET_ICC_LOCK_ENABLED_DONE = 79; private static final int CMD_SET_SYSTEM_SELECTION_CHANNELS = 80; private static final int EVENT_SET_SYSTEM_SELECTION_CHANNELS_DONE = 81; private static final int MSG_NOTIFY_USER_ACTIVITY = 82; private static final int CMD_GET_CALL_FORWARDING = 83; private static final int EVENT_GET_CALL_FORWARDING_DONE = 84; private static final int CMD_SET_CALL_FORWARDING = 85; private static final int EVENT_SET_CALL_FORWARDING_DONE = 86; private static final int CMD_GET_CALL_WAITING = 87; private static final int EVENT_GET_CALL_WAITING_DONE = 88; private static final int CMD_SET_CALL_WAITING = 89; private static final int EVENT_SET_CALL_WAITING_DONE = 90; private static final int CMD_ENABLE_NR_DUAL_CONNECTIVITY = 91; private static final int EVENT_ENABLE_NR_DUAL_CONNECTIVITY_DONE = 92; private static final int CMD_IS_NR_DUAL_CONNECTIVITY_ENABLED = 93; private static final int EVENT_IS_NR_DUAL_CONNECTIVITY_ENABLED_DONE = 94; private static final int CMD_GET_CDMA_SUBSCRIPTION_MODE = 95; private static final int EVENT_GET_CDMA_SUBSCRIPTION_MODE_DONE = 96; private static final int CMD_GET_SYSTEM_SELECTION_CHANNELS = 97; private static final int EVENT_GET_SYSTEM_SELECTION_CHANNELS_DONE = 98; private static final int CMD_SET_DATA_THROTTLING = 99; private static final int EVENT_SET_DATA_THROTTLING_DONE = 100; private static final int CMD_SET_SIM_POWER = 101; private static final int EVENT_SET_SIM_POWER_DONE = 102; private static final int CMD_SET_SIGNAL_STRENGTH_UPDATE_REQUEST = 103; private static final int EVENT_SET_SIGNAL_STRENGTH_UPDATE_REQUEST_DONE = 104; private static final int CMD_CLEAR_SIGNAL_STRENGTH_UPDATE_REQUEST = 105; private static final int EVENT_CLEAR_SIGNAL_STRENGTH_UPDATE_REQUEST_DONE = 106; private static final int CMD_SET_ALLOWED_NETWORK_TYPES_FOR_REASON = 107; private static final int EVENT_SET_ALLOWED_NETWORK_TYPES_FOR_REASON_DONE = 108; private static final int CMD_PREPARE_UNATTENDED_REBOOT = 109; private static final int CMD_GET_SLICING_CONFIG = 110; private static final int EVENT_GET_SLICING_CONFIG_DONE = 111; private static final int CMD_ERASE_DATA_SHARED_PREFERENCES = 112; private static final int CMD_ENABLE_VONR = 113; private static final int EVENT_ENABLE_VONR_DONE = 114; private static final int CMD_IS_VONR_ENABLED = 115; private static final int EVENT_IS_VONR_ENABLED_DONE = 116; // Parameters of select command. private static final int SELECT_COMMAND = 0xA4; private static final int SELECT_P1 = 0x04; private static final int SELECT_P2 = 0; private static final int SELECT_P3 = 0x10; /** The singleton instance. */ private static PhoneInterfaceManager sInstance; private static List sThermalMitigationAllowlistedPackages = new ArrayList<>(); private PhoneGlobals mApp; private CallManager mCM; private ImsResolver mImsResolver; private UserManager mUserManager; private AppOpsManager mAppOps; private PackageManager mPm; private MainThreadHandler mMainThreadHandler; private SubscriptionController mSubscriptionController; private SharedPreferences mTelephonySharedPreferences; private PhoneConfigurationManager mPhoneConfigurationManager; private final RadioInterfaceCapabilityController mRadioInterfaceCapabilities; /** User Activity */ private AtomicBoolean mNotifyUserActivity; private static final int USER_ACTIVITY_NOTIFICATION_DELAY = 200; private Set mCarrierPrivilegeTestOverrideSubIds = new ArraySet<>(); private static final String PREF_CARRIERS_ALPHATAG_PREFIX = "carrier_alphtag_"; private static final String PREF_CARRIERS_NUMBER_PREFIX = "carrier_number_"; private static final String PREF_CARRIERS_SUBSCRIBER_PREFIX = "carrier_subscriber_"; private static final String PREF_PROVISION_IMS_MMTEL_PREFIX = "provision_ims_mmtel_"; // String to store multi SIM allowed private static final String PREF_MULTI_SIM_RESTRICTED = "multisim_restricted"; // The AID of ISD-R. private static final String ISDR_AID = "A0000005591010FFFFFFFF8900000100"; private NetworkScanRequestTracker mNetworkScanRequestTracker; private static final int TYPE_ALLOCATION_CODE_LENGTH = 8; private static final int MANUFACTURER_CODE_LENGTH = 8; private static final int SET_DATA_THROTTLING_MODEM_THREW_INVALID_PARAMS = -1; private static final int MODEM_DOES_NOT_SUPPORT_DATA_THROTTLING_ERROR_CODE = -2; /** * Experiment flag to enable erase modem config on reset network, default value is false */ public static final String RESET_NETWORK_ERASE_MODEM_CONFIG_ENABLED = "reset_network_erase_modem_config_enabled"; private static final int SET_NETWORK_SELECTION_MODE_AUTOMATIC_TIMEOUT_MS = 2000; // 2 seconds /** * A request object to use for transmitting data to an ICC. */ private static final class IccAPDUArgument { public int channel, cla, command, p1, p2, p3; public String data; public IccAPDUArgument(int channel, int cla, int command, int p1, int p2, int p3, String data) { this.channel = channel; this.cla = cla; this.command = command; this.p1 = p1; this.p2 = p2; this.p3 = p3; this.data = data; } } /** * A request object to use for transmitting data to an ICC. */ private static final class ManualNetworkSelectionArgument { public OperatorInfo operatorInfo; public boolean persistSelection; public ManualNetworkSelectionArgument(OperatorInfo operatorInfo, boolean persistSelection) { this.operatorInfo = operatorInfo; this.persistSelection = persistSelection; } } /** * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the * request after sending. The main thread will notify the request when it is complete. */ private static final class MainThreadRequest { /** The argument to use for the request */ public Object argument; /** The result of the request that is run on the main thread */ public Object result; // The subscriber id that this request applies to. Defaults to // SubscriptionManager.INVALID_SUBSCRIPTION_ID public Integer subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; // In cases where subId is unavailable, the caller needs to specify the phone. public Phone phone; public WorkSource workSource; public MainThreadRequest(Object argument) { this.argument = argument; } MainThreadRequest(Object argument, Phone phone, WorkSource workSource) { this.argument = argument; if (phone != null) { this.phone = phone; } this.workSource = workSource; } MainThreadRequest(Object argument, Integer subId, WorkSource workSource) { this.argument = argument; if (subId != null) { this.subId = subId; } this.workSource = workSource; } } private static final class IncomingThirdPartyCallArgs { public final ComponentName component; public final String callId; public final String callerDisplayName; public IncomingThirdPartyCallArgs(ComponentName component, String callId, String callerDisplayName) { this.component = component; this.callId = callId; this.callerDisplayName = callerDisplayName; } } /** * A handler that processes messages on the main thread in the phone process. Since many * of the Phone calls are not thread safe this is needed to shuttle the requests from the * inbound binder threads to the main thread in the phone process. The Binder thread * may provide a {@link MainThreadRequest} object in the msg.obj field that they are waiting * on, which will be notified when the operation completes and will contain the result of the * request. * *

If a MainThreadRequest object is provided in the msg.obj field, * note that request.result must be set to something non-null for the calling thread to * unblock. */ private final class MainThreadHandler extends Handler { @Override public void handleMessage(Message msg) { MainThreadRequest request; Message onCompleted; AsyncResult ar; UiccCard uiccCard; IccAPDUArgument iccArgument; final Phone defaultPhone = getDefaultPhone(); switch (msg.what) { case CMD_HANDLE_USSD_REQUEST: { request = (MainThreadRequest) msg.obj; final Phone phone = getPhoneFromRequest(request); Pair ussdObject = (Pair) request.argument; String ussdRequest = ussdObject.first; ResultReceiver wrappedCallback = ussdObject.second; if (!isUssdApiAllowed(request.subId)) { // Carrier does not support use of this API, return failure. Rlog.w(LOG_TAG, "handleUssdRequest: carrier does not support USSD apis."); UssdResponse response = new UssdResponse(ussdRequest, null); Bundle returnData = new Bundle(); returnData.putParcelable(TelephonyManager.USSD_RESPONSE, response); wrappedCallback.send(TelephonyManager.USSD_RETURN_FAILURE, returnData); request.result = true; notifyRequester(request); return; } try { request.result = phone != null ? phone.handleUssdRequest(ussdRequest, wrappedCallback) : false; } catch (CallStateException cse) { request.result = false; } // Wake up the requesting thread notifyRequester(request); break; } case CMD_HANDLE_PIN_MMI: { request = (MainThreadRequest) msg.obj; final Phone phone = getPhoneFromRequest(request); request.result = phone != null ? getPhoneFromRequest(request).handlePinMmi((String) request.argument) : false; // Wake up the requesting thread notifyRequester(request); break; } case CMD_TRANSMIT_APDU_LOGICAL_CHANNEL: request = (MainThreadRequest) msg.obj; iccArgument = (IccAPDUArgument) request.argument; uiccCard = getUiccCardFromRequest(request); if (uiccCard == null) { loge("iccTransmitApduLogicalChannel: No UICC"); request.result = new IccIoResult(0x6F, 0, (byte[])null); notifyRequester(request); } else { onCompleted = obtainMessage(EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE, request); uiccCard.iccTransmitApduLogicalChannel( iccArgument.channel, iccArgument.cla, iccArgument.command, iccArgument.p1, iccArgument.p2, iccArgument.p3, iccArgument.data, onCompleted); } break; case EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception == null && ar.result != null) { request.result = ar.result; } else { request.result = new IccIoResult(0x6F, 0, (byte[])null); if (ar.result == null) { loge("iccTransmitApduLogicalChannel: Empty response"); } else if (ar.exception instanceof CommandException) { loge("iccTransmitApduLogicalChannel: CommandException: " + ar.exception); } else { loge("iccTransmitApduLogicalChannel: Unknown exception"); } } notifyRequester(request); break; case CMD_TRANSMIT_APDU_BASIC_CHANNEL: request = (MainThreadRequest) msg.obj; iccArgument = (IccAPDUArgument) request.argument; uiccCard = getUiccCardFromRequest(request); if (uiccCard == null) { loge("iccTransmitApduBasicChannel: No UICC"); request.result = new IccIoResult(0x6F, 0, (byte[])null); notifyRequester(request); } else { onCompleted = obtainMessage(EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE, request); uiccCard.iccTransmitApduBasicChannel( iccArgument.cla, iccArgument.command, iccArgument.p1, iccArgument.p2, iccArgument.p3, iccArgument.data, onCompleted); } break; case EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception == null && ar.result != null) { request.result = ar.result; } else { request.result = new IccIoResult(0x6F, 0, (byte[])null); if (ar.result == null) { loge("iccTransmitApduBasicChannel: Empty response"); } else if (ar.exception instanceof CommandException) { loge("iccTransmitApduBasicChannel: CommandException: " + ar.exception); } else { loge("iccTransmitApduBasicChannel: Unknown exception"); } } notifyRequester(request); break; case CMD_EXCHANGE_SIM_IO: request = (MainThreadRequest) msg.obj; iccArgument = (IccAPDUArgument) request.argument; uiccCard = getUiccCardFromRequest(request); if (uiccCard == null) { loge("iccExchangeSimIO: No UICC"); request.result = new IccIoResult(0x6F, 0, (byte[])null); notifyRequester(request); } else { onCompleted = obtainMessage(EVENT_EXCHANGE_SIM_IO_DONE, request); uiccCard.iccExchangeSimIO(iccArgument.cla, /* fileID */ iccArgument.command, iccArgument.p1, iccArgument.p2, iccArgument.p3, iccArgument.data, onCompleted); } break; case EVENT_EXCHANGE_SIM_IO_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception == null && ar.result != null) { request.result = ar.result; } else { request.result = new IccIoResult(0x6f, 0, (byte[])null); } notifyRequester(request); break; case CMD_SEND_ENVELOPE: request = (MainThreadRequest) msg.obj; uiccCard = getUiccCardFromRequest(request); if (uiccCard == null) { loge("sendEnvelopeWithStatus: No UICC"); request.result = new IccIoResult(0x6F, 0, (byte[])null); notifyRequester(request); } else { onCompleted = obtainMessage(EVENT_SEND_ENVELOPE_DONE, request); uiccCard.sendEnvelopeWithStatus((String)request.argument, onCompleted); } break; case EVENT_SEND_ENVELOPE_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception == null && ar.result != null) { request.result = ar.result; } else { request.result = new IccIoResult(0x6F, 0, (byte[])null); if (ar.result == null) { loge("sendEnvelopeWithStatus: Empty response"); } else if (ar.exception instanceof CommandException) { loge("sendEnvelopeWithStatus: CommandException: " + ar.exception); } else { loge("sendEnvelopeWithStatus: exception:" + ar.exception); } } notifyRequester(request); break; case CMD_OPEN_CHANNEL: request = (MainThreadRequest) msg.obj; uiccCard = getUiccCardFromRequest(request); Pair openChannelArgs = (Pair) request.argument; if (uiccCard == null) { loge("iccOpenLogicalChannel: No UICC"); request.result = new IccOpenLogicalChannelResponse(-1, IccOpenLogicalChannelResponse.STATUS_MISSING_RESOURCE, null); notifyRequester(request); } else { onCompleted = obtainMessage(EVENT_OPEN_CHANNEL_DONE, request); uiccCard.iccOpenLogicalChannel(openChannelArgs.first, openChannelArgs.second, onCompleted); } break; case EVENT_OPEN_CHANNEL_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; IccOpenLogicalChannelResponse openChannelResp; if (ar.exception == null && ar.result != null) { int[] result = (int[]) ar.result; int channelId = result[0]; byte[] selectResponse = null; if (result.length > 1) { selectResponse = new byte[result.length - 1]; for (int i = 1; i < result.length; ++i) { selectResponse[i - 1] = (byte) result[i]; } } openChannelResp = new IccOpenLogicalChannelResponse(channelId, IccOpenLogicalChannelResponse.STATUS_NO_ERROR, selectResponse); } else { if (ar.result == null) { loge("iccOpenLogicalChannel: Empty response"); } if (ar.exception != null) { loge("iccOpenLogicalChannel: Exception: " + ar.exception); } int errorCode = IccOpenLogicalChannelResponse.STATUS_UNKNOWN_ERROR; if (ar.exception instanceof CommandException) { CommandException.Error error = ((CommandException) (ar.exception)).getCommandError(); if (error == CommandException.Error.MISSING_RESOURCE) { errorCode = IccOpenLogicalChannelResponse.STATUS_MISSING_RESOURCE; } else if (error == CommandException.Error.NO_SUCH_ELEMENT) { errorCode = IccOpenLogicalChannelResponse.STATUS_NO_SUCH_ELEMENT; } } openChannelResp = new IccOpenLogicalChannelResponse( IccOpenLogicalChannelResponse.INVALID_CHANNEL, errorCode, null); } request.result = openChannelResp; notifyRequester(request); break; case CMD_CLOSE_CHANNEL: request = (MainThreadRequest) msg.obj; uiccCard = getUiccCardFromRequest(request); if (uiccCard == null) { loge("iccCloseLogicalChannel: No UICC"); request.result = false; notifyRequester(request); } else { onCompleted = obtainMessage(EVENT_CLOSE_CHANNEL_DONE, request); uiccCard.iccCloseLogicalChannel((Integer) request.argument, onCompleted); } break; case EVENT_CLOSE_CHANNEL_DONE: handleNullReturnEvent(msg, "iccCloseLogicalChannel"); break; case CMD_NV_READ_ITEM: request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_NV_READ_ITEM_DONE, request); defaultPhone.nvReadItem((Integer) request.argument, onCompleted, request.workSource); break; case EVENT_NV_READ_ITEM_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception == null && ar.result != null) { request.result = ar.result; // String } else { request.result = ""; if (ar.result == null) { loge("nvReadItem: Empty response"); } else if (ar.exception instanceof CommandException) { loge("nvReadItem: CommandException: " + ar.exception); } else { loge("nvReadItem: Unknown exception"); } } notifyRequester(request); break; case CMD_NV_WRITE_ITEM: request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_NV_WRITE_ITEM_DONE, request); Pair idValue = (Pair) request.argument; defaultPhone.nvWriteItem(idValue.first, idValue.second, onCompleted, request.workSource); break; case EVENT_NV_WRITE_ITEM_DONE: handleNullReturnEvent(msg, "nvWriteItem"); break; case CMD_NV_WRITE_CDMA_PRL: request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_NV_WRITE_CDMA_PRL_DONE, request); defaultPhone.nvWriteCdmaPrl((byte[]) request.argument, onCompleted); break; case EVENT_NV_WRITE_CDMA_PRL_DONE: handleNullReturnEvent(msg, "nvWriteCdmaPrl"); break; case CMD_RESET_MODEM_CONFIG: request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_RESET_MODEM_CONFIG_DONE, request); defaultPhone.resetModemConfig(onCompleted); break; case EVENT_RESET_MODEM_CONFIG_DONE: handleNullReturnEvent(msg, "resetModemConfig"); break; case CMD_IS_NR_DUAL_CONNECTIVITY_ENABLED: { request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_IS_NR_DUAL_CONNECTIVITY_ENABLED_DONE, request); Phone phone = getPhoneFromRequest(request); if (phone != null) { phone.isNrDualConnectivityEnabled(onCompleted, request.workSource); } else { loge("isNRDualConnectivityEnabled: No phone object"); request.result = false; notifyRequester(request); } break; } case EVENT_IS_NR_DUAL_CONNECTIVITY_ENABLED_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception == null && ar.result != null) { request.result = ar.result; } else { // request.result must be set to something non-null // for the calling thread to unblock if (ar.result != null) { request.result = ar.result; } else { request.result = false; } if (ar.result == null) { loge("isNRDualConnectivityEnabled: Empty response"); } else if (ar.exception instanceof CommandException) { loge("isNRDualConnectivityEnabled: CommandException: " + ar.exception); } else { loge("isNRDualConnectivityEnabled: Unknown exception"); } } notifyRequester(request); break; case CMD_IS_VONR_ENABLED: { request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_IS_VONR_ENABLED_DONE, request); Phone phone = getPhoneFromRequest(request); if (phone != null) { phone.isVoNrEnabled(onCompleted, request.workSource); } else { loge("isVoNrEnabled: No phone object"); request.result = false; notifyRequester(request); } break; } case EVENT_IS_VONR_ENABLED_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception == null && ar.result != null) { request.result = ar.result; } else { // request.result must be set to something non-null // for the calling thread to unblock if (ar.result != null) { request.result = ar.result; } else { request.result = false; } if (ar.result == null) { loge("isVoNrEnabled: Empty response"); } else if (ar.exception instanceof CommandException) { loge("isVoNrEnabled: CommandException: " + ar.exception); } else { loge("isVoNrEnabled: Unknown exception"); } } notifyRequester(request); break; case CMD_ENABLE_NR_DUAL_CONNECTIVITY: { request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_ENABLE_NR_DUAL_CONNECTIVITY_DONE, request); Phone phone = getPhoneFromRequest(request); if (phone != null) { phone.setNrDualConnectivityState((int) request.argument, onCompleted, request.workSource); } else { loge("enableNrDualConnectivity: No phone object"); request.result = TelephonyManager.ENABLE_NR_DUAL_CONNECTIVITY_RADIO_NOT_AVAILABLE; notifyRequester(request); } break; } case EVENT_ENABLE_NR_DUAL_CONNECTIVITY_DONE: { ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception == null) { request.result = TelephonyManager.ENABLE_NR_DUAL_CONNECTIVITY_SUCCESS; } else { request.result = TelephonyManager .ENABLE_NR_DUAL_CONNECTIVITY_RADIO_ERROR; if (ar.exception instanceof CommandException) { CommandException.Error error = ((CommandException) (ar.exception)).getCommandError(); if (error == CommandException.Error.RADIO_NOT_AVAILABLE) { request.result = TelephonyManager .ENABLE_NR_DUAL_CONNECTIVITY_RADIO_NOT_AVAILABLE; } else if (error == CommandException.Error.REQUEST_NOT_SUPPORTED) { request.result = TelephonyManager .ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED; } loge("enableNrDualConnectivity" + ": CommandException: " + ar.exception); } else { loge("enableNrDualConnectivity" + ": Unknown exception"); } } notifyRequester(request); break; } case CMD_ENABLE_VONR: { request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_ENABLE_VONR_DONE, request); Phone phone = getPhoneFromRequest(request); if (phone != null) { phone.setVoNrEnabled((boolean) request.argument, onCompleted, request.workSource); } else { loge("setVoNrEnabled: No phone object"); request.result = TelephonyManager.ENABLE_VONR_RADIO_NOT_AVAILABLE; notifyRequester(request); } break; } case EVENT_ENABLE_VONR_DONE: { ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception == null) { request.result = TelephonyManager.ENABLE_VONR_SUCCESS; } else { request.result = TelephonyManager.ENABLE_VONR_RADIO_ERROR; if (ar.exception instanceof CommandException) { CommandException.Error error = ((CommandException) (ar.exception)).getCommandError(); if (error == CommandException.Error.RADIO_NOT_AVAILABLE) { request.result = TelephonyManager.ENABLE_VONR_RADIO_NOT_AVAILABLE; } else if (error == CommandException.Error.REQUEST_NOT_SUPPORTED) { request.result = TelephonyManager.ENABLE_VONR_REQUEST_NOT_SUPPORTED; } else { request.result = TelephonyManager.ENABLE_VONR_RADIO_ERROR; } loge("setVoNrEnabled" + ": CommandException: " + ar.exception); } else { loge("setVoNrEnabled" + ": Unknown exception"); } } notifyRequester(request); break; } case CMD_GET_ALLOWED_NETWORK_TYPES_BITMASK: request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_GET_ALLOWED_NETWORK_TYPES_BITMASK_DONE, request); getPhoneFromRequest(request).getAllowedNetworkTypesBitmask(onCompleted); break; case EVENT_GET_ALLOWED_NETWORK_TYPES_BITMASK_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception == null && ar.result != null) { request.result = ar.result; // Integer } else { // request.result must be set to something non-null // for the calling thread to unblock request.result = new int[]{-1}; if (ar.result == null) { loge("getAllowedNetworkTypesBitmask: Empty response"); } else if (ar.exception instanceof CommandException) { loge("getAllowedNetworkTypesBitmask: CommandException: " + ar.exception); } else { loge("getAllowedNetworkTypesBitmask: Unknown exception"); } } notifyRequester(request); break; case CMD_SET_ALLOWED_NETWORK_TYPES_FOR_REASON: request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_SET_ALLOWED_NETWORK_TYPES_FOR_REASON_DONE, request); Pair reasonWithNetworkTypes = (Pair) request.argument; getPhoneFromRequest(request).setAllowedNetworkTypes( reasonWithNetworkTypes.first, reasonWithNetworkTypes.second, onCompleted); break; case EVENT_SET_ALLOWED_NETWORK_TYPES_FOR_REASON_DONE: handleNullReturnEvent(msg, "setAllowedNetworkTypesForReason"); break; case CMD_INVOKE_OEM_RIL_REQUEST_RAW: request = (MainThreadRequest)msg.obj; onCompleted = obtainMessage(EVENT_INVOKE_OEM_RIL_REQUEST_RAW_DONE, request); defaultPhone.invokeOemRilRequestRaw((byte[]) request.argument, onCompleted); break; case EVENT_INVOKE_OEM_RIL_REQUEST_RAW_DONE: ar = (AsyncResult)msg.obj; request = (MainThreadRequest)ar.userObj; request.result = ar; notifyRequester(request); break; case CMD_SET_VOICEMAIL_NUMBER: request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_SET_VOICEMAIL_NUMBER_DONE, request); Pair tagNum = (Pair) request.argument; getPhoneFromRequest(request).setVoiceMailNumber(tagNum.first, tagNum.second, onCompleted); break; case EVENT_SET_VOICEMAIL_NUMBER_DONE: handleNullReturnEvent(msg, "setVoicemailNumber"); break; case CMD_SET_NETWORK_SELECTION_MODE_AUTOMATIC: request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_SET_NETWORK_SELECTION_MODE_AUTOMATIC_DONE, request); getPhoneFromRequest(request).setNetworkSelectionModeAutomatic(onCompleted); break; case EVENT_SET_NETWORK_SELECTION_MODE_AUTOMATIC_DONE: handleNullReturnEvent(msg, "setNetworkSelectionModeAutomatic"); break; case CMD_PERFORM_NETWORK_SCAN: request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_PERFORM_NETWORK_SCAN_DONE, request); getPhoneFromRequest(request).getAvailableNetworks(onCompleted); break; case CMD_GET_CALL_FORWARDING: { request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_GET_CALL_FORWARDING_DONE, request); Pair args = (Pair) request.argument; int callForwardingReason = args.first; request.phone.getCallForwardingOption(callForwardingReason, onCompleted); break; } case EVENT_GET_CALL_FORWARDING_DONE: { ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; TelephonyManager.CallForwardingInfoCallback callback = ((Pair) request.argument).second; if (ar.exception == null && ar.result != null) { CallForwardingInfo callForwardingInfo = null; CallForwardInfo[] callForwardInfos = (CallForwardInfo[]) ar.result; for (CallForwardInfo callForwardInfo : callForwardInfos) { // Service Class is a bit mask per 3gpp 27.007. Search for // any service for voice call. if ((callForwardInfo.serviceClass & CommandsInterface.SERVICE_CLASS_VOICE) > 0) { callForwardingInfo = new CallForwardingInfo(true, callForwardInfo.reason, callForwardInfo.number, callForwardInfo.timeSeconds); break; } } // Didn't find a call forward info for voice call. if (callForwardingInfo == null) { callForwardingInfo = new CallForwardingInfo(false /* enabled */, 0 /* reason */, null /* number */, 0 /* timeout */); } callback.onCallForwardingInfoAvailable(callForwardingInfo); } else { if (ar.result == null) { loge("EVENT_GET_CALL_FORWARDING_DONE: Empty response"); } if (ar.exception != null) { loge("EVENT_GET_CALL_FORWARDING_DONE: Exception: " + ar.exception); } int errorCode = TelephonyManager .CallForwardingInfoCallback.RESULT_ERROR_UNKNOWN; if (ar.exception instanceof CommandException) { CommandException.Error error = ((CommandException) (ar.exception)).getCommandError(); if (error == CommandException.Error.FDN_CHECK_FAILURE) { errorCode = TelephonyManager .CallForwardingInfoCallback.RESULT_ERROR_FDN_CHECK_FAILURE; } else if (error == CommandException.Error.REQUEST_NOT_SUPPORTED) { errorCode = TelephonyManager .CallForwardingInfoCallback.RESULT_ERROR_NOT_SUPPORTED; } } callback.onError(errorCode); } break; } case CMD_SET_CALL_FORWARDING: { request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_SET_CALL_FORWARDING_DONE, request); request = (MainThreadRequest) msg.obj; CallForwardingInfo callForwardingInfoToSet = ((Pair>) request.argument).first; request.phone.setCallForwardingOption( callForwardingInfoToSet.isEnabled() ? CommandsInterface.CF_ACTION_REGISTRATION : CommandsInterface.CF_ACTION_DISABLE, callForwardingInfoToSet.getReason(), callForwardingInfoToSet.getNumber(), callForwardingInfoToSet.getTimeoutSeconds(), onCompleted); break; } case EVENT_SET_CALL_FORWARDING_DONE: { ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; Consumer callback = ((Pair>) request.argument).second; if (ar.exception != null) { loge("setCallForwarding exception: " + ar.exception); int errorCode = TelephonyManager.CallForwardingInfoCallback .RESULT_ERROR_UNKNOWN; if (ar.exception instanceof CommandException) { CommandException.Error error = ((CommandException) (ar.exception)).getCommandError(); if (error == CommandException.Error.FDN_CHECK_FAILURE) { errorCode = TelephonyManager.CallForwardingInfoCallback .RESULT_ERROR_FDN_CHECK_FAILURE; } else if (error == CommandException.Error.REQUEST_NOT_SUPPORTED) { errorCode = TelephonyManager.CallForwardingInfoCallback .RESULT_ERROR_NOT_SUPPORTED; } } callback.accept(errorCode); } else { callback.accept(TelephonyManager.CallForwardingInfoCallback.RESULT_SUCCESS); } break; } case CMD_GET_CALL_WAITING: { request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_GET_CALL_WAITING_DONE, request); getPhoneFromRequest(request).getCallWaiting(onCompleted); break; } case EVENT_GET_CALL_WAITING_DONE: { ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; Consumer callback = (Consumer) request.argument; int callForwardingStatus = TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR; if (ar.exception == null && ar.result != null) { int[] callForwardResults = (int[]) ar.result; // Service Class is a bit mask per 3gpp 27.007. // Search for any service for voice call. if (callForwardResults.length > 1 && ((callForwardResults[1] & CommandsInterface.SERVICE_CLASS_VOICE) > 0)) { callForwardingStatus = callForwardResults[0] == 0 ? TelephonyManager.CALL_WAITING_STATUS_DISABLED : TelephonyManager.CALL_WAITING_STATUS_ENABLED; } else { callForwardingStatus = TelephonyManager.CALL_WAITING_STATUS_DISABLED; } } else { if (ar.result == null) { loge("EVENT_GET_CALL_WAITING_DONE: Empty response"); } if (ar.exception != null) { loge("EVENT_GET_CALL_WAITING_DONE: Exception: " + ar.exception); } if (ar.exception instanceof CommandException) { CommandException.Error error = ((CommandException) (ar.exception)).getCommandError(); if (error == CommandException.Error.REQUEST_NOT_SUPPORTED) { callForwardingStatus = TelephonyManager.CALL_WAITING_STATUS_NOT_SUPPORTED; } } } callback.accept(callForwardingStatus); break; } case CMD_SET_CALL_WAITING: { request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_SET_CALL_WAITING_DONE, request); boolean enable = ((Pair>) request.argument).first; getPhoneFromRequest(request).setCallWaiting(enable, onCompleted); break; } case EVENT_SET_CALL_WAITING_DONE: { ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; boolean enable = ((Pair>) request.argument).first; Consumer callback = ((Pair>) request.argument).second; if (ar.exception != null) { loge("setCallWaiting exception: " + ar.exception); if (ar.exception instanceof CommandException) { CommandException.Error error = ((CommandException) (ar.exception)).getCommandError(); if (error == CommandException.Error.REQUEST_NOT_SUPPORTED) { callback.accept(TelephonyManager.CALL_WAITING_STATUS_NOT_SUPPORTED); } else { callback.accept(TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR); } } else { callback.accept(TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR); } } else { callback.accept(enable ? TelephonyManager.CALL_WAITING_STATUS_ENABLED : TelephonyManager.CALL_WAITING_STATUS_DISABLED); } break; } case EVENT_PERFORM_NETWORK_SCAN_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; CellNetworkScanResult cellScanResult; if (ar.exception == null && ar.result != null) { cellScanResult = new CellNetworkScanResult( CellNetworkScanResult.STATUS_SUCCESS, (List) ar.result); } else { if (ar.result == null) { loge("getCellNetworkScanResults: Empty response"); } if (ar.exception != null) { loge("getCellNetworkScanResults: Exception: " + ar.exception); } int errorCode = CellNetworkScanResult.STATUS_UNKNOWN_ERROR; if (ar.exception instanceof CommandException) { CommandException.Error error = ((CommandException) (ar.exception)).getCommandError(); if (error == CommandException.Error.RADIO_NOT_AVAILABLE) { errorCode = CellNetworkScanResult.STATUS_RADIO_NOT_AVAILABLE; } else if (error == CommandException.Error.GENERIC_FAILURE) { errorCode = CellNetworkScanResult.STATUS_RADIO_GENERIC_FAILURE; } } cellScanResult = new CellNetworkScanResult(errorCode, null); } request.result = cellScanResult; notifyRequester(request); break; case CMD_SET_NETWORK_SELECTION_MODE_MANUAL: request = (MainThreadRequest) msg.obj; ManualNetworkSelectionArgument selArg = (ManualNetworkSelectionArgument) request.argument; onCompleted = obtainMessage(EVENT_SET_NETWORK_SELECTION_MODE_MANUAL_DONE, request); getPhoneFromRequest(request).selectNetworkManually(selArg.operatorInfo, selArg.persistSelection, onCompleted); break; case EVENT_SET_NETWORK_SELECTION_MODE_MANUAL_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception == null) { request.result = true; } else { request.result = false; loge("setNetworkSelectionModeManual " + ar.exception); } notifyRequester(request); mApp.onNetworkSelectionChanged(request.subId); break; case CMD_GET_MODEM_ACTIVITY_INFO: request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_GET_MODEM_ACTIVITY_INFO_DONE, request); if (defaultPhone != null) { defaultPhone.getModemActivityInfo(onCompleted, request.workSource); } else { ResultReceiver result = (ResultReceiver) request.argument; Bundle bundle = new Bundle(); bundle.putParcelable(TelephonyManager.MODEM_ACTIVITY_RESULT_KEY, new ModemActivityInfo(0, 0, 0, new int[ModemActivityInfo.getNumTxPowerLevels()], 0)); result.send(0, bundle); } break; case EVENT_GET_MODEM_ACTIVITY_INFO_DONE: { ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; ResultReceiver result = (ResultReceiver) request.argument; ModemActivityInfo ret = null; int error = 0; if (ar.exception == null && ar.result != null) { // Update the last modem activity info and the result of the request. ModemActivityInfo info = (ModemActivityInfo) ar.result; if (isModemActivityInfoValid(info)) { int[] mergedTxTimeMs = new int[ModemActivityInfo.getNumTxPowerLevels()]; int[] txTimeMs = info.getTransmitTimeMillis(); int[] lastModemTxTimeMs = mLastModemActivityInfo .getTransmitTimeMillis(); for (int i = 0; i < mergedTxTimeMs.length; i++) { mergedTxTimeMs[i] = txTimeMs[i] + lastModemTxTimeMs[i]; } mLastModemActivityInfo.setTimestamp(info.getTimestampMillis()); mLastModemActivityInfo.setSleepTimeMillis(info.getSleepTimeMillis() + mLastModemActivityInfo.getSleepTimeMillis()); mLastModemActivityInfo.setIdleTimeMillis(info.getIdleTimeMillis() + mLastModemActivityInfo.getIdleTimeMillis()); mLastModemActivityInfo.setTransmitTimeMillis(mergedTxTimeMs); mLastModemActivityInfo.setReceiveTimeMillis( info.getReceiveTimeMillis() + mLastModemActivityInfo.getReceiveTimeMillis()); } ret = new ModemActivityInfo(mLastModemActivityInfo.getTimestampMillis(), mLastModemActivityInfo.getSleepTimeMillis(), mLastModemActivityInfo.getIdleTimeMillis(), mLastModemActivityInfo.getTransmitTimeMillis(), mLastModemActivityInfo.getReceiveTimeMillis()); } else { if (ar.result == null) { loge("queryModemActivityInfo: Empty response"); error = TelephonyManager.ModemActivityInfoException .ERROR_INVALID_INFO_RECEIVED; } else if (ar.exception instanceof CommandException) { loge("queryModemActivityInfo: CommandException: " + ar.exception); error = TelephonyManager.ModemActivityInfoException .ERROR_MODEM_RESPONSE_ERROR; } else { loge("queryModemActivityInfo: Unknown exception"); error = TelephonyManager.ModemActivityInfoException .ERROR_UNKNOWN; } } Bundle bundle = new Bundle(); if (ret != null) { bundle.putParcelable(TelephonyManager.MODEM_ACTIVITY_RESULT_KEY, ret); } else { bundle.putInt(TelephonyManager.EXCEPTION_RESULT_KEY, error); } result.send(0, bundle); notifyRequester(request); break; } case CMD_SET_ALLOWED_CARRIERS: request = (MainThreadRequest) msg.obj; CarrierRestrictionRules argument = (CarrierRestrictionRules) request.argument; onCompleted = obtainMessage(EVENT_SET_ALLOWED_CARRIERS_DONE, request); defaultPhone.setAllowedCarriers(argument, onCompleted, request.workSource); break; case EVENT_SET_ALLOWED_CARRIERS_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception == null && ar.result != null) { request.result = ar.result; } else { request.result = TelephonyManager.SET_CARRIER_RESTRICTION_ERROR; if (ar.exception instanceof CommandException) { loge("setAllowedCarriers: CommandException: " + ar.exception); CommandException.Error error = ((CommandException) (ar.exception)).getCommandError(); if (error == CommandException.Error.REQUEST_NOT_SUPPORTED) { request.result = TelephonyManager.SET_CARRIER_RESTRICTION_NOT_SUPPORTED; } } else { loge("setAllowedCarriers: Unknown exception"); } } notifyRequester(request); break; case CMD_GET_ALLOWED_CARRIERS: request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_GET_ALLOWED_CARRIERS_DONE, request); defaultPhone.getAllowedCarriers(onCompleted, request.workSource); break; case EVENT_GET_ALLOWED_CARRIERS_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception == null && ar.result != null) { request.result = ar.result; } else { request.result = new IllegalStateException( "Failed to get carrier restrictions"); if (ar.result == null) { loge("getAllowedCarriers: Empty response"); } else if (ar.exception instanceof CommandException) { loge("getAllowedCarriers: CommandException: " + ar.exception); } else { loge("getAllowedCarriers: Unknown exception"); } } notifyRequester(request); break; case EVENT_GET_FORBIDDEN_PLMNS_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception == null && ar.result != null) { request.result = ar.result; } else { request.result = new IllegalArgumentException( "Failed to retrieve Forbidden Plmns"); if (ar.result == null) { loge("getForbiddenPlmns: Empty response"); } else { loge("getForbiddenPlmns: Unknown exception"); } } notifyRequester(request); break; case CMD_GET_FORBIDDEN_PLMNS: request = (MainThreadRequest) msg.obj; uiccCard = getUiccCardFromRequest(request); if (uiccCard == null) { loge("getForbiddenPlmns() UiccCard is null"); request.result = new IllegalArgumentException( "getForbiddenPlmns() UiccCard is null"); notifyRequester(request); break; } Integer appType = (Integer) request.argument; UiccCardApplication uiccApp = uiccCard.getApplicationByType(appType); if (uiccApp == null) { loge("getForbiddenPlmns() no app with specified type -- " + appType); request.result = new IllegalArgumentException("Failed to get UICC App"); notifyRequester(request); break; } else { if (DBG) logv("getForbiddenPlmns() found app " + uiccApp.getAid() + " specified type -- " + appType); } onCompleted = obtainMessage(EVENT_GET_FORBIDDEN_PLMNS_DONE, request); ((SIMRecords) uiccApp.getIccRecords()).getForbiddenPlmns( onCompleted); break; case CMD_SWITCH_SLOTS: request = (MainThreadRequest) msg.obj; int[] physicalSlots = (int[]) request.argument; onCompleted = obtainMessage(EVENT_SWITCH_SLOTS_DONE, request); UiccController.getInstance().switchSlots(physicalSlots, onCompleted); break; case EVENT_SWITCH_SLOTS_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; request.result = (ar.exception == null); notifyRequester(request); break; case CMD_GET_NETWORK_SELECTION_MODE: request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_GET_NETWORK_SELECTION_MODE_DONE, request); getPhoneFromRequest(request).getNetworkSelectionMode(onCompleted); break; case EVENT_GET_NETWORK_SELECTION_MODE_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception != null) { request.result = TelephonyManager.NETWORK_SELECTION_MODE_UNKNOWN; } else { int mode = ((int[]) ar.result)[0]; if (mode == 0) { request.result = TelephonyManager.NETWORK_SELECTION_MODE_AUTO; } else { request.result = TelephonyManager.NETWORK_SELECTION_MODE_MANUAL; } } notifyRequester(request); break; case CMD_GET_CDMA_ROAMING_MODE: request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_GET_CDMA_ROAMING_MODE_DONE, request); getPhoneFromRequest(request).queryCdmaRoamingPreference(onCompleted); break; case EVENT_GET_CDMA_ROAMING_MODE_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception != null) { request.result = TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT; } else { request.result = ((int[]) ar.result)[0]; } notifyRequester(request); break; case CMD_SET_CDMA_ROAMING_MODE: request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_SET_CDMA_ROAMING_MODE_DONE, request); int mode = (int) request.argument; getPhoneFromRequest(request).setCdmaRoamingPreference(mode, onCompleted); break; case EVENT_SET_CDMA_ROAMING_MODE_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; request.result = ar.exception == null; notifyRequester(request); break; case CMD_GET_CDMA_SUBSCRIPTION_MODE: request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_MODE_DONE, request); getPhoneFromRequest(request).queryCdmaSubscriptionMode(onCompleted); break; case EVENT_GET_CDMA_SUBSCRIPTION_MODE_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception != null) { request.result = TelephonyManager.CDMA_SUBSCRIPTION_RUIM_SIM; } else { request.result = ((int[]) ar.result)[0]; } notifyRequester(request); break; case CMD_SET_CDMA_SUBSCRIPTION_MODE: request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_SET_CDMA_SUBSCRIPTION_MODE_DONE, request); int subscriptionMode = (int) request.argument; getPhoneFromRequest(request).setCdmaSubscriptionMode( subscriptionMode, onCompleted); break; case EVENT_SET_CDMA_SUBSCRIPTION_MODE_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; request.result = ar.exception == null; notifyRequester(request); break; case CMD_GET_ALL_CELL_INFO: request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_GET_ALL_CELL_INFO_DONE, request); request.phone.requestCellInfoUpdate(request.workSource, onCompleted); break; case EVENT_GET_ALL_CELL_INFO_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; // If a timeout occurs, the response will be null request.result = (ar.exception == null && ar.result != null) ? ar.result : new ArrayList(); synchronized (request) { request.notifyAll(); } break; case CMD_REQUEST_CELL_INFO_UPDATE: request = (MainThreadRequest) msg.obj; request.phone.requestCellInfoUpdate(request.workSource, obtainMessage(EVENT_REQUEST_CELL_INFO_UPDATE_DONE, request)); break; case EVENT_REQUEST_CELL_INFO_UPDATE_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; ICellInfoCallback cb = (ICellInfoCallback) request.argument; try { if (ar.exception != null) { Log.e(LOG_TAG, "Exception retrieving CellInfo=" + ar.exception); cb.onError( TelephonyManager.CellInfoCallback.ERROR_MODEM_ERROR, ar.exception.getClass().getName(), ar.exception.toString()); } else if (ar.result == null) { Log.w(LOG_TAG, "Timeout Waiting for CellInfo!"); cb.onError(TelephonyManager.CellInfoCallback.ERROR_TIMEOUT, null, null); } else { // use the result as returned cb.onCellInfo((List) ar.result); } } catch (RemoteException re) { Log.w(LOG_TAG, "Discarded CellInfo due to Callback RemoteException"); } break; case CMD_GET_CELL_LOCATION: { request = (MainThreadRequest) msg.obj; WorkSource ws = (WorkSource) request.argument; Phone phone = getPhoneFromRequest(request); phone.getCellIdentity(ws, obtainMessage(EVENT_GET_CELL_LOCATION_DONE, request)); break; } case EVENT_GET_CELL_LOCATION_DONE: { ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception == null) { request.result = ar.result; } else { Phone phone = getPhoneFromRequest(request); request.result = (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) ? new CellIdentityCdma() : new CellIdentityGsm(); } synchronized (request) { request.notifyAll(); } break; } case CMD_MODEM_REBOOT: request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_RESET_MODEM_CONFIG_DONE, request); defaultPhone.rebootModem(onCompleted); break; case EVENT_CMD_MODEM_REBOOT_DONE: handleNullReturnEvent(msg, "rebootModem"); break; case CMD_REQUEST_ENABLE_MODEM: request = (MainThreadRequest) msg.obj; boolean enable = (boolean) request.argument; onCompleted = obtainMessage(EVENT_ENABLE_MODEM_DONE, request); onCompleted.arg1 = enable ? 1 : 0; PhoneConfigurationManager.getInstance() .enablePhone(request.phone, enable, onCompleted); break; case EVENT_ENABLE_MODEM_DONE: { ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; request.result = (ar.exception == null); int phoneId = request.phone.getPhoneId(); //update the cache as modem status has changed if ((boolean) request.result) { mPhoneConfigurationManager.addToPhoneStatusCache(phoneId, msg.arg1 == 1); updateModemStateMetrics(); } else { Log.e(LOG_TAG, msg.what + " failure. Not updating modem status." + ar.exception); } notifyRequester(request); break; } case CMD_GET_MODEM_STATUS: request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_GET_MODEM_STATUS_DONE, request); PhoneConfigurationManager.getInstance() .getPhoneStatusFromModem(request.phone, onCompleted); break; case EVENT_GET_MODEM_STATUS_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; int id = request.phone.getPhoneId(); if (ar.exception == null && ar.result != null) { request.result = ar.result; //update the cache as modem status has changed mPhoneConfigurationManager.addToPhoneStatusCache(id, (boolean) request.result); } else { // Return true if modem status cannot be retrieved. For most cases, // modem status is on. And for older version modems, GET_MODEM_STATUS // and disable modem are not supported. Modem is always on. // TODO: this should be fixed in R to support a third // status UNKNOWN b/131631629 request.result = true; Log.e(LOG_TAG, msg.what + " failure. Not updating modem status." + ar.exception); } notifyRequester(request); break; case CMD_SET_SYSTEM_SELECTION_CHANNELS: { request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_SET_SYSTEM_SELECTION_CHANNELS_DONE, request); Pair, Consumer> args = (Pair, Consumer>) request.argument; request.phone.setSystemSelectionChannels(args.first, onCompleted); break; } case EVENT_SET_SYSTEM_SELECTION_CHANNELS_DONE: { ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; Pair, Consumer> args = (Pair, Consumer>) request.argument; args.second.accept(ar.exception == null); notifyRequester(request); break; } case CMD_GET_SYSTEM_SELECTION_CHANNELS: { request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_GET_SYSTEM_SELECTION_CHANNELS_DONE, request); Phone phone = getPhoneFromRequest(request); if (phone != null) { phone.getSystemSelectionChannels(onCompleted); } else { loge("getSystemSelectionChannels: No phone object"); request.result = new ArrayList(); notifyRequester(request); } break; } case EVENT_GET_SYSTEM_SELECTION_CHANNELS_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception == null && ar.result != null) { request.result = ar.result; } else { request.result = new IllegalStateException( "Failed to retrieve system selecton channels"); if (ar.result == null) { loge("getSystemSelectionChannels: Empty response"); } else { loge("getSystemSelectionChannels: Unknown exception"); } } notifyRequester(request); break; case EVENT_SET_FORBIDDEN_PLMNS_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception == null && ar.result != null) { request.result = ar.result; } else { request.result = -1; loge("Failed to set Forbidden Plmns"); if (ar.result == null) { loge("setForbidenPlmns: Empty response"); } else if (ar.exception != null) { loge("setForbiddenPlmns: Exception: " + ar.exception); request.result = -1; } else { loge("setForbiddenPlmns: Unknown exception"); } } notifyRequester(request); break; case CMD_SET_FORBIDDEN_PLMNS: request = (MainThreadRequest) msg.obj; uiccCard = getUiccCardFromRequest(request); if (uiccCard == null) { loge("setForbiddenPlmns: UiccCard is null"); request.result = -1; notifyRequester(request); break; } Pair> setFplmnsArgs = (Pair>) request.argument; appType = setFplmnsArgs.first; List fplmns = setFplmnsArgs.second; uiccApp = uiccCard.getApplicationByType(appType); if (uiccApp == null) { loge("setForbiddenPlmns: no app with specified type -- " + appType); request.result = -1; loge("Failed to get UICC App"); notifyRequester(request); } else { onCompleted = obtainMessage(EVENT_SET_FORBIDDEN_PLMNS_DONE, request); ((SIMRecords) uiccApp.getIccRecords()) .setForbiddenPlmns(onCompleted, fplmns); } break; case CMD_ERASE_MODEM_CONFIG: request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_ERASE_MODEM_CONFIG_DONE, request); defaultPhone.eraseModemConfig(onCompleted); break; case EVENT_ERASE_MODEM_CONFIG_DONE: handleNullReturnEvent(msg, "eraseModemConfig"); break; case CMD_ERASE_DATA_SHARED_PREFERENCES: request = (MainThreadRequest) msg.obj; request.result = defaultPhone.eraseDataInSharedPreferences(); notifyRequester(request); break; case CMD_CHANGE_ICC_LOCK_PASSWORD: request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_CHANGE_ICC_LOCK_PASSWORD_DONE, request); Pair changed = (Pair) request.argument; getPhoneFromRequest(request).getIccCard().changeIccLockPassword( changed.first, changed.second, onCompleted); break; case EVENT_CHANGE_ICC_LOCK_PASSWORD_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception == null) { request.result = TelephonyManager.CHANGE_ICC_LOCK_SUCCESS; // If the operation is successful, update the PIN storage Pair passwords = (Pair) request.argument; int phoneId = getPhoneFromRequest(request).getPhoneId(); UiccController.getInstance().getPinStorage() .storePin(passwords.second, phoneId); } else { request.result = msg.arg1; } notifyRequester(request); break; case CMD_SET_ICC_LOCK_ENABLED: { request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_SET_ICC_LOCK_ENABLED_DONE, request); Pair enabled = (Pair) request.argument; getPhoneFromRequest(request).getIccCard().setIccLockEnabled( enabled.first, enabled.second, onCompleted); break; } case EVENT_SET_ICC_LOCK_ENABLED_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception == null) { request.result = TelephonyManager.CHANGE_ICC_LOCK_SUCCESS; // If the operation is successful, update the PIN storage Pair enabled = (Pair) request.argument; int phoneId = getPhoneFromRequest(request).getPhoneId(); if (enabled.first) { UiccController.getInstance().getPinStorage() .storePin(enabled.second, phoneId); } else { UiccController.getInstance().getPinStorage().clearPin(phoneId); } } else { request.result = msg.arg1; } notifyRequester(request); break; case MSG_NOTIFY_USER_ACTIVITY: removeMessages(MSG_NOTIFY_USER_ACTIVITY); Intent intent = new Intent(TelephonyIntents.ACTION_USER_ACTIVITY_NOTIFICATION); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); getDefaultPhone().getContext().sendBroadcastAsUser( intent, UserHandle.ALL, permission.USER_ACTIVITY); break; case CMD_SET_DATA_THROTTLING: { request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_SET_DATA_THROTTLING_DONE, request); DataThrottlingRequest dataThrottlingRequest = (DataThrottlingRequest) request.argument; Phone phone = getPhoneFromRequest(request); if (phone != null) { phone.setDataThrottling(onCompleted, request.workSource, dataThrottlingRequest.getDataThrottlingAction(), dataThrottlingRequest.getCompletionDurationMillis()); } else { loge("setDataThrottling: No phone object"); request.result = TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE; notifyRequester(request); } break; } case EVENT_SET_DATA_THROTTLING_DONE: ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; if (ar.exception == null) { request.result = TelephonyManager.THERMAL_MITIGATION_RESULT_SUCCESS; } else if (ar.exception instanceof CommandException) { loge("setDataThrottling: CommandException: " + ar.exception); CommandException.Error error = ((CommandException) (ar.exception)).getCommandError(); if (error == CommandException.Error.RADIO_NOT_AVAILABLE) { request.result = TelephonyManager .THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE; } else if (error == CommandException.Error.INVALID_ARGUMENTS) { request.result = SET_DATA_THROTTLING_MODEM_THREW_INVALID_PARAMS; } else if (error == CommandException.Error.REQUEST_NOT_SUPPORTED) { request.result = MODEM_DOES_NOT_SUPPORT_DATA_THROTTLING_ERROR_CODE; } else { request.result = TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_ERROR; } } else { request.result = TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_ERROR; } Log.w(LOG_TAG, "DataThrottlingResult = " + request.result); notifyRequester(request); break; case CMD_SET_SIM_POWER: { request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_SET_SIM_POWER_DONE, request); request = (MainThreadRequest) msg.obj; int stateToSet = ((Pair) request.argument).first; request.phone.setSimPowerState(stateToSet, onCompleted, request.workSource); break; } case EVENT_SET_SIM_POWER_DONE: { ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; IIntegerConsumer callback = ((Pair) request.argument).second; if (ar.exception != null) { loge("setSimPower exception: " + ar.exception); int errorCode = TelephonyManager.CallForwardingInfoCallback .RESULT_ERROR_UNKNOWN; if (ar.exception instanceof CommandException) { CommandException.Error error = ((CommandException) (ar.exception)).getCommandError(); if (error == CommandException.Error.SIM_ERR) { errorCode = TelephonyManager.SET_SIM_POWER_STATE_SIM_ERROR; } else if (error == CommandException.Error.INVALID_ARGUMENTS) { errorCode = TelephonyManager.SET_SIM_POWER_STATE_ALREADY_IN_STATE; } else if (error == CommandException.Error.REQUEST_NOT_SUPPORTED) { errorCode = TelephonyManager.SET_SIM_POWER_STATE_NOT_SUPPORTED; } else { errorCode = TelephonyManager.SET_SIM_POWER_STATE_MODEM_ERROR; } } try { callback.accept(errorCode); } catch (RemoteException e) { // Ignore if the remote process is no longer available to call back. Log.w(LOG_TAG, "setSimPower: callback not available."); } } else { try { callback.accept(TelephonyManager.SET_SIM_POWER_STATE_SUCCESS); } catch (RemoteException e) { // Ignore if the remote process is no longer available to call back. Log.w(LOG_TAG, "setSimPower: callback not available."); } } break; } case CMD_SET_SIGNAL_STRENGTH_UPDATE_REQUEST: { request = (MainThreadRequest) msg.obj; final Phone phone = getPhoneFromRequest(request); if (phone == null || phone.getServiceStateTracker() == null) { request.result = new IllegalStateException("Phone or SST is null"); notifyRequester(request); break; } Pair pair = (Pair) request.argument; onCompleted = obtainMessage(EVENT_SET_SIGNAL_STRENGTH_UPDATE_REQUEST_DONE, request); phone.getServiceStateTracker().setSignalStrengthUpdateRequest( request.subId, pair.first /*callingUid*/, pair.second /*request*/, onCompleted); break; } case EVENT_SET_SIGNAL_STRENGTH_UPDATE_REQUEST_DONE: { ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; // request.result will be the exception of ar if present, true otherwise. // Be cautious not to leave result null which will wait() forever request.result = ar.exception != null ? ar.exception : true; notifyRequester(request); break; } case CMD_CLEAR_SIGNAL_STRENGTH_UPDATE_REQUEST: { request = (MainThreadRequest) msg.obj; Phone phone = getPhoneFromRequest(request); if (phone == null || phone.getServiceStateTracker() == null) { request.result = new IllegalStateException("Phone or SST is null"); notifyRequester(request); break; } Pair pair = (Pair) request.argument; onCompleted = obtainMessage(EVENT_CLEAR_SIGNAL_STRENGTH_UPDATE_REQUEST_DONE, request); phone.getServiceStateTracker().clearSignalStrengthUpdateRequest( request.subId, pair.first /*callingUid*/, pair.second /*request*/, onCompleted); break; } case EVENT_CLEAR_SIGNAL_STRENGTH_UPDATE_REQUEST_DONE: { ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; request.result = ar.exception != null ? ar.exception : true; notifyRequester(request); break; } case CMD_GET_SLICING_CONFIG: { request = (MainThreadRequest) msg.obj; onCompleted = obtainMessage(EVENT_GET_SLICING_CONFIG_DONE, request); request.phone.getSlicingConfig(onCompleted); break; } case EVENT_GET_SLICING_CONFIG_DONE: { ar = (AsyncResult) msg.obj; request = (MainThreadRequest) ar.userObj; ResultReceiver result = (ResultReceiver) request.argument; NetworkSlicingConfig slicingConfig = null; Bundle bundle = new Bundle(); int resultCode = 0; if (ar.exception != null) { Log.e(LOG_TAG, "Exception retrieving slicing configuration=" + ar.exception); resultCode = TelephonyManager.NetworkSlicingException.ERROR_MODEM_ERROR; } else if (ar.result == null) { Log.w(LOG_TAG, "Timeout Waiting for slicing configuration!"); resultCode = TelephonyManager.NetworkSlicingException.ERROR_TIMEOUT; } else { // use the result as returned resultCode = TelephonyManager.NetworkSlicingException.SUCCESS; slicingConfig = (NetworkSlicingConfig) ar.result; } if (slicingConfig == null) { slicingConfig = new NetworkSlicingConfig(); } bundle.putParcelable(TelephonyManager.KEY_SLICING_CONFIG_HANDLE, slicingConfig); result.send(resultCode, bundle); notifyRequester(request); break; } case CMD_PREPARE_UNATTENDED_REBOOT: request = (MainThreadRequest) msg.obj; request.result = UiccController.getInstance().getPinStorage() .prepareUnattendedReboot(request.workSource); notifyRequester(request); break; default: Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what); break; } } private void notifyRequester(MainThreadRequest request) { synchronized (request) { request.notifyAll(); } } private void handleNullReturnEvent(Message msg, String command) { AsyncResult ar = (AsyncResult) msg.obj; MainThreadRequest request = (MainThreadRequest) ar.userObj; if (ar.exception == null) { request.result = true; } else { request.result = false; if (ar.exception instanceof CommandException) { loge(command + ": CommandException: " + ar.exception); } else { loge(command + ": Unknown exception"); } } notifyRequester(request); } } /** * Posts the specified command to be executed on the main thread, * waits for the request to complete, and returns the result. * @see #sendRequestAsync */ private Object sendRequest(int command, Object argument) { return sendRequest(command, argument, SubscriptionManager.INVALID_SUBSCRIPTION_ID, null, null, -1 /*timeoutInMs*/); } /** * Posts the specified command to be executed on the main thread, * waits for the request to complete, and returns the result. * @see #sendRequestAsync */ private Object sendRequest(int command, Object argument, WorkSource workSource) { return sendRequest(command, argument, SubscriptionManager.INVALID_SUBSCRIPTION_ID, null, workSource, -1 /*timeoutInMs*/); } /** * Posts the specified command to be executed on the main thread, * waits for the request to complete, and returns the result. * @see #sendRequestAsync */ private Object sendRequest(int command, Object argument, Integer subId) { return sendRequest(command, argument, subId, null, null, -1 /*timeoutInMs*/); } /** * Posts the specified command to be executed on the main thread, * waits for the request to complete for at most {@code timeoutInMs}, and returns the result * if not timeout or null otherwise. * @see #sendRequestAsync */ private @Nullable Object sendRequest(int command, Object argument, Integer subId, long timeoutInMs) { return sendRequest(command, argument, subId, null, null, timeoutInMs); } /** * Posts the specified command to be executed on the main thread, * waits for the request to complete, and returns the result. * @see #sendRequestAsync */ private Object sendRequest(int command, Object argument, int subId, WorkSource workSource) { return sendRequest(command, argument, subId, null, workSource, -1 /*timeoutInMs*/); } /** * Posts the specified command to be executed on the main thread, * waits for the request to complete, and returns the result. * @see #sendRequestAsync */ private Object sendRequest(int command, Object argument, Phone phone, WorkSource workSource) { return sendRequest(command, argument, SubscriptionManager.INVALID_SUBSCRIPTION_ID, phone, workSource, -1 /*timeoutInMs*/); } /** * Posts the specified command to be executed on the main thread. If {@code timeoutInMs} is * negative, waits for the request to complete, and returns the result. Otherwise, wait for * maximum of {@code timeoutInMs} milliseconds, interrupt and return null. * @see #sendRequestAsync */ private @Nullable Object sendRequest(int command, Object argument, Integer subId, Phone phone, WorkSource workSource, long timeoutInMs) { if (Looper.myLooper() == mMainThreadHandler.getLooper()) { throw new RuntimeException("This method will deadlock if called from the main thread."); } MainThreadRequest request = null; if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID && phone != null) { throw new IllegalArgumentException("subId and phone cannot both be specified!"); } else if (phone != null) { request = new MainThreadRequest(argument, phone, workSource); } else { request = new MainThreadRequest(argument, subId, workSource); } Message msg = mMainThreadHandler.obtainMessage(command, request); msg.sendToTarget(); synchronized (request) { if (timeoutInMs >= 0) { // Wait for at least timeoutInMs before returning null request result long now = SystemClock.elapsedRealtime(); long deadline = now + timeoutInMs; while (request.result == null && now < deadline) { try { request.wait(deadline - now); } catch (InterruptedException e) { // Do nothing, go back and check if request is completed or timeout } finally { now = SystemClock.elapsedRealtime(); } } } else { // Wait for the request to complete while (request.result == null) { try { request.wait(); } catch (InterruptedException e) { // Do nothing, go back and wait until the request is complete } } } } if (request.result == null) { Log.wtf(LOG_TAG, "sendRequest: Blocking command timed out. Something has gone terribly wrong."); } return request.result; } /** * Asynchronous ("fire and forget") version of sendRequest(): * Posts the specified command to be executed on the main thread, and * returns immediately. * @see #sendRequest */ private void sendRequestAsync(int command) { mMainThreadHandler.sendEmptyMessage(command); } /** * Same as {@link #sendRequestAsync(int)} except it takes an argument. * @see {@link #sendRequest(int)} */ private void sendRequestAsync(int command, Object argument) { sendRequestAsync(command, argument, null, null); } /** * Same as {@link #sendRequestAsync(int,Object)} except it takes a Phone and WorkSource. * @see {@link #sendRequest(int,Object)} */ private void sendRequestAsync( int command, Object argument, Phone phone, WorkSource workSource) { MainThreadRequest request = new MainThreadRequest(argument, phone, workSource); Message msg = mMainThreadHandler.obtainMessage(command, request); msg.sendToTarget(); } /** * Initialize the singleton PhoneInterfaceManager instance. * This is only done once, at startup, from PhoneApp.onCreate(). */ /* package */ static PhoneInterfaceManager init(PhoneGlobals app) { synchronized (PhoneInterfaceManager.class) { if (sInstance == null) { sInstance = new PhoneInterfaceManager(app); } else { Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance); } return sInstance; } } /** Private constructor; @see init() */ private PhoneInterfaceManager(PhoneGlobals app) { mApp = app; mCM = PhoneGlobals.getInstance().mCM; mImsResolver = ImsResolver.getInstance(); mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE); mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE); mPm = app.getSystemService(PackageManager.class); mMainThreadHandler = new MainThreadHandler(); mSubscriptionController = SubscriptionController.getInstance(); mTelephonySharedPreferences = PreferenceManager.getDefaultSharedPreferences(mApp); mNetworkScanRequestTracker = new NetworkScanRequestTracker(); mPhoneConfigurationManager = PhoneConfigurationManager.getInstance(); mRadioInterfaceCapabilities = RadioInterfaceCapabilityController.getInstance(); mNotifyUserActivity = new AtomicBoolean(false); publish(); } private Phone getDefaultPhone() { Phone thePhone = getPhone(getDefaultSubscription()); return (thePhone != null) ? thePhone : PhoneFactory.getDefaultPhone(); } private void publish() { if (DBG) log("publish: " + this); TelephonyFrameworkInitializer .getTelephonyServiceManager() .getTelephonyServiceRegisterer() .register(this); } private Phone getPhoneFromRequest(MainThreadRequest request) { if (request.phone != null) { return request.phone; } else { return getPhoneFromSubId(request.subId); } } private Phone getPhoneFromSubId(int subId) { return (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) ? getDefaultPhone() : getPhone(subId); } private UiccCard getUiccCardFromRequest(MainThreadRequest request) { Phone phone = getPhoneFromRequest(request); return phone == null ? null : UiccController.getInstance().getUiccCard(phone.getPhoneId()); } // returns phone associated with the subId. private Phone getPhone(int subId) { return PhoneFactory.getPhone(mSubscriptionController.getPhoneId(subId)); } private void sendEraseModemConfig(@NonNull Phone phone) { Boolean success = (Boolean) sendRequest(CMD_ERASE_MODEM_CONFIG, null); if (DBG) log("eraseModemConfig:" + ' ' + (success ? "ok" : "fail")); } private void sendEraseDataInSharedPreferences(@NonNull Phone phone) { Boolean success = (Boolean) sendRequest(CMD_ERASE_DATA_SHARED_PREFERENCES, null); if (DBG) log("eraseDataInSharedPreferences:" + ' ' + (success ? "ok" : "fail")); } private boolean isImsAvailableOnDevice() { PackageManager pm = getDefaultPhone().getContext().getPackageManager(); if (pm == null) { // For some reason package manger is not available.. This will fail internally anyway, // so do not throw error and allow. return true; } return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS, 0); } public void dial(String number) { dialForSubscriber(getPreferredVoiceSubscription(), number); } public void dialForSubscriber(int subId, String number) { if (DBG) log("dial: " + number); // No permission check needed here: This is just a wrapper around the // ACTION_DIAL intent, which is available to any app since it puts up // the UI before it does anything. final long identity = Binder.clearCallingIdentity(); try { String url = createTelUrl(number); if (url == null) { return; } // PENDING: should we just silently fail if phone is offhook or ringing? PhoneConstants.State state = mCM.getState(subId); if (state != PhoneConstants.State.OFFHOOK && state != PhoneConstants.State.RINGING) { Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url)); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mApp.startActivity(intent); } } finally { Binder.restoreCallingIdentity(identity); } } public void call(String callingPackage, String number) { callForSubscriber(getPreferredVoiceSubscription(), callingPackage, number); } public void callForSubscriber(int subId, String callingPackage, String number) { if (DBG) log("call: " + number); // This is just a wrapper around the ACTION_CALL intent, but we still // need to do a permission check since we're calling startActivity() // from the context of the phone app. enforceCallPermission(); if (mAppOps.noteOp(AppOpsManager.OPSTR_CALL_PHONE, Binder.getCallingUid(), callingPackage) != AppOpsManager.MODE_ALLOWED) { return; } final long identity = Binder.clearCallingIdentity(); try { String url = createTelUrl(number); if (url == null) { return; } boolean isValid = false; final List slist = getActiveSubscriptionInfoListPrivileged(); if (slist != null) { for (SubscriptionInfo subInfoRecord : slist) { if (subInfoRecord.getSubscriptionId() == subId) { isValid = true; break; } } } if (!isValid) { return; } Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url)); intent.putExtra(SUBSCRIPTION_KEY, subId); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mApp.startActivity(intent); } finally { Binder.restoreCallingIdentity(identity); } } public boolean supplyPinForSubscriber(int subId, String pin) { int [] resultArray = supplyPinReportResultForSubscriber(subId, pin); return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false; } public boolean supplyPukForSubscriber(int subId, String puk, String pin) { int [] resultArray = supplyPukReportResultForSubscriber(subId, puk, pin); return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false; } public int[] supplyPinReportResultForSubscriber(int subId, String pin) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subId); final UnlockSim checkSimPin = new UnlockSim(phone.getPhoneId(), phone.getIccCard()); checkSimPin.start(); return checkSimPin.unlockSim(null, pin); } finally { Binder.restoreCallingIdentity(identity); } } public int[] supplyPukReportResultForSubscriber(int subId, String puk, String pin) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subId); final UnlockSim checkSimPuk = new UnlockSim(phone.getPhoneId(), phone.getIccCard()); checkSimPuk.start(); return checkSimPuk.unlockSim(puk, pin); } finally { Binder.restoreCallingIdentity(identity); } } /** * Helper thread to turn async call to SimCard#supplyPin into * a synchronous one. */ private static class UnlockSim extends Thread { private final IccCard mSimCard; private final int mPhoneId; private boolean mDone = false; private int mResult = PhoneConstants.PIN_GENERAL_FAILURE; private int mRetryCount = -1; // For replies from SimCard interface private Handler mHandler; // For async handler to identify request type private static final int SUPPLY_PIN_COMPLETE = 100; UnlockSim(int phoneId, IccCard simCard) { mPhoneId = phoneId; mSimCard = simCard; } @Override public void run() { Looper.prepare(); synchronized (UnlockSim.this) { mHandler = new Handler() { @Override public void handleMessage(Message msg) { AsyncResult ar = (AsyncResult) msg.obj; switch (msg.what) { case SUPPLY_PIN_COMPLETE: Log.d(LOG_TAG, "SUPPLY_PIN_COMPLETE"); synchronized (UnlockSim.this) { mRetryCount = msg.arg1; if (ar.exception != null) { if (ar.exception instanceof CommandException && ((CommandException)(ar.exception)).getCommandError() == CommandException.Error.PASSWORD_INCORRECT) { mResult = PhoneConstants.PIN_PASSWORD_INCORRECT; } //When UiccCardApp dispose,handle message and return exception else if (ar.exception instanceof CommandException && ((CommandException) (ar.exception)).getCommandError() == CommandException.Error.ABORTED) { mResult = PhoneConstants.PIN_OPERATION_ABORTED; } else { mResult = PhoneConstants.PIN_GENERAL_FAILURE; } } else { mResult = PhoneConstants.PIN_RESULT_SUCCESS; } mDone = true; UnlockSim.this.notifyAll(); } break; } } }; UnlockSim.this.notifyAll(); } Looper.loop(); } /* * Use PIN or PUK to unlock SIM card * * If PUK is null, unlock SIM card with PIN * * If PUK is not null, unlock SIM card with PUK and set PIN code */ synchronized int[] unlockSim(String puk, String pin) { while (mHandler == null) { try { wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } Message callback = Message.obtain(mHandler, SUPPLY_PIN_COMPLETE); if (puk == null) { mSimCard.supplyPin(pin, callback); } else { mSimCard.supplyPuk(puk, pin, callback); } while (!mDone) { try { Log.d(LOG_TAG, "wait for done"); wait(); } catch (InterruptedException e) { // Restore the interrupted status Thread.currentThread().interrupt(); } } Log.d(LOG_TAG, "done"); int[] resultArray = new int[2]; resultArray[0] = mResult; resultArray[1] = mRetryCount; if (mResult == PhoneConstants.PIN_RESULT_SUCCESS && pin.length() > 0) { UiccController.getInstance().getPinStorage().storePin(pin, mPhoneId); } return resultArray; } } /** * This method has been removed due to privacy and stability concerns. */ @Override public void updateServiceLocation() { Log.e(LOG_TAG, "Call to unsupported method updateServiceLocation()"); return; } @Override public void updateServiceLocationWithPackageName(String callingPackage) { mApp.getSystemService(AppOpsManager.class) .checkPackage(Binder.getCallingUid(), callingPackage); final int targetSdk = TelephonyPermissions.getTargetSdk(mApp, callingPackage); if (targetSdk > android.os.Build.VERSION_CODES.R) { // Callers targeting S have no business invoking this method. return; } LocationAccessPolicy.LocationPermissionResult locationResult = LocationAccessPolicy.checkLocationPermission(mApp, new LocationAccessPolicy.LocationPermissionQuery.Builder() .setCallingPackage(callingPackage) .setCallingFeatureId(null) .setCallingPid(Binder.getCallingPid()) .setCallingUid(Binder.getCallingUid()) .setMethod("updateServiceLocation") .setMinSdkVersionForCoarse(Build.VERSION_CODES.BASE) .setMinSdkVersionForFine(Build.VERSION_CODES.Q) .build()); // Apps that lack location permission have no business calling this method; // however, because no permission was declared in the public API, denials must // all be "soft". switch (locationResult) { case DENIED_HARD: /* fall through */ case DENIED_SOFT: return; } WorkSource workSource = getWorkSource(Binder.getCallingUid()); final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(getDefaultSubscription()); if (phone != null) { phone.updateServiceLocation(workSource); } } finally { Binder.restoreCallingIdentity(identity); } } @Deprecated @Override public boolean isRadioOn(String callingPackage) { return isRadioOnWithFeature(callingPackage, null); } @Override public boolean isRadioOnWithFeature(String callingPackage, String callingFeatureId) { return isRadioOnForSubscriberWithFeature(getDefaultSubscription(), callingPackage, callingFeatureId); } @Deprecated @Override public boolean isRadioOnForSubscriber(int subId, String callingPackage) { return isRadioOnForSubscriberWithFeature(subId, callingPackage, null); } @Override public boolean isRadioOnForSubscriberWithFeature(int subId, String callingPackage, String callingFeatureId) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mApp, subId, callingPackage, callingFeatureId, "isRadioOnForSubscriber")) { return false; } final long identity = Binder.clearCallingIdentity(); try { return isRadioOnForSubscriber(subId); } finally { Binder.restoreCallingIdentity(identity); } } private boolean isRadioOnForSubscriber(int subId) { final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone != null) { return phone.getServiceState().getState() != ServiceState.STATE_POWER_OFF; } else { return false; } } finally { Binder.restoreCallingIdentity(identity); } } public void toggleRadioOnOff() { toggleRadioOnOffForSubscriber(getDefaultSubscription()); } public void toggleRadioOnOffForSubscriber(int subId) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone != null) { phone.setRadioPower(!isRadioOnForSubscriber(subId)); } } finally { Binder.restoreCallingIdentity(identity); } } public boolean setRadio(boolean turnOn) { return setRadioForSubscriber(getDefaultSubscription(), turnOn); } public boolean setRadioForSubscriber(int subId, boolean turnOn) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone == null) { return false; } if ((phone.getServiceState().getState() != ServiceState.STATE_POWER_OFF) != turnOn) { toggleRadioOnOffForSubscriber(subId); } return true; } finally { Binder.restoreCallingIdentity(identity); } } public boolean needMobileRadioShutdown() { enforceReadPrivilegedPermission("needMobileRadioShutdown"); /* * If any of the Radios are available, it will need to be * shutdown. So return true if any Radio is available. */ final long identity = Binder.clearCallingIdentity(); try { for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) { Phone phone = PhoneFactory.getPhone(i); if (phone != null && phone.isRadioAvailable()) return true; } logv(TelephonyManager.getDefault().getPhoneCount() + " Phones are shutdown."); return false; } finally { Binder.restoreCallingIdentity(identity); } } @Override public void shutdownMobileRadios() { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) { logv("Shutting down Phone " + i); shutdownRadioUsingPhoneId(i); } } finally { Binder.restoreCallingIdentity(identity); } } private void shutdownRadioUsingPhoneId(int phoneId) { Phone phone = PhoneFactory.getPhone(phoneId); if (phone != null && phone.isRadioAvailable()) { phone.shutdownRadio(); } } public boolean setRadioPower(boolean turnOn) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { final Phone defaultPhone = PhoneFactory.getDefaultPhone(); if (defaultPhone != null) { defaultPhone.setRadioPower(turnOn); return true; } else { loge("There's no default phone."); return false; } } finally { Binder.restoreCallingIdentity(identity); } } public boolean setRadioPowerForSubscriber(int subId, boolean turnOn) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone != null) { phone.setRadioPower(turnOn); return true; } else { return false; } } finally { Binder.restoreCallingIdentity(identity); } } // FIXME: subId version needed @Override public boolean enableDataConnectivity() { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { int subId = mSubscriptionController.getDefaultDataSubId(); final Phone phone = getPhone(subId); if (phone != null) { phone.getDataEnabledSettings().setDataEnabled( TelephonyManager.DATA_ENABLED_REASON_USER, true); return true; } else { return false; } } finally { Binder.restoreCallingIdentity(identity); } } // FIXME: subId version needed @Override public boolean disableDataConnectivity() { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { int subId = mSubscriptionController.getDefaultDataSubId(); final Phone phone = getPhone(subId); if (phone != null) { phone.getDataEnabledSettings().setDataEnabled( TelephonyManager.DATA_ENABLED_REASON_USER, false); return true; } else { return false; } } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean isDataConnectivityPossible(int subId) { final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone != null) { return phone.isDataAllowed(ApnSetting.TYPE_DEFAULT); } else { return false; } } finally { Binder.restoreCallingIdentity(identity); } } public boolean handlePinMmi(String dialString) { return handlePinMmiForSubscriber(getDefaultSubscription(), dialString); } public void handleUssdRequest(int subId, String ussdRequest, ResultReceiver wrappedCallback) { enforceCallPermission(); final long identity = Binder.clearCallingIdentity(); try { if (!SubscriptionManager.isValidSubscriptionId(subId)) { return; } Pair ussdObject = new Pair(ussdRequest, wrappedCallback); sendRequest(CMD_HANDLE_USSD_REQUEST, ussdObject, subId); } finally { Binder.restoreCallingIdentity(identity); } }; public boolean handlePinMmiForSubscriber(int subId, String dialString) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { if (!SubscriptionManager.isValidSubscriptionId(subId)) { return false; } return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString, subId); } finally { Binder.restoreCallingIdentity(identity); } } /** * @deprecated This method is deprecated and is only being kept due to an UnsupportedAppUsage * tag on getCallState Binder call. */ @Deprecated @Override public int getCallState() { if (CompatChanges.isChangeEnabled( TelecomManager.ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION, Binder.getCallingUid())) { // Do not allow this API to be called on API version 31+, it should only be // called on old apps using this Binder call directly. throw new SecurityException("This method can only be used for applications " + "targeting API version 30 or less."); } final long identity = Binder.clearCallingIdentity(); try { Phone phone = getPhone(getDefaultSubscription()); return phone == null ? TelephonyManager.CALL_STATE_IDLE : PhoneConstantConversions.convertCallState(phone.getState()); } finally { Binder.restoreCallingIdentity(identity); } } @Override public int getCallStateForSubscription(int subId, String callingPackage, String featureId) { if (CompatChanges.isChangeEnabled( TelecomManager.ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION, Binder.getCallingUid())) { // Check READ_PHONE_STATE for API version 31+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, subId, callingPackage, featureId, "getCallStateForSubscription")) { throw new SecurityException("getCallState requires READ_PHONE_STATE for apps " + "targeting API level 31+."); } } final long identity = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subId); return phone == null ? TelephonyManager.CALL_STATE_IDLE : PhoneConstantConversions.convertCallState(phone.getState()); } finally { Binder.restoreCallingIdentity(identity); } } @Override public int getDataState() { return getDataStateForSubId(mSubscriptionController.getDefaultDataSubId()); } @Override public int getDataStateForSubId(int subId) { final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone != null) { return PhoneConstantConversions.convertDataState(phone.getDataConnectionState()); } else { return PhoneConstantConversions.convertDataState( PhoneConstants.DataState.DISCONNECTED); } } finally { Binder.restoreCallingIdentity(identity); } } @Override public int getDataActivity() { return getDataActivityForSubId(mSubscriptionController.getDefaultDataSubId()); } @Override public int getDataActivityForSubId(int subId) { final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone != null) { return DefaultPhoneNotifier.convertDataActivityState(phone.getDataActivityState()); } else { return TelephonyManager.DATA_ACTIVITY_NONE; } } finally { Binder.restoreCallingIdentity(identity); } } @Override public CellIdentity getCellLocation(String callingPackage, String callingFeatureId) { mApp.getSystemService(AppOpsManager.class) .checkPackage(Binder.getCallingUid(), callingPackage); LocationAccessPolicy.LocationPermissionResult locationResult = LocationAccessPolicy.checkLocationPermission(mApp, new LocationAccessPolicy.LocationPermissionQuery.Builder() .setCallingPackage(callingPackage) .setCallingFeatureId(callingFeatureId) .setCallingPid(Binder.getCallingPid()) .setCallingUid(Binder.getCallingUid()) .setMethod("getCellLocation") .setMinSdkVersionForCoarse(Build.VERSION_CODES.BASE) .setMinSdkVersionForFine(Build.VERSION_CODES.Q) .build()); switch (locationResult) { case DENIED_HARD: throw new SecurityException("Not allowed to access cell location"); case DENIED_SOFT: return (getDefaultPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) ? new CellIdentityCdma() : new CellIdentityGsm(); } WorkSource workSource = getWorkSource(Binder.getCallingUid()); final long identity = Binder.clearCallingIdentity(); try { if (DBG_LOC) log("getCellLocation: is active user"); int subId = mSubscriptionController.getDefaultDataSubId(); return (CellIdentity) sendRequest(CMD_GET_CELL_LOCATION, workSource, subId); } finally { Binder.restoreCallingIdentity(identity); } } @Override public String getNetworkCountryIsoForPhone(int phoneId) { // Reporting the correct network country is ambiguous when IWLAN could conflict with // registered cell info, so return a NULL country instead. final long identity = Binder.clearCallingIdentity(); try { if (phoneId == SubscriptionManager.INVALID_PHONE_INDEX) { // Get default phone in this case. phoneId = SubscriptionManager.DEFAULT_PHONE_INDEX; } final int subId = mSubscriptionController.getSubIdUsingPhoneId(phoneId); Phone phone = PhoneFactory.getPhone(phoneId); if (phone == null) return ""; ServiceStateTracker sst = phone.getServiceStateTracker(); if (sst == null) return ""; LocaleTracker lt = sst.getLocaleTracker(); if (lt == null) return ""; return lt.getCurrentCountry(); } finally { Binder.restoreCallingIdentity(identity); } } /** * This method was removed due to potential issues caused by performing partial * updates of service state, and lack of a credible use case. * * This has the ability to break the telephony implementation by disabling notification of * changes in device connectivity. DO NOT USE THIS! */ @Override public void enableLocationUpdates() { mApp.enforceCallingOrSelfPermission( android.Manifest.permission.CONTROL_LOCATION_UPDATES, null); } /** * This method was removed due to potential issues caused by performing partial * updates of service state, and lack of a credible use case. * * This has the ability to break the telephony implementation by disabling notification of * changes in device connectivity. DO NOT USE THIS! */ @Override public void disableLocationUpdates() { mApp.enforceCallingOrSelfPermission( android.Manifest.permission.CONTROL_LOCATION_UPDATES, null); } @Override @SuppressWarnings("unchecked") public List getNeighboringCellInfo(String callingPackage, String callingFeatureId) { try { mApp.getSystemService(AppOpsManager.class) .checkPackage(Binder.getCallingUid(), callingPackage); } catch (SecurityException e) { EventLog.writeEvent(0x534e4554, "190619791", Binder.getCallingUid()); throw e; } final int targetSdk = TelephonyPermissions.getTargetSdk(mApp, callingPackage); if (targetSdk >= android.os.Build.VERSION_CODES.Q) { throw new SecurityException( "getNeighboringCellInfo() is unavailable to callers targeting Q+ SDK levels."); } if (mAppOps.noteOp(AppOpsManager.OPSTR_NEIGHBORING_CELLS, Binder.getCallingUid(), callingPackage) != AppOpsManager.MODE_ALLOWED) { return null; } if (DBG_LOC) log("getNeighboringCellInfo: is active user"); List info = getAllCellInfo(callingPackage, callingFeatureId); if (info == null) return null; List neighbors = new ArrayList(); for (CellInfo ci : info) { if (ci instanceof CellInfoGsm) { neighbors.add(new NeighboringCellInfo((CellInfoGsm) ci)); } else if (ci instanceof CellInfoWcdma) { neighbors.add(new NeighboringCellInfo((CellInfoWcdma) ci)); } } return (neighbors.size()) > 0 ? neighbors : null; } private List getCachedCellInfo() { List cellInfos = new ArrayList(); for (Phone phone : PhoneFactory.getPhones()) { List info = phone.getAllCellInfo(); if (info != null) cellInfos.addAll(info); } return cellInfos; } @Override public List getAllCellInfo(String callingPackage, String callingFeatureId) { mApp.getSystemService(AppOpsManager.class) .checkPackage(Binder.getCallingUid(), callingPackage); LocationAccessPolicy.LocationPermissionResult locationResult = LocationAccessPolicy.checkLocationPermission(mApp, new LocationAccessPolicy.LocationPermissionQuery.Builder() .setCallingPackage(callingPackage) .setCallingFeatureId(callingFeatureId) .setCallingPid(Binder.getCallingPid()) .setCallingUid(Binder.getCallingUid()) .setMethod("getAllCellInfo") .setMinSdkVersionForCoarse(Build.VERSION_CODES.BASE) .setMinSdkVersionForFine(Build.VERSION_CODES.Q) .build()); switch (locationResult) { case DENIED_HARD: throw new SecurityException("Not allowed to access cell info"); case DENIED_SOFT: return new ArrayList<>(); } final int targetSdk = TelephonyPermissions.getTargetSdk(mApp, callingPackage); if (targetSdk >= android.os.Build.VERSION_CODES.Q) { return getCachedCellInfo(); } if (DBG_LOC) log("getAllCellInfo: is active user"); WorkSource workSource = getWorkSource(Binder.getCallingUid()); final long identity = Binder.clearCallingIdentity(); try { List cellInfos = new ArrayList(); for (Phone phone : PhoneFactory.getPhones()) { final List info = (List) sendRequest( CMD_GET_ALL_CELL_INFO, null, phone, workSource); if (info != null) cellInfos.addAll(info); } return cellInfos; } finally { Binder.restoreCallingIdentity(identity); } } @Override public void requestCellInfoUpdate(int subId, ICellInfoCallback cb, String callingPackage, String callingFeatureId) { requestCellInfoUpdateInternal(subId, cb, callingPackage, callingFeatureId, getWorkSource(Binder.getCallingUid())); } @Override public void requestCellInfoUpdateWithWorkSource(int subId, ICellInfoCallback cb, String callingPackage, String callingFeatureId, WorkSource workSource) { enforceModifyPermission(); requestCellInfoUpdateInternal(subId, cb, callingPackage, callingFeatureId, workSource); } private void requestCellInfoUpdateInternal(int subId, ICellInfoCallback cb, String callingPackage, String callingFeatureId, WorkSource workSource) { mApp.getSystemService(AppOpsManager.class) .checkPackage(Binder.getCallingUid(), callingPackage); LocationAccessPolicy.LocationPermissionResult locationResult = LocationAccessPolicy.checkLocationPermission(mApp, new LocationAccessPolicy.LocationPermissionQuery.Builder() .setCallingPackage(callingPackage) .setCallingFeatureId(callingFeatureId) .setCallingPid(Binder.getCallingPid()) .setCallingUid(Binder.getCallingUid()) .setMethod("requestCellInfoUpdate") .setMinSdkVersionForCoarse(Build.VERSION_CODES.BASE) .setMinSdkVersionForFine(Build.VERSION_CODES.BASE) .build()); switch (locationResult) { case DENIED_HARD: if (TelephonyPermissions .getTargetSdk(mApp, callingPackage) < Build.VERSION_CODES.Q) { // Safetynet logging for b/154934934 EventLog.writeEvent(0x534e4554, "154934934", Binder.getCallingUid()); } throw new SecurityException("Not allowed to access cell info"); case DENIED_SOFT: if (TelephonyPermissions .getTargetSdk(mApp, callingPackage) < Build.VERSION_CODES.Q) { // Safetynet logging for b/154934934 EventLog.writeEvent(0x534e4554, "154934934", Binder.getCallingUid()); } try { cb.onCellInfo(new ArrayList()); } catch (RemoteException re) { // Drop without consequences } return; } final Phone phone = getPhoneFromSubId(subId); if (phone == null) throw new IllegalArgumentException("Invalid Subscription Id: " + subId); sendRequestAsync(CMD_REQUEST_CELL_INFO_UPDATE, cb, phone, workSource); } @Override public void setCellInfoListRate(int rateInMillis) { enforceModifyPermission(); WorkSource workSource = getWorkSource(Binder.getCallingUid()); final long identity = Binder.clearCallingIdentity(); try { getDefaultPhone().setCellInfoListRate(rateInMillis, workSource); } finally { Binder.restoreCallingIdentity(identity); } } @Override public String getImeiForSlot(int slotIndex, String callingPackage, String callingFeatureId) { Phone phone = PhoneFactory.getPhone(slotIndex); if (phone == null) { return null; } int subId = phone.getSubId(); enforceCallingPackage(callingPackage, Binder.getCallingUid(), "getImeiForSlot"); if (!TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mApp, subId, callingPackage, callingFeatureId, "getImeiForSlot")) { return null; } final long identity = Binder.clearCallingIdentity(); try { return phone.getImei(); } finally { Binder.restoreCallingIdentity(identity); } } @Override public String getTypeAllocationCodeForSlot(int slotIndex) { Phone phone = PhoneFactory.getPhone(slotIndex); String tac = null; if (phone != null) { String imei = phone.getImei(); tac = imei == null ? null : imei.substring(0, TYPE_ALLOCATION_CODE_LENGTH); } return tac; } @Override public String getMeidForSlot(int slotIndex, String callingPackage, String callingFeatureId) { try { mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); } catch (SecurityException se) { EventLog.writeEvent(0x534e4554, "186530496", Binder.getCallingUid()); throw new SecurityException("Package " + callingPackage + " does not belong to " + Binder.getCallingUid()); } Phone phone = PhoneFactory.getPhone(slotIndex); if (phone == null) { return null; } int subId = phone.getSubId(); if (!TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mApp, subId, callingPackage, callingFeatureId, "getMeidForSlot")) { return null; } final long identity = Binder.clearCallingIdentity(); try { return phone.getMeid(); } finally { Binder.restoreCallingIdentity(identity); } } @Override public String getManufacturerCodeForSlot(int slotIndex) { Phone phone = PhoneFactory.getPhone(slotIndex); String manufacturerCode = null; if (phone != null) { String meid = phone.getMeid(); manufacturerCode = meid == null ? null : meid.substring(0, MANUFACTURER_CODE_LENGTH); } return manufacturerCode; } @Override public String getDeviceSoftwareVersionForSlot(int slotIndex, String callingPackage, String callingFeatureId) { Phone phone = PhoneFactory.getPhone(slotIndex); if (phone == null) { return null; } int subId = phone.getSubId(); if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mApp, subId, callingPackage, callingFeatureId, "getDeviceSoftwareVersionForSlot")) { return null; } final long identity = Binder.clearCallingIdentity(); try { return phone.getDeviceSvn(); } finally { Binder.restoreCallingIdentity(identity); } } @Override public int getSubscriptionCarrierId(int subId) { final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); return phone == null ? TelephonyManager.UNKNOWN_CARRIER_ID : phone.getCarrierId(); } finally { Binder.restoreCallingIdentity(identity); } } @Override public String getSubscriptionCarrierName(int subId) { final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); return phone == null ? null : phone.getCarrierName(); } finally { Binder.restoreCallingIdentity(identity); } } @Override public int getSubscriptionSpecificCarrierId(int subId) { final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); return phone == null ? TelephonyManager.UNKNOWN_CARRIER_ID : phone.getSpecificCarrierId(); } finally { Binder.restoreCallingIdentity(identity); } } @Override public String getSubscriptionSpecificCarrierName(int subId) { final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); return phone == null ? null : phone.getSpecificCarrierName(); } finally { Binder.restoreCallingIdentity(identity); } } @Override public int getCarrierIdFromMccMnc(int slotIndex, String mccmnc, boolean isSubscriptionMccMnc) { if (!isSubscriptionMccMnc) { enforceReadPrivilegedPermission("getCarrierIdFromMccMnc"); } final Phone phone = PhoneFactory.getPhone(slotIndex); if (phone == null) { return TelephonyManager.UNKNOWN_CARRIER_ID; } final long identity = Binder.clearCallingIdentity(); try { return CarrierResolver.getCarrierIdFromMccMnc(phone.getContext(), mccmnc); } finally { Binder.restoreCallingIdentity(identity); } } // // Internal helper methods. // /** * Make sure the caller is the calling package itself * * @throws SecurityException if the caller is not the calling package */ private void enforceCallingPackage(String callingPackage, int callingUid, String message) { int packageUid = -1; PackageManager pm = mApp.getBaseContext().createContextAsUser( UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager(); try { packageUid = pm.getPackageUid(callingPackage, 0); } catch (PackageManager.NameNotFoundException e) { // packageUid is -1 } if (packageUid != callingUid) { throw new SecurityException(message + ": Package " + callingPackage + " does not belong to " + callingUid); } } /** * Make sure the caller has the MODIFY_PHONE_STATE permission. * * @throws SecurityException if the caller does not have the required permission */ private void enforceModifyPermission() { mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null); } /** * Make sure the caller is system. * * @throws SecurityException if the caller is not system. */ private static void enforceSystemCaller() { if (Binder.getCallingUid() != Process.SYSTEM_UID) { throw new SecurityException("Caller must be system"); } } private void enforceActiveEmergencySessionPermission() { mApp.enforceCallingOrSelfPermission( android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null); } /** * Make sure the caller has the CALL_PHONE permission. * * @throws SecurityException if the caller does not have the required permission */ private void enforceCallPermission() { mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null); } private void enforceSettingsPermission() { mApp.enforceCallingOrSelfPermission(android.Manifest.permission.NETWORK_SETTINGS, null); } private void enforceRebootPermission() { mApp.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null); } private String createTelUrl(String number) { if (TextUtils.isEmpty(number)) { return null; } return "tel:" + number; } private static void log(String msg) { Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg); } private static void logv(String msg) { Log.v(LOG_TAG, "[PhoneIntfMgr] " + msg); } private static void loge(String msg) { Log.e(LOG_TAG, "[PhoneIntfMgr] " + msg); } @Override public int getActivePhoneType() { return getActivePhoneTypeForSlot(getSlotForDefaultSubscription()); } @Override public int getActivePhoneTypeForSlot(int slotIndex) { final long identity = Binder.clearCallingIdentity(); try { final Phone phone = PhoneFactory.getPhone(slotIndex); if (phone == null) { return PhoneConstants.PHONE_TYPE_NONE; } else { return phone.getPhoneType(); } } finally { Binder.restoreCallingIdentity(identity); } } /** * Returns the CDMA ERI icon index to display */ @Override public int getCdmaEriIconIndex(String callingPackage, String callingFeatureId) { return getCdmaEriIconIndexForSubscriber(getDefaultSubscription(), callingPackage, callingFeatureId); } @Override public int getCdmaEriIconIndexForSubscriber(int subId, String callingPackage, String callingFeatureId) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mApp, subId, callingPackage, callingFeatureId, "getCdmaEriIconIndexForSubscriber")) { return -1; } final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone != null) { return phone.getCdmaEriIconIndex(); } else { return -1; } } finally { Binder.restoreCallingIdentity(identity); } } /** * Returns the CDMA ERI icon mode, * 0 - ON * 1 - FLASHING */ @Override public int getCdmaEriIconMode(String callingPackage, String callingFeatureId) { return getCdmaEriIconModeForSubscriber(getDefaultSubscription(), callingPackage, callingFeatureId); } @Override public int getCdmaEriIconModeForSubscriber(int subId, String callingPackage, String callingFeatureId) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mApp, subId, callingPackage, callingFeatureId, "getCdmaEriIconModeForSubscriber")) { return -1; } final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone != null) { return phone.getCdmaEriIconMode(); } else { return -1; } } finally { Binder.restoreCallingIdentity(identity); } } /** * Returns the CDMA ERI text, */ @Override public String getCdmaEriText(String callingPackage, String callingFeatureId) { return getCdmaEriTextForSubscriber(getDefaultSubscription(), callingPackage, callingFeatureId); } @Override public String getCdmaEriTextForSubscriber(int subId, String callingPackage, String callingFeatureId) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mApp, subId, callingPackage, callingFeatureId, "getCdmaEriIconTextForSubscriber")) { return null; } final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone != null) { return phone.getCdmaEriText(); } else { return null; } } finally { Binder.restoreCallingIdentity(identity); } } /** * Returns the CDMA MDN. */ @Override public String getCdmaMdn(int subId) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "getCdmaMdn"); final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone != null && phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) { return phone.getLine1Number(); } else { loge("getCdmaMdn: no phone found. Invalid subId: " + subId); return null; } } finally { Binder.restoreCallingIdentity(identity); } } /** * Returns the CDMA MIN. */ @Override public String getCdmaMin(int subId) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "getCdmaMin"); final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone != null && phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) { return phone.getCdmaMin(); } else { return null; } } finally { Binder.restoreCallingIdentity(identity); } } @Override public void requestNumberVerification(PhoneNumberRange range, long timeoutMillis, INumberVerificationCallback callback, String callingPackage) { if (mApp.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) != PERMISSION_GRANTED) { throw new SecurityException("Caller must hold the MODIFY_PHONE_STATE permission"); } mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); String authorizedPackage = NumberVerificationManager.getAuthorizedPackage(mApp); if (!TextUtils.equals(callingPackage, authorizedPackage)) { throw new SecurityException("Calling package must be configured in the device config: " + "calling package: " + callingPackage + ", configured package: " + authorizedPackage); } if (range == null) { throw new NullPointerException("Range must be non-null"); } timeoutMillis = Math.min(timeoutMillis, TelephonyManager.getMaxNumberVerificationTimeoutMillis()); NumberVerificationManager.getInstance().requestVerification(range, callback, timeoutMillis); } /** * Returns true if CDMA provisioning needs to run. */ public boolean needsOtaServiceProvisioning() { final long identity = Binder.clearCallingIdentity(); try { return getDefaultPhone().needsOtaServiceProvisioning(); } finally { Binder.restoreCallingIdentity(identity); } } /** * Sets the voice mail number of a given subId. */ @Override public boolean setVoiceMailNumber(int subId, String alphaTag, String number) { TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege( mApp, subId, "setVoiceMailNumber"); final long identity = Binder.clearCallingIdentity(); try { Boolean success = (Boolean) sendRequest(CMD_SET_VOICEMAIL_NUMBER, new Pair(alphaTag, number), new Integer(subId)); return success; } finally { Binder.restoreCallingIdentity(identity); } } @Override public Bundle getVisualVoicemailSettings(String callingPackage, int subId) { mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); TelecomManager tm = mApp.getSystemService(TelecomManager.class); String systemDialer = tm.getSystemDialerPackage(); if (!TextUtils.equals(callingPackage, systemDialer)) { throw new SecurityException("caller must be system dialer"); } final long identity = Binder.clearCallingIdentity(); try { PhoneAccountHandle phoneAccountHandle = PhoneAccountHandleConverter.fromSubId(subId); if (phoneAccountHandle == null) { return null; } return VisualVoicemailSettingsUtil.dump(mApp, phoneAccountHandle); } finally { Binder.restoreCallingIdentity(identity); } } @Override public String getVisualVoicemailPackageName(String callingPackage, String callingFeatureId, int subId) { mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mApp, subId, callingPackage, callingFeatureId, "getVisualVoicemailPackageName")) { return null; } final long identity = Binder.clearCallingIdentity(); try { return RemoteVvmTaskManager.getRemotePackage(mApp, subId).getPackageName(); } finally { Binder.restoreCallingIdentity(identity); } } @Override public void enableVisualVoicemailSmsFilter(String callingPackage, int subId, VisualVoicemailSmsFilterSettings settings) { mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); final long identity = Binder.clearCallingIdentity(); try { VisualVoicemailSmsFilterConfig.enableVisualVoicemailSmsFilter( mApp, callingPackage, subId, settings); } finally { Binder.restoreCallingIdentity(identity); } } @Override public void disableVisualVoicemailSmsFilter(String callingPackage, int subId) { mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); final long identity = Binder.clearCallingIdentity(); try { VisualVoicemailSmsFilterConfig.disableVisualVoicemailSmsFilter( mApp, callingPackage, subId); } finally { Binder.restoreCallingIdentity(identity); } } @Override public VisualVoicemailSmsFilterSettings getVisualVoicemailSmsFilterSettings( String callingPackage, int subId) { mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); final long identity = Binder.clearCallingIdentity(); try { return VisualVoicemailSmsFilterConfig.getVisualVoicemailSmsFilterSettings( mApp, callingPackage, subId); } finally { Binder.restoreCallingIdentity(identity); } } @Override public VisualVoicemailSmsFilterSettings getActiveVisualVoicemailSmsFilterSettings(int subId) { enforceReadPrivilegedPermission("getActiveVisualVoicemailSmsFilterSettings"); final long identity = Binder.clearCallingIdentity(); try { return VisualVoicemailSmsFilterConfig.getActiveVisualVoicemailSmsFilterSettings( mApp, subId); } finally { Binder.restoreCallingIdentity(identity); } } @Override public void sendVisualVoicemailSmsForSubscriber(String callingPackage, String callingAttributionTag, int subId, String number, int port, String text, PendingIntent sentIntent) { mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); enforceVisualVoicemailPackage(callingPackage, subId); enforceSendSmsPermission(); SmsController smsController = PhoneFactory.getSmsController(); smsController.sendVisualVoicemailSmsForSubscriber(callingPackage, callingAttributionTag, subId, number, port, text, sentIntent); } /** * Sets the voice activation state of a given subId. */ @Override public void setVoiceActivationState(int subId, int activationState) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "setVoiceActivationState"); final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone != null) { phone.setVoiceActivationState(activationState); } else { loge("setVoiceActivationState fails with invalid subId: " + subId); } } finally { Binder.restoreCallingIdentity(identity); } } /** * Sets the data activation state of a given subId. */ @Override public void setDataActivationState(int subId, int activationState) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "setDataActivationState"); final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone != null) { phone.setDataActivationState(activationState); } else { loge("setDataActivationState fails with invalid subId: " + subId); } } finally { Binder.restoreCallingIdentity(identity); } } /** * Returns the voice activation state of a given subId. */ @Override public int getVoiceActivationState(int subId, String callingPackage) { enforceReadPrivilegedPermission("getVoiceActivationState"); final Phone phone = getPhone(subId); final long identity = Binder.clearCallingIdentity(); try { if (phone != null) { return phone.getVoiceActivationState(); } else { return TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN; } } finally { Binder.restoreCallingIdentity(identity); } } /** * Returns the data activation state of a given subId. */ @Override public int getDataActivationState(int subId, String callingPackage) { enforceReadPrivilegedPermission("getDataActivationState"); final Phone phone = getPhone(subId); final long identity = Binder.clearCallingIdentity(); try { if (phone != null) { return phone.getDataActivationState(); } else { return TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN; } } finally { Binder.restoreCallingIdentity(identity); } } /** * Returns the unread count of voicemails for a subId */ @Override public int getVoiceMessageCountForSubscriber(int subId, String callingPackage, String callingFeatureId) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mApp, subId, callingPackage, callingFeatureId, "getVoiceMessageCountForSubscriber")) { return 0; } final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone != null) { return phone.getVoiceMessageCount(); } else { return 0; } } finally { Binder.restoreCallingIdentity(identity); } } /** * returns true, if the device is in a state where both voice and data * are supported simultaneously. This can change based on location or network condition. */ @Override public boolean isConcurrentVoiceAndDataAllowed(int subId) { final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); return (phone == null ? false : phone.isConcurrentVoiceAndDataAllowed()); } finally { Binder.restoreCallingIdentity(identity); } } /** * Send the dialer code if called from the current default dialer or the caller has * carrier privilege. * @param inputCode The dialer code to send */ @Override public void sendDialerSpecialCode(String callingPackage, String inputCode) { final Phone defaultPhone = getDefaultPhone(); mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); TelecomManager tm = defaultPhone.getContext().getSystemService(TelecomManager.class); String defaultDialer = tm.getDefaultDialerPackage(); if (!TextUtils.equals(callingPackage, defaultDialer)) { TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mApp, getDefaultSubscription(), "sendDialerSpecialCode"); } final long identity = Binder.clearCallingIdentity(); try { defaultPhone.sendDialerSpecialCode(inputCode); } finally { Binder.restoreCallingIdentity(identity); } } @Override public int getNetworkSelectionMode(int subId) { TelephonyPermissions .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege( mApp, subId, "getNetworkSelectionMode"); final long identity = Binder.clearCallingIdentity(); try { if (!isActiveSubscription(subId)) { return TelephonyManager.NETWORK_SELECTION_MODE_UNKNOWN; } return (int) sendRequest(CMD_GET_NETWORK_SELECTION_MODE, null /* argument */, subId); } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean isInEmergencySmsMode() { enforceReadPrivilegedPermission("isInEmergencySmsMode"); final long identity = Binder.clearCallingIdentity(); try { for (Phone phone : PhoneFactory.getPhones()) { if (phone.isInEmergencySmsMode()) { return true; } } } finally { Binder.restoreCallingIdentity(identity); } return false; } /** * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission. * @param subId The subscription to use to check the configuration. * @param c The callback that will be used to send the result. */ @Override public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback c) throws RemoteException { TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege( mApp, subId, "registerImsRegistrationCallback"); if (!ImsManager.isImsSupportedOnDevice(mApp)) { throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION, "IMS not available on device."); } final long token = Binder.clearCallingIdentity(); try { int slotId = getSlotIndexOrException(subId); verifyImsMmTelConfiguredOrThrow(slotId); ImsManager.getInstance(mApp, slotId).addRegistrationCallbackForSubscription(c, subId); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(token); } } /** * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission. * @param subId The subscription to use to check the configuration. * @param c The callback that will be used to send the result. */ @Override public void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback c) { TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege( mApp, subId, "unregisterImsRegistrationCallback"); if (!SubscriptionManager.isValidSubscriptionId(subId)) { throw new IllegalArgumentException("Invalid Subscription ID: " + subId); } final long token = Binder.clearCallingIdentity(); try { ImsManager.getInstance(mApp, getSlotIndexOrException(subId)) .removeRegistrationCallbackForSubscription(c, subId); } catch (ImsException e) { Log.i(LOG_TAG, "unregisterImsRegistrationCallback: " + subId + "is inactive, ignoring unregister."); // If the subscription is no longer active, just return, since the callback // will already have been removed internally. } finally { Binder.restoreCallingIdentity(token); } } /** * Get the IMS service registration state for the MmTelFeature associated with this sub id. */ @Override public void getImsMmTelRegistrationState(int subId, IIntegerConsumer consumer) { enforceReadPrivilegedPermission("getImsMmTelRegistrationState"); if (!ImsManager.isImsSupportedOnDevice(mApp)) { throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION, "IMS not available on device."); } final long token = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subId); if (phone == null) { Log.w(LOG_TAG, "getImsMmTelRegistrationState: called with an invalid subscription '" + subId + "'"); throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION); } phone.getImsRegistrationState(regState -> { try { consumer.accept((regState == null) ? RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED : regState); } catch (RemoteException e) { // Ignore if the remote process is no longer available to call back. Log.w(LOG_TAG, "getImsMmTelRegistrationState: callback not available."); } }); } finally { Binder.restoreCallingIdentity(token); } } /** * Get the transport type for the IMS service registration state. */ @Override public void getImsMmTelRegistrationTransportType(int subId, IIntegerConsumer consumer) { TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege( mApp, subId, "getImsMmTelRegistrationTransportType"); if (!ImsManager.isImsSupportedOnDevice(mApp)) { throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION, "IMS not available on device."); } final long token = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subId); if (phone == null) { Log.w(LOG_TAG, "getImsMmTelRegistrationState: called with an invalid subscription '" + subId + "'"); throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION); } phone.getImsRegistrationTech(regTech -> { // Convert registration tech from ImsRegistrationImplBase -> RegistrationManager int regTechConverted = (regTech == null) ? ImsRegistrationImplBase.REGISTRATION_TECH_NONE : regTech; regTechConverted = RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get( regTechConverted); try { consumer.accept(regTechConverted); } catch (RemoteException e) { // Ignore if the remote process is no longer available to call back. Log.w(LOG_TAG, "getImsMmTelRegistrationState: callback not available."); } }); } finally { Binder.restoreCallingIdentity(token); } } /** * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission. * @param subId The subscription to use to check the configuration. * @param c The callback that will be used to send the result. */ @Override public void registerMmTelCapabilityCallback(int subId, IImsCapabilityCallback c) throws RemoteException { TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege( mApp, subId, "registerMmTelCapabilityCallback"); if (!ImsManager.isImsSupportedOnDevice(mApp)) { throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION, "IMS not available on device."); } final long token = Binder.clearCallingIdentity(); try { int slotId = getSlotIndexOrException(subId); verifyImsMmTelConfiguredOrThrow(slotId); ImsManager.getInstance(mApp, slotId).addCapabilitiesCallbackForSubscription(c, subId); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(token); } } /** * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission. * @param subId The subscription to use to check the configuration. * @param c The callback that will be used to send the result. */ @Override public void unregisterMmTelCapabilityCallback(int subId, IImsCapabilityCallback c) { TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege( mApp, subId, "unregisterMmTelCapabilityCallback"); if (!SubscriptionManager.isValidSubscriptionId(subId)) { throw new IllegalArgumentException("Invalid Subscription ID: " + subId); } final long token = Binder.clearCallingIdentity(); try { ImsManager.getInstance(mApp, getSlotIndexOrException(subId)) .removeCapabilitiesCallbackForSubscription(c, subId); } catch (ImsException e) { Log.i(LOG_TAG, "unregisterMmTelCapabilityCallback: " + subId + "is inactive, ignoring unregister."); // If the subscription is no longer active, just return, since the callback // will already have been removed internally. } finally { Binder.restoreCallingIdentity(token); } } @Override public boolean isCapable(int subId, int capability, int regTech) { enforceReadPrivilegedPermission("isCapable"); final long token = Binder.clearCallingIdentity(); try { int slotId = getSlotIndexOrException(subId); verifyImsMmTelConfiguredOrThrow(slotId); return ImsManager.getInstance(mApp, slotId).queryMmTelCapability(capability, regTech); } catch (com.android.ims.ImsException e) { Log.w(LOG_TAG, "IMS isCapable - service unavailable: " + e.getMessage()); return false; } catch (ImsException e) { Log.i(LOG_TAG, "isCapable: " + subId + " is inactive, returning false."); return false; } finally { Binder.restoreCallingIdentity(token); } } @Override public boolean isAvailable(int subId, int capability, int regTech) { enforceReadPrivilegedPermission("isAvailable"); final long token = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subId); if (phone == null) return false; return phone.isImsCapabilityAvailable(capability, regTech); } catch (com.android.ims.ImsException e) { Log.w(LOG_TAG, "IMS isAvailable - service unavailable: " + e.getMessage()); return false; } finally { Binder.restoreCallingIdentity(token); } } /** * Determines if the MmTel feature capability is supported by the carrier configuration for this * subscription. * @param subId The subscription to use to check the configuration. * @param callback The callback that will be used to send the result. * @param capability The MmTelFeature capability that will be used to send the result. * @param transportType The transport type of the MmTelFeature capability. */ @Override public void isMmTelCapabilitySupported(int subId, IIntegerConsumer callback, int capability, int transportType) { enforceReadPrivilegedPermission("isMmTelCapabilitySupported"); if (!ImsManager.isImsSupportedOnDevice(mApp)) { throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION, "IMS not available on device."); } final long token = Binder.clearCallingIdentity(); try { int slotId = getSlotIndex(subId); if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) { Log.w(LOG_TAG, "isMmTelCapabilitySupported: called with an inactive subscription '" + subId + "'"); throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION); } verifyImsMmTelConfiguredOrThrow(slotId); ImsManager.getInstance(mApp, slotId).isSupported(capability, transportType, aBoolean -> { try { callback.accept((aBoolean == null) ? 0 : (aBoolean ? 1 : 0)); } catch (RemoteException e) { Log.w(LOG_TAG, "isMmTelCapabilitySupported: remote caller is not " + "running. Ignore"); } }); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(token); } } /** * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission. * @param subId The subscription to use to check the configuration. */ @Override public boolean isAdvancedCallingSettingEnabled(int subId) { TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege( mApp, subId, "isAdvancedCallingSettingEnabled"); final long token = Binder.clearCallingIdentity(); try { int slotId = getSlotIndexOrException(subId); // This setting doesn't require an active ImsService connection, so do not verify. return ImsManager.getInstance(mApp, slotId).isEnhanced4gLteModeSettingEnabledByUser(); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(token); } } @Override public void setAdvancedCallingSettingEnabled(int subId, boolean isEnabled) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId, "setAdvancedCallingSettingEnabled"); final long identity = Binder.clearCallingIdentity(); try { int slotId = getSlotIndexOrException(subId); // This setting doesn't require an active ImsService connection, so do not verify. The // new setting will be picked up when the ImsService comes up next if it isn't up. ImsManager.getInstance(mApp, slotId).setEnhanced4gLteModeSetting(isEnabled); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(identity); } } /** * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission. * @param subId The subscription to use to check the configuration. */ @Override public boolean isVtSettingEnabled(int subId) { TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege( mApp, subId, "isVtSettingEnabled"); final long identity = Binder.clearCallingIdentity(); try { int slotId = getSlotIndexOrException(subId); // This setting doesn't require an active ImsService connection, so do not verify. return ImsManager.getInstance(mApp, slotId).isVtEnabledByUser(); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(identity); } } @Override public void setVtSettingEnabled(int subId, boolean isEnabled) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId, "setVtSettingEnabled"); final long identity = Binder.clearCallingIdentity(); try { int slotId = getSlotIndexOrException(subId); // This setting doesn't require an active ImsService connection, so do not verify. The // new setting will be picked up when the ImsService comes up next if it isn't up. ImsManager.getInstance(mApp, slotId).setVtSetting(isEnabled); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(identity); } } /** * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission. * @param subId The subscription to use to check the configuration. */ @Override public boolean isVoWiFiSettingEnabled(int subId) { TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege( mApp, subId, "isVoWiFiSettingEnabled"); final long identity = Binder.clearCallingIdentity(); try { int slotId = getSlotIndexOrException(subId); // This setting doesn't require an active ImsService connection, so do not verify. return ImsManager.getInstance(mApp, slotId).isWfcEnabledByUser(); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(identity); } } @Override public void setVoWiFiSettingEnabled(int subId, boolean isEnabled) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId, "setVoWiFiSettingEnabled"); final long identity = Binder.clearCallingIdentity(); try { int slotId = getSlotIndexOrException(subId); // This setting doesn't require an active ImsService connection, so do not verify. The // new setting will be picked up when the ImsService comes up next if it isn't up. ImsManager.getInstance(mApp, slotId).setWfcSetting(isEnabled); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(identity); } } /** * @return true if the user's setting for Voice over Cross SIM is enabled and false if it is not * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission. * @param subId The subscription to use to check the configuration. */ @Override public boolean isCrossSimCallingEnabledByUser(int subId) { TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege( mApp, subId, "isCrossSimCallingEnabledByUser"); final long identity = Binder.clearCallingIdentity(); try { int slotId = getSlotIndexOrException(subId); // This setting doesn't require an active ImsService connection, so do not verify. return ImsManager.getInstance(mApp, slotId).isCrossSimCallingEnabledByUser(); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(identity); } } /** * Sets the user's setting for whether or not Voice over Cross SIM is enabled. * Requires MODIFY_PHONE_STATE permission. * @param subId The subscription to use to check the configuration. * @param isEnabled true if the user's setting for Voice over Cross SIM is enabled, * false otherwise */ @Override public void setCrossSimCallingEnabled(int subId, boolean isEnabled) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId, "setCrossSimCallingEnabled"); final long identity = Binder.clearCallingIdentity(); try { int slotId = getSlotIndexOrException(subId); // This setting doesn't require an active ImsService connection, so do not verify. The // new setting will be picked up when the ImsService comes up next if it isn't up. ImsManager.getInstance(mApp, slotId).setCrossSimCallingEnabled(isEnabled); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(identity); } } /** * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission. * @param subId The subscription to use to check the configuration. */ @Override public boolean isVoWiFiRoamingSettingEnabled(int subId) { TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege( mApp, subId, "isVoWiFiRoamingSettingEnabled"); final long identity = Binder.clearCallingIdentity(); try { int slotId = getSlotIndexOrException(subId); // This setting doesn't require an active ImsService connection, so do not verify. return ImsManager.getInstance(mApp, slotId).isWfcRoamingEnabledByUser(); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(identity); } } @Override public void setVoWiFiRoamingSettingEnabled(int subId, boolean isEnabled) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId, "setVoWiFiRoamingSettingEnabled"); final long identity = Binder.clearCallingIdentity(); try { int slotId = getSlotIndexOrException(subId); // This setting doesn't require an active ImsService connection, so do not verify. The // new setting will be picked up when the ImsService comes up next if it isn't up. ImsManager.getInstance(mApp, slotId).setWfcRoamingSetting(isEnabled); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(identity); } } @Override public void setVoWiFiNonPersistent(int subId, boolean isCapable, int mode) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId, "setVoWiFiNonPersistent"); final long identity = Binder.clearCallingIdentity(); try { int slotId = getSlotIndexOrException(subId); // This setting will be ignored if the ImsService isn't up. ImsManager.getInstance(mApp, slotId).setWfcNonPersistent(isCapable, mode); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(identity); } } /** * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission. * @param subId The subscription to use to check the configuration. */ @Override public int getVoWiFiModeSetting(int subId) { TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege( mApp, subId, "getVoWiFiModeSetting"); final long identity = Binder.clearCallingIdentity(); try { int slotId = getSlotIndexOrException(subId); // This setting doesn't require an active ImsService connection, so do not verify. return ImsManager.getInstance(mApp, slotId).getWfcMode(false /*isRoaming*/); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(identity); } } @Override public void setVoWiFiModeSetting(int subId, int mode) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId, "setVoWiFiModeSetting"); final long identity = Binder.clearCallingIdentity(); try { int slotId = getSlotIndexOrException(subId); // This setting doesn't require an active ImsService connection, so do not verify. The // new setting will be picked up when the ImsService comes up next if it isn't up. ImsManager.getInstance(mApp, slotId).setWfcMode(mode, false /*isRoaming*/); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(identity); } } @Override public int getVoWiFiRoamingModeSetting(int subId) { enforceReadPrivilegedPermission("getVoWiFiRoamingModeSetting"); final long identity = Binder.clearCallingIdentity(); try { int slotId = getSlotIndexOrException(subId); // This setting doesn't require an active ImsService connection, so do not verify. return ImsManager.getInstance(mApp, slotId).getWfcMode(true /*isRoaming*/); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(identity); } } @Override public void setVoWiFiRoamingModeSetting(int subId, int mode) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId, "setVoWiFiRoamingModeSetting"); final long identity = Binder.clearCallingIdentity(); try { int slotId = getSlotIndexOrException(subId); // This setting doesn't require an active ImsService connection, so do not verify. The // new setting will be picked up when the ImsService comes up next if it isn't up. ImsManager.getInstance(mApp, slotId).setWfcMode(mode, true /*isRoaming*/); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(identity); } } @Override public void setRttCapabilitySetting(int subId, boolean isEnabled) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId, "setRttCapabilityEnabled"); final long identity = Binder.clearCallingIdentity(); try { int slotId = getSlotIndexOrException(subId); // This setting doesn't require an active ImsService connection, so do not verify. The // new setting will be picked up when the ImsService comes up next if it isn't up. ImsManager.getInstance(mApp, slotId).setRttEnabled(isEnabled); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(identity); } } /** * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission. * @param subId The subscription to use to check the configuration. */ @Override public boolean isTtyOverVolteEnabled(int subId) { TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege( mApp, subId, "isTtyOverVolteEnabled"); final long identity = Binder.clearCallingIdentity(); try { int slotId = getSlotIndexOrException(subId); // This setting doesn't require an active ImsService connection, so do not verify. return ImsManager.getInstance(mApp, slotId).isTtyOnVoLteCapable(); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(identity); } } @Override public void registerImsProvisioningChangedCallback(int subId, IImsConfigCallback callback) { enforceReadPrivilegedPermission("registerImsProvisioningChangedCallback"); final long identity = Binder.clearCallingIdentity(); try { if (!isImsAvailableOnDevice()) { throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION, "IMS not available on device."); } int slotId = getSlotIndexOrException(subId); verifyImsMmTelConfiguredOrThrow(slotId); ImsManager.getInstance(mApp, slotId) .addProvisioningCallbackForSubscription(callback, subId); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(identity); } } @Override public void unregisterImsProvisioningChangedCallback(int subId, IImsConfigCallback callback) { enforceReadPrivilegedPermission("unregisterImsProvisioningChangedCallback"); final long identity = Binder.clearCallingIdentity(); if (!SubscriptionManager.isValidSubscriptionId(subId)) { throw new IllegalArgumentException("Invalid Subscription ID: " + subId); } try { ImsManager.getInstance(mApp, getSlotIndexOrException(subId)) .removeProvisioningCallbackForSubscription(callback, subId); } catch (ImsException e) { Log.i(LOG_TAG, "unregisterImsProvisioningChangedCallback: " + subId + "is inactive, ignoring unregister."); // If the subscription is no longer active, just return, since the callback will already // have been removed internally. } finally { Binder.restoreCallingIdentity(identity); } } private void checkModifyPhoneStatePermission(int subId, String message) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId, message); } private boolean isImsProvisioningRequired(int subId, int capability, boolean isMmtelCapability) { Phone phone = getPhone(subId); if (phone == null) { loge("phone instance null for subid " + subId); return false; } if (isMmtelCapability) { if (!doesImsCapabilityRequireProvisioning(phone.getContext(), subId, capability)) { return false; } } else { if (!doesRcsCapabilityRequireProvisioning(phone.getContext(), subId, capability)) { return false; } } return true; } @Override public void setRcsProvisioningStatusForCapability(int subId, int capability, boolean isProvisioned) { checkModifyPhoneStatePermission(subId, "setRcsProvisioningStatusForCapability"); final long identity = Binder.clearCallingIdentity(); try { // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly. if (!isImsProvisioningRequired(subId, capability, false)) { return; } // this capability requires provisioning, route to the correct API. ImsManager ims = ImsManager.getInstance(mApp, getSlotIndex(subId)); switch (capability) { case RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE: case RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE: ims.setEabProvisioned(isProvisioned); break; default: { throw new IllegalArgumentException("Tried to set provisioning for " + "rcs capability '" + capability + "', which does not require " + "provisioning."); } } } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean getRcsProvisioningStatusForCapability(int subId, int capability) { enforceReadPrivilegedPermission("getRcsProvisioningStatusForCapability"); final long identity = Binder.clearCallingIdentity(); try { // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly. if (!isImsProvisioningRequired(subId, capability, false)) { return true; } ImsManager ims = ImsManager.getInstance(mApp, getSlotIndex(subId)); switch (capability) { case RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE: case RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE: return ims.isEabProvisionedOnDevice(); default: { throw new IllegalArgumentException("Tried to get rcs provisioning for " + "capability '" + capability + "', which does not require " + "provisioning."); } } } finally { Binder.restoreCallingIdentity(identity); } } @Override public void setImsProvisioningStatusForCapability(int subId, int capability, int tech, boolean isProvisioned) { if (tech != ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN && tech != ImsRegistrationImplBase.REGISTRATION_TECH_LTE && tech != ImsRegistrationImplBase.REGISTRATION_TECH_NR && tech != ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) { throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid"); } checkModifyPhoneStatePermission(subId, "setImsProvisioningStatusForCapability"); final long identity = Binder.clearCallingIdentity(); try { // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly. if (!isImsProvisioningRequired(subId, capability, true)) { return; } if (tech == ImsRegistrationImplBase.REGISTRATION_TECH_NR || tech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) { loge("setImsProvisioningStatusForCapability: called for technology that does " + "not support provisioning - " + tech); return; } // this capability requires provisioning, route to the correct API. ImsManager ims = ImsManager.getInstance(mApp, getSlotIndex(subId)); switch (capability) { case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE: { if (tech == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) { ims.setVolteProvisioned(isProvisioned); } else if (tech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) { ims.setWfcProvisioned(isProvisioned); } break; } case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO: { // There is currently no difference in VT provisioning type. ims.setVtProvisioned(isProvisioned); break; } case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT: { // There is no "deprecated" UT provisioning mechanism through ImsConfig, so // change the capability of the feature instead if needed. if (isMmTelCapabilityProvisionedInCache(subId, capability, tech) == isProvisioned) { // No change in provisioning. return; } cacheMmTelCapabilityProvisioning(subId, capability, tech, isProvisioned); try { ims.changeMmTelCapability(isProvisioned, capability, tech); } catch (com.android.ims.ImsException e) { loge("setImsProvisioningStatusForCapability: couldn't change UT capability" + ", Exception" + e.getMessage()); } break; } default: { throw new IllegalArgumentException("Tried to set provisioning for " + "MmTel capability '" + capability + "', which does not require " + "provisioning. "); } } } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean getImsProvisioningStatusForCapability(int subId, int capability, int tech) { if (tech != ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN && tech != ImsRegistrationImplBase.REGISTRATION_TECH_LTE && tech != ImsRegistrationImplBase.REGISTRATION_TECH_NR && tech != ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) { throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid"); } enforceReadPrivilegedPermission("getProvisioningStatusForCapability"); final long identity = Binder.clearCallingIdentity(); try { // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly. if (!isImsProvisioningRequired(subId, capability, true)) { return true; } if (tech == ImsRegistrationImplBase.REGISTRATION_TECH_NR || tech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) { loge("getImsProvisioningStatusForCapability: called for technology that does " + "not support provisioning - " + tech); return true; } ImsManager ims = ImsManager.getInstance(mApp, getSlotIndex(subId)); switch (capability) { case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE: { if (tech == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) { return ims.isVolteProvisionedOnDevice(); } else if (tech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) { return ims.isWfcProvisionedOnDevice(); } // This should never happen, since we are checking tech above to make sure it // is either LTE or IWLAN. throw new IllegalArgumentException("Invalid radio technology for voice " + "capability."); } case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO: { // There is currently no difference in VT provisioning type. return ims.isVtProvisionedOnDevice(); } case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT: { // There is no "deprecated" UT provisioning mechanism, so get from shared prefs. return isMmTelCapabilityProvisionedInCache(subId, capability, tech); } default: { throw new IllegalArgumentException( "Tried to get provisioning for MmTel capability '" + capability + "', which does not require provisioning."); } } } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean isMmTelCapabilityProvisionedInCache(int subId, int capability, int tech) { if (tech != ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN && tech != ImsRegistrationImplBase.REGISTRATION_TECH_LTE) { throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid"); } enforceReadPrivilegedPermission("isMmTelCapabilityProvisionedInCache"); int provisionedBits = getMmTelCapabilityProvisioningBitfield(subId, tech); return (provisionedBits & capability) > 0; } @Override public void cacheMmTelCapabilityProvisioning(int subId, int capability, int tech, boolean isProvisioned) { if (tech != ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN && tech != ImsRegistrationImplBase.REGISTRATION_TECH_LTE) { throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid"); } TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId, "setProvisioningStatusForCapability"); int provisionedBits = getMmTelCapabilityProvisioningBitfield(subId, tech); // If the current provisioning status for capability already matches isProvisioned, // do nothing. if (((provisionedBits & capability) > 0) == isProvisioned) { return; } if (isProvisioned) { setMmTelCapabilityProvisioningBitfield(subId, tech, (provisionedBits | capability)); } else { setMmTelCapabilityProvisioningBitfield(subId, tech, (provisionedBits & ~capability)); } } /** * @return the bitfield containing the MmTel provisioning for the provided subscription and * technology. The bitfield should mirror the bitfield defined by * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}. */ private int getMmTelCapabilityProvisioningBitfield(int subId, int tech) { String key = getMmTelProvisioningKey(subId, tech); // Default is no capabilities are provisioned. return mTelephonySharedPreferences.getInt(key, 0 /*default*/); } /** * Sets the MmTel capability provisioning bitfield (defined by * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}) for the subscription and * technology specified. * * Note: This is a synchronous command and should not be called on UI thread. */ private void setMmTelCapabilityProvisioningBitfield(int subId, int tech, int newField) { final SharedPreferences.Editor editor = mTelephonySharedPreferences.edit(); String key = getMmTelProvisioningKey(subId, tech); editor.putInt(key, newField); editor.commit(); } private static String getMmTelProvisioningKey(int subId, int tech) { // resulting key is provision_ims_mmtel_{subId}_{tech} return PREF_PROVISION_IMS_MMTEL_PREFIX + subId + "_" + tech; } /** * Query CarrierConfig to see if the specified capability requires provisioning for the * carrier associated with the subscription id. */ private boolean doesImsCapabilityRequireProvisioning(Context context, int subId, int capability) { CarrierConfigManager configManager = new CarrierConfigManager(context); PersistableBundle c = configManager.getConfigForSubId(subId); boolean requireUtProvisioning = c.getBoolean( CarrierConfigManager.KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL, false) && c.getBoolean(CarrierConfigManager.KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL, false); boolean requireVoiceVtProvisioning = c.getBoolean( CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false); // First check to make sure that the capability requires provisioning. switch (capability) { case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE: // intentional fallthrough case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO: { if (requireVoiceVtProvisioning) { // Voice and Video requires provisioning return true; } break; } case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT: { if (requireUtProvisioning) { // UT requires provisioning return true; } break; } } return false; } private boolean doesRcsCapabilityRequireProvisioning(Context context, int subId, int capability) { CarrierConfigManager configManager = new CarrierConfigManager(context); PersistableBundle c = configManager.getConfigForSubId(subId); boolean requireRcsProvisioning = c.getBoolean( CarrierConfigManager.KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL, false); // First check to make sure that the capability requires provisioning. switch (capability) { case RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE: // intentional fallthrough case RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE: { if (requireRcsProvisioning) { // OPTION or PRESENCE requires provisioning return true; } break; } } return false; } @Override public int getImsProvisioningInt(int subId, int key) { if (!SubscriptionManager.isValidSubscriptionId(subId)) { throw new IllegalArgumentException("Invalid Subscription id '" + subId + "'"); } enforceReadPrivilegedPermission("getImsProvisioningInt"); final long identity = Binder.clearCallingIdentity(); try { // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly. int slotId = getSlotIndex(subId); if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) { Log.w(LOG_TAG, "getImsProvisioningInt: called with an inactive subscription '" + subId + "' for key:" + key); return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN; } return ImsManager.getInstance(mApp, slotId).getConfigInt(key); } catch (com.android.ims.ImsException e) { Log.w(LOG_TAG, "getImsProvisioningInt: ImsService is not available for subscription '" + subId + "' for key:" + key); return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN; } finally { Binder.restoreCallingIdentity(identity); } } @Override public String getImsProvisioningString(int subId, int key) { if (!SubscriptionManager.isValidSubscriptionId(subId)) { throw new IllegalArgumentException("Invalid Subscription id '" + subId + "'"); } enforceReadPrivilegedPermission("getImsProvisioningString"); final long identity = Binder.clearCallingIdentity(); try { // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly. int slotId = getSlotIndex(subId); if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) { Log.w(LOG_TAG, "getImsProvisioningString: called for an inactive subscription id '" + subId + "' for key:" + key); return ProvisioningManager.STRING_QUERY_RESULT_ERROR_GENERIC; } return ImsManager.getInstance(mApp, slotId).getConfigString(key); } catch (com.android.ims.ImsException e) { Log.w(LOG_TAG, "getImsProvisioningString: ImsService is not available for sub '" + subId + "' for key:" + key); return ProvisioningManager.STRING_QUERY_RESULT_ERROR_NOT_READY; } finally { Binder.restoreCallingIdentity(identity); } } @Override public int setImsProvisioningInt(int subId, int key, int value) { if (!SubscriptionManager.isValidSubscriptionId(subId)) { throw new IllegalArgumentException("Invalid Subscription id '" + subId + "'"); } TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId, "setImsProvisioningInt"); final long identity = Binder.clearCallingIdentity(); try { // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly. int slotId = getSlotIndex(subId); if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) { Log.w(LOG_TAG, "setImsProvisioningInt: called with an inactive subscription id '" + subId + "' for key:" + key); return ImsConfigImplBase.CONFIG_RESULT_FAILED; } return ImsManager.getInstance(mApp, slotId).setConfig(key, value); } catch (com.android.ims.ImsException | RemoteException e) { Log.w(LOG_TAG, "setImsProvisioningInt: ImsService unavailable for sub '" + subId + "' for key:" + key, e); return ImsConfigImplBase.CONFIG_RESULT_FAILED; } finally { Binder.restoreCallingIdentity(identity); } } @Override public int setImsProvisioningString(int subId, int key, String value) { if (!SubscriptionManager.isValidSubscriptionId(subId)) { throw new IllegalArgumentException("Invalid Subscription id '" + subId + "'"); } TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId, "setImsProvisioningString"); final long identity = Binder.clearCallingIdentity(); try { // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly. int slotId = getSlotIndex(subId); if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) { Log.w(LOG_TAG, "setImsProvisioningString: called with an inactive subscription id '" + subId + "' for key:" + key); return ImsConfigImplBase.CONFIG_RESULT_FAILED; } return ImsManager.getInstance(mApp, slotId).setConfig(key, value); } catch (com.android.ims.ImsException | RemoteException e) { Log.w(LOG_TAG, "setImsProvisioningString: ImsService unavailable for sub '" + subId + "' for key:" + key, e); return ImsConfigImplBase.CONFIG_RESULT_FAILED; } finally { Binder.restoreCallingIdentity(identity); } } /** * Throw an ImsException if the IMS resolver does not have an ImsService configured for MMTEL * for the given slot ID or no ImsResolver instance has been created. * @param slotId The slot ID that the IMS service is created for. * @throws ImsException If there is no ImsService configured for this slot. */ private void verifyImsMmTelConfiguredOrThrow(int slotId) throws ImsException { if (mImsResolver == null || !mImsResolver.isImsServiceConfiguredForFeature(slotId, ImsFeature.FEATURE_MMTEL)) { throw new ImsException("This subscription does not support MMTEL over IMS", ImsException.CODE_ERROR_UNSUPPORTED_OPERATION); } } private int getSlotIndexOrException(int subId) throws ImsException { int slotId = SubscriptionManager.getSlotIndex(subId); if (!SubscriptionManager.isValidSlotIndex(slotId)) { throw new ImsException("Invalid Subscription Id, subId=" + subId, ImsException.CODE_ERROR_INVALID_SUBSCRIPTION); } return slotId; } private int getSlotIndex(int subId) { int slotId = SubscriptionManager.getSlotIndex(subId); if (!SubscriptionManager.isValidSlotIndex(slotId)) { return SubscriptionManager.INVALID_SIM_SLOT_INDEX; } return slotId; } /** * Returns the data network type for a subId; does not throw SecurityException. */ @Override public int getNetworkTypeForSubscriber(int subId, String callingPackage, String callingFeatureId) { try { mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); } catch (SecurityException se) { EventLog.writeEvent(0x534e4554, "186776740", Binder.getCallingUid()); throw new SecurityException("Package " + callingPackage + " does not belong to " + Binder.getCallingUid()); } final int targetSdk = TelephonyPermissions.getTargetSdk(mApp, callingPackage); if (targetSdk > android.os.Build.VERSION_CODES.Q) { return getDataNetworkTypeForSubscriber(subId, callingPackage, callingFeatureId); } else if (targetSdk == android.os.Build.VERSION_CODES.Q && !TelephonyPermissions.checkCallingOrSelfReadPhoneStateNoThrow( mApp, subId, callingPackage, callingFeatureId, "getNetworkTypeForSubscriber")) { return TelephonyManager.NETWORK_TYPE_UNKNOWN; } final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone != null) { return phone.getServiceState().getDataNetworkType(); } else { return TelephonyManager.NETWORK_TYPE_UNKNOWN; } } finally { Binder.restoreCallingIdentity(identity); } } /** * Returns the data network type */ @Override public int getDataNetworkType(String callingPackage, String callingFeatureId) { return getDataNetworkTypeForSubscriber(mSubscriptionController.getDefaultDataSubId(), callingPackage, callingFeatureId); } /** * Returns the data network type for a subId */ @Override public int getDataNetworkTypeForSubscriber(int subId, String callingPackage, String callingFeatureId) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mApp, subId, callingPackage, callingFeatureId, "getDataNetworkTypeForSubscriber")) { return TelephonyManager.NETWORK_TYPE_UNKNOWN; } final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone != null) { return phone.getServiceState().getDataNetworkType(); } else { return TelephonyManager.NETWORK_TYPE_UNKNOWN; } } finally { Binder.restoreCallingIdentity(identity); } } /** * Returns the Voice network type for a subId */ @Override public int getVoiceNetworkTypeForSubscriber(int subId, String callingPackage, String callingFeatureId) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mApp, subId, callingPackage, callingFeatureId, "getDataNetworkTypeForSubscriber")) { return TelephonyManager.NETWORK_TYPE_UNKNOWN; } final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone != null) { return phone.getServiceState().getVoiceNetworkType(); } else { return TelephonyManager.NETWORK_TYPE_UNKNOWN; } } finally { Binder.restoreCallingIdentity(identity); } } /** * @return true if a ICC card is present */ public boolean hasIccCard() { // FIXME Make changes to pass defaultSimId of type int return hasIccCardUsingSlotIndex(mSubscriptionController.getSlotIndex( getDefaultSubscription())); } /** * @return true if a ICC card is present for a slotIndex */ @Override public boolean hasIccCardUsingSlotIndex(int slotIndex) { final long identity = Binder.clearCallingIdentity(); try { final Phone phone = PhoneFactory.getPhone(slotIndex); if (phone != null) { return phone.getIccCard().hasIccCard(); } else { return false; } } finally { Binder.restoreCallingIdentity(identity); } } /** * Return if the current radio is LTE on CDMA. This * is a tri-state return value as for a period of time * the mode may be unknown. * * @param callingPackage the name of the package making the call. * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE} * or {@link Phone#LTE_ON_CDMA_TRUE} */ @Override public int getLteOnCdmaMode(String callingPackage, String callingFeatureId) { return getLteOnCdmaModeForSubscriber(getDefaultSubscription(), callingPackage, callingFeatureId); } @Override public int getLteOnCdmaModeForSubscriber(int subId, String callingPackage, String callingFeatureId) { try { enforceReadPrivilegedPermission("getLteOnCdmaModeForSubscriber"); } catch (SecurityException e) { return PhoneConstants.LTE_ON_CDMA_UNKNOWN; } final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone == null) { return PhoneConstants.LTE_ON_CDMA_UNKNOWN; } else { return TelephonyProperties.lte_on_cdma_device() .orElse(PhoneConstants.LTE_ON_CDMA_FALSE); } } finally { Binder.restoreCallingIdentity(identity); } } /** * {@hide} * Returns Default subId, 0 in the case of single standby. */ private int getDefaultSubscription() { return mSubscriptionController.getDefaultSubId(); } private int getSlotForDefaultSubscription() { return mSubscriptionController.getPhoneId(getDefaultSubscription()); } private int getPreferredVoiceSubscription() { return mSubscriptionController.getDefaultVoiceSubId(); } private boolean isActiveSubscription(int subId) { return mSubscriptionController.isActiveSubId(subId); } /** * @see android.telephony.TelephonyManager.WifiCallingChoices */ public int getWhenToMakeWifiCalls() { final long identity = Binder.clearCallingIdentity(); try { return Settings.System.getInt(mApp.getContentResolver(), Settings.System.WHEN_TO_MAKE_WIFI_CALLS, getWhenToMakeWifiCallsDefaultPreference()); } finally { Binder.restoreCallingIdentity(identity); } } /** * @see android.telephony.TelephonyManager.WifiCallingChoices */ public void setWhenToMakeWifiCalls(int preference) { final long identity = Binder.clearCallingIdentity(); try { if (DBG) log("setWhenToMakeWifiCallsStr, storing setting = " + preference); Settings.System.putInt(mApp.getContentResolver(), Settings.System.WHEN_TO_MAKE_WIFI_CALLS, preference); } finally { Binder.restoreCallingIdentity(identity); } } private static int getWhenToMakeWifiCallsDefaultPreference() { // TODO: Use a build property to choose this value. return TelephonyManager.WifiCallingChoices.ALWAYS_USE; } private Phone getPhoneFromSlotIdOrThrowException(int slotIndex) { int phoneId = UiccController.getInstance().getPhoneIdFromSlotId(slotIndex); if (phoneId == -1) { throw new IllegalArgumentException("Given slot index: " + slotIndex + " does not correspond to an active phone"); } return PhoneFactory.getPhone(phoneId); } @Override public IccOpenLogicalChannelResponse iccOpenLogicalChannel( int subId, String callingPackage, String aid, int p2) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "iccOpenLogicalChannel"); mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); if (DBG) { log("iccOpenLogicalChannel: subId=" + subId + " aid=" + aid + " p2=" + p2); } return iccOpenLogicalChannelWithPermission(getPhoneFromSubId(subId), callingPackage, aid, p2); } @Override public IccOpenLogicalChannelResponse iccOpenLogicalChannelBySlot( int slotIndex, String callingPackage, String aid, int p2) { enforceModifyPermission(); mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); if (DBG) { log("iccOpenLogicalChannelBySlot: slot=" + slotIndex + " aid=" + aid + " p2=" + p2); } return iccOpenLogicalChannelWithPermission(getPhoneFromSlotIdOrThrowException(slotIndex), callingPackage, aid, p2); } private IccOpenLogicalChannelResponse iccOpenLogicalChannelWithPermission(Phone phone, String callingPackage, String aid, int p2) { final long identity = Binder.clearCallingIdentity(); try { if (TextUtils.equals(ISDR_AID, aid)) { // Only allows LPA to open logical channel to ISD-R. ComponentInfo bestComponent = EuiccConnector.findBestComponent(getDefaultPhone() .getContext().getPackageManager()); if (bestComponent == null || !TextUtils.equals(callingPackage, bestComponent.packageName)) { loge("The calling package is not allowed to access ISD-R."); throw new SecurityException( "The calling package is not allowed to access ISD-R."); } } IccOpenLogicalChannelResponse response = (IccOpenLogicalChannelResponse) sendRequest( CMD_OPEN_CHANNEL, new Pair(aid, p2), phone, null /* workSource */); if (DBG) log("iccOpenLogicalChannelWithPermission: " + response); return response; } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean iccCloseLogicalChannel(int subId, int channel) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "iccCloseLogicalChannel"); if (DBG) log("iccCloseLogicalChannel: subId=" + subId + " chnl=" + channel); return iccCloseLogicalChannelWithPermission(getPhoneFromSubId(subId), channel); } @Override public boolean iccCloseLogicalChannelBySlot(int slotIndex, int channel) { enforceModifyPermission(); if (DBG) log("iccCloseLogicalChannelBySlot: slotIndex=" + slotIndex + " chnl=" + channel); return iccCloseLogicalChannelWithPermission(getPhoneFromSlotIdOrThrowException(slotIndex), channel); } private boolean iccCloseLogicalChannelWithPermission(Phone phone, int channel) { final long identity = Binder.clearCallingIdentity(); try { if (channel < 0) { return false; } Boolean success = (Boolean) sendRequest(CMD_CLOSE_CHANNEL, channel, phone, null /* workSource */); if (DBG) log("iccCloseLogicalChannelWithPermission: " + success); return success; } finally { Binder.restoreCallingIdentity(identity); } } @Override public String iccTransmitApduLogicalChannel(int subId, int channel, int cla, int command, int p1, int p2, int p3, String data) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "iccTransmitApduLogicalChannel"); if (DBG) { log("iccTransmitApduLogicalChannel: subId=" + subId + " chnl=" + channel + " cla=" + cla + " cmd=" + command + " p1=" + p1 + " p2=" + p2 + " p3=" + p3 + " data=" + data); } return iccTransmitApduLogicalChannelWithPermission(getPhoneFromSubId(subId), channel, cla, command, p1, p2, p3, data); } @Override public String iccTransmitApduLogicalChannelBySlot(int slotIndex, int channel, int cla, int command, int p1, int p2, int p3, String data) { enforceModifyPermission(); if (DBG) { log("iccTransmitApduLogicalChannelBySlot: slotIndex=" + slotIndex + " chnl=" + channel + " cla=" + cla + " cmd=" + command + " p1=" + p1 + " p2=" + p2 + " p3=" + p3 + " data=" + data); } return iccTransmitApduLogicalChannelWithPermission( getPhoneFromSlotIdOrThrowException(slotIndex), channel, cla, command, p1, p2, p3, data); } private String iccTransmitApduLogicalChannelWithPermission(Phone phone, int channel, int cla, int command, int p1, int p2, int p3, String data) { final long identity = Binder.clearCallingIdentity(); try { if (channel <= 0) { return ""; } IccIoResult response = (IccIoResult) sendRequest(CMD_TRANSMIT_APDU_LOGICAL_CHANNEL, new IccAPDUArgument(channel, cla, command, p1, p2, p3, data), phone, null /* workSource */); if (DBG) log("iccTransmitApduLogicalChannelWithPermission: " + response); // Append the returned status code to the end of the response payload. String s = Integer.toHexString( (response.sw1 << 8) + response.sw2 + 0x10000).substring(1); if (response.payload != null) { s = IccUtils.bytesToHexString(response.payload) + s; } return s; } finally { Binder.restoreCallingIdentity(identity); } } @Override public String iccTransmitApduBasicChannel(int subId, String callingPackage, int cla, int command, int p1, int p2, int p3, String data) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "iccTransmitApduBasicChannel"); mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); if (DBG) { log("iccTransmitApduBasicChannel: subId=" + subId + " cla=" + cla + " cmd=" + command + " p1=" + p1 + " p2=" + p2 + " p3=" + p3 + " data=" + data); } return iccTransmitApduBasicChannelWithPermission(getPhoneFromSubId(subId), callingPackage, cla, command, p1, p2, p3, data); } @Override public String iccTransmitApduBasicChannelBySlot(int slotIndex, String callingPackage, int cla, int command, int p1, int p2, int p3, String data) { enforceModifyPermission(); mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); if (DBG) { log("iccTransmitApduBasicChannelBySlot: slotIndex=" + slotIndex + " cla=" + cla + " cmd=" + command + " p1=" + p1 + " p2=" + p2 + " p3=" + p3 + " data=" + data); } return iccTransmitApduBasicChannelWithPermission( getPhoneFromSlotIdOrThrowException(slotIndex), callingPackage, cla, command, p1, p2, p3, data); } // open APDU basic channel assuming the caller has sufficient permissions private String iccTransmitApduBasicChannelWithPermission(Phone phone, String callingPackage, int cla, int command, int p1, int p2, int p3, String data) { final long identity = Binder.clearCallingIdentity(); try { if (command == SELECT_COMMAND && p1 == SELECT_P1 && p2 == SELECT_P2 && p3 == SELECT_P3 && TextUtils.equals(ISDR_AID, data)) { // Only allows LPA to select ISD-R. ComponentInfo bestComponent = EuiccConnector.findBestComponent(getDefaultPhone() .getContext().getPackageManager()); if (bestComponent == null || !TextUtils.equals(callingPackage, bestComponent.packageName)) { loge("The calling package is not allowed to select ISD-R."); throw new SecurityException( "The calling package is not allowed to select ISD-R."); } } IccIoResult response = (IccIoResult) sendRequest(CMD_TRANSMIT_APDU_BASIC_CHANNEL, new IccAPDUArgument(0, cla, command, p1, p2, p3, data), phone, null /* workSource */); if (DBG) log("iccTransmitApduBasicChannelWithPermission: " + response); // Append the returned status code to the end of the response payload. String s = Integer.toHexString( (response.sw1 << 8) + response.sw2 + 0x10000).substring(1); if (response.payload != null) { s = IccUtils.bytesToHexString(response.payload) + s; } return s; } finally { Binder.restoreCallingIdentity(identity); } } @Override public byte[] iccExchangeSimIO(int subId, int fileID, int command, int p1, int p2, int p3, String filePath) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "iccExchangeSimIO"); final long identity = Binder.clearCallingIdentity(); try { if (DBG) { log("Exchange SIM_IO " + subId + ":" + fileID + ":" + command + " " + p1 + " " + p2 + " " + p3 + ":" + filePath); } IccIoResult response = (IccIoResult) sendRequest(CMD_EXCHANGE_SIM_IO, new IccAPDUArgument(-1, fileID, command, p1, p2, p3, filePath), subId); if (DBG) { log("Exchange SIM_IO [R]" + response); } byte[] result = null; int length = 2; if (response.payload != null) { length = 2 + response.payload.length; result = new byte[length]; System.arraycopy(response.payload, 0, result, 0, response.payload.length); } else { result = new byte[length]; } result[length - 1] = (byte) response.sw2; result[length - 2] = (byte) response.sw1; return result; } finally { Binder.restoreCallingIdentity(identity); } } /** * Get the forbidden PLMN List from the given app type (ex APPTYPE_USIM) * on a particular subscription */ public String[] getForbiddenPlmns(int subId, int appType, String callingPackage, String callingFeatureId) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mApp, subId, callingPackage, callingFeatureId, "getForbiddenPlmns")) { return null; } final long identity = Binder.clearCallingIdentity(); try { if (appType != TelephonyManager.APPTYPE_USIM && appType != TelephonyManager.APPTYPE_SIM) { loge("getForbiddenPlmnList(): App Type must be USIM or SIM"); return null; } Object response = sendRequest( CMD_GET_FORBIDDEN_PLMNS, new Integer(appType), subId); if (response instanceof String[]) { return (String[]) response; } // Response is an Exception of some kind // which is signalled to the user as a NULL retval return null; } finally { Binder.restoreCallingIdentity(identity); } } /** * Set the forbidden PLMN list from the given app type (ex APPTYPE_USIM) on a particular * subscription. * * @param subId the id of the subscription. * @param appType the uicc app type, must be USIM or SIM. * @param fplmns the Forbiden plmns list that needed to be written to the SIM. * @param callingPackage the op Package name. * @param callingFeatureId the feature in the package. * @return number of fplmns that is successfully written to the SIM. */ public int setForbiddenPlmns(int subId, int appType, List fplmns, String callingPackage, String callingFeatureId) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "setForbiddenPlmns"); if (appType != TelephonyManager.APPTYPE_USIM && appType != TelephonyManager.APPTYPE_SIM) { loge("setForbiddenPlmnList(): App Type must be USIM or SIM"); throw new IllegalArgumentException("Invalid appType: App Type must be USIM or SIM"); } if (fplmns == null) { throw new IllegalArgumentException("Fplmn List provided is null"); } for (String fplmn : fplmns) { if (!CellIdentity.isValidPlmn(fplmn)) { throw new IllegalArgumentException("Invalid fplmn provided: " + fplmn); } } final long identity = Binder.clearCallingIdentity(); try { Object response = sendRequest( CMD_SET_FORBIDDEN_PLMNS, new Pair>(new Integer(appType), fplmns), subId); return (int) response; } finally { Binder.restoreCallingIdentity(identity); } } @Override public String sendEnvelopeWithStatus(int subId, String content) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "sendEnvelopeWithStatus"); final long identity = Binder.clearCallingIdentity(); try { IccIoResult response = (IccIoResult) sendRequest(CMD_SEND_ENVELOPE, content, subId); if (response.payload == null) { return ""; } // Append the returned status code to the end of the response payload. String s = Integer.toHexString( (response.sw1 << 8) + response.sw2 + 0x10000).substring(1); s = IccUtils.bytesToHexString(response.payload) + s; return s; } finally { Binder.restoreCallingIdentity(identity); } } /** * Read one of the NV items defined in {@link com.android.internal.telephony.RadioNVItems} * and {@code ril_nv_items.h}. Used for device configuration by some CDMA operators. * * @param itemID the ID of the item to read * @return the NV item as a String, or null on error. */ @Override public String nvReadItem(int itemID) { WorkSource workSource = getWorkSource(Binder.getCallingUid()); TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, getDefaultSubscription(), "nvReadItem"); final long identity = Binder.clearCallingIdentity(); try { if (DBG) log("nvReadItem: item " + itemID); String value = (String) sendRequest(CMD_NV_READ_ITEM, itemID, workSource); if (DBG) log("nvReadItem: item " + itemID + " is \"" + value + '"'); return value; } finally { Binder.restoreCallingIdentity(identity); } } /** * Write one of the NV items defined in {@link com.android.internal.telephony.RadioNVItems} * and {@code ril_nv_items.h}. Used for device configuration by some CDMA operators. * * @param itemID the ID of the item to read * @param itemValue the value to write, as a String * @return true on success; false on any failure */ @Override public boolean nvWriteItem(int itemID, String itemValue) { WorkSource workSource = getWorkSource(Binder.getCallingUid()); TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, getDefaultSubscription(), "nvWriteItem"); final long identity = Binder.clearCallingIdentity(); try { if (DBG) log("nvWriteItem: item " + itemID + " value \"" + itemValue + '"'); Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_ITEM, new Pair(itemID, itemValue), workSource); if (DBG) log("nvWriteItem: item " + itemID + ' ' + (success ? "ok" : "fail")); return success; } finally { Binder.restoreCallingIdentity(identity); } } /** * Update the CDMA Preferred Roaming List (PRL) in the radio NV storage. * Used for device configuration by some CDMA operators. * * @param preferredRoamingList byte array containing the new PRL * @return true on success; false on any failure */ @Override public boolean nvWriteCdmaPrl(byte[] preferredRoamingList) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, getDefaultSubscription(), "nvWriteCdmaPrl"); final long identity = Binder.clearCallingIdentity(); try { if (DBG) log("nvWriteCdmaPrl: value: " + HexDump.toHexString(preferredRoamingList)); Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_CDMA_PRL, preferredRoamingList); if (DBG) log("nvWriteCdmaPrl: " + (success ? "ok" : "fail")); return success; } finally { Binder.restoreCallingIdentity(identity); } } /** * Rollback modem configurations to factory default except some config which are in whitelist. * Used for device configuration by some CDMA operators. * * @param slotIndex - device slot. * * @return true on success; false on any failure */ @Override public boolean resetModemConfig(int slotIndex) { Phone phone = PhoneFactory.getPhone(slotIndex); if (phone != null) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, phone.getSubId(), "resetModemConfig"); final long identity = Binder.clearCallingIdentity(); try { Boolean success = (Boolean) sendRequest(CMD_RESET_MODEM_CONFIG, null); if (DBG) log("resetModemConfig:" + ' ' + (success ? "ok" : "fail")); return success; } finally { Binder.restoreCallingIdentity(identity); } } return false; } /** * Generate a radio modem reset. Used for device configuration by some CDMA operators. * * @param slotIndex - device slot. * * @return true on success; false on any failure */ @Override public boolean rebootModem(int slotIndex) { Phone phone = PhoneFactory.getPhone(slotIndex); if (phone != null) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, phone.getSubId(), "rebootModem"); final long identity = Binder.clearCallingIdentity(); try { Boolean success = (Boolean) sendRequest(CMD_MODEM_REBOOT, null); if (DBG) log("rebootModem:" + ' ' + (success ? "ok" : "fail")); return success; } finally { Binder.restoreCallingIdentity(identity); } } return false; } public String[] getPcscfAddress(String apnType, String callingPackage, String callingFeatureId) { final Phone defaultPhone = getDefaultPhone(); if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, defaultPhone.getSubId(), callingPackage, callingFeatureId, "getPcscfAddress")) { return new String[0]; } final long identity = Binder.clearCallingIdentity(); try { return defaultPhone.getPcscfAddress(apnType); } finally { Binder.restoreCallingIdentity(identity); } } /** * Toggle IMS disable and enable for the framework to reset it. See {@link #enableIms(int)} and * {@link #disableIms(int)}. * @param slotIndex device slot. */ public void resetIms(int slotIndex) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { if (mImsResolver == null) { // may happen if the does not support IMS. return; } mImsResolver.disableIms(slotIndex); mImsResolver.enableIms(slotIndex); } finally { Binder.restoreCallingIdentity(identity); } } /** * Enables IMS for the framework. This will trigger IMS registration and ImsFeature capability * status updates, if not already enabled. */ public void enableIms(int slotId) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { if (mImsResolver == null) { // may happen if the device does not support IMS. return; } mImsResolver.enableIms(slotId); } finally { Binder.restoreCallingIdentity(identity); } } /** * Disables IMS for the framework. This will trigger IMS de-registration and trigger ImsFeature * status updates to disabled. */ public void disableIms(int slotId) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { if (mImsResolver == null) { // may happen if the device does not support IMS. return; } mImsResolver.disableIms(slotId); } finally { Binder.restoreCallingIdentity(identity); } } /** * Registers for updates to the MmTelFeature connection through the IImsServiceFeatureCallback * callback. */ @Override public void registerMmTelFeatureCallback(int slotId, IImsServiceFeatureCallback callback) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { if (mImsResolver == null) { throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION, "Device does not support IMS"); } mImsResolver.listenForFeature(slotId, ImsFeature.FEATURE_MMTEL, callback); } finally { Binder.restoreCallingIdentity(identity); } } /** * Unregister a previously registered IImsServiceFeatureCallback associated with an ImsFeature. */ @Override public void unregisterImsFeatureCallback(IImsServiceFeatureCallback callback) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { if (mImsResolver == null) return; mImsResolver.unregisterImsFeatureCallback(callback); } finally { Binder.restoreCallingIdentity(identity); } } /** * Returns the {@link IImsRegistration} structure associated with the slotId and feature * specified or null if IMS is not supported on the slot specified. */ public IImsRegistration getImsRegistration(int slotId, int feature) throws RemoteException { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { if (mImsResolver == null) { // may happen if the device does not support IMS. return null; } return mImsResolver.getImsRegistration(slotId, feature); } finally { Binder.restoreCallingIdentity(identity); } } /** * Returns the {@link IImsConfig} structure associated with the slotId and feature * specified or null if IMS is not supported on the slot specified. */ public IImsConfig getImsConfig(int slotId, int feature) throws RemoteException { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { if (mImsResolver == null) { // may happen if the device does not support IMS. return null; } return mImsResolver.getImsConfig(slotId, feature); } finally { Binder.restoreCallingIdentity(identity); } } /** * Sets the ImsService Package Name that Telephony will bind to. * * @param slotIndex the slot ID that the ImsService should bind for. * @param isCarrierService true if the ImsService is the carrier override, false if the * ImsService is the device default ImsService. * @param featureTypes An integer array of feature types associated with a packageName. * @param packageName The name of the package that the current configuration will be replaced * with. * @return true if setting the ImsService to bind to succeeded, false if it did not. */ public boolean setBoundImsServiceOverride(int slotIndex, boolean isCarrierService, int[] featureTypes, String packageName) { int[] subIds = SubscriptionManager.getSubId(slotIndex); TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setBoundImsServiceOverride"); TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, (subIds != null ? subIds[0] : SubscriptionManager.INVALID_SUBSCRIPTION_ID), "setBoundImsServiceOverride"); final long identity = Binder.clearCallingIdentity(); try { if (mImsResolver == null) { // may happen if the device does not support IMS. return false; } Map featureConfig = new HashMap<>(); for (int featureType : featureTypes) { featureConfig.put(featureType, packageName); } return mImsResolver.overrideImsServiceConfiguration(slotIndex, isCarrierService, featureConfig); } finally { Binder.restoreCallingIdentity(identity); } } /** * Clears any carrier ImsService overrides for the slot index specified that were previously * set with {@link #setBoundImsServiceOverride(int, boolean, int[], String)}. * * This should only be used for testing. * * @param slotIndex the slot ID that the ImsService should bind for. * @return true if clearing the carrier ImsService override succeeded or false if it did not. */ @Override public boolean clearCarrierImsServiceOverride(int slotIndex) { int[] subIds = SubscriptionManager.getSubId(slotIndex); TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "clearCarrierImsServiceOverride"); TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, (subIds != null ? subIds[0] : SubscriptionManager.INVALID_SUBSCRIPTION_ID), "clearCarrierImsServiceOverride"); final long identity = Binder.clearCallingIdentity(); try { if (mImsResolver == null) { // may happen if the device does not support IMS. return false; } return mImsResolver.clearCarrierImsServiceConfiguration(slotIndex); } finally { Binder.restoreCallingIdentity(identity); } } /** * Return the package name of the currently bound ImsService. * * @param slotId The slot that the ImsService is associated with. * @param isCarrierImsService true, if the ImsService is a carrier override, false if it is * the device default. * @param featureType The feature associated with the queried configuration. * @return the package name of the ImsService configuration. */ public String getBoundImsServicePackage(int slotId, boolean isCarrierImsService, @ImsFeature.FeatureType int featureType) { int[] subIds = SubscriptionManager.getSubId(slotId); TelephonyPermissions .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege( mApp, (subIds != null ? subIds[0] : SubscriptionManager.INVALID_SUBSCRIPTION_ID), "getBoundImsServicePackage"); final long identity = Binder.clearCallingIdentity(); try { if (mImsResolver == null) { // may happen if the device does not support IMS. return ""; } // TODO: change API to query RCS separately. return mImsResolver.getImsServiceConfiguration(slotId, isCarrierImsService, featureType); } finally { Binder.restoreCallingIdentity(identity); } } /** * Get the MmTelFeature state associated with the requested subscription id. * @param subId The subscription that the MmTelFeature is associated with. * @param callback A callback with an integer containing the * {@link android.telephony.ims.feature.ImsFeature.ImsState} associated with the MmTelFeature. */ @Override public void getImsMmTelFeatureState(int subId, IIntegerConsumer callback) { enforceReadPrivilegedPermission("getImsMmTelFeatureState"); if (!ImsManager.isImsSupportedOnDevice(mApp)) { throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION, "IMS not available on device."); } final long token = Binder.clearCallingIdentity(); try { int slotId = getSlotIndex(subId); if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) { Log.w(LOG_TAG, "getImsMmTelFeatureState: called with an inactive subscription '" + subId + "'"); throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION); } verifyImsMmTelConfiguredOrThrow(slotId); ImsManager.getInstance(mApp, slotId).getImsServiceState(anInteger -> { try { callback.accept(anInteger == null ? ImsFeature.STATE_UNAVAILABLE : anInteger); } catch (RemoteException e) { Log.w(LOG_TAG, "getImsMmTelFeatureState: remote caller is no longer running. " + "Ignore"); } }); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(token); } } /** * Sets the ims registration state on all valid {@link Phone}s. */ public void setImsRegistrationState(final boolean registered) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { // NOTE: Before S, this method only set the default phone. for (final Phone phone : PhoneFactory.getPhones()) { if (SubscriptionManager.isValidSubscriptionId(phone.getSubId())) { phone.setImsRegistrationState(registered); } } } finally { Binder.restoreCallingIdentity(identity); } } /** * Set the network selection mode to automatic. * */ @Override public void setNetworkSelectionModeAutomatic(int subId) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "setNetworkSelectionModeAutomatic"); final long identity = Binder.clearCallingIdentity(); try { if (!isActiveSubscription(subId)) { return; } if (DBG) log("setNetworkSelectionModeAutomatic: subId " + subId); sendRequest(CMD_SET_NETWORK_SELECTION_MODE_AUTOMATIC, null, subId, SET_NETWORK_SELECTION_MODE_AUTOMATIC_TIMEOUT_MS); } finally { Binder.restoreCallingIdentity(identity); } } /** * Ask the radio to connect to the input network and change selection mode to manual. * * @param subId the id of the subscription. * @param operatorInfo the operator information, included the PLMN, long name and short name of * the operator to attach to. * @param persistSelection whether the selection will persist until reboot. If true, only allows * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume * normal network selection next time. * @return {@code true} on success; {@code true} on any failure. */ @Override public boolean setNetworkSelectionModeManual( int subId, OperatorInfo operatorInfo, boolean persistSelection) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "setNetworkSelectionModeManual"); if (!isActiveSubscription(subId)) { return false; } final long identity = Binder.clearCallingIdentity(); try { ManualNetworkSelectionArgument arg = new ManualNetworkSelectionArgument(operatorInfo, persistSelection); if (DBG) { log("setNetworkSelectionModeManual: subId: " + subId + " operator: " + operatorInfo); } return (Boolean) sendRequest(CMD_SET_NETWORK_SELECTION_MODE_MANUAL, arg, subId); } finally { Binder.restoreCallingIdentity(identity); } } /** * Get the manual network selection * * @param subId the id of the subscription. * * @return the previously saved user selected PLMN */ @Override public String getManualNetworkSelectionPlmn(int subId) { TelephonyPermissions .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege( mApp, subId, "getManualNetworkSelectionPlmn"); final long identity = Binder.clearCallingIdentity(); try { if (!isActiveSubscription(subId)) { throw new IllegalArgumentException("Invalid Subscription Id: " + subId); } final Phone phone = getPhone(subId); if (phone == null) { throw new IllegalArgumentException("Invalid Subscription Id: " + subId); } OperatorInfo networkSelection = phone.getSavedNetworkSelection(); return TextUtils.isEmpty(networkSelection.getOperatorNumeric()) ? phone.getManualNetworkSelectionPlmn() : networkSelection.getOperatorNumeric(); } finally { Binder.restoreCallingIdentity(identity); } } /** * Scans for available networks. */ @Override public CellNetworkScanResult getCellNetworkScanResults(int subId, String callingPackage, String callingFeatureId) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "getCellNetworkScanResults"); LocationAccessPolicy.LocationPermissionResult locationResult = LocationAccessPolicy.checkLocationPermission(mApp, new LocationAccessPolicy.LocationPermissionQuery.Builder() .setCallingPackage(callingPackage) .setCallingFeatureId(callingFeatureId) .setCallingPid(Binder.getCallingPid()) .setCallingUid(Binder.getCallingUid()) .setMethod("getCellNetworkScanResults") .setMinSdkVersionForFine(Build.VERSION_CODES.Q) .setMinSdkVersionForCoarse(Build.VERSION_CODES.Q) .setMinSdkVersionForEnforcement(Build.VERSION_CODES.Q) .build()); switch (locationResult) { case DENIED_HARD: throw new SecurityException("Not allowed to access scan results -- location"); case DENIED_SOFT: return null; } long identity = Binder.clearCallingIdentity(); try { if (DBG) log("getCellNetworkScanResults: subId " + subId); return (CellNetworkScanResult) sendRequest( CMD_PERFORM_NETWORK_SCAN, null, subId); } finally { Binder.restoreCallingIdentity(identity); } } /** * Get the call forwarding info, given the call forwarding reason. */ @Override public void getCallForwarding(int subId, int callForwardingReason, ICallForwardingInfoCallback callback) { enforceReadPrivilegedPermission("getCallForwarding"); long identity = Binder.clearCallingIdentity(); try { if (DBG) { log("getCallForwarding: subId " + subId + " callForwardingReason" + callForwardingReason); } Phone phone = getPhone(subId); if (phone == null) { try { callback.onError( TelephonyManager.CallForwardingInfoCallback.RESULT_ERROR_UNKNOWN); } catch (RemoteException e) { // ignore } return; } Pair argument = Pair.create( callForwardingReason, new TelephonyManager.CallForwardingInfoCallback() { @Override public void onCallForwardingInfoAvailable(CallForwardingInfo info) { try { callback.onCallForwardingInfoAvailable(info); } catch (RemoteException e) { // ignore } } @Override public void onError(int error) { try { callback.onError(error); } catch (RemoteException e) { // ignore } } }); sendRequestAsync(CMD_GET_CALL_FORWARDING, argument, phone, null); } finally { Binder.restoreCallingIdentity(identity); } } /** * Sets the voice call forwarding info including status (enable/disable), call forwarding * reason, the number to forward, and the timeout before the forwarding is attempted. */ @Override public void setCallForwarding(int subId, CallForwardingInfo callForwardingInfo, IIntegerConsumer callback) { enforceModifyPermission(); long identity = Binder.clearCallingIdentity(); try { if (DBG) { log("setCallForwarding: subId " + subId + " callForwardingInfo" + callForwardingInfo); } Phone phone = getPhone(subId); if (phone == null) { try { callback.accept( TelephonyManager.CallForwardingInfoCallback.RESULT_ERROR_UNKNOWN); } catch (RemoteException e) { // ignore } return; } Pair> arguments = Pair.create(callForwardingInfo, FunctionalUtils.ignoreRemoteException(callback::accept)); sendRequestAsync(CMD_SET_CALL_FORWARDING, arguments, phone, null); } finally { Binder.restoreCallingIdentity(identity); } } /** * Get the call waiting status for a subId. */ @Override public void getCallWaitingStatus(int subId, IIntegerConsumer callback) { enforceReadPrivilegedPermission("getCallWaitingStatus"); long identity = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subId); if (phone == null) { try { callback.accept(TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR); } catch (RemoteException e) { // ignore } return; } CarrierConfigManager configManager = new CarrierConfigManager(phone.getContext()); PersistableBundle c = configManager.getConfigForSubId(subId); boolean requireUssd = c.getBoolean( CarrierConfigManager.KEY_USE_CALL_WAITING_USSD_BOOL, false); if (DBG) log("getCallWaitingStatus: subId " + subId); if (requireUssd) { CarrierXmlParser carrierXmlParser = new CarrierXmlParser(phone.getContext(), getSubscriptionCarrierId(subId)); String newUssdCommand = ""; try { newUssdCommand = carrierXmlParser.getFeature( CarrierXmlParser.FEATURE_CALL_WAITING) .makeCommand(CarrierXmlParser.SsEntry.SSAction.QUERY, null); } catch (NullPointerException e) { loge("Failed to generate USSD number" + e); } ResultReceiver wrappedCallback = new CallWaitingUssdResultReceiver( mMainThreadHandler, callback, carrierXmlParser, CarrierXmlParser.SsEntry.SSAction.QUERY); final String ussdCommand = newUssdCommand; Executors.newSingleThreadExecutor().execute(() -> { handleUssdRequest(subId, ussdCommand, wrappedCallback); }); } else { Consumer argument = FunctionalUtils.ignoreRemoteException( callback::accept); sendRequestAsync(CMD_GET_CALL_WAITING, argument, phone, null); } } finally { Binder.restoreCallingIdentity(identity); } } /** * Sets whether call waiting is enabled for a given subId. */ @Override public void setCallWaitingStatus(int subId, boolean enable, IIntegerConsumer callback) { enforceModifyPermission(); long identity = Binder.clearCallingIdentity(); try { if (DBG) log("setCallWaitingStatus: subId " + subId + " enable: " + enable); Phone phone = getPhone(subId); if (phone == null) { try { callback.accept(TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR); } catch (RemoteException e) { // ignore } return; } CarrierConfigManager configManager = new CarrierConfigManager(phone.getContext()); PersistableBundle c = configManager.getConfigForSubId(subId); boolean requireUssd = c.getBoolean( CarrierConfigManager.KEY_USE_CALL_WAITING_USSD_BOOL, false); if (DBG) log("getCallWaitingStatus: subId " + subId); if (requireUssd) { CarrierXmlParser carrierXmlParser = new CarrierXmlParser(phone.getContext(), getSubscriptionCarrierId(subId)); CarrierXmlParser.SsEntry.SSAction ssAction = enable ? CarrierXmlParser.SsEntry.SSAction.UPDATE_ACTIVATE : CarrierXmlParser.SsEntry.SSAction.UPDATE_DEACTIVATE; String newUssdCommand = ""; try { newUssdCommand = carrierXmlParser.getFeature( CarrierXmlParser.FEATURE_CALL_WAITING) .makeCommand(ssAction, null); } catch (NullPointerException e) { loge("Failed to generate USSD number" + e); } ResultReceiver wrappedCallback = new CallWaitingUssdResultReceiver( mMainThreadHandler, callback, carrierXmlParser, ssAction); final String ussdCommand = newUssdCommand; Executors.newSingleThreadExecutor().execute(() -> { handleUssdRequest(subId, ussdCommand, wrappedCallback); }); } else { Pair> arguments = Pair.create(enable, FunctionalUtils.ignoreRemoteException(callback::accept)); sendRequestAsync(CMD_SET_CALL_WAITING, arguments, phone, null); } } finally { Binder.restoreCallingIdentity(identity); } } /** * Starts a new network scan and returns the id of this scan. * * @param subId id of the subscription * @param request contains the radio access networks with bands/channels to scan * @param messenger callback messenger for scan results or errors * @param binder for the purpose of auto clean when the user thread crashes * @return the id of the requested scan which can be used to stop the scan. */ @Override public int requestNetworkScan(int subId, NetworkScanRequest request, Messenger messenger, IBinder binder, String callingPackage, String callingFeatureId) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "requestNetworkScan"); LocationAccessPolicy.LocationPermissionResult locationResult = LocationAccessPolicy.checkLocationPermission(mApp, new LocationAccessPolicy.LocationPermissionQuery.Builder() .setCallingPackage(callingPackage) .setCallingFeatureId(callingFeatureId) .setCallingPid(Binder.getCallingPid()) .setCallingUid(Binder.getCallingUid()) .setMethod("requestNetworkScan") .setMinSdkVersionForFine(Build.VERSION_CODES.Q) .setMinSdkVersionForCoarse(Build.VERSION_CODES.Q) .setMinSdkVersionForEnforcement(Build.VERSION_CODES.Q) .build()); if (locationResult != LocationAccessPolicy.LocationPermissionResult.ALLOWED) { SecurityException e = checkNetworkRequestForSanitizedLocationAccess( request, subId, callingPackage); if (e != null) { if (locationResult == LocationAccessPolicy.LocationPermissionResult.DENIED_HARD) { throw e; } else { loge(e.getMessage()); return TelephonyScanManager.INVALID_SCAN_ID; } } } int callingUid = Binder.getCallingUid(); int callingPid = Binder.getCallingPid(); final long identity = Binder.clearCallingIdentity(); try { return mNetworkScanRequestTracker.startNetworkScan( request, messenger, binder, getPhone(subId), callingUid, callingPid, callingPackage); } finally { Binder.restoreCallingIdentity(identity); } } private SecurityException checkNetworkRequestForSanitizedLocationAccess( NetworkScanRequest request, int subId, String callingPackage) { boolean hasCarrierPriv = checkCarrierPrivilegesForPackage(subId, callingPackage) == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; boolean hasNetworkScanPermission = mApp.checkCallingOrSelfPermission(android.Manifest.permission.NETWORK_SCAN) == PERMISSION_GRANTED; if (!hasCarrierPriv && !hasNetworkScanPermission) { return new SecurityException("permission.NETWORK_SCAN or carrier privileges is needed" + " for network scans without location access."); } if (request.getSpecifiers() != null && request.getSpecifiers().length > 0) { for (RadioAccessSpecifier ras : request.getSpecifiers()) { if (ras.getChannels() != null && ras.getChannels().length > 0) { return new SecurityException("Specific channels must not be" + " scanned without location access."); } } } return null; } /** * Stops an existing network scan with the given scanId. * * @param subId id of the subscription * @param scanId id of the scan that needs to be stopped */ @Override public void stopNetworkScan(int subId, int scanId) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "stopNetworkScan"); int callingUid = Binder.getCallingUid(); final long identity = Binder.clearCallingIdentity(); try { mNetworkScanRequestTracker.stopNetworkScan(scanId, callingUid); } finally { Binder.restoreCallingIdentity(identity); } } /** * Get the allowed network types bitmask. * * @return the allowed network types bitmask, defined in RILConstants.java. */ @Override public int getAllowedNetworkTypesBitmask(int subId) { TelephonyPermissions .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege( mApp, subId, "getAllowedNetworkTypesBitmask"); final long identity = Binder.clearCallingIdentity(); try { if (DBG) log("getAllowedNetworkTypesBitmask"); int[] result = (int[]) sendRequest(CMD_GET_ALLOWED_NETWORK_TYPES_BITMASK, null, subId); int networkTypesBitmask = (result != null ? result[0] : -1); if (DBG) log("getAllowedNetworkTypesBitmask: " + networkTypesBitmask); return networkTypesBitmask; } finally { Binder.restoreCallingIdentity(identity); } } /** * Get the allowed network types for certain reason. * * @param subId the id of the subscription. * @param reason the reason the allowed network type change is taking place * @return the allowed network types. */ @Override public long getAllowedNetworkTypesForReason(int subId, @TelephonyManager.AllowedNetworkTypesReason int reason) { TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege( mApp, subId, "getAllowedNetworkTypesForReason"); final long identity = Binder.clearCallingIdentity(); try { return getPhoneFromSubId(subId).getAllowedNetworkTypes(reason); } finally { Binder.restoreCallingIdentity(identity); } } /** * Enable/Disable E-UTRA-NR Dual Connectivity * @param subId subscription id of the sim card * @param nrDualConnectivityState expected NR dual connectivity state * This can be passed following states *

    *
  1. Enable NR dual connectivity {@link TelephonyManager#NR_DUAL_CONNECTIVITY_ENABLE} *
  2. Disable NR dual connectivity {@link TelephonyManager#NR_DUAL_CONNECTIVITY_DISABLE} *
  3. Disable NR dual connectivity and force secondary cell to be released * {@link TelephonyManager#NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE} *
* @return operation result. */ @Override public int setNrDualConnectivityState(int subId, @TelephonyManager.NrDualConnectivityState int nrDualConnectivityState) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "enableNRDualConnectivity"); if (!isRadioInterfaceCapabilitySupported( TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE)) { return TelephonyManager.ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED; } WorkSource workSource = getWorkSource(Binder.getCallingUid()); final long identity = Binder.clearCallingIdentity(); try { int result = (int) sendRequest(CMD_ENABLE_NR_DUAL_CONNECTIVITY, nrDualConnectivityState, subId, workSource); if (DBG) log("enableNRDualConnectivity result: " + result); return result; } finally { Binder.restoreCallingIdentity(identity); } } /** * Is E-UTRA-NR Dual Connectivity enabled * @return true if dual connectivity is enabled else false */ @Override public boolean isNrDualConnectivityEnabled(int subId) { TelephonyPermissions .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege( mApp, subId, "isNRDualConnectivityEnabled"); if (!isRadioInterfaceCapabilitySupported( TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE)) { return false; } WorkSource workSource = getWorkSource(Binder.getCallingUid()); final long identity = Binder.clearCallingIdentity(); try { boolean isEnabled = (boolean) sendRequest(CMD_IS_NR_DUAL_CONNECTIVITY_ENABLED, null, subId, workSource); if (DBG) log("isNRDualConnectivityEnabled: " + isEnabled); return isEnabled; } finally { Binder.restoreCallingIdentity(identity); } } /** * Set the allowed network types of the device and * provide the reason triggering the allowed network change. * * @param subId the id of the subscription. * @param reason the reason the allowed network type change is taking place * @param allowedNetworkTypes the allowed network types. * @return true on success; false on any failure. */ @Override public boolean setAllowedNetworkTypesForReason(int subId, @TelephonyManager.AllowedNetworkTypesReason int reason, @TelephonyManager.NetworkTypeBitMask long allowedNetworkTypes) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "setAllowedNetworkTypesForReason"); if (!TelephonyManager.isValidAllowedNetworkTypesReason(reason)) { loge("setAllowedNetworkTypesForReason: Invalid allowed network type reason: " + reason); return false; } if (!SubscriptionManager.isUsableSubscriptionId(subId)) { loge("setAllowedNetworkTypesForReason: Invalid subscriptionId:" + subId); return false; } log("setAllowedNetworkTypesForReason: " + reason + " value: " + TelephonyManager.convertNetworkTypeBitmaskToString(allowedNetworkTypes)); if (allowedNetworkTypes == getPhoneFromSubId(subId).getAllowedNetworkTypes(reason)) { log("setAllowedNetworkTypesForReason: " + reason + "does not change value"); return true; } final long identity = Binder.clearCallingIdentity(); try { Boolean success = (Boolean) sendRequest( CMD_SET_ALLOWED_NETWORK_TYPES_FOR_REASON, new Pair(reason, allowedNetworkTypes), subId); if (DBG) log("setAllowedNetworkTypesForReason: " + (success ? "ok" : "fail")); return success; } finally { Binder.restoreCallingIdentity(identity); } } /** * Check whether DUN APN is required for tethering with subId. * * @param subId the id of the subscription to require tethering. * @return {@code true} if DUN APN is required for tethering. * @hide */ @Override public boolean isTetheringApnRequiredForSubscriber(int subId) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); final Phone phone = getPhone(subId); try { if (phone != null) { return phone.hasMatchedTetherApnSetting(); } else { return false; } } finally { Binder.restoreCallingIdentity(identity); } } /** * Enable or disable always reporting signal strength changes from radio. * * @param isEnable {@code true} for enabling; {@code false} for disabling. */ @Override public void setAlwaysReportSignalStrength(int subId, boolean isEnable) { enforceModifyPermission(); enforceSystemCaller(); final long identity = Binder.clearCallingIdentity(); final Phone phone = getPhone(subId); try { if (phone != null) { if (DBG) { log("setAlwaysReportSignalStrength: subId=" + subId + " isEnable=" + isEnable); } phone.setAlwaysReportSignalStrength(isEnable); } else { loge("setAlwaysReportSignalStrength: no phone found for subId=" + subId); } } finally { Binder.restoreCallingIdentity(identity); } } /** * Get the user enabled state of Mobile Data. * * TODO: remove and use isUserDataEnabled. * This can't be removed now because some vendor codes * calls through ITelephony directly while they should * use TelephonyManager. * * @return true on enabled */ @Override public boolean getDataEnabled(int subId) { return isUserDataEnabled(subId); } /** * Get whether mobile data is enabled per user setting. * * There are other factors deciding whether mobile data is actually enabled, but they are * not considered here. See {@link #isDataEnabled(int)} for more details. * * Accepts either ACCESS_NETWORK_STATE, MODIFY_PHONE_STATE or carrier privileges. * * @return {@code true} if data is enabled else {@code false} */ @Override public boolean isUserDataEnabled(int subId) { try { mApp.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE, null); } catch (Exception e) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "isUserDataEnabled"); } final long identity = Binder.clearCallingIdentity(); try { int phoneId = mSubscriptionController.getPhoneId(subId); if (DBG) log("isUserDataEnabled: subId=" + subId + " phoneId=" + phoneId); Phone phone = PhoneFactory.getPhone(phoneId); if (phone != null) { boolean retVal = phone.isUserDataEnabled(); if (DBG) log("isUserDataEnabled: subId=" + subId + " retVal=" + retVal); return retVal; } else { if (DBG) loge("isUserDataEnabled: no phone subId=" + subId + " retVal=false"); return false; } } finally { Binder.restoreCallingIdentity(identity); } } /** * Checks if the device is capable of mobile data by considering whether whether the * user has enabled mobile data, whether the carrier has enabled mobile data, and * whether the network policy allows data connections. * * @return {@code true} if the overall data connection is capable; {@code false} if not. */ @Override public boolean isDataEnabled(int subId) { try { try { mApp.enforceCallingOrSelfPermission( android.Manifest.permission.ACCESS_NETWORK_STATE, null); } catch (Exception e) { mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, "isDataEnabled"); } } catch (Exception e) { enforceReadPrivilegedPermission("isDataEnabled"); } final long identity = Binder.clearCallingIdentity(); try { int phoneId = mSubscriptionController.getPhoneId(subId); if (DBG) log("isDataEnabled: subId=" + subId + " phoneId=" + phoneId); Phone phone = PhoneFactory.getPhone(phoneId); if (phone != null) { boolean retVal = phone.getDataEnabledSettings().isDataEnabled(); if (DBG) log("isDataEnabled: subId=" + subId + " retVal=" + retVal); return retVal; } else { if (DBG) loge("isDataEnabled: no phone subId=" + subId + " retVal=false"); return false; } } finally { Binder.restoreCallingIdentity(identity); } } /** * Check if data is enabled for a specific reason * @param subId Subscription index * @param reason the reason the data enable change is taking place * @return {@code true} if the overall data is enabled; {@code false} if not. */ @Override public boolean isDataEnabledForReason(int subId, @TelephonyManager.DataEnabledReason int reason) { try { mApp.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE, null); } catch (Exception e) { mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, "isDataEnabledForReason"); } final long identity = Binder.clearCallingIdentity(); try { int phoneId = mSubscriptionController.getPhoneId(subId); if (DBG) { log("isDataEnabledForReason: subId=" + subId + " phoneId=" + phoneId + " reason=" + reason); } Phone phone = PhoneFactory.getPhone(phoneId); if (phone != null) { boolean retVal; if (reason == TelephonyManager.DATA_ENABLED_REASON_USER) { retVal = phone.isUserDataEnabled(); } else { retVal = phone.getDataEnabledSettings().isDataEnabledForReason(reason); } if (DBG) log("isDataEnabledForReason: retVal=" + retVal); return retVal; } else { if (DBG) { loge("isDataEnabledForReason: no phone subId=" + subId + " retVal=false"); } return false; } } finally { Binder.restoreCallingIdentity(identity); } } private int getCarrierPrivilegeStatusFromCarrierConfigRules(int privilegeFromSim, int uid, Phone phone) { if (uid == Process.PHONE_UID) { // Skip the check if it's the phone UID (system UID removed in b/184713596) // TODO (b/184954344): Check for system/phone UID at call site instead of here return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; } //load access rules from carrier configs, and check those as well: b/139133814 SubscriptionController subController = SubscriptionController.getInstance(); if (privilegeFromSim == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS || subController == null) return privilegeFromSim; PackageManager pkgMgr = phone.getContext().getPackageManager(); String[] packages = pkgMgr.getPackagesForUid(uid); final long identity = Binder.clearCallingIdentity(); try { int subId = phone.getSubId(); if (mCarrierPrivilegeTestOverrideSubIds.contains(subId)) { // A test override is in place for the privileges for this subId, so don't try to // read the subscription privileges. return privilegeFromSim; } SubscriptionInfo subInfo = subController.getSubscriptionInfo(subId); SubscriptionManager subManager = (SubscriptionManager) phone.getContext().getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); for (String pkg : packages) { if (subManager.canManageSubscription(subInfo, pkg)) { return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; } } return privilegeFromSim; } finally { Binder.restoreCallingIdentity(identity); } } private int getCarrierPrivilegeStatusFromCarrierConfigRules(int privilegeFromSim, Phone phone, String pkgName) { //load access rules from carrier configs, and check those as well: b/139133814 SubscriptionController subController = SubscriptionController.getInstance(); if (privilegeFromSim == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS || subController == null) return privilegeFromSim; final long identity = Binder.clearCallingIdentity(); try { int subId = phone.getSubId(); if (mCarrierPrivilegeTestOverrideSubIds.contains(subId)) { // A test override is in place for the privileges for this subId, so don't try to // read the subscription privileges. return privilegeFromSim; } SubscriptionInfo subInfo = subController.getSubscriptionInfo(subId); SubscriptionManager subManager = (SubscriptionManager) phone.getContext().getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); return subManager.canManageSubscription(subInfo, pkgName) ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS : privilegeFromSim; } finally { Binder.restoreCallingIdentity(identity); } } @Override public int getCarrierPrivilegeStatus(int subId) { final Phone phone = getPhone(subId); if (phone == null) { loge("getCarrierPrivilegeStatus: Invalid subId"); return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS; } UiccCard card = UiccController.getInstance().getUiccCard(phone.getPhoneId()); if (card == null) { loge("getCarrierPrivilegeStatus: No UICC"); return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED; } return getCarrierPrivilegeStatusFromCarrierConfigRules( card.getCarrierPrivilegeStatusForCurrentTransaction( phone.getContext().getPackageManager()), Binder.getCallingUid(), phone); } @Override public int getCarrierPrivilegeStatusForUid(int subId, int uid) { enforceReadPrivilegedPermission("getCarrierPrivilegeStatusForUid"); final Phone phone = getPhone(subId); if (phone == null) { loge("getCarrierPrivilegeStatusForUid: Invalid subId"); return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS; } UiccProfile profile = UiccController.getInstance().getUiccProfileForPhone(phone.getPhoneId()); if (profile == null) { loge("getCarrierPrivilegeStatusForUid: No UICC"); return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED; } return getCarrierPrivilegeStatusFromCarrierConfigRules( profile.getCarrierPrivilegeStatusForUid( phone.getContext().getPackageManager(), uid), uid, phone); } @Override public int checkCarrierPrivilegesForPackage(int subId, String pkgName) { enforceReadPrivilegedPermission("checkCarrierPrivilegesForPackage"); if (TextUtils.isEmpty(pkgName)) { return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS; } int phoneId = SubscriptionManager.getPhoneId(subId); UiccCard card = UiccController.getInstance().getUiccCard(phoneId); if (card == null) { loge("checkCarrierPrivilegesForPackage: No UICC on subId " + subId); return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED; } return getCarrierPrivilegeStatusFromCarrierConfigRules( card.getCarrierPrivilegeStatus(mApp.getPackageManager(), pkgName), getPhone(phoneId), pkgName); } @Override public int checkCarrierPrivilegesForPackageAnyPhone(String pkgName) { // TODO(b/186774706): Remove @RequiresPermission from TelephonyManager API if (TextUtils.isEmpty(pkgName)) return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS; int result = TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED; for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) { UiccCard card = UiccController.getInstance().getUiccCard(i); if (card == null) { // No UICC in that slot. continue; } result = getCarrierPrivilegeStatusFromCarrierConfigRules( card.getCarrierPrivilegeStatus(mApp.getPackageManager(), pkgName), getPhone(i), pkgName); if (result == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { break; } } return result; } @Override public List getCarrierPackageNamesForIntentAndPhone(Intent intent, int phoneId) { enforceReadPrivilegedPermission("getCarrierPackageNamesForIntentAndPhone"); if (!SubscriptionManager.isValidPhoneId(phoneId)) { loge("phoneId " + phoneId + " is not valid."); return null; } UiccCard card = UiccController.getInstance().getUiccCard(phoneId); if (card == null) { loge("getCarrierPackageNamesForIntentAndPhone: No UICC"); return null ; } return card.getCarrierPackageNamesForIntent(mApp.getPackageManager(), intent); } @Override public List getPackagesWithCarrierPrivileges(int phoneId) { enforceReadPrivilegedPermission("getPackagesWithCarrierPrivileges"); PackageManager pm = mApp.getPackageManager(); List privilegedPackages = new ArrayList<>(); List packages = null; UiccCard card = UiccController.getInstance().getUiccCard(phoneId); // has UICC in that slot. if (card != null) { if (card.hasCarrierPrivilegeRules()) { if (packages == null) { // Only check packages in user 0 for now packages = pm.getInstalledPackagesAsUser( PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS | PackageManager.GET_SIGNING_CERTIFICATES, UserHandle.SYSTEM.getIdentifier()); } for (int p = packages.size() - 1; p >= 0; p--) { PackageInfo pkgInfo = packages.get(p); if (pkgInfo != null && pkgInfo.packageName != null && getCarrierPrivilegeStatusFromCarrierConfigRules( card.getCarrierPrivilegeStatus(pkgInfo), getPhone(phoneId), pkgInfo.packageName) == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { privilegedPackages.add(pkgInfo.packageName); } } } } return privilegedPackages; } @Override public List getPackagesWithCarrierPrivilegesForAllPhones() { enforceReadPrivilegedPermission("getPackagesWithCarrierPrivilegesForAllPhones"); final long identity = Binder.clearCallingIdentity(); List privilegedPackages = new ArrayList<>(); try { for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) { privilegedPackages.addAll(getPackagesWithCarrierPrivileges(i)); } } finally { Binder.restoreCallingIdentity(identity); } return privilegedPackages; } private String getIccId(int subId) { final Phone phone = getPhone(subId); UiccCard card = phone == null ? null : phone.getUiccCard(); if (card == null) { return null; } String iccId = card.getIccId(); if (TextUtils.isEmpty(iccId)) { return null; } return iccId; } @Override public void setCallComposerStatus(int subId, int status) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subId); if (phone != null) { Phone defaultPhone = phone.getImsPhone(); if (defaultPhone != null && defaultPhone.getPhoneType() == PHONE_TYPE_IMS) { ImsPhone imsPhone = (ImsPhone) defaultPhone; imsPhone.setCallComposerStatus(status); ImsManager.getInstance(mApp, getSlotIndexOrException(subId)) .updateImsServiceConfig(); } } } catch (ImsException e) { throw new ServiceSpecificException(e.getCode()); } finally { Binder.restoreCallingIdentity(identity); } } @Override public int getCallComposerStatus(int subId) { enforceReadPrivilegedPermission("getCallComposerStatus"); final long identity = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subId); if (phone != null) { Phone defaultPhone = phone.getImsPhone(); if (defaultPhone != null && defaultPhone.getPhoneType() == PHONE_TYPE_IMS) { ImsPhone imsPhone = (ImsPhone) defaultPhone; return imsPhone.getCallComposerStatus(); } } } finally { Binder.restoreCallingIdentity(identity); } return TelephonyManager.CALL_COMPOSER_STATUS_OFF; } @Override public boolean setLine1NumberForDisplayForSubscriber(int subId, String alphaTag, String number) { TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mApp, subId, "setLine1NumberForDisplayForSubscriber"); final long identity = Binder.clearCallingIdentity(); try { final String iccId = getIccId(subId); final Phone phone = getPhone(subId); if (phone == null) { return false; } final String subscriberId = phone.getSubscriberId(); if (DBG_MERGE) { Rlog.d(LOG_TAG, "Setting line number for ICC=" + iccId + ", subscriberId=" + subscriberId + " to " + number); } if (TextUtils.isEmpty(iccId)) { return false; } final SharedPreferences.Editor editor = mTelephonySharedPreferences.edit(); final String alphaTagPrefKey = PREF_CARRIERS_ALPHATAG_PREFIX + iccId; if (alphaTag == null) { editor.remove(alphaTagPrefKey); } else { editor.putString(alphaTagPrefKey, alphaTag); } // Record both the line number and IMSI for this ICCID, since we need to // track all merged IMSIs based on line number final String numberPrefKey = PREF_CARRIERS_NUMBER_PREFIX + iccId; final String subscriberPrefKey = PREF_CARRIERS_SUBSCRIBER_PREFIX + iccId; if (number == null) { editor.remove(numberPrefKey); editor.remove(subscriberPrefKey); } else { editor.putString(numberPrefKey, number); editor.putString(subscriberPrefKey, subscriberId); } editor.commit(); return true; } finally { Binder.restoreCallingIdentity(identity); } } @Override public String getLine1NumberForDisplay(int subId, String callingPackage, String callingFeatureId) { // This is open to apps with WRITE_SMS. if (!TelephonyPermissions.checkCallingOrSelfReadPhoneNumber( mApp, subId, callingPackage, callingFeatureId, "getLine1NumberForDisplay")) { if (DBG_MERGE) log("getLine1NumberForDisplay returning null due to permission"); return null; } final long identity = Binder.clearCallingIdentity(); try { String iccId = getIccId(subId); if (iccId != null) { String numberPrefKey = PREF_CARRIERS_NUMBER_PREFIX + iccId; if (DBG_MERGE) { log("getLine1NumberForDisplay returning " + mTelephonySharedPreferences.getString(numberPrefKey, null)); } return mTelephonySharedPreferences.getString(numberPrefKey, null); } if (DBG_MERGE) log("getLine1NumberForDisplay returning null as iccId is null"); return null; } finally { Binder.restoreCallingIdentity(identity); } } @Override public String getLine1AlphaTagForDisplay(int subId, String callingPackage, String callingFeatureId) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mApp, subId, callingPackage, callingFeatureId, "getLine1AlphaTagForDisplay")) { return null; } final long identity = Binder.clearCallingIdentity(); try { String iccId = getIccId(subId); if (iccId != null) { String alphaTagPrefKey = PREF_CARRIERS_ALPHATAG_PREFIX + iccId; return mTelephonySharedPreferences.getString(alphaTagPrefKey, null); } return null; } finally { Binder.restoreCallingIdentity(identity); } } @Override public String[] getMergedSubscriberIds(int subId, String callingPackage, String callingFeatureId) { // This API isn't public, so no need to provide a valid subscription ID - we're not worried // about carrier-privileged callers not having access. if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mApp, SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage, callingFeatureId, "getMergedSubscriberIds")) { return null; } // Clear calling identity, when calling TelephonyManager, because callerUid must be // the process, where TelephonyManager was instantiated. // Otherwise AppOps check will fail. final long identity = Binder.clearCallingIdentity(); try { final Context context = mApp; final TelephonyManager tele = TelephonyManager.from(context); final SubscriptionManager sub = SubscriptionManager.from(context); // Figure out what subscribers are currently active final ArraySet activeSubscriberIds = new ArraySet<>(); // Only consider subs which match the current subId // This logic can be simplified. See b/131189269 for progress. if (isActiveSubscription(subId)) { activeSubscriberIds.add(tele.getSubscriberId(subId)); } // First pass, find a number override for an active subscriber String mergeNumber = null; final Map prefs = mTelephonySharedPreferences.getAll(); for (String key : prefs.keySet()) { if (key.startsWith(PREF_CARRIERS_SUBSCRIBER_PREFIX)) { final String subscriberId = (String) prefs.get(key); if (activeSubscriberIds.contains(subscriberId)) { final String iccId = key.substring( PREF_CARRIERS_SUBSCRIBER_PREFIX.length()); final String numberKey = PREF_CARRIERS_NUMBER_PREFIX + iccId; mergeNumber = (String) prefs.get(numberKey); if (DBG_MERGE) { Rlog.d(LOG_TAG, "Found line number " + mergeNumber + " for active subscriber " + subscriberId); } if (!TextUtils.isEmpty(mergeNumber)) { break; } } } } // Shortcut when no active merged subscribers if (TextUtils.isEmpty(mergeNumber)) { return null; } // Second pass, find all subscribers under that line override final ArraySet result = new ArraySet<>(); for (String key : prefs.keySet()) { if (key.startsWith(PREF_CARRIERS_NUMBER_PREFIX)) { final String number = (String) prefs.get(key); if (mergeNumber.equals(number)) { final String iccId = key.substring(PREF_CARRIERS_NUMBER_PREFIX.length()); final String subscriberKey = PREF_CARRIERS_SUBSCRIBER_PREFIX + iccId; final String subscriberId = (String) prefs.get(subscriberKey); if (!TextUtils.isEmpty(subscriberId)) { result.add(subscriberId); } } } } final String[] resultArray = result.toArray(new String[result.size()]); Arrays.sort(resultArray); if (DBG_MERGE) { Rlog.d(LOG_TAG, "Found subscribers " + Arrays.toString(resultArray) + " after merge"); } return resultArray; } finally { Binder.restoreCallingIdentity(identity); } } @Override public String[] getMergedImsisFromGroup(int subId, String callingPackage) { enforceReadPrivilegedPermission("getMergedImsisFromGroup"); final long identity = Binder.clearCallingIdentity(); try { final TelephonyManager telephonyManager = mApp.getSystemService( TelephonyManager.class); String subscriberId = telephonyManager.getSubscriberId(subId); if (subscriberId == null) { if (DBG) { log("getMergedImsisFromGroup can't find subscriberId for subId " + subId); } return null; } final SubscriptionInfo info = SubscriptionController.getInstance() .getSubscriptionInfo(subId); final ParcelUuid groupUuid = info.getGroupUuid(); // If it doesn't belong to any group, return just subscriberId of itself. if (groupUuid == null) { return new String[]{subscriberId}; } // Get all subscriberIds from the group. final List mergedSubscriberIds = new ArrayList<>(); final List groupInfos = SubscriptionController.getInstance() .getSubscriptionsInGroup(groupUuid, mApp.getOpPackageName(), mApp.getAttributionTag()); for (SubscriptionInfo subInfo : groupInfos) { subscriberId = telephonyManager.getSubscriberId(subInfo.getSubscriptionId()); if (subscriberId != null) { mergedSubscriberIds.add(subscriberId); } } return mergedSubscriberIds.toArray(new String[mergedSubscriberIds.size()]); } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean setOperatorBrandOverride(int subId, String brand) { TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mApp, subId, "setOperatorBrandOverride"); final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); return phone == null ? false : phone.setOperatorBrandOverride(brand); } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean setRoamingOverride(int subId, List gsmRoamingList, List gsmNonRoamingList, List cdmaRoamingList, List cdmaNonRoamingList) { TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege( mApp, subId, "setRoamingOverride"); final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone == null) { return false; } return phone.setRoamingOverride(gsmRoamingList, gsmNonRoamingList, cdmaRoamingList, cdmaNonRoamingList); } finally { Binder.restoreCallingIdentity(identity); } } @Override @Deprecated public int invokeOemRilRequestRaw(byte[] oemReq, byte[] oemResp) { enforceModifyPermission(); int returnValue = 0; try { AsyncResult result = (AsyncResult) sendRequest(CMD_INVOKE_OEM_RIL_REQUEST_RAW, oemReq); if(result.exception == null) { if (result.result != null) { byte[] responseData = (byte[])(result.result); if(responseData.length > oemResp.length) { Log.w(LOG_TAG, "Buffer to copy response too small: Response length is " + responseData.length + "bytes. Buffer Size is " + oemResp.length + "bytes."); } System.arraycopy(responseData, 0, oemResp, 0, responseData.length); returnValue = responseData.length; } } else { CommandException ex = (CommandException) result.exception; returnValue = ex.getCommandError().ordinal(); if(returnValue > 0) returnValue *= -1; } } catch (RuntimeException e) { Log.w(LOG_TAG, "sendOemRilRequestRaw: Runtime Exception"); returnValue = (CommandException.Error.GENERIC_FAILURE.ordinal()); if(returnValue > 0) returnValue *= -1; } return returnValue; } @Override public int getRadioAccessFamily(int phoneId, String callingPackage) { Phone phone = PhoneFactory.getPhone(phoneId); try { TelephonyPermissions .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege( mApp, phone.getSubId(), "getRadioAccessFamily"); } catch (SecurityException e) { EventLog.writeEvent(0x534e4554, "150857259", -1, "Missing Permission"); throw e; } int raf = RadioAccessFamily.RAF_UNKNOWN; if (phone == null) { return raf; } final long identity = Binder.clearCallingIdentity(); try { TelephonyPermissions .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege( mApp, phone.getSubId(), "getRadioAccessFamily"); raf = ProxyController.getInstance().getRadioAccessFamily(phoneId); } finally { Binder.restoreCallingIdentity(identity); } return raf; } @Override public void uploadCallComposerPicture(int subscriptionId, String callingPackage, String contentType, ParcelFileDescriptor fd, ResultReceiver callback) { try { if (!Objects.equals(mApp.getPackageManager().getPackageUid(callingPackage, 0), Binder.getCallingUid())) { throw new SecurityException("Invalid package:" + callingPackage); } } catch (PackageManager.NameNotFoundException e) { throw new SecurityException("Invalid package:" + callingPackage); } RoleManager rm = mApp.getSystemService(RoleManager.class); List dialerRoleHolders = rm.getRoleHolders(RoleManager.ROLE_DIALER); if (!dialerRoleHolders.contains(callingPackage)) { throw new SecurityException("App must be the dialer role holder to" + " upload a call composer pic"); } Executors.newSingleThreadExecutor().execute(() -> { ByteArrayOutputStream output = new ByteArrayOutputStream( (int) TelephonyManager.getMaximumCallComposerPictureSize()); InputStream input = new ParcelFileDescriptor.AutoCloseInputStream(fd); boolean readUntilEnd = false; int totalBytesRead = 0; byte[] buffer = new byte[16 * 1024]; while (true) { int numRead; try { numRead = input.read(buffer); } catch (IOException e) { try { fd.checkError(); callback.send(TelephonyManager.CallComposerException.ERROR_INPUT_CLOSED, null); } catch (IOException e1) { // This means that the other side closed explicitly with an error. If this // happens, log and ignore. loge("Remote end of call composer picture pipe closed: " + e1); } break; } if (numRead == -1) { readUntilEnd = true; break; } totalBytesRead += numRead; if (totalBytesRead > TelephonyManager.getMaximumCallComposerPictureSize()) { loge("Too many bytes read for call composer picture: " + totalBytesRead); try { input.close(); } catch (IOException e) { // ignore } break; } output.write(buffer, 0, numRead); } // Generally, the remote end will close the file descriptors. The only case where we // close is above, where the picture size is too big. try { fd.checkError(); } catch (IOException e) { loge("Remote end for call composer closed with an error: " + e); return; } if (!readUntilEnd) { loge("Did not finish reading entire image; aborting"); return; } ImageData imageData = new ImageData(output.toByteArray(), contentType, null); CallComposerPictureManager.getInstance(mApp, subscriptionId).handleUploadToServer( new CallComposerPictureTransfer.Factory() {}, imageData, (result) -> { if (result.first != null) { ParcelUuid parcelUuid = new ParcelUuid(result.first); Bundle outputResult = new Bundle(); outputResult.putParcelable( TelephonyManager.KEY_CALL_COMPOSER_PICTURE_HANDLE, parcelUuid); callback.send(TelephonyManager.CallComposerException.SUCCESS, outputResult); } else { callback.send(result.second, null); } } ); }); } @Override public void enableVideoCalling(boolean enable) { final Phone defaultPhone = getDefaultPhone(); enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { ImsManager.getInstance(defaultPhone.getContext(), defaultPhone.getPhoneId()).setVtSetting(enable); } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean isVideoCallingEnabled(String callingPackage, String callingFeatureId) { final Phone defaultPhone = getDefaultPhone(); if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, defaultPhone.getSubId(), callingPackage, callingFeatureId, "isVideoCallingEnabled")) { return false; } final long identity = Binder.clearCallingIdentity(); try { // Check the user preference and the system-level IMS setting. Even if the user has // enabled video calling, if IMS is disabled we aren't able to support video calling. // In the long run, we may instead need to check if there exists a connection service // which can support video calling. ImsManager imsManager = ImsManager.getInstance(defaultPhone.getContext(), defaultPhone.getPhoneId()); return imsManager.isVtEnabledByPlatform() && imsManager.isEnhanced4gLteModeSettingEnabledByUser() && imsManager.isVtEnabledByUser(); } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean canChangeDtmfToneLength(int subId, String callingPackage, String callingFeatureId) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mApp, subId, callingPackage, callingFeatureId, "isVideoCallingEnabled")) { return false; } final long identity = Binder.clearCallingIdentity(); try { CarrierConfigManager configManager = (CarrierConfigManager) mApp.getSystemService(Context.CARRIER_CONFIG_SERVICE); return configManager.getConfigForSubId(subId) .getBoolean(CarrierConfigManager.KEY_DTMF_TYPE_ENABLED_BOOL); } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean isWorldPhone(int subId, String callingPackage, String callingFeatureId) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mApp, subId, callingPackage, callingFeatureId, "isVideoCallingEnabled")) { return false; } final long identity = Binder.clearCallingIdentity(); try { CarrierConfigManager configManager = (CarrierConfigManager) mApp.getSystemService(Context.CARRIER_CONFIG_SERVICE); return configManager.getConfigForSubId(subId) .getBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL); } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean isTtyModeSupported() { TelecomManager telecomManager = mApp.getSystemService(TelecomManager.class); return telecomManager.isTtySupported(); } @Override public boolean isHearingAidCompatibilitySupported() { final long identity = Binder.clearCallingIdentity(); try { return mApp.getResources().getBoolean(R.bool.hac_enabled); } finally { Binder.restoreCallingIdentity(identity); } } /** * Determines whether the device currently supports RTT (Real-time text). Based both on carrier * support for the feature and device firmware support. * * @return {@code true} if the device and carrier both support RTT, {@code false} otherwise. */ @Override public boolean isRttSupported(int subscriptionId) { final long identity = Binder.clearCallingIdentity(); final Phone phone = getPhone(subscriptionId); if (phone == null) { loge("isRttSupported: no Phone found. Invalid subId:" + subscriptionId); return false; } try { boolean isCarrierSupported = mApp.getCarrierConfigForSubId(subscriptionId).getBoolean( CarrierConfigManager.KEY_RTT_SUPPORTED_BOOL); boolean isDeviceSupported = phone.getContext().getResources().getBoolean(R.bool.config_support_rtt); return isCarrierSupported && isDeviceSupported; } finally { Binder.restoreCallingIdentity(identity); } } /** * Determines whether the user has turned on RTT. If the carrier wants to ignore the user-set * RTT setting, will return true if the device and carrier both support RTT. * Otherwise. only returns true if the device and carrier both also support RTT. */ public boolean isRttEnabled(int subscriptionId) { final long identity = Binder.clearCallingIdentity(); try { boolean isRttSupported = isRttSupported(subscriptionId); boolean isUserRttSettingOn = Settings.Secure.getInt( mApp.getContentResolver(), Settings.Secure.RTT_CALLING_MODE, 0) != 0; boolean shouldIgnoreUserRttSetting = mApp.getCarrierConfigForSubId(subscriptionId) .getBoolean(CarrierConfigManager.KEY_IGNORE_RTT_MODE_SETTING_BOOL); return isRttSupported && (isUserRttSettingOn || shouldIgnoreUserRttSetting); } finally { Binder.restoreCallingIdentity(identity); } } @Deprecated @Override public String getDeviceId(String callingPackage) { return getDeviceIdWithFeature(callingPackage, null); } /** * Returns the unique device ID of phone, for example, the IMEI for * GSM and the MEID for CDMA phones. Return null if device ID is not available. * *

Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} */ @Override public String getDeviceIdWithFeature(String callingPackage, String callingFeatureId) { try { mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); } catch (SecurityException se) { EventLog.writeEvent(0x534e4554, "186530889", Binder.getCallingUid()); throw new SecurityException("Package " + callingPackage + " does not belong to " + Binder.getCallingUid()); } final Phone phone = PhoneFactory.getPhone(0); if (phone == null) { return null; } int subId = phone.getSubId(); if (!TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mApp, subId, callingPackage, callingFeatureId, "getDeviceId")) { return null; } final long identity = Binder.clearCallingIdentity(); try { return phone.getDeviceId(); } finally { Binder.restoreCallingIdentity(identity); } } /** * {@hide} * Returns the IMS Registration Status on a particular subid * * @param subId */ public boolean isImsRegistered(int subId) { Phone phone = getPhone(subId); if (phone != null) { return phone.isImsRegistered(); } else { return false; } } @Override public int getSubIdForPhoneAccount(PhoneAccount phoneAccount) { final long identity = Binder.clearCallingIdentity(); try { return PhoneUtils.getSubIdForPhoneAccount(phoneAccount); } finally { Binder.restoreCallingIdentity(identity); } } @Override public int getSubIdForPhoneAccountHandle( PhoneAccountHandle phoneAccountHandle, String callingPackage, String callingFeatureId) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, getDefaultSubscription(), callingPackage, callingFeatureId, "getSubIdForPhoneAccountHandle")) { throw new SecurityException("Requires READ_PHONE_STATE permission."); } final long identity = Binder.clearCallingIdentity(); try { return PhoneUtils.getSubIdForPhoneAccountHandle(phoneAccountHandle); } finally { Binder.restoreCallingIdentity(identity); } } @Override public @Nullable PhoneAccountHandle getPhoneAccountHandleForSubscriptionId(int subscriptionId) { TelephonyPermissions .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege( mApp, subscriptionId, "getPhoneAccountHandleForSubscriptionId, " + "subscriptionId: " + subscriptionId); final long identity = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subscriptionId); if (phone == null) { return null; } return PhoneUtils.makePstnPhoneAccountHandle(phone); } finally { Binder.restoreCallingIdentity(identity); } } /** * @return the VoWiFi calling availability. */ public boolean isWifiCallingAvailable(int subId) { final long identity = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subId); if (phone != null) { return phone.isWifiCallingEnabled(); } else { return false; } } finally { Binder.restoreCallingIdentity(identity); } } /** * @return the VT calling availability. */ public boolean isVideoTelephonyAvailable(int subId) { final long identity = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subId); if (phone != null) { return phone.isVideoEnabled(); } else { return false; } } finally { Binder.restoreCallingIdentity(identity); } } /** * @return the IMS registration technology for the MMTEL feature. Valid return values are * defined in {@link ImsRegistrationImplBase}. */ public @ImsRegistrationImplBase.ImsRegistrationTech int getImsRegTechnologyForMmTel(int subId) { final long identity = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subId); if (phone != null) { return phone.getImsRegistrationTech(); } else { return ImsRegistrationImplBase.REGISTRATION_TECH_NONE; } } finally { Binder.restoreCallingIdentity(identity); } } @Override public void factoryReset(int subId) { enforceSettingsPermission(); if (mUserManager.hasUserRestriction(UserManager.DISALLOW_NETWORK_RESET)) { return; } Phone defaultPhone = getDefaultPhone(); if (defaultPhone != null) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, getDefaultPhone().getSubId(), "factoryReset"); } final long identity = Binder.clearCallingIdentity(); try { if (SubscriptionManager.isUsableSubIdValue(subId) && !mUserManager.hasUserRestriction( UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) { setDataEnabledForReason(subId, TelephonyManager.DATA_ENABLED_REASON_USER, getDefaultDataEnabled()); setNetworkSelectionModeAutomatic(subId); Phone phone = getPhone(subId); cleanUpAllowedNetworkTypes(phone, subId); setDataRoamingEnabled(subId, getDefaultDataRoamingEnabled(subId)); getPhone(subId).resetCarrierKeysForImsiEncryption(); } // There has been issues when Sms raw table somehow stores orphan // fragments. They lead to garbled message when new fragments come // in and combined with those stale ones. In case this happens again, // user can reset all network settings which will clean up this table. cleanUpSmsRawTable(getDefaultPhone().getContext()); // Clean up IMS settings as well here. int slotId = getSlotIndex(subId); if (slotId > SubscriptionManager.INVALID_SIM_SLOT_INDEX) { ImsManager.getInstance(mApp, slotId).factoryReset(); } if (defaultPhone == null) { return; } // Erase modem config if erase modem on network setting is enabled. String configValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TELEPHONY, RESET_NETWORK_ERASE_MODEM_CONFIG_ENABLED); if (configValue != null && Boolean.parseBoolean(configValue)) { sendEraseModemConfig(defaultPhone); } sendEraseDataInSharedPreferences(defaultPhone); } finally { Binder.restoreCallingIdentity(identity); } } @VisibleForTesting void cleanUpAllowedNetworkTypes(Phone phone, int subId) { if (phone == null || !SubscriptionManager.isUsableSubscriptionId(subId)) { return; } long defaultNetworkType = RadioAccessFamily.getRafFromNetworkType( RILConstants.PREFERRED_NETWORK_MODE); SubscriptionManager.setSubscriptionProperty(subId, SubscriptionManager.ALLOWED_NETWORK_TYPES, "user=" + defaultNetworkType); phone.loadAllowedNetworksFromSubscriptionDatabase(); phone.setAllowedNetworkTypes(TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER, defaultNetworkType, null); } private void cleanUpSmsRawTable(Context context) { ContentResolver resolver = context.getContentResolver(); Uri uri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw/permanentDelete"); resolver.delete(uri, null, null); } @Override public String getSimLocaleForSubscriber(int subId) { enforceReadPrivilegedPermission("getSimLocaleForSubscriber, subId: " + subId); final Phone phone = getPhone(subId); if (phone == null) { log("getSimLocaleForSubscriber, invalid subId"); return null; } final long identity = Binder.clearCallingIdentity(); try { final SubscriptionInfo info = mSubscriptionController.getActiveSubscriptionInfo(subId, phone.getContext().getOpPackageName(), phone.getContext().getAttributionTag()); if (info == null) { log("getSimLocaleForSubscriber, inactive subId: " + subId); return null; } // Try and fetch the locale from the carrier properties or from the SIM language // preferences (EF-PL and EF-LI)... final int mcc = info.getMcc(); String simLanguage = null; final Locale localeFromDefaultSim = phone.getLocaleFromSimAndCarrierPrefs(); if (localeFromDefaultSim != null) { if (!localeFromDefaultSim.getCountry().isEmpty()) { if (DBG) log("Using locale from subId: " + subId + " locale: " + localeFromDefaultSim); return localeFromDefaultSim.toLanguageTag(); } else { simLanguage = localeFromDefaultSim.getLanguage(); } } // The SIM language preferences only store a language (e.g. fr = French), not an // exact locale (e.g. fr_FR = French/France). So, if the locale returned from // the SIM and carrier preferences does not include a country we add the country // determined from the SIM MCC to provide an exact locale. final Locale mccLocale = LocaleUtils.getLocaleFromMcc(mApp, mcc, simLanguage); if (mccLocale != null) { if (DBG) log("No locale from SIM, using mcc locale:" + mccLocale); return mccLocale.toLanguageTag(); } if (DBG) log("No locale found - returning null"); return null; } finally { Binder.restoreCallingIdentity(identity); } } private List getAllSubscriptionInfoList() { return mSubscriptionController.getAllSubInfoList(mApp.getOpPackageName(), mApp.getAttributionTag()); } /** * NOTE: this method assumes permission checks are done and caller identity has been cleared. */ private List getActiveSubscriptionInfoListPrivileged() { return mSubscriptionController.getActiveSubscriptionInfoList(mApp.getOpPackageName(), mApp.getAttributionTag()); } private final ModemActivityInfo mLastModemActivityInfo = new ModemActivityInfo(0, 0, 0, new int[ModemActivityInfo.getNumTxPowerLevels()], 0); /** * Responds to the ResultReceiver with the {@link android.telephony.ModemActivityInfo} object * representing the state of the modem. * * NOTE: The underlying implementation clears the modem state, so there should only ever be one * caller to it. Everyone should call this class to get cumulative data. * @hide */ @Override public void requestModemActivityInfo(ResultReceiver result) { enforceModifyPermission(); WorkSource workSource = getWorkSource(Binder.getCallingUid()); final long identity = Binder.clearCallingIdentity(); try { sendRequestAsync(CMD_GET_MODEM_ACTIVITY_INFO, result, null, workSource); } finally { Binder.restoreCallingIdentity(identity); } } // Checks that ModemActivityInfo is valid. Sleep time, Idle time, Rx time and Tx time should be // less than total activity duration. private boolean isModemActivityInfoValid(ModemActivityInfo info) { if (info == null) { return false; } int activityDurationMs = (int) (info.getTimestampMillis() - mLastModemActivityInfo.getTimestampMillis()); int totalTxTimeMs = Arrays.stream(info.getTransmitTimeMillis()).sum(); return (info.isValid() && (info.getSleepTimeMillis() <= activityDurationMs) && (info.getIdleTimeMillis() <= activityDurationMs) && (info.getReceiveTimeMillis() <= activityDurationMs) && (totalTxTimeMs <= activityDurationMs)); } /** * {@hide} * Returns the service state information on specified subscription. */ @Override public ServiceState getServiceStateForSubscriber(int subId, String callingPackage, String callingFeatureId) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mApp, subId, callingPackage, callingFeatureId, "getServiceStateForSubscriber")) { return null; } LocationAccessPolicy.LocationPermissionResult fineLocationResult = LocationAccessPolicy.checkLocationPermission(mApp, new LocationAccessPolicy.LocationPermissionQuery.Builder() .setCallingPackage(callingPackage) .setCallingFeatureId(callingFeatureId) .setCallingPid(Binder.getCallingPid()) .setCallingUid(Binder.getCallingUid()) .setMethod("getServiceStateForSubscriber") .setLogAsInfo(true) .setMinSdkVersionForFine(Build.VERSION_CODES.Q) .setMinSdkVersionForCoarse(Build.VERSION_CODES.Q) .setMinSdkVersionForEnforcement(Build.VERSION_CODES.Q) .build()); LocationAccessPolicy.LocationPermissionResult coarseLocationResult = LocationAccessPolicy.checkLocationPermission(mApp, new LocationAccessPolicy.LocationPermissionQuery.Builder() .setCallingPackage(callingPackage) .setCallingFeatureId(callingFeatureId) .setCallingPid(Binder.getCallingPid()) .setCallingUid(Binder.getCallingUid()) .setMethod("getServiceStateForSubscriber") .setLogAsInfo(true) .setMinSdkVersionForCoarse(Build.VERSION_CODES.Q) .setMinSdkVersionForFine(Integer.MAX_VALUE) .setMinSdkVersionForEnforcement(Build.VERSION_CODES.Q) .build()); // We don't care about hard or soft here -- all we need to know is how much info to scrub. boolean hasFinePermission = fineLocationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED; boolean hasCoarsePermission = coarseLocationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED; final Phone phone = getPhone(subId); if (phone == null) { return null; } final long identity = Binder.clearCallingIdentity(); boolean isCallingPackageDataService = phone.getDataServicePackages() .contains(callingPackage); try { // isActiveSubId requires READ_PHONE_STATE, which we already check for above if (!mSubscriptionController.isActiveSubId(subId, callingPackage, callingFeatureId)) { Rlog.d(LOG_TAG, "getServiceStateForSubscriber returning null for inactive subId=" + subId); return null; } ServiceState ss = phone.getServiceState(); // Scrub out the location info in ServiceState depending on what level of access // the caller has. if (hasFinePermission || isCallingPackageDataService) return ss; if (hasCoarsePermission) return ss.createLocationInfoSanitizedCopy(false); return ss.createLocationInfoSanitizedCopy(true); } finally { Binder.restoreCallingIdentity(identity); } } /** * Returns the URI for the per-account voicemail ringtone set in Phone settings. * * @param accountHandle The handle for the {@link PhoneAccount} for which to retrieve the * voicemail ringtone. * @return The URI for the ringtone to play when receiving a voicemail from a specific * PhoneAccount. */ @Override public Uri getVoicemailRingtoneUri(PhoneAccountHandle accountHandle) { final long identity = Binder.clearCallingIdentity(); try { Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(accountHandle); if (phone == null) { phone = getDefaultPhone(); } return VoicemailNotificationSettingsUtil.getRingtoneUri(phone.getContext()); } finally { Binder.restoreCallingIdentity(identity); } } /** * Sets the per-account voicemail ringtone. * *

Requires that the calling app is the default dialer, or has carrier privileges, or * has permission {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. * * @param phoneAccountHandle The handle for the {@link PhoneAccount} for which to set the * voicemail ringtone. * @param uri The URI for the ringtone to play when receiving a voicemail from a specific * PhoneAccount. */ @Override public void setVoicemailRingtoneUri(String callingPackage, PhoneAccountHandle phoneAccountHandle, Uri uri) { final Phone defaultPhone = getDefaultPhone(); mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); TelecomManager tm = defaultPhone.getContext().getSystemService(TelecomManager.class); if (!TextUtils.equals(callingPackage, tm.getDefaultDialerPackage())) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, PhoneUtils.getSubIdForPhoneAccountHandle(phoneAccountHandle), "setVoicemailRingtoneUri"); } final long identity = Binder.clearCallingIdentity(); try { Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(phoneAccountHandle); if (phone == null) { phone = defaultPhone; } VoicemailNotificationSettingsUtil.setRingtoneUri(phone.getContext(), uri); } finally { Binder.restoreCallingIdentity(identity); } } /** * Returns whether vibration is set for voicemail notification in Phone settings. * * @param accountHandle The handle for the {@link PhoneAccount} for which to retrieve the * voicemail vibration setting. * @return {@code true} if the vibration is set for this PhoneAccount, {@code false} otherwise. */ @Override public boolean isVoicemailVibrationEnabled(PhoneAccountHandle accountHandle) { final long identity = Binder.clearCallingIdentity(); try { Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(accountHandle); if (phone == null) { phone = getDefaultPhone(); } return VoicemailNotificationSettingsUtil.isVibrationEnabled(phone.getContext()); } finally { Binder.restoreCallingIdentity(identity); } } /** * Sets the per-account voicemail vibration. * *

Requires that the calling app is the default dialer, or has carrier privileges, or * has permission {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. * * @param phoneAccountHandle The handle for the {@link PhoneAccount} for which to set the * voicemail vibration setting. * @param enabled Whether to enable or disable vibration for voicemail notifications from a * specific PhoneAccount. */ @Override public void setVoicemailVibrationEnabled(String callingPackage, PhoneAccountHandle phoneAccountHandle, boolean enabled) { final Phone defaultPhone = getDefaultPhone(); mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); TelecomManager tm = defaultPhone.getContext().getSystemService(TelecomManager.class); if (!TextUtils.equals(callingPackage, tm.getDefaultDialerPackage())) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, PhoneUtils.getSubIdForPhoneAccountHandle(phoneAccountHandle), "setVoicemailVibrationEnabled"); } final long identity = Binder.clearCallingIdentity(); try { Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(phoneAccountHandle); if (phone == null) { phone = defaultPhone; } VoicemailNotificationSettingsUtil.setVibrationEnabled(phone.getContext(), enabled); } finally { Binder.restoreCallingIdentity(identity); } } /** * Make sure either called from same process as self (phone) or IPC caller has read privilege. * * @throws SecurityException if the caller does not have the required permission */ private void enforceReadPrivilegedPermission(String message) { mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message); } /** * Make sure either called from same process as self (phone) or IPC caller has send SMS * permission. * * @throws SecurityException if the caller does not have the required permission */ private void enforceSendSmsPermission() { mApp.enforceCallingOrSelfPermission(permission.SEND_SMS, null); } /** * Make sure called from the package in charge of visual voicemail. * * @throws SecurityException if the caller is not the visual voicemail package. */ private void enforceVisualVoicemailPackage(String callingPackage, int subId) { final long identity = Binder.clearCallingIdentity(); try { ComponentName componentName = RemoteVvmTaskManager.getRemotePackage(mApp, subId); if (componentName == null) { throw new SecurityException( "Caller not current active visual voicemail package[null]"); } String vvmPackage = componentName.getPackageName(); if (!callingPackage.equals(vvmPackage)) { throw new SecurityException("Caller not current active visual voicemail package[" + vvmPackage + "]"); } } finally { Binder.restoreCallingIdentity(identity); } } /** * Return the application ID for the app type. * * @param subId the subscription ID that this request applies to. * @param appType the uicc app type. * @return Application ID for specificied app type, or null if no uicc. */ @Override public String getAidForAppType(int subId, int appType) { enforceReadPrivilegedPermission("getAidForAppType"); Phone phone = getPhone(subId); final long identity = Binder.clearCallingIdentity(); try { if (phone == null) { return null; } String aid = null; try { aid = UiccController.getInstance().getUiccCard(phone.getPhoneId()) .getApplicationByType(appType).getAid(); } catch (Exception e) { Log.e(LOG_TAG, "Not getting aid. Exception ex=" + e); } return aid; } finally { Binder.restoreCallingIdentity(identity); } } /** * Return the Electronic Serial Number. * * @param subId the subscription ID that this request applies to. * @return ESN or null if error. */ @Override public String getEsn(int subId) { enforceReadPrivilegedPermission("getEsn"); Phone phone = getPhone(subId); final long identity = Binder.clearCallingIdentity(); try { if (phone == null) { return null; } String esn = null; try { esn = phone.getEsn(); } catch (Exception e) { Log.e(LOG_TAG, "Not getting ESN. Exception ex=" + e); } return esn; } finally { Binder.restoreCallingIdentity(identity); } } /** * Return the Preferred Roaming List Version. * * @param subId the subscription ID that this request applies to. * @return PRLVersion or null if error. */ @Override public String getCdmaPrlVersion(int subId) { enforceReadPrivilegedPermission("getCdmaPrlVersion"); Phone phone = getPhone(subId); final long identity = Binder.clearCallingIdentity(); try { if (phone == null) { return null; } String cdmaPrlVersion = null; try { cdmaPrlVersion = phone.getCdmaPrlVersion(); } catch (Exception e) { Log.e(LOG_TAG, "Not getting PRLVersion", e); } return cdmaPrlVersion; } finally { Binder.restoreCallingIdentity(identity); } } /** * Get snapshot of Telephony histograms * @return List of Telephony histograms * @hide */ @Override public List getTelephonyHistograms() { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, getDefaultSubscription(), "getTelephonyHistograms"); final long identity = Binder.clearCallingIdentity(); try { return RIL.getTelephonyRILTimingHistograms(); } finally { Binder.restoreCallingIdentity(identity); } } /** * {@hide} * Set the allowed carrier list and the excluded carrier list, indicating the priority between * the two lists. * Require system privileges. In the future we may add this to carrier APIs. * * @return Integer with the result of the operation, as defined in {@link TelephonyManager}. */ @Override @TelephonyManager.SetCarrierRestrictionResult public int setAllowedCarriers(CarrierRestrictionRules carrierRestrictionRules) { enforceModifyPermission(); WorkSource workSource = getWorkSource(Binder.getCallingUid()); if (carrierRestrictionRules == null) { throw new NullPointerException("carrier restriction cannot be null"); } final long identity = Binder.clearCallingIdentity(); try { return (int) sendRequest(CMD_SET_ALLOWED_CARRIERS, carrierRestrictionRules, workSource); } finally { Binder.restoreCallingIdentity(identity); } } /** * {@hide} * Get the allowed carrier list and the excluded carrier list, including the priority between * the two lists. * Require system privileges. In the future we may add this to carrier APIs. * * @return {@link android.telephony.CarrierRestrictionRules} */ @Override public CarrierRestrictionRules getAllowedCarriers() { enforceReadPrivilegedPermission("getAllowedCarriers"); WorkSource workSource = getWorkSource(Binder.getCallingUid()); final long identity = Binder.clearCallingIdentity(); try { Object response = sendRequest(CMD_GET_ALLOWED_CARRIERS, null, workSource); if (response instanceof CarrierRestrictionRules) { return (CarrierRestrictionRules) response; } // Response is an Exception of some kind, // which is signalled to the user as a NULL retval return null; } catch (Exception e) { Log.e(LOG_TAG, "getAllowedCarriers. Exception ex=" + e); return null; } finally { Binder.restoreCallingIdentity(identity); } } /** * Action set from carrier signalling broadcast receivers to enable/disable radio * @param subId the subscription ID that this action applies to. * @param enabled control enable or disable radio. * {@hide} */ @Override public void carrierActionSetRadioEnabled(int subId, boolean enabled) { enforceModifyPermission(); final Phone phone = getPhone(subId); final long identity = Binder.clearCallingIdentity(); if (phone == null) { loge("carrierAction: SetRadioEnabled fails with invalid sibId: " + subId); return; } try { phone.carrierActionSetRadioEnabled(enabled); } catch (Exception e) { Log.e(LOG_TAG, "carrierAction: SetRadioEnabled fails. Exception ex=" + e); } finally { Binder.restoreCallingIdentity(identity); } } /** * Enable or disable Voice over NR (VoNR) * @param subId the subscription ID that this action applies to. * @param enabled enable or disable VoNR. * @return operation result. */ @Override public int setVoNrEnabled(int subId, boolean enabled) { enforceModifyPermission(); final Phone phone = getPhone(subId); final long identity = Binder.clearCallingIdentity(); if (phone == null) { loge("setVoNrEnabled fails with no phone object for subId: " + subId); return TelephonyManager.ENABLE_VONR_RADIO_NOT_AVAILABLE; } WorkSource workSource = getWorkSource(Binder.getCallingUid()); try { int result = (int) sendRequest(CMD_ENABLE_VONR, enabled, subId, workSource); if (DBG) log("setVoNrEnabled result: " + result); if (result == TelephonyManager.ENABLE_VONR_SUCCESS) { if (DBG) { log("Set VoNR settings in siminfo db; subId=" + subId + ", value:" + enabled); } SubscriptionManager.setSubscriptionProperty( subId, SubscriptionManager.NR_ADVANCED_CALLING_ENABLED, (enabled ? "1" : "0")); } return result; } finally { Binder.restoreCallingIdentity(identity); } } /** * Is voice over NR enabled * @return true if VoNR is enabled else false */ @Override public boolean isVoNrEnabled(int subId) { enforceReadPrivilegedPermission("isVoNrEnabled"); WorkSource workSource = getWorkSource(Binder.getCallingUid()); final long identity = Binder.clearCallingIdentity(); try { boolean isEnabled = (boolean) sendRequest(CMD_IS_VONR_ENABLED, null, subId, workSource); if (DBG) log("isVoNrEnabled: " + isEnabled); return isEnabled; } finally { Binder.restoreCallingIdentity(identity); } } /** * Action set from carrier signalling broadcast receivers to start/stop reporting the default * network status based on which carrier apps could apply actions accordingly, * enable/disable default url handler for example. * * @param subId the subscription ID that this action applies to. * @param report control start/stop reporting the default network status. * {@hide} */ @Override public void carrierActionReportDefaultNetworkStatus(int subId, boolean report) { enforceModifyPermission(); final Phone phone = getPhone(subId); final long identity = Binder.clearCallingIdentity(); if (phone == null) { loge("carrierAction: ReportDefaultNetworkStatus fails with invalid sibId: " + subId); return; } try { phone.carrierActionReportDefaultNetworkStatus(report); } catch (Exception e) { Log.e(LOG_TAG, "carrierAction: ReportDefaultNetworkStatus fails. Exception ex=" + e); } finally { Binder.restoreCallingIdentity(identity); } } /** * Action set from carrier signalling broadcast receivers to reset all carrier actions * @param subId the subscription ID that this action applies to. * {@hide} */ @Override public void carrierActionResetAll(int subId) { enforceModifyPermission(); final Phone phone = getPhone(subId); if (phone == null) { loge("carrierAction: ResetAll fails with invalid sibId: " + subId); return; } try { phone.carrierActionResetAll(); } catch (Exception e) { Log.e(LOG_TAG, "carrierAction: ResetAll fails. Exception ex=" + e); } } /** * Called when "adb shell dumpsys phone" is invoked. Dump is also automatically invoked when a * bug report is being generated. */ @Override protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { if (mApp.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { writer.println("Permission Denial: can't dump Phone from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + "without permission " + android.Manifest.permission.DUMP); return; } DumpsysHandler.dump(mApp, fd, writer, args); } @Override public int handleShellCommand(@NonNull ParcelFileDescriptor in, @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, @NonNull String[] args) { return new TelephonyShellCommand(this, getDefaultPhone().getContext()).exec( this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), args); } /** * Policy control of data connection with reason {@@TelephonyManager.DataEnabledReason} * @param subId Subscription index * @param reason the reason the data enable change is taking place * @param enabled True if enabling the data, otherwise disabling. * @hide */ @Override public void setDataEnabledForReason(int subId, @TelephonyManager.DataEnabledReason int reason, boolean enabled) { if (reason == TelephonyManager.DATA_ENABLED_REASON_USER || reason == TelephonyManager.DATA_ENABLED_REASON_CARRIER) { try { TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege( mApp, subId, "setDataEnabledForReason"); } catch (SecurityException se) { enforceModifyPermission(); } } else { enforceModifyPermission(); } final long identity = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subId); if (phone != null) { if (reason == TelephonyManager.DATA_ENABLED_REASON_CARRIER) { phone.carrierActionSetMeteredApnsEnabled(enabled); } else { phone.getDataEnabledSettings().setDataEnabled(reason, enabled); } } } finally { Binder.restoreCallingIdentity(identity); } } /** * Get Client request stats * @return List of Client Request Stats * @hide */ @Override public List getClientRequestStats(String callingPackage, String callingFeatureId, int subId) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mApp, subId, callingPackage, callingFeatureId, "getClientRequestStats")) { return null; } Phone phone = getPhone(subId); final long identity = Binder.clearCallingIdentity(); try { if (phone != null) { return phone.getClientRequestStats(); } return null; } finally { Binder.restoreCallingIdentity(identity); } } private WorkSource getWorkSource(int uid) { String packageName = mApp.getPackageManager().getNameForUid(uid); return new WorkSource(uid, packageName); } /** * Set SIM card power state. * * @param slotIndex SIM slot id. * @param state State of SIM (power down, power up, pass through) * - {@link android.telephony.TelephonyManager#CARD_POWER_DOWN} * - {@link android.telephony.TelephonyManager#CARD_POWER_UP} * - {@link android.telephony.TelephonyManager#CARD_POWER_UP_PASS_THROUGH} * **/ @Override public void setSimPowerStateForSlot(int slotIndex, int state) { enforceModifyPermission(); Phone phone = PhoneFactory.getPhone(slotIndex); WorkSource workSource = getWorkSource(Binder.getCallingUid()); final long identity = Binder.clearCallingIdentity(); try { if (phone != null) { phone.setSimPowerState(state, null, workSource); } } finally { Binder.restoreCallingIdentity(identity); } } /** * Set SIM card power state. * * @param slotIndex SIM slot id. * @param state State of SIM (power down, power up, pass through) * @param callback callback to trigger after success or failure * - {@link android.telephony.TelephonyManager#CARD_POWER_DOWN} * - {@link android.telephony.TelephonyManager#CARD_POWER_UP} * - {@link android.telephony.TelephonyManager#CARD_POWER_UP_PASS_THROUGH} * **/ @Override public void setSimPowerStateForSlotWithCallback(int slotIndex, int state, IIntegerConsumer callback) { enforceModifyPermission(); Phone phone = PhoneFactory.getPhone(slotIndex); WorkSource workSource = getWorkSource(Binder.getCallingUid()); final long identity = Binder.clearCallingIdentity(); try { if (phone != null) { Pair arguments = Pair.create(state, callback); sendRequestAsync(CMD_SET_SIM_POWER, arguments, phone, workSource); } } finally { Binder.restoreCallingIdentity(identity); } } private boolean isUssdApiAllowed(int subId) { CarrierConfigManager configManager = (CarrierConfigManager) mApp.getSystemService(Context.CARRIER_CONFIG_SERVICE); if (configManager == null) { return false; } PersistableBundle pb = configManager.getConfigForSubId(subId); if (pb == null) { return false; } return pb.getBoolean( CarrierConfigManager.KEY_ALLOW_USSD_REQUESTS_VIA_TELEPHONY_MANAGER_BOOL); } /** * Check if phone is in emergency callback mode * @return true if phone is in emergency callback mode * @param subId sub id */ @Override public boolean getEmergencyCallbackMode(int subId) { enforceReadPrivilegedPermission("getEmergencyCallbackMode"); final Phone phone = getPhone(subId); final long identity = Binder.clearCallingIdentity(); try { if (phone != null) { return phone.isInEcm(); } else { return false; } } finally { Binder.restoreCallingIdentity(identity); } } /** * Get the current signal strength information for the given subscription. * Because this information is not updated when the device is in a low power state * it should not be relied-upon to be current. * @param subId Subscription index * @return the most recent cached signal strength info from the modem */ @Override public SignalStrength getSignalStrength(int subId) { final long identity = Binder.clearCallingIdentity(); try { Phone p = getPhone(subId); if (p == null) { return null; } return p.getSignalStrength(); } finally { Binder.restoreCallingIdentity(identity); } } /** * Get the current modem radio state for the given slot. * @param slotIndex slot index. * @param callingPackage the name of the package making the call. * @param callingFeatureId The feature in the package. * @return the current radio power state from the modem */ @Override public int getRadioPowerState(int slotIndex, String callingPackage, String callingFeatureId) { Phone phone = PhoneFactory.getPhone(slotIndex); if (phone != null) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, phone.getSubId(), callingPackage, callingFeatureId, "getRadioPowerState")) { return TelephonyManager.RADIO_POWER_UNAVAILABLE; } final long identity = Binder.clearCallingIdentity(); try { return phone.getRadioPowerState(); } finally { Binder.restoreCallingIdentity(identity); } } return TelephonyManager.RADIO_POWER_UNAVAILABLE; } /** * Checks if data roaming is enabled on the subscription with id {@code subId}. * *

Requires one of the following permissions: * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}, * {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling app has carrier * privileges. * * @param subId subscription id * @return {@code true} if data roaming is enabled on this subscription, otherwise return * {@code false}. */ @Override public boolean isDataRoamingEnabled(int subId) { try { mApp.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE, null); } catch (Exception e) { TelephonyPermissions.enforceCallingOrSelfReadPhoneStatePermissionOrCarrierPrivilege( mApp, subId, "isDataRoamingEnabled"); } boolean isEnabled = false; final long identity = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subId); isEnabled = phone != null ? phone.getDataRoamingEnabled() : false; } finally { Binder.restoreCallingIdentity(identity); } return isEnabled; } /** * Enables/Disables the data roaming on the subscription with id {@code subId}. * *

Requires permission: * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or that the calling app has carrier * privileges. * * @param subId subscription id * @param isEnabled {@code true} means enable, {@code false} means disable. */ @Override public void setDataRoamingEnabled(int subId, boolean isEnabled) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "setDataRoamingEnabled"); final long identity = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subId); if (phone != null) { phone.setDataRoamingEnabled(isEnabled); } } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean isManualNetworkSelectionAllowed(int subId) { TelephonyPermissions .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege( mApp, subId, "isManualNetworkSelectionAllowed"); boolean isAllowed = true; final long identity = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subId); if (phone != null) { isAllowed = phone.isCspPlmnEnabled(); } } finally { Binder.restoreCallingIdentity(identity); } return isAllowed; } @Override public List getUiccCardsInfo(String callingPackage) { // Verify that tha callingPackage belongs to the calling UID mApp.getSystemService(AppOpsManager.class) .checkPackage(Binder.getCallingUid(), callingPackage); boolean hasReadPermission = false; try { enforceReadPrivilegedPermission("getUiccCardsInfo"); hasReadPermission = true; } catch (SecurityException e) { // even without READ_PRIVILEGED_PHONE_STATE, we allow the call to continue if the caller // has carrier privileges on an active UICC if (checkCarrierPrivilegesForPackageAnyPhone(callingPackage) != TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { throw new SecurityException("Caller does not have permission."); } } final long identity = Binder.clearCallingIdentity(); try { UiccController uiccController = UiccController.getInstance(); ArrayList cardInfos = uiccController.getAllUiccCardInfos(); if (hasReadPermission) { return cardInfos; } // Remove private info if the caller doesn't have access ArrayList filteredInfos = new ArrayList<>(); for (UiccCardInfo cardInfo : cardInfos) { // For an inactive eUICC, the UiccCard will be null even though the UiccCardInfo // is available UiccCard card = uiccController.getUiccCardForSlot(cardInfo.getSlotIndex()); if (card == null || card.getUiccProfile() == null) { // assume no access if the card or profile is unavailable filteredInfos.add(cardInfo.getUnprivileged()); continue; } UiccProfile profile = card.getUiccProfile(); if (profile.getCarrierPrivilegeStatus(mApp.getPackageManager(), callingPackage) == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { filteredInfos.add(cardInfo); } else { filteredInfos.add(cardInfo.getUnprivileged()); } } return filteredInfos; } finally { Binder.restoreCallingIdentity(identity); } } @Override public UiccSlotInfo[] getUiccSlotsInfo() { enforceReadPrivilegedPermission("getUiccSlotsInfo"); final long identity = Binder.clearCallingIdentity(); try { UiccSlot[] slots = UiccController.getInstance().getUiccSlots(); if (slots == null) { Rlog.i(LOG_TAG, "slots is null."); return null; } UiccSlotInfo[] infos = new UiccSlotInfo[slots.length]; for (int i = 0; i < slots.length; i++) { UiccSlot slot = slots[i]; if (slot == null) { continue; } String cardId; UiccCard card = slot.getUiccCard(); if (card != null) { cardId = card.getCardId(); } else { cardId = slot.getEid(); if (TextUtils.isEmpty(cardId)) { cardId = slot.getIccId(); } } if (cardId != null) { // if cardId is an ICCID, strip off trailing Fs before exposing to user // if cardId is an EID, it's all digits so this is fine cardId = IccUtils.stripTrailingFs(cardId); } int cardState = 0; switch (slot.getCardState()) { case CARDSTATE_ABSENT: cardState = UiccSlotInfo.CARD_STATE_INFO_ABSENT; break; case CARDSTATE_PRESENT: cardState = UiccSlotInfo.CARD_STATE_INFO_PRESENT; break; case CARDSTATE_ERROR: cardState = UiccSlotInfo.CARD_STATE_INFO_ERROR; break; case CARDSTATE_RESTRICTED: cardState = UiccSlotInfo.CARD_STATE_INFO_RESTRICTED; break; default: break; } infos[i] = new UiccSlotInfo( slot.isActive(), slot.isEuicc(), cardId, cardState, slot.getPhoneId(), slot.isExtendedApduSupported(), slot.isRemovable()); } return infos; } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean switchSlots(int[] physicalSlots) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { return (Boolean) sendRequest(CMD_SWITCH_SLOTS, physicalSlots); } finally { Binder.restoreCallingIdentity(identity); } } @Override public int getCardIdForDefaultEuicc(int subId, String callingPackage) { final long identity = Binder.clearCallingIdentity(); try { return UiccController.getInstance().getCardIdForDefaultEuicc(); } finally { Binder.restoreCallingIdentity(identity); } } /** * A test API to reload the UICC profile. * *

Requires that the calling app has permission * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}. * @hide */ @Override public void refreshUiccProfile(int subId) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subId); if (phone == null) { return; } UiccCard uiccCard = phone.getUiccCard(); if (uiccCard == null) { return; } UiccProfile uiccProfile = uiccCard.getUiccProfile(); if (uiccProfile == null) { return; } uiccProfile.refresh(); } finally { Binder.restoreCallingIdentity(identity); } } /** * Returns false if the mobile data is disabled by default, otherwise return true. */ private boolean getDefaultDataEnabled() { return TelephonyProperties.mobile_data().orElse(true); } /** * Returns true if the data roaming is enabled by default, i.e the system property * of {@link #DEFAULT_DATA_ROAMING_PROPERTY_NAME} is true or the config of * {@link CarrierConfigManager#KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL} is true. */ private boolean getDefaultDataRoamingEnabled(int subId) { final CarrierConfigManager configMgr = (CarrierConfigManager) mApp.getSystemService(Context.CARRIER_CONFIG_SERVICE); boolean isDataRoamingEnabled = TelephonyProperties.data_roaming().orElse(false); isDataRoamingEnabled |= configMgr.getConfigForSubId(subId).getBoolean( CarrierConfigManager.KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL); return isDataRoamingEnabled; } /** * Returns the default network type for the given {@code subId}, if the default network type is * not set, return {@link Phone#PREFERRED_NT_MODE}. */ private int getDefaultNetworkType(int subId) { List list = TelephonyProperties.default_network(); int phoneId = mSubscriptionController.getPhoneId(subId); if (phoneId >= 0 && phoneId < list.size() && list.get(phoneId) != null) { return list.get(phoneId); } return Phone.PREFERRED_NT_MODE; } @Override public void setCarrierTestOverride(int subId, String mccmnc, String imsi, String iccid, String gid1, String gid2, String plmn, String spn, String carrierPrivilegeRules, String apn) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone == null) { loge("setCarrierTestOverride fails with invalid subId: " + subId); return; } phone.setCarrierTestOverride(mccmnc, imsi, iccid, gid1, gid2, plmn, spn, carrierPrivilegeRules, apn); if (carrierPrivilegeRules == null) { mCarrierPrivilegeTestOverrideSubIds.remove(subId); } else { mCarrierPrivilegeTestOverrideSubIds.add(subId); } } finally { Binder.restoreCallingIdentity(identity); } } @Override public int getCarrierIdListVersion(int subId) { enforceReadPrivilegedPermission("getCarrierIdListVersion"); final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone == null) { loge("getCarrierIdListVersion fails with invalid subId: " + subId); return TelephonyManager.UNKNOWN_CARRIER_ID_LIST_VERSION; } return phone.getCarrierIdListVersion(); } finally { Binder.restoreCallingIdentity(identity); } } @Override public int getNumberOfModemsWithSimultaneousDataConnections(int subId, String callingPackage, String callingFeatureId) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mApp, subId, callingPackage, callingFeatureId, "getNumberOfModemsWithSimultaneousDataConnections")) { return -1; } final long identity = Binder.clearCallingIdentity(); try { return mPhoneConfigurationManager.getNumberOfModemsWithSimultaneousDataConnections(); } finally { Binder.restoreCallingIdentity(identity); } } @Override public int getCdmaRoamingMode(int subId) { TelephonyPermissions .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege( mApp, subId, "getCdmaRoamingMode"); final long identity = Binder.clearCallingIdentity(); try { return (int) sendRequest(CMD_GET_CDMA_ROAMING_MODE, null /* argument */, subId); } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean setCdmaRoamingMode(int subId, int mode) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "setCdmaRoamingMode"); final long identity = Binder.clearCallingIdentity(); try { return (boolean) sendRequest(CMD_SET_CDMA_ROAMING_MODE, mode, subId); } finally { Binder.restoreCallingIdentity(identity); } } @Override public int getCdmaSubscriptionMode(int subId) { TelephonyPermissions .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege( mApp, subId, "getCdmaSubscriptionMode"); final long identity = Binder.clearCallingIdentity(); try { return (int) sendRequest(CMD_GET_CDMA_SUBSCRIPTION_MODE, null /* argument */, subId); } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean setCdmaSubscriptionMode(int subId, int mode) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "setCdmaSubscriptionMode"); final long identity = Binder.clearCallingIdentity(); try { return (boolean) sendRequest(CMD_SET_CDMA_SUBSCRIPTION_MODE, mode, subId); } finally { Binder.restoreCallingIdentity(identity); } } @Override public Map> getEmergencyNumberList( String callingPackage, String callingFeatureId) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mApp, getDefaultSubscription(), callingPackage, callingFeatureId, "getEmergencyNumberList")) { throw new SecurityException("Requires READ_PHONE_STATE permission."); } final long identity = Binder.clearCallingIdentity(); try { Map> emergencyNumberListInternal = new HashMap<>(); for (Phone phone: PhoneFactory.getPhones()) { if (phone.getEmergencyNumberTracker() != null && phone.getEmergencyNumberTracker().getEmergencyNumberList() != null) { emergencyNumberListInternal.put( phone.getSubId(), phone.getEmergencyNumberTracker().getEmergencyNumberList()); } } return emergencyNumberListInternal; } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean isEmergencyNumber(String number, boolean exactMatch) { final Phone defaultPhone = getDefaultPhone(); if (!exactMatch) { TelephonyPermissions .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege( mApp, defaultPhone.getSubId(), "isEmergencyNumber(Potential)"); } final long identity = Binder.clearCallingIdentity(); try { for (Phone phone: PhoneFactory.getPhones()) { if (phone.getEmergencyNumberTracker() != null && phone.getEmergencyNumberTracker() .isEmergencyNumber(number, exactMatch)) { return true; } } return false; } finally { Binder.restoreCallingIdentity(identity); } } /** * Start emergency callback mode for GsmCdmaPhone for testing. */ @Override public void startEmergencyCallbackMode() { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "startEmergencyCallbackMode"); enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { for (Phone phone : PhoneFactory.getPhones()) { Rlog.d(LOG_TAG, "startEmergencyCallbackMode phone type: " + phone.getPhoneType()); if (phone != null && ((phone.getPhoneType() == PHONE_TYPE_GSM) || (phone.getPhoneType() == PHONE_TYPE_CDMA))) { GsmCdmaPhone gsmCdmaPhone = (GsmCdmaPhone) phone; gsmCdmaPhone.obtainMessage( GsmCdmaPhone.EVENT_EMERGENCY_CALLBACK_MODE_ENTER).sendToTarget(); Rlog.d(LOG_TAG, "startEmergencyCallbackMode: triggered"); } } } finally { Binder.restoreCallingIdentity(identity); } } /** * Update emergency number list for test mode. */ @Override public void updateEmergencyNumberListTestMode(int action, EmergencyNumber num) { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "updateEmergencyNumberListTestMode"); final long identity = Binder.clearCallingIdentity(); try { for (Phone phone: PhoneFactory.getPhones()) { EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker(); if (tracker != null) { tracker.executeEmergencyNumberTestModeCommand(action, num); } } } finally { Binder.restoreCallingIdentity(identity); } } /** * Get the full emergency number list for test mode. */ @Override public List getEmergencyNumberListTestMode() { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getEmergencyNumberListTestMode"); final long identity = Binder.clearCallingIdentity(); try { Set emergencyNumbers = new HashSet<>(); for (Phone phone: PhoneFactory.getPhones()) { EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker(); if (tracker != null) { for (EmergencyNumber num : tracker.getEmergencyNumberList()) { emergencyNumbers.add(num.getNumber()); } } } return new ArrayList<>(emergencyNumbers); } finally { Binder.restoreCallingIdentity(identity); } } @Override public int getEmergencyNumberDbVersion(int subId) { enforceReadPrivilegedPermission("getEmergencyNumberDbVersion"); final long identity = Binder.clearCallingIdentity(); try { final Phone phone = getPhone(subId); if (phone == null) { loge("getEmergencyNumberDbVersion fails with invalid subId: " + subId); return TelephonyManager.INVALID_EMERGENCY_NUMBER_DB_VERSION; } return phone.getEmergencyNumberDbVersion(); } finally { Binder.restoreCallingIdentity(identity); } } @Override public void notifyOtaEmergencyNumberDbInstalled() { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { for (Phone phone: PhoneFactory.getPhones()) { EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker(); if (tracker != null) { tracker.updateOtaEmergencyNumberDatabase(); } } } finally { Binder.restoreCallingIdentity(identity); } } @Override public void updateOtaEmergencyNumberDbFilePath(ParcelFileDescriptor otaParcelFileDescriptor) { enforceActiveEmergencySessionPermission(); final long identity = Binder.clearCallingIdentity(); try { for (Phone phone: PhoneFactory.getPhones()) { EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker(); if (tracker != null) { tracker.updateOtaEmergencyNumberDbFilePath(otaParcelFileDescriptor); } } } finally { Binder.restoreCallingIdentity(identity); } } @Override public void resetOtaEmergencyNumberDbFilePath() { enforceActiveEmergencySessionPermission(); final long identity = Binder.clearCallingIdentity(); try { for (Phone phone: PhoneFactory.getPhones()) { EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker(); if (tracker != null) { tracker.resetOtaEmergencyNumberDbFilePath(); } } } finally { Binder.restoreCallingIdentity(identity); } } @Override public List getCertsFromCarrierPrivilegeAccessRules(int subId) { enforceReadPrivilegedPermission("getCertsFromCarrierPrivilegeAccessRules"); Phone phone = getPhone(subId); if (phone == null) { return null; } final long identity = Binder.clearCallingIdentity(); try { UiccProfile profile = UiccController.getInstance() .getUiccProfileForPhone(phone.getPhoneId()); if (profile != null) { return profile.getCertsFromCarrierPrivilegeAccessRules(); } } finally { Binder.restoreCallingIdentity(identity); } return null; } /** * Enable or disable a modem stack. */ @Override public boolean enableModemForSlot(int slotIndex, boolean enable) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { Phone phone = PhoneFactory.getPhone(slotIndex); if (phone == null) { return false; } else { return (Boolean) sendRequest(CMD_REQUEST_ENABLE_MODEM, enable, phone, null); } } finally { Binder.restoreCallingIdentity(identity); } } /** * Whether a modem stack is enabled or not. */ @Override public boolean isModemEnabledForSlot(int slotIndex, String callingPackage, String callingFeatureId) { Phone phone = PhoneFactory.getPhone(slotIndex); if (phone == null) return false; if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mApp, phone.getSubId(), callingPackage, callingFeatureId, "isModemEnabledForSlot")) { throw new SecurityException("Requires READ_PHONE_STATE permission."); } final long identity = Binder.clearCallingIdentity(); try { try { return mPhoneConfigurationManager.getPhoneStatusFromCache(phone.getPhoneId()); } catch (NoSuchElementException ex) { return (Boolean) sendRequest(CMD_GET_MODEM_STATUS, null, phone, null); } } finally { Binder.restoreCallingIdentity(identity); } } @Override public void setMultiSimCarrierRestriction(boolean isMultiSimCarrierRestricted) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { mTelephonySharedPreferences.edit() .putBoolean(PREF_MULTI_SIM_RESTRICTED, isMultiSimCarrierRestricted) .commit(); } finally { Binder.restoreCallingIdentity(identity); } } @Override @TelephonyManager.IsMultiSimSupportedResult public int isMultiSimSupported(String callingPackage, String callingFeatureId) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, getDefaultPhone().getSubId(), callingPackage, callingFeatureId, "isMultiSimSupported")) { return TelephonyManager.MULTISIM_NOT_SUPPORTED_BY_HARDWARE; } final long identity = Binder.clearCallingIdentity(); try { return isMultiSimSupportedInternal(); } finally { Binder.restoreCallingIdentity(identity); } } @TelephonyManager.IsMultiSimSupportedResult private int isMultiSimSupportedInternal() { // If the device has less than 2 SIM cards, indicate that multisim is restricted. int numPhysicalSlots = UiccController.getInstance().getUiccSlots().length; if (numPhysicalSlots < 2) { loge("isMultiSimSupportedInternal: requires at least 2 cards"); return TelephonyManager.MULTISIM_NOT_SUPPORTED_BY_HARDWARE; } // Check if the hardware supports multisim functionality. If usage of multisim is not // supported by the modem, indicate that it is restricted. PhoneCapability staticCapability = mPhoneConfigurationManager.getStaticPhoneCapability(); if (staticCapability == null) { loge("isMultiSimSupportedInternal: no static configuration available"); return TelephonyManager.MULTISIM_NOT_SUPPORTED_BY_HARDWARE; } if (staticCapability.getLogicalModemList().size() < 2) { loge("isMultiSimSupportedInternal: maximum number of modem is < 2"); return TelephonyManager.MULTISIM_NOT_SUPPORTED_BY_HARDWARE; } // Check if support of multiple SIMs is restricted by carrier if (mTelephonySharedPreferences.getBoolean(PREF_MULTI_SIM_RESTRICTED, false)) { return TelephonyManager.MULTISIM_NOT_SUPPORTED_BY_CARRIER; } return TelephonyManager.MULTISIM_ALLOWED; } /** * Switch configs to enable multi-sim or switch back to single-sim * Note: Switch from multi-sim to single-sim is only possible with MODIFY_PHONE_STATE * permission, but the other way around is possible with either MODIFY_PHONE_STATE * or carrier privileges * @param numOfSims number of active sims we want to switch to */ @Override public void switchMultiSimConfig(int numOfSims) { if (numOfSims == 1) { enforceModifyPermission(); } else { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, "switchMultiSimConfig"); } final long identity = Binder.clearCallingIdentity(); try { //only proceed if multi-sim is not restricted if (isMultiSimSupportedInternal() != TelephonyManager.MULTISIM_ALLOWED) { loge("switchMultiSimConfig not possible. It is restricted or not supported."); return; } mPhoneConfigurationManager.switchMultiSimConfig(numOfSims); } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean isApplicationOnUicc(int subId, int appType) { enforceReadPrivilegedPermission("isApplicationOnUicc"); Phone phone = getPhone(subId); if (phone == null) { return false; } final long identity = Binder.clearCallingIdentity(); try { UiccCard uiccCard = phone.getUiccCard(); if (uiccCard == null) { return false; } UiccProfile uiccProfile = uiccCard.getUiccProfile(); if (uiccProfile == null) { return false; } if (TelephonyManager.APPTYPE_SIM <= appType && appType <= TelephonyManager.APPTYPE_ISIM) { return uiccProfile.isApplicationOnIcc(AppType.values()[appType]); } return false; } finally { Binder.restoreCallingIdentity(identity); } } /** * Get whether making changes to modem configurations will trigger reboot. * Return value defaults to true. */ @Override public boolean doesSwitchMultiSimConfigTriggerReboot(int subId, String callingPackage, String callingFeatureId) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mApp, subId, callingPackage, callingFeatureId, "doesSwitchMultiSimConfigTriggerReboot")) { return false; } final long identity = Binder.clearCallingIdentity(); try { return mPhoneConfigurationManager.isRebootRequiredForModemConfigChange(); } finally { Binder.restoreCallingIdentity(identity); } } private void updateModemStateMetrics() { TelephonyMetrics metrics = TelephonyMetrics.getInstance(); // TODO: check the state for each modem if the api is ready. metrics.updateEnabledModemBitmap((1 << TelephonyManager.from(mApp).getPhoneCount()) - 1); } @Override public int[] getSlotsMapping() { enforceReadPrivilegedPermission("getSlotsMapping"); final long identity = Binder.clearCallingIdentity(); try { int phoneCount = TelephonyManager.getDefault().getPhoneCount(); // All logical slots should have a mapping to a physical slot. int[] logicalSlotsMapping = new int[phoneCount]; UiccSlotInfo[] slotInfos = getUiccSlotsInfo(); for (int i = 0; i < slotInfos.length; i++) { if (SubscriptionManager.isValidPhoneId(slotInfos[i].getLogicalSlotIdx())) { logicalSlotsMapping[slotInfos[i].getLogicalSlotIdx()] = i; } } return logicalSlotsMapping; } finally { Binder.restoreCallingIdentity(identity); } } /** * Get the IRadio HAL Version */ @Override public int getRadioHalVersion() { Phone phone = getDefaultPhone(); if (phone == null) return -1; HalVersion hv = phone.getHalVersion(); if (hv.equals(HalVersion.UNKNOWN)) return -1; return hv.major * 100 + hv.minor; } /** * Get the current calling package name. * @return the current calling package name */ @Override public String getCurrentPackageName() { return mApp.getPackageManager().getPackagesForUid(Binder.getCallingUid())[0]; } /** * Return whether data is enabled for certain APN type. This will tell if framework will accept * corresponding network requests on a subId. * * Data is enabled if: * 1) user data is turned on, or * 2) APN is un-metered for this subscription, or * 3) APN type is whitelisted. E.g. MMS is whitelisted if * {@link TelephonyManager#MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED} is enabled. * * @return whether data is allowed for a apn type. * * @hide */ @Override public boolean isDataEnabledForApn(int apnType, int subId, String callingPackage) { enforceReadPrivilegedPermission("Needs READ_PRIVILEGED_PHONE_STATE for " + "isDataEnabledForApn"); // Now that all security checks passes, perform the operation as ourselves. final long identity = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subId); if (phone == null) return false; boolean isMetered = ApnSettingUtils.isMeteredApnType(apnType, phone); return !isMetered || phone.getDataEnabledSettings().isDataEnabled(apnType); } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean isApnMetered(@ApnType int apnType, int subId) { enforceReadPrivilegedPermission("isApnMetered"); // Now that all security checks passes, perform the operation as ourselves. final long identity = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subId); if (phone == null) return true; // By default return true. return ApnSettingUtils.isMeteredApnType(apnType, phone); } finally { Binder.restoreCallingIdentity(identity); } } @Override public void setSystemSelectionChannels(List specifiers, int subscriptionId, IBooleanConsumer resultCallback) { enforceModifyPermission(); long token = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subscriptionId); if (phone == null) { try { if (resultCallback != null) { resultCallback.accept(false); } } catch (RemoteException e) { // ignore } return; } Pair, Consumer> argument = Pair.create(specifiers, (x) -> { try { if (resultCallback != null) { resultCallback.accept(x); } } catch (RemoteException e) { // ignore } }); sendRequestAsync(CMD_SET_SYSTEM_SELECTION_CHANNELS, argument, phone, null); } finally { Binder.restoreCallingIdentity(token); } } @Override public List getSystemSelectionChannels(int subId) { TelephonyPermissions .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege( mApp, subId, "getSystemSelectionChannels"); WorkSource workSource = getWorkSource(Binder.getCallingUid()); final long identity = Binder.clearCallingIdentity(); try { Object result = sendRequest(CMD_GET_SYSTEM_SELECTION_CHANNELS, null, subId, workSource); if (result instanceof IllegalStateException) { throw (IllegalStateException) result; } List specifiers = (List) result; if (DBG) log("getSystemSelectionChannels: " + specifiers); return specifiers; } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean isMvnoMatched(int subId, int mvnoType, @NonNull String mvnoMatchData) { enforceReadPrivilegedPermission("isMvnoMatched"); IccRecords iccRecords = UiccController.getInstance().getIccRecords( SubscriptionManager.getPhoneId(subId), UiccController.APP_FAM_3GPP); if (iccRecords == null) { Log.d(LOG_TAG, "isMvnoMatched# IccRecords is null"); return false; } return ApnSettingUtils.mvnoMatches(iccRecords, mvnoType, mvnoMatchData); } @Override public void enqueueSmsPickResult(String callingPackage, String callingAttributionTag, IIntegerConsumer pendingSubIdResult) { if (callingPackage == null) { callingPackage = getCurrentPackageName(); } SmsPermissions permissions = new SmsPermissions(getDefaultPhone(), mApp, (AppOpsManager) mApp.getSystemService(Context.APP_OPS_SERVICE)); if (!permissions.checkCallingCanSendSms(callingPackage, callingAttributionTag, "Sending message")) { throw new SecurityException("Requires SEND_SMS permission to perform this operation"); } PickSmsSubscriptionActivity.addPendingResult(pendingSubIdResult); Intent intent = new Intent(); intent.setClass(mApp, PickSmsSubscriptionActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // Bring up choose default SMS subscription dialog right now intent.putExtra(PickSmsSubscriptionActivity.DIALOG_TYPE_KEY, PickSmsSubscriptionActivity.SMS_PICK_FOR_MESSAGE); mApp.startActivity(intent); } @Override public String getMmsUAProfUrl(int subId) { //TODO investigate if this API should require proper permission check in R b/133791609 final long identity = Binder.clearCallingIdentity(); try { String carrierUAProfUrl = mApp.getCarrierConfigForSubId(subId).getString( CarrierConfigManager.KEY_MMS_UA_PROF_URL_STRING); if (!TextUtils.isEmpty(carrierUAProfUrl)) { return carrierUAProfUrl; } return SubscriptionManager.getResourcesForSubId(getDefaultPhone().getContext(), subId) .getString(com.android.internal.R.string.config_mms_user_agent_profile_url); } finally { Binder.restoreCallingIdentity(identity); } } @Override public String getMmsUserAgent(int subId) { //TODO investigate if this API should require proper permission check in R b/133791609 final long identity = Binder.clearCallingIdentity(); try { String carrierUserAgent = mApp.getCarrierConfigForSubId(subId).getString( CarrierConfigManager.KEY_MMS_USER_AGENT_STRING); if (!TextUtils.isEmpty(carrierUserAgent)) { return carrierUserAgent; } return SubscriptionManager.getResourcesForSubId(getDefaultPhone().getContext(), subId) .getString(com.android.internal.R.string.config_mms_user_agent); } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean isMobileDataPolicyEnabled(int subscriptionId, int policy) { enforceReadPrivilegedPermission("isMobileDataPolicyEnabled"); final long identity = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subscriptionId); if (phone == null) return false; switch (policy) { case TelephonyManager.MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL: return phone.getDataEnabledSettings().isDataAllowedInVoiceCall(); case TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED: return phone.getDataEnabledSettings().isMmsAlwaysAllowed(); default: throw new IllegalArgumentException(policy + " is not a valid policy"); } } finally { Binder.restoreCallingIdentity(identity); } } @Override public void setMobileDataPolicyEnabled(int subscriptionId, int policy, boolean enabled) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subscriptionId); if (phone == null) return; switch (policy) { case TelephonyManager.MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL: phone.getDataEnabledSettings().setAllowDataDuringVoiceCall(enabled); break; case TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED: phone.getDataEnabledSettings().setAlwaysAllowMmsData(enabled); break; default: throw new IllegalArgumentException(policy + " is not a valid policy"); } } finally { Binder.restoreCallingIdentity(identity); } } /** * Updates whether conference event package handling is enabled. * @param isCepEnabled {@code true} if CEP handling is enabled (default), or {@code false} * otherwise. */ @Override public void setCepEnabled(boolean isCepEnabled) { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setCepEnabled"); final long identity = Binder.clearCallingIdentity(); try { Rlog.i(LOG_TAG, "setCepEnabled isCepEnabled=" + isCepEnabled); for (Phone phone : PhoneFactory.getPhones()) { Phone defaultPhone = phone.getImsPhone(); if (defaultPhone != null && defaultPhone.getPhoneType() == PHONE_TYPE_IMS) { ImsPhone imsPhone = (ImsPhone) defaultPhone; ImsPhoneCallTracker imsPhoneCallTracker = (ImsPhoneCallTracker) imsPhone.getCallTracker(); imsPhoneCallTracker.setConferenceEventPackageEnabled(isCepEnabled); Rlog.i(LOG_TAG, "setCepEnabled isCepEnabled=" + isCepEnabled + ", for imsPhone " + imsPhone.getMsisdn()); } } } finally { Binder.restoreCallingIdentity(identity); } } /** * Notify that an RCS autoconfiguration XML file has been received for provisioning. * * @param config The XML file to be read. ASCII/UTF8 encoded text if not compressed. * @param isCompressed The XML file is compressed in gzip format and must be decompressed * before being read. */ @Override public void notifyRcsAutoConfigurationReceived(int subId, @NonNull byte[] config, boolean isCompressed) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "notifyRcsAutoConfigurationReceived"); if (!SubscriptionManager.isValidSubscriptionId(subId)) { throw new IllegalArgumentException("Invalid Subscription ID: " + subId); } if (!isImsAvailableOnDevice()) { throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION, "IMS not available on device."); } final long identity = Binder.clearCallingIdentity(); try { RcsProvisioningMonitor.getInstance().updateConfig(subId, config, isCompressed); } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean isIccLockEnabled(int subId) { enforceReadPrivilegedPermission("isIccLockEnabled"); // Now that all security checks passes, perform the operation as ourselves. final long identity = Binder.clearCallingIdentity(); try { Phone phone = getPhone(subId); if (phone != null && phone.getIccCard() != null) { return phone.getIccCard().getIccLockEnabled(); } else { return false; } } finally { Binder.restoreCallingIdentity(identity); } } /** * Set the ICC pin lock enabled or disabled. * * @return an integer representing the status of IccLock enabled or disabled in the following * three cases: * - {@link TelephonyManager#CHANGE_ICC_LOCK_SUCCESS} if enabled or disabled IccLock * successfully. * - Positive number and zero for remaining password attempts. * - Negative number for other failure cases (such like enabling/disabling PIN failed). * */ @Override public int setIccLockEnabled(int subId, boolean enabled, String password) { enforceModifyPermission(); Phone phone = getPhone(subId); if (phone == null) { return 0; } // Now that all security checks passes, perform the operation as ourselves. final long identity = Binder.clearCallingIdentity(); try { int attemptsRemaining = (int) sendRequest(CMD_SET_ICC_LOCK_ENABLED, new Pair(enabled, password), phone, null); return attemptsRemaining; } catch (Exception e) { Log.e(LOG_TAG, "setIccLockEnabled. Exception e =" + e); } finally { Binder.restoreCallingIdentity(identity); } return 0; } /** * Change the ICC password used in ICC pin lock. * * @return an integer representing the status of IccLock changed in the following three cases: * - {@link TelephonyManager#CHANGE_ICC_LOCK_SUCCESS} if changed IccLock successfully. * - Positive number and zero for remaining password attempts. * - Negative number for other failure cases (such like enabling/disabling PIN failed). * */ @Override public int changeIccLockPassword(int subId, String oldPassword, String newPassword) { enforceModifyPermission(); Phone phone = getPhone(subId); if (phone == null) { return 0; } // Now that all security checks passes, perform the operation as ourselves. final long identity = Binder.clearCallingIdentity(); try { int attemptsRemaining = (int) sendRequest(CMD_CHANGE_ICC_LOCK_PASSWORD, new Pair(oldPassword, newPassword), phone, null); return attemptsRemaining; } catch (Exception e) { Log.e(LOG_TAG, "changeIccLockPassword. Exception e =" + e); } finally { Binder.restoreCallingIdentity(identity); } return 0; } /** * Request for receiving user activity notification */ @Override public void requestUserActivityNotification() { if (!mNotifyUserActivity.get() && !mMainThreadHandler.hasMessages(MSG_NOTIFY_USER_ACTIVITY)) { mNotifyUserActivity.set(true); } } /** * Called when userActivity is signalled in the power manager. * This is safe to call from any thread, with any window manager locks held or not. */ @Override public void userActivity() { // *************************************** // * Inherited from PhoneWindowManager * // *************************************** // THIS IS CALLED FROM DEEP IN THE POWER MANAGER // WITH ITS LOCKS HELD. // // This code must be VERY careful about the locks // it acquires. // In fact, the current code acquires way too many, // and probably has lurking deadlocks. if (Binder.getCallingUid() != Process.SYSTEM_UID) { throw new SecurityException("Only the OS may call notifyUserActivity()"); } if (mNotifyUserActivity.getAndSet(false)) { mMainThreadHandler.sendEmptyMessageDelayed(MSG_NOTIFY_USER_ACTIVITY, USER_ACTIVITY_NOTIFICATION_DELAY); } } @Override public boolean canConnectTo5GInDsdsMode() { return mApp.getResources().getBoolean(R.bool.config_5g_connection_in_dsds_mode); } @Override public @NonNull List getEquivalentHomePlmns(int subId, String callingPackage, String callingFeatureId) { if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState( mApp, subId, callingPackage, callingFeatureId, "getEquivalentHomePlmns")) { throw new SecurityException("Requires READ_PHONE_STATE permission."); } Phone phone = getPhone(subId); if (phone == null) { throw new RuntimeException("phone is not available"); } // Now that all security checks passes, perform the operation as ourselves. final long identity = Binder.clearCallingIdentity(); try { return phone.getEquivalentHomePlmns(); } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean isRadioInterfaceCapabilitySupported( final @NonNull @TelephonyManager.RadioInterfaceCapability String capability) { Set radioInterfaceCapabilities = mRadioInterfaceCapabilities.getCapabilities(); if (radioInterfaceCapabilities == null) { throw new RuntimeException("radio interface capabilities are not available"); } return radioInterfaceCapabilities.contains(capability); } @Override public void bootstrapAuthenticationRequest(int subId, int appType, Uri nafUrl, UaSecurityProtocolIdentifier securityProtocol, boolean forceBootStrapping, IBootstrapAuthenticationCallback callback) { TelephonyPermissions.enforceAnyPermissionGrantedOrCarrierPrivileges(mApp, subId, Binder.getCallingUid(), "bootstrapAuthenticationRequest", Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION, Manifest.permission.MODIFY_PHONE_STATE); if (DBG) { log("bootstrapAuthenticationRequest, subId:" + subId + ", appType:" + appType + ", NAF:" + nafUrl + ", sp:" + securityProtocol + ", forceBootStrapping:" + forceBootStrapping + ", callback:" + callback); } if (!SubscriptionManager.isValidSubscriptionId(subId) || appType < TelephonyManager.APPTYPE_UNKNOWN || appType > TelephonyManager.APPTYPE_ISIM || nafUrl == null || securityProtocol == null || callback == null) { Log.d(LOG_TAG, "bootstrapAuthenticationRequest failed due to invalid parameters"); if (callback != null) { try { callback.onAuthenticationFailure( 0, TelephonyManager.GBA_FAILURE_REASON_FEATURE_NOT_SUPPORTED); } catch (RemoteException exception) { log("Fail to notify onAuthenticationFailure due to " + exception); } return; } } final long token = Binder.clearCallingIdentity(); try { getGbaManager(subId).bootstrapAuthenticationRequest( new GbaAuthRequest(subId, appType, nafUrl, securityProtocol.toByteArray(), forceBootStrapping, callback)); } finally { Binder.restoreCallingIdentity(token); } } /** * Attempts to set the radio power state for all phones for thermal reason. * This does not guarantee that the * requested radio power state will actually be set. See {@link * PhoneInternalInterface#setRadioPowerForReason} for more details. * * @param enable {@code true} if trying to turn radio on. * @return {@code true} if phone setRadioPowerForReason was called. Otherwise, returns {@code * false}. */ private boolean setRadioPowerForThermal(boolean enable) { boolean isPhoneAvailable = false; for (int i = 0; i < TelephonyManager.getDefault().getActiveModemCount(); i++) { Phone phone = PhoneFactory.getPhone(i); if (phone != null) { phone.setRadioPowerForReason(enable, Phone.RADIO_POWER_REASON_THERMAL); isPhoneAvailable = true; } } // return true if successfully informed the phone object about the thermal radio power // request. return isPhoneAvailable; } private int handleDataThrottlingRequest(int subId, DataThrottlingRequest dataThrottlingRequest) { boolean isDataThrottlingSupported = isRadioInterfaceCapabilitySupported( TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING); if (!isDataThrottlingSupported && dataThrottlingRequest.getDataThrottlingAction() != DataThrottlingRequest.DATA_THROTTLING_ACTION_NO_DATA_THROTTLING) { throw new IllegalArgumentException("modem does not support data throttling"); } // Ensure that radio is on. If not able to power on due to phone being unavailable, return // THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE. if (!setRadioPowerForThermal(true)) { return TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE; } setDataEnabledForReason(subId, TelephonyManager.DATA_ENABLED_REASON_THERMAL, true); if (isDataThrottlingSupported) { int thermalMitigationResult = (int) sendRequest(CMD_SET_DATA_THROTTLING, dataThrottlingRequest, subId); if (thermalMitigationResult == SET_DATA_THROTTLING_MODEM_THREW_INVALID_PARAMS) { throw new IllegalArgumentException("modem returned INVALID_ARGUMENTS"); } else if (thermalMitigationResult == MODEM_DOES_NOT_SUPPORT_DATA_THROTTLING_ERROR_CODE) { log("Modem likely does not support data throttling on secondary carrier. Data " + "throttling action = " + dataThrottlingRequest.getDataThrottlingAction()); return TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_ERROR; } return thermalMitigationResult; } return TelephonyManager.THERMAL_MITIGATION_RESULT_SUCCESS; } private static List getThermalMitigationAllowlist(Context context) { if (sThermalMitigationAllowlistedPackages.isEmpty()) { for (String pckg : context.getResources() .getStringArray(R.array.thermal_mitigation_allowlisted_packages)) { sThermalMitigationAllowlistedPackages.add(pckg); } } return sThermalMitigationAllowlistedPackages; } private boolean isAnyPhoneInEmergencyState() { TelecomManager tm = mApp.getSystemService(TelecomManager.class); if (tm.isInEmergencyCall()) { Log.e(LOG_TAG , "Phone state is not valid. One of the phones is in an emergency call"); return true; } for (Phone phone : PhoneFactory.getPhones()) { if (phone.isInEmergencySmsMode() || phone.isInEcm()) { Log.e(LOG_TAG, "Phone state is not valid. isInEmergencySmsMode = " + phone.isInEmergencySmsMode() + " isInEmergencyCallbackMode = " + phone.isInEcm()); return true; } } return false; } /** * Used by shell commands to add an authorized package name for thermal mitigation. * @param packageName name of package to be allowlisted * @param context */ static void addPackageToThermalMitigationAllowlist(String packageName, Context context) { sThermalMitigationAllowlistedPackages = getThermalMitigationAllowlist(context); sThermalMitigationAllowlistedPackages.add(packageName); } /** * Used by shell commands to remove an authorized package name for thermal mitigation. * @param packageName name of package to remove from allowlist * @param context */ static void removePackageFromThermalMitigationAllowlist(String packageName, Context context) { sThermalMitigationAllowlistedPackages = getThermalMitigationAllowlist(context); sThermalMitigationAllowlistedPackages.remove(packageName); } /** * Thermal mitigation request to control functionalities at modem. * * @param subId the id of the subscription. * @param thermalMitigationRequest holds all necessary information to be passed down to modem. * @param callingPackage the package name of the calling package. * * @return thermalMitigationResult enum as defined in android.telephony.Annotation. */ @Override @ThermalMitigationResult public int sendThermalMitigationRequest( int subId, ThermalMitigationRequest thermalMitigationRequest, String callingPackage) throws IllegalArgumentException { enforceModifyPermission(); mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); if (!getThermalMitigationAllowlist(getDefaultPhone().getContext()) .contains(callingPackage)) { throw new SecurityException("Calling package must be configured in the device config. " + "calling package: " + callingPackage); } WorkSource workSource = getWorkSource(Binder.getCallingUid()); final long identity = Binder.clearCallingIdentity(); int thermalMitigationResult = TelephonyManager.THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR; try { int thermalMitigationAction = thermalMitigationRequest.getThermalMitigationAction(); switch (thermalMitigationAction) { case ThermalMitigationRequest.THERMAL_MITIGATION_ACTION_DATA_THROTTLING: thermalMitigationResult = handleDataThrottlingRequest(subId, thermalMitigationRequest.getDataThrottlingRequest()); break; case ThermalMitigationRequest.THERMAL_MITIGATION_ACTION_VOICE_ONLY: if (thermalMitigationRequest.getDataThrottlingRequest() != null) { throw new IllegalArgumentException("dataThrottlingRequest must be null for " + "ThermalMitigationRequest.THERMAL_MITIGATION_ACTION_VOICE_ONLY"); } // Ensure that radio is on. If not able to power on due to phone being // unavailable, return THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE. if (!setRadioPowerForThermal(true)) { thermalMitigationResult = TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE; break; } setDataEnabledForReason(subId, TelephonyManager.DATA_ENABLED_REASON_THERMAL, false); thermalMitigationResult = TelephonyManager.THERMAL_MITIGATION_RESULT_SUCCESS; break; case ThermalMitigationRequest.THERMAL_MITIGATION_ACTION_RADIO_OFF: if (thermalMitigationRequest.getDataThrottlingRequest() != null) { throw new IllegalArgumentException("dataThrottlingRequest must be null for" + " ThermalMitigationRequest.THERMAL_MITIGATION_ACTION_RADIO_OFF"); } TelecomAccountRegistry registry = TelecomAccountRegistry.getInstance(null); if (registry != null) { Phone phone = getPhone(subId); if (phone == null) { thermalMitigationResult = TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE; break; } TelephonyConnectionService service = registry.getTelephonyConnectionService(); if (service != null && service.isEmergencyCallPending()) { Log.e(LOG_TAG, "An emergency call is pending"); thermalMitigationResult = TelephonyManager.THERMAL_MITIGATION_RESULT_INVALID_STATE; break; } else if (isAnyPhoneInEmergencyState()) { thermalMitigationResult = TelephonyManager.THERMAL_MITIGATION_RESULT_INVALID_STATE; break; } } else { thermalMitigationResult = TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE; break; } // Turn radio off. If not able to power off due to phone being unavailable, // return THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE. if (!setRadioPowerForThermal(false)) { thermalMitigationResult = TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE; break; } thermalMitigationResult = TelephonyManager.THERMAL_MITIGATION_RESULT_SUCCESS; break; default: throw new IllegalArgumentException("the requested thermalMitigationAction does " + "not exist. Requested action: " + thermalMitigationAction); } } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { Log.e(LOG_TAG, "thermalMitigationRequest. Exception e =" + e); thermalMitigationResult = TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_ERROR; } finally { Binder.restoreCallingIdentity(identity); } if (DBG) { log("thermalMitigationRequest returning with thermalMitigationResult: " + thermalMitigationResult); } return thermalMitigationResult; } /** * Set the GbaService Package Name that Telephony will bind to. * * @param subId The sim that the GbaService is associated with. * @param packageName The name of the package to be replaced with. * @return true if setting the GbaService to bind to succeeded, false if it did not. */ @Override public boolean setBoundGbaServiceOverride(int subId, String packageName) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { return getGbaManager(subId).overrideServicePackage(packageName); } finally { Binder.restoreCallingIdentity(identity); } } /** * Return the package name of the currently bound GbaService. * * @param subId The sim that the GbaService is associated with. * @return the package name of the GbaService configuration, null if GBA is not supported. */ @Override public String getBoundGbaService(int subId) { enforceReadPrivilegedPermission("getBoundGbaServicePackage"); final long identity = Binder.clearCallingIdentity(); try { return getGbaManager(subId).getServicePackage(); } finally { Binder.restoreCallingIdentity(identity); } } /** * Set the release time for telephony to unbind GbaService. * * @param subId The sim that the GbaService is associated with. * @param interval The release time to unbind GbaService by millisecond. * @return true if setting the GbaService to bind to succeeded, false if it did not. */ @Override public boolean setGbaReleaseTimeOverride(int subId, int interval) { enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { return getGbaManager(subId).overrideReleaseTime(interval); } finally { Binder.restoreCallingIdentity(identity); } } /** * Return the release time for telephony to unbind GbaService. * * @param subId The sim that the GbaService is associated with. * @return The release time to unbind GbaService by millisecond. */ @Override public int getGbaReleaseTime(int subId) { enforceReadPrivilegedPermission("getGbaReleaseTime"); final long identity = Binder.clearCallingIdentity(); try { return getGbaManager(subId).getReleaseTime(); } finally { Binder.restoreCallingIdentity(identity); } } private GbaManager getGbaManager(int subId) { GbaManager instance = GbaManager.getInstance(subId); if (instance == null) { String packageName = mApp.getResources().getString(R.string.config_gba_package); int releaseTime = mApp.getResources().getInteger(R.integer.config_gba_release_time); instance = GbaManager.make(mApp, subId, packageName, releaseTime); } return instance; } /** * indicate whether the device and the carrier can support * RCS VoLTE single registration. */ @Override public boolean isRcsVolteSingleRegistrationCapable(int subId) { TelephonyPermissions.enforceAnyPermissionGrantedOrCarrierPrivileges(mApp, subId, Binder.getCallingUid(), "isRcsVolteSingleRegistrationCapable", Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION, permission.READ_PRIVILEGED_PHONE_STATE); if (!SubscriptionManager.isValidSubscriptionId(subId)) { throw new IllegalArgumentException("Invalid Subscription ID: " + subId); } final long identity = Binder.clearCallingIdentity(); try { RcsProvisioningMonitor rpm = RcsProvisioningMonitor.getInstance(); if (rpm != null) { Boolean isCapable = rpm.isRcsVolteSingleRegistrationEnabled(subId); if (isCapable != null) { return isCapable; } } throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE, "service is temporarily unavailable."); } finally { Binder.restoreCallingIdentity(identity); } } /** * Register RCS provisioning callback. */ @Override public void registerRcsProvisioningCallback(int subId, IRcsConfigCallback callback) { TelephonyPermissions.enforceAnyPermissionGrantedOrCarrierPrivileges(mApp, subId, Binder.getCallingUid(), "registerRcsProvisioningCallback", Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION, permission.READ_PRIVILEGED_PHONE_STATE); if (!SubscriptionManager.isValidSubscriptionId(subId)) { throw new IllegalArgumentException("Invalid Subscription ID: " + subId); } if (!isImsAvailableOnDevice()) { throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION, "IMS not available on device."); } final long identity = Binder.clearCallingIdentity(); try { if (!RcsProvisioningMonitor.getInstance() .registerRcsProvisioningCallback(subId, callback)) { throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION, "Active subscription not found."); } } finally { Binder.restoreCallingIdentity(identity); } } /** * Unregister RCS provisioning callback. */ @Override public void unregisterRcsProvisioningCallback(int subId, IRcsConfigCallback callback) { TelephonyPermissions.enforceAnyPermissionGrantedOrCarrierPrivileges(mApp, subId, Binder.getCallingUid(), "unregisterRcsProvisioningCallback", Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION, permission.READ_PRIVILEGED_PHONE_STATE); if (!SubscriptionManager.isValidSubscriptionId(subId)) { throw new IllegalArgumentException("Invalid Subscription ID: " + subId); } if (!isImsAvailableOnDevice()) { throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION, "IMS not available on device."); } final long identity = Binder.clearCallingIdentity(); try { RcsProvisioningMonitor.getInstance() .unregisterRcsProvisioningCallback(subId, callback); } finally { Binder.restoreCallingIdentity(identity); } } /** * trigger RCS reconfiguration. */ public void triggerRcsReconfiguration(int subId) { TelephonyPermissions.enforceAnyPermissionGranted(mApp, Binder.getCallingUid(), "triggerRcsReconfiguration", Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION); if (!SubscriptionManager.isValidSubscriptionId(subId)) { throw new IllegalArgumentException("Invalid Subscription ID: " + subId); } if (!isImsAvailableOnDevice()) { throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION, "IMS not available on device."); } final long identity = Binder.clearCallingIdentity(); try { RcsProvisioningMonitor.getInstance().requestReconfig(subId); } finally { Binder.restoreCallingIdentity(identity); } } /** * Provide the client configuration parameters of the RCS application. */ public void setRcsClientConfiguration(int subId, RcsClientConfiguration rcc) { TelephonyPermissions.enforceAnyPermissionGranted(mApp, Binder.getCallingUid(), "setRcsClientConfiguration", Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION); if (!SubscriptionManager.isValidSubscriptionId(subId)) { throw new IllegalArgumentException("Invalid Subscription ID: " + subId); } if (!isImsAvailableOnDevice()) { throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION, "IMS not available on device."); } final long identity = Binder.clearCallingIdentity(); try { IImsConfig configBinder = getImsConfig(getSlotIndex(subId), ImsFeature.FEATURE_RCS); if (configBinder == null) { Rlog.e(LOG_TAG, "null result for setRcsClientConfiguration"); throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION, "could not find the requested subscription"); } else { configBinder.setRcsClientConfiguration(rcc); } RcsStats.getInstance().onRcsClientProvisioningStats(subId, RCS_CLIENT_PROVISIONING_STATS__EVENT__CLIENT_PARAMS_SENT); } catch (RemoteException e) { Rlog.e(LOG_TAG, "fail to setRcsClientConfiguration " + e.getMessage()); throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE, "service is temporarily unavailable."); } finally { Binder.restoreCallingIdentity(identity); } } /** * Enables or disables the test mode for RCS VoLTE single registration. */ @Override public void setRcsSingleRegistrationTestModeEnabled(boolean enabled) { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setRcsSingleRegistrationTestModeEnabled"); RcsProvisioningMonitor.getInstance().setTestModeEnabled(enabled); } /** * Gets the test mode for RCS VoLTE single registration. */ @Override public boolean getRcsSingleRegistrationTestModeEnabled() { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getRcsSingleRegistrationTestModeEnabled"); return RcsProvisioningMonitor.getInstance().getTestModeEnabled(); } /** * Overrides the config of RCS VoLTE single registration enabled for the device. */ @Override public void setDeviceSingleRegistrationEnabledOverride(String enabledStr) { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setDeviceSingleRegistrationEnabledOverride"); enforceModifyPermission(); Boolean enabled = "NULL".equalsIgnoreCase(enabledStr) ? null : Boolean.parseBoolean(enabledStr); RcsProvisioningMonitor.getInstance().overrideDeviceSingleRegistrationEnabled(enabled); mApp.imsRcsController.setDeviceSingleRegistrationSupportOverride(enabled); } /** * Sends a device to device communication message. Only usable via shell. * @param message message to send. * @param value message value. */ @Override public void sendDeviceToDeviceMessage(int message, int value) { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "sendDeviceToDeviceMessage"); enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { TelephonyConnectionService service = TelecomAccountRegistry.getInstance(null).getTelephonyConnectionService(); if (service == null) { Rlog.e(LOG_TAG, "sendDeviceToDeviceMessage: not in a call."); return; } service.sendTestDeviceToDeviceMessage(message, value); } finally { Binder.restoreCallingIdentity(identity); } } /** * Sets the specified device to device transport active. * @param transport The transport to set active. */ @Override public void setActiveDeviceToDeviceTransport(@NonNull String transport) { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setActiveDeviceToDeviceTransport"); enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { TelephonyConnectionService service = TelecomAccountRegistry.getInstance(null).getTelephonyConnectionService(); if (service == null) { Rlog.e(LOG_TAG, "setActiveDeviceToDeviceTransport: not in a call."); return; } service.setActiveDeviceToDeviceTransport(transport); } finally { Binder.restoreCallingIdentity(identity); } } @Override public void setDeviceToDeviceForceEnabled(boolean isForceEnabled) { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setDeviceToDeviceForceEnabled"); final long identity = Binder.clearCallingIdentity(); try { Arrays.stream(PhoneFactory.getPhones()).forEach( p -> { Phone thePhone = p.getImsPhone(); if (thePhone != null && thePhone instanceof ImsPhone) { ImsPhone imsPhone = (ImsPhone) thePhone; CallTracker tracker = imsPhone.getCallTracker(); if (tracker != null && tracker instanceof ImsPhoneCallTracker) { ImsPhoneCallTracker imsPhoneCallTracker = (ImsPhoneCallTracker) tracker; imsPhoneCallTracker.setDeviceToDeviceForceEnabled(isForceEnabled); } } } ); } finally { Binder.restoreCallingIdentity(identity); } } /** * Gets the config of RCS VoLTE single registration enabled for the device. */ @Override public boolean getDeviceSingleRegistrationEnabled() { enforceReadPrivilegedPermission("getDeviceSingleRegistrationEnabled"); return RcsProvisioningMonitor.getInstance().getDeviceSingleRegistrationEnabled(); } /** * Overrides the config of RCS VoLTE single registration enabled for the carrier/subscription. */ @Override public boolean setCarrierSingleRegistrationEnabledOverride(int subId, String enabledStr) { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setCarrierSingleRegistrationEnabledOverride"); enforceModifyPermission(); Boolean enabled = "NULL".equalsIgnoreCase(enabledStr) ? null : Boolean.parseBoolean(enabledStr); return RcsProvisioningMonitor.getInstance().overrideCarrierSingleRegistrationEnabled( subId, enabled); } /** * Gets the config of RCS VoLTE single registration enabled for the carrier/subscription. */ @Override public boolean getCarrierSingleRegistrationEnabled(int subId) { enforceReadPrivilegedPermission("getCarrierSingleRegistrationEnabled"); return RcsProvisioningMonitor.getInstance().getCarrierSingleRegistrationEnabled(subId); } /** * Overrides the ims feature validation result */ @Override public boolean setImsFeatureValidationOverride(int subId, String enabledStr) { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setImsFeatureValidationOverride"); Boolean enabled = "NULL".equalsIgnoreCase(enabledStr) ? null : Boolean.parseBoolean(enabledStr); return RcsProvisioningMonitor.getInstance().overrideImsFeatureValidation( subId, enabled); } /** * Gets the ims feature validation override value */ @Override public boolean getImsFeatureValidationOverride(int subId) { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getImsFeatureValidationOverride"); return RcsProvisioningMonitor.getInstance().getImsFeatureValidationOverride(subId); } /** * Get the mobile provisioning url that is used to launch a browser to allow users to manage * their mobile plan. */ @Override public String getMobileProvisioningUrl() { enforceReadPrivilegedPermission("getMobileProvisioningUrl"); final long identity = Binder.clearCallingIdentity(); try { return getDefaultPhone().getMobileProvisioningUrl(); } finally { Binder.restoreCallingIdentity(identity); } } /** * Get the EAB contact from the EAB database. */ @Override public String getContactFromEab(String contact) { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getContactFromEab"); enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { return EabUtil.getContactFromEab(getDefaultPhone().getContext(), contact); } finally { Binder.restoreCallingIdentity(identity); } } /** * Get the EAB capability from the EAB database. */ @Override public String getCapabilityFromEab(String contact) { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getCapabilityFromEab"); enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { return EabUtil.getCapabilityFromEab(getDefaultPhone().getContext(), contact); } finally { Binder.restoreCallingIdentity(identity); } } /** * Remove the EAB contacts from the EAB database. */ @Override public int removeContactFromEab(int subId, String contacts) { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "removeCapabilitiesFromEab"); enforceModifyPermission(); final long identity = Binder.clearCallingIdentity(); try { return EabUtil.removeContactFromEab(subId, contacts, getDefaultPhone().getContext()); } finally { Binder.restoreCallingIdentity(identity); } } @Override public boolean getDeviceUceEnabled() { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getDeviceUceEnabled"); final long identity = Binder.clearCallingIdentity(); try { return mApp.getDeviceUceEnabled(); } finally { Binder.restoreCallingIdentity(identity); } } @Override public void setDeviceUceEnabled(boolean isEnabled) { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setDeviceUceEnabled"); final long identity = Binder.clearCallingIdentity(); try { mApp.setDeviceUceEnabled(isEnabled); } finally { Binder.restoreCallingIdentity(identity); } } /** * Add new feature tags to the Set used to calculate the capabilities in PUBLISH. * @return current RcsContactUceCapability instance that will be used for PUBLISH. */ // Used for SHELL command only right now. @Override public RcsContactUceCapability addUceRegistrationOverrideShell(int subId, List featureTags) { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "addUceRegistrationOverrideShell"); final long identity = Binder.clearCallingIdentity(); try { return mApp.imsRcsController.addUceRegistrationOverrideShell(subId, new ArraySet<>(featureTags)); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode(), e.getMessage()); } finally { Binder.restoreCallingIdentity(identity); } } /** * Remove existing feature tags to the Set used to calculate the capabilities in PUBLISH. * @return current RcsContactUceCapability instance that will be used for PUBLISH. */ // Used for SHELL command only right now. @Override public RcsContactUceCapability removeUceRegistrationOverrideShell(int subId, List featureTags) { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "removeUceRegistrationOverrideShell"); final long identity = Binder.clearCallingIdentity(); try { return mApp.imsRcsController.removeUceRegistrationOverrideShell(subId, new ArraySet<>(featureTags)); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode(), e.getMessage()); } finally { Binder.restoreCallingIdentity(identity); } } /** * Clear all overrides in the Set used to calculate the capabilities in PUBLISH. * @return current RcsContactUceCapability instance that will be used for PUBLISH. */ // Used for SHELL command only right now. @Override public RcsContactUceCapability clearUceRegistrationOverrideShell(int subId) { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "clearUceRegistrationOverrideShell"); final long identity = Binder.clearCallingIdentity(); try { return mApp.imsRcsController.clearUceRegistrationOverrideShell(subId); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode(), e.getMessage()); } finally { Binder.restoreCallingIdentity(identity); } } /** * @return current RcsContactUceCapability instance that will be used for PUBLISH. */ // Used for SHELL command only right now. @Override public RcsContactUceCapability getLatestRcsContactUceCapabilityShell(int subId) { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getLatestRcsContactUceCapabilityShell"); final long identity = Binder.clearCallingIdentity(); try { return mApp.imsRcsController.getLatestRcsContactUceCapabilityShell(subId); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode(), e.getMessage()); } finally { Binder.restoreCallingIdentity(identity); } } /** * Returns the last PIDF XML sent to the network during the last PUBLISH or "none" if the * device does not have an active PUBLISH. */ // Used for SHELL command only right now. @Override public String getLastUcePidfXmlShell(int subId) { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "uceGetLastPidfXml"); final long identity = Binder.clearCallingIdentity(); try { return mApp.imsRcsController.getLastUcePidfXmlShell(subId); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode(), e.getMessage()); } finally { Binder.restoreCallingIdentity(identity); } } /** * Remove UCE requests cannot be sent to the network status. */ // Used for SHELL command only right now. @Override public boolean removeUceRequestDisallowedStatus(int subId) { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "uceRemoveDisallowedStatus"); final long identity = Binder.clearCallingIdentity(); try { return mApp.imsRcsController.removeUceRequestDisallowedStatus(subId); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode(), e.getMessage()); } finally { Binder.restoreCallingIdentity(identity); } } /** * Remove UCE requests cannot be sent to the network status. */ // Used for SHELL command only. @Override public boolean setCapabilitiesRequestTimeout(int subId, long timeoutAfterMs) { TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setCapRequestTimeout"); final long identity = Binder.clearCallingIdentity(); try { return mApp.imsRcsController.setCapabilitiesRequestTimeout(subId, timeoutAfterMs); } catch (ImsException e) { throw new ServiceSpecificException(e.getCode(), e.getMessage()); } finally { Binder.restoreCallingIdentity(identity); } } @Override public void setSignalStrengthUpdateRequest(int subId, SignalStrengthUpdateRequest request, String callingPackage) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "setSignalStrengthUpdateRequest"); final int callingUid = Binder.getCallingUid(); // Verify that tha callingPackage belongs to the calling UID mApp.getSystemService(AppOpsManager.class) .checkPackage(callingUid, callingPackage); validateSignalStrengthUpdateRequest(request, callingUid); final long identity = Binder.clearCallingIdentity(); try { Object result = sendRequest(CMD_SET_SIGNAL_STRENGTH_UPDATE_REQUEST, new Pair(callingUid, request), subId); if (result instanceof IllegalStateException) { throw (IllegalStateException) result; } } finally { Binder.restoreCallingIdentity(identity); } } @Override public void clearSignalStrengthUpdateRequest(int subId, SignalStrengthUpdateRequest request, String callingPackage) { TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege( mApp, subId, "clearSignalStrengthUpdateRequest"); final int callingUid = Binder.getCallingUid(); // Verify that tha callingPackage belongs to the calling UID mApp.getSystemService(AppOpsManager.class) .checkPackage(callingUid, callingPackage); final long identity = Binder.clearCallingIdentity(); try { Object result = sendRequest(CMD_CLEAR_SIGNAL_STRENGTH_UPDATE_REQUEST, new Pair(callingUid, request), subId); if (result instanceof IllegalStateException) { throw (IllegalStateException) result; } } finally { Binder.restoreCallingIdentity(identity); } } private static void validateSignalStrengthUpdateRequest(SignalStrengthUpdateRequest request, int callingUid) { if (callingUid == Process.PHONE_UID || callingUid == Process.SYSTEM_UID) { // phone/system process do not have further restriction on request return; } // Applications has restrictions on how to use the request: // Only system caller can set mIsSystemThresholdReportingRequestedWhileIdle if (request.isSystemThresholdReportingRequestedWhileIdle()) { // This is not system caller which has been checked above throw new IllegalArgumentException( "Only system can set isSystemThresholdReportingRequestedWhileIdle"); } for (SignalThresholdInfo info : request.getSignalThresholdInfos()) { // Only system caller can set mHysteresisMs/mHysteresisDb/mIsEnabled. if (info.getHysteresisMs() != SignalThresholdInfo.HYSTERESIS_MS_DISABLED || info.getHysteresisDb() != SignalThresholdInfo.HYSTERESIS_DB_DISABLED || info.isEnabled()) { throw new IllegalArgumentException( "Only system can set hide fields in SignalThresholdInfo"); } // Thresholds length for each RAN need in range. This has been validated in // SignalThresholdInfo#Builder#setThreshold. Here we prevent apps calling hide method // setThresholdUnlimited (e.g. through reflection) with too short or too long thresholds final int[] thresholds = info.getThresholds(); Objects.requireNonNull(thresholds); if (thresholds.length < SignalThresholdInfo.getMinimumNumberOfThresholdsAllowed() || thresholds.length > SignalThresholdInfo.getMaximumNumberOfThresholdsAllowed()) { throw new IllegalArgumentException( "thresholds length is out of range: " + thresholds.length); } } } /** * Gets the current phone capability. * * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission. * @return the PhoneCapability which describes the data connection capability of modem. * It's used to evaluate possible phone config change, for example from single * SIM device to multi-SIM device. */ @Override public PhoneCapability getPhoneCapability() { enforceReadPrivilegedPermission("getPhoneCapability"); final long identity = Binder.clearCallingIdentity(); try { return mPhoneConfigurationManager.getCurrentPhoneCapability(); } finally { Binder.restoreCallingIdentity(identity); } } /** * Prepare TelephonyManager for an unattended reboot. The reboot is * required to be done shortly after the API is invoked. */ @Override @TelephonyManager.PrepareUnattendedRebootResult public int prepareForUnattendedReboot() { WorkSource workSource = getWorkSource(Binder.getCallingUid()); enforceRebootPermission(); final long identity = Binder.clearCallingIdentity(); try { return (int) sendRequest(CMD_PREPARE_UNATTENDED_REBOOT, null, workSource); } finally { Binder.restoreCallingIdentity(identity); } } /** * Request to get the current slicing configuration including URSP rules and * NSSAIs (configured, allowed and rejected). * * Requires carrier privileges or READ_PRIVILEGED_PHONE_STATE permission. */ @Override public void getSlicingConfig(ResultReceiver callback) { enforceReadPrivilegedPermission("getSlicingConfig"); final long identity = Binder.clearCallingIdentity(); try { Phone phone = getDefaultPhone(); sendRequestAsync(CMD_GET_SLICING_CONFIG, callback, phone, null); } finally { Binder.restoreCallingIdentity(identity); } } }