Merge "WifiStateMachine: check for null config after dhcp" into oc-mr1-dev
diff --git a/service/java/com/android/server/wifi/ConnectToNetworkNotificationBuilder.java b/service/java/com/android/server/wifi/ConnectToNetworkNotificationBuilder.java
new file mode 100644
index 0000000..38c0ad0
--- /dev/null
+++ b/service/java/com/android/server/wifi/ConnectToNetworkNotificationBuilder.java
@@ -0,0 +1,146 @@
+/*
+ * 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.server.wifi;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.wifi.ScanResult;
+
+import com.android.internal.R;
+import com.android.internal.notification.SystemNotificationChannels;
+
+/**
+ * Helper to create notifications for {@link OpenNetworkNotifier}.
+ */
+public class ConnectToNetworkNotificationBuilder {
+
+    /** Intent when user dismissed the "Connect to Network" notification. */
+    public static final String ACTION_USER_DISMISSED_NOTIFICATION =
+            "com.android.server.wifi.ConnectToNetworkNotification.USER_DISMISSED_NOTIFICATION";
+
+    /** Intent when user tapped action button to connect to recommended network. */
+    public static final String ACTION_CONNECT_TO_NETWORK =
+            "com.android.server.wifi.ConnectToNetworkNotification.CONNECT_TO_NETWORK";
+
+    /** Intent when user tapped action button to open Wi-Fi Settings. */
+    public static final String ACTION_PICK_WIFI_NETWORK =
+            "com.android.server.wifi.ConnectToNetworkNotification.PICK_WIFI_NETWORK";
+
+    /** Intent when user tapped "Failed to connect" notification to open Wi-Fi Settings. */
+    public static final String ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE =
+            "com.android.server.wifi.ConnectToNetworkNotification.PICK_NETWORK_AFTER_FAILURE";
+
+    private Context mContext;
+    private Resources mResources;
+    private FrameworkFacade mFrameworkFacade;
+
+    public ConnectToNetworkNotificationBuilder(
+            Context context,
+            FrameworkFacade framework) {
+        mContext = context;
+        mResources = context.getResources();
+        mFrameworkFacade = framework;
+    }
+
+    /**
+     * Creates the connect to network notification that alerts users of a recommended connectable
+     * network.
+     *
+     * There are two actions - "Options" link to the Wi-Fi picker activity, and "Connect" prompts
+     * the connection to the recommended network.
+     *
+     * @param network The network to be recommended
+     */
+    public Notification createConnectToNetworkNotification(ScanResult network) {
+        Notification.Action connectAction = new Notification.Action.Builder(
+                null /* icon */,
+                mResources.getText(R.string.wifi_available_action_connect),
+                getPrivateBroadcast(ACTION_CONNECT_TO_NETWORK)).build();
+        Notification.Action allNetworksAction = new Notification.Action.Builder(
+                null /* icon */,
+                mResources.getText(R.string.wifi_available_action_all_networks),
+                getPrivateBroadcast(ACTION_PICK_WIFI_NETWORK)).build();
+        return createNotificationBuilder(
+                mContext.getText(R.string.wifi_available_title), network.SSID)
+                .addAction(connectAction)
+                .addAction(allNetworksAction)
+                .build();
+    }
+
+    /**
+     * Creates the notification that indicates the controller is attempting to connect to the
+     * recommended network.
+     *
+     * @param network The network to be recommended
+     */
+    public Notification createNetworkConnectingNotification(ScanResult network) {
+        return createNotificationBuilder(
+                mContext.getText(R.string.wifi_available_title_connecting), network.SSID)
+                .setProgress(0 /* max */, 0 /* progress */, true /* indeterminate */)
+                .build();
+    }
+
+    /**
+     * Creates the notification that indicates the controller successfully connected to the
+     * recommended network.
+     *
+     * @param network The network to be recommended
+     */
+    public Notification createNetworkConnectedNotification(ScanResult network) {
+        return createNotificationBuilder(
+                mContext.getText(R.string.wifi_available_title_connected), network.SSID)
+                .build();
+    }
+
+    /**
+     * Creates the notification that indicates the controller failed to connect to the recommended
+     * network. Tapping this notification opens the wifi picker.
+     */
+    public Notification createNetworkFailedNotification() {
+        return createNotificationBuilder(
+                mContext.getText(R.string.wifi_available_title_failed_to_connect),
+                mContext.getText(R.string.wifi_available_content_failed_to_connect))
+                .setContentIntent(
+                        getPrivateBroadcast(ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE))
+                .setAutoCancel(true)
+                .build();
+    }
+
+    private Notification.Builder createNotificationBuilder(
+            CharSequence title, CharSequence content) {
+        return mFrameworkFacade.makeNotificationBuilder(mContext,
+                SystemNotificationChannels.NETWORK_AVAILABLE)
+                .setSmallIcon(R.drawable.stat_notify_wifi_in_range)
+                .setTicker(title)
+                .setContentTitle(title)
+                .setContentText(content)
+                .setDeleteIntent(getPrivateBroadcast(ACTION_USER_DISMISSED_NOTIFICATION))
+                .setShowWhen(false)
+                .setLocalOnly(true)
+                .setColor(mResources.getColor(R.color.system_notification_accent_color,
+                        mContext.getTheme()));
+    }
+
+    private PendingIntent getPrivateBroadcast(String action) {
+        Intent intent = new Intent(action).setPackage("android");
+        return mFrameworkFacade.getBroadcast(
+                mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+    }
+}
diff --git a/service/java/com/android/server/wifi/OpenNetworkNotificationBuilder.java b/service/java/com/android/server/wifi/OpenNetworkNotificationBuilder.java
deleted file mode 100644
index 5963b57..0000000
--- a/service/java/com/android/server/wifi/OpenNetworkNotificationBuilder.java
+++ /dev/null
@@ -1,91 +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.server.wifi;
-
-import static com.android.server.wifi.OpenNetworkNotifier.ACTION_USER_DISMISSED_NOTIFICATION;
-import static com.android.server.wifi.OpenNetworkNotifier.ACTION_USER_TAPPED_CONTENT;
-
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-
-import com.android.internal.R;
-import com.android.internal.notification.SystemNotificationChannels;
-
-/**
- * Helper to create notifications for {@link OpenNetworkNotifier}.
- */
-public class OpenNetworkNotificationBuilder {
-
-    private Context mContext;
-    private Resources mResources;
-    private FrameworkFacade mFrameworkFacade;
-
-    public OpenNetworkNotificationBuilder(
-            Context context,
-            FrameworkFacade framework) {
-        mContext = context;
-        mResources = context.getResources();
-        mFrameworkFacade = framework;
-    }
-
-    /**
-     * Creates the open network available notification that alerts users there are open networks
-     * nearby.
-     */
-    public Notification createOpenNetworkAvailableNotification(int numNetworks) {
-
-        CharSequence title = mResources.getQuantityText(
-                com.android.internal.R.plurals.wifi_available, numNetworks);
-        CharSequence content = mResources.getQuantityText(
-                com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
-
-        PendingIntent contentIntent =
-                mFrameworkFacade.getBroadcast(
-                        mContext,
-                        0,
-                        new Intent(ACTION_USER_TAPPED_CONTENT),
-                        PendingIntent.FLAG_UPDATE_CURRENT);
-        return createNotificationBuilder(title, content)
-                .setContentIntent(contentIntent)
-                .build();
-    }
-
-    private Notification.Builder createNotificationBuilder(
-            CharSequence title, CharSequence content) {
-        PendingIntent deleteIntent =
-                mFrameworkFacade.getBroadcast(
-                        mContext,
-                        0,
-                        new Intent(ACTION_USER_DISMISSED_NOTIFICATION),
-                        PendingIntent.FLAG_UPDATE_CURRENT);
-        return mFrameworkFacade.makeNotificationBuilder(mContext,
-                SystemNotificationChannels.NETWORK_AVAILABLE)
-                .setSmallIcon(R.drawable.stat_notify_wifi_in_range)
-                .setAutoCancel(true)
-                .setTicker(title)
-                .setContentTitle(title)
-                .setContentText(content)
-                .setDeleteIntent(deleteIntent)
-                .setShowWhen(false)
-                .setLocalOnly(true)
-                .setColor(mResources.getColor(R.color.system_notification_accent_color,
-                        mContext.getTheme()));
-    }
-}
diff --git a/service/java/com/android/server/wifi/OpenNetworkNotifier.java b/service/java/com/android/server/wifi/OpenNetworkNotifier.java
index 2792237..31ff44b 100644
--- a/service/java/com/android/server/wifi/OpenNetworkNotifier.java
+++ b/service/java/com/android/server/wifi/OpenNetworkNotifier.java
@@ -16,7 +16,14 @@
 
 package com.android.server.wifi;
 
+import static com.android.server.wifi.ConnectToNetworkNotificationBuilder.ACTION_CONNECT_TO_NETWORK;
+import static com.android.server.wifi.ConnectToNetworkNotificationBuilder.ACTION_PICK_WIFI_NETWORK;
+import static com.android.server.wifi.ConnectToNetworkNotificationBuilder.ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE;
+import static com.android.server.wifi.ConnectToNetworkNotificationBuilder.ACTION_USER_DISMISSED_NOTIFICATION;
+
+import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.app.Notification;
 import android.app.NotificationManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -42,6 +49,8 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 import java.util.Set;
 
@@ -55,12 +64,39 @@
 
     private static final String TAG = "OpenNetworkNotifier";
 
-    static final String ACTION_USER_DISMISSED_NOTIFICATION =
-            "com.android.server.wifi.OpenNetworkNotifier.USER_DISMISSED_NOTIFICATION";
-    static final String ACTION_USER_TAPPED_CONTENT =
-            "com.android.server.wifi.OpenNetworkNotifier.USER_TAPPED_CONTENT";
-    static final String ACTION_CONNECT_TO_NETWORK =
-            "com.android.server.wifi.OpenNetworkNotifier.CONNECT_TO_NETWORK";
+    /** Time in milliseconds to display the Connecting notification. */
+    private static final int TIME_TO_SHOW_CONNECTING_MILLIS = 10000;
+
+    /** Time in milliseconds to display the Connected notification. */
+    private static final int TIME_TO_SHOW_CONNECTED_MILLIS = 5000;
+
+    /** Time in milliseconds to display the Failed To Connect notification. */
+    private static final int TIME_TO_SHOW_FAILED_MILLIS = 5000;
+
+    /** The state of the notification */
+    @IntDef({
+            STATE_NO_NOTIFICATION,
+            STATE_SHOWING_RECOMMENDATION_NOTIFICATION,
+            STATE_CONNECTING_IN_NOTIFICATION,
+            STATE_CONNECTED_NOTIFICATION,
+            STATE_CONNECT_FAILED_NOTIFICATION
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface State {}
+
+    /** No recommendation is made and no notifications are shown. */
+    private static final int STATE_NO_NOTIFICATION = 0;
+    /** The initial notification recommending an open network to connect to is shown. */
+    private static final int STATE_SHOWING_RECOMMENDATION_NOTIFICATION = 1;
+    /** The notification of status of connecting to the recommended network is shown. */
+    private static final int STATE_CONNECTING_IN_NOTIFICATION = 2;
+    /** The notification that the connection to the recommended network was successful is shown. */
+    private static final int STATE_CONNECTED_NOTIFICATION = 3;
+    /** The notification to show that connection to the recommended network failed is shown. */
+    private static final int STATE_CONNECT_FAILED_NOTIFICATION = 4;
+
+    /** Current state of the notification. */
+    @State private int mState = STATE_NO_NOTIFICATION;
 
     /** Identifier of the {@link SsidSetStoreData}. */
     private static final String STORE_DATA_IDENTIFIER = "OpenNetworkNotifierBlacklist";
@@ -79,8 +115,6 @@
 
     /** Whether the user has set the setting to show the 'available networks' notification. */
     private boolean mSettingEnabled;
-    /** Whether the notification is being shown. */
-    private boolean mNotificationShown;
     /** Whether the screen is on or not. */
     private boolean mScreenOn;
 
@@ -95,7 +129,7 @@
     private final WifiStateMachine mWifiStateMachine;
     private final Messenger mSrcMessenger;
     private final OpenNetworkRecommender mOpenNetworkRecommender;
-    private final OpenNetworkNotificationBuilder mOpenNetworkNotificationBuilder;
+    private final ConnectToNetworkNotificationBuilder mNotificationBuilder;
 
     private ScanResult mRecommendedNetwork;
 
@@ -107,7 +141,8 @@
             WifiConfigManager wifiConfigManager,
             WifiConfigStore wifiConfigStore,
             WifiStateMachine wifiStateMachine,
-            OpenNetworkRecommender openNetworkRecommender) {
+            OpenNetworkRecommender openNetworkRecommender,
+            ConnectToNetworkNotificationBuilder connectToNetworkNotificationBuilder) {
         mContext = context;
         mHandler = new Handler(looper);
         mFrameworkFacade = framework;
@@ -115,7 +150,7 @@
         mConfigManager = wifiConfigManager;
         mWifiStateMachine = wifiStateMachine;
         mOpenNetworkRecommender = openNetworkRecommender;
-        mOpenNetworkNotificationBuilder = new OpenNetworkNotificationBuilder(context, framework);
+        mNotificationBuilder = connectToNetworkNotificationBuilder;
         mScreenOn = false;
         mSrcMessenger = new Messenger(new Handler(looper, mConnectionStateCallback));
 
@@ -133,8 +168,9 @@
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(ACTION_USER_DISMISSED_NOTIFICATION);
-        filter.addAction(ACTION_USER_TAPPED_CONTENT);
         filter.addAction(ACTION_CONNECT_TO_NETWORK);
+        filter.addAction(ACTION_PICK_WIFI_NETWORK);
+        filter.addAction(ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE);
         mContext.registerReceiver(
                 mBroadcastReceiver, filter, null /* broadcastPermission */, mHandler);
     }
@@ -144,15 +180,18 @@
                 @Override
                 public void onReceive(Context context, Intent intent) {
                     switch (intent.getAction()) {
-                        case ACTION_USER_TAPPED_CONTENT:
-                            handleUserClickedContentAction();
-                            break;
                         case ACTION_USER_DISMISSED_NOTIFICATION:
                             handleUserDismissedAction();
                             break;
                         case ACTION_CONNECT_TO_NETWORK:
                             handleConnectToNetworkAction();
                             break;
+                        case ACTION_PICK_WIFI_NETWORK:
+                            handleSeeAllNetworksAction();
+                            break;
+                        case ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE:
+                            handlePickWifiNetworkAfterConnectFailure();
+                            break;
                         default:
                             Log.e(TAG, "Unknown action " + intent.getAction());
                     }
@@ -178,17 +217,17 @@
     /**
      * Clears the pending notification. This is called by {@link WifiConnectivityManager} on stop.
      *
-     * @param resetRepeatDelay resets the time delay for repeated notification if true.
+     * @param resetRepeatTime resets the time delay for repeated notification if true.
      */
-    public void clearPendingNotification(boolean resetRepeatDelay) {
-        if (resetRepeatDelay) {
+    public void clearPendingNotification(boolean resetRepeatTime) {
+        if (resetRepeatTime) {
             mNotificationRepeatTime = 0;
         }
 
-        if (mNotificationShown) {
+        if (mState != STATE_NO_NOTIFICATION) {
             getNotificationManager().cancel(SystemMessage.NOTE_NETWORK_AVAILABLE);
+            mState = STATE_NO_NOTIFICATION;
             mRecommendedNetwork = null;
-            mNotificationShown = false;
         }
     }
 
@@ -205,26 +244,37 @@
      */
     public void handleScanResults(@NonNull List<ScanDetail> availableNetworks) {
         if (!isControllerEnabled()) {
-            clearPendingNotification(true /* resetRepeatDelay */);
+            clearPendingNotification(true /* resetRepeatTime */);
             return;
         }
         if (availableNetworks.isEmpty()) {
-            clearPendingNotification(false /* resetRepeatDelay */);
+            clearPendingNotification(false /* resetRepeatTime */);
             return;
         }
 
-        // Do not show or update the notification if screen is off. We want to avoid a race that
-        // could occur between a user picking a network in settings and a network candidate picked
-        // through network selection, which will happen because screen on triggers a new
-        // connectivity scan.
-        if (mNotificationShown || !mScreenOn) {
+        // Not enough time has passed to show a recommendation notification again
+        if (mState == STATE_NO_NOTIFICATION
+                && mClock.getWallClockMillis() < mNotificationRepeatTime) {
             return;
         }
 
-        mRecommendedNetwork = mOpenNetworkRecommender.recommendNetwork(
-                availableNetworks, new ArraySet<>(mBlacklistedSsids));
+        // Do nothing when the screen is off and no notification is showing.
+        if (mState == STATE_NO_NOTIFICATION && !mScreenOn) {
+            return;
+        }
 
-        postNotification(availableNetworks.size());
+        // Only show a new or update an existing recommendation notification.
+        if (mState == STATE_NO_NOTIFICATION
+                || mState == STATE_SHOWING_RECOMMENDATION_NOTIFICATION) {
+            ScanResult recommendation = mOpenNetworkRecommender.recommendNetwork(
+                    availableNetworks, new ArraySet<>(mBlacklistedSsids));
+
+            if (recommendation != null) {
+                postInitialNotification(recommendation);
+            } else {
+                clearPendingNotification(false /* resetRepeatTime */);
+            }
+        }
     }
 
     /** Handles screen state changes. */
@@ -232,28 +282,70 @@
         mScreenOn = screenOn;
     }
 
+    /**
+     * Called by {@link WifiConnectivityManager} when Wi-Fi is connected. If the notification
+     * was in the connecting state, update the notification to show that it has connected to the
+     * recommended network.
+     */
+    public void handleWifiConnected() {
+        if (mState != STATE_CONNECTING_IN_NOTIFICATION) {
+            clearPendingNotification(true /* resetRepeatTime */);
+            return;
+        }
+
+        postNotification(mNotificationBuilder.createNetworkConnectedNotification(
+                mRecommendedNetwork));
+        mState = STATE_CONNECTED_NOTIFICATION;
+        mHandler.postDelayed(
+                () -> {
+                    if (mState == STATE_CONNECTED_NOTIFICATION) {
+                        clearPendingNotification(true /* resetRepeatTime */);
+                    }
+                },
+                TIME_TO_SHOW_CONNECTED_MILLIS);
+    }
+
+    /**
+     * Handles when a Wi-Fi connection attempt failed.
+     */
+    public void handleConnectionFailure() {
+        if (mState != STATE_CONNECTING_IN_NOTIFICATION) {
+            return;
+        }
+        postNotification(mNotificationBuilder.createNetworkFailedNotification());
+        mState = STATE_CONNECT_FAILED_NOTIFICATION;
+        mHandler.postDelayed(
+                () -> {
+                    if (mState == STATE_CONNECT_FAILED_NOTIFICATION) {
+                        clearPendingNotification(false /* resetRepeatTime */);
+                    }
+                },
+                TIME_TO_SHOW_FAILED_MILLIS);
+    }
+
     private NotificationManager getNotificationManager() {
         return (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
     }
 
-    private void postNotification(int numNetworks) {
-        // Not enough time has passed to show the notification again
-        if (mClock.getWallClockMillis() < mNotificationRepeatTime) {
-            return;
-        }
-
-        getNotificationManager().notify(
-                SystemMessage.NOTE_NETWORK_AVAILABLE,
-                mOpenNetworkNotificationBuilder.createOpenNetworkAvailableNotification(
-                        numNetworks));
-        mNotificationShown = true;
+    private void postInitialNotification(ScanResult recommendedNetwork) {
+        postNotification(mNotificationBuilder.createConnectToNetworkNotification(
+                recommendedNetwork));
+        mState = STATE_SHOWING_RECOMMENDATION_NOTIFICATION;
+        mRecommendedNetwork = recommendedNetwork;
         mNotificationRepeatTime = mClock.getWallClockMillis() + mNotificationRepeatDelay;
     }
 
+    private void postNotification(Notification notification) {
+        getNotificationManager().notify(SystemMessage.NOTE_NETWORK_AVAILABLE, notification);
+    }
+
     private void handleConnectToNetworkAction() {
-        if (mRecommendedNetwork == null) {
+        if (mState != STATE_SHOWING_RECOMMENDATION_NOTIFICATION) {
             return;
         }
+        postNotification(mNotificationBuilder.createNetworkConnectingNotification(
+                mRecommendedNetwork));
+
         Log.d(TAG, "User initiated connection to recommended network: " + mRecommendedNetwork.SSID);
         WifiConfiguration network = ScanResultUtil.createNetworkFromScanResult(mRecommendedNetwork);
         Message msg = Message.obtain();
@@ -262,32 +354,49 @@
         msg.obj = network;
         msg.replyTo = mSrcMessenger;
         mWifiStateMachine.sendMessage(msg);
+
+        mState = STATE_CONNECTING_IN_NOTIFICATION;
+        mHandler.postDelayed(
+                () -> {
+                    if (mState == STATE_CONNECTING_IN_NOTIFICATION) {
+                        handleConnectionFailure();
+                    }
+                },
+                TIME_TO_SHOW_CONNECTING_MILLIS);
     }
 
-    /**
-     * Handles when a Wi-Fi connection attempt failed.
-     */
-    public void handleConnectionFailure() {
-        // Stub. Should post connection failure notification once implemented.
+    private void handleSeeAllNetworksAction() {
+        startWifiSettings();
     }
 
-    /** Opens Wi-Fi picker. */
-    private void handleUserClickedContentAction() {
-        mNotificationShown = false;
+    private void startWifiSettings() {
+        // Close notification drawer before opening the picker.
+        mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
         mContext.startActivity(
                 new Intent(Settings.ACTION_WIFI_SETTINGS)
                         .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+        clearPendingNotification(false /* resetRepeatTime */);
+    }
+
+    private void handlePickWifiNetworkAfterConnectFailure() {
+        startWifiSettings();
     }
 
     private void handleUserDismissedAction() {
-        if (mRecommendedNetwork != null) {
+        if (mState == STATE_SHOWING_RECOMMENDATION_NOTIFICATION) {
             // blacklist dismissed network
             mBlacklistedSsids.add(mRecommendedNetwork.SSID);
             mConfigManager.saveToStore(false /* forceWrite */);
             Log.d(TAG, "Network is added to the open network notification blacklist: "
                     + mRecommendedNetwork.SSID);
         }
-        mNotificationShown = false;
+        resetStateAndDelayNotification();
+    }
+
+    private void resetStateAndDelayNotification() {
+        mState = STATE_NO_NOTIFICATION;
+        mNotificationRepeatTime = System.currentTimeMillis() + mNotificationRepeatDelay;
+        mRecommendedNetwork = null;
     }
 
     /** Dump ONA controller state. */
@@ -296,7 +405,7 @@
         pw.println("mSettingEnabled " + mSettingEnabled);
         pw.println("currentTime: " + mClock.getWallClockMillis());
         pw.println("mNotificationRepeatTime: " + mNotificationRepeatTime);
-        pw.println("mNotificationShown: " + mNotificationShown);
+        pw.println("mState: " + mState);
         pw.println("mBlacklistedSsids: " + mBlacklistedSsids.toString());
     }
 
@@ -327,7 +436,7 @@
         public void onChange(boolean selfChange) {
             super.onChange(selfChange);
             mSettingEnabled = getValue();
-            clearPendingNotification(true /* resetRepeatDelay */);
+            clearPendingNotification(true /* resetRepeatTime */);
         }
 
         private boolean getValue() {
diff --git a/service/java/com/android/server/wifi/WifiConnectivityManager.java b/service/java/com/android/server/wifi/WifiConnectivityManager.java
index 1c5ac28..7e730c8 100644
--- a/service/java/com/android/server/wifi/WifiConnectivityManager.java
+++ b/service/java/com/android/server/wifi/WifiConnectivityManager.java
@@ -1073,7 +1073,7 @@
         mWifiState = state;
 
         if (mWifiState == WIFI_STATE_CONNECTED) {
-            mOpenNetworkNotifier.clearPendingNotification(false /* resetRepeatDelay */);
+            mOpenNetworkNotifier.handleWifiConnected();
         }
 
         // Reset BSSID of last connection attempt and kick off
diff --git a/service/java/com/android/server/wifi/WifiCountryCode.java b/service/java/com/android/server/wifi/WifiCountryCode.java
index e69fb8e..66a035f 100644
--- a/service/java/com/android/server/wifi/WifiCountryCode.java
+++ b/service/java/com/android/server/wifi/WifiCountryCode.java
@@ -83,11 +83,9 @@
     public synchronized void simCardRemoved() {
         if (DBG) Log.d(TAG, "SIM Card Removed");
         // SIM card is removed, we need to reset the country code to phone default.
-        if (mRevertCountryCodeOnCellularLoss) {
-            mTelephonyCountryCode = null;
-            if (mReady) {
-                updateCountryCode();
-            }
+        mTelephonyCountryCode = null;
+        if (mReady) {
+            updateCountryCode();
         }
     }
 
@@ -98,12 +96,9 @@
      */
     public synchronized void airplaneModeEnabled() {
         if (DBG) Log.d(TAG, "Airplane Mode Enabled");
-        mTelephonyCountryCode = null;
         // Airplane mode is enabled, we need to reset the country code to phone default.
-        if (mRevertCountryCodeOnCellularLoss) {
-            mTelephonyCountryCode = null;
-            // Country code will be set upon when wpa_supplicant starts next time.
-        }
+        // Country code will be set upon when wpa_supplicant starts next time.
+        mTelephonyCountryCode = null;
     }
 
     /**
@@ -133,8 +128,10 @@
         if (DBG) Log.d(TAG, "Receive set country code request: " + countryCode);
         // Empty country code.
         if (TextUtils.isEmpty(countryCode)) {
-            if (DBG) Log.d(TAG, "Received empty country code, reset to default country code");
-            mTelephonyCountryCode = null;
+            if (mRevertCountryCodeOnCellularLoss) {
+                if (DBG) Log.d(TAG, "Received empty country code, reset to default country code");
+                mTelephonyCountryCode = null;
+            }
         } else {
             mTelephonyCountryCode = countryCode.toUpperCase();
         }
diff --git a/service/java/com/android/server/wifi/WifiDiagnostics.java b/service/java/com/android/server/wifi/WifiDiagnostics.java
index 42078ef..921faa3 100644
--- a/service/java/com/android/server/wifi/WifiDiagnostics.java
+++ b/service/java/com/android/server/wifi/WifiDiagnostics.java
@@ -77,6 +77,8 @@
     public static final int REPORT_REASON_UNEXPECTED_DISCONNECT     = 5;
     public static final int REPORT_REASON_SCAN_FAILURE              = 6;
     public static final int REPORT_REASON_USER_ACTION               = 7;
+    public static final int REPORT_REASON_WIFICOND_CRASH            = 8;
+    public static final int REPORT_REASON_HAL_CRASH                 = 9;
 
     /** number of bug reports to hold */
     public static final int MAX_BUG_REPORTS                         = 4;
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index 7778c2c..fc3af83 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -227,7 +227,8 @@
         mOpenNetworkNotifier = new OpenNetworkNotifier(mContext,
                 mWifiStateMachineHandlerThread.getLooper(), mFrameworkFacade, mClock,
                 mWifiConfigManager, mWifiConfigStore, mWifiStateMachine,
-                new OpenNetworkRecommender());
+                new OpenNetworkRecommender(),
+                new ConnectToNetworkNotificationBuilder(mContext, mFrameworkFacade));
         mLockManager = new WifiLockManager(mContext, BatteryStatsService.getService());
         mWifiController = new WifiController(mContext, mWifiStateMachine, mSettingsStore,
                 mLockManager, mWifiServiceHandlerThread.getLooper(), mFrameworkFacade);
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index 87ceee8..e96c15d 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -4118,11 +4118,14 @@
                 case CMD_CLIENT_INTERFACE_BINDER_DEATH:
                     Log.e(TAG, "wificond died unexpectedly. Triggering recovery");
                     mWifiMetrics.incrementNumWificondCrashes();
+                    mWifiDiagnostics.captureBugReportData(
+                            WifiDiagnostics.REPORT_REASON_WIFICOND_CRASH);
                     mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_WIFICOND_CRASH);
                     break;
                 case CMD_VENDOR_HAL_HWBINDER_DEATH:
                     Log.e(TAG, "Vendor HAL died unexpectedly. Triggering recovery");
                     mWifiMetrics.incrementNumHalCrashes();
+                    mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_HAL_CRASH);
                     mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_HAL_CRASH);
                     break;
                 case CMD_DIAGS_CONNECT_TIMEOUT:
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
index 16888c8..efeca65 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
@@ -60,10 +60,11 @@
 import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
 
 /**
  * Manages Aware data-path lifetime: interface creation/deletion, data-path setup and tear-down.
@@ -323,6 +324,8 @@
              * - The discovery session (pub/sub ID) must match.
              * - The peer MAC address (if specified - i.e. non-null) must match. A null peer MAC ==
              *   accept (otherwise matching) requests from any peer MAC.
+             * - The request must be pending (i.e. we could have completed requests for the same
+             *   parameters)
              */
             if (entry.getValue().pubSubId != 0 && entry.getValue().pubSubId != pubSubId) {
                 continue;
@@ -333,6 +336,11 @@
                 continue;
             }
 
+            if (entry.getValue().state
+                    != AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_REQUEST) {
+                continue;
+            }
+
             networkSpecifier = entry.getKey();
             nnri = entry.getValue();
             break;
@@ -884,18 +892,43 @@
     }
 
     /**
-     * Select one of the existing interfaces for the new network request.
+     * Select one of the existing interfaces for the new network request. A request is canonical
+     * (otherwise it wouldn't be executed).
      *
-     * TODO: for now there is only a single interface - simply pick it.
+     * Construct a list of all interfaces currently used to communicate to the peer. The remaining
+     * interfaces are available for use for this request - if none are left then the request should
+     * fail (signaled to the caller by returning a null).
      */
     private String selectInterfaceForRequest(AwareNetworkRequestInformation req) {
-        Iterator<String> it = mInterfaces.iterator();
-        if (it.hasNext()) {
-            return it.next();
+        SortedSet<String> potential = new TreeSet<>(mInterfaces);
+        Set<String> used = new HashSet<>();
+
+        if (VDBG) {
+            Log.v(TAG, "selectInterfaceForRequest: req=" + req + ", mNetworkRequestsCache="
+                    + mNetworkRequestsCache);
         }
 
-        Log.e(TAG, "selectInterfaceForRequest: req=" + req + " - but no interfaces available!");
+        for (AwareNetworkRequestInformation nnri : mNetworkRequestsCache.values()) {
+            if (nnri == req) {
+                continue;
+            }
 
+            if (Arrays.equals(req.peerDiscoveryMac, nnri.peerDiscoveryMac)) {
+                used.add(nnri.interfaceName);
+            }
+        }
+
+        if (VDBG) {
+            Log.v(TAG, "selectInterfaceForRequest: potential=" + potential + ", used=" + used);
+        }
+
+        for (String ifName: potential) {
+            if (!used.contains(ifName)) {
+                return ifName;
+            }
+        }
+
+        Log.e(TAG, "selectInterfaceForRequest: req=" + req + " - no interfaces available!");
         return null;
     }
 
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java b/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java
index 2f424db..2121160 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java
@@ -167,10 +167,6 @@
                     capabilities.maxSubscribeInterfaceAddresses;
             frameworkCapabilities.supportedCipherSuites = capabilities.supportedCipherSuites;
 
-            // TODO (b/63635857): enable framework support of >1 NDI
-            // Until then: force corresponding capability to 1.
-            frameworkCapabilities.maxNdiInterfaces = 1;
-
             mWifiAwareStateManager.onCapabilitiesUpdateResponse(id, frameworkCapabilities);
         } else {
             Log.e(TAG, "notifyCapabilitiesResponse: error code=" + status.status + " ("
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareShellCommand.java b/service/java/com/android/server/wifi/aware/WifiAwareShellCommand.java
index 41eef69..a2f6046 100644
--- a/service/java/com/android/server/wifi/aware/WifiAwareShellCommand.java
+++ b/service/java/com/android/server/wifi/aware/WifiAwareShellCommand.java
@@ -55,6 +55,7 @@
                 for (DelegatedShellCommand dsc: mDelegatedCommands.values()) {
                     dsc.onReset();
                 }
+                return 0;
             } else {
                 DelegatedShellCommand delegatedCmd = null;
                 if (!TextUtils.isEmpty(cmd)) {
diff --git a/tests/wifitests/src/com/android/server/wifi/OpenNetworkNotifierTest.java b/tests/wifitests/src/com/android/server/wifi/OpenNetworkNotifierTest.java
index 7c1223a..46ec159 100644
--- a/tests/wifitests/src/com/android/server/wifi/OpenNetworkNotifierTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/OpenNetworkNotifierTest.java
@@ -18,22 +18,23 @@
 
 import static com.android.server.wifi.OpenNetworkNotifier.DEFAULT_REPEAT_DELAY_SEC;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.Notification;
 import android.app.NotificationManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
 import android.net.wifi.ScanResult;
+import android.net.wifi.WifiManager;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.test.TestLooper;
@@ -42,7 +43,6 @@
 
 import org.junit.Before;
 import org.junit.Test;
-import org.mockito.Answers;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -65,12 +65,13 @@
     @Mock private Clock mClock;
     @Mock private WifiConfigStore mWifiConfigStore;
     @Mock private WifiConfigManager mWifiConfigManager;
-    @Mock(answer = Answers.RETURNS_DEEP_STUBS) private Notification.Builder mNotificationBuilder;
     @Mock private NotificationManager mNotificationManager;
     @Mock private WifiStateMachine mWifiStateMachine;
     @Mock private OpenNetworkRecommender mOpenNetworkRecommender;
+    @Mock private ConnectToNetworkNotificationBuilder mNotificationBuilder;
     @Mock private UserManager mUserManager;
     private OpenNetworkNotifier mNotificationController;
+    private TestLooper mLooper;
     private BroadcastReceiver mBroadcastReceiver;
     private ScanResult mDummyNetwork;
     private List<ScanDetail> mOpenNetworks;
@@ -88,8 +89,6 @@
         when(mFrameworkFacade.getIntegerSetting(mContext,
                 Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, DEFAULT_REPEAT_DELAY_SEC))
                 .thenReturn(DEFAULT_REPEAT_DELAY_SEC);
-        when(mFrameworkFacade.makeNotificationBuilder(any(), anyString()))
-                .thenReturn(mNotificationBuilder);
         when(mContext.getSystemService(Context.USER_SERVICE))
                 .thenReturn(mUserManager);
         when(mContext.getResources()).thenReturn(mResources);
@@ -102,10 +101,10 @@
         mOpenNetworks.add(new ScanDetail(mDummyNetwork, null /* networkDetail */));
         mBlacklistedSsids = new ArraySet<>();
 
-        TestLooper mock_looper = new TestLooper();
+        mLooper = new TestLooper();
         mNotificationController = new OpenNetworkNotifier(
-                mContext, mock_looper.getLooper(), mFrameworkFacade, mClock, mWifiConfigManager,
-                mWifiConfigStore, mWifiStateMachine, mOpenNetworkRecommender);
+                mContext, mLooper.getLooper(), mFrameworkFacade, mClock, mWifiConfigManager,
+                mWifiConfigStore, mWifiStateMachine, mOpenNetworkRecommender, mNotificationBuilder);
         ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
                 ArgumentCaptor.forClass(BroadcastReceiver.class);
         verify(mContext).registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
@@ -121,6 +120,7 @@
         mNotificationController.handleScanResults(mOpenNetworks);
 
         verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids);
+        verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork);
         verify(mNotificationManager).notify(anyInt(), any());
     }
 
@@ -144,12 +144,32 @@
         mNotificationController.handleScanResults(mOpenNetworks);
 
         verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids);
+        verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork);
         verify(mNotificationManager).notify(anyInt(), any());
 
         mNotificationController.handleScanResults(new ArrayList<>());
 
         verify(mNotificationManager).cancel(anyInt());
     }
+
+    /**
+     * When a notification is showing and no recommendation is made for the new scan results, the
+     * notification is cleared.
+     */
+    @Test
+    public void handleScanResults_notificationShown_noRecommendation_notificationCleared() {
+        mNotificationController.handleScanResults(mOpenNetworks);
+
+        verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids);
+        verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork);
+        verify(mNotificationManager).notify(anyInt(), any());
+
+        when(mOpenNetworkRecommender.recommendNetwork(any(), any())).thenReturn(null);
+        mNotificationController.handleScanResults(mOpenNetworks);
+
+        verify(mNotificationManager).cancel(anyInt());
+    }
+
     /**
      * When a notification is showing, screen is off, and scan results with no open networks are
      * handled, the notification is cleared.
@@ -159,6 +179,7 @@
         mNotificationController.handleScanResults(mOpenNetworks);
 
         verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids);
+        verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork);
         verify(mNotificationManager).notify(anyInt(), any());
 
         mNotificationController.handleScreenStateChanged(false);
@@ -168,18 +189,6 @@
     }
 
     /**
-     * If notification is showing, do not post another notification.
-     */
-    @Test
-    public void handleScanResults_notificationShowing_doesNotRepostNotification() {
-        mNotificationController.handleScanResults(mOpenNetworks);
-        mNotificationController.handleScanResults(mOpenNetworks);
-
-        verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids);
-        verify(mNotificationManager).notify(anyInt(), any());
-    }
-
-    /**
      * When {@link OpenNetworkNotifier#clearPendingNotification(boolean)} is called and a
      * notification is shown, clear the notification.
      */
@@ -188,6 +197,7 @@
         mNotificationController.handleScanResults(mOpenNetworks);
 
         verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids);
+        verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork);
         verify(mNotificationManager).notify(anyInt(), any());
 
         mNotificationController.clearPendingNotification(true);
@@ -211,7 +221,7 @@
      * new scan results with open networks.
      */
     @Test
-    public void screenOff_handleScanResults_notificationNotDisplayed() {
+    public void screenOff_notificationNotShowing_handleScanResults_notificationNotDisplayed() {
         mNotificationController.handleScreenStateChanged(false);
         mNotificationController.handleScanResults(mOpenNetworks);
 
@@ -220,7 +230,29 @@
     }
 
     /**
-     * When a notification is posted and cleared without reseting delay, the next scan with open
+     * When screen is off and notification is displayed, the notification can be updated with a new
+     * recommendation.
+     */
+    @Test
+    public void screenOff_notificationShowing_handleScanResults_recommendationCanBeUpdated() {
+        mNotificationController.handleScanResults(mOpenNetworks);
+
+        verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids);
+        verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork);
+        verify(mNotificationManager).notify(anyInt(), any());
+
+        mNotificationController.handleScreenStateChanged(false);
+        mNotificationController.handleScanResults(mOpenNetworks);
+
+        // Recommendation made twice
+        verify(mOpenNetworkRecommender, times(2)).recommendNetwork(
+                mOpenNetworks, mBlacklistedSsids);
+        verify(mNotificationBuilder, times(2)).createConnectToNetworkNotification(mDummyNetwork);
+        verify(mNotificationManager, times(2)).notify(anyInt(), any());
+    }
+
+    /**
+     * When a notification is posted and cleared without resetting delay, the next scan with open
      * networks should not post another notification.
      */
     @Test
