/** * Some dial strings in GSM are defined to do non-call setup things, such as modify or query * supplementary service settings (eg, call forwarding). These are generally referred to as "MMI * codes". We look to see if the dial string contains a valid MMI code (potentially with a dial * string at the end as well) and return info here. * * <p>If the dial string contains no MMI code, we return an instance with only "dialingNumber" set * * <p>Please see flow chart in TS 22.030 6.5.3.2 */ static GsmMmiCode newFromDialString(String dialString, GSMPhone phone, UiccCardApplication app) { Matcher m; GsmMmiCode ret = null; if (SystemProperties.getBoolean("ro.config.multimode_cdma", false)) { m = sPatternSuppServiceGlobalDev.matcher(dialString); if (m.matches()) { ret = new GsmMmiCode(phone, app); ret.action = makeEmptyNull(m.group(MATCH_GROUP_ACTION)); String DialCode = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE)); if (DialCode.equals(SC_GLOBALDEV_VM)) { ret.sc = SC_GLOBALDEV_VM; ret.dialingNumber = "+1" + phone.getMdn(); return ret; } else if (DialCode.equals(SC_GLOBALDEV_CS)) { ret.sc = SC_GLOBALDEV_CS; ret.dialingNumber = GLOBALDEV_CS; return ret; } else if (DialCode.length() >= 3 && DialCode.startsWith(SC_GLOBALDEV_CLIR_INVK)) { // Dial "#31#PhoneNum" to invoke CLIR temporarily dialString = ACTION_DEACTIVATE + SC_CLIR + ACTION_DEACTIVATE + DialCode.substring(2); } else if (DialCode.length() >= 3 && DialCode.startsWith(SC_GLOBALDEV_CLIR_SUPP)) { // Dial "*31#PhoneNum" to suppress CLIR temporarily dialString = ACTION_ACTIVATE + SC_CLIR + ACTION_DEACTIVATE + DialCode.substring(2); } } } m = sPatternSuppService.matcher(dialString); // Is this formatted like a standard supplementary service code? if (m.matches()) { ret = new GsmMmiCode(phone, app); ret.poundString = makeEmptyNull(m.group(MATCH_GROUP_POUND_STRING)); ret.action = makeEmptyNull(m.group(MATCH_GROUP_ACTION)); ret.sc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE)); ret.sia = makeEmptyNull(m.group(MATCH_GROUP_SIA)); ret.sib = makeEmptyNull(m.group(MATCH_GROUP_SIB)); ret.sic = makeEmptyNull(m.group(MATCH_GROUP_SIC)); ret.pwd = makeEmptyNull(m.group(MATCH_GROUP_PWD_CONFIRM)); ret.dialingNumber = makeEmptyNull(m.group(MATCH_GROUP_DIALING_NUMBER)); } else if (dialString.endsWith("#")) { // TS 22.030 sec 6.5.3.2 // "Entry of any characters defined in the 3GPP TS 23.038 [8] Default Alphabet // (up to the maximum defined in 3GPP TS 24.080 [10]), followed by #SEND". ret = new GsmMmiCode(phone, app); ret.poundString = dialString; } else if (isTwoDigitShortCode(phone.getContext(), dialString)) { // Is a country-specific exception to short codes as defined in TS 22.030, 6.5.3.2 ret = null; } else if (isShortCode(dialString, phone)) { // this may be a short code, as defined in TS 22.030, 6.5.3.2 ret = new GsmMmiCode(phone, app); ret.dialingNumber = dialString; } return ret; }
GsmMmiCode(GSMPhone phone, UiccCardApplication app) { // The telephony unit-test cases may create GsmMmiCode's // in secondary threads super(phone.getHandler().getLooper()); this.phone = phone; this.context = phone.getContext(); mUiccApp = app; }
/** clirMode is one of the CLIR_ constants */ synchronized Connection dial(String dialString, int clirMode, UUSInfo uusInfo) throws CallStateException { // note that this triggers call state changed notif clearDisconnected(); if (!canDial()) { throw new CallStateException("cannot dial in current state"); } // The new call must be assigned to the foreground call. // That call must be idle, so place anything that's // there on hold if (foregroundCall.getState() == GsmCall.State.ACTIVE) { // this will probably be done by the radio anyway // but the dial might fail before this happens // and we need to make sure the foreground call is clear // for the newly dialed connection switchWaitingOrHoldingAndActive(); // Fake local state so that // a) foregroundCall is empty for the newly dialed connection // b) hasNonHangupStateChanged remains false in the // next poll, so that we don't clear a failed dialing call fakeHoldForegroundBeforeDial(); } if (foregroundCall.getState() != GsmCall.State.IDLE) { // we should have failed in !canDial() above before we get here throw new CallStateException("cannot dial in current state"); } pendingMO = new GsmConnection( phone.getContext(), checkForTestEmergencyNumber(dialString), this, foregroundCall); hangupPendingMO = false; if (pendingMO.address == null || pendingMO.address.length() == 0 || pendingMO.address.indexOf(PhoneNumberUtils.WILD) >= 0) { // Phone number is invalid pendingMO.cause = Connection.DisconnectCause.INVALID_NUMBER; // handlePollCalls() will notice this call not present // and will mark it as dropped. pollCallsWhenSafe(); } else { // Always unmute when initiating a new call setMute(false); cm.dial(pendingMO.address, clirMode, uusInfo, obtainCompleteMessage()); } updatePhoneState(); phone.notifyPreciseCallStateChanged(); return pendingMO; }
private void onQueryCfComplete(AsyncResult ar) { StringBuilder sb = new StringBuilder(getScString()); sb.append("\n"); if (ar.exception != null) { state = State.FAILED; sb.append(getErrorMessage(ar)); } else { CallForwardInfo infos[]; infos = (CallForwardInfo[]) ar.result; if (infos.length == 0) { // Assume the default is not active sb.append(context.getText(com.android.internal.R.string.serviceDisabled)); // Set unconditional CFF in SIM to false if (phone.mSIMRecords != null) { phone.setCallForwardingPreference(false); phone.mSIMRecords.setVoiceCallForwardingFlag(1, false); } else { Log.w(LOG_TAG, "setVoiceCallForwardingFlag aborted. sim records is null."); } } else { SpannableStringBuilder tb = new SpannableStringBuilder(); // Each bit in the service class gets its own result line // The service classes may be split up over multiple // CallForwardInfos. So, for each service class, find out // which CallForwardInfo represents it and then build // the response text based on that for (int serviceClassMask = 1; serviceClassMask <= SERVICE_CLASS_MAX; serviceClassMask <<= 1) { for (int i = 0, s = infos.length; i < s; i++) { if ((serviceClassMask & infos[i].serviceClass) != 0) { tb.append(makeCFQueryResultMessage(infos[i], serviceClassMask)); tb.append("\n"); } } } sb.append(tb); } state = State.COMPLETE; } message = sb; phone.onMMIDone(this); }
/* package */ void hangup(GsmCall call) throws CallStateException { if (call.getConnections().size() == 0) { throw new CallStateException("no connections in call"); } if (call == ringingCall) { if (Phone.DEBUG_PHONE) log("(ringing) hangup waiting or background"); cm.hangupWaitingOrBackground(obtainCompleteMessage()); } else if (call == foregroundCall) { if (call.isDialingOrAlerting()) { if (Phone.DEBUG_PHONE) { log("(foregnd) hangup dialing or alerting..."); } hangup((GsmConnection) (call.getConnections().get(0))); } else { hangupForegroundResumeBackground(); } } else if (call == backgroundCall) { if (ringingCall.isRinging()) { if (Phone.DEBUG_PHONE) { log("hangup all conns in background call"); } hangupAllConnections(call); } else { hangupWaitingOrBackground(); } } else { throw new RuntimeException("GsmCall " + call + "does not belong to GsmCallTracker " + this); } call.onHangupLocal(); phone.notifyPreciseCallStateChanged(); }
// inherited javadoc suffices public void cancel() { // Complete or failed cannot be cancelled if (state == State.COMPLETE || state == State.FAILED) { return; } state = State.CANCELLED; if (isPendingUSSD) { /* * There can only be one pending USSD session, so tell the radio to * cancel it. */ phone.mCM.cancelPendingUssd(obtainMessage(EVENT_USSD_CANCEL_COMPLETE, this)); /* * Don't call phone.onMMIDone here; wait for CANCEL_COMPLETE notice * from RIL. */ } else { // TODO in cases other than USSD, it would be nice to cancel // the pending radio operation. This requires RIL cancellation // support, which does not presently exist. phone.onMMIDone(this); } }
private void onQueryComplete(AsyncResult ar) { StringBuilder sb = new StringBuilder(getScString()); sb.append("\n"); if (ar.exception != null) { state = State.FAILED; sb.append(getErrorMessage(ar)); } else { int[] ints = (int[]) ar.result; if (ints.length != 0) { if (ints[0] == 0) { sb.append(context.getText(com.android.internal.R.string.serviceDisabled)); } else if (sc.equals(SC_WAIT)) { // Call Waiting includes additional data in the response. sb.append(createQueryCallWaitingResultMessage(ints[1])); } else if (isServiceCodeCallBarring(sc)) { // ints[0] for Call Barring is a bit vector of services sb.append(createQueryCallBarringResultMessage(ints[0])); } else if (ints[0] == 1) { // for all other services, treat it as a boolean sb.append(context.getText(com.android.internal.R.string.serviceEnabled)); } else { sb.append(context.getText(com.android.internal.R.string.mmiError)); } } else { sb.append(context.getText(com.android.internal.R.string.mmiError)); } state = State.COMPLETE; } message = sb; phone.onMMIDone(this); }
private void handlePasswordError(int res) { state = State.FAILED; StringBuilder sb = new StringBuilder(getScString()); sb.append("\n"); sb.append(context.getText(res)); message = sb; phone.onMMIDone(this); }
/** * Called from GSMPhone * * <p>The radio has reset, and this is still pending */ void onUssdFinishedError() { if (state == State.PENDING) { state = State.FAILED; message = context.getText(com.android.internal.R.string.mmiError); phone.onMMIDone(this); } }
/** one CallForwardInfo + serviceClassMask -> one line of text */ private CharSequence makeCFQueryResultMessage(CallForwardInfo info, int serviceClassMask) { CharSequence template; String sources[] = {"{0}", "{1}", "{2}"}; CharSequence destinations[] = new CharSequence[3]; boolean needTimeTemplate; // CF_REASON_NO_REPLY also has a time value associated with // it. All others don't. needTimeTemplate = (info.reason == CommandsInterface.CF_REASON_NO_REPLY); if (info.status == 1) { if (needTimeTemplate) { template = context.getText(com.android.internal.R.string.cfTemplateForwardedTime); } else { template = context.getText(com.android.internal.R.string.cfTemplateForwarded); } } else if (info.status == 0 && isEmptyOrNull(info.number)) { template = context.getText(com.android.internal.R.string.cfTemplateNotForwarded); } else { /* (info.status == 0) && !isEmptyOrNull(info.number) */ // A call forward record that is not active but contains // a phone number is considered "registered" if (needTimeTemplate) { template = context.getText(com.android.internal.R.string.cfTemplateRegisteredTime); } else { template = context.getText(com.android.internal.R.string.cfTemplateRegistered); } } // In the template (from strings.xmls) // {0} is one of "bearerServiceCode*" // {1} is dialing number // {2} is time in seconds destinations[0] = serviceClassToCFString(info.serviceClass & serviceClassMask); destinations[1] = PhoneNumberUtils.stringFromStringAndTOA(info.number, info.toa); destinations[2] = Integer.toString(info.timeSeconds); if (info.reason == CommandsInterface.CF_REASON_UNCONDITIONAL && (info.serviceClass & serviceClassMask) == CommandsInterface.SERVICE_CLASS_VOICE) { boolean cffEnabled = (info.status == 1); if (phone.mSIMRecords != null) { phone.setCallForwardingPreference(cffEnabled); phone.mSIMRecords.setVoiceCallForwardingFlag(1, cffEnabled); } else { Log.w(LOG_TAG, "setVoiceCallForwardingFlag aborted. sim records is null."); } } return TextUtils.replace(template, sources, destinations); }
boolean canDial() { boolean ret; int serviceState = phone.getServiceState().getState(); String disableCall = SystemProperties.get(TelephonyProperties.PROPERTY_DISABLE_CALL, "false"); ret = (serviceState != ServiceState.STATE_POWER_OFF) && pendingMO == null && !ringingCall.isRinging() && !disableCall.equals("true") && (!foregroundCall.getState().isAlive() || !backgroundCall.getState().isAlive()); return ret; }
/** * Called from GSMPhone * * <p>An unsolicited USSD NOTIFY or REQUEST has come in matching up with this pending USSD request * * <p>Note: If REQUEST, this exchange is complete, but the session remains active (ie, the network * expects user input). */ void onUssdFinished(String ussdMessage, boolean isUssdRequest) { if (state == State.PENDING) { if (ussdMessage == null) { message = context.getText(com.android.internal.R.string.mmiComplete); } else { message = ussdMessage; } this.isUssdRequest = isUssdRequest; // If it's a request, leave it PENDING so that it's cancelable. if (!isUssdRequest) { state = State.COMPLETE; } phone.onMMIDone(this); } }
private void updatePhoneState() { PhoneConstants.State oldState = state; if (ringingCall.isRinging()) { state = PhoneConstants.State.RINGING; } else if (pendingMO != null || !(foregroundCall.isIdle() && backgroundCall.isIdle())) { state = PhoneConstants.State.OFFHOOK; } else { state = PhoneConstants.State.IDLE; } if (state == PhoneConstants.State.IDLE && oldState != state) { voiceCallEndedRegistrants.notifyRegistrants(new AsyncResult(null, null, null)); } else if (oldState == PhoneConstants.State.IDLE && oldState != state) { voiceCallStartedRegistrants.notifyRegistrants(new AsyncResult(null, null, null)); } if (state != oldState) { phone.notifyPhoneStateChanged(); } }
/** * Helper function for newFromDialString. Returns true if dialString appears to be a short code * AND conditions are correct for it to be treated as such. */ private static boolean isShortCode(String dialString, GSMPhone phone) { // check for any Adapt change requirements. boolean shortCodeExclusionFlag = false; if (SystemProperties.getBoolean("persist.cust.tel.adapt", false)) { if (dialString.length() == 2) { Log.i(LOG_TAG, "Adapt, Number needs to be checked for short code exclusion list"); shortCodeExclusionFlag = isExcludedShortCode(dialString); } } // Refer to TS 22.030 Figure 3.5.3.2: // A 1 or 2 digit "short code" is treated as USSD if it is entered while on a call or // does not satisfy the condition (exactly 2 digits && starts with '1'). return ((dialString != null && dialString.length() <= 2) && !PhoneNumberUtils.isEmergencyNumber(dialString) && (phone.isInCall() || !((dialString.length() == 2 && dialString.charAt(0) == '1') || shortCodeExclusionFlag /* * While contrary to TS 22.030, there is strong precedence for * treating "0" and "00" as call setup strings. */ || dialString.equals("0") || dialString.equals("00")))); }
public void handleMessage(Message msg) { AsyncResult ar; switch (msg.what) { case EVENT_POLL_CALLS_RESULT: ar = (AsyncResult) msg.obj; if (msg == lastRelevantPoll) { if (DBG_POLL) log("handle EVENT_POLL_CALL_RESULT: set needsPoll=F"); needsPoll = false; lastRelevantPoll = null; handlePollCalls((AsyncResult) msg.obj); } break; case EVENT_OPERATION_COMPLETE: ar = (AsyncResult) msg.obj; operationComplete(); break; case EVENT_SWITCH_RESULT: case EVENT_CONFERENCE_RESULT: case EVENT_SEPARATE_RESULT: case EVENT_ECT_RESULT: ar = (AsyncResult) msg.obj; if (ar.exception != null) { phone.notifySuppServiceFailed(getFailedService(msg.what)); } operationComplete(); break; case EVENT_GET_LAST_CALL_FAIL_CAUSE: int causeCode; ar = (AsyncResult) msg.obj; operationComplete(); if (ar.exception != null) { // An exception occurred...just treat the disconnect // cause as "normal" causeCode = CallFailCause.NORMAL_CLEARING; Rlog.i(LOG_TAG, "Exception during getLastCallFailCause, assuming normal disconnect"); } else { causeCode = ((int[]) ar.result)[0]; } // Log the causeCode if its not normal if (causeCode == CallFailCause.NO_CIRCUIT_AVAIL || causeCode == CallFailCause.TEMPORARY_FAILURE || causeCode == CallFailCause.SWITCHING_CONGESTION || causeCode == CallFailCause.CHANNEL_NOT_AVAIL || causeCode == CallFailCause.QOS_NOT_AVAIL || causeCode == CallFailCause.BEARER_NOT_AVAIL || causeCode == CallFailCause.ERROR_UNSPECIFIED) { GsmCellLocation loc = ((GsmCellLocation) phone.getCellLocation()); EventLog.writeEvent( EventLogTags.CALL_DROP, causeCode, loc != null ? loc.getCid() : -1, TelephonyManager.getDefault().getNetworkType()); } for (int i = 0, s = droppedDuringPoll.size(); i < s; i++) { GsmConnection conn = droppedDuringPoll.get(i); conn.onRemoteDisconnect(causeCode); } updatePhoneState(); phone.notifyPreciseCallStateChanged(); droppedDuringPoll.clear(); break; case EVENT_REPOLL_AFTER_DELAY: case EVENT_CALL_STATE_CHANGE: pollCallsWhenSafe(); break; case EVENT_RADIO_AVAILABLE: handleRadioAvailable(); break; case EVENT_RADIO_NOT_AVAILABLE: handleRadioNotAvailable(); break; } }
protected synchronized void handlePollCalls(AsyncResult ar) { List polledCalls; if (ar.exception == null) { polledCalls = (List) ar.result; } else if (isCommandExceptionRadioNotAvailable(ar.exception)) { // just a dummy empty ArrayList to cause the loop // to hang up all the calls polledCalls = new ArrayList(); } else { // Radio probably wasn't ready--try again in a bit // But don't keep polling if the channel is closed pollCallsAfterDelay(); return; } Connection newRinging = null; // or waiting boolean hasNonHangupStateChanged = false; // Any change besides // a dropped connection boolean needsPollDelay = false; boolean unknownConnectionAppeared = false; for (int i = 0, curDC = 0, dcSize = polledCalls.size(); i < connections.length; i++) { GsmConnection conn = connections[i]; DriverCall dc = null; // polledCall list is sparse if (curDC < dcSize) { dc = (DriverCall) polledCalls.get(curDC); if (dc.index == i + 1) { curDC++; } else { dc = null; } } if (DBG_POLL) log("poll: conn[i=" + i + "]=" + conn + ", dc=" + dc); if (conn == null && dc != null) { // Connection appeared in CLCC response that we don't know about if (pendingMO != null && pendingMO.compareTo(dc)) { if (DBG_POLL) log("poll: pendingMO=" + pendingMO); // It's our pending mobile originating call connections[i] = pendingMO; pendingMO.index = i; pendingMO.update(dc); pendingMO = null; // Someone has already asked to hangup this call if (hangupPendingMO) { hangupPendingMO = false; try { if (Phone.DEBUG_PHONE) log("poll: hangupPendingMO, hangup conn " + i); hangup(connections[i]); } catch (CallStateException ex) { Rlog.e(LOG_TAG, "unexpected error on hangup"); } // Do not continue processing this poll // Wait for hangup and repoll return; } } else { connections[i] = new GsmConnection(phone.getContext(), dc, this, i); // it's a ringing call if (connections[i].getCall() == ringingCall) { newRinging = connections[i]; } else { // Something strange happened: a call appeared // which is neither a ringing call or one we created. // Either we've crashed and re-attached to an existing // call, or something else (eg, SIM) initiated the call. Rlog.i(LOG_TAG, "Phantom call appeared " + dc); // If it's a connected call, set the connect time so that // it's non-zero. It may not be accurate, but at least // it won't appear as a Missed Call. if (dc.state != DriverCall.State.ALERTING && dc.state != DriverCall.State.DIALING) { connections[i].onConnectedInOrOut(); if (dc.state == DriverCall.State.HOLDING) { // We've transitioned into HOLDING connections[i].onStartedHolding(); } } unknownConnectionAppeared = true; } } hasNonHangupStateChanged = true; } else if (conn != null && dc == null) { // Connection missing in CLCC response that we were // tracking. droppedDuringPoll.add(conn); // Dropped connections are removed from the CallTracker // list but kept in the GsmCall list connections[i] = null; } else if (conn != null && dc != null && !conn.compareTo(dc)) { // Connection in CLCC response does not match what // we were tracking. Assume dropped call and new call droppedDuringPoll.add(conn); connections[i] = new GsmConnection(phone.getContext(), dc, this, i); if (connections[i].getCall() == ringingCall) { newRinging = connections[i]; } // else something strange happened hasNonHangupStateChanged = true; } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */ boolean changed; changed = conn.update(dc); hasNonHangupStateChanged = hasNonHangupStateChanged || changed; } if (REPEAT_POLLING) { if (dc != null) { // FIXME with RIL, we should not need this anymore if ((dc.state == DriverCall.State.DIALING /*&& cm.getOption(cm.OPTION_POLL_DIALING)*/ ) || (dc.state == DriverCall.State.ALERTING /*&& cm.getOption(cm.OPTION_POLL_ALERTING)*/ ) || (dc.state == DriverCall.State.INCOMING /*&& cm.getOption(cm.OPTION_POLL_INCOMING)*/ ) || (dc.state == DriverCall.State.WAITING /*&& cm.getOption(cm.OPTION_POLL_WAITING)*/ )) { // Sometimes there's no unsolicited notification // for state transitions needsPollDelay = true; } } } } // This is the first poll after an ATD. // We expect the pending call to appear in the list // If it does not, we land here if (pendingMO != null) { Rlog.d(LOG_TAG, "Pending MO dropped before poll fg state:" + foregroundCall.getState()); droppedDuringPoll.add(pendingMO); pendingMO = null; hangupPendingMO = false; } if (newRinging != null) { phone.notifyNewRingingConnection(newRinging); } // clear the "local hangup" and "missed/rejected call" // cases from the "dropped during poll" list // These cases need no "last call fail" reason for (int i = droppedDuringPoll.size() - 1; i >= 0; i--) { GsmConnection conn = droppedDuringPoll.get(i); if (conn.isIncoming() && conn.getConnectTime() == 0) { // Missed or rejected call Connection.DisconnectCause cause; if (conn.cause == Connection.DisconnectCause.LOCAL) { cause = Connection.DisconnectCause.INCOMING_REJECTED; } else { cause = Connection.DisconnectCause.INCOMING_MISSED; } if (Phone.DEBUG_PHONE) { log("missed/rejected call, conn.cause=" + conn.cause); log("setting cause to " + cause); } droppedDuringPoll.remove(i); conn.onDisconnect(cause); } else if (conn.cause == Connection.DisconnectCause.LOCAL) { // Local hangup droppedDuringPoll.remove(i); conn.onDisconnect(Connection.DisconnectCause.LOCAL); } else if (conn.cause == Connection.DisconnectCause.INVALID_NUMBER) { droppedDuringPoll.remove(i); conn.onDisconnect(Connection.DisconnectCause.INVALID_NUMBER); } } // Any non-local disconnects: determine cause if (droppedDuringPoll.size() > 0) { cm.getLastCallFailCause(obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE)); } if (needsPollDelay) { pollCallsAfterDelay(); } // Cases when we can no longer keep disconnected Connection's // with their previous calls // 1) the phone has started to ring // 2) A Call/Connection object has changed state... // we may have switched or held or answered (but not hung up) if (newRinging != null || hasNonHangupStateChanged) { internalClearDisconnected(); } updatePhoneState(); if (unknownConnectionAppeared) { phone.notifyUnknownConnection(); } if (hasNonHangupStateChanged || newRinging != null) { phone.notifyPreciseCallStateChanged(); } // dumpState(); }
/** Process a MMI code or short code...anything that isn't a dialing number */ void processCode() { try { if (isShortCode()) { Log.d(LOG_TAG, "isShortCode"); // These just get treated as USSD. sendUssd(dialingNumber); } else if (dialingNumber != null) { // We should have no dialing numbers here throw new RuntimeException("Invalid or Unsupported MMI Code"); } else if (sc != null && sc.equals(SC_CLIP)) { Log.d(LOG_TAG, "is CLIP"); if (isInterrogate()) { phone.mCM.queryCLIP(obtainMessage(EVENT_QUERY_COMPLETE, this)); } else { throw new RuntimeException("Invalid or Unsupported MMI Code"); } } else if (sc != null && sc.equals(SC_CLIR)) { Log.d(LOG_TAG, "is CLIR"); if (isActivate()) { phone.mCM.setCLIR( CommandsInterface.CLIR_INVOCATION, obtainMessage(EVENT_SET_COMPLETE, this)); } else if (isDeactivate()) { phone.mCM.setCLIR( CommandsInterface.CLIR_SUPPRESSION, obtainMessage(EVENT_SET_COMPLETE, this)); } else if (isInterrogate()) { phone.mCM.getCLIR(obtainMessage(EVENT_GET_CLIR_COMPLETE, this)); } else { throw new RuntimeException("Invalid or Unsupported MMI Code"); } } else if (isServiceCodeCallForwarding(sc)) { Log.d(LOG_TAG, "is CF"); String dialingNumber = sia; int serviceClass = siToServiceClass(sib); int reason = scToCallForwardReason(sc); int time = siToTime(sic); if (isInterrogate()) { phone.mCM.queryCallForwardStatus( reason, serviceClass, dialingNumber, obtainMessage(EVENT_QUERY_CF_COMPLETE, this)); } else { int cfAction; if (isActivate()) { if (dialingNumber != null) { isCallFwdRegister = true; cfAction = CommandsInterface.CF_ACTION_REGISTRATION; } else { cfAction = CommandsInterface.CF_ACTION_ENABLE; } } else if (isDeactivate()) { cfAction = CommandsInterface.CF_ACTION_DISABLE; } else if (isRegister()) { cfAction = CommandsInterface.CF_ACTION_REGISTRATION; } else if (isErasure()) { cfAction = CommandsInterface.CF_ACTION_ERASURE; } else { throw new RuntimeException("invalid action"); } int isSettingUnconditionalVoice = (((reason == CommandsInterface.CF_REASON_UNCONDITIONAL) || (reason == CommandsInterface.CF_REASON_ALL)) && (((serviceClass & CommandsInterface.SERVICE_CLASS_VOICE) != 0) || (serviceClass == CommandsInterface.SERVICE_CLASS_NONE))) ? 1 : 0; int isEnableDesired = ((cfAction == CommandsInterface.CF_ACTION_ENABLE) || (cfAction == CommandsInterface.CF_ACTION_REGISTRATION)) ? 1 : 0; Log.d(LOG_TAG, "is CF setCallForward"); phone.mCM.setCallForward( cfAction, reason, serviceClass, dialingNumber, time, obtainMessage( EVENT_SET_CFF_COMPLETE, isSettingUnconditionalVoice, isEnableDesired, this)); } } else if (isServiceCodeCallBarring(sc)) { // sia = password // sib = basic service group String password = sia; int serviceClass = siToServiceClass(sib); String facility = scToBarringFacility(sc); if (isInterrogate()) { phone.mCM.queryFacilityLock( 0, "", facility, password, serviceClass, obtainMessage(EVENT_QUERY_COMPLETE, this)); } else if (isActivate() || isDeactivate()) { phone.mCM.setFacilityLock( 0, "", facility, isActivate(), password, serviceClass, obtainMessage(EVENT_SET_COMPLETE, this)); } else { throw new RuntimeException("Invalid or Unsupported MMI Code"); } } else if (sc != null && sc.equals(SC_PWD)) { // sia = fac // sib = old pwd // sic = new pwd // pwd = new pwd String facility; String oldPwd = sib; String newPwd = sic; if (isActivate() || isRegister()) { // Even though ACTIVATE is acceptable, this is really termed a REGISTER action = ACTION_REGISTER; if (sia == null) { // If sc was not specified, treat it as BA_ALL. facility = CommandsInterface.CB_FACILITY_BA_ALL; } else { facility = scToBarringFacility(sia); } if (newPwd.equals(pwd)) { phone.mCM.changeBarringPassword( facility, oldPwd, newPwd, obtainMessage(EVENT_SET_COMPLETE, this)); } else { // password mismatch; return error handlePasswordError(com.android.internal.R.string.passwordIncorrect); } } else { throw new RuntimeException("Invalid or Unsupported MMI Code"); } } else if (sc != null && sc.equals(SC_WAIT)) { // sia = basic service group int serviceClass = siToServiceClass(sia); if (isActivate() || isDeactivate()) { phone.mCM.setCallWaiting( isActivate(), serviceClass, obtainMessage(EVENT_SET_COMPLETE, this)); } else if (isInterrogate()) { phone.mCM.queryCallWaiting(serviceClass, obtainMessage(EVENT_QUERY_COMPLETE, this)); } else { throw new RuntimeException("Invalid or Unsupported MMI Code"); } } else if (isPinCommand()) { // sia = old PIN or PUK // sib = new PIN // sic = new PIN String oldPinOrPuk = sia; String newPin = sib; int pinLen = newPin.length(); if (isRegister()) { if (!newPin.equals(sic)) { // password mismatch; return error handlePasswordError(com.android.internal.R.string.mismatchPin); } else if (pinLen < 4 || pinLen > 8) { // invalid length handlePasswordError(com.android.internal.R.string.invalidPin); } else if (sc.equals(SC_PIN) && phone.m3gppApplication != null && phone.m3gppApplication.getState() == UiccConstants.AppState.APPSTATE_PUK) { // Sim is puk-locked handlePasswordError(com.android.internal.R.string.needPuk); } else { // pre-checks OK if (sc.equals(SC_PIN)) { if (mUiccApp != null) mUiccApp.changeIccLockPassword( oldPinOrPuk, newPin, obtainMessage(EVENT_SET_COMPLETE, this)); } else if (sc.equals(SC_PIN2)) { if (mUiccApp != null) mUiccApp.changeIccFdnPassword( oldPinOrPuk, newPin, obtainMessage(EVENT_SET_COMPLETE, this)); } else if (sc.equals(SC_PUK)) { if (mUiccApp != null) mUiccApp.supplyPuk(oldPinOrPuk, newPin, obtainMessage(EVENT_SET_COMPLETE, this)); } else if (sc.equals(SC_PUK2)) { if (mUiccApp != null) mUiccApp.supplyPuk2(oldPinOrPuk, newPin, obtainMessage(EVENT_SET_COMPLETE, this)); } } } else { throw new RuntimeException("Invalid or Unsupported MMI Code"); } } else if (isServiceCodeUnsupported(sc)) { Log.d(LOG_TAG, "Unsupported MMI code: " + sc); state = State.FAILED; message = context.getText(com.android.internal.R.string.unsupportedMmiCode); phone.onMMIDone(this); } else if (poundString != null) { sendUssd(poundString); } else { throw new RuntimeException("Invalid or Unsupported MMI Code"); } } catch (RuntimeException exc) { state = State.FAILED; message = context.getText(com.android.internal.R.string.mmiError); phone.onMMIDone(this); } }
void clearDisconnected() { internalClearDisconnected(); updatePhoneState(); phone.notifyPreciseCallStateChanged(); }
protected void handleBroadcastSms(AsyncResult ar) { try { byte[][] pdus = null; byte[] receivedPdu = (byte[]) ar.result; if (Config.LOGD) { for (int i = 0; i < receivedPdu.length; i += 8) { StringBuilder sb = new StringBuilder("SMS CB pdu data: "); for (int j = i; j < i + 8 && j < receivedPdu.length; j++) { int b = receivedPdu[j] & 0xff; if (b < 0x10) { sb.append("0"); } sb.append(Integer.toHexString(b)).append(" "); } Log.d(TAG, sb.toString()); } } SmsCbHeader header = new SmsCbHeader(receivedPdu); String plmn = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC); GsmCellLocation cellLocation = (GsmCellLocation) mGsmPhone.getCellLocation(); int lac = cellLocation.getLac(); int cid = cellLocation.getCid(); if (header.nrOfPages > 1) { // Multi-page message SmsCbConcatInfo concatInfo = new SmsCbConcatInfo(header, plmn, lac, cid); // Try to find other pages of the same message pdus = mSmsCbPageMap.get(concatInfo); if (pdus == null) { // This it the first page of this message, make room for all // pages and keep until complete pdus = new byte[header.nrOfPages][]; mSmsCbPageMap.put(concatInfo, pdus); } // Page parameter is one-based pdus[header.pageIndex - 1] = receivedPdu; for (int i = 0; i < pdus.length; i++) { if (pdus[i] == null) { // Still missing pages, exit return; } } // Message complete, remove and dispatch mSmsCbPageMap.remove(concatInfo); } else { // Single page message pdus = new byte[1][]; pdus[0] = receivedPdu; } dispatchBroadcastPdus(pdus); // Remove messages that are out of scope to prevent the map from // growing indefinitely, containing incomplete messages that were // never assembled Iterator<SmsCbConcatInfo> iter = mSmsCbPageMap.keySet().iterator(); while (iter.hasNext()) { SmsCbConcatInfo info = iter.next(); if (!info.matchesLocation(plmn, lac, cid)) { iter.remove(); } } } catch (RuntimeException e) { Log.e(TAG, "Error in decoding SMS CB pdu", e); } }
private void onSetComplete(AsyncResult ar) { StringBuilder sb = new StringBuilder(getScString()); sb.append("\n"); if (ar.exception != null) { state = State.FAILED; if (ar.exception instanceof CommandException) { CommandException.Error err = ((CommandException) (ar.exception)).getCommandError(); if (err == CommandException.Error.PASSWORD_INCORRECT) { if (isPinCommand()) { // look specifically for the PUK commands and adjust // the message accordingly. if (sc.equals(SC_PUK) || sc.equals(SC_PUK2)) { sb.append(context.getText(com.android.internal.R.string.badPuk)); } else { sb.append(context.getText(com.android.internal.R.string.badPin)); } } else { sb.append(context.getText(com.android.internal.R.string.passwordIncorrect)); } } else if (err == CommandException.Error.SIM_PUK2) { sb.append(context.getText(com.android.internal.R.string.badPin)); sb.append("\n"); sb.append(context.getText(com.android.internal.R.string.needPuk2)); } else if (err == CommandException.Error.FDN_CHECK_FAILURE) { Log.i(LOG_TAG, "FDN_CHECK_FAILURE"); sb.append(context.getText(com.android.internal.R.string.mmiFdnError)); } else { sb.append(context.getText(com.android.internal.R.string.mmiError)); } } else { sb.append(context.getText(com.android.internal.R.string.mmiError)); } } else if (isActivate()) { state = State.COMPLETE; if (isCallFwdRegister) { sb.append(context.getText(com.android.internal.R.string.serviceRegistered)); isCallFwdRegister = false; } else { sb.append(context.getText(com.android.internal.R.string.serviceEnabled)); } // Record CLIR setting if (sc.equals(SC_CLIR)) { phone.saveClirSetting(CommandsInterface.CLIR_INVOCATION); } } else if (isDeactivate()) { state = State.COMPLETE; sb.append(context.getText(com.android.internal.R.string.serviceDisabled)); // Record CLIR setting if (sc.equals(SC_CLIR)) { phone.saveClirSetting(CommandsInterface.CLIR_SUPPRESSION); } } else if (isRegister()) { state = State.COMPLETE; sb.append(context.getText(com.android.internal.R.string.serviceRegistered)); } else if (isErasure()) { state = State.COMPLETE; sb.append(context.getText(com.android.internal.R.string.serviceErased)); } else { state = State.FAILED; sb.append(context.getText(com.android.internal.R.string.mmiError)); } message = sb; phone.onMMIDone(this); }
/** Called from GSMPhone.handleMessage; not a Handler subclass */ public void handleMessage(Message msg) { AsyncResult ar; switch (msg.what) { case EVENT_SET_COMPLETE: ar = (AsyncResult) (msg.obj); onSetComplete(ar); break; case EVENT_SET_CFF_COMPLETE: ar = (AsyncResult) (msg.obj); /* * msg.arg1 = 1 means to set unconditional voice call forwarding * msg.arg2 = 1 means to enable voice call forwarding */ if ((ar.exception == null) && (msg.arg1 == 1)) { boolean cffEnabled = (msg.arg2 == 1); if (phone.mSIMRecords != null) { phone.mSIMRecords.setVoiceCallForwardingFlag(1, cffEnabled); phone.setCallForwardingPreference(cffEnabled); } else { Log.w(LOG_TAG, "setVoiceCallForwardingFlag aborted. sim records is null."); } } onSetComplete(ar); break; case EVENT_GET_CLIR_COMPLETE: ar = (AsyncResult) (msg.obj); onGetClirComplete(ar); break; case EVENT_QUERY_CF_COMPLETE: ar = (AsyncResult) (msg.obj); onQueryCfComplete(ar); break; case EVENT_QUERY_COMPLETE: ar = (AsyncResult) (msg.obj); onQueryComplete(ar); break; case EVENT_USSD_COMPLETE: ar = (AsyncResult) (msg.obj); if (ar.exception != null) { state = State.FAILED; message = getErrorMessage(ar); phone.onMMIDone(this); } // Note that unlike most everything else, the USSD complete // response does not complete this MMI code...we wait for // an unsolicited USSD "Notify" or "Request". // The matching up of this is done in GSMPhone. break; case EVENT_USSD_CANCEL_COMPLETE: phone.onMMIDone(this); break; } }
/** {@inheritDoc} */ protected int dispatchMessage(SmsMessageBase smsb) { // If sms is null, means there was a parsing error. if (smsb == null) { return Intents.RESULT_SMS_GENERIC_ERROR; } SmsMessage sms = (SmsMessage) smsb; boolean handled = false; if (sms.isTypeZero()) { // As per 3GPP TS 23.040 9.2.3.9, Type Zero messages should not be // Displayed/Stored/Notified. They should only be acknowledged. Log.d(TAG, "Received short message type 0, Don't display or store it. Send Ack"); return Intents.RESULT_SMS_HANDLED; } // Special case the message waiting indicator messages if (sms.isMWISetMessage()) { mGsmPhone.updateMessageWaitingIndicator(true); handled = sms.isMwiDontStore(); if (Config.LOGD) { Log.d(TAG, "Received voice mail indicator set SMS shouldStore=" + !handled); } } else if (sms.isMWIClearMessage()) { mGsmPhone.updateMessageWaitingIndicator(false); handled = sms.isMwiDontStore(); if (Config.LOGD) { Log.d(TAG, "Received voice mail indicator clear SMS shouldStore=" + !handled); } } if (handled) { return Intents.RESULT_SMS_HANDLED; } if (!mStorageAvailable && (sms.getMessageClass() != MessageClass.CLASS_0)) { // It's a storable message and there's no storage available. Bail. // (See TS 23.038 for a description of class 0 messages.) return Intents.RESULT_SMS_OUT_OF_MEMORY; } SmsHeader smsHeader = sms.getUserDataHeader(); // See if message is partial or port addressed. if ((smsHeader == null) || (smsHeader.concatRef == null)) { // Message is not partial (not part of concatenated sequence). byte[][] pdus = new byte[1][]; pdus[0] = sms.getPdu(); if (smsHeader != null && smsHeader.portAddrs != null) { if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) { return mWapPush.dispatchWapPdu(sms.getUserData()); } else { // The message was sent to a port, so concoct a URI for it. dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort); } } else { // Normal short and non-port-addressed message, dispatch it. dispatchPdus(pdus); } return Activity.RESULT_OK; } else { // Process the message part. return processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs); } }
private void onGetClirComplete(AsyncResult ar) { StringBuilder sb = new StringBuilder(getScString()); sb.append("\n"); if (ar.exception != null) { state = State.FAILED; sb.append(getErrorMessage(ar)); } else { int clirArgs[]; clirArgs = (int[]) ar.result; // the 'm' parameter from TS 27.007 7.7 switch (clirArgs[1]) { case 0: // CLIR not provisioned sb.append(context.getText(com.android.internal.R.string.serviceNotProvisioned)); state = State.COMPLETE; break; case 1: // CLIR provisioned in permanent mode sb.append(context.getText(com.android.internal.R.string.CLIRPermanent)); state = State.COMPLETE; break; case 2: // unknown (e.g. no network, etc.) sb.append(context.getText(com.android.internal.R.string.mmiError)); state = State.FAILED; break; case 3: // CLIR temporary mode presentation restricted // the 'n' parameter from TS 27.007 7.7 switch (clirArgs[0]) { default: case 0: // Default sb.append(context.getText(com.android.internal.R.string.CLIRDefaultOnNextCallOn)); break; case 1: // CLIR invocation sb.append(context.getText(com.android.internal.R.string.CLIRDefaultOnNextCallOn)); break; case 2: // CLIR suppression sb.append(context.getText(com.android.internal.R.string.CLIRDefaultOnNextCallOff)); break; } state = State.COMPLETE; break; case 4: // CLIR temporary mode presentation allowed // the 'n' parameter from TS 27.007 7.7 switch (clirArgs[0]) { default: case 0: // Default sb.append(context.getText(com.android.internal.R.string.CLIRDefaultOffNextCallOff)); break; case 1: // CLIR invocation sb.append(context.getText(com.android.internal.R.string.CLIRDefaultOffNextCallOn)); break; case 2: // CLIR suppression sb.append(context.getText(com.android.internal.R.string.CLIRDefaultOffNextCallOff)); break; } state = State.COMPLETE; break; } } message = sb; phone.onMMIDone(this); }