Merge changes I38c31645,Ib6f61b81 into oc-mr1-dev
am: 9d7cf1434f

Change-Id: I79cdd12cae7ae31543f2c5b85598de0967ce4a47
diff --git a/service/java/com/android/server/wifi/NetworkListStoreData.java b/service/java/com/android/server/wifi/NetworkListStoreData.java
index 5ddfd4d..f287d4b 100644
--- a/service/java/com/android/server/wifi/NetworkListStoreData.java
+++ b/service/java/com/android/server/wifi/NetworkListStoreData.java
@@ -16,10 +16,12 @@
 
 package com.android.server.wifi;
 
+import android.content.Context;
 import android.net.IpConfiguration;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
 import android.net.wifi.WifiEnterpriseConfig;
+import android.os.Process;
 import android.util.Log;
 import android.util.Pair;
 
@@ -52,6 +54,8 @@
     private static final String XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION =
             "WifiEnterpriseConfiguration";
 
+    private final Context mContext;
+
     /**
      * List of saved shared networks visible to all the users to be stored in the shared store file.
      */
@@ -62,7 +66,9 @@
      */
     private List<WifiConfiguration> mUserConfigurations;
 
-    NetworkListStoreData() {}
+    NetworkListStoreData(Context context) {
+        mContext = context;
+    }
 
     @Override
     public void serializeData(XmlSerializer out, boolean shared)
@@ -282,6 +288,19 @@
                     "Configuration key does not match. Retrieved: " + configKeyParsed
                             + ", Calculated: " + configKeyCalculated);
         }
+        // Set creatorUid/creatorName for networks which don't have it set to valid value.
+        String creatorName = mContext.getPackageManager().getNameForUid(configuration.creatorUid);
+        if (creatorName == null) {
+            Log.e(TAG, "Invalid creatorUid for saved network " + configuration.configKey()
+                    + ", creatorUid=" + configuration.creatorUid);
+            configuration.creatorUid = Process.SYSTEM_UID;
+            configuration.creatorName = creatorName;
+        } else if (!creatorName.equals(configuration.creatorName)) {
+            Log.w(TAG, "Invalid creatorName for saved network " + configuration.configKey()
+                    + ", creatorUid=" + configuration.creatorUid
+                    + ", creatorName=" + configuration.creatorName);
+            configuration.creatorName = creatorName;
+        }
 
         configuration.setNetworkSelectionStatus(status);
         configuration.setIpConfiguration(ipConfiguration);
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index 4a42c33..4b8e682 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -197,7 +197,7 @@
         mWifiConfigManager = new WifiConfigManager(mContext, mClock,
                 UserManager.get(mContext), TelephonyManager.from(mContext),
                 mWifiKeyStore, mWifiConfigStore, mWifiPermissionsUtil,
-                mWifiPermissionsWrapper, new NetworkListStoreData(),
+                mWifiPermissionsWrapper, new NetworkListStoreData(mContext),
                 new DeletedEphemeralSsidsStoreData());
         mWifiMetrics.setWifiConfigManager(mWifiConfigManager);
         mWifiConnectivityHelper = new WifiConnectivityHelper(mWifiNative);
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index e96c15d..a3054c9 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -3980,9 +3980,7 @@
                     deleteNetworkConfigAndSendReply(message, true);
                     break;
                 case WifiManager.SAVE_NETWORK:
