blob: 1acd593ed458ab3dfda4626adaf817c4783460f4 [file] [log] [blame]
/*
* Copyright (C) 2020 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.internal.telephony.vendor.dataconnection;
import android.app.AlertDialog;
import android.telephony.AccessNetworkConstants;
import android.telephony.DataFailCause;
import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.view.WindowManager;
import com.android.internal.telephony.DctConstants;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.dataconnection.ApnContext;
import com.android.internal.telephony.dataconnection.DcTracker;
import java.util.HashSet;
import java.util.Iterator;
public class VendorDcTracker extends DcTracker {
private String LOG_TAG = "VendorDCT";
private HashSet<String> mIccidSet = new HashSet<String>();
private int mTransportType = AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
// Maximum data reject count
public static final int MAX_PDP_REJECT_COUNT = 3;
// Data reset event tracker to know reset events.
private VendorDataResetEventTracker mVendorDataResetEventTracker = null;
// data reject dialog, made static because only one dialog object can be
// used between multiple dataconnection objects.
protected static AlertDialog mDataRejectDialog = null;
//Store data reject cause for comparison
private String mDataRejectReason = "NONE";
//Store data reject count
private int mDataRejectCount = 0;
//Store data reject cause code
private int mPdpRejectCauseCode = 0;
// Constructor
public VendorDcTracker(Phone phone, int transportType) {
super(phone, transportType);
mTransportType = transportType;
LOG_TAG = LOG_TAG + "-" +
((transportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) ? "C" : "I");
if (DBG) log(LOG_TAG + ".constructor");
fillIccIdSet();
}
@Override
protected boolean allowInitialAttachForOperator() {
String iccId = mPhone.getIccSerialNumber();
if (iccId != null) {
Iterator<String> itr = mIccidSet.iterator();
while (itr.hasNext()) {
if (iccId.contains(itr.next())) {
return false;
}
}
}
return true;
}
private void fillIccIdSet() {
mIccidSet.add("8991840");
mIccidSet.add("8991854");
mIccidSet.add("8991855");
mIccidSet.add("8991856");
mIccidSet.add("8991857");
mIccidSet.add("8991858");
mIccidSet.add("8991859");
mIccidSet.add("899186");
mIccidSet.add("8991870");
mIccidSet.add("8991871");
mIccidSet.add("8991872");
mIccidSet.add("8991873");
mIccidSet.add("8991874");
}
@Override
protected void onVoiceCallEnded() {
if (DBG) log("onVoiceCallEnded");
mInVoiceCall = false;
if (isAnyDataConnected()) {
if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
startNetStatPoll();
startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
mPhone.notifyAllActiveDataConnections();
} else {
// clean slate after call end.
resetPollStats();
}
}
//Allow data call retry only on DDS sub
if (mPhone.getSubId() == SubscriptionManager.getDefaultDataSubscriptionId()) {
// reset reconnect timer
setupDataOnAllConnectableApns(Phone.REASON_VOICE_CALL_ENDED, RetryFailures.ALWAYS);
}
}
@Override
protected void setupDataOnConnectableApn(ApnContext apnContext, String reason,
RetryFailures retryFailures) {
if (mPhone.getContext().getResources().getBoolean(
com.android.internal.R.bool.config_pdp_reject_enable_retry) &&
mDataRejectCount > 0) {
log("setupDataOnConnectableApn: data retry in progress, skip processing");
} else {
super.setupDataOnConnectableApn(apnContext, reason, retryFailures);
}
}
@Override
protected void onDataSetupComplete(ApnContext apnContext, boolean success, int cause,
@RequestNetworkType int requestType) {
boolean isPdpRejectConfigEnabled = mPhone.getContext().getResources().getBoolean(
com.android.internal.R.bool.config_pdp_reject_enable_retry);
if (success) {
if (isPdpRejectConfigEnabled) {
handlePdpRejectCauseSuccess();
}
} else {
mPdpRejectCauseCode = cause;
}
super.onDataSetupComplete(apnContext, success, cause, requestType);
}
@Override
protected void onDataSetupCompleteError(ApnContext apnContext,
@RequestNetworkType int requestType) {
long delay = apnContext.getDelayForNextApn(mFailFast);
if (mPhone.getContext().getResources().getBoolean(
com.android.internal.R.bool.config_pdp_reject_enable_retry)) {
String reason = DataFailCause.toString(mPdpRejectCauseCode);
if (isMatchingPdpRejectCause(reason)) {
if (mVendorDataResetEventTracker == null) {
mVendorDataResetEventTracker = new VendorDataResetEventTracker(mTransportType,
mPhone, mResetEventListener);
}
if (mDataRejectCount == 0) {
mVendorDataResetEventTracker.startResetEventTracker();
}
boolean isHandled = handlePdpRejectCauseFailure(reason);
/* If MAX Reject count reached, display pop-up to user */
if (MAX_PDP_REJECT_COUNT <= mDataRejectCount) {
if (DBG) log("onDataSetupCompleteError: reached max retry count");
displayPopup(mDataRejectReason);
delay = -1;
} else if (isHandled) {
delay = mPhone.getContext().getResources().getInteger(
com.android.internal.R.integer.config_pdp_reject_retry_delay_ms);
if (DBG) log("onDataSetupCompleteError: delay from config: " + delay);
}
} else {
if (DBG) log("onDataSetupCompleteError: reset reject count");
resetDataRejectCounter();
}
}
// Check if we need to retry or not.
// TODO: We should support handover retry in the future.
if (delay >= 0) {
if (DBG) log("onDataSetupCompleteError: Try next APN. delay = " + delay);
apnContext.setState(DctConstants.State.RETRYING);
// Wait a bit before trying the next APN, so that
// we're not tying up the RIL command channel
startReconnect(delay, apnContext);
} else {
// If we are not going to retry any APN, set this APN context to failed state.
// This would be the final state of a data connection.
apnContext.setState(DctConstants.State.FAILED);
mPhone.notifyDataConnection(apnContext.getApnType());
apnContext.setDataConnection(null);
if (DBG) log("onDataSetupCompleteError: Stop retrying APNs. delay=" + delay
+ ", requestType=" + requestTypeToString(requestType));
}
}
/*
* Reset data reject params on data call success
*/
private void handlePdpRejectCauseSuccess() {
if (mDataRejectCount > 0) {
if (DBG) log("handlePdpRejectCauseSuccess: reset reject count");
resetDataRejectCounter();
}
}
/*
* Process data failure if RAT is WCDMA
* And if the failure cause matches one of the following cause codes:
* 1. USER_AUTHENTICATION
* 2. SERVICE_OPTION_NOT_SUBSCRIBED
* 3. MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED
*/
private boolean handlePdpRejectCauseFailure(String reason) {
boolean handleFailure = false;
// Check if data rat is WCDMA
if (isWCDMA(getDataRat())) {
if (DBG) log("handlePdpRejectCauseFailure: reason=" + reason +
", mDataRejectReason=" + mDataRejectReason);
/*
* If previously rejected code is not same as current data reject reason,
* then reset the count and reset the reject reason
*/
if (!reason.equalsIgnoreCase(mDataRejectReason)) {
resetDataRejectCounter();
}
/*
* If failure reason is USER_AUTHENTICATION or
* SERVICE_OPTION_NOT_SUBSCRIBED or MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED,
* increment counter and store reject cause
*/
if (isMatchingPdpRejectCause(reason)) {
mDataRejectCount++;
mDataRejectReason = reason;
if (DBG) log ("handlePdpRejectCauseFailure: DataRejectCount = " +
mDataRejectCount);
handleFailure = true;
}
} else {
if (DBG) log("isPdpRejectCauseFailureHandled: DataConnection not on wcdma");
resetDataRejectCounter();
}
return handleFailure;
}
/*
* Data reset event listener. Dc will get get onResetEvent
* whenever any data reset event occurs
*/
private VendorDataResetEventTracker.ResetEventListener mResetEventListener =
new VendorDataResetEventTracker.ResetEventListener() {
@Override
public void onResetEvent(boolean retry) {
if (DBG) log("onResetEvent: retry=" + retry);
//Dismiss dialog
if (mDataRejectDialog != null && mDataRejectDialog.isShowing()) {
if (DBG) log("onResetEvent: Dismiss dialog");
mDataRejectDialog.dismiss();
}
mVendorDataResetEventTracker.stopResetEventTracker();
for (ApnContext apnContext : mApnContexts.values()) {
if (mDataRejectCount > 0) {
if (DBG) log("onResetEvent: reset reject count=" + mDataRejectCount);
resetDataRejectCounter();
cancelReconnect(apnContext);
if (retry) {
if (DBG) log("onResetEvent: retry data call on apnContext=" + apnContext);
sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, apnContext));
}
}
}
}
};
/**
* This function will display the pdp reject message
*/
private void displayPopup(String pdpRejectCause) {
if (DBG) log("displayPopup : " + pdpRejectCause);
String title = mPhone.getContext().getResources().
getString(com.android.internal.R.string.config_pdp_reject_dialog_title);
String message = null;
if (pdpRejectCause.equalsIgnoreCase("USER_AUTHENTICATION")) {
message = mPhone.getContext().getResources().
getString(com.android.internal.R.string.config_pdp_reject_user_authentication_failed);
} else if (pdpRejectCause.equalsIgnoreCase("SERVICE_OPTION_NOT_SUBSCRIBED")) {
message = mPhone.getContext().getResources().getString(
com.android.internal.R.string.config_pdp_reject_service_not_subscribed);
} else if (pdpRejectCause.equalsIgnoreCase("MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED")) {
message = mPhone.getContext().getResources().getString(
com.android.internal.R.string.config_pdp_reject_multi_conn_to_same_pdn_not_allowed);
}
if (mDataRejectDialog == null || !mDataRejectDialog.isShowing()) {
AlertDialog.Builder builder = new AlertDialog.Builder(
mPhone.getContext());
builder.setPositiveButton(android.R.string.ok, null);
mDataRejectDialog = builder.create();
}
mDataRejectDialog.setMessage(message);
mDataRejectDialog.setCanceledOnTouchOutside(false);
mDataRejectDialog.setTitle(title);
mDataRejectDialog.getWindow().setType(
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
mDataRejectDialog.show();
}
/*
* returns true if data reject cause matches errors listed
*/
private boolean isMatchingPdpRejectCause(String reason) {
return reason.equalsIgnoreCase("USER_AUTHENTICATION") ||
reason.equalsIgnoreCase("SERVICE_OPTION_NOT_SUBSCRIBED") ||
reason.equalsIgnoreCase("MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED");
}
/**
* returns true if radioTechnology is WCDMA rat, else false
*/
private boolean isWCDMA(int radioTechnology) {
return radioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_UMTS
|| radioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA
|| radioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA
|| radioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_HSPA
|| radioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP;
}
/*
* Reset data reject count and reason
*/
private void resetDataRejectCounter() {
mDataRejectCount = 0;
mDataRejectReason = "NONE";
}
@Override
protected void log(String s) {
Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
}
}