Make sure registrants are notified when provisionDataEnabled changes

When DEVICE_PROVISIONED or DEVICE_PROVISIONING_MOBILE_DATA_ENABLED
value change in Settings.Global, make sure DcTracker and
DataEnabledSettings updates isDataEnabled and notify registrants.

Test: unittest
Bug: 112020101
Change-Id: I01a2dce51e0ea7e5481cf65b56070030be286b34
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java b/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
index 3b856a7..87521b1 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
@@ -52,6 +52,10 @@
 
     public static final int REASON_DATA_ENABLED_BY_CARRIER = 4;
 
+    public static final int REASON_PROVISIONED_CHANGED = 5;
+
+    public static final int REASON_PROVISIONING_DATA_ENABLED_CHANGED = 6;
+
     /**
      * responds to the setInternalDataEnabled call - used internally to turn off data.
      * For example during emergency calls
@@ -69,6 +73,8 @@
      */
     private boolean mCarrierDataEnabled = true;
 
+    private boolean mIsDataEnabled = false;
+
     private Phone mPhone = null;
     private ContentResolver mResolver = null;
 
@@ -88,15 +94,14 @@
     public DataEnabledSettings(Phone phone) {
         mPhone = phone;
         mResolver = mPhone.getContext().getContentResolver();
+        updateDataEnabled();
     }
 
     public synchronized void setInternalDataEnabled(boolean enabled) {
         localLog("InternalDataEnabled", enabled);
-        boolean prevDataEnabled = isDataEnabled();
         mInternalDataEnabled = enabled;
-        if (prevDataEnabled != isDataEnabled()) {
-            notifyDataEnabledChanged(!prevDataEnabled, REASON_INTERNAL_DATA_ENABLED);
-        }
+
+        updateDataEnabledAndNotify(REASON_INTERNAL_DATA_ENABLED);
     }
     public synchronized boolean isInternalDataEnabled() {
         return mInternalDataEnabled;
@@ -104,14 +109,11 @@
 
     public synchronized void setUserDataEnabled(boolean enabled) {
         localLog("UserDataEnabled", enabled);
-        boolean prevDataEnabled = isDataEnabled();
-
         Settings.Global.putInt(mResolver, getMobileDataSettingName(), enabled ? 1 : 0);
 
-        if (prevDataEnabled != isDataEnabled()) {
-            notifyDataEnabledChanged(!prevDataEnabled, REASON_USER_DATA_ENABLED);
-        }
+        updateDataEnabledAndNotify(REASON_USER_DATA_ENABLED);
     }
+
     public synchronized boolean isUserDataEnabled() {
         boolean defaultVal = "true".equalsIgnoreCase(SystemProperties.get(
                 "ro.com.android.mobiledata", "true"));
@@ -134,33 +136,53 @@
 
     public synchronized void setPolicyDataEnabled(boolean enabled) {
         localLog("PolicyDataEnabled", enabled);
-        boolean prevDataEnabled = isDataEnabled();
         mPolicyDataEnabled = enabled;
-        if (prevDataEnabled != isDataEnabled()) {
-            notifyDataEnabledChanged(!prevDataEnabled, REASON_POLICY_DATA_ENABLED);
-        }
+
+        updateDataEnabledAndNotify(REASON_POLICY_DATA_ENABLED);
     }
+
     public synchronized boolean isPolicyDataEnabled() {
         return mPolicyDataEnabled;
     }
 
     public synchronized void setCarrierDataEnabled(boolean enabled) {
         localLog("CarrierDataEnabled", enabled);
-        boolean prevDataEnabled = isDataEnabled();
         mCarrierDataEnabled = enabled;
-        if (prevDataEnabled != isDataEnabled()) {
-            notifyDataEnabledChanged(!prevDataEnabled, REASON_DATA_ENABLED_BY_CARRIER);
-        }
+
+        updateDataEnabledAndNotify(REASON_DATA_ENABLED_BY_CARRIER);
     }
+
     public synchronized boolean isCarrierDataEnabled() {
         return mCarrierDataEnabled;
     }
 
+    public synchronized void updateProvisionedChanged() {
+        updateDataEnabledAndNotify(REASON_PROVISIONED_CHANGED);
+    }
+
+    public synchronized void updateProvisioningDataEnabled() {
+        updateDataEnabledAndNotify(REASON_PROVISIONING_DATA_ENABLED_CHANGED);
+    }
+
     public synchronized boolean isDataEnabled() {
+        return mIsDataEnabled;
+    }
+
+    private synchronized void updateDataEnabledAndNotify(int reason) {
+        boolean prevDataEnabled = mIsDataEnabled;
+
+        updateDataEnabled();
+
+        if (prevDataEnabled != mIsDataEnabled) {
+            notifyDataEnabledChanged(!prevDataEnabled, reason);
+        }
+    }
+
+    private synchronized void updateDataEnabled() {
         if (isProvisioning()) {
-            return isProvisioningDataEnabled();
+            mIsDataEnabled = isProvisioningDataEnabled();
         } else {
-            return mInternalDataEnabled && isUserDataEnabled()
+            mIsDataEnabled = mInternalDataEnabled && isUserDataEnabled()
                     && mPolicyDataEnabled && mCarrierDataEnabled;
         }
     }
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index 9a682e0..25dcb6a 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -329,7 +329,7 @@
                 DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE);
         mSettingsObserver.observe(
                 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED),
-                DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE);
+                DctConstants.EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE);
     }
 
     /**
@@ -864,6 +864,9 @@
     }
 
     private void onDeviceProvisionedChange() {
+        mDataEnabledSettings.updateProvisionedChanged();
+        // TODO: We should register for DataEnabledSetting's data enabled/disabled event and
+        // handle the rest from there.
         if (isDataEnabled()) {
             reevaluateDataConnections();
             onTrySetupData(Phone.REASON_DATA_ENABLED);
@@ -872,6 +875,17 @@
         }
     }
 
+    private void onDeviceProvisioningDataChange() {
+        mDataEnabledSettings.updateProvisioningDataEnabled();
+        // TODO: We should register for DataEnabledSetting's data enabled/disabled event and
+        // handle the rest from there.
+        if (isDataEnabled()) {
+            reevaluateDataConnections();
+            onTrySetupData(Phone.REASON_DATA_ENABLED);
+        } else {
+            onCleanUpAllConnections(Phone.REASON_DATA_SPECIFIC_DISABLED);
+        }
+    }
 
     public long getSubId() {
         return mPhone.getSubId();
@@ -3718,6 +3732,10 @@
                 onDeviceProvisionedChange();
                 break;
 
+            case DctConstants.EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE:
+                onDeviceProvisioningDataChange();
+                break;
+
             case DctConstants.EVENT_REDIRECTION_DETECTED:
                 String url = (String) msg.obj;
                 log("dataConnectionTracker.handleMessage: EVENT_REDIRECTION_DETECTED=" + url);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
index ae1a4dd..3dede43 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
@@ -29,6 +29,7 @@
 import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
@@ -51,6 +52,7 @@
 import android.net.NetworkRequest;
 import android.net.Uri;
 import android.os.AsyncResult;
+import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Message;
@@ -73,6 +75,7 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.text.TextUtils;
 import android.util.LocalLog;
+import android.util.Pair;
 
 import com.android.internal.R;
 import com.android.internal.telephony.DctConstants;
@@ -128,6 +131,7 @@
             1 << (TelephonyManager.NETWORK_TYPE_EHRPD - 1);
     private static final Uri PREFERAPN_URI = Uri.parse(
             Telephony.Carriers.CONTENT_URI + "/preferapn");
+    private static final int DATA_ENABLED_CHANGED = 0;
 
     @Mock
     ISub mIsub;
@@ -145,6 +149,8 @@
     DataConnection mDataConnection;
     @Mock
     PackageManagerService mMockPackageManagerInternal;
+    @Mock
+    Handler mHandler;
 
     private DcTracker mDct;
     private DcTrackerTestHandler mDcTrackerTestHandler;
@@ -158,6 +164,8 @@
     private final ApnSettingContentProvider mApnSettingContentProvider =
             new ApnSettingContentProvider();
 
+    private Message mMessage;
+
     private void addDataService() {
         CellularDataService cellularDataService = new CellularDataService();
         ServiceInfo serviceInfo = new ServiceInfo();
@@ -1443,7 +1451,6 @@
         assertTrue(mDct.isDataEnabled());
         assertTrue(mDct.isUserDataEnabled());
 
-
         mDct.setUserDataEnabled(false);
         waitForMs(200);
 
@@ -1453,6 +1460,8 @@
 
         // Changing provisioned to 0.
         Settings.Global.putInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0);
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE, null));
+        waitForMs(200);
 
         assertTrue(mDct.isDataEnabled());
         assertTrue(mDct.isUserDataEnabled());
@@ -1461,10 +1470,57 @@
         // Settings.Global.MOBILE_DATA and keep data enabled when provisioned.
         mDct.setUserDataEnabled(true);
         Settings.Global.putInt(resolver, Settings.Global.DEVICE_PROVISIONED, 1);
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE, null));
         waitForMs(200);
 
         assertTrue(mDct.isDataEnabled());
         assertTrue(mDct.isUserDataEnabled());
         assertEquals(1, Settings.Global.getInt(resolver, Settings.Global.MOBILE_DATA));
     }
+
+    @Test
+    @SmallTest
+    public void testNotifyDataEnabledChanged() throws Exception {
+        doAnswer(invocation -> {
+            mMessage = (Message) invocation.getArguments()[0];
+            return true;
+        }).when(mHandler).sendMessageDelayed(any(), anyLong());
+
+        // Test registration.
+        mDct.registerForDataEnabledChanged(mHandler, DATA_ENABLED_CHANGED, null);
+        verifyDataEnabledChangedMessage(true, DataEnabledSettings.REASON_REGISTERED);
+
+        // Disable user data. Should receive data enabled change to false.
+        mDct.setUserDataEnabled(false);
+        waitForMs(200);
+        verifyDataEnabledChangedMessage(false, DataEnabledSettings.REASON_USER_DATA_ENABLED);
+
+        // Changing provisioned to 0. Shouldn't receive any message, as data enabled remains false.
+        ContentResolver resolver = mContext.getContentResolver();
+        Settings.Global.putInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0);
+        Settings.Global.putInt(resolver, Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED,
+                0);
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE, null));
+        waitForMs(200);
+        assertFalse(mDct.isDataEnabled());
+        verify(mHandler, never()).sendMessageDelayed(any(), anyLong());
+
+        // Changing provisioningDataEnabled to 1. It should trigger data enabled change to true.
+        Settings.Global.putInt(resolver,
+                Settings.Global.DEVICE_PROVISIONING_MOBILE_DATA_ENABLED, 1);
+        mDct.sendMessage(mDct.obtainMessage(
+                DctConstants.EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE, null));
+        waitForMs(200);
+        verifyDataEnabledChangedMessage(
+                true, DataEnabledSettings.REASON_PROVISIONING_DATA_ENABLED_CHANGED);
+    }
+
+    private void verifyDataEnabledChangedMessage(boolean enabled, int reason) {
+        verify(mHandler, times(1)).sendMessageDelayed(any(), anyLong());
+        Pair<Boolean, Integer> result = (Pair) ((AsyncResult) mMessage.obj).result;
+        assertEquals(DATA_ENABLED_CHANGED, mMessage.what);
+        assertEquals(enabled, result.first);
+        assertEquals(reason, (int) result.second);
+        clearInvocations(mHandler);
+    }
 }