-                    messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
-                    replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
-                            WifiManager.BUSY);
+                    saveNetworkConfigAndSendReply(message);
                     break;
                 case WifiManager.START_WPS:
                     replyToMessage(message, WifiManager.WPS_FAILED,
@@ -5270,36 +5268,12 @@
                     replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
                     break;
                 case WifiManager.SAVE_NETWORK:
-                    config = (WifiConfiguration) message.obj;
-                    mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
-                    if (config == null) {
-                        loge("SAVE_NETWORK with null configuration"
-                                + mSupplicantStateTracker.getSupplicantStateName()
-                                + " my state " + getCurrentState().getName());
-                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
-                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
-                                WifiManager.ERROR);
-                        break;
-                    }
-                    result = mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid);
-                    if (!result.isSuccess()) {
-                        loge("SAVE_NETWORK adding/updating config=" + config + " failed");
-                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
-                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
-                                WifiManager.ERROR);
-                        break;
-                    }
-                    if (!mWifiConfigManager.enableNetwork(
-                            result.getNetworkId(), false, message.sendingUid)) {
-                        loge("SAVE_NETWORK enabling config=" + config + " failed");
-                        messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
-                        replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
-                                WifiManager.ERROR);
-                        break;
-                    }
+                    result = saveNetworkConfigAndSendReply(message);
                     netId = result.getNetworkId();
-                    if (mWifiInfo.getNetworkId() == netId) {
+                    if (result.isSuccess() && mWifiInfo.getNetworkId() == netId) {
+                        mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
                         if (result.hasCredentialChanged()) {
+                            config = (WifiConfiguration) message.obj;
                             // The network credentials changed and we're connected to this network,
                             // start a new connection with the updated credentials.
                             logi("SAVE_NETWORK credential changed for config=" + config.configKey()
@@ -5322,8 +5296,6 @@
                             }
                         }
                     }
-                    broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
-                    replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
                     break;
                 case WifiManager.FORGET_NETWORK:
                     if (!deleteNetworkConfigAndSendReply(message, true)) {
@@ -7217,6 +7189,43 @@
         }
     }
 
