Cache NetworkScorerAppData in the ScoringServiceConnection.

Store the entire NetworkScorerAppData instance in the
ScoringServiceConnection instead of just most of its fields.

Test: runtest frameworks-services -c com.android.server.NetworkScoreServiceTest
Bug: 34773276
Change-Id: Id2ed7c431dee9895e85e1966903ac919f1704eba
diff --git a/core/java/android/net/NetworkScorerAppManager.java b/core/java/android/net/NetworkScorerAppManager.java
index 9e4dd87..9dcf4f4 100644
--- a/core/java/android/net/NetworkScorerAppManager.java
+++ b/core/java/android/net/NetworkScorerAppManager.java
@@ -18,6 +18,7 @@
 
 import android.Manifest.permission;
 import android.annotation.Nullable;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -33,6 +34,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Internal class for discovering and managing the network scorer/recommendation application.
@@ -53,33 +55,43 @@
      * Holds metadata about a discovered network scorer/recommendation application.
      */
     public static class NetworkScorerAppData {
-        /** Package name of this scorer app. */
-        public final String packageName;
-
         /** UID of the scorer app. */
         public final int packageUid;
+        private final ComponentName mRecommendationService;
 
-        /**
-         * Name of the recommendation service we can bind to.
-         */
-        public final String recommendationServiceClassName;
-
-        public NetworkScorerAppData(String packageName, int packageUid,
-                String recommendationServiceClassName) {
-            this.packageName = packageName;
+        public NetworkScorerAppData(int packageUid, ComponentName recommendationServiceComp) {
             this.packageUid = packageUid;
-            this.recommendationServiceClassName = recommendationServiceClassName;
+            this.mRecommendationService = recommendationServiceComp;
+        }
+
+        public String getRecommendationServicePackageName() {
+            return mRecommendationService.getPackageName();
+        }
+
+        public ComponentName getRecommendationServiceComponent() {
+            return mRecommendationService;
         }
 
         @Override
         public String toString() {
-            final StringBuilder sb = new StringBuilder("NetworkScorerAppData{");
-            sb.append("mPackageName='").append(packageName).append('\'');
-            sb.append(", packageUid=").append(packageUid);
-            sb.append(", recommendationServiceClassName='")
-                    .append(recommendationServiceClassName).append('\'');
-            sb.append('}');
-            return sb.toString();
+            return "NetworkScorerAppData{" +
+                    "packageUid=" + packageUid +
+                    ", mRecommendationService=" + mRecommendationService +
+                    '}';
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            NetworkScorerAppData that = (NetworkScorerAppData) o;
+            return packageUid == that.packageUid &&
+                    Objects.equals(mRecommendationService, that.mRecommendationService);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(packageUid, mRecommendationService);
         }
     }
 
@@ -110,16 +122,16 @@
             return null;
         }
 
