Merge "wifi: hotspot2: verify getter methods" am: b296f84ce8 am: c5d2212da7
am: c2207fd342

Change-Id: I54b6969ccc2f0b8d50825c0eb4bd2cddc3f04829
diff --git a/tests/cts/hostside/Android.mk b/tests/cts/hostside/Android.mk
index ad97ecd..1c3f053 100644
--- a/tests/cts/hostside/Android.mk
+++ b/tests/cts/hostside/Android.mk
@@ -21,9 +21,7 @@
 
 LOCAL_MODULE := CtsHostsideNetworkTests
 
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt
-
-LOCAL_STATIC_JAVA_LIBRARIES := cts-migration-lib
+LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed
 
 LOCAL_CTS_TEST_PACKAGE := android.net.hostsidenetwork
 
diff --git a/tests/cts/hostside/aidl/Android.mk b/tests/cts/hostside/aidl/Android.mk
index 4aa55b6..85f71c3 100644
--- a/tests/cts/hostside/aidl/Android.mk
+++ b/tests/cts/hostside/aidl/Android.mk
@@ -17,6 +17,9 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := com/android/cts/net/hostside/IRemoteSocketFactory.aidl
+LOCAL_SRC_FILES := \
+        com/android/cts/net/hostside/IMyService.aidl \
+        com/android/cts/net/hostside/INetworkStateObserver.aidl \
+        com/android/cts/net/hostside/IRemoteSocketFactory.aidl
 LOCAL_MODULE := CtsHostsideNetworkTestsAidl
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl b/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl
new file mode 100644
index 0000000..72d1059
--- /dev/null
+++ b/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 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.cts.net.hostside;
+
+interface IMyService {
+    void registerBroadcastReceiver();
+    int getCounters(String receiverName, String action);
+    String checkNetworkStatus();
+    String getRestrictBackgroundStatus();
+    void sendNotification(int notificationId, String notificationType);
+}
diff --git a/tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkStateObserver.aidl b/tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkStateObserver.aidl
new file mode 100644
index 0000000..09f3120
--- /dev/null
+++ b/tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkStateObserver.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 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.cts.net.hostside;
+
+oneway interface INetworkStateObserver {
+    void onNetworkStateChecked(String resultData);
+}
\ No newline at end of file
diff --git a/tests/cts/hostside/app/Android.mk b/tests/cts/hostside/app/Android.mk
index 9519ec5..1c1a798 100644
--- a/tests/cts/hostside/app/Android.mk
+++ b/tests/cts/hostside/app/Android.mk
@@ -20,7 +20,7 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner ub-uiautomator \
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner ub-uiautomator \
         CtsHostsideNetworkTestsAidl
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
index 79435f1..2fc0bc2 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
@@ -82,9 +82,7 @@
 
         // Make sure foreground app doesn't lose access upon enabling it.
         setAppIdle(true);
-        launchActivity();
-        assertAppIdle(false); // Sanity check - not idle anymore, since activity was launched...
-        assertForegroundNetworkAccess();
+        launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
         finishActivity();
         assertAppIdle(false); // Sanity check - not idle anymore, since activity was launched...
         assertBackgroundNetworkAccess(true);
@@ -93,9 +91,7 @@
 
         // Same for foreground service.
         setAppIdle(true);
-        startForegroundService();
-        assertAppIdle(true); // Sanity check - still idle
-        assertForegroundServiceNetworkAccess();
+        launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
         stopForegroundService();
         assertAppIdle(true);
         assertBackgroundNetworkAccess(false);
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
index b5637be..60253f9 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
@@ -86,8 +86,7 @@
 
         // Make sure foreground app doesn't lose access upon Battery Saver.
         setBatterySaverMode(false);
-        launchActivity();
-        assertForegroundNetworkAccess();
+        launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
         setBatterySaverMode(true);
         assertForegroundNetworkAccess();
 
@@ -103,8 +102,7 @@
 
         // Make sure foreground service doesn't lose access upon enabling Battery Saver.
         setBatterySaverMode(false);
-        startForegroundService();
-        assertForegroundNetworkAccess();
+        launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
         setBatterySaverMode(true);
         assertForegroundNetworkAccess();
         stopForegroundService();
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
index fc674a7..baeaa47 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
@@ -87,8 +87,7 @@
 
         // Make sure foreground service doesn't lose network access upon enabling doze.
         setDozeMode(false);
-        startForegroundService();
-        assertForegroundNetworkAccess();
+        launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
         setDozeMode(true);
         assertForegroundNetworkAccess();
         stopForegroundService();
@@ -158,8 +157,7 @@
     // leaves Doze Mode.
     @Override
     protected void assertsForegroundAlwaysHasNetworkAccess() throws Exception {
-        startForegroundService();
-        assertForegroundServiceNetworkAccess();
+        launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
         stopForegroundService();
         assertBackgroundState();
     }
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index a500348..15daed9 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -16,17 +16,19 @@
 
 package com.android.cts.net.hostside;
 
-import static android.cts.util.SystemUtil.runShellCommand;
 import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
 
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
 import android.app.Instrumentation;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -35,11 +37,15 @@
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkInfo.State;
 import android.net.wifi.WifiManager;
+import android.os.Binder;
+import android.os.Bundle;
 import android.os.SystemClock;
 import android.service.notification.NotificationListenerService;
 import android.test.InstrumentationTestCase;
 import android.util.Log;
 
+import com.android.cts.net.hostside.INetworkStateObserver;
+
 /**
  * Superclass for tests related to background network restrictions.
  */
@@ -49,31 +55,20 @@
     protected static final String TEST_PKG = "com.android.cts.net.hostside";
     protected static final String TEST_APP2_PKG = "com.android.cts.net.hostside.app2";
 