+    /**
+     * Private method to handle calling WifiConfigManager to add & enable network configs and reply
+     * to the message from the sender of the outcome.
+     *
+     * @return NetworkUpdateResult with networkId of the added/updated configuration. Will return
+     * {@link WifiConfiguration#INVALID_NETWORK_ID} in case of error.
+     */
+    private NetworkUpdateResult saveNetworkConfigAndSendReply(Message message) {
+        WifiConfiguration config = (WifiConfiguration) message.obj;
+        if (config == null) {
+            loge("SAVE_NETWORK with null configuration "
+                    + mSupplicantStateTracker.getSupplicantStateName()
+                    + " my state " + getCurrentState().getName());
+            messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
+            replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR);
+            return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
+        }
+        NetworkUpdateResult result =
+                mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid);
+        if (!result.isSuccess()) {
+            loge("SAVE_NETWORK adding/updating config=" + config + " failed");
+            messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
+            replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR);
+            return result;
+        }
+        if (!mWifiConfigManager.enableNetwork(
+                result.getNetworkId(), false, message.sendingUid)) {
+            loge("SAVE_NETWORK enabling config=" + config + " failed");
+            messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
+            replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR);
+            return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
+        }
+        broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
+        replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
+        return result;
+    }
+
     private static String getLinkPropertiesSummary(LinkProperties lp) {
         List<String> attributes = new ArrayList<>(6);
         if (lp.hasIPv4Address()) {
diff --git a/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java
index cbad3bb..19a92b8 100644
--- a/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java
@@ -16,9 +16,13 @@
 
 package com.android.server.wifi;
 
+import static android.os.Process.SYSTEM_UID;
+
 import static org.junit.Assert.*;
 import static org.mockito.Mockito.*;
 
+import android.content.Context;
+import android.content.pm.PackageManager;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiEnterpriseConfig;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -29,6 +33,8 @@
 
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -49,6 +55,7 @@
     private static final String TEST_SSID = "WifiConfigStoreDataSSID_";
     private static final String TEST_CONNECT_CHOICE = "XmlUtilConnectChoice";
     private static final long TEST_CONNECT_CHOICE_TIMESTAMP = 0x4566;
+    private static final String TEST_CREATOR_NAME = "CreatorName";
     private static final String SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT =
             "<Network>\n"
                     + "<WifiConfiguration>\n"
@@ -79,7 +86,7 @@
                     + "<boolean name=\"UseExternalScores\" value=\"false\" />\n"
                     + "<int name=\"NumAssociation\" value=\"0\" />\n"
                     + "<int name=\"CreatorUid\" value=\"%d\" />\n"
-                    + "<null name=\"CreatorName\" />\n"
+                    + "<string name=\"CreatorName\">%s</string>\n"
                     + "<null name=\"CreationTime\" />\n"
                     + "<int name=\"LastUpdateUid\" value=\"-1\" />\n"
                     + "<null name=\"LastUpdateName\" />\n"
@@ -130,7 +137,7 @@
                     + "<boolean name=\"UseExternalScores\" value=\"false\" />\n"
                     + "<int name=\"NumAssociation\" value=\"0\" />\n"
                     + "<int name=\"CreatorUid\" value=\"%d\" />\n"
-                    + "<null name=\"CreatorName\" />\n"
+                    + "<string name=\"CreatorName\">%s</string>\n"
                     + "<null name=\"CreationTime\" />\n"
                     + "<int name=\"LastUpdateUid\" value=\"-1\" />\n"
                     + "<null name=\"LastUpdateName\" />\n"
@@ -170,10 +177,15 @@
                     + "</Network>\n";
 
     private NetworkListStoreData mNetworkListStoreData;
+    @Mock private Context mContext;
+    @Mock private PackageManager mPackageManager;
 
     @Before
     public void setUp() throws Exception {
-        mNetworkListStoreData = new NetworkListStoreData();
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mPackageManager.getNameForUid(anyInt())).thenReturn(TEST_CREATOR_NAME);
+        mNetworkListStoreData = new NetworkListStoreData(mContext);
     }
 
     /**
@@ -221,11 +233,13 @@
      */
     private List<WifiConfiguration> getTestNetworksConfig(boolean shared) {
         WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+        openNetwork.creatorName = TEST_CREATOR_NAME;
         openNetwork.shared = shared;
         openNetwork.setIpConfiguration(
                 WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy());
         WifiConfiguration eapNetwork = WifiConfigurationTestUtil.createEapNetwork();
         eapNetwork.shared = shared;
+        eapNetwork.creatorName = TEST_CREATOR_NAME;
         eapNetwork.setIpConfiguration(
                 WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy());
         List<WifiConfiguration> networkList = new ArrayList<>();
@@ -247,11 +261,11 @@
         String openNetworkXml = String.format(SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT,
                 openNetwork.configKey().replaceAll("\"", "&quot;"),
                 openNetwork.SSID.replaceAll("\"", "&quot;"),
-                openNetwork.shared, openNetwork.creatorUid);
+                openNetwork.shared, openNetwork.creatorUid, openNetwork.creatorName);
         String eapNetworkXml = String.format(SINGLE_EAP_NETWORK_DATA_XML_STRING_FORMAT,
                 eapNetwork.configKey().replaceAll("\"", "&quot;"),
                 eapNetwork.SSID.replaceAll("\"", "&quot;"),
-                eapNetwork.shared, eapNetwork.creatorUid);
+                eapNetwork.shared, eapNetwork.creatorUid, openNetwork.creatorName);
         return (openNetworkXml + eapNetworkXml).getBytes(StandardCharsets.UTF_8);
     }
 
@@ -420,7 +434,8 @@
         byte[] xmlData = String.format(SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT,
                 "InvalidConfigKey",
                 openNetwork.SSID.replaceAll("\"", "&quot;"),
-                openNetwork.shared, openNetwork.creatorUid).getBytes(StandardCharsets.UTF_8);
+                openNetwork.shared, openNetwork.creatorUid, openNetwork.creatorName)
+            .getBytes(StandardCharsets.UTF_8);
         deserializeData(xmlData, true);
     }
 
@@ -448,4 +463,96 @@
             assertNotEquals(eapNetwork.SSID, network.SSID);
         }
     }
