Merge "Add shadow methods for clearing cross-profile app-ops" into rvc-dev am: 53aefd975c

Change-Id: I7b2bb35e196f0fe8419734e552801473a0f71d09
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowCrossProfileAppsTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowCrossProfileAppsTest.java
index 6bc47c3..cae9151 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowCrossProfileAppsTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowCrossProfileAppsTest.java
@@ -1,15 +1,23 @@
 package org.robolectric.shadows;
 
 import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
 import static android.os.Build.VERSION_CODES.P;
 import static android.os.Build.VERSION_CODES.Q;
+import static android.os.Build.VERSION_CODES.R;
 import static com.google.common.truth.Truth.assertThat;
 import static org.junit.Assert.fail;
 import static org.robolectric.Shadows.shadowOf;
 
 import android.app.Application;
+import android.app.AppOpsManager;
 import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.CrossProfileApps;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
 import android.os.Process;
 import android.os.UserHandle;
@@ -28,6 +36,7 @@
   private final Application application = ApplicationProvider.getApplicationContext();
   private final CrossProfileApps crossProfileApps =
       application.getSystemService(CrossProfileApps.class);
+  private final PackageManager packageManager = ((Context) application).getPackageManager();
 
   private final UserHandle userHandle1 = UserHandle.of(10);
   private final UserHandle userHandle2 = UserHandle.of(11);
@@ -339,6 +348,35 @@
     assertThat(shadowOf(crossProfileApps).peekNextStartedActivity()).isNull();
   }
 
+  // BEGIN-INTERNAL
+  @Test
+  @Config(minSdk = R)
+  public void clearInteractAcrossProfilesAppOps_clearsAppOps() {
+    String testPackage1 = "com.example.testpackage1";
+    String testPackage2 = "com.example.testpackage2";
+    shadowOf(packageManager).installPackage(buildTestPackageInfo(testPackage1));
+    shadowOf(packageManager).installPackage(buildTestPackageInfo(testPackage2));
+    shadowOf(crossProfileApps).setInteractAcrossProfilesAppOp(testPackage1, MODE_ALLOWED);
+    shadowOf(crossProfileApps).setInteractAcrossProfilesAppOp(testPackage2, MODE_ALLOWED);
+
+    shadowOf(crossProfileApps).clearInteractAcrossProfilesAppOps();
+
+    assertThat(shadowOf(crossProfileApps).getInteractAcrossProfilesAppOp(testPackage1))
+            .isEqualTo(MODE_DEFAULT);
+    assertThat(shadowOf(crossProfileApps).getInteractAcrossProfilesAppOp(testPackage2))
+            .isEqualTo(MODE_DEFAULT);
+  }
+
+  private PackageInfo buildTestPackageInfo(String packageName) {
+    PackageInfo packageInfo = new PackageInfo();
+    packageInfo.packageName = packageName;
+    packageInfo.applicationInfo = new ApplicationInfo();
+    packageInfo.applicationInfo.packageName = packageName;
+    packageInfo.applicationInfo.name = "test";
+    return packageInfo;
+  }
+  // END-INTERNAL
+
   private static void assertThrowsSecurityException(Runnable runnable) {
     assertThrows(SecurityException.class, runnable);
   }
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowActivityThread.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowActivityThread.java
index 932f78d..f41493e 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowActivityThread.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowActivityThread.java
@@ -6,7 +6,10 @@
 import android.content.ComponentName;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
 import android.os.RemoteException;
+import android.os.UserHandle;
+
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
@@ -74,6 +77,15 @@
               } catch (PackageManager.NameNotFoundException e) {
                 throw new RemoteException(e.getMessage());
               }
+            } else if (method.getName().equals("getInstalledApplications")) {
+              int flags = (Integer) args[0];
+              int userId = (Integer) args[1];
+              return new ParceledListSlice<>(
+                  RuntimeEnvironment.application
+                      .getApplicationContext()
+                      .createContextAsUser(UserHandle.of(userId), /* flags= */ 0)
+                      .getPackageManager()
+                      .getInstalledApplications(flags));
             } else if (method.getName().equals("notifyPackageUse")) {
               return null;
             } else if (method.getName().equals("getPackageInstaller")) {
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowApplicationPackageManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowApplicationPackageManager.java
index 9b7d36d..118d2f6 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowApplicationPackageManager.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowApplicationPackageManager.java
@@ -1107,7 +1107,7 @@
   @Implementation(minSdk = N)
   protected PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
       throws NameNotFoundException {
-    return null;
+    return getPackageInfo(packageName, flags);
   }
 
   @Implementation
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowCrossProfileApps.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowCrossProfileApps.java
index f54ca71..7eb7388 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowCrossProfileApps.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowCrossProfileApps.java
@@ -320,6 +320,23 @@
     }
   }
 
+  // BEGIN-INTERNAL
+  @Implementation(minSdk = R)
+  protected void clearInteractAcrossProfilesAppOps() {
+    findAllPackageNames().forEach(
+        packageName -> setInteractAcrossProfilesAppOp(
+            packageName, AppOpsManager.opToDefaultMode(INTERACT_ACROSS_PROFILES_APPOP)));
+  }
+
+  private List<String> findAllPackageNames() {
+    return context.getPackageManager()
+        .getInstalledApplications(/* flags= */ 0)
+        .stream()
+        .map(applicationInfo -> applicationInfo.packageName)
+        .collect(Collectors.toList());
+  }
+  // END-INTERNAL
+
   @Implementation
   protected boolean canConfigureInteractAcrossProfiles(@NonNull String packageName) {
     return configurableInteractAcrossProfilePackages.contains(packageName);
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUserManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUserManager.java
index 9464c2f..2d8bfee 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUserManager.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUserManager.java
@@ -10,6 +10,7 @@
 import static org.robolectric.shadow.api.Shadow.directlyOn;
 
 import android.Manifest.permission;
+import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -23,9 +24,12 @@
 import com.google.common.collect.HashBiMap;
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
+
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
 import org.robolectric.annotation.RealObject;
@@ -522,6 +526,29 @@
     return true;
   }
 
+  // BEGIN-INTERNAL
+  @Implementation(minSdk = R)
+  protected UserInfo createProfileForUserEvenWhenDisallowed(String name,
+          @NonNull String userType, @UserInfo.UserInfoFlag int flags, @UserIdInt int userId,
+          String[] disallowedPackages) throws UserManager.UserOperationException {
+    List<UserInfo> userIdProfiles = profiles.computeIfAbsent(userId, ignored -> new ArrayList<>());
+    int profileUserId = userIdProfiles.isEmpty() ? 10 : findMaxProfileId(userIdProfiles) + 1;
+    UserInfo profileUserInfo = new UserInfo(profileUserId, name, flags);
+    userIdProfiles.add(profileUserInfo);
+    profileToParent.put(profileUserId, userId);
+    addUserProfile(UserHandle.of(profileUserId));
+    return profileUserInfo;
+  }
+
+  /** Assumes the given list of profile infos is non-empty. */
+  private int findMaxProfileId(List<UserInfo> userIdProfiles) {
+    return Collections.max(
+            userIdProfiles.stream()
+                    .map(userInfo -> userInfo.id)
+                    .collect(Collectors.toList()));
+  }
+  // END-INTERNAL
+
   /**
    * Switches the current user to {@code userHandle}.
    *