@@ -228,21 +260,21 @@
         mNotificationController.handleScanResults(mOpenNetworks);
 
         verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids);
+        verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork);
         verify(mNotificationManager).notify(anyInt(), any());
 
         mNotificationController.clearPendingNotification(false);
 
+        verify(mNotificationManager).cancel(anyInt());
+
         mNotificationController.handleScanResults(mOpenNetworks);
 
-        // Recommendation made twice but no new notification posted.
-        verify(mOpenNetworkRecommender, times(2)).recommendNetwork(
-                mOpenNetworks, mBlacklistedSsids);
+        // no new notification posted
         verify(mNotificationManager).notify(anyInt(), any());
-        verify(mNotificationManager).cancel(anyInt());
     }
 
     /**
-     * When a notification is posted and cleared without reseting delay, the next scan with open
+     * When a notification is posted and cleared without resetting delay, the next scan with open
      * networks should post a notification.
      */
     @Test
@@ -250,6 +282,7 @@
         mNotificationController.handleScanResults(mOpenNetworks);
 
         verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids);
+        verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork);
         verify(mNotificationManager).notify(anyInt(), any());
 
         mNotificationController.clearPendingNotification(true);
@@ -258,26 +291,11 @@
 
         verify(mOpenNetworkRecommender, times(2)).recommendNetwork(
                 mOpenNetworks, mBlacklistedSsids);