+
+    /**
+     * Verify that a saved network config with invalid creatorUid resets it to
+     * {@link android.os.Process#SYSTEM_UID}.
+     */
+    public void parseNetworkWithInvalidCreatorUidResetsToSystem() throws Exception {
+        WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+        openNetwork.creatorUid = -1;
+        // Return null for invalid uid.
+        when(mPackageManager.getNameForUid(eq(openNetwork.creatorUid))).thenReturn(null);
+
+        byte[] xmlData = String.format(SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT,
+                openNetwork.configKey().replaceAll("\"", "&quot;"),
+                openNetwork.SSID.replaceAll("\"", "&quot;"),
+                openNetwork.shared, openNetwork.creatorUid, openNetwork.creatorName)
+            .getBytes(StandardCharsets.UTF_8);
+        List<WifiConfiguration> deserializedNetworks = deserializeData(xmlData, true);
+        assertEquals(1, deserializedNetworks.size());
+        assertEquals(openNetwork.configKey(), deserializedNetworks.get(0).configKey());
+        assertEquals(SYSTEM_UID, deserializedNetworks.get(0).creatorUid);
+        assertEquals(TEST_CREATOR_NAME, deserializedNetworks.get(0).creatorName);
+    }
+
+    /**
+     * Verify that a saved network config with invalid creatorName resets it to the package name
+     * provided {@link PackageManager} for the creatorUid.
+     */
+    public void parseNetworkWithInvalidCreatorNameResetsToPackageNameForCreatorUid()
+            throws Exception {
+        String badCreatorName = "bad";
+        String correctCreatorName = "correct";
+        WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+        openNetwork.creatorUid = 1324422;
+        openNetwork.creatorName = badCreatorName;
+        when(mPackageManager.getNameForUid(eq(openNetwork.creatorUid)))
+            .thenReturn(correctCreatorName);
+
+        byte[] xmlData = String.format(SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT,
+                openNetwork.configKey().replaceAll("\"", "&quot;"),
+                openNetwork.SSID.replaceAll("\"", "&quot;"),
+                openNetwork.shared, openNetwork.creatorUid, openNetwork.creatorName)
+            .getBytes(StandardCharsets.UTF_8);
+        List<WifiConfiguration> deserializedNetworks = deserializeData(xmlData, true);
+        assertEquals(1, deserializedNetworks.size());
+        assertEquals(openNetwork.configKey(), deserializedNetworks.get(0).configKey());
+        assertEquals(openNetwork.creatorUid, deserializedNetworks.get(0).creatorUid);
+        assertEquals(correctCreatorName, deserializedNetworks.get(0).creatorName);
+    }
+
+    /**
+     * Verify that a saved network config with invalid creatorName resets it to the package name
+     * provided {@link PackageManager} for the creatorUid.
+     */
+    public void parseNetworkWithNullCreatorNameResetsToPackageNameForCreatorUid()
+            throws Exception {
+        String correctCreatorName = "correct";
+        WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+        openNetwork.creatorUid = 1324422;
+        openNetwork.creatorName = null;
+        when(mPackageManager.getNameForUid(eq(openNetwork.creatorUid)))
+            .thenReturn(correctCreatorName);
+
+        byte[] xmlData = String.format(SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT,
+                openNetwork.configKey().replaceAll("\"", "&quot;"),
+                openNetwork.SSID.replaceAll("\"", "&quot;"),
+                openNetwork.shared, openNetwork.creatorUid, openNetwork.creatorName)
+            .getBytes(StandardCharsets.UTF_8);
+        List<WifiConfiguration> deserializedNetworks = deserializeData(xmlData, true);
+        assertEquals(1, deserializedNetworks.size());
+        assertEquals(openNetwork.configKey(), deserializedNetworks.get(0).configKey());
+        assertEquals(openNetwork.creatorUid, deserializedNetworks.get(0).creatorUid);
+        assertEquals(correctCreatorName, deserializedNetworks.get(0).creatorName);
+    }
+
+    /**
+     * Verify that a saved network config with valid creatorUid is preserved.
+     */
+    public void parseNetworkWithValidCreatorUid() throws Exception {
+        WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+        openNetwork.creatorUid = 1324422;
+
+        byte[] xmlData = String.format(SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT,
+                openNetwork.configKey().replaceAll("\"", "&quot;"),
+                openNetwork.SSID.replaceAll("\"", "&quot;"),
+                openNetwork.shared, openNetwork.creatorUid, openNetwork.creatorName)
+            .getBytes(StandardCharsets.UTF_8);
+        List<WifiConfiguration> deserializedNetworks = deserializeData(xmlData, true);
+        assertEquals(1, deserializedNetworks.size());
+        assertEquals(openNetwork.configKey(), deserializedNetworks.get(0).configKey());
+        assertEquals(openNetwork.creatorUid, deserializedNetworks.get(0).creatorUid);
+        assertEquals(TEST_CREATOR_NAME, deserializedNetworks.get(0).creatorName);
+    }
 }
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
index 86d6e11..0958fea 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
@@ -21,6 +21,7 @@
 
 import android.app.test.TestAlarmManager;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.net.wifi.WifiConfiguration;
 import android.os.test.TestLooper;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -60,6 +61,7 @@
 
     private static final String TEST_USER_DATA = "UserData";
     private static final String TEST_SHARE_DATA = "ShareData";