+    private static final String TEST_APP2_ACTIVITY_CLASS = TEST_APP2_PKG + ".MyActivity";
+    private static final String TEST_APP2_SERVICE_CLASS = TEST_APP2_PKG + ".MyForegroundService";
+
     private static final int SLEEP_TIME_SEC = 1;
     private static final boolean DEBUG = true;
 
     // Constants below must match values defined on app2's Common.java
     private static final String MANIFEST_RECEIVER = "ManifestReceiver";
     private static final String DYNAMIC_RECEIVER = "DynamicReceiver";
-    private static final String ACTION_GET_COUNTERS =
-            "com.android.cts.net.hostside.app2.action.GET_COUNTERS";
-    private static final String ACTION_GET_RESTRICT_BACKGROUND_STATUS =
-            "com.android.cts.net.hostside.app2.action.GET_RESTRICT_BACKGROUND_STATUS";
-    private static final String ACTION_CHECK_NETWORK =
-            "com.android.cts.net.hostside.app2.action.CHECK_NETWORK";
+
     private static final String ACTION_RECEIVER_READY =
             "com.android.cts.net.hostside.app2.action.RECEIVER_READY";
-    static final String ACTION_SEND_NOTIFICATION =
-            "com.android.cts.net.hostside.app2.action.SEND_NOTIFICATION";
     static final String ACTION_SHOW_TOAST =
             "com.android.cts.net.hostside.app2.action.SHOW_TOAST";
-    private static final String EXTRA_ACTION = "com.android.cts.net.hostside.app2.extra.ACTION";
-    private static final String EXTRA_RECEIVER_NAME =
-            "com.android.cts.net.hostside.app2.extra.RECEIVER_NAME";
-    private static final String EXTRA_NOTIFICATION_ID =
-            "com.android.cts.net.hostside.app2.extra.NOTIFICATION_ID";
-    private static final String EXTRA_NOTIFICATION_TYPE =
-            "com.android.cts.net.hostside.app2.extra.NOTIFICATION_TYPE";
 
     protected static final String NOTIFICATION_TYPE_CONTENT = "CONTENT";
     protected static final String NOTIFICATION_TYPE_DELETE = "DELETE";
@@ -90,6 +85,12 @@
     private static final int PROCESS_STATE_FOREGROUND_SERVICE = 4;
     private static final int PROCESS_STATE_TOP = 2;
 
+    private static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
+
+    protected static final int TYPE_COMPONENT_ACTIVTIY = 0;
+    protected static final int TYPE_COMPONENT_FOREGROUND_SERVICE = 1;
+
+    private static final int FOREGROUND_PROC_NETWORK_TIMEOUT_MS = 6000;
 
     // Must be higher than NETWORK_TIMEOUT_MS
     private static final int ORDERED_BROADCAST_TIMEOUT_MS = NETWORK_TIMEOUT_MS * 4;
@@ -100,6 +101,7 @@
     protected WifiManager mWfm;
     protected int mUid;
     private String mMeteredWifi;
+    private MyServiceClient mServiceClient;
     private boolean mHasWatch;
     private String mDeviceIdleConstantsSetting;
     private boolean mSupported;
@@ -114,6 +116,8 @@
         mWfm = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
         mUid = getUid(TEST_APP2_PKG);
         final int myUid = getUid(mContext.getPackageName());
+        mServiceClient = new MyServiceClient(mContext);
+        mServiceClient.bind();
         mHasWatch = mContext.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_WATCH);
         if (mHasWatch) {
@@ -128,6 +132,13 @@
                 + "\tapp2: uid=" + mUid + ", state=" + getProcessStateByUid(mUid));
    }
 
+    @Override
+    protected void tearDown() throws Exception {
+        mServiceClient.unbind();
+
+        super.tearDown();
+    }
+
     protected int getUid(String packageName) throws Exception {
         return mContext.getPackageManager().getPackageUid(packageName, 0);
     }
@@ -183,19 +194,13 @@
     }
 
     protected int getNumberBroadcastsReceived(String receiverName, String action) throws Exception {
-        final Intent intent = new Intent(ACTION_GET_COUNTERS);
-        intent.putExtra(EXTRA_ACTION, ACTION_RESTRICT_BACKGROUND_CHANGED);
-        intent.putExtra(EXTRA_RECEIVER_NAME, receiverName);
-        final String resultData = sendOrderedBroadcast(intent);
-        assertNotNull("timeout waiting for ordered broadcast result", resultData);
-        return Integer.valueOf(resultData);
+        return mServiceClient.getCounters(receiverName, action);
     }
 
     protected void assertRestrictBackgroundStatus(int expectedStatus) throws Exception {
-        final Intent intent = new Intent(ACTION_GET_RESTRICT_BACKGROUND_STATUS);
-        final String resultData = sendOrderedBroadcast(intent);
-        assertNotNull("timeout waiting for ordered broadcast result", resultData);
-        final String actualStatus = toString(Integer.parseInt(resultData));
+        final String status = mServiceClient.getRestrictBackgroundStatus();
+        assertNotNull("didn't get API status from app2", status);
+        final String actualStatus = toString(Integer.parseInt(status));
         assertEquals("wrong status", toString(expectedStatus), actualStatus);
     }
 
@@ -240,13 +245,11 @@
      */
     protected void assertsForegroundAlwaysHasNetworkAccess() throws Exception{
         // Checks foreground first.
-        launchActivity();
-        assertForegroundNetworkAccess();
+        launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
         finishActivity();
 
         // Then foreground service
-        startForegroundService();
-        assertForegroundServiceNetworkAccess();
+        launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
         stopForegroundService();
     }
 