+        verify(mNotificationBuilder, times(2)).createConnectToNetworkNotification(mDummyNetwork);
         verify(mNotificationManager, times(2)).notify(anyInt(), any());
     }
 
     /**
-     * When a notification is tapped, open Wi-Fi settings.
-     */
-    @Test
-    public void notificationTap_opensWifiSettings() {
-        mNotificationController.handleScanResults(mOpenNetworks);
-
-        verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids);
-        verify(mNotificationManager).notify(anyInt(), any());
-
-        mBroadcastReceiver.onReceive(
-                mContext, new Intent(OpenNetworkNotifier.ACTION_USER_TAPPED_CONTENT));
-
-        verify(mContext).startActivity(any());
-    }
-
-    /**
      * When user dismissed notification and there is a recommended network, network ssid should be
      * blacklisted.
      */
@@ -286,14 +304,18 @@
         mNotificationController.handleScanResults(mOpenNetworks);
 
         verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids);
+        verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork);
         verify(mNotificationManager).notify(anyInt(), any());
 
         mBroadcastReceiver.onReceive(
-                mContext, new Intent(OpenNetworkNotifier.ACTION_USER_DISMISSED_NOTIFICATION));
+                mContext,
+                new Intent(ConnectToNetworkNotificationBuilder.ACTION_USER_DISMISSED_NOTIFICATION));
 
         verify(mWifiConfigManager).saveToStore(false /* forceWrite */);
 
