/*
* 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 android.content.Intent;
import android.net.Uri;
import android.os.AsyncResult;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.telephony.NeighboringCellInfo;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.telephony.DefaultPhoneNotifier;
import com.android.internal.telephony.IccCard;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.CallManager;
import com.android.internal.telephony.PhoneFactory;
import java.util.List;
import java.util.ArrayList;
/**
* Implementation of the ITelephony interface.
*/
public class PhoneInterfaceManager extends ITelephony.Stub {
private static final String LOG_TAG = "PhoneInterfaceManager";
private static final boolean DBG = (PhoneApp.DBG_LEVEL >= 2);
// Message codes used with mMainThreadHandler
private static final int CMD_HANDLE_PIN_MMI = 1;
private static final int CMD_HANDLE_NEIGHBORING_CELL = 2;
private static final int EVENT_NEIGHBORING_CELL_DONE = 3;
private static final int CMD_ANSWER_RINGING_CALL = 4;
private static final int CMD_END_CALL = 5; // not used yet
private static final int CMD_SILENCE_RINGER = 6;
/** The singleton instance. */
private static PhoneInterfaceManager sInstance;
PhoneApp mApp;
Phone mPhone;
CallManager mCM;
MainThreadHandler mMainThreadHandler;
/**
* 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;
public MainThreadRequest(Object argument) {
this.argument = argument;
}
}
/**
* 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;
switch (msg.what) {
case CMD_HANDLE_PIN_MMI:
request = (MainThreadRequest) msg.obj;
request.result = Boolean.valueOf(
mPhone.handlePinMmi((String) request.argument));
// Wake up the requesting thread
synchronized (request) {
request.notifyAll();
}
break;
case CMD_HANDLE_NEIGHBORING_CELL:
request = (MainThreadRequest) msg.obj;
onCompleted = obtainMessage(EVENT_NEIGHBORING_CELL_DONE,
request);
mPhone.getNeighboringCids(onCompleted);
break;
case EVENT_NEIGHBORING_CELL_DONE:
ar = (AsyncResult) msg.obj;
request = (MainThreadRequest) ar.userObj;
if (ar.exception == null && ar.result != null) {
request.result = ar.result;
} else {
// create an empty list to notify the waiting thread
request.result = new ArrayList<NeighboringCellInfo>();
}
// Wake up the requesting thread
synchronized (request) {
request.notifyAll();
}
break;
case CMD_ANSWER_RINGING_CALL:
answerRingingCallInternal();
break;
case CMD_SILENCE_RINGER:
silenceRingerInternal();
break;
case CMD_END_CALL:
request = (MainThreadRequest) msg.obj;
boolean hungUp = false;
int phoneType = mPhone.getPhoneType();
if (phoneType == Phone.PHONE_TYPE_CDMA) {
// CDMA: If the user presses the Power button we treat it as
// ending the complete call session
hungUp = PhoneUtils.hangupRingingAndActive(mPhone);
} else if (phoneType == Phone.PHONE_TYPE_GSM) {
// GSM: End the call as per the Phone state
hungUp = PhoneUtils.hangup(mCM);
} else {
throw new IllegalStateException("Unexpected phone type: " + phoneType);
}
if (DBG) log("CMD_END_CALL: " + (hungUp ? "hung up!" : "no call to hang up"));
request.result = hungUp;
// Wake up the requesting thread
synchronized (request) {
request.notifyAll();
}
break;
default:
Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
break;
}
}
}
/**
* 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) {
if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
throw new RuntimeException("This method will deadlock if called from the main thread.");
}
MainThreadRequest request = new MainThreadRequest(argument);
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);
}
/**
* Initialize the singleton PhoneInterfaceManager instance.
* This is only done once, at startup, from PhoneApp.onCreate().
*/
/* package */ static PhoneInterfaceManager init(PhoneApp app, Phone phone) {
synchronized (PhoneInterfaceManager.class) {
if (sInstance == null) {
sInstance = new PhoneInterfaceManager(app, phone);
} else {
Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
}
return sInstance;
}
}
/** Private constructor; @see init() */
PhoneInterfaceManager(PhoneApp app, Phone phone) {
mApp = app;
mPhone = phone;
mCM = PhoneApp.getInstance().mCM;
mMainThreadHandler = new MainThreadHandler();
publish();
}
private void publish() {
String service = "phone";
int phoneId = mPhone.getPhoneId();
ServiceManager.addService(PhoneFactory.getServiceName(service, phoneId), this);
}
//
// Implementation of the ITelephony interface.
//
public void dial(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.
String url = createTelUrl(number);
if (url == null) {
return;
}
// PENDING: should we just silently fail if phone is offhook or ringing?
Phone.State state = mPhone.getState();
if (state != Phone.State.OFFHOOK && state != Phone.State.RINGING) {
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mApp.startActivity(intent);
}
}
public void call(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();
String url = createTelUrl(number);
if (url == null) {
return;
}
Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClassName(mApp, PhoneApp.getCallScreenClassName());
mApp.startActivity(intent);
}
private boolean showCallScreenInternal(boolean specifyInitialDialpadState,
boolean initialDialpadState) {
if (isIdle()) {
return false;
}
// If the phone isn't idle then go to the in-call screen
long callingId = Binder.clearCallingIdentity();
try {
Intent intent;
if (specifyInitialDialpadState) {
intent = PhoneApp.createInCallIntent(initialDialpadState);
} else {
intent = PhoneApp.createInCallIntent();
}
mApp.startActivity(intent);
} finally {
Binder.restoreCallingIdentity(callingId);
}
return true;
}
// Show the in-call screen without specifying the initial dialpad state.
public boolean showCallScreen() {
return showCallScreenInternal(false, false);
}
// The variation of showCallScreen() that specifies the initial dialpad state.
// (Ideally this would be called showCallScreen() too, just with a different
// signature, but AIDL doesn't allow that.)
public boolean showCallScreenWithDialpad(boolean showDialpad) {
return showCallScreenInternal(true, showDialpad);
}
/**
* End a call based on call state
* @return true is a call was ended
*/
public boolean endCall() {
enforceCallPermission();
return (Boolean) sendRequest(CMD_END_CALL, null);
}
public void holdCall() {
PhoneUtils.switchHoldingAndActive((mCM.getFirstActiveBgCall()));
}
public void answerRingingCall() {
if (DBG) log("answerRingingCall...");
// TODO: there should eventually be a separate "ANSWER_PHONE" permission,
// but that can probably wait till the big TelephonyManager API overhaul.
// For now, protect this call with the MODIFY_PHONE_STATE permission.
enforceModifyPermission();
sendRequestAsync(CMD_ANSWER_RINGING_CALL);
}
/**
* Make the actual telephony calls to implement answerRingingCall().
* This should only be called from the main thread of the Phone app.
* @see answerRingingCall
*
* TODO: it would be nice to return true if we answered the call, or
* false if there wasn't actually a ringing incoming call, or some
* other error occurred. (In other words, pass back the return value
* from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().)
* But that would require calling this method via sendRequest() rather
* than sendRequestAsync(), and right now we don't actually *need* that
* return value, so let's just return void for now.
*/
private void answerRingingCallInternal() {
final boolean hasRingingCall = !mPhone.getRingingCall().isIdle();
if (hasRingingCall) {
final boolean hasActiveCall = !mPhone.getForegroundCall().isIdle();
final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle();
if (hasActiveCall && hasHoldingCall) {
// Both lines are in use!
// TODO: provide a flag to let the caller specify what
// policy to use if both lines are in use. (The current
// behavior is hardwired to "answer incoming, end ongoing",
// which is how the CALL button is specced to behave.)
PhoneUtils.answerAndEndActive(mCM, mCM.getFirstActiveRingingCall());
return;
} else {
// answerCall() will automatically hold the current active
// call, if there is one.
PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
return;
}
} else {
// No call was ringing.
return;
}
}
public void silenceRinger() {
if (DBG) log("silenceRinger...");
// TODO: find a more appropriate permission to check here.
// (That can probably wait till the big TelephonyManager API overhaul.
// For now, protect this call with the MODIFY_PHONE_STATE permission.)
enforceModifyPermission();
sendRequestAsync(CMD_SILENCE_RINGER);
}
/**
* Internal implemenation of silenceRinger().
* This should only be called from the main thread of the Phone app.
* @see silenceRinger
*/
private void silenceRingerInternal() {
if ((mPhone.getState() == Phone.State.RINGING)
&& mApp.notifier.isRinging()) {
// Ringer is actually playing, so silence it.
if (DBG) log("silenceRingerInternal: silencing...");
PhoneUtils.setAudioControlState(PhoneUtils.AUDIO_IDLE);
mApp.notifier.silenceRinger();
}
}
public boolean isOffhook() {
return (mPhone.getState() == Phone.State.OFFHOOK);
}
public boolean isRinging() {
return (mPhone.getState() == Phone.State.RINGING);
}
public boolean isIdle() {
return (mPhone.getState() == Phone.State.IDLE);
}
public boolean isSimPinEnabled() {
enforceReadPermission();
return (PhoneApp.getInstance().isSimPinEnabled());
}
public boolean supplyPin(String pin) {
enforceModifyPermission();
final CheckSimPin checkSimPin = new CheckSimPin(mPhone.getIccCard());
checkSimPin.start();
return checkSimPin.checkPin(pin);
}
//PUK Input Add Start
public boolean supplyPuk(String puk,String pin) {
enforceModifyPermission();
final CheckSimPuk checkSimPuk = new CheckSimPuk(mPhone.getIccCard());
checkSimPuk.start();
return checkSimPuk.checkPuk(puk,pin);
}
/**
* Helper thread to turn async call to {@link SimCard#supplyPin} into
* a synchronous one.
*/
private static class CheckSimPuk extends Thread {
private final IccCard mSimCard;
private boolean mDone = false;
private boolean mResult = false;
// For replies from SimCard interface
private Handler mHandler;
// For async handler to identify request type
private static final int SUPPLY_PUK_COMPLETE = 100;
public CheckSimPuk(IccCard simCard) {
mSimCard = simCard;
}
@Override
public void run() {
Looper.prepare();
synchronized (CheckSimPuk.this) {
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
AsyncResult ar = (AsyncResult) msg.obj;
switch (msg.what) {
case SUPPLY_PUK_COMPLETE:
Log.d(LOG_TAG, "SUPPLY_PUK_COMPLETE");
synchronized (CheckSimPuk.this) {
mResult = (ar.exception == null);
mDone = true;
CheckSimPuk.this.notifyAll();
}
break;
}
}
};
CheckSimPuk.this.notifyAll();
}
Looper.loop();
}
synchronized boolean checkPuk(String puk,String pin) {
while (mHandler == null) {
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
Message callback = Message.obtain(mHandler, SUPPLY_PUK_COMPLETE);
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");
return mResult;
}
}
//PUK Input Add End
/**
* Helper thread to turn async call to {@link SimCard#supplyPin} into
* a synchronous one.
*/
private static class CheckSimPin extends Thread {
private final IccCard mSimCard;
private boolean mDone = false;
private boolean mResult = false;
// For replies from SimCard interface
private Handler mHandler;
// For async handler to identify request type
private static final int SUPPLY_PIN_COMPLETE = 100;
public CheckSimPin(IccCard simCard) {
mSimCard = simCard;
}
@Override
public void run() {
Looper.prepare();
synchronized (CheckSimPin.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 (CheckSimPin.this) {
mResult = (ar.exception == null);
mDone = true;
CheckSimPin.this.notifyAll();
}
break;
}
}
};
CheckSimPin.this.notifyAll();
}
Looper.loop();
}
synchronized boolean checkPin(String pin) {
while (mHandler == null) {
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
Message callback = Message.obtain(mHandler, SUPPLY_PIN_COMPLETE);
mSimCard.supplyPin(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");
return mResult;
}
}
public String getSmsc() {
enforceModifyPermission();
final GetSetSMSC getSMSC = new GetSetSMSC(mPhone, null);
getSMSC.start();
return getSMSC.getSmsc();
}
public boolean setSmsc(String smscAddr) {
enforceModifyPermission();
final GetSetSMSC getSMSC = new GetSetSMSC(mPhone, smscAddr);
getSMSC.start();
return getSMSC.setSmsc();
}
private static class GetSetSMSC extends Thread {
private final Phone mPhone;
private final String mSmscStr;
private boolean mDone = false;
private String mResult;
private boolean bResult = false;
private Handler mHandler;
// For async handler to identify request type
private static final int QUERY_SMSC_DONE = 100;
private static final int UPDATE_SMSC_DONE = 101;
public GetSetSMSC(Phone phone, String SmscStr) {
mPhone = phone;
mSmscStr = SmscStr;
}
@Override
public void run() {
Looper.prepare();
synchronized (GetSetSMSC.this) {
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
AsyncResult ar = (AsyncResult) msg.obj;
switch (msg.what) {
case QUERY_SMSC_DONE:
Log.d(LOG_TAG, "[smsc]QUERY_SMSC_DONE");
synchronized (GetSetSMSC.this) {
if (ar.exception == null) {
mResult = (String)ar.result;
} else {
mResult = "";
}
mDone = true;
GetSetSMSC.this.notifyAll();
}
break;
case UPDATE_SMSC_DONE:
Log.d(LOG_TAG, "[smsc]UPDATE_SMSC_DONE");
synchronized (GetSetSMSC.this) {
bResult = (ar.exception == null);
mDone = true;
GetSetSMSC.this.notifyAll();
}
break;
}
}
};
GetSetSMSC.this.notifyAll();
}
Looper.loop();
}
synchronized String getSmsc() {
while (mHandler == null) {
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
mPhone.getSmscAddress(mHandler.obtainMessage(QUERY_SMSC_DONE));
while (!mDone) {
try {
Log.d(LOG_TAG, "[smsc]wait get for done");
wait();
} catch (InterruptedException e) {
// Restore the interrupted status
Thread.currentThread().interrupt();
}
}
Log.d(LOG_TAG, "[smsc]get done");
return mResult;
}
synchronized boolean setSmsc() {
while (mHandler == null) {
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
mPhone.setSmscAddress(mSmscStr, mHandler.obtainMessage(UPDATE_SMSC_DONE));
while (!mDone) {
try {
Log.d(LOG_TAG, "[smsc]wait set for done");
wait();
} catch (InterruptedException e) {
// Restore the interrupted status
Thread.currentThread().interrupt();
}
}
Log.d(LOG_TAG, "[smsc]set done. result = "+bResult);
return bResult;
}
}
public void updateServiceLocation() {
// 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.)
mPhone.updateServiceLocation();
}
public boolean isIccCardOn() {
return mPhone.getIccCard().isIccCardOn();
}
public boolean setIccCard(boolean turnOn) {
enforceModifyPermission();
mPhone.setIccCard(turnOn);
return true;
}
public boolean isRadioOn() {
return mPhone.getServiceState().getState() != ServiceState.STATE_POWER_OFF;
}
public void toggleRadioOnOff() {
enforceModifyPermission();
mPhone.setRadioPower(!isRadioOn());
}
public boolean setRadio(boolean turnOn) {
enforceModifyPermission();
if ((mPhone.getServiceState().getState() != ServiceState.STATE_POWER_OFF) != turnOn) {
toggleRadioOnOff();
}
return true;
}
public boolean enableDataConnectivity() {
enforceModifyPermission();
return mPhone.enableDataConnectivity();
}
public int enableApnType(String type) {
enforceModifyPermission();
return mPhone.enableApnType(type);
}
public int disableApnType(String type) {
enforceModifyPermission();
return mPhone.disableApnType(type);
}
public boolean disableDataConnectivity() {
enforceModifyPermission();
return mPhone.disableDataConnectivity();
}
public boolean isDataConnectivityPossible() {
return mPhone.isDataConnectivityPossible();
}
public boolean handlePinMmi(String dialString) {
enforceModifyPermission();
return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString);
}
public void cancelMissedCallsNotification() {
enforceModifyPermission();
mApp.notificationMgr.cancelMissedCallNotification();
}
public int getCallState() {
return DefaultPhoneNotifier.convertCallState(mPhone.getState());
}
public int getDataState() {
return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
}
public int getDataActivity() {
return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState());
}
public Bundle getCellLocation() {
try {
mApp.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_FINE_LOCATION, null);
} catch (SecurityException e) {
// If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
// A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
// is the weaker precondition
mApp.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
}
Bundle data = new Bundle();
mPhone.getCellLocation().fillInNotifierBundle(data);
return data;
}
public void enableLocationUpdates() {
mApp.enforceCallingOrSelfPermission(
android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
mPhone.enableLocationUpdates();
}
public void disableLocationUpdates() {
mApp.enforceCallingOrSelfPermission(
android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
mPhone.disableLocationUpdates();
}
@SuppressWarnings("unchecked")
public List<NeighboringCellInfo> getNeighboringCellInfo() {
try {
mApp.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_FINE_LOCATION, null);
} catch (SecurityException e) {
// If we have ACCESS_FINE_LOCATION permission, skip the check
// for ACCESS_COARSE_LOCATION
// A failure should throw the SecurityException from
// ACCESS_COARSE_LOCATION since this is the weaker precondition
mApp.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
}
ArrayList<NeighboringCellInfo> cells = null;
try {
cells = (ArrayList<NeighboringCellInfo>) sendRequest(
CMD_HANDLE_NEIGHBORING_CELL, null);
} catch (RuntimeException e) {
Log.e(LOG_TAG, "getNeighboringCellInfo " + e);
}
return (List <NeighboringCellInfo>) cells;
}
//
// Internal helper methods.
//
/**
* Make sure the caller has the READ_PHONE_STATE permission.
*
* @throws SecurityException if the caller does not have the required permission
*/
private void enforceReadPermission() {
mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, null);
}
/**
* 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 String createTelUrl(String number) {
if (TextUtils.isEmpty(number)) {
return null;
}
StringBuilder buf = new StringBuilder("tel:");
buf.append(number);
return buf.toString();
}
private void log(String msg) {
Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
}
public int getActivePhoneType() {
return mPhone.getPhoneType();
}
/**
* Returns the CDMA ERI icon index to display
*/
public int getCdmaEriIconIndex() {
return mPhone.getCdmaEriIconIndex();
}
/**
* Returns the CDMA ERI icon mode,
* 0 - ON
* 1 - FLASHING
*/
public int getCdmaEriIconMode() {
return mPhone.getCdmaEriIconMode();
}
/**
* Returns the CDMA ERI text,
*/
public String getCdmaEriText() {
return mPhone.getCdmaEriText();
}
/**
* Returns true if CDMA provisioning needs to run.
*/
public boolean getCdmaNeedsProvisioning() {
if (getActivePhoneType() == Phone.PHONE_TYPE_GSM) {
return false;
}
boolean needsProvisioning = false;
String cdmaMin = mPhone.getCdmaMin();
try {
needsProvisioning = OtaUtils.needsActivation(cdmaMin);
} catch (IllegalArgumentException e) {
// shouldn't get here unless hardware is misconfigured
Log.e(LOG_TAG, "CDMA MIN string " + ((cdmaMin == null) ? "was null" : "was too short"));
}
return needsProvisioning;
}
/**
* Returns the unread count of voicemails
*/
public int getVoiceMessageCount() {
return mPhone.getVoiceMessageCount();
}
/**
* Returns the network type
*/
public int getNetworkType() {
int radiotech = mPhone.getServiceState().getRadioTechnology();
switch(radiotech) {
case ServiceState.RADIO_TECHNOLOGY_GPRS:
return TelephonyManager.NETWORK_TYPE_GPRS;
case ServiceState.RADIO_TECHNOLOGY_EDGE:
return TelephonyManager.NETWORK_TYPE_EDGE;
case ServiceState.RADIO_TECHNOLOGY_UMTS:
return TelephonyManager.NETWORK_TYPE_UMTS;
case ServiceState.RADIO_TECHNOLOGY_HSDPA:
return TelephonyManager.NETWORK_TYPE_HSDPA;
case ServiceState.RADIO_TECHNOLOGY_HSUPA:
return TelephonyManager.NETWORK_TYPE_HSUPA;
case ServiceState.RADIO_TECHNOLOGY_HSPA:
return TelephonyManager.NETWORK_TYPE_HSPA;
case ServiceState.RADIO_TECHNOLOGY_IS95A:
case ServiceState.RADIO_TECHNOLOGY_IS95B:
return TelephonyManager.NETWORK_TYPE_CDMA;
case ServiceState.RADIO_TECHNOLOGY_1xRTT:
return TelephonyManager.NETWORK_TYPE_1xRTT;
case ServiceState.RADIO_TECHNOLOGY_EVDO_0:
return TelephonyManager.NETWORK_TYPE_EVDO_0;
case ServiceState.RADIO_TECHNOLOGY_EVDO_A:
return TelephonyManager.NETWORK_TYPE_EVDO_A;
case ServiceState.RADIO_TECHNOLOGY_EVDO_B:
return TelephonyManager.NETWORK_TYPE_EVDO_B;
default:
return TelephonyManager.NETWORK_TYPE_UNKNOWN;
}
}
/**
* @return true if a ICC card is present
*/
public boolean hasIccCard() {
return mPhone.getIccCard().hasIccCard();
}
/**
* @return true if a ICC card type is USIM/TD
*/
public boolean isUsimCard() {
return mPhone.getIccCard().isUsimCard();
}
/**
* @return true if a IccFdn enabled
*/
public boolean getIccFdnEnabled() {
return mPhone.getIccCard().getIccFdnEnabled();
}
/**
* Returns the array, String[0] - sres,String[1] - kc
*
* @hide
*/
public String[] Mbbms_Gsm_Authenticate(String nonce) {
String authen = mPhone.Mbbms_Gsm_Authenticate(nonce);
Log.d(LOG_TAG, "Mbbms_Gsm_Authenticate return:"+authen);
if (authen != null) {
String [] rep = new String[2];
String temp;
int offset = authen.indexOf(',',0);
int str_begin, str_end;
str_begin = offset + 1;
offset = authen.indexOf(',',str_begin);
if (authen.charAt(str_begin) == '\"') {
str_begin++;
}
if ((offset > 1) && (authen.charAt(offset - 1) == '\"')) {
str_end = offset - 1;
}
else {
str_end = offset;
}
temp = authen.substring(str_begin, str_end);
if (authen.charAt(offset + 1) == '\"') {
str_begin = offset + 1;
}
else {
str_begin = offset;
}
if (authen.charAt(authen.length() -1) == '\"'){
str_end = authen.length() -1;
}
else {
str_end = authen.length();
}
rep[0] = authen.substring(str_begin+1, str_end);
Log.d(LOG_TAG, "Mbbms_Gsm_Authenticate resp:"+rep[0]);
rep[1] = temp;
Log.d(LOG_TAG, "Mbbms_Gsm_Authenticate resp:"+rep[1]);
return rep;
}
else {
return null;
}
}
/**
* Returns the array, String[0], "1" -need GBA recynchronization, "0" - succeed
* String[1] - res, String[2] -ck, String[3] - ik
*
* @hide
*/
public String[] Mbbms_USim_Authenticate(String nonce, String autn) {
String authen = mPhone.Mbbms_USim_Authenticate(nonce, autn);
Log.d(LOG_TAG, "Mbbms_USim_Authenticate return:"+authen);
if (authen != null) {
String [] rep = new String[4];
int offset = 0;
int str_begin = 0, str_end = 0;
//int testlen = authen1.indexOf(',',3);
//String authen = authen1.substring(0, testlen);
int len = authen.length();
//Log.d(LOG_TAG, "Mbbms_USim_Authenticate return:"+authen);
for (int i = 0; i < 2; i++) {
str_begin = offset;
offset = authen.indexOf(',',offset);
str_end = offset;
if ((offset < 0) && (str_begin >= len)) break;
if (offset < 0){
offset = len;
str_end = len;
}
if (i == 1) {
if (authen.charAt(str_begin) == '\"'){
str_begin++;
}
if (authen.charAt(str_end -1) == '\"') {
str_end--;
}
rep[i] = authen.substring(str_begin, str_end);
}
else {
rep[i] = authen.substring(str_begin, str_end);
}
offset++;
}
str_begin = offset;
offset = authen.indexOf(',',offset);
if (offset > 0) {
if (authen.charAt(str_begin) == '\"'){
str_begin++;
}
if (authen.charAt(offset - 1) == '\"'){
str_end = offset - 1;
}
else {
str_end = offset;
}
rep[2] = authen.substring(str_begin, str_end);
if (authen.charAt(offset + 1) == '\"') {
str_begin = offset + 1;
}
else {
str_begin = offset;
}
if (authen.charAt(authen.length() -1) == '\"'){
str_end = authen.length() -1;
}
else {
str_end = authen.length();
}
rep[3] = authen.substring(str_begin+1, str_end);
//rep[3] = temp;
}
else {
rep[2] = " ";
rep[3] = " ";
}
return rep;
}
else {
return null;
}
}
/**
* Returns the type,0 --SIM,1 -- USIM
*
* @hide
*/
public String getSimType() {
String type = mPhone.getSimType();
Log.d(LOG_TAG, "getSimType type:"+type);
return type;
}
public String[] getRegistrationState() {
String[] state = mPhone.getRegistrationState();
Log.d(LOG_TAG, "getRegistrationState state:"+state.length);
return state;
}
public boolean isVTCall() {
boolean is_vtcall = mPhone.isVTCall();
Log.d(LOG_TAG, "isVTCall is_vtcall:"+is_vtcall);
return is_vtcall;
}
public int getRemainTimes(int type) {
int count = mPhone.getRemainTimes(type);
Log.d(LOG_TAG, "getRemainTimes:"+count);
return count;
}
public boolean setApnActivePdpFilter(String apntype,boolean filterenable)
{
Log.d(LOG_TAG, " setApnActivePdpFilter:"+apntype+filterenable);
return mPhone.setApnActivePdpFilter(apntype,filterenable);
}
public boolean getApnActivePdpFilter(String apntype)
{
Log.d(LOG_TAG, " getApnActivePdpFilter:"+apntype);
return mPhone.getApnActivePdpFilter(apntype);
}
public String[] getActiveApnTypes()
{
return mPhone.getActiveApnTypes();
}
public String getActiveApn()
{
return mPhone.getActiveApn();
}
}