Refactored no data due to roaming notification
Instead of listening to ACTION_ANY_DATA_CONNECTION_STATE_CHANGED,
we now listent to mobile data settings changed, data roaming
settings changed, carrier config changed, roaming status changed,
and default data subscription changed event. This will make the
notification show/hide become more reliable.
Test: manual
bug: 63027846
Change-Id: I413711c4af6374609f87f5aad63eb1a09fd22655
diff --git a/src/com/android/phone/DumpsysHandler.java b/src/com/android/phone/DumpsysHandler.java
index a0277fc..47f6105 100644
--- a/src/com/android/phone/DumpsysHandler.java
+++ b/src/com/android/phone/DumpsysHandler.java
@@ -15,6 +15,7 @@
public static void dump(Context context, FileDescriptor fd, PrintWriter writer,
String[] args) {
+ PhoneGlobals.getInstance().dump(fd, writer, args);
// Dump OMTP visual voicemail log.
VvmDumpHandler.dump(context, fd, writer, args);
}
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 1d61c8e..3b9ea03 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -43,6 +43,8 @@
import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.LocalLog;
import android.util.Log;
import android.widget.Toast;
@@ -53,19 +55,26 @@
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.SettingsObserver;
import com.android.internal.telephony.TelephonyCapabilities;
import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.dataconnection.DataConnectionReasons;
+import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataDisallowedReasonType;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.phone.common.CallLogAsync;
import com.android.phone.settings.SettingsConstants;
import com.android.phone.vvm.CarrierVvmPackageInstalledReceiver;
import com.android.services.telephony.sip.SipUtil;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
/**
* Global state for the telephony subsystem when running in the primary
* phone process.
*/
public class PhoneGlobals extends ContextWrapper {
- public static final String LOG_TAG = "PhoneApp";
+ public static final String LOG_TAG = "PhoneGlobals";
/**
* Phone app-wide debug level:
@@ -96,6 +105,8 @@
private static final int EVENT_DATA_ROAMING_OK = 11;
private static final int EVENT_UNSOL_CDMA_INFO_RECORD = 12;
private static final int EVENT_RESTART_SIP = 13;
+ private static final int EVENT_DATA_ROAMING_SETTINGS_CHANGED = 14;
+ private static final int EVENT_MOBILE_DATA_SETTINGS_CHANGED = 15;
// The MMI codes are also used by the InCallScreen.
public static final int MMI_INITIATE = 51;
@@ -146,7 +157,7 @@
private Activity mPUKEntryActivity;
private ProgressDialog mPUKEntryProgressDialog;
- private boolean mDataDisconnectedDueToRoaming = false;
+ private boolean mNoDataDueToRoaming = false;
private WakeState mWakeState = WakeState.SLEEP;
@@ -157,16 +168,22 @@
private UpdateLock mUpdateLock;
+ private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private final LocalLog mDataRoamingNotifLog = new LocalLog(50);
+
// Broadcast receiver for various intent broadcasts (see onCreate())
private final BroadcastReceiver mReceiver = new PhoneAppBroadcastReceiver();
private final CarrierVvmPackageInstalledReceiver mCarrierVvmPackageInstalledReceiver =
new CarrierVvmPackageInstalledReceiver();
+ private final SettingsObserver mSettingsObserver;
+
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
PhoneConstants.State phoneState;
+ if (VDBG) Log.v(LOG_TAG, "event=" + msg.what);
switch (msg.what) {
// TODO: This event should be handled by the lock screen, just
// like the "SIM missing" and "Sim locked" cases (bug 1804111).
@@ -233,6 +250,10 @@
SipUtil.startSipService();
}
break;
+ case EVENT_DATA_ROAMING_SETTINGS_CHANGED:
+ case EVENT_MOBILE_DATA_SETTINGS_CHANGED:
+ updateDataRoamingStatus();
+ break;
}
}
};
@@ -240,6 +261,7 @@
public PhoneGlobals(Context context) {
super(context);
sMe = this;
+ mSettingsObserver = new SettingsObserver(context, mHandler);
}
public void onCreate() {
@@ -315,7 +337,7 @@
configLoader = CarrierConfigLoader.init(this);
- // Create the CallNotifer singleton, which handles
+ // Create the CallNotifier singleton, which handles
// asynchronous events from the telephony layer (like
// launching the incoming-call UI when an incoming call comes
// in.)
@@ -332,11 +354,12 @@
// Register for misc other intent broadcasts.
IntentFilter intentFilter =
new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- intentFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
intentFilter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
intentFilter.addAction(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED);
intentFilter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
+ intentFilter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+ intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
registerReceiver(mReceiver, intentFilter);
mCarrierVvmPackageInstalledReceiver.register(this);
@@ -414,6 +437,27 @@
return configLoader.getConfigForSubId(subId);
}
+ private void registerSettingsObserver() {
+ mSettingsObserver.unobserve();
+ String dataRoamingSetting = Settings.Global.DATA_ROAMING;
+ String mobileDataSetting = Settings.Global.MOBILE_DATA;
+ if (TelephonyManager.getDefault().getSimCount() > 1) {
+ int subId = mDefaultDataSubId;
+ if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ dataRoamingSetting += subId;
+ mobileDataSetting += subId;
+ }
+ }
+
+ // Listen for user data roaming setting changed event
+ mSettingsObserver.observe(Settings.Global.getUriFor(dataRoamingSetting),
+ EVENT_DATA_ROAMING_SETTINGS_CHANGED);
+
+ // Listen for mobile data setting changed event
+ mSettingsObserver.observe(Settings.Global.getUriFor(mobileDataSetting),
+ EVENT_MOBILE_DATA_SETTINGS_CHANGED);
+ }
+
/**
* Sets the activity responsible for un-PUK-blocking the device
* so that we may close it when we receive a positive result.
@@ -653,50 +697,6 @@
airplaneMode = AIRPLANE_ON;
}
handleAirplaneModeChange(context, airplaneMode);
- } else if (action.equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
- int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- int phoneId = SubscriptionManager.getPhoneId(subId);
- final String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
- final String state = intent.getStringExtra(PhoneConstants.STATE_KEY);
- final String reason = intent.getStringExtra(PhoneConstants.STATE_CHANGE_REASON_KEY);
- if (VDBG) {
- Log.d(LOG_TAG, "mReceiver: ACTION_ANY_DATA_CONNECTION_STATE_CHANGED");
- Log.d(LOG_TAG, "- state: " + state);
- Log.d(LOG_TAG, "- reason: " + reason);
- Log.d(LOG_TAG, "- subId: " + subId);
- }
- Phone phone = SubscriptionManager.isValidPhoneId(phoneId) ?
- PhoneFactory.getPhone(phoneId) : PhoneFactory.getDefaultPhone();
-
- // If the apn type of data connection state changed event is NOT default,
- // ignore the broadcast intent and avoid action.
- if (!PhoneConstants.APN_TYPE_DEFAULT.equals(apnType)) {
- if (VDBG) Log.d(LOG_TAG, "Ignore broadcast intent as not default apn type");
- return;
- }
-
- // The "data disconnected due to roaming" notification is shown
- // if (a) you have the "data roaming" feature turned off, and
- // (b) you just lost data connectivity because you're roaming.
- // (c) if we haven't shown the notification for this disconnection earlier.
- // (d) if data was enabled for the sim
- if (!mDataDisconnectedDueToRoaming
- && PhoneConstants.DataState.DISCONNECTED.name().equals(state)
- && Phone.REASON_ROAMING_ON.equals(reason)
- && !phone.getDataRoamingEnabled()
- && phone.getDataEnabled()) {
- // Notify the user that data call is disconnected due to roaming. Note that
- // calling this multiple times will not cause multiple notifications.
- mHandler.sendEmptyMessage(EVENT_DATA_ROAMING_DISCONNECTED);
- mDataDisconnectedDueToRoaming = true;
- } else if (mDataDisconnectedDueToRoaming
- && PhoneConstants.DataState.CONNECTED.name().equals(state)) {
- // Cancel the notification when data is available. Note it is okay to call this
- // even if the notification doesn't exist.
- mHandler.sendEmptyMessage(EVENT_DATA_ROAMING_OK);
- mDataDisconnectedDueToRoaming = false;
- }
} else if ((action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) &&
(mPUKEntryActivity != null)) {
// if an attempt to un-PUK-lock the device was made, while we're
@@ -735,6 +735,19 @@
} else {
Log.w(LOG_TAG, "phoneInEcm is null.");
}
+ } else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
+ // Roaming status could be overridden by carrier config, so we need to update it.
+ if (VDBG) Log.v(LOG_TAG, "carrier config changed.");
+ updateDataRoamingStatus();
+ } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
+ // We also need to pay attention when default data subscription changes.
+ if (VDBG) Log.v(LOG_TAG, "default data sub changed.");
+ mDefaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+ registerSettingsObserver();
+ Phone phone = getPhone(mDefaultDataSubId);
+ if (phone != null) {
+ updateDataRoamingStatus();
+ }
}
}
}
@@ -747,6 +760,7 @@
* future.
*/
+ if (VDBG) Log.v(LOG_TAG, "handleServiceStateChanged");
// If service just returned, start sending out the queued messages
Bundle extras = intent.getExtras();
if (extras != null) {
@@ -756,10 +770,54 @@
int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
notificationMgr.updateNetworkSelection(state, subId);
+
+ if (VDBG) {
+ Log.v(LOG_TAG, "subId=" + subId + ",mDefaultDataSubId="
+ + mDefaultDataSubId + ",ss roaming=" + ss.getDataRoaming());
+ }
+ if (subId == mDefaultDataSubId) {
+ updateDataRoamingStatus();
+ }
}
}
}
+ /**
+ * When roaming, if mobile data cannot be established due to data roaming not enabled, we need
+ * to notify the user so they can enable it through settings. Vise versa if the condition
+ * changes, we need to dismiss the notification.
+ */
+ private void updateDataRoamingStatus() {
+ if (VDBG) Log.v(LOG_TAG, "updateDataRoamingStatus");
+ Phone phone = getPhone(mDefaultDataSubId);
+ if (phone == null) {
+ Log.w(LOG_TAG, "Can't get phone with sub id = " + mDefaultDataSubId);
+ return;
+ }
+
+ DataConnectionReasons reasons = new DataConnectionReasons();
+ boolean dataAllowed = phone.isDataAllowed(reasons);
+ mDataRoamingNotifLog.log("dataAllowed=" + dataAllowed + ", reasons=" + reasons);
+ if (VDBG) Log.v(LOG_TAG, "dataAllowed=" + dataAllowed + ", reasons=" + reasons);
+ if (!mNoDataDueToRoaming
+ && !dataAllowed
+ && reasons.containsOnly(DataDisallowedReasonType.ROAMING_DISABLED)) {
+ // If the only reason of no data is data roaming disabled, then we notify the user
+ // so the user can turn on data roaming.
+ mNoDataDueToRoaming = true;
+ Log.d(LOG_TAG, "Show roaming disconnected notification");
+ mDataRoamingNotifLog.log("Show");
+ mHandler.sendEmptyMessage(EVENT_DATA_ROAMING_DISCONNECTED);
+ } else if (mNoDataDueToRoaming && (dataAllowed
+ || !reasons.containsOnly(DataDisallowedReasonType.ROAMING_DISABLED))) {
+ // Otherwise dismiss the notification we showed earlier.
+ mNoDataDueToRoaming = false;
+ Log.d(LOG_TAG, "Dismiss roaming disconnected notification");
+ mDataRoamingNotifLog.log("Hide. data allowed=" + dataAllowed + ", reasons=" + reasons);
+ mHandler.sendEmptyMessage(EVENT_DATA_ROAMING_OK);
+ }
+ }
+
public Phone getPhoneInEcm() {
return phoneInEcm;
}
@@ -807,4 +865,25 @@
public void setShouldCheckVisualVoicemailConfigurationForMwi(int subId, boolean enabled) {
notificationMgr.setShouldCheckVisualVoicemailConfigurationForMwi(subId, enabled);
}
+
+ /**
+ * Dump the state of the object, add calls to other objects as desired.
+ *
+ * @param fd File descriptor
+ * @param printWriter Print writer
+ * @param args Arguments
+ */
+ public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+ pw.println("------- PhoneGlobals -------");
+ pw.increaseIndent();
+ pw.println("mNoDataDueToRoaming=" + mNoDataDueToRoaming);
+ pw.println("mDefaultDataSubId=" + mDefaultDataSubId);
+ pw.println("mDataRoamingNotifLog:");
+ pw.increaseIndent();
+ mDataRoamingNotifLog.dump(fd, pw, args);
+ pw.decreaseIndent();
+ pw.decreaseIndent();
+ pw.println("------- End PhoneGlobals -------");
+ }
}
diff --git a/tests/src/com/android/phone/PhoneGlobalsTest.java b/tests/src/com/android/phone/PhoneGlobalsTest.java
deleted file mode 100644
index b862ee3..0000000
--- a/tests/src/com/android/phone/PhoneGlobalsTest.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.phone;
-
-import android.content.Intent;
-import android.os.Handler;
-import android.os.Message;
-import android.support.test.runner.AndroidJUnit4;
-
-import com.android.TelephonyTestBase;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.TelephonyIntents;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-
-import java.lang.reflect.Field;
-
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-@RunWith(AndroidJUnit4.class)
-public class PhoneGlobalsTest extends TelephonyTestBase {
-
- private Phone mPhone = PhoneFactory.getDefaultPhone();
-
- private PhoneGlobals mPhoneGlobals = PhoneGlobals.getInstance();
-
- private Handler mHandler = mock(Handler.class);
-
- private static final int EVENT_DATA_ROAMING_DISCONNECTED = 10;
-
- @Before
- public void setUp() throws Exception {
- super.setUp();
- Field handler = PhoneGlobals.class.getDeclaredField("mHandler");
- handler.setAccessible(true);
- handler.set(mPhoneGlobals, mHandler);
- }
-
- @Test
- public void testDataDisconnectedNotification() {
- mPhone.setDataRoamingEnabled(false);
- // Test no notification sent out when data is disabled, data raoming enabled and data
- // disconnected because of roaming.
- mPhone.setDataEnabled(false);
- Intent intent = new Intent(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
- intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY,
- PhoneConstants.APN_TYPE_DEFAULT);
- intent.putExtra(PhoneConstants.STATE_KEY, PhoneConstants.DataState.DISCONNECTED.name());
- intent.putExtra(PhoneConstants.STATE_CHANGE_REASON_KEY, Phone.REASON_ROAMING_ON);
- mContext.sendBroadcast(intent);
-
- waitForMs(300);
-
- ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
- verify(mHandler, atLeast(0)).sendMessageAtTime(any(Message.class), anyLong());
- boolean flag = true;
- for (Message msg : messageArgumentCaptor.getAllValues()) {
- if (msg.what == EVENT_DATA_ROAMING_DISCONNECTED) {
- flag = false;
- }
- }
- assertTrue(flag);
-
-
- // Test notification sent out when data is enabled, data raoming enabled and data
- // disconnected because of roaming.
- mPhone.setDataEnabled(true);
- mContext.sendBroadcast(intent);
-
- waitForMs(300);
-
-
- verify(mHandler, atLeast(1)).sendMessageAtTime(messageArgumentCaptor.capture(), anyLong());
- flag = false;
- for (Message msg : messageArgumentCaptor.getAllValues()) {
- if (msg.what == EVENT_DATA_ROAMING_DISCONNECTED) {
- flag = true;
- }
- }
- assertTrue(flag);
- }
-}