-        mNotificationController.handleScanResults(mOpenNetworks);
+        mNotificationController.clearPendingNotification(true);
+        List<ScanDetail> scanResults = mOpenNetworks;
+        mNotificationController.handleScanResults(scanResults);
 
         Set<String> expectedBlacklist = new ArraySet<>();
         expectedBlacklist.add(mDummyNetwork.SSID);
@@ -301,7 +323,7 @@
     }
 
     /**
-     * When a notification is posted and cleared without reseting delay, after the delay has passed
+     * When a notification is posted and cleared without resetting delay, after the delay has passed
      * the next scan with open networks should post a notification.
      */
     @Test
@@ -309,6 +331,7 @@
         mNotificationController.handleScanResults(mOpenNetworks);
 
         verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids);
+        verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork);
         verify(mNotificationManager).notify(anyInt(), any());
 
         mNotificationController.clearPendingNotification(false);
@@ -320,6 +343,7 @@
 
         verify(mOpenNetworkRecommender, times(2)).recommendNetwork(
                 mOpenNetworks, mBlacklistedSsids);
+        verify(mNotificationBuilder, times(2)).createConnectToNetworkNotification(mDummyNetwork);
         verify(mNotificationManager, times(2)).notify(anyInt(), any());
     }
 
@@ -341,6 +365,7 @@
         mNotificationController.handleScanResults(mOpenNetworks);
 
         verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids);
