Merge "Create whitelisted apps for all users." into qt-dev
diff --git a/car-lib/src/android/car/content/pm/AppBlockingPackageInfo.java b/car-lib/src/android/car/content/pm/AppBlockingPackageInfo.java
index 3c1a356..02610ab 100644
--- a/car-lib/src/android/car/content/pm/AppBlockingPackageInfo.java
+++ b/car-lib/src/android/car/content/pm/AppBlockingPackageInfo.java
@@ -45,7 +45,7 @@
     @IntDef(flag = true,
             value = {FLAG_SYSTEM_APP, FLAG_WHOLE_ACTIVITY})
     @Retention(RetentionPolicy.SOURCE)
-    public @interface ConstrcutorFlags {}
+    public @interface ConstructorFlags {}
 
     /**
      * flags to give additional information on the package.
@@ -82,7 +82,7 @@
 
 
     public AppBlockingPackageInfo(String packageName, int minRevisionCode, int maxRevisionCode,
-            @ConstrcutorFlags int flags, @Nullable Signature[] signatures,
+            @ConstructorFlags int flags, @Nullable Signature[] signatures,
             @Nullable String[] activities) {
         if (packageName == null) {
             throw new IllegalArgumentException("packageName cannot be null");
@@ -156,8 +156,6 @@
         return false;
     }
 
-
-
     @Override
     public int hashCode() {
         final int prime = 31;
diff --git a/service/src/com/android/car/pm/CarAppMetadataReader.java b/service/src/com/android/car/pm/CarAppMetadataReader.java
index 7c1e115..648fded 100644
--- a/service/src/com/android/car/pm/CarAppMetadataReader.java
+++ b/service/src/com/android/car/pm/CarAppMetadataReader.java
@@ -51,18 +51,19 @@
      * @return Array of DO activity names in the given package
      */
     @Nullable
-    public static String[] findDistractionOptimizedActivities(Context context, String packageName)
-            throws NameNotFoundException {
+    public static String[] findDistractionOptimizedActivitiesAsUser(Context context,
+            String packageName, int userId) throws NameNotFoundException {
         final PackageManager pm = context.getPackageManager();
 
         // Check if any of the activities in the package are DO by checking all the
         // <activity> elements.
         PackageInfo pkgInfo =
-                pm.getPackageInfo(
+                pm.getPackageInfoAsUser(
                         packageName, PackageManager.GET_ACTIVITIES
                                 | PackageManager.GET_META_DATA
                                 | PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
+                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                        userId);
         if (pkgInfo == null) {
             return null;
         }
diff --git a/service/src/com/android/car/pm/CarPackageManagerService.java b/service/src/com/android/car/pm/CarPackageManagerService.java
index 08e13e1..7615d28 100644
--- a/service/src/com/android/car/pm/CarPackageManagerService.java
+++ b/service/src/com/android/car/pm/CarPackageManagerService.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.app.ActivityManager.StackInfo;
 import android.car.Car;
 import android.car.content.pm.AppBlockingPackageInfo;
@@ -48,7 +49,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Process;
-import android.text.TextUtils;
+import android.os.UserHandle;
 import android.text.format.DateFormat;
 import android.util.ArraySet;
 import android.util.Log;
@@ -87,6 +88,7 @@
     private final Context mContext;
     private final SystemActivityMonitoringService mSystemActivityMonitoringService;
     private final PackageManager mPackageManager;
+    private final ActivityManager mActivityManager;
 
     private final HandlerThread mHandlerThread;
     private final PackageHandler mHandler;
@@ -110,8 +112,6 @@
     private HashMap<String, AppBlockingPackageInfoWrapper> mActivityWhitelistMap = new HashMap<>();
     // The list corresponding to the one configured in <activityBlacklist>
     @GuardedBy("this")
-    private HashMap<String, AppBlockingPackageInfoWrapper> mActivityBlacklistMap = new HashMap<>();
-    @GuardedBy("this")
     private LinkedList<AppBlockingPolicyProxy> mProxies;
 
     @GuardedBy("this")
@@ -179,6 +179,7 @@
         mCarUxRestrictionsService = uxRestrictionsService;
         mSystemActivityMonitoringService = systemActivityMonitoringService;
         mPackageManager = mContext.getPackageManager();
+        mActivityManager = mContext.getSystemService(ActivityManager.class);
         mUxRestrictionsListener = new UxRestrictionsListener(uxRestrictionsService);
         mHandlerThread = new HandlerThread(CarLog.TAG_PACKAGE);
         mHandlerThread.start();
@@ -318,8 +319,7 @@
                 return wrapper.info;
             }
         }
-        AppBlockingPackageInfoWrapper wrapper = mActivityBlacklistMap.get(packageName);
-        return (wrapper != null) ? wrapper.info : null;
+        return null;
     }
 
     @GuardedBy("this")