-        final PackageManager pm = mContext.getPackageManager();
         for (int i = 0; i < potentialPkgs.size(); i++) {
             final String potentialPkg = potentialPkgs.get(i);
 
             // Look for the recommendation service class and required receiver.
             final ResolveInfo resolveServiceInfo = findRecommendationService(potentialPkg);
             if (resolveServiceInfo != null) {
-                return new NetworkScorerAppData(potentialPkg,
-                    resolveServiceInfo.serviceInfo.applicationInfo.uid,
-                    resolveServiceInfo.serviceInfo.name);
+                final ComponentName componentName =
+                        new ComponentName(potentialPkg, resolveServiceInfo.serviceInfo.name);
+                return new NetworkScorerAppData(resolveServiceInfo.serviceInfo.applicationInfo.uid,
+                        componentName);
             } else {
                 if (DEBUG) {
                     Log.d(TAG, potentialPkg + " does not have the required components, skipping.");
diff --git a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
index 5bfff26..ce5d3ef 100644
--- a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
+++ b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
@@ -19,6 +19,7 @@
 import static org.mockito.Mockito.when;
 
 import android.Manifest.permission;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -30,13 +31,16 @@
 import android.net.NetworkScorerAppManager.NetworkScorerAppData;
 import android.provider.Settings;
 import android.test.InstrumentationTestCase;
+
 import com.android.internal.R;
-import java.util.List;
+
 import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import java.util.List;
+
 public class NetworkScorerAppManagerTest extends InstrumentationTestCase {
     @Mock private Context mMockContext;
     @Mock private PackageManager mMockPm;
@@ -114,39 +118,40 @@
 
     public void testGetNetworkRecommendationProviderData_scoreNetworksNotGranted()
             throws Exception {
-        setNetworkRecommendationPackageNames("package1");
-        mockScoreNetworksDenied("package1");
-        mockRecommendationServiceAvailable("package1", 924 /* packageUid */);
+        final ComponentName recoComponent = new ComponentName("package1", "class1");
+        setNetworkRecommendationPackageNames(recoComponent.getPackageName());
+        mockScoreNetworksDenied(recoComponent.getPackageName());
+        mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */);
 
         assertNull(mNetworkScorerAppManager.getNetworkRecommendationProviderData());
     }
 
     public void testGetNetworkRecommendationProviderData_available() throws Exception {
-        setNetworkRecommendationPackageNames("package1");
-        mockScoreNetworksGranted("package1");
-        mockRecommendationServiceAvailable("package1", 924 /* packageUid */);
+        final ComponentName recoComponent = new ComponentName("package1", "class1");
+        setNetworkRecommendationPackageNames(recoComponent.getPackageName());
+        mockScoreNetworksGranted(recoComponent.getPackageName());
+        mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */);
 
         NetworkScorerAppData appData =
                 mNetworkScorerAppManager.getNetworkRecommendationProviderData();
         assertNotNull(appData);
-        assertEquals("package1", appData.packageName);
+        assertEquals(recoComponent, appData.getRecommendationServiceComponent());
         assertEquals(924, appData.packageUid);
-        assertEquals(".RecommendationService", appData.recommendationServiceClassName);
     }
 
     public void testGetActiveScorer_providerAvailable() throws Exception {
-        setNetworkRecommendationPackageNames("package1");
-        mockScoreNetworksGranted("package1");
-        mockRecommendationServiceAvailable("package1", 924 /* packageUid */);
+        final ComponentName recoComponent = new ComponentName("package1", "class1");
+        setNetworkRecommendationPackageNames(recoComponent.getPackageName());
+        mockScoreNetworksGranted(recoComponent.getPackageName());
+        mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */);
 
         ContentResolver cr = mTargetContext.getContentResolver();
         Settings.Global.putInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 1);
 
         final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
         assertNotNull(activeScorer);
-        assertEquals("package1", activeScorer.packageName);
+        assertEquals(recoComponent, activeScorer.getRecommendationServiceComponent());
         assertEquals(924, activeScorer.packageUid);
-        assertEquals(".RecommendationService", activeScorer.recommendationServiceClassName);
     }
 
     public void testGetActiveScorer_providerNotAvailable()
@@ -159,9 +164,10 @@
     }
 
     public void testGetActiveScorer_recommendationsDisabled() throws Exception {
-        setNetworkRecommendationPackageNames("package1");
-        mockScoreNetworksGranted("package1");
-        mockRecommendationServiceAvailable("package1", 924 /* packageUid */);
+        final ComponentName recoComponent = new ComponentName("package1", "class1");
+        setNetworkRecommendationPackageNames(recoComponent.getPackageName());
+        mockScoreNetworksGranted(recoComponent.getPackageName());
+        mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */);
         ContentResolver cr = mTargetContext.getContentResolver();
         Settings.Global.putInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 0);
 
@@ -187,11 +193,11 @@
                 .thenReturn(PackageManager.PERMISSION_DENIED);
     }
 
-    private void mockRecommendationServiceAvailable(final String packageName, int packageUid) {
+    private void mockRecommendationServiceAvailable(final ComponentName compName, int packageUid) {
         final ResolveInfo serviceInfo = new ResolveInfo();
         serviceInfo.serviceInfo = new ServiceInfo();
-        serviceInfo.serviceInfo.name = ".RecommendationService";
-        serviceInfo.serviceInfo.packageName = packageName;
+        serviceInfo.serviceInfo.name = compName.getClassName();
+        serviceInfo.serviceInfo.packageName = compName.getPackageName();
         serviceInfo.serviceInfo.applicationInfo = new ApplicationInfo();
         serviceInfo.serviceInfo.applicationInfo.uid = packageUid;
 
@@ -203,7 +209,7 @@
                         Intent intent = (Intent) object;
                         return NetworkScoreManager.ACTION_RECOMMEND_NETWORKS
                                 .equals(intent.getAction())
-                                && packageName.equals(intent.getPackage());
+                                && compName.getPackageName().equals(intent.getPackage());
                     }
                 }), Mockito.eq(flags))).thenReturn(serviceInfo);
     }
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 25016f6..abb6a53 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -183,11 +183,12 @@
                 // connection.
                 if (DBG) Log.d(TAG, "No active scorers available.");
                 unbindFromScoringServiceIfNeeded();
