Merge "Add new vendor prefix files"
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index d1f8ea2..18006710 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -3670,7 +3670,7 @@
cdmaApplication.getType() == AppType.APPTYPE_RUIM);
}
- private void phoneObjectUpdater(int newVoiceRadioTech) {
+ protected void phoneObjectUpdater(int newVoiceRadioTech) {
logd("phoneObjectUpdater: newVoiceRadioTech=" + newVoiceRadioTech);
// Check for a voice over LTE/NR replacement
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index 9b181d3..5aa6237 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -169,8 +169,9 @@
sUiccController = UiccController.make(context);
Rlog.i(LOG_TAG, "Creating SubscriptionController");
- SubscriptionController sc = SubscriptionController.init(context);
- MultiSimSettingController.init(context, sc);
+ TelephonyComponentFactory.getInstance().inject(SubscriptionController.class.
+ getName()).initSubscriptionController(context);
+ MultiSimSettingController.init(context, SubscriptionController.getInstance());
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TELEPHONY_EUICC)) {
@@ -228,7 +229,9 @@
int maxActivePhones = sPhoneConfigurationManager
.getNumberOfModemsWithSimultaneousDataConnections();
- sPhoneSwitcher = PhoneSwitcher.make(maxActivePhones, sContext, Looper.myLooper());
+ sPhoneSwitcher = TelephonyComponentFactory.getInstance().inject(
+ PhoneSwitcher.class.getName()).
+ makePhoneSwitcher(maxActivePhones, sContext, Looper.myLooper());
sProxyController = ProxyController.getInstance(context);
@@ -285,8 +288,10 @@
// We always use PHONE_TYPE_CDMA_LTE now.
if (phoneType == PHONE_TYPE_CDMA) phoneType = PHONE_TYPE_CDMA_LTE;
+ TelephonyComponentFactory injectedComponentFactory =
+ TelephonyComponentFactory.getInstance().inject(GsmCdmaPhone.class.getName());
- return new GsmCdmaPhone(context,
+ return injectedComponentFactory.makePhone(context,
sCommandsInterfaces[phoneId], sPhoneNotifier, phoneId, phoneType,
TelephonyComponentFactory.getInstance());
}
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index b8b4a8c..b2ccb99 100755
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -446,7 +446,7 @@
//Common
@UnsupportedAppUsage
- private final GsmCdmaPhone mPhone;
+ protected final GsmCdmaPhone mPhone;
private CellIdentity mCellIdentity;
private CellIdentity mNewCellIdentity;
@@ -2076,7 +2076,7 @@
}
}
- void handlePollStateResultMessage(int what, AsyncResult ar) {
+ protected void handlePollStateResultMessage(int what, AsyncResult ar) {
int ints[];
switch (what) {
case EVENT_POLL_STATE_CS_CELLULAR_REGISTRATION: {
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index 7d6a073..00690ef 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -133,7 +133,7 @@
protected final Object mLock = new Object();
/** The singleton instance. */
- private static SubscriptionController sInstance = null;
+ protected static SubscriptionController sInstance = null;
@UnsupportedAppUsage
protected Context mContext;
protected TelephonyManager mTelephonyManager;
@@ -143,7 +143,7 @@
// Each slot can have multiple subs.
private static Map<Integer, ArrayList<Integer>> sSlotIndexToSubIds = new ConcurrentHashMap<>();
- private static int mDefaultFallbackSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ protected static int mDefaultFallbackSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@UnsupportedAppUsage
private static int mDefaultPhoneId = SubscriptionManager.DEFAULT_PHONE_INDEX;
@@ -247,7 +247,7 @@
}
@UnsupportedAppUsage
- private void enforceModifyPhoneState(String message) {
+ protected void enforceModifyPhoneState(String message) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE, message);
}
@@ -2150,7 +2150,7 @@
}
@UnsupportedAppUsage
- private void logdl(String msg) {
+ protected void logdl(String msg) {
logd(msg);
mLocalLog.log(msg);
}
@@ -2383,7 +2383,7 @@
* the first sub is set as default subscription
*/
@UnsupportedAppUsage
- private void setDefaultFallbackSubId(int subId, int subscriptionType) {
+ protected void setDefaultFallbackSubId(int subId, int subscriptionType) {
if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
throw new RuntimeException("setDefaultSubId called with DEFAULT_SUB_ID");
}
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index b84a407..979f29c 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -420,4 +420,20 @@
public DataEnabledSettings makeDataEnabledSettings(Phone phone) {
return new DataEnabledSettings(phone);
}
+
+ public Phone makePhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
+ int phoneId, int precisePhoneType,
+ TelephonyComponentFactory telephonyComponentFactory) {
+ return new GsmCdmaPhone(context, ci, notifier, phoneId, precisePhoneType,
+ telephonyComponentFactory);
+ }
+
+ public SubscriptionController initSubscriptionController(Context c) {
+ return SubscriptionController.init(c);
+ }
+
+ public PhoneSwitcher makePhoneSwitcher(int maxDataAttachModemCount, Context context,
+ Looper looper) {
+ return PhoneSwitcher.make(maxDataAttachModemCount, context, looper);
+ }
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index cb0d0a9..0251e90 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -586,9 +586,9 @@
private RegistrantList mAllDataDisconnectedRegistrants = new RegistrantList();
// member variables
- private final Phone mPhone;
+ protected final Phone mPhone;
private final UiccController mUiccController;
- private final AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>();
+ protected final AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>();
private DctConstants.Activity mActivity = DctConstants.Activity.NONE;
private DctConstants.State mState = DctConstants.State.IDLE;
private final Handler mDataConnectionTracker;
@@ -621,7 +621,7 @@
private PendingIntent mReconnectIntent = null;
// When false we will not auto attach and manually attaching is required.
- private boolean mAutoAttachOnCreationConfig = false;
+ protected boolean mAutoAttachOnCreationConfig = false;
private AtomicBoolean mAutoAttachEnabled = new AtomicBoolean(false);
// State of screen
@@ -1495,7 +1495,7 @@
}
// arg for setupDataOnAllConnectableApns
- private enum RetryFailures {
+ protected enum RetryFailures {
// retry failed networks always (the old default)
ALWAYS,
// retry only when a substantial change has occurred. Either:
@@ -1504,7 +1504,7 @@
ONLY_ON_CHANGE
};
- private void setupDataOnAllConnectableApns(String reason, RetryFailures retryFailures) {
+ protected void setupDataOnAllConnectableApns(String reason, RetryFailures retryFailures) {
if (VDBG) log("setupDataOnAllConnectableApns: " + reason);
if (DBG && !VDBG) {
@@ -2056,7 +2056,7 @@
return true;
}
- private void setInitialAttachApn() {
+ protected void setInitialAttachApn() {
ApnSetting iaApnSetting = null;
ApnSetting defaultApnSetting = null;
ApnSetting firstNonEmergencyApnSetting = null;
@@ -2088,6 +2088,12 @@
}
}
+ if ((iaApnSetting == null) && (defaultApnSetting == null) &&
+ !allowInitialAttachForOperator()) {
+ log("Abort Initial attach");
+ return;
+ }
+
// The priority of apn candidates from highest to lowest is:
// 1) APN_TYPE_IA (Initial Attach)
// 2) mPreferredApn, i.e. the current preferred apn
@@ -2120,6 +2126,10 @@
}
}
+ protected boolean allowInitialAttachForOperator() {
+ return true;
+ }
+
/**
* Handles changes to the APN database.
*/
@@ -2269,7 +2279,7 @@
removeMessages(DctConstants.EVENT_DATA_RECONNECT, apnContext);
}
- private void onRecordsLoadedOrSubIdChanged() {
+ protected void onRecordsLoadedOrSubIdChanged() {
if (DBG) log("onRecordsLoadedOrSubIdChanged: createAllApnList");
if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
// Auto attach is for cellular only.
@@ -3104,7 +3114,7 @@
return true;
}
- private void setDataProfilesAsNeeded() {
+ protected void setDataProfilesAsNeeded() {
if (DBG) log("setDataProfilesAsNeeded");
ArrayList<DataProfile> dataProfileList = new ArrayList<>();
@@ -3131,7 +3141,7 @@
* Based on the sim operator numeric, create a list for all possible
* Data Connections and setup the preferredApn.
*/
- private void createAllApnList() {
+ protected void createAllApnList() {
mAllApnSettings.clear();
IccRecords r = mIccRecords.get();
String operator = (r != null) ? r.getOperatorNumeric() : "";
@@ -4069,7 +4079,7 @@
|| plan.getDataLimitBehavior() == SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED);
}
- private void log(String s) {
+ protected void log(String s) {
Rlog.d(mLogTag, s);
}
diff --git a/src/java/com/android/internal/telephony/vendor/VendorGsmCdmaPhone.java b/src/java/com/android/internal/telephony/vendor/VendorGsmCdmaPhone.java
new file mode 100644
index 0000000..0c92159
--- /dev/null
+++ b/src/java/com/android/internal/telephony/vendor/VendorGsmCdmaPhone.java
@@ -0,0 +1,175 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.os.AsyncResult;
+import android.os.Message;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.GsmCdmaPhone;
+import com.android.internal.telephony.PhoneNotifier;
+import com.android.internal.telephony.TelephonyComponentFactory;
+
+public class VendorGsmCdmaPhone extends GsmCdmaPhone {
+ private static final String LOG_TAG = "VendorGsmCdmaPhone";
+ private static final int PROP_EVENT_START = EVENT_LAST;
+ private static final int DEFAULT_PHONE_INDEX = 0;
+
+ private boolean mIsPhoneReadySent = false;
+ private boolean mIsPhoneReadyPending = false;
+ private static int READY = 1;
+
+ public VendorGsmCdmaPhone(Context context,
+ CommandsInterface ci, PhoneNotifier notifier, int phoneId,
+ int precisePhoneType, TelephonyComponentFactory telephonyComponentFactory) {
+ this(context, ci, notifier, false, phoneId, precisePhoneType,
+ telephonyComponentFactory);
+ }
+
+ public VendorGsmCdmaPhone(Context context,
+ CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode, int phoneId,
+ int precisePhoneType, TelephonyComponentFactory telephonyComponentFactory) {
+ super(context, ci, notifier, unitTestMode, phoneId, precisePhoneType,
+ telephonyComponentFactory);
+ Rlog.d(LOG_TAG, "Constructor");
+ }
+
+ @Override
+ protected void phoneObjectUpdater(int newVoiceTech) {
+ super.phoneObjectUpdater(newVoiceTech);
+ }
+
+ @Override
+ public boolean getCallForwardingIndicator() {
+ if (!isCurrentSubValid()) {
+ return false;
+ }
+ return super.getCallForwardingIndicator();
+ }
+
+ private boolean isCurrentSubValid() {
+ boolean isUiccApplicationEnabled = true;
+ // FIXME get the SubscriptionManager.UICC_APPLICATIONS_ENABLED value and use it above
+
+ SubscriptionManager subscriptionManager = SubscriptionManager.from(mContext);
+
+ Rlog.d(LOG_TAG, "ProvisionStatus: " + isUiccApplicationEnabled + " phone id:" + mPhoneId);
+ return subscriptionManager.isActiveSubscriptionId(getSubId()) && isUiccApplicationEnabled;
+ }
+
+ public void fetchIMEI() {
+ Rlog.d(LOG_TAG, "fetching device id");
+ mCi.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE));
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ Rlog.d(LOG_TAG, "handleMessage: Event: " + msg.what);
+ AsyncResult ar;
+ switch(msg.what) {
+
+ case EVENT_SIM_RECORDS_LOADED:
+ if(isPhoneTypeGsm()) {
+ Rlog.d(LOG_TAG, "notify call forward indication, phone id:" + mPhoneId);
+ notifyCallForwardingIndicator();
+ }
+
+ super.handleMessage(msg);
+ break;
+
+ case EVENT_RADIO_AVAILABLE:
+ mIsPhoneReadySent = false;
+ super.handleMessage(msg);
+ break;
+
+ case EVENT_RIL_CONNECTED:
+ mIsPhoneReadySent = false;
+ super.handleMessage(msg);
+ break;
+
+ default: {
+ super.handleMessage(msg);
+ }
+
+ }
+ }
+
+ // In DSDA, char 'D' is used as DTMF char for playing supervisory tone for G/W.
+ // For CDMA, '#' is used. A, B, C & D are also supported as DTMF digits for G/W networks.
+ @Override
+ public void startDtmf(char c) {
+ if (!(PhoneNumberUtils.is12Key(c) || (c == 'D'))) {
+ Rlog.e(LOG_TAG, "startDtmf called with invalid character '" + c + "'");
+ } else {
+ if (isPhoneTypeCdma() && c == 'D') {
+ c = '#';
+ }
+ mCi.startDtmf(c, null);
+ }
+ }
+
+ // For CDMA sendBurstDtmf is used, if dtmf char is 'D' then it with '#'
+ // since 'D' is used for SCH tone and for CDMA it has to be '#'.
+ @Override
+ public void sendBurstDtmf(String dtmfString, int on, int off, Message onComplete) {
+ Character c = dtmfString.charAt(0);
+ if(dtmfString.length() == 1 && c == 'D') {
+ dtmfString = c.toString();
+ }
+ super.sendBurstDtmf(dtmfString, on, off, onComplete);
+ }
+
+ // When OOS occurs, IMS registration may be still available so that IMS service
+ // state is also in-service, then reports in-service to upper layer.
+ // Add a precondition to merge IMS service so that notifies proper service state
+ // after IMS changes RAT.
+ @Override
+ public ServiceState getServiceState() {
+ if (mSST == null || mSST.mSS.getState() != ServiceState.STATE_IN_SERVICE) {
+ // Ensure UE has IMS service capability, then merge IMS service state.
+ // Video enabled includes WIFI video
+ final boolean isImsEnabled = mImsPhone != null && (mImsPhone.isVolteEnabled()
+ || mImsPhone.isVideoEnabled()
+ || mImsPhone.isWifiCallingEnabled());
+ if (isImsEnabled) {
+ return ServiceState.mergeServiceStates(
+ ((mSST == null) ? new ServiceState() : mSST.mSS),
+ mImsPhone.getServiceState());
+ }
+ }
+
+ if (mSST != null) {
+ return mSST.mSS;
+ } else {
+ // avoid potential NPE in EmergencyCallHelper during Phone switch
+ return new ServiceState();
+ }
+ }
+
+ private void logd(String msg) {
+ Rlog.d(LOG_TAG, "[" + mPhoneId +" ] " + msg);
+ }
+
+ private void loge(String msg) {
+ Rlog.e(LOG_TAG, "[" + mPhoneId +" ] " + msg);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/vendor/VendorServiceStateTracker.java b/src/java/com/android/internal/telephony/vendor/VendorServiceStateTracker.java
new file mode 100644
index 0000000..de9d42c
--- /dev/null
+++ b/src/java/com/android/internal/telephony/vendor/VendorServiceStateTracker.java
@@ -0,0 +1,94 @@
+/*
+ * 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;
+
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.os.AsyncResult;
+import android.os.Message;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.GsmCdmaPhone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.ServiceStateTracker;
+
+public class VendorServiceStateTracker extends ServiceStateTracker {
+ private static final String LOG_TAG = "VendorServiceStateTracker";
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false; // STOPSHIP if true
+ private static final String ACTION_MANAGED_ROAMING_IND =
+ "android.intent.action.ACTION_MANAGED_ROAMING_IND";
+
+ public VendorServiceStateTracker(GsmCdmaPhone phone, CommandsInterface ci) {
+ super(phone,ci);
+ }
+
+ @Override
+ protected void handlePollStateResultMessage(int what, AsyncResult ar) {
+ switch (what) {
+ case EVENT_POLL_STATE_CS_CELLULAR_REGISTRATION: {
+ super.handlePollStateResultMessage(what, ar);
+ if (mPhone.isPhoneTypeGsm()) {
+ NetworkRegistrationInfo regStates = (NetworkRegistrationInfo) ar.result;
+ int regState = regStates.getRegistrationState();
+
+ if (regState == NetworkRegistrationInfo.REGISTRATION_STATE_DENIED) {
+ int rejCode = regStates.getRejectCause();
+ // Check if rejCode is "Persistent location update reject",
+ if (rejCode == 10) {
+ log(" Posting Managed roaming intent sub = "
+ + mPhone.getSubId());
+ try {
+ Intent intent =
+ new Intent(ACTION_MANAGED_ROAMING_IND);
+ // component would display Dialog to perform Manual scan
+ // if current Network selection Mode is Manual.
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY,
+ mPhone.getSubId());
+ mPhone.getContext().startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ loge("unable to start activity: " + e);
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ default:
+ super.handlePollStateResultMessage(what, ar);
+ }
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == EVENT_RADIO_STATE_CHANGED) {
+ if (mPhone.mCi.getRadioState() == TelephonyManager.RADIO_POWER_OFF) {
+ setPowerStateToDesired();
+ log("Trigger as manual polling");
+ pollState();
+ } else {
+ super.handleMessage(msg);
+ }
+ } else {
+ super.handleMessage(msg);
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/vendor/VendorSubscriptionController.java b/src/java/com/android/internal/telephony/vendor/VendorSubscriptionController.java
new file mode 100644
index 0000000..37fa9af
--- /dev/null
+++ b/src/java/com/android/internal/telephony/vendor/VendorSubscriptionController.java
@@ -0,0 +1,384 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.content.Intent;
+import android.Manifest;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Registrant;
+import android.os.RegistrantList;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.PhoneFactory;
+
+import java.util.Iterator;
+import java.util.List;
+
+/*
+ * Extending SubscriptionController here:
+ * To implement fall back of sms/data user preferred subId value to next
+ * available subId when current preferred SIM deactivated or removed.
+ */
+public class VendorSubscriptionController extends SubscriptionController {
+ static final String LOG_TAG = "VendorSubscriptionController";
+ private static final boolean DBG = true;
+ private static final boolean VDBG = Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
+
+ private static int sNumPhones;
+
+ private static final int PROVISIONED = 1;
+ private static final int NOT_PROVISIONED = 0;
+
+ private TelecomManager mTelecomManager;
+ private TelephonyManager mTelephonyManager;
+
+ private RegistrantList mAddSubscriptionRecordRegistrants = new RegistrantList();
+
+ private static final String SETTING_USER_PREF_DATA_SUB = "user_preferred_data_sub";
+ /**
+ * This intent would be broadcasted when a subId/slotId pair added to the
+ * sSlotIdxToSubId hashmap.
+ */
+ private static final String ACTION_SUBSCRIPTION_RECORD_ADDED =
+ "android.intent.action.SUBSCRIPTION_INFO_RECORD_ADDED";
+
+ public static VendorSubscriptionController init(Context c) {
+ synchronized (VendorSubscriptionController.class) {
+ if (sInstance == null) {
+ sInstance = new VendorSubscriptionController(c);
+ } else {
+ Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
+ }
+ return (VendorSubscriptionController)sInstance;
+ }
+ }
+
+ public static VendorSubscriptionController getInstance() {
+ if (sInstance == null) {
+ Log.wtf(LOG_TAG, "getInstance null");
+ }
+
+ return (VendorSubscriptionController)sInstance;
+ }
+
+ protected VendorSubscriptionController(Context c) {
+ super(c);
+ if (DBG) logd(" init by Context");
+
+ mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
+ mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ sNumPhones = TelephonyManager.getDefault().getPhoneCount();
+ }
+
+ public void registerForAddSubscriptionRecord(Handler handler, int what, Object obj) {
+ Registrant r = new Registrant(handler, what, obj);
+ synchronized (mAddSubscriptionRecordRegistrants) {
+ mAddSubscriptionRecordRegistrants.add(r);
+ List<SubscriptionInfo> subInfoList =
+ getActiveSubscriptionInfoList(mContext.getOpPackageName());
+ if (subInfoList != null) {
+ r.notifyRegistrant();
+ }
+ }
+ }
+
+ public void unregisterForAddSubscriptionRecord(Handler handler) {
+ synchronized (mAddSubscriptionRecordRegistrants) {
+ mAddSubscriptionRecordRegistrants.remove(handler);
+ }
+ }
+
+ @Override
+ public int addSubInfoRecord(String iccId, int slotIndex) {
+ logd("addSubInfoRecord: broadcast intent subId[" + slotIndex + "]");
+ return addSubInfo(iccId, null, slotIndex, SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+ }
+
+ @Override
+ public int addSubInfo(String uniqueId, String displayName, int slotIndex,
+ int subscriptionType) {
+
+ int retVal = super.addSubInfo(uniqueId, displayName, slotIndex, subscriptionType);
+
+ int[] subId = getSubId(slotIndex);
+ if (subId != null && (subId.length > 0)) {
+ // When a new entry added in sSlotIdxToSubId for slotId, broadcast intent
+ logd("addSubInfoRecord: broadcast intent subId[" + slotIndex + "] = " + subId[0]);
+ mAddSubscriptionRecordRegistrants.notifyRegistrants(
+ new AsyncResult(null, slotIndex, null));
+ Intent intent = new Intent(ACTION_SUBSCRIPTION_RECORD_ADDED);
+ SubscriptionManager.putPhoneIdAndSubIdExtra(intent, slotIndex, subId[0]);
+ mContext.sendBroadcast(intent, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ }
+ return retVal;
+ }
+
+ protected boolean isRadioAvailableOnAllSubs() {
+ for (int i = 0; i < sNumPhones; i++) {
+ if (PhoneFactory.getPhone(i).mCi != null &&
+ PhoneFactory.getPhone(i).mCi.getRadioState() ==
+ TelephonyManager.RADIO_POWER_UNAVAILABLE) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ protected boolean isShuttingDown() {
+ for (int i = 0; i < sNumPhones; i++) {
+ if (PhoneFactory.getPhone(i) != null &&
+ PhoneFactory.getPhone(i).isShuttingDown()) return true;
+ }
+ return false;
+ }
+
+ public boolean isRadioInValidState() {
+
+ // Radio Unavailable, do not updateUserPrefs. As this may happened due to SSR or RIL Crash.
+ if (!isRadioAvailableOnAllSubs()) {
+ logd(" isRadioInValidState, radio not available");
+ return false;
+ }
+
+ //Do not updateUserPrefs when Shutdown is in progress
+ if (isShuttingDown()) {
+ logd(" isRadioInValidState: device shutdown in progress ");
+ return false;
+ }
+ return true;
+ }
+
+ // If any of the voice/data/sms preference related SIM
+ // deactivated/re-activated this will update the preference
+ // with next available/activated SIM.
+ public void updateUserPreferences() {
+ SubscriptionInfo mNextActivatedSub = null;
+ int activeCount = 0;
+ if (!isRadioInValidState()) {
+ logd("Radio is in Invalid state, Ignore Updating User Preference!!!");
+ return;
+ }
+ List<SubscriptionInfo> sil = getActiveSubscriptionInfoList(mContext.getOpPackageName());
+ // If list of active subscriptions empty OR non of the SIM provisioned
+ // clear defaults preference of voice/sms/data.
+ if (sil == null || sil.size() < 1) {
+ logi("updateUserPreferences: Subscription list is empty");
+ return;
+ }
+
+ // Do not fallback to next available sub if AOSP feature
+ // "User choice of selecting data/sms fallback preference" enabled.
+ if (SystemProperties.getBoolean("persist.vendor.radio.aosp_usr_pref_sel", false)) {
+ logi("updateUserPreferences: AOSP user preference option enabled ");
+ return;
+ }
+
+ //Get num of activated Subs and next available activated sub info.
+ for (SubscriptionInfo subInfo : sil) {
+ if (getUiccProvisionStatus(subInfo.getSimSlotIndex()) == PROVISIONED) {
+ activeCount++;
+ if (mNextActivatedSub == null) mNextActivatedSub = subInfo;
+ }
+ }
+ logd("updateUserPreferences:: active sub count = " + activeCount + " dds = "
+ + getDefaultDataSubId() + " voice = " + getDefaultVoiceSubId() +
+ " sms = " + getDefaultSmsSubId());
+
+ // If active SUB count is 1, Always Ask Prompt to be disabled and
+ // preference fallback to the next available SUB.
+ if (activeCount == 1) {
+ setSmsPromptEnabled(false);
+ }
+
+ // TODO Set all prompt options to false ?
+
+ // in Single SIM case or if there are no activated subs available, no need to update. EXIT.
+ if ((mNextActivatedSub == null) || (getActiveSubInfoCountMax() == 1)) return;
+
+ handleDataPreference(mNextActivatedSub.getSubscriptionId());
+
+ if (!isSubProvisioned(getDefaultSmsSubId())) {
+ setDefaultSmsSubId(mNextActivatedSub.getSubscriptionId());
+ }
+ if (!isSubProvisioned(getDefaultVoiceSubId())) {
+ setDefaultVoiceSubId(mNextActivatedSub.getSubscriptionId());
+ }
+
+ // voice preference is handled in such a way that
+ // 1. Whenever current Sub is deactivated or removed It fall backs to
+ // next available Sub.
+ // 2. When device is flashed for the first time, initial voice preference
+ // would be set to always ask.
+ if (!isNonSimAccountFound() && activeCount == 1) {
+ final int subId = mNextActivatedSub.getSubscriptionId();
+ PhoneAccountHandle phoneAccountHandle = subscriptionIdToPhoneAccountHandle(subId);
+ logi("set default phoneaccount to " + subId);
+ mTelecomManager.setUserSelectedOutgoingPhoneAccount(phoneAccountHandle);
+ }
+ if (!isSubProvisioned(mDefaultFallbackSubId)) {
+ setDefaultFallbackSubId(mNextActivatedSub.getSubscriptionId(),
+ SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM);
+ }
+
+ notifySubscriptionInfoChanged();
+ logd("updateUserPreferences: after currentDds = " + getDefaultDataSubId() + " voice = " +
+ getDefaultVoiceSubId() + " sms = " + getDefaultSmsSubId());
+ }
+
+ protected void handleDataPreference(int nextActiveSubId) {
+ int userPrefDataSubId = getUserPrefDataSubIdFromDB();
+ int currentDataSubId = getDefaultDataSubId();
+
+ List<SubscriptionInfo> subInfoList =
+ getActiveSubscriptionInfoList(mContext.getOpPackageName());
+ if (subInfoList == null) {
+ return;
+ }
+ boolean userPrefSubValid = false;
+ for (SubscriptionInfo subInfo : subInfoList) {
+ if (subInfo.getSubscriptionId() == userPrefDataSubId) {
+ userPrefSubValid = true;
+ }
+ }
+ logd("havePrefSub = " + userPrefSubValid + " user pref subId = "
+ + userPrefDataSubId + " current dds " + currentDataSubId
+ + " next active subId " + nextActiveSubId);
+
+ // If earlier user selected DDS is now available, set that as DDS subId.
+ if (userPrefSubValid && isSubProvisioned(userPrefDataSubId) &&
+ (currentDataSubId != userPrefDataSubId)) {
+ setDefaultDataSubId(userPrefDataSubId);
+ } else if (!isSubProvisioned(currentDataSubId)) {
+ setDefaultDataSubId(nextActiveSubId);
+ }
+
+ }
+
+ protected int getUiccProvisionStatus(int slotId) {
+ // FIXME use valid SIM provision value
+ return PROVISIONED;
+ }
+
+ // This method returns true if subId and corresponding slotId is in valid
+ // range and the Uicc card corresponds to this slot is provisioned.
+ protected boolean isSubProvisioned(int subId) {
+ boolean isSubIdUsable = SubscriptionManager.isUsableSubIdValue(subId);
+
+ if (isSubIdUsable) {
+ int slotId = getSlotIndex(subId);
+ if (!SubscriptionManager.isValidSlotIndex(slotId)) {
+ loge(" Invalid slotId " + slotId + " or subId = " + subId);
+ isSubIdUsable = false;
+ } else {
+ if (getUiccProvisionStatus(slotId) != PROVISIONED) {
+ isSubIdUsable = false;
+ }
+ loge("isSubProvisioned, state = " + isSubIdUsable + " subId = " + subId);
+ }
+ }
+ return isSubIdUsable;
+ }
+
+ /* Returns User SMS Prompt property, enabled or not */
+ public boolean isSmsPromptEnabled() {
+ boolean prompt = false;
+ int value = 0;
+ try {
+ value = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_SMS_PROMPT);
+ } catch (SettingNotFoundException snfe) {
+ loge("Settings Exception Reading Dual Sim SMS Prompt Values");
+ }
+ prompt = (value == 0) ? false : true ;
+ if (VDBG) logd("SMS Prompt option:" + prompt);
+
+ return prompt;
+ }
+
+ /*Sets User SMS Prompt property, enable or not */
+ public void setSmsPromptEnabled(boolean enabled) {
+ enforceModifyPhoneState("setSMSPromptEnabled");
+ int value = (enabled == false) ? 0 : 1;
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.MULTI_SIM_SMS_PROMPT, value);
+ logi("setSMSPromptOption to " + enabled);
+ }
+
+ protected boolean isNonSimAccountFound() {
+ final Iterator<PhoneAccountHandle> phoneAccounts =
+ mTelecomManager.getCallCapablePhoneAccounts().listIterator();
+
+ while (phoneAccounts.hasNext()) {
+ final PhoneAccountHandle phoneAccountHandle = phoneAccounts.next();
+ final PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(phoneAccountHandle);
+ if (mTelephonyManager.getSubIdForPhoneAccount(phoneAccount) ==
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ logi("Other than SIM account found. ");
+ return true;
+ }
+ }
+ logi("Other than SIM account not found ");
+ return false;
+ }
+
+ protected PhoneAccountHandle subscriptionIdToPhoneAccountHandle(final int subId) {
+ final Iterator<PhoneAccountHandle> phoneAccounts =
+ mTelecomManager.getCallCapablePhoneAccounts().listIterator();
+
+ while (phoneAccounts.hasNext()) {
+ final PhoneAccountHandle phoneAccountHandle = phoneAccounts.next();
+ final PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(phoneAccountHandle);
+ if (subId == mTelephonyManager.getSubIdForPhoneAccount(phoneAccount)) {
+ return phoneAccountHandle;
+ }
+ }
+
+ return null;
+ }
+
+ protected int getUserPrefDataSubIdFromDB() {
+ return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
+ SETTING_USER_PREF_DATA_SUB, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ }
+
+ private void logd(String string) {
+ if (DBG) Rlog.d(LOG_TAG, string);
+ }
+
+ private void logi(String string) {
+ Rlog.i(LOG_TAG, string);
+ }
+
+ private void loge(String string) {
+ Rlog.e(LOG_TAG, string);
+ }
+}
diff --git a/src/java/com/android/internal/telephony/vendor/dataconnection/VendorDcTracker.java b/src/java/com/android/internal/telephony/vendor/dataconnection/VendorDcTracker.java
new file mode 100644
index 0000000..6adc1ef
--- /dev/null
+++ b/src/java/com/android/internal/telephony/vendor/dataconnection/VendorDcTracker.java
@@ -0,0 +1,111 @@
+/*
+ * 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.telephony.AccessNetworkConstants;
+import android.telephony.Rlog;
+
+import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.uicc.IccRecords;
+
+import java.util.HashSet;
+import java.util.Iterator;
+
+public class VendorDcTracker extends DcTracker {
+ private String LOG_TAG = "VendorDCT";
+ private static final boolean DBG = true;
+ private HashSet<String> mIccidSet = new HashSet<String>();
+ private int mTransportType = AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
+
+ // 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();
+ }
+
+ protected boolean isRecordsLoaded() {
+ boolean recordsLoaded = false;
+
+ IccRecords r = mIccRecords.get();
+ if (r != null) {
+ recordsLoaded = r.getRecordsLoaded();
+ }
+
+ return recordsLoaded;
+ }
+
+ @Override
+ protected void onRecordsLoadedOrSubIdChanged() {
+ if (DBG) log("onRecordsLoaded: createAllApnList");
+ // Just support auto attach for WWAN only
+ if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
+ mAutoAttachOnCreationConfig = mPhone.getContext().getResources()
+ .getBoolean(com.android.internal.R.bool.config_auto_attach_data_on_creation);
+ }
+
+ createAllApnList();
+ setDataProfilesAsNeeded();
+ // Send initial attach apn only if sim records are loaded
+ if (isRecordsLoaded()) {
+ setInitialAttachApn();
+ }
+ mPhone.notifyAllActiveDataConnections();
+ setupDataOnAllConnectableApns(Phone.REASON_SIM_LOADED, RetryFailures.ALWAYS);
+ }
+
+ @Override
+ protected boolean allowInitialAttachForOperator() {
+ IccRecords r = mIccRecords.get();
+ String iccId = (r != null) ? r.getIccId() : "";
+ if (iccId != null) {
+ Iterator<String> itr = mIccidSet.iterator();
+ while (itr.hasNext()) {
+ if (iccId.contains(itr.next())) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ // Support added to allow initial attach request with only default for a Carrier
+ protected 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 log(String s) {
+ Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
+ }
+}