+        verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork);
         verify(mNotificationManager).notify(anyInt(), any());
 
         when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT))
@@ -352,30 +377,172 @@
     }
 
     /**
-     * {@link OpenNetworkNotifier#ACTION_CONNECT_TO_NETWORK} does not connect to any network if
-     * there is no current recommendation.
+     * {@link ConnectToNetworkNotificationBuilder#ACTION_CONNECT_TO_NETWORK} does not connect to
+     * any network if the initial notification is not showing.
      */
     @Test
-    public void actionConnectToNetwork_currentRecommendationIsNull_doesNothing() {
+    public void actionConnectToNetwork_notificationNotShowing_doesNothing() {
         mBroadcastReceiver.onReceive(mContext,
-                new Intent(OpenNetworkNotifier.ACTION_CONNECT_TO_NETWORK));
+                new Intent(ConnectToNetworkNotificationBuilder.ACTION_CONNECT_TO_NETWORK));
 
         verify(mWifiStateMachine, never()).sendMessage(any(Message.class));
     }
 
     /**
-     * {@link OpenNetworkNotifier#ACTION_CONNECT_TO_NETWORK} connects to the currently recommended
-     * network if it exists.
+     * {@link ConnectToNetworkNotificationBuilder#ACTION_CONNECT_TO_NETWORK} connects to the
+     * currently recommended network if it exists.
      */
     @Test
-    public void actionConnectToNetwork_currentRecommendationExists_connectsToNetwork() {
+    public void actionConnectToNetwork_currentRecommendationExists_connectsAndPostsNotification() {
         mNotificationController.handleScanResults(mOpenNetworks);
 
         verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids);
+        // Initial Notification
+        verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork);
+        verify(mNotificationManager).notify(anyInt(), any());
 
         mBroadcastReceiver.onReceive(mContext,
-                new Intent(OpenNetworkNotifier.ACTION_CONNECT_TO_NETWORK));
+                new Intent(ConnectToNetworkNotificationBuilder.ACTION_CONNECT_TO_NETWORK));
 
         verify(mWifiStateMachine).sendMessage(any(Message.class));
+        // Connecting Notification
+        verify(mNotificationBuilder).createNetworkConnectingNotification(mDummyNetwork);
+        verify(mNotificationManager, times(2)).notify(anyInt(), any());
+    }
+
+    /**
+     * {@link OpenNetworkNotifier#handleWifiConnected()} does not post connected notification if
+     * the connecting notification is not showing
+     */
+    @Test
+    public void networkConnectionSuccess_wasNotInConnectingFlow_doesNothing() {
+        mNotificationController.handleWifiConnected();
+
+        verify(mNotificationManager, never()).notify(anyInt(), any());
+    }
+
+    /**
+     * {@link OpenNetworkNotifier#handleWifiConnected()} clears notification that is not connecting.
+     */
+    @Test
+    public void networkConnectionSuccess_wasShowingNotification_clearsNotification() {
+        mNotificationController.handleScanResults(mOpenNetworks);
+
+        verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids);
+        // Initial Notification
+        verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork);
+        verify(mNotificationManager).notify(anyInt(), any());
+
+        mNotificationController.handleWifiConnected();
+
+        verify(mNotificationManager).cancel(anyInt());
+    }
+
+    /**
+     * {@link OpenNetworkNotifier#handleWifiConnected()} posts the connected notification if
+     * the connecting notification is showing.
+     */
+    @Test
+    public void networkConnectionSuccess_wasInConnectingFlow_postsConnectedNotification() {
+        mNotificationController.handleScanResults(mOpenNetworks);
+
+        verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids);
+        // Initial Notification
+        verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork);
+        verify(mNotificationManager).notify(anyInt(), any());
+
+        mBroadcastReceiver.onReceive(mContext,
+                new Intent(ConnectToNetworkNotificationBuilder.ACTION_CONNECT_TO_NETWORK));
+
+        // Connecting Notification
+        verify(mNotificationBuilder).createNetworkConnectingNotification(mDummyNetwork);
+        verify(mNotificationManager, times(2)).notify(anyInt(), any());
+
+        mNotificationController.handleWifiConnected();
+
+        // Connected Notification
+        verify(mNotificationBuilder).createNetworkConnectedNotification(mDummyNetwork);
+        verify(mNotificationManager, times(3)).notify(anyInt(), any());
+    }
+
+    /**
+     * {@link OpenNetworkNotifier#handleConnectionFailure()} posts the Failed to Connect
+     * notification if the connecting notification is showing.
+     */
+    @Test
+    public void networkConnectionFailure_wasNotInConnectingFlow_doesNothing() {
+        mNotificationController.handleConnectionFailure();
+
+        verify(mNotificationManager, never()).notify(anyInt(), any());
+    }
+
+    /**
+     * {@link OpenNetworkNotifier#handleConnectionFailure()} posts the Failed to Connect
+     * notification if the connecting notification is showing.
+     */
+    @Test
+    public void networkConnectionFailure_wasInConnectingFlow_postsFailedToConnectNotification() {
+        mNotificationController.handleScanResults(mOpenNetworks);
+
+        verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids);
+        // Initial Notification
+        verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork);
+        verify(mNotificationManager).notify(anyInt(), any());
+
+        mBroadcastReceiver.onReceive(mContext,
+                new Intent(ConnectToNetworkNotificationBuilder.ACTION_CONNECT_TO_NETWORK));
+
+        // Connecting Notification
+        verify(mNotificationBuilder).createNetworkConnectingNotification(mDummyNetwork);
+        verify(mNotificationManager, times(2)).notify(anyInt(), any());
+
+        mNotificationController.handleConnectionFailure();
+
+        // Failed to Connect Notification
+        verify(mNotificationBuilder).createNetworkFailedNotification();
+        verify(mNotificationManager, times(3)).notify(anyInt(), any());
+    }
+
+    /**
+     * When a {@link WifiManager#CONNECT_NETWORK_FAILED} is received from the connection callback
+     * of {@link WifiStateMachine#sendMessage(Message)}, a Failed to Connect notification should
+     * be posted. On tapping this notification, Wi-Fi Settings should be launched.
+     */
+    @Test
+    public void connectionFailedCallback_postsFailedToConnectNotification() throws RemoteException {
+        mNotificationController.handleScanResults(mOpenNetworks);
+
+        verify(mOpenNetworkRecommender).recommendNetwork(mOpenNetworks, mBlacklistedSsids);
+        // Initial Notification
+        verify(mNotificationBuilder).createConnectToNetworkNotification(mDummyNetwork);
+        verify(mNotificationManager).notify(anyInt(), any());
+
+        mBroadcastReceiver.onReceive(mContext,
+                new Intent(ConnectToNetworkNotificationBuilder.ACTION_CONNECT_TO_NETWORK));
+
+        ArgumentCaptor<Message> connectMessageCaptor = ArgumentCaptor.forClass(Message.class);
+        verify(mWifiStateMachine).sendMessage(connectMessageCaptor.capture());
+        Message connectMessage = connectMessageCaptor.getValue();
+
+        // Connecting Notification
+        verify(mNotificationBuilder).createNetworkConnectingNotification(mDummyNetwork);
+        verify(mNotificationManager, times(2)).notify(anyInt(), any());
+
+        Message connectFailedMsg = Message.obtain();
+        connectFailedMsg.what = WifiManager.CONNECT_NETWORK_FAILED;
+        connectMessage.replyTo.send(connectFailedMsg);
+        mLooper.dispatchAll();
+
+        // Failed to Connect Notification
+        verify(mNotificationBuilder).createNetworkFailedNotification();
+        verify(mNotificationManager, times(3)).notify(anyInt(), any());
+
+        mBroadcastReceiver.onReceive(mContext,
+                new Intent(ConnectToNetworkNotificationBuilder
+                        .ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE));
+
+        ArgumentCaptor<Intent> pickerIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext).startActivity(pickerIntentCaptor.capture());
+        assertEquals(pickerIntentCaptor.getValue().getAction(), Settings.ACTION_WIFI_SETTINGS);
     }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
index cb1160c..6420fac 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
@@ -637,18 +637,17 @@
     }
 
     /**
-     * When wifi is connected, {@link OpenNetworkNotifier} tries to clear the pending
-     * notification and does not reset notification repeat delay.
+     * When wifi is connected, {@link OpenNetworkNotifier} handles the Wi-Fi connected behavior.
      *
-     * Expected behavior: ONA clears pending notification and does not reset repeat delay.
+     * Expected behavior: ONA handles connected behavior
      */
     @Test
-    public void wifiConnected_openNetworkNotifierClearsPendingNotification() {
+    public void wifiConnected_openNetworkNotifierHandlesConnection() {
         // Set WiFi to connected state
         mWifiConnectivityManager.handleConnectionStateChanged(
                 WifiConnectivityManager.WIFI_STATE_CONNECTED);
 
-        verify(mOpenNetworkNotifier).clearPendingNotification(false /* resetRepeatDelay*/);
+        verify(mOpenNetworkNotifier).handleWifiConnected();
     }
 
     /**
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java b/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java
index 33aab60..fb4e71e 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java
@@ -169,7 +169,8 @@
     }
 
     /**
-     * Test if we can reset to the default country code when phone is out of service.
+     * Test if we can reset to the default country code when phone is out of service, when
+     * |config_wifi_revert_country_code_on_cellular_loss| is set to true;
      * Telephony service calls |setCountryCode| with an empty string when phone is out of service.
      * In this case we should fall back to the default country code.
      * @throws Exception
@@ -184,4 +185,28 @@
         assertEquals(mDefaultCountryCode, mWifiCountryCode.getCountryCode());
     }
 
+    /**
+     * Test if we can keep using the last known country code when phone is out of service, when
+     * |config_wifi_revert_country_code_on_cellular_loss| is set to false;
+     * Telephony service calls |setCountryCode| with an empty string when phone is out of service.
+     * In this case we should keep using the last known country code.
+     * @throws Exception
+     */
+    @Test
+    public void doNotResetCountryCodeWhenOutOfService() throws Exception {
+        // Refresh mWifiCountryCode with |config_wifi_revert_country_code_on_cellular_loss|
+        // setting to false.
+        mWifiCountryCode = new WifiCountryCode(
+                mWifiNative,
+                mDefaultCountryCode,
+                false /* config_wifi_revert_country_code_on_cellular_loss */);
+
+        assertEquals(mDefaultCountryCode, mWifiCountryCode.getCountryCode());
+        mWifiCountryCode.setCountryCode(mTelephonyCountryCode);
+        assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCountryCode());
+        // Out of service.
+        mWifiCountryCode.setCountryCode("");
+        assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCountryCode());
+    }
+
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
index 3a8385b..1772616 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
@@ -359,6 +359,7 @@
     @Mock WrongPasswordNotifier mWrongPasswordNotifier;
     @Mock Clock mClock;
     @Mock ScanDetailCache mScanDetailCache;
+    @Mock BaseWifiDiagnostics mWifiDiagnostics;
 
     public WifiStateMachineTest() throws Exception {
     }
@@ -383,8 +384,7 @@
         when(mWifiInjector.getBuildProperties()).thenReturn(mBuildProperties);
         when(mWifiInjector.getKeyStore()).thenReturn(mock(KeyStore.class));
         when(mWifiInjector.getWifiBackupRestore()).thenReturn(mock(WifiBackupRestore.class));
-        when(mWifiInjector.makeWifiDiagnostics(anyObject())).thenReturn(
-                mock(BaseWifiDiagnostics.class));
+        when(mWifiInjector.makeWifiDiagnostics(anyObject())).thenReturn(mWifiDiagnostics);
         when(mWifiInjector.makeWificond()).thenReturn(mWificond);
         when(mWifiInjector.getWifiConfigManager()).thenReturn(mWifiConfigManager);
         when(mWifiInjector.getWifiScanner()).thenReturn(mWifiScanner);
@@ -1814,6 +1814,7 @@
 
         verify(mWifiMetrics).incrementNumHalCrashes();
         verify(mSelfRecovery).trigger(eq(SelfRecovery.REASON_HAL_CRASH));
+        verify(mWifiDiagnostics).captureBugReportData(WifiDiagnostics.REPORT_REASON_HAL_CRASH);
     }
 
     @Test
@@ -1836,6 +1837,7 @@
 
         verify(mWifiMetrics).incrementNumWificondCrashes();
         verify(mSelfRecovery).trigger(eq(SelfRecovery.REASON_WIFICOND_CRASH));
+        verify(mWifiDiagnostics).captureBugReportData(WifiDiagnostics.REPORT_REASON_WIFICOND_CRASH);
     }
 
     private void setupMocksForWpsNetworkMigration() {
diff --git a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java
index 227a196..61143d9 100644
--- a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java
@@ -19,6 +19,7 @@
 import static android.hardware.wifi.V1_0.NanDataPathChannelCfg.CHANNEL_NOT_REQUESTED;
 
 import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -84,6 +85,8 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * Unit test harness for WifiAwareDataPathStateManager class.
@@ -359,8 +362,7 @@
         final byte pubSubId = 58;
         final int requestorId = 1341234;
         final int ndpId = 2;
-        final byte[] peerDiscoveryMac = HexEncoding.decode("000102030405".toCharArray(), false);
-        final byte[] peerDataPathMac = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false);
+
         final int[] startOrder = {0, 1, 2};
         final int[] endOrder = {1, 0, 2};
         int networkRequestId = 0;
@@ -378,8 +380,10 @@
         boolean first = true;
         for (int i : startOrder) {
             networkRequestId += 1;
-            peerDiscoveryMac[5] = (byte) (peerDiscoveryMac[5] + 1);
-            peerDataPathMac[5] = (byte) (peerDataPathMac[5] + 1);
+            byte[] peerDiscoveryMac = HexEncoding.decode("000102030405".toCharArray(), false);
+            byte[] peerDataPathMac = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false);
+            peerDiscoveryMac[5] = (byte) (peerDiscoveryMac[5] + i);
+            peerDataPathMac[5] = (byte) (peerDataPathMac[5] + i);
 
             // (0) initialize
             ress[i] = initDataPathEndPoint(first, clientId, (byte) (pubSubId + i),
@@ -474,16 +478,16 @@
         InOrder inOrderM = inOrder(mAwareMetricsMock);
 
         // (1) initialize all clients
-        Messenger messenger = initOobDataPathEndPoint(true, clientId, inOrder, inOrderM);
+        Messenger messenger = initOobDataPathEndPoint(true, 1, clientId, inOrder, inOrderM);
         for (int i = 1; i < numRequestsPre + numRequestsPost; ++i) {
-            initOobDataPathEndPoint(false, clientId + i, inOrder, inOrderM);
+            initOobDataPathEndPoint(false, 1, clientId + i, inOrder, inOrderM);
         }
 
         // (2) make 3 network requests (all identical under the hood)
         for (int i = 0; i < numRequestsPre; ++i) {
             nrs[i] = getDirectNetworkRequest(clientId + i,
                     WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, peerDiscoveryMac, null,
-                    null);
+                    null, i);
 
             Message reqNetworkMsg = Message.obtain();
             reqNetworkMsg.what = NetworkFactory.CMD_REQUEST_NETWORK;
@@ -532,7 +536,7 @@
         for (int i = numRequestsPre; i < numRequestsPre + numRequestsPost; ++i) {
             nrs[i] = getDirectNetworkRequest(clientId + i,
                     WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, peerDiscoveryMac, null,
-                    null);
+                    null, i);
 
             Message reqNetworkMsg = Message.obtain();
             reqNetworkMsg.what = NetworkFactory.CMD_REQUEST_NETWORK;
@@ -570,6 +574,74 @@
                 mAwareMetricsMock, mMockNwMgt);
     }
 