@@ -374,7 +374,6 @@
             }
             mHasParsedPackages = false;
             mActivityWhitelistMap.clear();
-            mActivityBlacklistMap.clear();
             mClientPolicies.clear();
             if (mProxies != null) {
                 for (AppBlockingPolicyProxy proxy : mProxies) {
@@ -382,7 +381,7 @@
                 }
                 mProxies.clear();
             }
-            wakeupClientsWaitingForPolicySetitngLocked();
+            wakeupClientsWaitingForPolicySettingLocked();
         }
         mContext.unregisterReceiver(mPackageParsingEventReceiver);
         mContext.unregisterReceiver(mUserSwitchedEventReceiver);
@@ -401,7 +400,8 @@
             pkgParseIntent.addAction(action);
         }
         pkgParseIntent.addDataScheme("package");
-        mContext.registerReceiver(mPackageParsingEventReceiver, pkgParseIntent);
+        mContext.registerReceiverAsUser(mPackageParsingEventReceiver, UserHandle.ALL,
+                pkgParseIntent, null, null);
         try {
             // TODO(128456985): register listener for each display in order to
             // properly launch blocking screens.
@@ -418,8 +418,8 @@
     }
 
     private void doParseInstalledPackages() {
-        generateActivityWhitelistMap();
-        generateActivityBlacklistMap();
+        int userId = mActivityManager.getCurrentUser();
+        generateActivityWhitelistMap(userId);
         synchronized (this) {
             mHasParsedPackages = true;
         }
@@ -432,7 +432,7 @@
     }
 
     @GuardedBy("this")
