[WifiTrackerLib] Do not show SUW or GMS app label in Saved Networks

Networks added by SUW or GMS should not show "Saved by..." in
the Saved Networks page. Add a resource string that specifies packages
that we should not attribution annotations (i.e. "Saved by...") for.

Bug: 191879207
Test: atest com.android.wifitrackerlib.UtilsTest, manually verify that
networks added by SUW do not show "Saved by Android Setup" anymore.

Change-Id: I6debf7e4360afcc2e113882ff3e296aadc469a2b
diff --git a/libs/WifiTrackerLib/res/values/strings.xml b/libs/WifiTrackerLib/res/values/strings.xml
index 7cf49a0..de7a21c 100644
--- a/libs/WifiTrackerLib/res/values/strings.xml
+++ b/libs/WifiTrackerLib/res/values/strings.xml
@@ -222,4 +222,7 @@
 
     <!-- Toast message for when the user selects cellular as the internet provider and Wi-Fi auto-connect is temporarily disabled -->
     <string name="wifitrackerlib_wifi_wont_autoconnect_for_now">Wi-Fi won\u2019t auto-connect for now</string>
+
+    <!-- [DO NOT TRANSLATE] Comma-separated list of packages whose saved networks should show no attribution annotation. -->
+    <string name="wifitrackerlib_no_attribution_annotation_packages">com.google.android.setupwizard,com.google.android.gms</string>
 </resources>
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/PasspointNetworkDetailsTracker.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/PasspointNetworkDetailsTracker.java
index e4b9c80..5e5236f 100644
--- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/PasspointNetworkDetailsTracker.java
+++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/PasspointNetworkDetailsTracker.java
@@ -95,7 +95,7 @@
                                 uniqueIdToPasspointWifiEntryKey(passpointConfig.getUniqueId())))
                         .findAny();
         if (optionalPasspointConfig.isPresent()) {
-            mChosenEntry = new PasspointWifiEntry(mContext, mMainHandler,
+            mChosenEntry = new PasspointWifiEntry(mInjector, mContext, mMainHandler,
                     optionalPasspointConfig.get(), mWifiManager,
                     false /* forSavedNetworksPage */);
         } else {
@@ -107,7 +107,7 @@
                                             uniqueIdToPasspointWifiEntryKey(wifiConfig.getKey())))
                             .findAny();
             if (optionalWifiConfig.isPresent()) {
-                mChosenEntry = new PasspointWifiEntry(mContext, mMainHandler,
+                mChosenEntry = new PasspointWifiEntry(mInjector, mContext, mMainHandler,
                         optionalWifiConfig.get(), mWifiManager,
                         false /* forSavedNetworksPage */);
             } else {
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/PasspointWifiEntry.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/PasspointWifiEntry.java
index ad9d808..5f7a742 100644
--- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/PasspointWifiEntry.java
+++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/PasspointWifiEntry.java
@@ -70,6 +70,7 @@
     @NonNull private final String mKey;
     @NonNull private final String mFqdn;
     @NonNull private final String mFriendlyName;
+    @NonNull private final WifiTrackerInjector mInjector;
     @NonNull private final Context mContext;
     @Nullable
     private PasspointConfiguration mPasspointConfig;
@@ -93,14 +94,16 @@
     /**
      * Create a PasspointWifiEntry with the associated PasspointConfiguration
      */
-    PasspointWifiEntry(@NonNull Context context, @NonNull Handler callbackHandler,
+    PasspointWifiEntry(
+            @NonNull WifiTrackerInjector injector,
+            @NonNull Context context, @NonNull Handler callbackHandler,
             @NonNull PasspointConfiguration passpointConfig,
             @NonNull WifiManager wifiManager,
             boolean forSavedNetworksPage) throws IllegalArgumentException {
         super(callbackHandler, wifiManager, forSavedNetworksPage);
 
         checkNotNull(passpointConfig, "Cannot construct with null PasspointConfiguration!");
-
+        mInjector = injector;
         mContext = context;
         mPasspointConfig = passpointConfig;
         mKey = uniqueIdToPasspointWifiEntryKey(passpointConfig.getUniqueId());
@@ -117,7 +120,9 @@
      * suggestions, since WifiManager#getAllMatchingWifiConfigs() does not provide a corresponding
      * PasspointConfiguration.
      */
-    PasspointWifiEntry(@NonNull Context context, @NonNull Handler callbackHandler,
+    PasspointWifiEntry(
+            @NonNull WifiTrackerInjector injector,
+            @NonNull Context context, @NonNull Handler callbackHandler,
             @NonNull WifiConfiguration wifiConfig,
             @NonNull WifiManager wifiManager,
             boolean forSavedNetworksPage) throws IllegalArgumentException {
@@ -127,7 +132,7 @@
         if (!wifiConfig.isPasspoint()) {
             throw new IllegalArgumentException("Given WifiConfiguration is not for Passpoint!");
         }
-
+        mInjector = injector;
         mContext = context;
         mWifiConfig = wifiConfig;
         mKey = uniqueIdToPasspointWifiEntryKey(wifiConfig.getKey());
@@ -174,7 +179,7 @@
             final @ConnectedState int connectedState = getConnectedState();
             switch (connectedState) {
                 case CONNECTED_STATE_DISCONNECTED:
-                    connectedStateDescription = getDisconnectedDescription(mContext,
+                    connectedStateDescription = getDisconnectedDescription(mInjector, mContext,
                             mWifiConfig,
                             mForSavedNetworksPage,
                             concise);
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/SavedNetworkTracker.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/SavedNetworkTracker.java
index d36dd53..bc52111 100644
--- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/SavedNetworkTracker.java
+++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/SavedNetworkTracker.java
@@ -395,8 +395,9 @@
         // Create new entry for each unmatched config
         for (String key : passpointConfigsByKey.keySet()) {
             mPasspointWifiEntryCache.put(key,
-                    new PasspointWifiEntry(mContext, mMainHandler, passpointConfigsByKey.get(key),
-                            mWifiManager, true /* forSavedNetworksPage */));
+                    new PasspointWifiEntry(mInjector, mContext, mMainHandler,
+                            passpointConfigsByKey.get(key), mWifiManager,
+                            true /* forSavedNetworksPage */));
         }
     }
 
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java
index 0f79fa0..0925938 100644
--- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java
+++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java
@@ -181,7 +181,7 @@
         final @ConnectedState int connectedState = getConnectedState();
         switch (connectedState) {
             case CONNECTED_STATE_DISCONNECTED:
-                connectedStateDescription = getDisconnectedDescription(mContext,
+                connectedStateDescription = getDisconnectedDescription(mInjector, mContext,
                         mTargetWifiConfig,
                         mForSavedNetworksPage,
                         concise);
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/Utils.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/Utils.java
index 171ee45..324ac71 100644
--- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/Utils.java
+++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/Utils.java
@@ -294,11 +294,13 @@
     }
 
 
-    static String getDisconnectedDescription(Context context,
+    static String getDisconnectedDescription(
+            @NonNull WifiTrackerInjector injector,
+            Context context,
             WifiConfiguration wifiConfiguration,
             boolean forSavedNetworksPage,
             boolean concise) {
-        if (context == null) {
+        if (context == null || wifiConfiguration == null) {
             return "";
         }
         final StringJoiner sj = new StringJoiner(context.getString(
@@ -307,24 +309,26 @@
         // For "Saved", "Saved by ...", and "Available via..."
         if (concise) {
             sj.add(context.getString(R.string.wifitrackerlib_wifi_disconnected));
-        } else if (wifiConfiguration != null) {
-            if (forSavedNetworksPage && !wifiConfiguration.isPasspoint()) {
-                final CharSequence appLabel = getAppLabel(context, wifiConfiguration.creatorName);
+        } else if (forSavedNetworksPage && !wifiConfiguration.isPasspoint()) {
+            if (!injector.getNoAttributionAnnotationPackages().contains(
+                    wifiConfiguration.creatorName)) {
+                final CharSequence appLabel = getAppLabel(context,
+                        wifiConfiguration.creatorName);
                 if (!TextUtils.isEmpty(appLabel)) {
                     sj.add(context.getString(R.string.wifitrackerlib_saved_network, appLabel));
                 }
-            } else {
-                if (wifiConfiguration.fromWifiNetworkSuggestion) {
-                    final String suggestionOrSpecifierLabel =
-                            getSuggestionOrSpecifierLabel(context, wifiConfiguration);
-                    if (!TextUtils.isEmpty(suggestionOrSpecifierLabel)) {
-                        sj.add(context.getString(
-                                R.string.wifitrackerlib_available_via_app,
-                                suggestionOrSpecifierLabel));
-                    }
-                } else {
-                    sj.add(context.getString(R.string.wifitrackerlib_wifi_remembered));
+            }
+        } else {
+            if (wifiConfiguration.fromWifiNetworkSuggestion) {
+                final String suggestionOrSpecifierLabel =
+                        getSuggestionOrSpecifierLabel(context, wifiConfiguration);
+                if (!TextUtils.isEmpty(suggestionOrSpecifierLabel)) {
+                    sj.add(context.getString(
+                            R.string.wifitrackerlib_available_via_app,
+                            suggestionOrSpecifierLabel));
                 }
+            } else {
+                sj.add(context.getString(R.string.wifitrackerlib_wifi_remembered));
             }
         }
 
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiPickerTracker.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiPickerTracker.java
index 387aa35..2845079 100644
--- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiPickerTracker.java
+++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiPickerTracker.java
@@ -565,11 +565,11 @@
             // Create PasspointWifiEntry if one doesn't exist for the seen key yet.
             if (!mPasspointWifiEntryCache.containsKey(key)) {
                 if (wifiConfig.fromWifiNetworkSuggestion) {
-                    mPasspointWifiEntryCache.put(key, new PasspointWifiEntry(mContext,
+                    mPasspointWifiEntryCache.put(key, new PasspointWifiEntry(mInjector, mContext,
                             mMainHandler, wifiConfig, mWifiManager,
                             false /* forSavedNetworksPage */));
                 } else if (mPasspointConfigCache.containsKey(key)) {
-                    mPasspointWifiEntryCache.put(key, new PasspointWifiEntry(mContext,
+                    mPasspointWifiEntryCache.put(key, new PasspointWifiEntry(mInjector, mContext,
                             mMainHandler, mPasspointConfigCache.get(key), mWifiManager,
                             false /* forSavedNetworksPage */));
                 } else {
@@ -933,12 +933,12 @@
                 uniqueIdToPasspointWifiEntryKey(cachedWifiConfig.getKey()));
         PasspointWifiEntry connectedEntry;
         if (passpointConfig != null) {
-            connectedEntry = new PasspointWifiEntry(mContext, mMainHandler,
+            connectedEntry = new PasspointWifiEntry(mInjector, mContext, mMainHandler,
                     passpointConfig, mWifiManager,
                     false /* forSavedNetworksPage */);
         } else {
             // Suggested PasspointWifiEntry without a corresponding PasspointConfiguration
-            connectedEntry = new PasspointWifiEntry(mContext, mMainHandler,
+            connectedEntry = new PasspointWifiEntry(mInjector, mContext, mMainHandler,
                     cachedWifiConfig, mWifiManager,
                     false /* forSavedNetworksPage */);
         }
diff --git a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiTrackerInjector.java b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiTrackerInjector.java
index a8aca72..99e7baf 100644
--- a/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiTrackerInjector.java
+++ b/libs/WifiTrackerLib/src/com/android/wifitrackerlib/WifiTrackerInjector.java
@@ -18,9 +18,11 @@
 
 import android.content.Context;
 import android.os.UserManager;
+import android.util.ArraySet;
 
 import androidx.annotation.NonNull;
 
+import java.util.Set;
 
 /**
  * Wrapper class for commonly referenced objects and static data.
@@ -28,11 +30,18 @@
 class WifiTrackerInjector {
     private final boolean mIsDemoMode;
     private final UserManager mUserManager;
+    @NonNull private final Set<String> mNoAttributionAnnotationPackages;
 
     // TODO(b/201571677): Migrate the rest of the common objects to WifiTrackerInjector.
     WifiTrackerInjector(@NonNull Context context) {
         mIsDemoMode = HiddenApiWrapper.isDemoMode(context);
         mUserManager = context.getSystemService(UserManager.class);
+        mNoAttributionAnnotationPackages = new ArraySet<>();
+        String[] noAttributionAnnotationPackages = context.getString(
+                R.string.wifitrackerlib_no_attribution_annotation_packages).split(",");
+        for (int i = 0; i < noAttributionAnnotationPackages.length; i++) {
+            mNoAttributionAnnotationPackages.add(noAttributionAnnotationPackages[i]);
+        }
     }
 
     boolean isDemoMode() {
@@ -42,4 +51,11 @@
     public UserManager getUserManager() {
         return mUserManager;
     }
+
+    /**
+     * Returns the set of package names which we should not show attribution annotations for.
+     */
+    @NonNull Set<String> getNoAttributionAnnotationPackages() {
+        return mNoAttributionAnnotationPackages;
+    }
 }
diff --git a/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/PasspointWifiEntryTest.java b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/PasspointWifiEntryTest.java
index 452f2d9..4df5130 100644
--- a/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/PasspointWifiEntryTest.java
+++ b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/PasspointWifiEntryTest.java
@@ -50,6 +50,7 @@
 import java.util.Arrays;
 
 public class PasspointWifiEntryTest {
+    @Mock private WifiTrackerInjector mMockInjector;
     @Mock private Context mMockContext;
     @Mock private WifiManager mMockWifiManager;
     @Mock private Resources mMockResources;
@@ -92,8 +93,8 @@
         when(mMockContext.getString(R.string.wifitrackerlib_wifi_passpoint_expired))
                 .thenReturn(expired);
 
-        PasspointWifiEntry passpointWifiEntry = new PasspointWifiEntry(mMockContext, mTestHandler,
-                passpointConfiguration, mMockWifiManager,
+        PasspointWifiEntry passpointWifiEntry = new PasspointWifiEntry(mMockInjector, mMockContext,
+                mTestHandler, passpointConfiguration, mMockWifiManager,
                 false /* forSavedNetworksPage */);
 
         assertThat(passpointWifiEntry.getSummary()).isNotEqualTo(expired);
@@ -105,8 +106,8 @@
         String expired = "Expired";
         when(mMockContext.getString(R.string.wifitrackerlib_wifi_passpoint_expired))
                 .thenReturn(expired);
-        PasspointWifiEntry passpointWifiEntry = new PasspointWifiEntry(mMockContext, mTestHandler,
-                passpointConfiguration, mMockWifiManager,
+        PasspointWifiEntry passpointWifiEntry = new PasspointWifiEntry(mMockInjector, mMockContext,
+                mTestHandler, passpointConfiguration, mMockWifiManager,
                 false /* forSavedNetworksPage */);
         PasspointWifiEntry spyEntry = spy(passpointWifiEntry);
         when(spyEntry.isExpired()).thenReturn(true);
@@ -125,7 +126,7 @@
 
     @Test
     public void testGetMeteredChoice_afterSetMeteredChoice_getCorrectValue() {
-        PasspointWifiEntry entry = new PasspointWifiEntry(mMockContext, mTestHandler,
+        PasspointWifiEntry entry = new PasspointWifiEntry(mMockInjector, mMockContext, mTestHandler,
                 getPasspointConfiguration(), mMockWifiManager,
                 false /* forSavedNetworksPage */);
 
@@ -157,7 +158,7 @@
                 new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */, "WIFI", "");
         networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "", "");
 
-        PasspointWifiEntry entry = new PasspointWifiEntry(mMockContext, mTestHandler,
+        PasspointWifiEntry entry = new PasspointWifiEntry(mMockInjector, mMockContext, mTestHandler,
                 getPasspointConfiguration(), mMockWifiManager,
                 false /* forSavedNetworksPage */);
         entry.updateConnectionInfo(wifiInfo, networkInfo);
@@ -196,7 +197,7 @@
                 new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */, "WIFI", "");
         networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "", "");
 
-        PasspointWifiEntry entry = new PasspointWifiEntry(mMockContext, mTestHandler,
+        PasspointWifiEntry entry = new PasspointWifiEntry(mMockInjector, mMockContext, mTestHandler,
                 getPasspointConfiguration(), mMockWifiManager,
                 false /* forSavedNetworksPage */);
         entry.updateConnectionInfo(wifiInfo, networkInfo);
@@ -216,7 +217,7 @@
                 new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */, "WIFI", "");
         networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "", "");
 
-        PasspointWifiEntry entry = new PasspointWifiEntry(mMockContext, mTestHandler,
+        PasspointWifiEntry entry = new PasspointWifiEntry(mMockInjector, mMockContext, mTestHandler,
                 getPasspointConfiguration(), mMockWifiManager,
                 false /* forSavedNetworksPage */);
 
@@ -242,8 +243,8 @@
         when(mMockContext.getString(R.string.wifitrackerlib_wifi_security_passpoint))
                 .thenReturn(passpointSecurity);
 
-        PasspointWifiEntry passpointWifiEntry = new PasspointWifiEntry(mMockContext, mTestHandler,
-                passpointConfiguration, mMockWifiManager,
+        PasspointWifiEntry passpointWifiEntry = new PasspointWifiEntry(mMockInjector, mMockContext,
+                mTestHandler, passpointConfiguration, mMockWifiManager,
                 false /* forSavedNetworksPage */);
 
         assertThat(passpointWifiEntry.getSecurityString(false)).isEqualTo(passpointSecurity);
@@ -263,7 +264,7 @@
                 new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */, "WIFI", "");
         networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "", "");
 
-        PasspointWifiEntry entry = new PasspointWifiEntry(mMockContext, mTestHandler,
+        PasspointWifiEntry entry = new PasspointWifiEntry(mMockInjector, mMockContext, mTestHandler,
                 getPasspointConfiguration(), mMockWifiManager,
                 false /* forSavedNetworksPage */);
 
@@ -307,7 +308,7 @@
         NetworkInfo networkInfo =
                 new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */, "WIFI", "");
         networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "", "");
-        PasspointWifiEntry entry = new PasspointWifiEntry(mMockContext, mTestHandler,
+        PasspointWifiEntry entry = new PasspointWifiEntry(mMockInjector, mMockContext, mTestHandler,
                 getPasspointConfiguration(), mMockWifiManager,
                 false /* forSavedNetworksPage */);
 
@@ -318,7 +319,7 @@
 
     @Test
     public void testIsAutoJoinEnabled_nullConfigs_returnsFalse() {
-        PasspointWifiEntry entry = new PasspointWifiEntry(mMockContext, mTestHandler,
+        PasspointWifiEntry entry = new PasspointWifiEntry(mMockInjector, mMockContext, mTestHandler,
                 getPasspointConfiguration(), mMockWifiManager,
                 false /* forSavedNetworksPage */);
 
@@ -329,7 +330,7 @@
 
     @Test
     public void testCanSignIn_captivePortalCapability_returnsTrue() {
-        PasspointWifiEntry entry = new PasspointWifiEntry(mMockContext, mTestHandler,
+        PasspointWifiEntry entry = new PasspointWifiEntry(mMockInjector, mMockContext, mTestHandler,
                 getPasspointConfiguration(), mMockWifiManager,
                 false /* forSavedNetworksPage */);
 
@@ -344,7 +345,7 @@
     public void testUpdateNetworkCapabilities_userConnect_autoOpenCaptivePortalOnce() {
         when(mMockContext.getSystemService(ConnectivityManager.class))
                 .thenReturn(mMockConnectivityManager);
-        PasspointWifiEntry entry = new PasspointWifiEntry(mMockContext, mTestHandler,
+        PasspointWifiEntry entry = new PasspointWifiEntry(mMockInjector, mMockContext, mTestHandler,
                 getPasspointConfiguration(), mMockWifiManager,
                 false /* forSavedNetworksPage */);
         NetworkCapabilities captivePortalCapabilities = new NetworkCapabilities.Builder()
@@ -391,7 +392,7 @@
         NetworkInfo networkInfo =
                 new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */, "WIFI", "");
         networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "", "");
-        PasspointWifiEntry entry = new PasspointWifiEntry(mMockContext, mTestHandler,
+        PasspointWifiEntry entry = new PasspointWifiEntry(mMockInjector, mMockContext, mTestHandler,
                 getPasspointConfiguration(), mMockWifiManager,
                 false /* forSavedNetworksPage */);
         entry.updateConnectionInfo(wifiInfo, networkInfo);
diff --git a/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/UtilsTest.java b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/UtilsTest.java
index de8afc2..bdca5ba 100644
--- a/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/UtilsTest.java
+++ b/libs/WifiTrackerLib/tests/src/com/android/wifitrackerlib/UtilsTest.java
@@ -36,12 +36,18 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
+import android.content.ContentResolver;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.net.NetworkInfo;
 import android.net.wifi.ScanResult;
@@ -73,6 +79,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 @Config(shadows = {ShadowSystem.class})
 public class UtilsTest {
@@ -91,6 +98,9 @@
     @Mock private WifiTrackerInjector mMockInjector;
     @Mock private Context mMockContext;
     @Mock private Resources mMockResources;
+    @Mock private PackageManager mPackageManager;
+    @Mock private ApplicationInfo mApplicationInfo;
+    @Mock private ContentResolver mContentResolver;
     @Mock private WifiManager mMockWifiManager;
     @Mock private SubscriptionManager mSubscriptionManager;
     @Mock private TelephonyManager mTelephonyManager;
@@ -100,12 +110,13 @@
     private Handler mTestHandler;
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
         TestLooper testLooper = new TestLooper();
         mTestHandler = new Handler(testLooper.getLooper());
         when(mMockContext.getResources()).thenReturn(mMockResources);
+        when(mMockContext.getString(R.string.wifitrackerlib_summary_separator)).thenReturn("/");
         when(mMockContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
                 .thenReturn(mCarrierConfigManager);
         when(mMockContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE))
@@ -113,6 +124,12 @@
         when(mMockContext.getSystemService(Context.TELEPHONY_SERVICE))
                 .thenReturn(mTelephonyManager);
         when(mTelephonyManager.createForSubscriptionId(TEST_CARRIER_ID)).thenReturn(mSpecifiedTm);
+        when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mPackageManager.getApplicationInfo(anyString(), anyInt()))
+                .thenReturn(mApplicationInfo);
+        when(mMockContext.getContentResolver()).thenReturn(mContentResolver);
+        when(mContentResolver.getUserId()).thenReturn(0);
+        when(mMockInjector.getNoAttributionAnnotationPackages()).thenReturn(Collections.emptySet());
     }
 
     @Test
@@ -457,6 +474,31 @@
                 WifiInfo.SECURITY_TYPE_WAPI_CERT);
     }
 
+    @Test
+    public void testDisconnectedDescription_noAttributionAnnotationPackage_returnsEmpty() {
+        String savedByAppLabel = "Saved by app label";
+        String appLabel = "app label";
+        when(mApplicationInfo.loadLabel(any())).thenReturn(appLabel);
+        when(mMockContext.getString(R.string.wifitrackerlib_saved_network, appLabel))
+                .thenReturn(savedByAppLabel);
+        String normalPackage = "normalPackage";
+        String noAttributionPackage = "noAttributionPackage";
+        when(mMockInjector.getNoAttributionAnnotationPackages())
+                .thenReturn(Set.of(noAttributionPackage));
+
+        // Normal package should display the summary "Saved by <app label>" in Saved Networks
+        WifiConfiguration normalConfig = new WifiConfiguration();
+        normalConfig.creatorName = normalPackage;
+        assertThat(Utils.getDisconnectedDescription(
+                mMockInjector, mMockContext, normalConfig, true, false)).isEqualTo(
+                        savedByAppLabel);
+
+        // No-attribution package should display a blank summary in Saved Networks
+        WifiConfiguration noAttributionConfig = new WifiConfiguration();
+        noAttributionConfig.creatorName = noAttributionPackage;
+        assertThat(Utils.getDisconnectedDescription(
+                mMockInjector, mMockContext, noAttributionConfig, true, false)).isEmpty();
+    }
 
     private StandardWifiEntry getStandardWifiEntry(WifiConfiguration config) {
         final StandardWifiEntry entry = new StandardWifiEntry(mMockInjector, mMockContext,