+    /**
+     * Validate that multiple NDP requests to the same peer target different NDIs.
+     */
+    @Test
+    public void testMultipleNdi() throws Exception {
+        final int numNdis = 5;
+        final int clientId = 123;
+        final int ndpId = 5;
+        final byte[] peerDiscoveryMac = HexEncoding.decode("000102030405".toCharArray(), false);
+        final byte[] peerDataPathMac = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false);
+
+        ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+        ArgumentCaptor<String> ifNameCaptor = ArgumentCaptor.forClass(String.class);
+
+        InOrder inOrder = inOrder(mMockNative, mMockCm, mMockCallback, mMockSessionCallback,
+                mMockNwMgt);
+        InOrder inOrderM = inOrder(mAwareMetricsMock);
+
+        // (1) initialize all clients
+        Messenger messenger = initOobDataPathEndPoint(true, numNdis, clientId, inOrder, inOrderM);
+        for (int i = 1; i < numNdis + 3; ++i) {
+            initOobDataPathEndPoint(false, numNdis, clientId + i, inOrder, inOrderM);
+        }
+
+        // (2) make N network requests: each unique
+        Set<String> interfaces = new HashSet<>();
+        for (int i = 0; i < numNdis + 1; ++i) {
+            byte[] pmk = new byte[32];
+            pmk[0] = (byte) i;
+
+            NetworkRequest nr = getDirectNetworkRequest(clientId + i,
+                    WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, peerDiscoveryMac, pmk,
+                    null, i);
+
+            Message reqNetworkMsg = Message.obtain();
+            reqNetworkMsg.what = NetworkFactory.CMD_REQUEST_NETWORK;
+            reqNetworkMsg.obj = nr;
+            reqNetworkMsg.arg1 = 0;
+            messenger.send(reqNetworkMsg);
+            mMockLooper.dispatchAll();
+
+            if (i < numNdis) {
+                inOrder.verify(mMockNative).initiateDataPath(transactionId.capture(), eq(0),
+                        eq(CHANNEL_NOT_REQUESTED), anyInt(), eq(peerDiscoveryMac),
+                        ifNameCaptor.capture(), eq(pmk), eq(null), eq(true), any());
+                interfaces.add(ifNameCaptor.getValue());
+
+                mDut.onInitiateDataPathResponseSuccess(transactionId.getValue(), ndpId + i);
+                mDut.onDataPathConfirmNotification(ndpId + i, peerDataPathMac, true, 0, null);
+                mMockLooper.dispatchAll();
+
+                inOrder.verify(mMockNwMgt).setInterfaceUp(anyString());
+                inOrder.verify(mMockNwMgt).enableIpv6(anyString());
+                inOrder.verify(mMockCm).registerNetworkAgent(any(), any(), any(), any(), anyInt(),
+                        any());
+                inOrderM.verify(mAwareMetricsMock).recordNdpStatus(eq(NanStatusType.SUCCESS),
+                        eq(true), anyLong());
+                inOrderM.verify(mAwareMetricsMock).recordNdpCreation(anyInt(), any());
+            }
+        }
+
+        // verify that each interface name is unique
+        assertEquals("Number of unique interface names", numNdis, interfaces.size());
+
+        verifyNoMoreInteractions(mMockNative, mMockCm, mMockCallback, mMockSessionCallback,
+                mAwareMetricsMock, mMockNwMgt);
+    }
+
     /*
      * Initiator tests
      */
@@ -929,7 +1001,7 @@
             nr = getDirectNetworkRequest(clientId,
                     WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR,
                     provideMac ? peerDiscoveryMac : null, providePmk ? pmk : null,
-                    providePassphrase ? passphrase : null);
+                    providePassphrase ? passphrase : null, 0);
         } else {
             nr = getSessionNetworkRequest(clientId, res.mSessionId,
                     provideMac ? res.mPeerHandle : null, providePmk ? pmk : null,
@@ -1038,7 +1110,7 @@
             nr = getDirectNetworkRequest(clientId,
                     WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER,
                     provideMac ? peerDiscoveryMac : null, providePmk ? pmk : null,
-                    providePassphrase ? passphrase : null);
+                    providePassphrase ? passphrase : null, 0);
         } else {
             nr = getSessionNetworkRequest(clientId, res.mSessionId,
                     provideMac ? res.mPeerHandle : null, providePmk ? pmk : null,
@@ -1183,7 +1255,7 @@
     }
 
     private NetworkRequest getDirectNetworkRequest(int clientId, int role, byte[] peer,
-            byte[] pmk, String passphrase) throws Exception {
+            byte[] pmk, String passphrase, int requestId) throws Exception {
         final IWifiAwareManager mockAwareService = mock(IWifiAwareManager.class);
         final ConfigRequest configRequest = new ConfigRequest.Builder().build();
         final WifiAwareManager mgr = new WifiAwareManager(mMockContext, mockAwareService);
@@ -1220,7 +1292,7 @@
         nc.setLinkDownstreamBandwidthKbps(1);
         nc.setSignalStrength(1);
 
-        return new NetworkRequest(nc, 0, 0, NetworkRequest.Type.REQUEST);
+        return new NetworkRequest(nc, 0, requestId, NetworkRequest.Type.REQUEST);
     }
 
     private DataPathEndPointInfo initDataPathEndPoint(boolean isFirstIteration, int clientId,
@@ -1237,7 +1309,7 @@
 
         Messenger messenger = null;
         if (isFirstIteration) {
-            messenger = initOobDataPathEndPoint(true, clientId, inOrder, inOrderM);
+            messenger = initOobDataPathEndPoint(true, 1, clientId, inOrder, inOrderM);
         }
 
         if (doPublish) {
@@ -1271,8 +1343,8 @@
                 isFirstIteration ? messenger : null);
     }
 
-    private Messenger initOobDataPathEndPoint(boolean startUpSequence, int clientId,
-            InOrder inOrder, InOrder inOrderM) throws Exception {
+    private Messenger initOobDataPathEndPoint(boolean startUpSequence, int maxNdiInterfaces,
+            int clientId, InOrder inOrder, InOrder inOrderM) throws Exception {
         final int pid = 2000;
         final String callingPackage = "com.android.somePackage";
         final ConfigRequest configRequest = new ConfigRequest.Builder().build();
@@ -1282,7 +1354,7 @@
         ArgumentCaptor<String> strCaptor = ArgumentCaptor.forClass(String.class);
 
         Capabilities capabilities = new Capabilities();
-        capabilities.maxNdiInterfaces = 1;
+        capabilities.maxNdiInterfaces = maxNdiInterfaces;
 
         if (startUpSequence) {
             // (0) start/registrations
@@ -1322,12 +1394,14 @@
                 any());
 
         if (startUpSequence) {
-            inOrder.verify(mMockNative).createAwareNetworkInterface(transactionId.capture(),
-                    strCaptor.capture());
-            collector.checkThat("interface created -- 0", sAwareInterfacePrefix + 0,
-                    equalTo(strCaptor.getValue()));
-            mDut.onCreateDataPathInterfaceResponse(transactionId.getValue(), true, 0);
-            mMockLooper.dispatchAll();
+            for (int i = 0; i < maxNdiInterfaces; ++i) {
+                inOrder.verify(mMockNative).createAwareNetworkInterface(transactionId.capture(),
+                        strCaptor.capture());
+                collector.checkThat("interface created -- " + i, sAwareInterfacePrefix + i,
+                        equalTo(strCaptor.getValue()));
+                mDut.onCreateDataPathInterfaceResponse(transactionId.getValue(), true, 0);
+                mMockLooper.dispatchAll();
+            }
             return messengerCaptor.getValue();
         }