-    private void wakeupClientsWaitingForPolicySetitngLocked() {
+    private void wakeupClientsWaitingForPolicySettingLocked() {
         for (CarAppBlockingPolicy waitingPolicy : mWaitingPolicies) {
             synchronized (waitingPolicy) {
                 waitingPolicy.notifyAll();
@@ -443,7 +443,7 @@
 
     private void doSetPolicy() {
         synchronized (this) {
-            wakeupClientsWaitingForPolicySetitngLocked();
+            wakeupClientsWaitingForPolicySettingLocked();
         }
         blockTopActivitiesIfNecessary();
     }
@@ -559,12 +559,36 @@
      * Generate a map of whitelisted packages and activities of the form {pkgName, Whitelisted
      * activities}.  The whitelist information can come from a configuration XML resource or from
      * the apps marking their activities as distraction optimized.
+     *
+     * @param userId Generate whitelist based on packages installed for this user.
      */
-    private void generateActivityWhitelistMap() {
-        HashMap<String, AppBlockingPackageInfoWrapper> activityWhitelist = new HashMap<>();
-
+    private void generateActivityWhitelistMap(int userId) {
         // Get the apps/activities that are whitelisted in the configuration XML resources.
-        HashMap<String, Set<String>> configWhitelist = new HashMap<>();
+        Map<String, Set<String>> configWhitelist = generateConfigWhitelist();
+        Map<String, Set<String>> configBlacklist = generateConfigBlacklist();
+
+        Map<String, AppBlockingPackageInfoWrapper> activityWhitelist =
+                generateActivityWhitelistAsUser(UserHandle.USER_SYSTEM,
+                        configWhitelist, configBlacklist);
+        // Also parse packages for current user.
+        if (userId != UserHandle.USER_SYSTEM) {
+            Map<String, AppBlockingPackageInfoWrapper> userWhitelistedPackages =
+                    generateActivityWhitelistAsUser(userId, configWhitelist, configBlacklist);
+            for (String packageName : userWhitelistedPackages.keySet()) {
+                if (activityWhitelist.containsKey(packageName)) {
+                    continue;
+                }
+                activityWhitelist.put(packageName, userWhitelistedPackages.get(packageName));
+            }
+        }
+        synchronized (this) {
+            mActivityWhitelistMap.clear();
+            mActivityWhitelistMap.putAll(activityWhitelist);
+        }
+    }
+
+    private Map<String, Set<String>> generateConfigWhitelist() {
+        Map<String, Set<String>> configWhitelist = new HashMap<>();
         mConfiguredWhitelist = mContext.getString(R.string.activityWhitelist);
         if (mConfiguredWhitelist == null) {
             if (DBG_POLICY_CHECK) {
@@ -572,6 +596,7 @@
             }
         }
         parseConfigList(mConfiguredWhitelist, configWhitelist);
+
         mConfiguredSystemWhitelist = mContext.getString(R.string.systemActivityWhitelist);
         if (mConfiguredSystemWhitelist == null) {
             if (DBG_POLICY_CHECK) {
@@ -588,11 +613,43 @@
             configWhitelist.put(mActivityBlockingActivity.getPackageName(), defaultActivity);
         }
 
-        List<PackageInfo> packages = mPackageManager.getInstalledPackages(
+        return configWhitelist;
+    }
+
+    private Map<String, Set<String>> generateConfigBlacklist() {
+        Map<String, Set<String>> configBlacklist = new HashMap<>();
+        mConfiguredBlacklist = mContext.getString(R.string.activityBlacklist);
+        if (mConfiguredBlacklist == null) {
+            if (DBG_POLICY_CHECK) {
+                Log.d(CarLog.TAG_PACKAGE, "Null blacklist in config");
+            }
+        }
+        parseConfigList(mConfiguredBlacklist, configBlacklist);
+
+        return configBlacklist;
+    }
+
+    /**
+     * Generates whitelisted activities based on packages installed for system user and current
+     * user (if different). Factors affecting whitelist:
+     * - whitelist from resource config;
+     * - activity declared as Distraction Optimized (D.O.) in manifest;
+     * - blacklist from resource config - package/activity blacklisted will not exist
+     *                                    in returned whitelist.
+     *
+     * @param userId Parse packages installed for user.
+     * @param configWhitelist Whitelist from config.
+     * @param configBlacklist Blacklist from config.
+     */
+    private Map<String, AppBlockingPackageInfoWrapper> generateActivityWhitelistAsUser(int userId,
+            Map<String, Set<String>> configWhitelist, Map<String, Set<String>> configBlacklist) {
+        HashMap<String, AppBlockingPackageInfoWrapper> activityWhitelist = new HashMap<>();
+
+        List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
                 PackageManager.GET_SIGNATURES | PackageManager.GET_ACTIVITIES
                         | PackageManager.MATCH_DIRECT_BOOT_AWARE
-                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
-
+                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                userId);
         for (PackageInfo info : packages) {
             if (info.applicationInfo == null) {
                 continue;
@@ -606,8 +663,8 @@
                 flags = AppBlockingPackageInfo.FLAG_SYSTEM_APP;
             }
 
-            /* 1. Check if all or some of this app is in the <activityWhitelist>
-                  in config.xml */
+            /* 1. Check if all or some of this app is in the <activityWhitelist> or
+                  <systemActivityWhitelist> in config.xml */
             Set<String> configActivitiesForPackage = configWhitelist.get(info.packageName);
             if (configActivitiesForPackage != null) {
                 if (DBG_POLICY_CHECK) {
@@ -662,8 +719,9 @@
             }
 
             try {
-                String[] doActivities = CarAppMetadataReader.findDistractionOptimizedActivities(
-                        mContext, info.packageName);
+                String[] doActivities =
+                        CarAppMetadataReader.findDistractionOptimizedActivitiesAsUser(
+                                mContext, info.packageName, userId);
                 if (doActivities != null) {
                     // Some of the activities in this app are Distraction Optimized.
                     if (DBG_POLICY_CHECK) {
@@ -685,6 +743,17 @@
                 continue;
             }
 
+            /* 3. Check if parsed activity is in <activityBlacklist> in config.xml. Anything
+                  in blacklist should not be whitelisted, either as D.O. or by config. */
+            if (configBlacklist.containsKey(info.packageName)) {
+                Set<String> configBlacklistActivities = configBlacklist.get(info.packageName);
+                if (configBlacklistActivities.isEmpty()) {
+                    // Whole package should be blacklisted.
+                    continue;
+                }
+                activities.removeAll(configBlacklistActivities);
+            }
+
             Signature[] signatures;
             signatures = info.signatures;
             AppBlockingPackageInfo appBlockingInfo = new AppBlockingPackageInfo(
@@ -694,10 +763,7 @@
                     appBlockingInfo, true);
             activityWhitelist.put(info.packageName, wrapper);
         }
-        synchronized (this) {
-            mActivityWhitelistMap.clear();
-            mActivityWhitelistMap.putAll(activityWhitelist);
-        }
+        return activityWhitelist;
     }
 
     private boolean isDebugBuild() {
@@ -705,74 +771,6 @@
     }
 
     /**
-     * Generate a map of blacklisted packages and activities of the form {pkgName, Blacklisted
-     * activities}.  The blacklist information comes from a configuration XML resource.
-     */
-    private void generateActivityBlacklistMap() {
-        HashMap<String, AppBlockingPackageInfoWrapper> activityBlacklist = new HashMap<>();
-
-        // Parse blacklisted activities.
-        Map<String, Set<String>> configBlacklist = new HashMap<>();
-        mConfiguredBlacklist = mContext.getString(R.string.activityBlacklist);
-        if (mConfiguredBlacklist == null) {
-            if (DBG_POLICY_CHECK) {
-                Log.d(CarLog.TAG_PACKAGE, "Null blacklist in config");
-            }
-        }
-        parseConfigList(mConfiguredBlacklist, configBlacklist);
-
-        for (String pkg : configBlacklist.keySet()) {
-            if (TextUtils.isEmpty(pkg)) {
-                // This means there is nothing to blacklist
-                Log.d(CarLog.TAG_PACKAGE, "Empty string in blacklist pkg");
-                continue;
-            }
-            int flags = 0;
-            PackageInfo pkgInfo;
-            Set<String> activities = new ArraySet<>();
-            try {
-                pkgInfo = mPackageManager.getPackageInfo(
-                        pkg, PackageManager.GET_ACTIVITIES
-                                | PackageManager.GET_SIGNING_CERTIFICATES
-                                | PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
-            } catch (NameNotFoundException e) {
-                Log.e(CarLog.TAG_PACKAGE, pkg + " not found to blacklist ", e);
-                continue;
-            }
-
-            if (pkgInfo.applicationInfo.isSystemApp()
-                    || pkgInfo.applicationInfo.isUpdatedSystemApp()) {
-                flags |= AppBlockingPackageInfo.FLAG_SYSTEM_APP;
-            }
-            Set<String> configActivities = configBlacklist.get(pkg);
-            if (configActivities.size() == 0) {
-                // whole package
-                flags |= AppBlockingPackageInfo.FLAG_WHOLE_ACTIVITY;
-                List<String> activitiesInPackage = getActivitiesInPackage(pkgInfo);
-                if (activitiesInPackage != null && !activitiesInPackage.isEmpty()) {
-                    activities.addAll(activitiesInPackage);
-                }
-            } else {
-                activities.addAll(configActivities);
-            }
-
-            if (activities.isEmpty()) {
-                continue;
-            }
-            AppBlockingPackageInfo appBlockingInfo = new AppBlockingPackageInfo(pkg, 0, 0, flags,
-                    pkgInfo.signatures, activities.toArray(new String[activities.size()]));
-            AppBlockingPackageInfoWrapper wrapper = new AppBlockingPackageInfoWrapper(
-                    appBlockingInfo, true);
-            activityBlacklist.put(pkg, wrapper);
-        }
-        synchronized (this) {
-            mActivityBlacklistMap.clear();
-            mActivityBlacklistMap.putAll(activityBlacklist);
-        }
-    }
-
-    /**
      * Parses the given resource and updates the input map of packages and activities.
      *
      * Key is package name and value is list of activities. Empty set implies whole package is
@@ -917,10 +915,6 @@
             for (AppBlockingPackageInfoWrapper wrapper : mActivityWhitelistMap.values()) {
                 sb.append(wrapper.toString() + "\n");
             }
-            sb.append("**System Black list**\n");
-            for (AppBlockingPackageInfoWrapper wrapper : mActivityBlacklistMap.values()) {
-                sb.append(wrapper.toString() + "\n");
-            }
         }
         sb.append("**Client Policies**\n");
         for (Entry<String, ClientPolicy> entry : mClientPolicies.entrySet()) {
@@ -943,6 +937,9 @@
         sb.append("**Whitelist string in resource**\n");
         sb.append(mConfiguredWhitelist + "\n");
 
+        sb.append("**System whitelist string in resource**\n");
+        sb.append(mConfiguredSystemWhitelist + "\n");
+
         sb.append("**Blacklist string in resource**\n");
         sb.append(mConfiguredBlacklist + "\n");
 
@@ -1091,7 +1088,8 @@
     @Nullable
     public String[] getDistractionOptimizedActivities(String pkgName) {
         try {
-            return CarAppMetadataReader.findDistractionOptimizedActivities(mContext, pkgName);
+            return CarAppMetadataReader.findDistractionOptimizedActivitiesAsUser(mContext, pkgName,
+                    mActivityManager.getCurrentUser());
         } catch (NameNotFoundException e) {
             return null;
         }
@@ -1154,6 +1152,9 @@
         }
 
         private void requestParsingInstalledPkgs(long delayMs) {
+            // Parse packages for current user.
+            removeMessages(MSG_PARSE_PKG);
+
             Message msg = obtainMessage(MSG_PARSE_PKG);
             if (delayMs == 0) {
                 sendMessage(msg);
@@ -1169,7 +1170,6 @@
                     doHandleInit();
                     break;
                 case MSG_PARSE_PKG:
-                    removeMessages(MSG_PARSE_PKG);
                     doParseInstalledPackages();
                     break;
                 case MSG_SET_POLICY: