Merge "Support backward compatibility for restricted network permission" am: 3e8de1cade
am: 5d443532b6

Change-Id: I9b2faf60ff007e2cc08dacd6988fe98377468e7e
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 7b8571c..deaa334 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -23,6 +23,8 @@
 import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
 import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
+import static android.os.Process.INVALID_UID;
+import static android.os.Process.SYSTEM_UID;
 
 import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
@@ -64,6 +66,7 @@
     private static final boolean DBG = true;
     private static final Boolean SYSTEM = Boolean.TRUE;
     private static final Boolean NETWORK = Boolean.FALSE;
+    private static final int VERSION_Q = Build.VERSION_CODES.Q;
 
     private final Context mContext;
     private final PackageManager mPackageManager;
@@ -87,7 +90,7 @@
             public void onReceive(Context context, Intent intent) {
                 String action = intent.getAction();
                 int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
-                int appUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+                int appUid = intent.getIntExtra(Intent.EXTRA_UID, INVALID_UID);
                 Uri appData = intent.getData();
                 String appName = appData != null ? appData.getSchemeSpecificPart() : null;
 
@@ -127,7 +130,7 @@
         }
 
         for (PackageInfo app : apps) {
-            int uid = app.applicationInfo != null ? app.applicationInfo.uid : -1;
+            int uid = app.applicationInfo != null ? app.applicationInfo.uid : INVALID_UID;
             if (uid < 0) {
                 continue;
             }
@@ -162,6 +165,11 @@
     }
 
     @VisibleForTesting
+    int getDeviceFirstSdkInt() {
+        return Build.VERSION.FIRST_SDK_INT;
+    }
+
+    @VisibleForTesting
     boolean hasPermission(PackageInfo app, String permission) {
         if (app.requestedPermissions != null) {
             for (String p : app.requestedPermissions) {
@@ -180,10 +188,17 @@
     private boolean hasRestrictedNetworkPermission(PackageInfo app) {
         // TODO : remove this check in the future(b/31479477). All apps should just
         // request the appropriate permission for their use case since android Q.
-        if (app.applicationInfo != null
-                && app.applicationInfo.targetSdkVersion < Build.VERSION_CODES.Q
-                && isVendorApp(app.applicationInfo)) {
-            return true;
+        if (app.applicationInfo != null) {
+            // Backward compatibility for b/114245686, on devices that launched before Q daemons
+            // and apps running as the system UID are exempted from this check.
+            if (app.applicationInfo.uid == SYSTEM_UID && getDeviceFirstSdkInt() < VERSION_Q) {
+                return true;
+            }
+
+            if (app.applicationInfo.targetSdkVersion < VERSION_Q
+                    && isVendorApp(app.applicationInfo)) {
+                return true;
+            }
         }
         return hasPermission(app, CONNECTIVITY_INTERNAL)
                 || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index 4dc63f2..f12756a 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -25,6 +25,7 @@
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRODUCT;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_VENDOR;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
+import static android.os.Process.SYSTEM_UID;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -55,26 +56,41 @@
     private static final String PARTITION_OEM = "oem";
     private static final String PARTITION_PRODUCT = "product";
     private static final String PARTITION_VENDOR = "vendor";
+    private static final int VERSION_P = Build.VERSION_CODES.P;
+    private static final int VERSION_Q = Build.VERSION_CODES.Q;
 
     @Mock private Context mContext;
     @Mock private PackageManager mPackageManager;
 
     private PermissionMonitor mPermissionMonitor;
+    private int mMockFirstSdkInt;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
-        when(mPackageManager.getPackagesForUid(MOCK_UID)).thenReturn(MOCK_PACKAGE_NAMES);
-        mPermissionMonitor = new PermissionMonitor(mContext, null);
+        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(MOCK_PACKAGE_NAMES);
+        // Try to use spy() here for stubbing getDeviceFirstSdkInt value but the spies are loaded
+        // by a custom class loader that's different from the loader used for loading the real
+        // thing. That means those two classes are not in the same package, so a package private
+        // method is not accessible. Hence, using override method to control FIRST_SDK_INT value
+        // instead of spy function for testing.
+        mPermissionMonitor = new PermissionMonitor(mContext, null) {
+            @Override
+            int getDeviceFirstSdkInt() {
+                return mMockFirstSdkInt;
+            }
+        };
     }
 
-    private void expectPermission(String[] permissions, String partition,
-            int targetSdkVersion) throws Exception {
-        final PackageInfo packageInfo = packageInfoWithPermissions(permissions, partition);
+    private boolean hasBgPermission(String partition, int targetSdkVersion, int uid,
+            String... permission) throws Exception {
+        final PackageInfo packageInfo = packageInfoWithPermissions(permission, partition);
         packageInfo.applicationInfo.targetSdkVersion = targetSdkVersion;
+        packageInfo.applicationInfo.uid = uid;
         when(mPackageManager.getPackageInfoAsUser(
                 eq(MOCK_PACKAGE_NAMES[0]), eq(GET_PERMISSIONS), anyInt())).thenReturn(packageInfo);
+        return mPermissionMonitor.hasUseBackgroundNetworksPermission(uid);
     }
 
     private PackageInfo packageInfoWithPermissions(String[] permissions, String partition) {
@@ -136,51 +152,44 @@
 
     @Test
     public void testHasUseBackgroundNetworksPermission() throws Exception {
-        expectPermission(new String[] { CHANGE_NETWORK_STATE },
-            PARTITION_SYSTEM, Build.VERSION_CODES.P);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] { NETWORK_STACK }, PARTITION_SYSTEM, Build.VERSION_CODES.P);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] { CONNECTIVITY_INTERNAL },
-            PARTITION_SYSTEM, Build.VERSION_CODES.P);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] { CONNECTIVITY_USE_RESTRICTED_NETWORKS },
-            PARTITION_SYSTEM, Build.VERSION_CODES.P);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CHANGE_NETWORK_STATE));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, NETWORK_STACK));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CONNECTIVITY_INTERNAL));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID,
+                CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CHANGE_WIFI_STATE));
 
-        expectPermission(new String[] { CHANGE_NETWORK_STATE },
-            PARTITION_VENDOR, Build.VERSION_CODES.P);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] { NETWORK_STACK },
-            PARTITION_VENDOR, Build.VERSION_CODES.P);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] { CONNECTIVITY_INTERNAL },
-            PARTITION_VENDOR, Build.VERSION_CODES.P);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] { CONNECTIVITY_USE_RESTRICTED_NETWORKS },
-            PARTITION_VENDOR, Build.VERSION_CODES.P);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID, CHANGE_WIFI_STATE));
+    }
 
-        expectPermission(new String[] {}, PARTITION_SYSTEM, Build.VERSION_CODES.P);
-        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] { CHANGE_WIFI_STATE },
-            PARTITION_SYSTEM, Build.VERSION_CODES.P);
-        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] {}, PARTITION_VENDOR, Build.VERSION_CODES.P);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] { CHANGE_WIFI_STATE },
-            PARTITION_VENDOR, Build.VERSION_CODES.P);
-        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+    @Test
+    public void testHasUseBackgroundNetworksPermissionSystemUid() throws Exception {
+        mMockFirstSdkInt = VERSION_P;
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CHANGE_WIFI_STATE));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID,
+                CONNECTIVITY_USE_RESTRICTED_NETWORKS));
 
-        expectPermission(new String[] {}, PARTITION_SYSTEM, Build.VERSION_CODES.Q);
-        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] { CHANGE_WIFI_STATE },
-            PARTITION_SYSTEM, Build.VERSION_CODES.Q);
-        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] {}, PARTITION_VENDOR, Build.VERSION_CODES.Q);
-        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
-        expectPermission(new String[] { CHANGE_WIFI_STATE },
-            PARTITION_VENDOR, Build.VERSION_CODES.Q);
-        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+        mMockFirstSdkInt = VERSION_Q;
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
+        assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CHANGE_WIFI_STATE));
+        assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID,
+                CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+    }
+
+    @Test
+    public void testHasUseBackgroundNetworksPermissionVendorApp() throws Exception {
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CHANGE_NETWORK_STATE));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, NETWORK_STACK));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CONNECTIVITY_INTERNAL));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID,
+                CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+        assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CHANGE_WIFI_STATE));
+
+        assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID));
+        assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID, CHANGE_WIFI_STATE));
     }
 }