wifi: Wi-Fi data usage per configuration (Wifi part) am: fa06a9c21d

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Wifi/+/1908675

Change-Id: I2856f193db3495a422b9bdc00305bf7ddb48a8cf
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index dda7481..1231f50 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -362,6 +362,7 @@
   }
 
   @Deprecated public class WifiConfiguration implements android.os.Parcelable {
+    method @Deprecated @NonNull public java.util.Set<java.lang.String> getAllPersistableNetworkKeys();
     method @Deprecated public int getAuthType();
     method @Deprecated public int getDeletionPriority();
     method @Deprecated @NonNull public android.net.IpConfiguration getIpConfiguration();
@@ -494,6 +495,7 @@
   }
 
   public class WifiInfo implements android.os.Parcelable android.net.TransportInfo {
+    method @Nullable public String getCurrentNetworkKey();
     method public double getLostTxPacketsPerSecond();
     method @Nullable public String getRequestingPackageName();
     method public double getRetriedTxPacketsPerSecond();
diff --git a/framework/java/android/net/wifi/WifiConfiguration.java b/framework/java/android/net/wifi/WifiConfiguration.java
index 924cd33..0859bd0 100644
--- a/framework/java/android/net/wifi/WifiConfiguration.java
+++ b/framework/java/android/net/wifi/WifiConfiguration.java
@@ -54,7 +54,9 @@
 import java.util.Calendar;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 /**
@@ -3939,4 +3941,55 @@
         return SECURITY_TYPE_NAMES[securityType];
     }
 
+    /**
+     * Returns the key for storing the data usage bucket.
+     *
+     * Note: DO NOT change this function. It is used to be a key to store Wi-Fi data usage data.
+     * Create a new function if we plan to change the key for Wi-Fi data usage and add the new key
+     * to {@link #getAllPersistableNetworkKeys()}.
+     *
+     * @param securityType the security type corresponding to the target network.
+     * @hide
+     */
+    public String getNetworkKeyFromSecurityType(@SecurityType int securityType) {
+        if (mPasspointUniqueId != null) {
+            // It might happen that there are two connections which use the same passpoint
+            // coniguration but different sim card (maybe same carriers?). Add subscriptionId to be
+            // the part of key to separate data in usage bucket.
+            // But now we only show one WifiConfiguration entry in Wifi picker for this case.
+            // It means that user only have a way to query usage with configuration on default SIM.
+            // (We always connect to network with default SIM). So returns the key with associated
+            // subscriptionId (the default one) first.
+            return subscriptionId + "-" + mPasspointUniqueId;
+        } else {
+            String key = SSID + getSecurityTypeName(securityType);
+            if (!shared) {
+                key += "-" + UserHandle.getUserHandleForUid(creatorUid).getIdentifier();
+            }
+            if (fromWifiNetworkSuggestion) {
+                key += "_" + creatorName + "-" + carrierId + "-" + subscriptionId;
+            }
+            return key;
+        }
+    }
+
+    /**
+     * Returns a list of all persistable network keys corresponding to this configuration.
+     * There may be multiple keys since they are security-type specific and a configuration may
+     * support multiple security types. The persistable key of a specific network connection may
+     * be obtained from {@link WifiInfo#getCurrentNetworkKey()}.
+     * An example of usage of such persistable network keys is to query the Wi-Fi data usage
+     * corresponding to this configuration. See {@code NetworkTemplate} to know the detail.
+     *
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public Set<String> getAllPersistableNetworkKeys() {
+        Set<String> keys = new HashSet<>();
+        for (SecurityParams securityParam : mSecurityParamsList) {
+            keys.add(getNetworkKeyFromSecurityType(securityParam.getSecurityType()));
+        }
+        return keys;
+    }
 }
diff --git a/framework/java/android/net/wifi/WifiInfo.java b/framework/java/android/net/wifi/WifiInfo.java
index ead4361..ed0288f 100644
--- a/framework/java/android/net/wifi/WifiInfo.java
+++ b/framework/java/android/net/wifi/WifiInfo.java
@@ -425,6 +425,11 @@
      */
     private @IsPrimaryValues int mIsPrimary;
 
+    /**
+     * Key of the current network.
+     */
+    private String mCurrentNetworkKey;
+
     /** @hide */
     @UnsupportedAppUsage
     public WifiInfo() {
@@ -438,6 +443,7 @@
         mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
         mSecurityType = -1;
         mIsPrimary = IS_PRIMARY_FALSE;
+        mCurrentNetworkKey = null;
     }
 
     /** @hide */
@@ -477,6 +483,7 @@
         mTxRetriedTxPacketsPerSecond = 0;
         score = 0;
         mSecurityType = -1;
+        mCurrentNetworkKey = null;
     }
 
     /**
@@ -544,6 +551,8 @@
             mIsPrimary = shouldRedactNetworkSettingsFields(redactions)
                     ? IS_PRIMARY_NO_PERMISSION : source.mIsPrimary;
             mSecurityType = source.mSecurityType;
+            mCurrentNetworkKey = shouldRedactLocationSensitiveFields(redactions)
+                    ? null : source.mCurrentNetworkKey;
         }
     }
 
@@ -1206,7 +1215,8 @@
                 .append(", score: ").append(Integer.toString(score))
                 .append(", CarrierMerged: ").append(mCarrierMerged)
                 .append(", SubscriptionId: ").append(mSubscriptionId)
-                .append(", IsPrimary: ").append(mIsPrimary);
+                .append(", IsPrimary: ").append(mIsPrimary)
+                .append(mCurrentNetworkKey == null ? none : mCurrentNetworkKey);
         return sb.toString();
     }
 
@@ -1279,6 +1289,7 @@
             dest.writeInt(mIsPrimary);
         }
         dest.writeInt(mSecurityType);
+        dest.writeString(mCurrentNetworkKey);
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -1334,6 +1345,7 @@
                     info.mIsPrimary = in.readInt();
                 }
                 info.mSecurityType = in.readInt();
+                info.mCurrentNetworkKey = in.readString();
                 return info;
             }
 
@@ -1477,7 +1489,8 @@
                 && Objects.equals(mPasspointUniqueId, thatWifiInfo.mPasspointUniqueId)
                 && Objects.equals(mInformationElements, thatWifiInfo.mInformationElements)
                 && Objects.equals(mIsPrimary, thatWifiInfo.mIsPrimary)
-                && Objects.equals(mSecurityType, thatWifiInfo.mSecurityType);
+                && Objects.equals(mSecurityType, thatWifiInfo.mSecurityType)
+                && Objects.equals(mCurrentNetworkKey, thatWifiInfo.mCurrentNetworkKey);
     }
 
     @Override
@@ -1522,7 +1535,8 @@
                 mPasspointUniqueId,
                 mInformationElements,
                 mIsPrimary,
-                mSecurityType);
+                mSecurityType,
+                mCurrentNetworkKey);
     }
 
     /**
@@ -1607,4 +1621,28 @@
                 return SECURITY_TYPE_UNKNOWN;
         }
     }
+
+    /**
+     * Set the network key for the current Wi-Fi network.
+     *
+     * Now we are using this identity to be a key when storing Wi-Fi data usage data.
+     * See: {@link WifiConfiguration#getNetworkKeyFromSecurityType(int)}.
+     *
+     * @param currentNetworkKey the network key of the current Wi-Fi network.
+     * @hide
+     */
+    public void setCurrentNetworkKey(@NonNull String currentNetworkKey) {
+        mCurrentNetworkKey = currentNetworkKey;
+    }
+
+    /**
+     * Returns the network key of the current Wi-Fi network.
+     *
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public String getCurrentNetworkKey() {
+        return mCurrentNetworkKey;
+    }
 }
diff --git a/framework/tests/src/android/net/wifi/WifiConfigurationTest.java b/framework/tests/src/android/net/wifi/WifiConfigurationTest.java
index 276d85f..80fb52f 100644
--- a/framework/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/framework/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -17,6 +17,7 @@
 package android.net.wifi;
 
 import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_EAP;
+import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B;
 import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE;
 import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT;
 import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_OPEN;
@@ -29,6 +30,7 @@
 import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_WAPI_CERT;
 import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_WAPI_PSK;
 import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_WEP;
+import static android.net.wifi.WifiConfiguration.getSecurityTypeName;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -69,6 +71,18 @@
     private static final int TEST_CARRIER_ID = 1234;
     private static final int TEST_SUB_ID = 3;
     private static final String TEST_PACKAGE_NAME = "google.com";
+    private static final int[] SECURITY_TYPES_EXCEPT_PASSPOINT = {
+            SECURITY_TYPE_OPEN,
+            SECURITY_TYPE_WEP,
+            SECURITY_TYPE_PSK,
+            SECURITY_TYPE_EAP,
+            SECURITY_TYPE_SAE,
+            SECURITY_TYPE_EAP_SUITE_B,
+            SECURITY_TYPE_OWE,
+            SECURITY_TYPE_WAPI_PSK,
+            SECURITY_TYPE_WAPI_CERT,
+            SECURITY_TYPE_EAP_WPA3_ENTERPRISE,
+            SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT};
 
     @Before
     public void setUp() {
@@ -930,6 +944,17 @@
         }
     }
 
+    private String createNetworkKey(String ssid, String keyMgmt, String providerName,
+            int carrierId, int subId, boolean isFromSuggestion) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(ssid).append(keyMgmt);
+        if (isFromSuggestion) {
+            sb.append("_").append(providerName).append('-')
+                    .append(carrierId).append('-').append(subId);
+        }
+        return sb.toString();
+    }
+
     /** Verify that adding security types works as expected. */
     @Test
     public void testAddSecurityTypes() {
@@ -1214,4 +1239,69 @@
         List<SecurityParams> list = null;
         config.setSecurityParams(list);
     }
+
+    /**
+     * Verifies that getNetworkKeyFromSecurityType returns the correct String
+     * for networks of various different security types, the result should be stable.
+     *
+     * Note: DO NOT update the test if it happens failure! Fixed it is necessary, otherwise
+     * we will break Wi-Fi data usage functionality.
+     */
+    @Test
+    public void testGetNetworkKeyFromSecurityTypeString() {
+        WifiConfiguration config = new WifiConfiguration();
+        final String mSsid = "TestAP";
+        config.SSID = mSsid;
+        config.carrierId = TEST_CARRIER_ID;
+        config.subscriptionId = TEST_SUB_ID;
+        config.creatorName = TEST_PACKAGE_NAME;
+
+        for (int securityType : SECURITY_TYPES_EXCEPT_PASSPOINT) {
+            assertEquals(createNetworkKey(mSsid, getSecurityTypeName(securityType),
+                    TEST_PACKAGE_NAME, TEST_CARRIER_ID, TEST_SUB_ID, false),
+                    config.getNetworkKeyFromSecurityType(securityType));
+        }
+
+        // Test for passpoint configuration.
+        config.setPasspointUniqueId(TEST_PASSPOINT_UNIQUE_ID);
+        assertEquals(TEST_SUB_ID + "-" + TEST_PASSPOINT_UNIQUE_ID,
+                config.getNetworkKeyFromSecurityType(SECURITY_TYPE_PASSPOINT_R1_R2));
+    }
+
+    /**
+     * Verifies that getAllPersistableNetworkKeys returns the correct String set
+     * for networks of various different security types, the result should be stable.
+     *
+     * Note: DO NOT update the test if it happens failure! Fixed it is necessary, otherwise
+     * we will break Wi-Fi data usage functionality.
+     */
+    @Test
+    public void testGetAllPersistableNetworkKeysString() {
+        WifiConfiguration config = new WifiConfiguration();
+        final String mSsid = "TestAP";
+        config.SSID = mSsid;
+        config.carrierId = TEST_CARRIER_ID;
+        config.subscriptionId = TEST_SUB_ID;
+        config.creatorName = TEST_PACKAGE_NAME;
+        for (int securityType : SECURITY_TYPES_EXCEPT_PASSPOINT) {
+            config.setSecurityParams(securityType);
+            assertTrue(config.getAllPersistableNetworkKeys().contains(
+                    createNetworkKey(mSsid, getSecurityTypeName(securityType),
+                    TEST_PACKAGE_NAME, TEST_CARRIER_ID, TEST_SUB_ID, false)));
+        }
+
+        // Test with multi-security types
+        config.setSecurityParams(SECURITY_TYPE_PSK);
+        config.addSecurityParams(SECURITY_TYPE_SAE);
+        assertTrue(config.getAllPersistableNetworkKeys().contains(
+                createNetworkKey(mSsid, getSecurityTypeName(SECURITY_TYPE_PSK),
+                TEST_PACKAGE_NAME, TEST_CARRIER_ID, TEST_SUB_ID, false)));
+        assertTrue(config.getAllPersistableNetworkKeys().contains(
+                createNetworkKey(mSsid, getSecurityTypeName(SECURITY_TYPE_SAE),
+                TEST_PACKAGE_NAME, TEST_CARRIER_ID, TEST_SUB_ID, false)));
+        // Test for passpoint configuration.
+        config.setPasspointUniqueId(TEST_PASSPOINT_UNIQUE_ID);
+        assertTrue(config.getAllPersistableNetworkKeys().contains(
+                config.getNetworkKeyFromSecurityType(SECURITY_TYPE_PASSPOINT_R1_R2)));
+    }
 }
diff --git a/framework/tests/src/android/net/wifi/WifiInfoTest.java b/framework/tests/src/android/net/wifi/WifiInfoTest.java
index 7b72fc7..1d40d9f 100644
--- a/framework/tests/src/android/net/wifi/WifiInfoTest.java
+++ b/framework/tests/src/android/net/wifi/WifiInfoTest.java
@@ -62,6 +62,7 @@
     private static final int TEST_NETWORK_ID = 5;
     private static final int TEST_NETWORK_ID2 = 6;
     private static final int TEST_SUB_ID = 1;
+    private static final String TEST_NETWORK_KEY = "TestNetworkKey";
 
     private WifiInfo makeWifiInfoForNoRedactions(
             List<ScanResult.InformationElement> informationElements) {
@@ -182,6 +183,7 @@
         info.setInformationElements(generateIes());
         info.setIsPrimary(true);
         info.setMacAddress(TEST_BSSID);
+        info.setCurrentNetworkKey(TEST_NETWORK_KEY);
         return info;
     }
 
@@ -212,6 +214,7 @@
             assertEquals(TEST_SUB_ID, info.getSubscriptionId());
             assertTrue(info.isPrimary());
         }
+        assertEquals(null, info.getCurrentNetworkKey());
     }
 
     /**
@@ -324,6 +327,7 @@
         info.setProviderFriendlyName(TEST_PROVIDER_NAME);
         info.setInformationElements(generateIes());
         info.setMacAddress(TEST_BSSID);
+        info.setCurrentNetworkKey(TEST_NETWORK_KEY);
         return info;
     }
 
@@ -336,6 +340,7 @@
         assertNull(info.getPasspointProviderFriendlyName());
         assertEquals(WifiInfo.DEFAULT_MAC_ADDRESS, info.getMacAddress());
         assertNull(info.getInformationElements());
+        assertNull(info.getCurrentNetworkKey());
     }
 
     @Test
@@ -475,6 +480,7 @@
             assertEquals(SubscriptionManager.INVALID_SUBSCRIPTION_ID, wifiInfo.getSubscriptionId());
             assertFalse(wifiInfo.isPrimary());
         }
+        assertEquals(null, wifiInfo.getCurrentNetworkKey());
     }
 
     /**
diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java
index 3e22b60..40b71a4 100644
--- a/service/java/com/android/server/wifi/ClientModeImpl.java
+++ b/service/java/com/android/server/wifi/ClientModeImpl.java
@@ -4401,6 +4401,8 @@
                             mPasspointManager.requestVenueUrlAnqpElement(scanResult);
                         }
                     }
+                    mWifiInfo.setCurrentNetworkKey(config.getNetworkKeyFromSecurityType(
+                            mWifiInfo.getCurrentSecurityType()));
                     transitionTo(mL3ProvisioningState);
                     break;
                 }
@@ -4679,6 +4681,9 @@
                                 mWifiInfo.setOsuAp(true);
                             }
                             mWifiInfo.setProviderFriendlyName(config.providerFriendlyName);
+                            mWifiInfo.setCurrentNetworkKey(
+                                    config.getNetworkKeyFromSecurityType(
+                                            mWifiInfo.getCurrentSecurityType()));
                         }
                     }
                     sendNetworkChangeBroadcast(
diff --git a/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java
index f31b8c4..8e6bf77 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java
@@ -1009,6 +1009,7 @@
         }
         assertEquals(Arrays.asList(scanResult.informationElements),
                     wifiInfo.getInformationElements());
+        assertNotNull(wifiInfo.getCurrentNetworkKey());
         expectRegisterNetworkAgent((na) -> {
             if (!mConnectedNetwork.carrierMerged) {
                 assertNull(na.subscriberId);