@@ -343,15 +346,20 @@
      * @return error message with the mismatch (or empty if assertion passed).
      */
     private String checkNetworkAccess(boolean expectAvailable) throws Exception {
-        String resultData = sendOrderedBroadcast(new Intent(ACTION_CHECK_NETWORK));
+        final String resultData = mServiceClient.checkNetworkStatus();
+        return checkForAvailabilityInResultData(resultData, expectAvailable);
+    }
+
+    private String checkForAvailabilityInResultData(String resultData, boolean expectAvailable) {
         if (resultData == null) {
-            return "timeout waiting for ordered broadcast";
+            assertNotNull("Network status from app2 is null", resultData);
         }
         // Network status format is described on MyBroadcastReceiver.checkNetworkStatus()
         final String[] parts = resultData.split(NETWORK_STATUS_SEPARATOR);
         assertEquals("Wrong network status: " + resultData, 5, parts.length); // Sanity check
-        final State state = State.valueOf(parts[0]);
-        final DetailedState detailedState = DetailedState.valueOf(parts[1]);
+        final State state = parts[0].equals("null") ? null : State.valueOf(parts[0]);
+        final DetailedState detailedState = parts[1].equals("null")
+                ? null : DetailedState.valueOf(parts[1]);
         final boolean connected = Boolean.valueOf(parts[2]);
         final String connectionCheckDetails = parts[3];
         final String networkInfo = parts[4];
@@ -593,6 +601,9 @@
     protected void addRestrictBackgroundWhitelist(int uid) throws Exception {
         executeShellCommand("cmd netpolicy add restrict-background-whitelist " + uid);
         assertRestrictBackgroundWhitelist(uid, true);
+        // UID policies live by the Highlander rule: "There can be only one".
+        // Hence, if app is whitelisted, it should not be blacklisted.
+        assertRestrictBackgroundBlacklist(uid, false);
     }
 
     protected void removeRestrictBackgroundWhitelist(int uid) throws Exception {
@@ -607,6 +618,9 @@
     protected void addRestrictBackgroundBlacklist(int uid) throws Exception {
         executeShellCommand("cmd netpolicy add restrict-background-blacklist " + uid);
         assertRestrictBackgroundBlacklist(uid, true);
+        // UID policies live by the Highlander rule: "There can be only one".
+        // Hence, if app is blacklisted, it should not be whitelisted.
+        assertRestrictBackgroundWhitelist(uid, false);
     }
 
     protected void removeRestrictBackgroundBlacklist(int uid) throws Exception {
@@ -686,7 +700,6 @@
         Log.i(TAG, "Setting Battery Saver Mode to " + enabled);
         if (enabled) {
             turnBatteryOff();
-            executeSilentShellCommand("cmd battery unplug");
             executeSilentShellCommand("settings put global low_power 1");
         } else {
             executeSilentShellCommand("settings put global low_power 0");
@@ -739,9 +752,10 @@
      * {@link #runDeviceTests(String, String)} is executed.
      */
     protected void registerBroadcastReceiver() throws Exception {
-        executeShellCommand("am startservice com.android.cts.net.hostside.app2/.MyService");
+        mServiceClient.registerBroadcastReceiver();
+
         // Wait until receiver is ready.
-        final int maxTries = 5;
+        final int maxTries = 10;
         for (int i = 1; i <= maxTries; i++) {
             final String message =
                     sendOrderedBroadcast(new Intent(ACTION_RECEIVER_READY), SECOND_IN_MS);
@@ -786,35 +800,60 @@
                 mDeviceIdleConstantsSetting));
     }
 
-    protected void startForegroundService() throws Exception {
-        executeShellCommand(
-                "am startservice -f 1 com.android.cts.net.hostside.app2/.MyForegroundService");
-        assertForegroundServiceState();
+    protected void launchComponentAndAssertNetworkAccess(int type) throws Exception {
+        if (type == TYPE_COMPONENT_ACTIVTIY) {
+            turnScreenOn();
+        }
+        final CountDownLatch latch = new CountDownLatch(1);
+        final Intent launchIntent = getIntentForComponent(type);
+        final Bundle extras = new Bundle();
+        final String[] errors = new String[] {null};
+        extras.putBinder(KEY_NETWORK_STATE_OBSERVER, getNewNetworkStateObserver(latch, errors));
+        launchIntent.putExtras(extras);
+        if (type == TYPE_COMPONENT_ACTIVTIY) {
+            mContext.startActivity(launchIntent);
+        } else if (type == TYPE_COMPONENT_FOREGROUND_SERVICE) {
+            mContext.startService(launchIntent);
+        }
+        if (latch.await(FOREGROUND_PROC_NETWORK_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            if (!errors[0].isEmpty()) {
+                // TODO: revert this change once b/35523062 is fixed.
+//                fail("Network is not available for app2 (" + mUid + "): " + errors[0]);
+                assertForegroundNetworkAccess();
+            }
+        } else {
+            fail("Timed out waiting for network availability status from app2 (" + mUid + ")");
+        }
+    }
+
+    private Intent getIntentForComponent(int type) {
+        final Intent intent = new Intent();
+        if (type == TYPE_COMPONENT_ACTIVTIY) {
+            intent.setComponent(new ComponentName(TEST_APP2_PKG, TEST_APP2_ACTIVITY_CLASS));
+        } else if (type == TYPE_COMPONENT_FOREGROUND_SERVICE) {
+            intent.setComponent(new ComponentName(TEST_APP2_PKG, TEST_APP2_SERVICE_CLASS))
+                    .setFlags(1);
+        } else {
+            fail("Unknown type: " + type);
+        }
+        return intent;
     }
 
     protected void stopForegroundService() throws Exception {
-        executeShellCommand(
-                "am startservice -f 2 com.android.cts.net.hostside.app2/.MyForegroundService");
+        executeShellCommand(String.format("am startservice -f 2 %s/%s",
+                TEST_APP2_PKG, TEST_APP2_SERVICE_CLASS));
         // NOTE: cannot assert state because it depends on whether activity was on top before.
     }
 
-    /**
-     * Launches an activity on app2 so its process is elevated to foreground status.
-     */
-    protected void launchActivity() throws Exception {
-        turnScreenOn();
-        executeShellCommand("am start com.android.cts.net.hostside.app2/.MyActivity");
-        final int maxTries = 30;
-        ProcessState state = null;
-        for (int i = 1; i <= maxTries; i++) {
-            state = getProcessStateByUid(mUid);
-            if (state.state == PROCESS_STATE_TOP) return;
-            Log.w(TAG, "launchActivity(): uid " + mUid + " not on TOP state on attempt #" + i
-                    + "; turning screen on and sleeping 1s before checking again");
-            turnScreenOn();
-            SystemClock.sleep(SECOND_IN_MS);
-        }
-        fail("App2 is not on foreground state after " + maxTries + " attempts: " + state);
+    private Binder getNewNetworkStateObserver(final CountDownLatch latch,
+            final String[] errors) {
+        return new INetworkStateObserver.Stub() {
+            @Override
+            public void onNetworkStateChecked(String resultData) {
+                errors[0] = checkForAvailabilityInResultData(resultData, true);
+                latch.countDown();
+            }
+        };
     }
 
     /**
@@ -826,13 +865,10 @@
                 + "--receiver-foreground --receiver-registered-only");
     }
 
-    protected void sendNotification(int notificationId, String notificationType) {
-        final Intent intent = new Intent(ACTION_SEND_NOTIFICATION);
-        intent.putExtra(EXTRA_NOTIFICATION_ID, notificationId);
-        intent.putExtra(EXTRA_NOTIFICATION_TYPE, notificationType);
-        Log.d(TAG, "Sending notification broadcast (id=" + notificationId + ", type="
-                + notificationType + ": " + intent);
-        mContext.sendBroadcast(intent);
+    protected void sendNotification(int notificationId, String notificationType) throws Exception {
+        Log.d(TAG, "Sending notification broadcast (id=" + notificationId
+                + ", type=" + notificationType);
+        mServiceClient.sendNotification(notificationId, notificationType);
     }
 
     protected String showToast() {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
index 9e4b0c1..c3537c8 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
@@ -104,8 +104,7 @@
 
         // Make sure foreground app doesn't lose access upon enabling Data Saver.
         setRestrictBackground(false);
-        launchActivity();
-        assertForegroundNetworkAccess();
+        launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
         setRestrictBackground(true);
         assertForegroundNetworkAccess();
 
@@ -121,8 +120,7 @@
 
         // Make sure foreground service doesn't lose access upon enabling Data Saver.
         setRestrictBackground(false);
-        startForegroundService();
-        assertForegroundNetworkAccess();
+        launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
         setRestrictBackground(true);
         assertForegroundNetworkAccess();
         stopForegroundService();
@@ -137,26 +135,34 @@
         assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
 
         assertsForegroundAlwaysHasNetworkAccess();
+        assertRestrictBackgroundChangedReceived(1);
         assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
 
-        // Make sure blacklist prevails over whitelist.
+        // UID policies live by the Highlander rule: "There can be only one".
+        // Hence, if app is whitelisted, it should not be blacklisted anymore.
         setRestrictBackground(true);
         assertRestrictBackgroundChangedReceived(2);
         assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
         addRestrictBackgroundWhitelist(mUid);
         assertRestrictBackgroundChangedReceived(3);
-        assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+        assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_WHITELISTED);
 
         // Check status after removing blacklist.
+        // ...re-enables first
+        addRestrictBackgroundBlacklist(mUid);
+        assertRestrictBackgroundChangedReceived(4);
+        assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+        assertsForegroundAlwaysHasNetworkAccess();
+        // ... remove blacklist - access's still rejected because Data Saver is on
         removeRestrictBackgroundBlacklist(mUid);
         assertRestrictBackgroundChangedReceived(4);
-        assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_WHITELISTED);
+        assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+        assertsForegroundAlwaysHasNetworkAccess();
+        // ... finally, disable Data Saver
         setRestrictBackground(false);
         assertRestrictBackgroundChangedReceived(5);
         assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
-
         assertsForegroundAlwaysHasNetworkAccess();
-        assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
     }
 
     public void testGetRestrictBackgroundStatus_requiredWhitelistedPackages() throws Exception {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
new file mode 100644
index 0000000..ff05d8c
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 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.cts.net.hostside;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.ConditionVariable;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.cts.net.hostside.IMyService;
+
+import java.io.FileDescriptor;
+
+public class MyServiceClient {
+    private static final int TIMEOUT_MS = 5000;
+    private static final String PACKAGE = MyServiceClient.class.getPackage().getName();
+    private static final String APP2_PACKAGE = PACKAGE + ".app2";
+    private static final String SERVICE_NAME = APP2_PACKAGE + ".MyService";
+
+    private Context mContext;
+    private ServiceConnection mServiceConnection;
+    private IMyService mService;
+
+    public MyServiceClient(Context context) {
+        mContext = context;
+    }
+
+    public void bind() {
+        if (mService != null) {
+            throw new IllegalStateException("Already bound");
+        }
+
+        final ConditionVariable cv = new ConditionVariable();
+        mServiceConnection = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                mService = IMyService.Stub.asInterface(service);
+                cv.open();
+            }
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                mService = null;
+            }
+        };
+
+        final Intent intent = new Intent();
+        intent.setComponent(new ComponentName(APP2_PACKAGE, SERVICE_NAME));
+        // Needs to use BIND_ALLOW_OOM_MANAGEMENT and BIND_NOT_FOREGROUND so app2 does not run in
+        // the same process state as app
+        mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE
+                | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_FOREGROUND);
+        cv.block(TIMEOUT_MS);
+        if (mService == null) {
+            throw new IllegalStateException(
+                    "Could not bind to MyService service after " + TIMEOUT_MS + "ms");
+        }
+    }
+
+    public void unbind() {
+        if (mService != null) {
+            mContext.unbindService(mServiceConnection);
+        }
+    }
+
+    public void registerBroadcastReceiver() throws RemoteException {
+        mService.registerBroadcastReceiver();
+    }
+
+    public int getCounters(String receiverName, String action) throws RemoteException {
+        return mService.getCounters(receiverName, action);
+    }
+
+    public String checkNetworkStatus() throws RemoteException {
+        return mService.checkNetworkStatus();
+    }
+
+    public String getRestrictBackgroundStatus() throws RemoteException {
+        return mService.getRestrictBackgroundStatus();
+    }
+
+    public void sendNotification(int notificationId, String notificationType) throws RemoteException {
+        mService.sendNotification(notificationId, notificationType);
+    }
+}
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java
index e07c0f5..20bbd5a 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java
@@ -16,7 +16,14 @@
 package com.android.cts.net.hostside.app2;
 
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.cts.net.hostside.INetworkStateObserver;
 
 public final class Common {
 
@@ -26,27 +33,13 @@
     // AbstractRestrictBackgroundNetworkTestCase.java
     static final String MANIFEST_RECEIVER = "ManifestReceiver";
     static final String DYNAMIC_RECEIVER = "DynamicReceiver";
-    static final String ACTION_GET_COUNTERS =
-            "com.android.cts.net.hostside.app2.action.GET_COUNTERS";
-    static final String ACTION_GET_RESTRICT_BACKGROUND_STATUS =
-            "com.android.cts.net.hostside.app2.action.GET_RESTRICT_BACKGROUND_STATUS";
-    static final String ACTION_CHECK_NETWORK =
-            "com.android.cts.net.hostside.app2.action.CHECK_NETWORK";
+
     static final String ACTION_RECEIVER_READY =
             "com.android.cts.net.hostside.app2.action.RECEIVER_READY";
     static final String ACTION_FINISH_ACTIVITY =
             "com.android.cts.net.hostside.app2.action.FINISH_ACTIVITY";
-    static final String ACTION_SEND_NOTIFICATION =
-            "com.android.cts.net.hostside.app2.action.SEND_NOTIFICATION";
     static final String ACTION_SHOW_TOAST =
             "com.android.cts.net.hostside.app2.action.SHOW_TOAST";
-    static final String EXTRA_ACTION = "com.android.cts.net.hostside.app2.extra.ACTION";
-    static final String EXTRA_RECEIVER_NAME =
-            "com.android.cts.net.hostside.app2.extra.RECEIVER_NAME";
-    static final String EXTRA_NOTIFICATION_ID =
-            "com.android.cts.net.hostside.app2.extra.NOTIFICATION_ID";
-    static final String EXTRA_NOTIFICATION_TYPE =
-            "com.android.cts.net.hostside.app2.extra.NOTIFICATION_TYPE";
 
     static final String NOTIFICATION_TYPE_CONTENT = "CONTENT";
     static final String NOTIFICATION_TYPE_DELETE = "DELETE";
@@ -56,6 +49,9 @@
     static final String NOTIFICATION_TYPE_ACTION_BUNDLE = "ACTION_BUNDLE";
     static final String NOTIFICATION_TYPE_ACTION_REMOTE_INPUT = "ACTION_REMOTE_INPUT";
 
+    static final String TEST_PKG = "com.android.cts.net.hostside";
+    static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
+
     static int getUid(Context context) {
         final String packageName = context.getPackageName();
         try {
@@ -64,4 +60,26 @@
             throw new IllegalStateException("Could not get UID for " + packageName, e);
         }
     }
+
+    static void notifyNetworkStateObserver(Context context, Intent intent) {
+        if (intent == null) {
+            return;
+        }
+        final Bundle extras = intent.getExtras();
+        if (extras == null) {
+            return;
+        }
+        final INetworkStateObserver observer = INetworkStateObserver.Stub.asInterface(
+                extras.getBinder(KEY_NETWORK_STATE_OBSERVER));
+        if (observer != null) {
+            AsyncTask.execute(() -> {
+                try {
+                    observer.onNetworkStateChecked(
+                            MyBroadcastReceiver.checkNetworkStatus(context));
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error occured while notifying the observer: " + e);
+                }
+            });
+        }
+    }
 }
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
index 444b696..da7e704 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
@@ -17,30 +17,47 @@
 
 import static com.android.cts.net.hostside.app2.Common.ACTION_FINISH_ACTIVITY;
 import static com.android.cts.net.hostside.app2.Common.TAG;
+import static com.android.cts.net.hostside.app2.Common.TEST_PKG;
 
 import android.app.Activity;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.AsyncTask;
 import android.os.Bundle;
+import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.cts.net.hostside.INetworkStateObserver;
+
 /**
  * Activity used to bring process to foreground.
  */
 public class MyActivity extends Activity {
 
+    private BroadcastReceiver finishCommandReceiver = null;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        registerReceiver(new BroadcastReceiver() {
-
+        Common.notifyNetworkStateObserver(this, getIntent());
+        finishCommandReceiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
                 Log.d(TAG, "Finishing MyActivity");
                 MyActivity.this.finish();
-            }}, new IntentFilter(ACTION_FINISH_ACTIVITY));
+            }
+        };
+        registerReceiver(finishCommandReceiver, new IntentFilter(ACTION_FINISH_ACTIVITY));
+    }
+
+    @Override
+    public void finish() {
+        if (finishCommandReceiver != null) {
+            unregisterReceiver(finishCommandReceiver);
+        }
+        super.finish();
     }
 
     @Override
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
index 733c3aa..aa54075 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
@@ -18,16 +18,8 @@
 
 import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
 
-import static com.android.cts.net.hostside.app2.Common.ACTION_CHECK_NETWORK;
-import static com.android.cts.net.hostside.app2.Common.ACTION_GET_COUNTERS;
-import static com.android.cts.net.hostside.app2.Common.ACTION_GET_RESTRICT_BACKGROUND_STATUS;
 import static com.android.cts.net.hostside.app2.Common.ACTION_RECEIVER_READY;
-import static com.android.cts.net.hostside.app2.Common.ACTION_SEND_NOTIFICATION;
 import static com.android.cts.net.hostside.app2.Common.ACTION_SHOW_TOAST;
-import static com.android.cts.net.hostside.app2.Common.EXTRA_ACTION;
-import static com.android.cts.net.hostside.app2.Common.EXTRA_NOTIFICATION_ID;
-import static com.android.cts.net.hostside.app2.Common.EXTRA_NOTIFICATION_TYPE;
-import static com.android.cts.net.hostside.app2.Common.EXTRA_RECEIVER_NAME;
 import static com.android.cts.net.hostside.app2.Common.MANIFEST_RECEIVER;
 import static com.android.cts.net.hostside.app2.Common.NOTIFICATION_TYPE_ACTION;
 import static com.android.cts.net.hostside.app2.Common.NOTIFICATION_TYPE_ACTION_BUNDLE;
@@ -56,14 +48,12 @@
 
 import java.net.HttpURLConnection;
 import java.net.URL;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
 
 /**
  * Receiver used to:
  * <ol>
- * <li>Stored received RESTRICT_BACKGROUND_CHANGED broadcasts in a shared preference.
- * <li>Returned the number of RESTRICT_BACKGROUND_CHANGED broadcasts in an ordered broadcast.
+ *   <li>Count number of {@code RESTRICT_BACKGROUND_CHANGED} broadcasts received.
+ *   <li>Show a toast.
  * </ol>
  */
 public class MyBroadcastReceiver extends BroadcastReceiver {
@@ -89,23 +79,11 @@
             case ACTION_RESTRICT_BACKGROUND_CHANGED:
                 increaseCounter(context, action);
                 break;
-            case ACTION_GET_COUNTERS:
-                setResultDataFromCounter(context, intent);
-                break;
-            case ACTION_GET_RESTRICT_BACKGROUND_STATUS:
-                getRestrictBackgroundStatus(context, intent);
-                break;
-            case ACTION_CHECK_NETWORK:
-                checkNetwork(context, intent);
-                break;
             case ACTION_RECEIVER_READY:
                 final String message = mName + " is ready to rumble";
                 Log.d(TAG, message);
                 setResultData(message);
                 break;
-            case ACTION_SEND_NOTIFICATION:
-                sendNotification(context, intent);
-                break;
             case ACTION_SHOW_TOAST:
                 showToast(context);
                 break;
@@ -114,14 +92,20 @@
         }
     }
 
+    @Override
+    public String toString() {
+        return "[MyBroadcastReceiver: mName=" + mName + "]";
+    }
+
     private void increaseCounter(Context context, String action) {
-        final SharedPreferences prefs = context.getSharedPreferences(mName, Context.MODE_PRIVATE);
+        final SharedPreferences prefs = context.getApplicationContext()
+                .getSharedPreferences(mName, Context.MODE_PRIVATE);
         final int value = prefs.getInt(action, 0) + 1;
         Log.d(TAG, "increaseCounter('" + action + "'): setting '" + mName + "' to " + value);
         prefs.edit().putInt(action, value).apply();
     }
 
-    private int getCounter(Context context, String action, String receiverName) {
+    static int getCounter(Context context, String action, String receiverName) {
         final SharedPreferences prefs = context.getSharedPreferences(receiverName,
                 Context.MODE_PRIVATE);
         final int value = prefs.getInt(action, 0);
@@ -129,29 +113,14 @@
         return value;
     }
 
-    private void getRestrictBackgroundStatus(Context context, Intent intent) {
+    static String getRestrictBackgroundStatus(Context context) {
         final ConnectivityManager cm = (ConnectivityManager) context
                 .getSystemService(Context.CONNECTIVITY_SERVICE);
         final int apiStatus = cm.getRestrictBackgroundStatus();
         Log.d(TAG, "getRestrictBackgroundStatus: returning " + apiStatus);
-        setResultData(Integer.toString(apiStatus));
+        return String.valueOf(apiStatus);
     }
 
-    private void checkNetwork(final Context context, Intent intent) {
-        final ConnectivityManager cm = (ConnectivityManager) context
-                .getSystemService(Context.CONNECTIVITY_SERVICE);
-
-        String netStatus = null;
-        try {
-            netStatus = checkNetworkStatus(context, cm);
-        } catch (InterruptedException e) {
-            Log.e(TAG, "Timeout checking network status");
-        }
-        Log.d(TAG, "checkNetwork(): returning " + netStatus);
-        setResultData(netStatus);
-    }
-
-
     private static final String NETWORK_STATUS_TEMPLATE = "%s|%s|%s|%s|%s";
     /**
      * Checks whether the network is available and return a string which can then be send as a
@@ -182,78 +151,61 @@
      * </code></pre>
      *
      */
-    private String checkNetworkStatus(final Context context, final ConnectivityManager cm)
-            throws InterruptedException {
-        final LinkedBlockingQueue<String> result = new LinkedBlockingQueue<>(1);
-        new Thread(new Runnable() {
-
-            @Override
-            public void run() {
-                // TODO: connect to a hostside server instead
-                final String address = "http://example.com";
-                final NetworkInfo networkInfo = cm.getActiveNetworkInfo();
-                Log.d(TAG, "Running checkNetworkStatus() on thread "
-                        + Thread.currentThread().getName() + " for UID " + getUid(context)
-                        + "\n\tactiveNetworkInfo: " + networkInfo + "\n\tURL: " + address);
-                boolean checkStatus = false;
-                String checkDetails = "N/A";
-                try {
-                    final URL url = new URL(address);
-                    final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
-                    conn.setReadTimeout(NETWORK_TIMEOUT_MS);
-                    conn.setConnectTimeout(NETWORK_TIMEOUT_MS / 2);
-                    conn.setRequestMethod("GET");
-                    conn.setDoInput(true);
-                    conn.connect();
-                    final int response = conn.getResponseCode();
-                    checkStatus = true;
-                    checkDetails = "HTTP response for " + address + ": " + response;
-                } catch (Exception e) {
-                    checkStatus = false;
-                    checkDetails = "Exception getting " + address + ": " + e;
-                }
-                Log.d(TAG, checkDetails);
-                final String status = String.format(NETWORK_STATUS_TEMPLATE,
-                        networkInfo.getState().name(), networkInfo.getDetailedState().name(),
-                        Boolean.toString(checkStatus), checkDetails, networkInfo);
-                Log.d(TAG, "Offering " + status);
-                result.offer(status);
-            }
-        }, mName).start();
-        return result.poll(NETWORK_TIMEOUT_MS * 2, TimeUnit.MILLISECONDS);
-    }
-
-    private void setResultDataFromCounter(Context context, Intent intent) {
-        final String action = intent.getStringExtra(EXTRA_ACTION);
-        if (action == null) {
-            Log.e(TAG, "Missing extra '" + EXTRA_ACTION + "' on " + intent);
-            return;
+    // TODO: now that it uses Binder, it counl return a Bundle with the data parts instead...
+    static String checkNetworkStatus(Context context) {
+        final ConnectivityManager cm =
+                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        // TODO: connect to a hostside server instead
+        final String address = "http://example.com";
+        final NetworkInfo networkInfo = cm.getActiveNetworkInfo();
+        Log.d(TAG, "Running checkNetworkStatus() on thread "
+                + Thread.currentThread().getName() + " for UID " + getUid(context)
+                + "\n\tactiveNetworkInfo: " + networkInfo + "\n\tURL: " + address);
+        boolean checkStatus = false;
+        String checkDetails = "N/A";
+        try {
+            final URL url = new URL(address);
+            final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+            conn.setReadTimeout(NETWORK_TIMEOUT_MS);
+            conn.setConnectTimeout(NETWORK_TIMEOUT_MS / 2);
+            conn.setRequestMethod("GET");
+            conn.setDoInput(true);
+            conn.connect();
+            final int response = conn.getResponseCode();
+            checkStatus = true;
+            checkDetails = "HTTP response for " + address + ": " + response;
+        } catch (Exception e) {
+            checkStatus = false;
+            checkDetails = "Exception getting " + address + ": " + e;
         }
-        final String receiverName = intent.getStringExtra(EXTRA_RECEIVER_NAME);
-        if (receiverName == null) {
-            Log.e(TAG, "Missing extra '" + EXTRA_RECEIVER_NAME + "' on " + intent);
-            return;
+        Log.d(TAG, checkDetails);
+        final String state, detailedState;
+        if (networkInfo != null) {
+            state = networkInfo.getState().name();
+            detailedState = networkInfo.getDetailedState().name();
+        } else {
+            state = detailedState = "null";
         }
-        final int counter = getCounter(context, action, receiverName);
-        setResultData(String.valueOf(counter));
+        final String status = String.format(NETWORK_STATUS_TEMPLATE, state, detailedState,
+                Boolean.valueOf(checkStatus), checkDetails, networkInfo);
+        Log.d(TAG, "Offering " + status);
+        return status;
     }
 
     /**
      * Sends a system notification containing actions with pending intents to launch the app's
      * main activitiy or service.
      */
-    private void sendNotification(Context context, Intent intent) {
-        final int notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
-        final String notificationType = intent.getStringExtra(EXTRA_NOTIFICATION_TYPE);
-        Log.d(TAG, "sendNotification: id=" + notificationId + ", type=" + notificationType
-                + ", intent=" + intent);
+    static void sendNotification(Context context, String channelId, int notificationId,
+            String notificationType ) {
+        Log.d(TAG, "sendNotification: id=" + notificationId + ", type=" + notificationType);
         final Intent serviceIntent = new Intent(context, MyService.class);
         final PendingIntent pendingIntent = PendingIntent.getService(context, 0, serviceIntent,
                 notificationId);
         final Bundle bundle = new Bundle();
         bundle.putCharSequence("parcelable", "I am not");
 
-        final Notification.Builder builder = new Notification.Builder(context)
+        final Notification.Builder builder = new Notification.Builder(context, channelId)
                 .setSmallIcon(R.drawable.ic_notification);
 
         Action action = null;
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java
index b88c45d..ff4ba65 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java
@@ -16,18 +16,27 @@
 package com.android.cts.net.hostside.app2;
 
 import static com.android.cts.net.hostside.app2.Common.TAG;
+import static com.android.cts.net.hostside.app2.Common.TEST_PKG;
+
 import android.R;
 import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
 import android.app.Service;
 import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.cts.net.hostside.INetworkStateObserver;
+
 /**
  * Service used to change app state to FOREGROUND_SERVICE.
  */
 public class MyForegroundService extends Service {
-
+    private static final String NOTIFICATION_CHANNEL_ID = "cts/MyForegroundService";
     private static final int FLAG_START_FOREGROUND = 1;
     private static final int FLAG_STOP_FOREGROUND = 2;
 
@@ -39,12 +48,17 @@
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
         Log.v(TAG, "MyForegroundService.onStartCommand(): " + intent);
+        NotificationManager notificationManager = getSystemService(NotificationManager.class);
+        notificationManager.createNotificationChannel(new NotificationChannel(
+                NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_ID,
+                NotificationManager.IMPORTANCE_DEFAULT));
         switch (intent.getFlags()) {
             case FLAG_START_FOREGROUND:
                 Log.d(TAG, "Starting foreground");
-                startForeground(42, new Notification.Builder(this)
+                startForeground(42, new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
                         .setSmallIcon(R.drawable.ic_dialog_alert) // any icon is fine
                         .build());
+                Common.notifyNetworkStateObserver(this, intent);
                 break;
             case FLAG_STOP_FOREGROUND:
                 Log.d(TAG, "Stopping foreground");
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java
index e6454c7..2496c4a 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java
@@ -20,31 +20,92 @@
 import static com.android.cts.net.hostside.app2.Common.DYNAMIC_RECEIVER;
 import static com.android.cts.net.hostside.app2.Common.TAG;
 
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.SharedPreferences;
 import android.os.IBinder;
+import android.os.Looper;
 import android.util.Log;
+import android.widget.Toast;
+
+import com.android.cts.net.hostside.IMyService;
 
 /**
  * Service used to dynamically register a broadcast receiver.
  */
 public class MyService extends Service {
+    private static final String NOTIFICATION_CHANNEL_ID = "MyService";
+
+    private MyBroadcastReceiver mReceiver;
+
+    // TODO: move MyBroadcast static functions here - they were kept there to make git diff easier.
+
+    private IMyService.Stub mBinder =
+        new IMyService.Stub() {
+
+        @Override
+        public void registerBroadcastReceiver() {
+            if (mReceiver != null) {
+                Log.d(TAG, "receiver already registered: " + mReceiver);
+                return;
+            }
+            final Context context = getApplicationContext();
+            mReceiver = new MyBroadcastReceiver(DYNAMIC_RECEIVER);
+            context.registerReceiver(mReceiver, new IntentFilter(ACTION_RECEIVER_READY));
+            context.registerReceiver(mReceiver,
+                    new IntentFilter(ACTION_RESTRICT_BACKGROUND_CHANGED));
+            Log.d(TAG, "receiver registered");
+        }
+
+        @Override
+        public int getCounters(String receiverName, String action) {
+            return MyBroadcastReceiver.getCounter(getApplicationContext(), action, receiverName);
+        }
+
+        @Override
+        public String checkNetworkStatus() {
+            return MyBroadcastReceiver.checkNetworkStatus(getApplicationContext());
+        }
+
+        @Override
+        public String getRestrictBackgroundStatus() {
+            return MyBroadcastReceiver.getRestrictBackgroundStatus(getApplicationContext());
+        }
+
+        @Override
+        public void sendNotification(int notificationId, String notificationType) {
+            MyBroadcastReceiver .sendNotification(getApplicationContext(), NOTIFICATION_CHANNEL_ID,
+                    notificationId, notificationType);
+        }
+      };
 
     @Override
     public IBinder onBind(Intent intent) {
-        return null;
+        return mBinder;
     }
 
     @Override
-    public int onStartCommand(Intent intent, int flags, int startId) {
-        Log.d(TAG, "MyService.onStartCommand: " + intent);
+    public void onCreate() {
         final Context context = getApplicationContext();
-        final MyBroadcastReceiver myReceiver = new MyBroadcastReceiver(DYNAMIC_RECEIVER);
-        context.registerReceiver(myReceiver, new IntentFilter(ACTION_RECEIVER_READY));
-        context.registerReceiver(myReceiver, new IntentFilter(ACTION_RESTRICT_BACKGROUND_CHANGED));
-        Log.d(TAG, "receiver registered");
-        return START_STICKY;
+        ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
+                .createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID,
+                        NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT));
+    }
+
+    @Override
+    public void onDestroy() {
+        final Context context = getApplicationContext();
+        ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
+                .deleteNotificationChannel(NOTIFICATION_CHANNEL_ID);
+        if (mReceiver != null) {
+            Log.d(TAG, "onDestroy(): unregistering " + mReceiver);
+            getApplicationContext().unregisterReceiver(mReceiver);
+        }
+
+        super.onDestroy();
     }
 }
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
index e96537c..c6df893 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
@@ -16,7 +16,7 @@
 
 package com.android.cts.net;
 
-import com.android.cts.migration.MigrationHelper;
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.ddmlib.Log;
 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
 import com.android.ddmlib.testrunner.TestIdentifier;
@@ -78,7 +78,8 @@
 
     protected void installPackage(String apk) throws FileNotFoundException,
             DeviceNotAvailableException {
-        assertNull(getDevice().installPackage(MigrationHelper.getTestFile(mCtsBuild, apk), false));
+        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
+        assertNull(getDevice().installPackage(buildHelper.getTestFile(apk), false));
     }
 
     protected void uninstallPackage(String packageName, boolean shouldSucceed)
diff --git a/tests/cts/net/Android.mk b/tests/cts/net/Android.mk
index 7d2b0ba..98cde9b 100644
--- a/tests/cts/net/Android.mk
+++ b/tests/cts/net/Android.mk
@@ -36,7 +36,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     core-tests-support \
-    ctsdeviceutil \
+    compatibility-device-util \
     ctstestrunner \
     ctstestserver \
     mockwebserver \
diff --git a/tests/cts/net/src/android/net/http/cts/HttpResponseCacheTest.java b/tests/cts/net/src/android/net/http/cts/HttpResponseCacheTest.java
index 7987a50..198f973 100644
--- a/tests/cts/net/src/android/net/http/cts/HttpResponseCacheTest.java
+++ b/tests/cts/net/src/android/net/http/cts/HttpResponseCacheTest.java
@@ -21,9 +21,10 @@
 
 import junit.framework.TestCase;
 
-import android.cts.util.FileUtils;
 import android.net.http.HttpResponseCache;
 
+import com.android.compatibility.common.util.FileUtils;
+
 import java.io.File;
 import java.io.InputStream;
 import java.net.CacheRequest;
diff --git a/tests/cts/net/src/android/net/wifi/cts/WifiInfoTest.java b/tests/cts/net/src/android/net/wifi/cts/WifiInfoTest.java
index 696d215..5983cb7 100644
--- a/tests/cts/net/src/android/net/wifi/cts/WifiInfoTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiInfoTest.java
@@ -21,7 +21,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.cts.util.PollingCheck;
 import android.net.wifi.SupplicantState;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
@@ -29,6 +28,8 @@
 import android.net.wifi.WifiSsid;
 import android.test.AndroidTestCase;
 
+import com.android.compatibility.common.util.PollingCheck;
+
 import java.util.concurrent.Callable;
 
 public class WifiInfoTest extends AndroidTestCase {