-            } else if (activeScorer.packageName.equals(scorerPackageName)) {
+            } else if (activeScorer.getRecommendationServicePackageName().equals(scorerPackageName))
+            {
                 // The active scoring service changed in some way.
                 if (DBG) {
                     Log.d(TAG, "Possible change to the active scorer: "
-                            + activeScorer.packageName);
+                            + activeScorer.getRecommendationServicePackageName());
                 }
                 if (forceUnbind) {
                     unbindFromScoringServiceIfNeeded();
@@ -198,7 +199,8 @@
                 // bound to the correct scoring app. The logic in bindToScoringServiceIfNeeded()
                 // will sort that out to leave us bound to the most recent active scorer.
                 if (DBG) {
-                    Log.d(TAG, "Binding to " + activeScorer.packageName + " if needed.");
+                    Log.d(TAG, "Binding to " + activeScorer.getRecommendationServiceComponent()
+                            + " if needed.");
                 }
                 bindToScoringServiceIfNeeded(activeScorer);
             }
@@ -334,22 +336,19 @@
         bindToScoringServiceIfNeeded(scorerData);
     }
 
-    private void bindToScoringServiceIfNeeded(NetworkScorerAppData scorerData) {
-        if (DBG) Log.d(TAG, "bindToScoringServiceIfNeeded(" + scorerData + ")");
-        if (scorerData != null && scorerData.recommendationServiceClassName != null) {
-            ComponentName componentName = new ComponentName(scorerData.packageName,
-                    scorerData.recommendationServiceClassName);
+    private void bindToScoringServiceIfNeeded(NetworkScorerAppData appData) {
+        if (DBG) Log.d(TAG, "bindToScoringServiceIfNeeded(" + appData + ")");
+        if (appData != null) {
             synchronized (mServiceConnectionLock) {
                 // If we're connected to a different component then drop it.
                 if (mServiceConnection != null
-                        && !mServiceConnection.mComponentName.equals(componentName)) {
+                        && !mServiceConnection.mAppData.equals(appData)) {
                     unbindFromScoringServiceIfNeeded();
                 }
 
                 // If we're not connected at all then create a new connection.
                 if (mServiceConnection == null) {
-                    mServiceConnection = new ScoringServiceConnection(componentName,
-                            scorerData.packageUid);
+                    mServiceConnection = new ScoringServiceConnection(appData);
                 }
 
                 // Make sure the connection is connected (idempotent)
@@ -672,7 +671,8 @@
     @Override
     public boolean isCallerActiveScorer(int callingUid) {
         synchronized (mServiceConnectionLock) {
-            return mServiceConnection != null && mServiceConnection.mScoringAppUid == callingUid;
+            return mServiceConnection != null
+                    && mServiceConnection.mAppData.packageUid == callingUid;
         }
     }
 
@@ -686,7 +686,7 @@
     public String getActiveScorerPackage() {
         synchronized (mServiceConnectionLock) {
             if (mServiceConnection != null) {
-                return mServiceConnection.mComponentName.getPackageName();
+                return mServiceConnection.getPackageName();
             }
         }
         return null;
@@ -881,7 +881,7 @@
                 writer.println("Scoring is disabled.");
                 return;
             }
-            writer.println("Current scorer: " + currentScorer.packageName);
+            writer.println("Current scorer: " + currentScorer);
 
             sendCacheUpdateCallback(new BiConsumer<INetworkScoreCache, Object>() {
                 @Override
@@ -966,21 +966,19 @@
     }
 
     private static class ScoringServiceConnection implements ServiceConnection {
-        private final ComponentName mComponentName;
-        private final int mScoringAppUid;
+        private final NetworkScorerAppData mAppData;
         private volatile boolean mBound = false;
         private volatile boolean mConnected = false;
         private volatile INetworkRecommendationProvider mRecommendationProvider;
 
-        ScoringServiceConnection(ComponentName componentName, int scoringAppUid) {
-            mComponentName = componentName;
-            mScoringAppUid = scoringAppUid;
+        ScoringServiceConnection(NetworkScorerAppData appData) {
+            mAppData = appData;
         }
 
         void connect(Context context) {
             if (!mBound) {
                 Intent service = new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS);
-                service.setComponent(mComponentName);
+                service.setComponent(mAppData.getRecommendationServiceComponent());
                 mBound = context.bindServiceAsUser(service, this,
                         Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
                         UserHandle.SYSTEM);
@@ -1010,6 +1008,10 @@
             return mRecommendationProvider;
         }
 
+        String getPackageName() {
+            return mAppData.getRecommendationServiceComponent().getPackageName();
+        }
+
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             if (DBG) Log.d(TAG, "ScoringServiceConnection: " + name.flattenToString());
@@ -1027,7 +1029,9 @@
         }
 
         public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
-            writer.println("ScoringServiceConnection: " + mComponentName + ", bound: " + mBound
+            writer.println("ScoringServiceConnection: "
+                    + mAppData.getRecommendationServiceComponent()
+                    + ", bound: " + mBound
                     + ", connected: " + mConnected);
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index 152b9c9..fa9e9a8 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -117,6 +117,8 @@
     private static final String SSID = "ssid";
     private static final String SSID_2 = "ssid_2";
     private static final String SSID_3 = "ssid_3";
+    private static final ComponentName RECOMMENDATION_SERVICE_COMP =
+            new ComponentName("newPackageName", "newScoringServiceClass");
     private static final ScoredNetwork SCORED_NETWORK =
             new ScoredNetwork(new NetworkKey(new WifiKey(quote(SSID), "00:00:00:00:00:00")),
                     null /* rssiCurve*/);
@@ -124,7 +126,7 @@
             new ScoredNetwork(new NetworkKey(new WifiKey(quote(SSID_2), "00:00:00:00:00:00")),
                     null /* rssiCurve*/);
     private static final NetworkScorerAppData NEW_SCORER =
-        new NetworkScorerAppData("newPackageName", 1, "newScoringServiceClass");
+        new NetworkScorerAppData(1, RECOMMENDATION_SERVICE_COMP);
 
     @Mock private NetworkScorerAppManager mNetworkScorerAppManager;
     @Mock private Context mContext;
@@ -203,8 +205,7 @@
 
         verify(mContext).bindServiceAsUser(MockUtils.checkIntent(
                 new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS)
-                        .setComponent(new ComponentName(NEW_SCORER.packageName,
-                                NEW_SCORER.recommendationServiceClassName))),
+                        .setComponent(RECOMMENDATION_SERVICE_COMP)),
                 any(ServiceConnection.class),
                 eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
                 eq(UserHandle.SYSTEM));
@@ -657,7 +658,8 @@
         when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
         mNetworkScoreService.systemRunning();
 
-        assertEquals(NEW_SCORER.packageName, mNetworkScoreService.getActiveScorerPackage());
+        assertEquals(NEW_SCORER.getRecommendationServicePackageName(),
+                mNetworkScoreService.getActiveScorerPackage());
     }
 
     @Test
@@ -829,8 +831,6 @@
 
     // "injects" the mock INetworkRecommendationProvider into the NetworkScoreService.
     private void injectProvider() {
-        final ComponentName componentName = new ComponentName(NEW_SCORER.packageName,
-                NEW_SCORER.recommendationServiceClassName);
         when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER);
         when(mContext.bindServiceAsUser(isA(Intent.class), isA(ServiceConnection.class), anyInt(),
                 isA(UserHandle.class))).thenAnswer(new Answer<Boolean>() {
@@ -840,7 +840,8 @@
                 when(mockBinder.queryLocalInterface(anyString()))
                         .thenReturn(mRecommendationProvider);
                 invocation.getArgumentAt(1, ServiceConnection.class)
-                        .onServiceConnected(componentName, mockBinder);
+                        .onServiceConnected(NEW_SCORER.getRecommendationServiceComponent(),
+                                mockBinder);
                 return true;
             }
         });
@@ -849,8 +850,8 @@
 
     private void bindToScorer(boolean callerIsScorer) {
         final int callingUid = callerIsScorer ? Binder.getCallingUid() : 0;
-        NetworkScorerAppData appData = new NetworkScorerAppData(NEW_SCORER.packageName,
-                callingUid, NEW_SCORER.recommendationServiceClassName);
+        NetworkScorerAppData appData =
+                new NetworkScorerAppData(callingUid, RECOMMENDATION_SERVICE_COMP);
         when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(appData);
         when(mContext.bindServiceAsUser(isA(Intent.class), isA(ServiceConnection.class), anyInt(),
                 isA(UserHandle.class))).thenReturn(true);