+    private static final String TEST_CREATOR_NAME = "CreatorName";
 
     private static final String TEST_DATA_XML_STRING_FORMAT =
             "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
@@ -95,7 +97,7 @@
                     + "<boolean name=\"UseExternalScores\" value=\"false\" />\n"
                     + "<int name=\"NumAssociation\" value=\"0\" />\n"
                     + "<int name=\"CreatorUid\" value=\"%d\" />\n"
-                    + "<null name=\"CreatorName\" />\n"
+                    + "<string name=\"CreatorName\">%s</string>\n"
                     + "<null name=\"CreationTime\" />\n"
                     + "<int name=\"LastUpdateUid\" value=\"-1\" />\n"
                     + "<null name=\"LastUpdateName\" />\n"
@@ -125,6 +127,7 @@
 
     // Test mocks
     @Mock private Context mContext;
+    @Mock private PackageManager mPackageManager;
     private TestAlarmManager mAlarmManager;
     private TestLooper mLooper;
     @Mock private Clock mClock;
@@ -146,6 +149,8 @@
         mLooper = new TestLooper();
         when(mContext.getSystemService(Context.ALARM_SERVICE))
                 .thenReturn(mAlarmManager.getAlarmManager());
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mPackageManager.getNameForUid(anyInt())).thenReturn(TEST_CREATOR_NAME);
         mUserStore = new MockStoreFile();
         mSharedStore = new MockStoreFile();
         mStoreData = new MockStoreData();
@@ -364,9 +369,10 @@
     @Test
     public void testReadWifiConfigStoreData() throws Exception {
         // Setup network list.
-        NetworkListStoreData networkList = new NetworkListStoreData();
+        NetworkListStoreData networkList = new NetworkListStoreData(mContext);
         mWifiConfigStore.registerStoreData(networkList);
         WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+        openNetwork.creatorName = TEST_CREATOR_NAME;
         openNetwork.setIpConfiguration(
                 WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy());
         List<WifiConfiguration> userConfigs = new ArrayList<>();
@@ -384,7 +390,7 @@
         String xmlString = String.format(TEST_DATA_XML_STRING_FORMAT,
                 openNetwork.configKey().replaceAll("\"", "&quot;"),
                 openNetwork.SSID.replaceAll("\"", "&quot;"),
-                openNetwork.shared, openNetwork.creatorUid, testSsid);
+                openNetwork.shared, openNetwork.creatorUid, openNetwork.creatorName, testSsid);
         byte[] xmlBytes = xmlString.getBytes(StandardCharsets.UTF_8);
         mUserStore.storeRawDataToWrite(xmlBytes);
 
@@ -406,9 +412,10 @@
         mWifiConfigStore.switchUserStoreAndRead(mUserStore);
 
         // Setup network list store data.
