blob: 13accc971d214ef207edcf66b9caf099341ea725 [file] [log] [blame]
/*
* 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.SUBSCRIPTION_KEY;
import android.Manifest.permission;
import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.NetworkStats;
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.PersistableBundle;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.WorkSource;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.CarrierConfigManager;
import android.telephony.CarrierRestrictionRules;
import android.telephony.CellInfo;
import android.telephony.CellInfoGsm;
import android.telephony.CellInfoWcdma;
import android.telephony.CellLocation;
import android.telephony.ClientRequestStats;
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.PhoneNumberRange;
import android.telephony.RadioAccessFamily;
import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SmsManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyHistogram;
import android.telephony.TelephonyManager;
import android.telephony.UiccCardInfo;
import android.telephony.UiccSlotInfo;
import android.telephony.UssdResponse;
import android.telephony.VisualVoicemailSmsFilterSettings;
import android.telephony.cdma.CdmaCellLocation;
import android.telephony.data.ApnSetting;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.gsm.GsmCellLocation;
import android.telephony.ims.ProvisioningManager;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsConfigCallback;
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsConfigImplBase;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import com.android.ims.ImsException;
import com.android.ims.ImsManager;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.telephony.CallManager;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.CarrierInfoManager;
import com.android.internal.telephony.CarrierResolver;
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.DefaultPhoneNotifier;
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.MccTable;
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.ServiceStateTracker;
import com.android.internal.telephony.SmsApplication;
import com.android.internal.telephony.SmsApplication.SmsApplicationData;
import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.TelephonyPermissions;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import com.android.internal.telephony.euicc.EuiccConnector;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.uicc.IccIoResult;
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.VoicemailNotificationSettingsUtil;
import com.android.internal.util.HexDump;
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 java.io.FileDescriptor;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
/**
* 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_PREFERRED_NETWORK_TYPE = 21;
private static final int EVENT_GET_PREFERRED_NETWORK_TYPE_DONE = 22;
private static final int CMD_SET_PREFERRED_NETWORK_TYPE = 23;
private static final int EVENT_SET_PREFERRED_NETWORK_TYPE_DONE = 24;
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;
// 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;
private static final String DEFAULT_NETWORK_MODE_PROPERTY_NAME = "ro.telephony.default_network";
private static final String DEFAULT_DATA_ROAMING_PROPERTY_NAME = "ro.com.android.dataroaming";
private static final String DEFAULT_MOBILE_DATA_PROPERTY_NAME = "ro.com.android.mobiledata";
/** The singleton instance. */
private static PhoneInterfaceManager sInstance;
private PhoneGlobals mApp;
private CallManager mCM;
private UserManager mUserManager;
private AppOpsManager mAppOps;
private MainThreadHandler mMainThreadHandler;
private SubscriptionController mSubscriptionController;
private SharedPreferences mTelephonySharedPreferences;
private PhoneConfigurationManager mPhoneConfigurationManager;
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;
/**
* 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.
*
* <p>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<String, ResultReceiver> 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<String, Integer> openChannelArgs = (Pair<String, Integer>) 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<Integer, String> idValue = (Pair<Integer, String>) 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_GET_PREFERRED_NETWORK_TYPE:
request = (MainThreadRequest) msg.obj;
onCompleted = obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE_DONE, request);
getPhoneFromRequest(request).getPreferredNetworkType(onCompleted);
break;
case EVENT_GET_PREFERRED_NETWORK_TYPE_DONE:
ar = (AsyncResult) msg.obj;
request = (MainThreadRequest) ar.userObj;
if (ar.exception == null && ar.result != null) {
request.result = ar.result; // Integer
} else {
request.result = null;
if (ar.result == null) {
loge("getPreferredNetworkType: Empty response");
} else if (ar.exception instanceof CommandException) {
loge("getPreferredNetworkType: CommandException: " +
ar.exception);
} else {
loge("getPreferredNetworkType: Unknown exception");
}
}
notifyRequester(request);
break;
case CMD_SET_PREFERRED_NETWORK_TYPE:
request = (MainThreadRequest) msg.obj;
onCompleted = obtainMessage(EVENT_SET_PREFERRED_NETWORK_TYPE_DONE, request);
int networkType = (Integer) request.argument;
getPhoneFromRequest(request).setPreferredNetworkType(networkType, onCompleted);
break;
case EVENT_SET_PREFERRED_NETWORK_TYPE_DONE:
handleNullReturnEvent(msg, "setPreferredNetworkType");
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<String, String> tagNum = (Pair<String, String>) 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 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<OperatorInfo>) 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);
defaultPhone.getModemActivityInfo(onCompleted, request.workSource);
break;
case EVENT_GET_MODEM_ACTIVITY_INFO_DONE:
ar = (AsyncResult) msg.obj;
request = (MainThreadRequest) ar.userObj;
if (ar.exception == null && ar.result != null) {
request.result = ar.result;
} else {
if (ar.result == null) {
loge("queryModemActivityInfo: Empty response");
} else if (ar.exception instanceof CommandException) {
loge("queryModemActivityInfo: CommandException: " +
ar.exception);
} else {
loge("queryModemActivityInfo: Unknown exception");
}
}
// Result cannot be null. Return ModemActivityInfo with all fields set to 0.
if (request.result == null) {
request.result = new ModemActivityInfo(0, 0, 0, null, 0, 0);
}
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_SET_CDMA_SUBSCRIPTION_MODE:
request = (MainThreadRequest) msg.obj;
onCompleted = obtainMessage(EVENT_SET_CDMA_SUBSCRIPTION_MODE_DONE, request);
int subscriptionMode = (int) request.argument;
getPhoneFromRequest(request).setCdmaSubscription(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<CellInfo>();
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,
new android.os.ParcelableException(ar.exception));
} else if (ar.result == null) {
Log.w(LOG_TAG, "Timeout Waiting for CellInfo!");
cb.onError(TelephonyManager.CellInfoCallback.ERROR_TIMEOUT, null);
} else {
// use the result as returned
cb.onCellInfo((List<CellInfo>) 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.getCellLocation(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 = getPhoneFromRequest(request);
request.result = (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA)
? new CdmaCellLocation() : new GsmCellLocation();
}
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);
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);
updateModemStateMetrics();
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);
}
/**
* 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);
}
/**
* 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);
}
/**
* 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);
}
/**
* 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);
}
/**
* 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, Phone phone, WorkSource workSource) {
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();
// Wait for the request to complete
synchronized (request) {
while (request.result == null) {
try {
request.wait();
} catch (InterruptedException e) {
// Do nothing, go back and wait until the request is complete
}
}
}
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;
mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
mMainThreadHandler = new MainThreadHandler();
mSubscriptionController = SubscriptionController.getInstance();
mTelephonySharedPreferences =
PreferenceManager.getDefaultSharedPreferences(mApp);
mNetworkScanRequestTracker = new NetworkScanRequestTracker();
mPhoneConfigurationManager = PhoneConfigurationManager.getInstance();
publish();
}
private Phone getDefaultPhone() {
Phone thePhone = getPhone(getDefaultSubscription());
return (thePhone != null) ? thePhone : PhoneFactory.getDefaultPhone();
}
private void publish() {
if (DBG) log("publish: " + this);
ServiceManager.addService("phone", this);
}
private Phone getPhoneFromRequest(MainThreadRequest request) {
return (request.subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID)
? getDefaultPhone() : getPhone(request.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));
}
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.OP_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<SubscriptionInfo> 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 supplyPin(String pin) {
return supplyPinForSubscriber(getDefaultSubscription(), pin);
}
public boolean supplyPinForSubscriber(int subId, String pin) {
int [] resultArray = supplyPinReportResultForSubscriber(subId, pin);
return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
}
public boolean supplyPuk(String puk, String pin) {
return supplyPukForSubscriber(getDefaultSubscription(), puk, pin);
}
public boolean supplyPukForSubscriber(int subId, String puk, String pin) {
int [] resultArray = supplyPukReportResultForSubscriber(subId, puk, pin);
return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
}
/** {@hide} */
public int[] supplyPinReportResult(String pin) {
return supplyPinReportResultForSubscriber(getDefaultSubscription(), pin);
}
public int[] supplyPinReportResultForSubscriber(int subId, String pin) {
enforceModifyPermission();
final long identity = Binder.clearCallingIdentity();
try {
final UnlockSim checkSimPin = new UnlockSim(getPhone(subId).getIccCard());
checkSimPin.start();
return checkSimPin.unlockSim(null, pin);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
/** {@hide} */
public int[] supplyPukReportResult(String puk, String pin) {
return supplyPukReportResultForSubscriber(getDefaultSubscription(), puk, pin);
}
public int[] supplyPukReportResultForSubscriber(int subId, String puk, String pin) {
enforceModifyPermission();
final long identity = Binder.clearCallingIdentity();
try {
final UnlockSim checkSimPuk = new UnlockSim(getPhone(subId).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 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;
public UnlockSim(IccCard simCard) {
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;
} 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;
return resultArray;
}
}
public void updateServiceLocation() {
updateServiceLocationForSubscriber(getDefaultSubscription());
}
public void updateServiceLocationForSubscriber(int subId) {
// No permission check needed here: this call is harmless, and it's
// needed for the ServiceState.requestStateUpdate() call (which is
// already intentionally exposed to 3rd parties.)
final long identity = Binder.clearCallingIdentity();
try {
final Phone phone = getPhone(subId);
if (phone != null) {
phone.updateServiceLocation();
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public boolean isRadioOn(String callingPackage) {
return isRadioOnForSubscriber(getDefaultSubscription(), callingPackage);
}
@Override
public boolean isRadioOnForSubscriber(int subId, String callingPackage) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
mApp, subId, callingPackage, "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() {
/*
* 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().setUserDataEnabled(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().setUserDataEnabled(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<String, ResultReceiver> 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);
}
}
public int getCallState() {
return getCallStateForSlot(getSlotForDefaultSubscription());
}
public int getCallStateForSlot(int slotIndex) {
final long identity = Binder.clearCallingIdentity();
try {
Phone phone = PhoneFactory.getPhone(slotIndex);
return phone == null ? TelephonyManager.CALL_STATE_IDLE :
PhoneConstantConversions.convertCallState(phone.getState());
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public int getDataState() {
final long identity = Binder.clearCallingIdentity();
try {
Phone phone = getPhone(mSubscriptionController.getDefaultDataSubId());
if (phone != null) {
return PhoneConstantConversions.convertDataState(phone.getDataConnectionState());
} else {
return PhoneConstantConversions.convertDataState(
PhoneConstants.DataState.DISCONNECTED);
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public int getDataActivity() {
final long identity = Binder.clearCallingIdentity();
try {
Phone phone = getPhone(mSubscriptionController.getDefaultDataSubId());
if (phone != null) {
return DefaultPhoneNotifier.convertDataActivityState(phone.getDataActivityState());
} else {
return TelephonyManager.DATA_ACTIVITY_NONE;
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public Bundle getCellLocation(String callingPackage) {
mApp.getSystemService(AppOpsManager.class)
.checkPackage(Binder.getCallingUid(), callingPackage);
LocationAccessPolicy.LocationPermissionResult locationResult =
LocationAccessPolicy.checkLocationPermission(mApp,
new LocationAccessPolicy.LocationPermissionQuery.Builder()
.setCallingPackage(callingPackage)
.setCallingPid(Binder.getCallingPid())
.setCallingUid(Binder.getCallingUid())
.setMethod("getCellLocation")
.setMinSdkVersionForFine(Build.VERSION_CODES.Q)
.build());
switch (locationResult) {
case DENIED_HARD:
throw new SecurityException("Not allowed to access cell location");
case DENIED_SOFT:
return new Bundle();
}
WorkSource workSource = getWorkSource(Binder.getCallingUid());
final long identity = Binder.clearCallingIdentity();
try {
if (DBG_LOC) log("getCellLocation: is active user");
Bundle data = new Bundle();
int subId = mSubscriptionController.getDefaultDataSubId();
CellLocation cl = (CellLocation) sendRequest(CMD_GET_CELL_LOCATION, workSource, subId);
cl.fillInNotifierBundle(data);
return data;
} 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);
// Todo: fix this when we can get the actual cellular network info when the device
// is on IWLAN.
if (TelephonyManager.NETWORK_TYPE_IWLAN
== getVoiceNetworkTypeForSubscriber(subId, mApp.getPackageName())) {
return "";
}
Phone phone = PhoneFactory.getPhone(phoneId);
if (phone != null) {
ServiceStateTracker sst = phone.getServiceStateTracker();
if (sst != null) {
LocaleTracker lt = sst.getLocaleTracker();
if (lt != null) {
return lt.getCurrentCountry();
}
}
}
return "";
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void enableLocationUpdates() {
enableLocationUpdatesForSubscriber(getDefaultSubscription());
}
@Override
public void enableLocationUpdatesForSubscriber(int subId) {
mApp.enforceCallingOrSelfPermission(
android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
final long identity = Binder.clearCallingIdentity();
try {
final Phone phone = getPhone(subId);
if (phone != null) {
phone.enableLocationUpdates();
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void disableLocationUpdates() {
disableLocationUpdatesForSubscriber(getDefaultSubscription());
}
@Override
public void disableLocationUpdatesForSubscriber(int subId) {
mApp.enforceCallingOrSelfPermission(
android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
final long identity = Binder.clearCallingIdentity();
try {
final Phone phone = getPhone(subId);
if (phone != null) {
phone.disableLocationUpdates();
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
/**
* Returns the target SDK version number for a given package name.
*
* @return target SDK if the package is found or INT_MAX.
*/
private int getTargetSdk(String packageName) {
try {
final ApplicationInfo ai = mApp.getPackageManager().getApplicationInfo(
packageName, 0);
if (ai != null) return ai.targetSdkVersion;
} catch (PackageManager.NameNotFoundException unexpected) {
}
return Integer.MAX_VALUE;
}
@Override
@SuppressWarnings("unchecked")
public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) {
final int targetSdk = getTargetSdk(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.OP_NEIGHBORING_CELLS, Binder.getCallingUid(),
callingPackage) != AppOpsManager.MODE_ALLOWED) {
return null;
}
if (DBG_LOC) log("getNeighboringCellInfo: is active user");
List<CellInfo> info = getAllCellInfo(callingPackage);
if (info == null) return null;
List<NeighboringCellInfo> neighbors = new ArrayList<NeighboringCellInfo>();
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<CellInfo> getCachedCellInfo() {
List<CellInfo> cellInfos = new ArrayList<CellInfo>();
for (Phone phone : PhoneFactory.getPhones()) {
List<CellInfo> info = phone.getAllCellInfo();
if (info != null) cellInfos.addAll(info);
}
return cellInfos;
}
@Override
public List<CellInfo> getAllCellInfo(String callingPackage) {
mApp.getSystemService(AppOpsManager.class)
.checkPackage(Binder.getCallingUid(), callingPackage);
LocationAccessPolicy.LocationPermissionResult locationResult =
LocationAccessPolicy.checkLocationPermission(mApp,
new LocationAccessPolicy.LocationPermissionQuery.Builder()
.setCallingPackage(callingPackage)
.setCallingPid(Binder.getCallingPid())
.setCallingUid(Binder.getCallingUid())
.setMethod("getAllCellInfo")
.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 = getTargetSdk(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<CellInfo> cellInfos = new ArrayList<CellInfo>();
for (Phone phone : PhoneFactory.getPhones()) {
final List<CellInfo> info = (List<CellInfo>) 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) {
requestCellInfoUpdateInternal(
subId, cb, callingPackage, getWorkSource(Binder.getCallingUid()));
}
@Override
public void requestCellInfoUpdateWithWorkSource(
int subId, ICellInfoCallback cb, String callingPackage, WorkSource workSource) {
enforceModifyPermission();
requestCellInfoUpdateInternal(subId, cb, callingPackage, workSource);
}
private void requestCellInfoUpdateInternal(
int subId, ICellInfoCallback cb, String callingPackage, WorkSource workSource) {
mApp.getSystemService(AppOpsManager.class)
.checkPackage(Binder.getCallingUid(), callingPackage);
LocationAccessPolicy.LocationPermissionResult locationResult =
LocationAccessPolicy.checkLocationPermission(mApp,
new LocationAccessPolicy.LocationPermissionQuery.Builder()
.setCallingPackage(callingPackage)
.setCallingPid(Binder.getCallingPid())
.setCallingUid(Binder.getCallingUid())
.setMethod("requestCellInfoUpdate")
.setMinSdkVersionForFine(Build.VERSION_CODES.Q)
.build());
switch (locationResult) {
case DENIED_HARD:
throw new SecurityException("Not allowed to access cell info");
case DENIED_SOFT:
return;
}
final Phone phone = getPhone(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) {
Phone phone = PhoneFactory.getPhone(slotIndex);
if (phone == null) {
return null;
}
int subId = phone.getSubId();
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
mApp, subId, callingPackage, "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) {
Phone phone = PhoneFactory.getPhone(slotIndex);
if (phone == null) {
return null;
}
int subId = phone.getSubId();
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
mApp, subId, callingPackage, "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) {
Phone phone = PhoneFactory.getPhone(slotIndex);
if (phone == null) {
return null;
}
int subId = phone.getSubId();
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
mApp, subId, callingPackage, "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 getSubscriptionPreciseCarrierId(int subId) {
final long identity = Binder.clearCallingIdentity();
try {
final Phone phone = getPhone(subId);
return phone == null ? TelephonyManager.UNKNOWN_CARRIER_ID
: phone.getPreciseCarrierId();
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public String getSubscriptionPreciseCarrierName(int subId) {
final long identity = Binder.clearCallingIdentity();
try {
final Phone phone = getPhone(subId);
return phone == null ? null : phone.getPreciseCarrierName();
} 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 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 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 enforceConnectivityInternalPermission() {
mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL,
"ConnectivityService");
}
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) {
return getCdmaEriIconIndexForSubscriber(getDefaultSubscription(), callingPackage);
}
@Override
public int getCdmaEriIconIndexForSubscriber(int subId, String callingPackage) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
mApp, subId, callingPackage, "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) {
return getCdmaEriIconModeForSubscriber(getDefaultSubscription(), callingPackage);
}
@Override
public int getCdmaEriIconModeForSubscriber(int subId, String callingPackage) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
mApp, subId, callingPackage, "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) {
return getCdmaEriTextForSubscriber(getDefaultSubscription(), callingPackage);
}
@Override
public String getCdmaEriTextForSubscriber(int subId, String callingPackage) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
mApp, subId, callingPackage, "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