// Workaround for Samsung CDMA "ring of death" bug: // // Symptom: As soon as the phone receives notice of an incoming call, an // audible "old fashioned ring" is emitted through the earpiece and // persists through the duration of the call, or until reboot if the call // isn't answered. // // Background: The CDMA telephony stack implements a number of "signal info // tones" that are locally generated by ToneGenerator and mixed into the // voice call path in response to radio RIL_UNSOL_CDMA_INFO_REC requests. // One of these tones, IS95_CONST_IR_SIG_IS54B_L, is requested by the // radio just prior to notice of an incoming call when the voice call // path is muted. CallNotifier is responsible for stopping all signal // tones (by "playing" the TONE_CDMA_SIGNAL_OFF tone) upon receipt of a // "new ringing connection", prior to unmuting the voice call path. // // Problem: CallNotifier's incoming call path is designed to minimize // latency to notify users of incoming calls ASAP. Thus, // SignalInfoTonePlayer requests are handled asynchronously by spawning a // one-shot thread for each. Unfortunately the ToneGenerator API does // not provide a mechanism to specify an ordering on requests, and thus, // unexpected thread interleaving may result in ToneGenerator processing // them in the opposite order that CallNotifier intended. In this case, // playing the "signal off" tone first, followed by playing the "old // fashioned ring" indefinitely. // // Solution: An API change to ToneGenerator is required to enable // SignalInfoTonePlayer to impose an ordering on requests (i.e., drop any // request that's older than the most recent observed). Such a change, // or another appropriate fix should be implemented in AOSP first. // // Workaround: Intercept RIL_UNSOL_CDMA_INFO_REC requests from the radio, // check for a signal info record matching IS95_CONST_IR_SIG_IS54B_L, and // drop it so it's never seen by CallNotifier. If other signal tones are // observed to cause this problem, they should be dropped here as well. @Override protected void notifyRegistrantsCdmaInfoRec(CdmaInformationRecords infoRec) { final int response = RIL_UNSOL_CDMA_INFO_REC; if (infoRec.record instanceof CdmaSignalInfoRec) { CdmaSignalInfoRec sir = (CdmaSignalInfoRec) infoRec.record; if (sir != null && sir.isPresent && sir.signalType == SignalToneUtil.IS95_CONST_IR_SIGNAL_IS54B && sir.alertPitch == SignalToneUtil.IS95_CONST_IR_ALERT_MED && sir.signal == SignalToneUtil.IS95_CONST_IR_SIG_IS54B_L) { Rlog.d( RILJ_LOG_TAG, "Dropping \"" + responseToString(response) + " " + retToString(response, sir) + "\" to prevent \"ring of death\" bug."); return; } } super.notifyRegistrantsCdmaInfoRec(infoRec); }
@Override public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) { if (!dialCode) { super.dial(address, clirMode, uusInfo, result); return; } RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result); rr.mParcel.writeString(address); rr.mParcel.writeInt(clirMode); rr.mParcel.writeInt(0); rr.mParcel.writeInt(1); rr.mParcel.writeString(""); if (uusInfo == null) { rr.mParcel.writeInt(0); // UUS information is absent } else { rr.mParcel.writeInt(1); // UUS information is present rr.mParcel.writeInt(uusInfo.getType()); rr.mParcel.writeInt(uusInfo.getDcs()); rr.mParcel.writeByteArray(uusInfo.getUserData()); } if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); }
static String requestToString(int request) { switch (request) { case RIL_REQUEST_DIAL_EMERGENCY: return "DIAL_EMERGENCY"; default: return RIL.requestToString(request); } }
@Override protected void processUnsolicited(Parcel p) { Object ret; int dataPosition = p.dataPosition(); // save off position within the Parcel int response = p.readInt(); switch (response) { case RIL_UNSOL_RIL_CONNECTED: // Fix for NV/RUIM setting on CDMA SIM devices // skip getcdmascriptionsource as if qualcomm handles it in the ril binary ret = responseInts(p); setRadioPower(false, null); setPreferredNetworkType(mPreferredNetworkType, null); int cdmaSubscription = Settings.Global.getInt( mContext.getContentResolver(), Settings.Global.CDMA_SUBSCRIPTION_MODE, -1); if (cdmaSubscription != -1) { setCdmaSubscriptionSource(mCdmaSubscription, null); } setCellInfoListRate(Integer.MAX_VALUE, null); notifyRegistrantsRilConnectionChanged(((int[]) ret)[0]); break; case RIL_UNSOL_NITZ_TIME_RECEIVED: handleNitzTimeReceived(p); break; // SAMSUNG STATES case SamsungExynos4RIL.RIL_UNSOL_AM: ret = responseString(p); String amString = (String) ret; Rlog.d(RILJ_LOG_TAG, "Executing AM: " + amString); try { Runtime.getRuntime().exec("am " + amString); } catch (IOException e) { e.printStackTrace(); Rlog.e(RILJ_LOG_TAG, "am " + amString + " could not be executed."); } break; case SamsungExynos4RIL.RIL_UNSOL_RESPONSE_HANDOVER: ret = responseVoid(p); break; case 1036: ret = responseVoid(p); break; case SamsungExynos4RIL.RIL_UNSOL_WB_AMR_STATE: ret = responseInts(p); setWbAmr(((int[]) ret)[0]); break; default: // Rewind the Parcel p.setDataPosition(dataPosition); // Forward responses that we are not overriding to the super class super.processUnsolicited(p); return; } }
@Override protected RadioState getRadioStateFromInt(int stateInt) { if (!oldRilState) super.getRadioStateFromInt(stateInt); RadioState state; /* RIL_RadioState ril.h */ switch (stateInt) { case 0: state = RadioState.RADIO_OFF; break; case 1: case 2: state = RadioState.RADIO_UNAVAILABLE; break; case 4: // When SIM is PIN-unlocked, RIL doesn't respond with RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED. // We notify the system here. Rlog.d(RILJ_LOG_TAG, "SIM is PIN-unlocked now"); if (mIccStatusChangedRegistrants != null) { mIccStatusChangedRegistrants.notifyRegistrants(); } case 3: case 5: case 6: case 7: case 8: case 9: case 10: case 13: state = RadioState.RADIO_ON; break; default: throw new RuntimeException("Unrecognized RIL_RadioState: " + stateInt); } return state; }
@Override protected void processUnsolicited(Parcel p) { Object ret; int dataPosition = p.dataPosition(); int response = p.readInt(); switch (response) { case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: ret = responseVoid(p); break; case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS: ret = responseString(p); break; case RIL_UNSOL_RIL_CONNECTED: ret = responseInts(p); break; // SAMSUNG STATES case RIL_UNSOL_AM: ret = responseString(p); break; case RIL_UNSOL_DUN_PIN_CONTROL_SIGNAL: ret = responseVoid(p); break; case RIL_UNSOL_DATA_SUSPEND_RESUME: ret = responseInts(p); break; case RIL_UNSOL_STK_CALL_CONTROL_RESULT: ret = responseVoid(p); break; case RIL_UNSOL_TWO_MIC_STATE: ret = responseInts(p); break; case RIL_UNSOL_WB_AMR_STATE: ret = responseInts(p); break; default: // Rewind the Parcel p.setDataPosition(dataPosition); // Forward responses that we are not overriding to the super class super.processUnsolicited(p); return; } switch (response) { case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: /* has bonus radio state int */ int state = p.readInt(); Log.d(LOG_TAG, "Radio state: " + state); switch (state) { case 2: // RADIO_UNAVAILABLE state = 1; break; case 3: // RADIO_ON state = 10; break; case 4: // RADIO_ON state = 10; // When SIM is PIN-unlocked, RIL doesn't respond with // RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED. // We notify the system here. Log.d(LOG_TAG, "SIM is PIN-unlocked now"); if (mIccStatusChangedRegistrants != null) { mIccStatusChangedRegistrants.notifyRegistrants(); } break; } RadioState newState = getRadioStateFromInt(state); Log.d(LOG_TAG, "New Radio state: " + state + " (" + newState.toString() + ")"); switchToRadioState(newState); break; case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS: if (RILJ_LOGD) unsljLogRet(response, ret); if (mGsmBroadcastSmsRegistrant != null) { mGsmBroadcastSmsRegistrant.notifyRegistrant(new AsyncResult(null, ret, null)); } break; case RIL_UNSOL_RIL_CONNECTED: if (RILJ_LOGD) unsljLogRet(response, ret); // Initial conditions setRadioPower(false, null); sendPreferedNetworktype(mPreferredNetworkType, null); setCdmaSubscriptionSource(mCdmaSubscription, null); notifyRegistrantsRilConnectionChanged(((int[]) ret)[0]); break; // SAMSUNG STATES case RIL_UNSOL_AM: if (RILJ_LOGD) samsungUnsljLogRet(response, ret); String amString = (String) ret; Log.d(LOG_TAG, "Executing AM: " + amString); try { Runtime.getRuntime().exec("am " + amString); } catch (IOException e) { e.printStackTrace(); Log.e(LOG_TAG, "am " + amString + " could not be executed."); } break; case RIL_UNSOL_DUN_PIN_CONTROL_SIGNAL: if (RILJ_LOGD) samsungUnsljLogRet(response, ret); break; case RIL_UNSOL_DATA_SUSPEND_RESUME: if (RILJ_LOGD) samsungUnsljLogRet(response, ret); break; case RIL_UNSOL_STK_CALL_CONTROL_RESULT: if (RILJ_LOGD) samsungUnsljLogRet(response, ret); break; case RIL_UNSOL_TWO_MIC_STATE: if (RILJ_LOGD) samsungUnsljLogRet(response, ret); break; case RIL_UNSOL_WB_AMR_STATE: if (RILJ_LOGD) samsungUnsljLogRet(response, ret); setWbAmr(((int[]) ret)[0]); break; } }
@Override public void setPhoneType(int phoneType) { super.setPhoneType(phoneType); isGSM = (phoneType != RILConstants.CDMA_PHONE); }
@Override public void sendSMS(String smscPDU, String pdu, Message result) { smsLock(); super.sendSMS(smscPDU, pdu, result); }
@Override public void sendCdmaSms(byte[] pdu, Message result) { smsLock(); super.sendCdmaSms(pdu, result); }