OSDN Git Service

Allow network scorers to provide an opt-in activity.
authorJeff Davidson <jpd@google.com>
Fri, 27 Jun 2014 23:24:42 +0000 (16:24 -0700)
committerJeff Davidson <jpd@google.com>
Mon, 30 Jun 2014 21:06:40 +0000 (14:06 -0700)
A candidate scorer may have an activity registered to listen for the
ACTION_CUSTOM_ENABLE intent action. If so, when the user attempts to
select this scorer as the active one, this custom activity will be
launched and will be responsible for requesting that the framework
activate the scorer. This is to enable more complex opt-in flows prior
to becoming the active scorer.

Scorers which do not specify this activity will default to using the
framework switcher (currently ActiveNetworkScorerDialog, though this
may change).

Note that all switches must still go through the framework switcher;
the opt-in activity simply allows scorers to first show another screen
and perform the framework switch on their own terms.

Bug: 15775314
Change-Id: I3847e5d3161a8fcc9622abc03218795697c32778

core/java/android/net/NetworkScoreManager.java
core/java/android/net/NetworkScorerAppManager.java
core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java

index 1a9a637..c09492c 100644 (file)
@@ -81,6 +81,17 @@ public class NetworkScoreManager {
     public static final String ACTION_SCORE_NETWORKS = "android.net.scoring.SCORE_NETWORKS";
 
     /**
+     * Activity action: launch a custom activity for configuring a scorer before enabling it.
+     * Scorer applications may choose to specify an activity for this action, in which case the
+     * framework will launch that activity which should return RESULT_OK if scoring was enabled.
+     *
+     * <p>If no activity is included in a scorer which implements this action, the system dialog for
+     * selecting a scorer will be shown instead.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CUSTOM_ENABLE = "android.net.scoring.CUSTOM_ENABLE";
+
+    /**
      * Extra used with {@link #ACTION_SCORE_NETWORKS} to specify the networks to be scored, as an
      * array of {@link NetworkKey}s. Can be obtained with
      * {@link android.content.Intent#getParcelableArrayExtra(String)}}.
index 7c61710..87a68f7 100644 (file)
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.Manifest.permission;
+import android.annotation.Nullable;
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.Intent;
@@ -54,9 +55,18 @@ public final class NetworkScorerAppManager {
         /** Name of this scorer app for display. */
         public final CharSequence mScorerName;
 
-        public NetworkScorerAppData(String packageName, CharSequence scorerName) {
+        /**
+         * Optional class name of a configuration activity. Null if none is set.
+         *
+         * @see NetworkScoreManager#ACTION_CUSTOM_ENABLE
+         */
+        public final String mConfigurationActivityClassName;
+
+        public NetworkScorerAppData(String packageName, CharSequence scorerName,
+                @Nullable String configurationActivityClassName) {
             mScorerName = scorerName;
             mPackageName = packageName;
+            mConfigurationActivityClassName = configurationActivityClassName;
         }
     }
 
@@ -95,10 +105,23 @@ public final class NetworkScorerAppManager {
                 // approved it as a network scorer.
                 continue;
             }
+
+            // Optionally, this package may specify a configuration activity.
+            String configurationActivityClassName = null;
+            Intent intent = new Intent(NetworkScoreManager.ACTION_CUSTOM_ENABLE);
+            intent.setPackage(receiverInfo.packageName);
+            List<ResolveInfo> configActivities = pm.queryIntentActivities(intent, 0 /* flags */);
+            if (!configActivities.isEmpty()) {
+                ActivityInfo activityInfo = configActivities.get(0).activityInfo;
+                if (activityInfo != null) {
+                    configurationActivityClassName = activityInfo.name;
+                }
+            }
+
             // NOTE: loadLabel will attempt to load the receiver's label and fall back to the app
             // label if none is present.
-            scorers.add(new NetworkScorerAppData(
-                    receiverInfo.packageName, receiverInfo.loadLabel(pm)));
+            scorers.add(new NetworkScorerAppData(receiverInfo.packageName,
+                    receiverInfo.loadLabel(pm), configurationActivityClassName));
         }
 
         return scorers;
index ff2c8f0..f916711 100644 (file)
@@ -25,15 +25,17 @@ import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.net.NetworkScorerAppManager.NetworkScorerAppData;
 import android.test.InstrumentationTestCase;
-
-import com.google.android.collect.Lists;
+import android.util.Pair;
 
 import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