-        NetworkListStoreData networkList = new NetworkListStoreData();
+        NetworkListStoreData networkList = new NetworkListStoreData(mContext);
         mWifiConfigStore.registerStoreData(networkList);
         WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+        openNetwork.creatorName = TEST_CREATOR_NAME;
         openNetwork.setIpConfiguration(
                 WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy());
         List<WifiConfiguration> userConfigs = new ArrayList<>();
@@ -428,7 +435,7 @@
         String xmlString = String.format(TEST_DATA_XML_STRING_FORMAT,
                 openNetwork.configKey().replaceAll("\"", "&quot;"),
                 openNetwork.SSID.replaceAll("\"", "&quot;"),
-                openNetwork.shared, openNetwork.creatorUid, testSsid);
+                openNetwork.shared, openNetwork.creatorUid, openNetwork.creatorName, testSsid);
         byte[] xmlBytes = xmlString.getBytes(StandardCharsets.UTF_8);
 
         mWifiConfigStore.write(true);
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
index 1772616..5b0f596 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
@@ -139,6 +139,7 @@
                     : WifiStateMachine.NUM_LOG_RECS_VERBOSE);
     private static final int FRAMEWORK_NETWORK_ID = 7;
     private static final int TEST_RSSI = -54;
+    private static final int TEST_NETWORK_ID = 54;
     private static final int WPS_SUPPLICANT_NETWORK_ID = 5;
     private static final int WPS_FRAMEWORK_NETWORK_ID = 10;
     private static final String DEFAULT_TEST_SSID = "\"GoogleGuest\"";
@@ -750,25 +751,7 @@
                 (Intent) argThat(new WifiEnablingStateIntentMatcher()), any());
     }
 
-    /**
-     * Verifies that configs can be removed when in client mode.
-     */
-    @Test
-    public void canRemoveNetworkConfigInClientMode() throws Exception {
-        boolean result;
-        when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true);
-        initializeAndAddNetworkAndVerifySuccess();
-        mLooper.startAutoDispatch();
-        result = mWsm.syncRemoveNetwork(mWsmAsyncChannel, 0);
-        mLooper.stopAutoDispatch();
-        assertTrue(result);
-    }
-
-    /**
-     * Verifies that configs can be removed when not in client mode.
-     */
-    @Test
-    public void canRemoveNetworkConfigWhenWifiDisabled() {
+    private void canRemoveNetwork() {
         boolean result;
         when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true);
         mLooper.startAutoDispatch();
@@ -780,12 +763,25 @@
     }
 
     /**
-     * Verifies that configs can be forgotten when in client mode.
+     * Verifies that configs can be removed when not in client mode.
      */
     @Test
-    public void canForgetNetworkConfigInClientMode() throws Exception {
-        when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true);
+    public void canRemoveNetworkConfigWhenWifiDisabled() {
+        canRemoveNetwork();
+    }
+
+
+    /**
+     * Verifies that configs can be removed when in client mode.
+     */
+    @Test
+    public void canRemoveNetworkConfigInClientMode() throws Exception {
         initializeAndAddNetworkAndVerifySuccess();
+        canRemoveNetwork();
+    }
+
+    private void canForgetNetwork() {
+        when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true);
         mWsm.sendMessage(WifiManager.FORGET_NETWORK, 0, MANAGED_PROFILE_UID);
         mLooper.dispatchAll();
         verify(mWifiConfigManager).removeNetwork(anyInt(), anyInt());