+import java.util.List;
 
 public class NetworkScorerAppManagerTest extends InstrumentationTestCase {
     @Mock private Context mMockContext;
@@ -55,26 +57,60 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase {
 
     public void testGetAllValidScorers() throws Exception {
         // Package 1 - Valid scorer.
-        ResolveInfo package1 = buildResolveInfo("package1", true, true);
+        Pair<ResolveInfo, ResolveInfo> package1 = buildResolveInfo("package1", true, true, false);
 
         // Package 2 - Receiver does not have BROADCAST_SCORE_NETWORKS permission.
-        ResolveInfo package2 = buildResolveInfo("package2", false, true);
+        Pair<ResolveInfo, ResolveInfo> package2 = buildResolveInfo("package2", false, true, false);
 
         // Package 3 - App does not have SCORE_NETWORKS permission.
-        ResolveInfo package3 = buildResolveInfo("package3", true, false);
+        Pair<ResolveInfo, ResolveInfo> package3 = buildResolveInfo("package3", true, false, false);
+
+        // Package 4 - Valid scorer w/ optional config activity.
+        Pair<ResolveInfo, ResolveInfo> package4 = buildResolveInfo("package4", true, true, true);
 
-        setScorers(package1, package2, package3);
+        List<Pair<ResolveInfo, ResolveInfo>> scorers = new ArrayList<>();
+        scorers.add(package1);
+        scorers.add(package2);
+        scorers.add(package3);
+        scorers.add(package4);
+        setScorers(scorers);
 
         Iterator<NetworkScorerAppData> result =
                 NetworkScorerAppManager.getAllValidScorers(mMockContext).iterator();
 
         assertTrue(result.hasNext());
-        assertEquals("package1", result.next().mPackageName);
+        NetworkScorerAppData next = result.next();
+        assertEquals("package1", next.mPackageName);
+        assertNull(next.mConfigurationActivityClassName);
+
+        assertTrue(result.hasNext());
+        next = result.next();
+        assertEquals("package4", next.mPackageName);
+        assertEquals(".ConfigActivity", next.mConfigurationActivityClassName);
 
         assertFalse(result.hasNext());
     }
 
-    private void setScorers(ResolveInfo... scorers) {
+    private void setScorers(List<Pair<ResolveInfo, ResolveInfo>> scorers) {
+        List<ResolveInfo> receivers = new ArrayList<>();
+        for (final Pair<ResolveInfo, ResolveInfo> scorer : scorers) {
+            receivers.add(scorer.first);
+            if (scorer.second != null) {
+                // This scorer has a config activity.
+                Mockito.when(mMockPm.queryIntentActivities(
+                        Mockito.argThat(new ArgumentMatcher<Intent>() {
+                            @Override
+                            public boolean matches(Object object) {
+                                Intent intent = (Intent) object;
+                                return NetworkScoreManager.ACTION_CUSTOM_ENABLE.equals(
+                                        intent.getAction())
+                                        && scorer.first.activityInfo.packageName.equals(
+                                                intent.getPackage());
+                            }
+                        }), Mockito.eq(0))).thenReturn(Collections.singletonList(scorer.second));
+            }
+        }
+
         Mockito.when(mMockPm.queryBroadcastReceivers(
                 Mockito.argThat(new ArgumentMatcher<Intent>() {
                     @Override
@@ -83,11 +119,12 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase {
                         return NetworkScoreManager.ACTION_SCORE_NETWORKS.equals(intent.getAction());
                     }
                 }), Mockito.eq(0)))
-                .thenReturn(Lists.newArrayList(scorers));
+                .thenReturn(receivers);
     }
 
-    private ResolveInfo buildResolveInfo(String packageName,
-            boolean hasReceiverPermission, boolean hasScorePermission) throws Exception {
+    private Pair<ResolveInfo, ResolveInfo> buildResolveInfo(String packageName,
+            boolean hasReceiverPermission, boolean hasScorePermission, boolean hasConfigActivity)
+            throws Exception {
         Mockito.when(mMockPm.checkPermission(permission.SCORE_NETWORKS, packageName))
                 .thenReturn(hasScorePermission ?
                         PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED);
@@ -99,6 +136,13 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase {
         if (hasReceiverPermission) {
             resolveInfo.activityInfo.permission = permission.BROADCAST_SCORE_NETWORKS;
         }
-        return resolveInfo;
+
+        ResolveInfo configActivityInfo = null;
+        if (hasConfigActivity) {
+            configActivityInfo = new ResolveInfo();
+            configActivityInfo.activityInfo = new ActivityInfo();
+            configActivityInfo.activityInfo.name = ".ConfigActivity";
+        }
+        return Pair.create(resolveInfo, configActivityInfo);
     }
 }