@@ -796,10 +792,109 @@
      */
     @Test
     public void canForgetNetworkConfigWhenWifiDisabled() throws Exception {
-        when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true);
-        mWsm.sendMessage(WifiManager.FORGET_NETWORK, 0, MANAGED_PROFILE_UID);
-        mLooper.dispatchAll();
-        verify(mWifiConfigManager).removeNetwork(anyInt(), anyInt());
+        canForgetNetwork();
+    }
+
+    /**
+     * Verifies that configs can be forgotten when in client mode.
+     */
+    @Test
+    public void canForgetNetworkConfigInClientMode() throws Exception {
+        initializeAndAddNetworkAndVerifySuccess();
+        canForgetNetwork();
+    }
+
+    private void canSaveNetworkConfig() {
+        WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
+
+        int networkId = TEST_NETWORK_ID;
+        when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()))
+                .thenReturn(new NetworkUpdateResult(networkId));
+        when(mWifiConfigManager.enableNetwork(eq(networkId), eq(false), anyInt()))
+                .thenReturn(true);
+
+        mLooper.startAutoDispatch();
+        Message reply = mWsmAsyncChannel.sendMessageSynchronously(WifiManager.SAVE_NETWORK, config);
+        mLooper.stopAutoDispatch();
+        assertEquals(WifiManager.SAVE_NETWORK_SUCCEEDED, reply.what);
+
+        verify(mWifiConfigManager).addOrUpdateNetwork(any(WifiConfiguration.class), anyInt());
+        verify(mWifiConfigManager).enableNetwork(eq(networkId), eq(false), anyInt());
+    }
+
+    /**
+     * Verifies that configs can be saved when not in client mode.
+     */
+    @Test
+    public void canSaveNetworkConfigWhenWifiDisabled() throws Exception {
+        canSaveNetworkConfig();
+    }
+
+    /**
+     * Verifies that configs can be saved when in client mode.
+     */
+    @Test
+    public void canSaveNetworkConfigInClientMode() throws Exception {
+        loadComponentsInStaMode();
+        canSaveNetworkConfig();
+    }
+
+    /**
+     * Verifies that null configs are rejected in SAVE_NETWORK message.
+     */
+    @Test
+    public void saveNetworkConfigFailsWithNullConfig() throws Exception {
+        mLooper.startAutoDispatch();
+        Message reply = mWsmAsyncChannel.sendMessageSynchronously(WifiManager.SAVE_NETWORK, null);
+        mLooper.stopAutoDispatch();
+        assertEquals(WifiManager.SAVE_NETWORK_FAILED, reply.what);
+
+        verify(mWifiConfigManager, never())
+                .addOrUpdateNetwork(any(WifiConfiguration.class), anyInt());
+        verify(mWifiConfigManager, never())
+                .enableNetwork(anyInt(), anyBoolean(), anyInt());
+    }
+
+    /**
+     * Verifies that configs save fails when the addition of network fails.
+     */
+    @Test
+    public void saveNetworkConfigFailsWithConfigAddFailure() throws Exception {
+        WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
+
+        when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()))
+                .thenReturn(new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID));
+
+        mLooper.startAutoDispatch();
+        Message reply = mWsmAsyncChannel.sendMessageSynchronously(WifiManager.SAVE_NETWORK, config);
+        mLooper.stopAutoDispatch();
+        assertEquals(WifiManager.SAVE_NETWORK_FAILED, reply.what);
+
+        verify(mWifiConfigManager).addOrUpdateNetwork(any(WifiConfiguration.class), anyInt());
+        verify(mWifiConfigManager, never())
+                .enableNetwork(anyInt(), anyBoolean(), anyInt());
+    }
+
+    /**
+     * Verifies that configs save fails when the enable of network fails.
+     */
+    @Test
+    public void saveNetworkConfigFailsWithConfigEnableFailure() throws Exception {
+        WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
+
+        int networkId = 5;
+        when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()))
+                .thenReturn(new NetworkUpdateResult(networkId));
+        when(mWifiConfigManager.enableNetwork(eq(networkId), eq(false), anyInt()))
+                .thenReturn(false);
+
+        mLooper.startAutoDispatch();
+        Message reply = mWsmAsyncChannel.sendMessageSynchronously(WifiManager.SAVE_NETWORK, config);
+        mLooper.stopAutoDispatch();
+        assertEquals(WifiManager.SAVE_NETWORK_FAILED, reply.what);
+
+        verify(mWifiConfigManager).addOrUpdateNetwork(any(WifiConfiguration.class), anyInt());
+        verify(mWifiConfigManager).enableNetwork(eq(networkId), eq(false), anyInt());
     }
 
     /**