Replace user switch broadcast by user callback

Bug: 143712534
Test: atest CarServiceTest CarServiceUnitTest
Change-Id: I0bee05ba4ea6e3d283f08db8d54dee40a8617f6f
diff --git a/service/src/com/android/car/CarMediaService.java b/service/src/com/android/car/CarMediaService.java
index f3c9b79..ce8e328 100644
--- a/service/src/com/android/car/CarMediaService.java
+++ b/service/src/com/android/car/CarMediaService.java
@@ -51,6 +51,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.car.user.CarUserService;
+
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
@@ -86,6 +88,7 @@
     private static final int AUTOPLAY_CONFIG_ADAPTIVE = 2;
 
     private final Context mContext;
+    private final CarUserService mUserService;
     private final UserManager mUserManager;
     private final MediaSessionManager mMediaSessionManager;
     private final MediaSessionUpdater mMediaSessionUpdater = new MediaSessionUpdater();
@@ -148,18 +151,24 @@
         }
     };
 
-    private final BroadcastReceiver mUserSwitchReceiver = new BroadcastReceiver() {
+    private final CarUserService.UserCallback mUserCallback = new CarUserService.UserCallback() {
+
         @Override
-        public void onReceive(Context context, Intent intent) {
-            mCurrentUser = ActivityManager.getCurrentUser();
+        public void onSwitchUser(int userId) {
             if (Log.isLoggable(CarLog.TAG_MEDIA, Log.DEBUG)) {
-                Log.d(CarLog.TAG_MEDIA, "Switched to user " + mCurrentUser);
+                Log.d(CarLog.TAG_MEDIA, "Switched to user " + userId);
             }
             maybeInitUser();
         }
+
+        @Override
+        public void onUserLockChanged(int userId, boolean unlocked) {
+            // Do Nothing
+        }
+
     };
 
-    public CarMediaService(Context context) {
+    public CarMediaService(Context context, CarUserService userService) {
         mContext = context;
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class);
@@ -173,10 +182,8 @@
         mPackageUpdateFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
         mPackageUpdateFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
         mPackageUpdateFilter.addDataScheme("package");
-
-        IntentFilter userSwitchFilter = new IntentFilter();
-        userSwitchFilter.addAction(Intent.ACTION_USER_SWITCHED);
-        mContext.registerReceiver(mUserSwitchReceiver, userSwitchFilter);
+        mUserService = userService;
+        mUserService.addUserCallback(mUserCallback);
 
         mPlayOnMediaSourceChangedConfig =
                 mContext.getResources().getInteger(R.integer.config_mediaSourceChangedAutoplay);
@@ -219,8 +226,8 @@
                 mPackageUpdateFilter, null, null);
         mIsPackageUpdateReceiverRegistered = true;
 
-        mPrimaryMediaComponent =
-                isCurrentUserEphemeral() ? getDefaultMediaSource() : getLastMediaSource();
+        mPrimaryMediaComponent = isCurrentUserEphemeral() ? getDefaultMediaSource()
+                : getLastMediaSource();
         mActiveUserMediaController = null;
 
         updateMediaSessionCallbackForCurrentUser();
@@ -267,6 +274,7 @@
     @Override
     public void release() {
         mMediaSessionUpdater.unregisterCallbacks();
+        mUserService.removeUserCallback(mUserCallback);
     }
 
     @Override
@@ -282,9 +290,8 @@
             writer.println(
                     "\tCurrent browse service extra: " + getClassName(mActiveUserMediaController));
         }
-        writer.println("\tNumber of active media sessions: "
-                + mMediaSessionManager.getActiveSessionsForUser(null,
-                ActivityManager.getCurrentUser()).size());
+        writer.println("\tNumber of active media sessions: " + mMediaSessionManager
+                .getActiveSessionsForUser(null, ActivityManager.getCurrentUser()).size());
     }
 
     /**
@@ -674,8 +681,7 @@
             Deque<String> componentNames = getComponentNameList(serialized);
             componentNames.remove(componentName);
             componentNames.addFirst(componentName);
-            mSharedPrefs.edit().putString(key, serializeComponentNameList(componentNames))
-                    .apply();
+            mSharedPrefs.edit().putString(key, serializeComponentNameList(componentNames)).apply();
         }
     }
 
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index 89f14e1..f81f614 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -188,8 +188,9 @@
                 mCarDrivingStateService, mCarPropertyService);
         mCarPackageManagerService = new CarPackageManagerService(serviceContext,
                 mCarUXRestrictionsService,
-                mSystemActivityMonitoringService);
-        mPerUserCarServiceHelper = new PerUserCarServiceHelper(serviceContext);
+                mSystemActivityMonitoringService,
+                mCarUserService);
+        mPerUserCarServiceHelper = new PerUserCarServiceHelper(serviceContext, mCarUserService);
         mCarBluetoothService = new CarBluetoothService(serviceContext, mPerUserCarServiceHelper);
         mCarInputService = new CarInputService(serviceContext, mHal.getInputHal());
         mCarProjectionService = new CarProjectionService(
@@ -224,7 +225,7 @@
                 new CarConfigurationService(serviceContext, new JsonReaderImpl());
         mCarLocationService = new CarLocationService(serviceContext);
         mCarTrustedDeviceService = new CarTrustedDeviceService(serviceContext);
-        mCarMediaService = new CarMediaService(serviceContext);
+        mCarMediaService = new CarMediaService(serviceContext, mCarUserService);
         mCarBugreportManagerService = new CarBugreportManagerService(serviceContext);
         if (!Build.IS_USER) {
             mCarExperimentalFeatureServiceController = new CarExperimentalFeatureServiceController(
diff --git a/service/src/com/android/car/PerUserCarServiceHelper.java b/service/src/com/android/car/PerUserCarServiceHelper.java
index c72223f..a4148aa 100644
--- a/service/src/com/android/car/PerUserCarServiceHelper.java
+++ b/service/src/com/android/car/PerUserCarServiceHelper.java
@@ -17,16 +17,15 @@
 package com.android.car;
 
 import android.car.IPerUserCarService;
-import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.os.IBinder;
 import android.os.UserHandle;
 import android.util.Log;
 
+import com.android.car.user.CarUserService;
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.PrintWriter;
@@ -42,22 +41,21 @@
 public class PerUserCarServiceHelper implements CarServiceBase {
     private static final String TAG = "PerUserCarSvcHelper";
     private static boolean DBG = false;
-    private Context mContext;
+    private final Context mContext;
+    private final CarUserService mUserService;
     private IPerUserCarService mPerUserCarService;
     // listener to call on a ServiceConnection to PerUserCarService
     private List<ServiceCallback> mServiceCallbacks;
-    private UserSwitchBroadcastReceiver mReceiver;
-    private IntentFilter mUserSwitchFilter;
     private static final String EXTRA_USER_HANDLE = "android.intent.extra.user_handle";
     private final Object mServiceBindLock = new Object();
     @GuardedBy("mServiceBindLock")
     private boolean mBound = false;
 
-    public PerUserCarServiceHelper(Context context) {
+    public PerUserCarServiceHelper(Context context, CarUserService userService) {
         mContext = context;
         mServiceCallbacks = new ArrayList<>();
-        mReceiver = new UserSwitchBroadcastReceiver();
-        setupUserSwitchListener();
+        mUserService = userService;
+        mUserService.addUserCallback(mUserCallback);
     }
 
     @Override
@@ -71,39 +69,24 @@
     public void release() {
         synchronized (mServiceBindLock) {
             unbindFromPerUserCarService();
+            mUserService.removeUserCallback(mUserCallback);
         }
     }
 
-    /**
-     * Setting up the intent filter for
-     * 2. UserSwitch events
-     */
-    private void setupUserSwitchListener() {
-        mUserSwitchFilter = new IntentFilter();
-        mUserSwitchFilter.addAction(Intent.ACTION_USER_SWITCHED);
-        mContext.registerReceiver(mReceiver, mUserSwitchFilter);
-        if (DBG) {
-            Log.d(TAG, "UserSwitch Listener Registered");
-        }
-    }
+    private final CarUserService.UserCallback mUserCallback = new CarUserService.UserCallback() {
 
-    /**
-     * UserSwitchBroadcastReceiver receives broadcasts on User account switches.
-     */
-    public class UserSwitchBroadcastReceiver extends BroadcastReceiver {
         @Override
-        public void onReceive(Context context, Intent intent) {
+        public void onUserLockChanged(int userId, boolean unlocked) {
+            // Do Nothing
+        }
+
+        @Override
+        public void onSwitchUser(int userId) {
             List<ServiceCallback> callbacks;
             if (DBG) {
-                Log.d(TAG, "User Switch Happened");
-                boolean userSwitched = intent.getAction().equals(
-                        Intent.ACTION_USER_SWITCHED);
-
-                int user = intent.getExtras().getInt(EXTRA_USER_HANDLE);
-                if (userSwitched) {
-                    Log.d(TAG, "New User " + user);
-                }
+                Log.d(TAG, "User Switch Happened. New User" + userId);
             }
+
             // Before unbinding, notify the callbacks about unbinding from the service
             // so the callbacks can clean up their state through the binder before the service is
             // killed.
@@ -120,7 +103,7 @@
             // bind to the service running as the new user
             bindToPerUserCarService();
         }
-    }
+    };
 
     /**
      * ServiceConnection to detect connecting/disconnecting to {@link PerUserCarService}
diff --git a/service/src/com/android/car/pm/CarPackageManagerService.java b/service/src/com/android/car/pm/CarPackageManagerService.java
index 085576b..9d8274f 100644
--- a/service/src/com/android/car/pm/CarPackageManagerService.java
+++ b/service/src/com/android/car/pm/CarPackageManagerService.java
@@ -65,6 +65,7 @@
 import com.android.car.R;
 import com.android.car.SystemActivityMonitoringService;
 import com.android.car.SystemActivityMonitoringService.TopTaskInfoContainer;
+import com.android.car.user.CarUserService;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -90,6 +91,7 @@
     private static final int LOG_SIZE = 20;
 
     private final Context mContext;
+    private final CarUserService mUserService;
     private final SystemActivityMonitoringService mSystemActivityMonitoringService;
     private final PackageManager mPackageManager;
     private final ActivityManager mActivityManager;
@@ -143,8 +145,6 @@
 
     private final PackageParsingEventReceiver mPackageParsingEventReceiver =
             new PackageParsingEventReceiver();
-    private final UserSwitchedEventReceiver mUserSwitchedEventReceiver =
-            new UserSwitchedEventReceiver();
 
     // To track if the packages have been parsed for building white/black lists. If we haven't had
     // received any intents (boot complete or package changed), then the white list is null leading
@@ -186,8 +186,10 @@
 
     public CarPackageManagerService(Context context,
             CarUxRestrictionsManagerService uxRestrictionsService,
-            SystemActivityMonitoringService systemActivityMonitoringService) {
+            SystemActivityMonitoringService systemActivityMonitoringService,
+            CarUserService userService) {
         mContext = context;
+        mUserService = userService;
         mCarUxRestrictionsService = uxRestrictionsService;
         mSystemActivityMonitoringService = systemActivityMonitoringService;
         mPackageManager = mContext.getPackageManager();
@@ -407,7 +409,7 @@
             mLock.notifyAll();
         }
         mContext.unregisterReceiver(mPackageParsingEventReceiver);
-        mContext.unregisterReceiver(mUserSwitchedEventReceiver);
+        mUserService.removeUserCallback(mUserCallback);
         mSystemActivityMonitoringService.registerActivityLaunchListener(null);
         for (int i = 0; i < mUxRestrictionsListeners.size(); i++) {
             UxRestrictionsListener listener = mUxRestrictionsListeners.valueAt(i);
@@ -415,12 +417,24 @@
         }
     }
 
+    private final CarUserService.UserCallback mUserCallback = new CarUserService.UserCallback() {
+
+        @Override
+        public void onUserLockChanged(int userId, boolean unlocked) {
+            // Do Nothing
+        }
+
+        @Override
+        public void onSwitchUser(int userId) {
+            mHandler.requestParsingInstalledPkgs(0);
+        }
+
+    };
+
     // run from HandlerThread
     private void doHandleInit() {
         startAppBlockingPolicies();
-        IntentFilter intent = new IntentFilter();
-        intent.addAction(Intent.ACTION_USER_SWITCHED);
-        mContext.registerReceiver(mUserSwitchedEventReceiver, intent);
+        mUserService.addUserCallback(mUserCallback);
         IntentFilter pkgParseIntent = new IntentFilter();
         for (String action : mPackageManagerActions) {
             pkgParseIntent.addAction(action);
@@ -666,11 +680,10 @@
             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,
-                userId);
+        List<PackageInfo> packages = mPackageManager
+                .getInstalledPackagesAsUser(PackageManager.GET_SIGNATURES
+                        | PackageManager.GET_ACTIVITIES | PackageManager.MATCH_DIRECT_BOOT_AWARE
+                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
         for (PackageInfo info : packages) {
             if (info.applicationInfo == null) {
                 continue;
@@ -747,9 +760,8 @@
                     // Some of the activities in this app are Distraction Optimized.
                     if (DBG_POLICY_CHECK) {
                         for (String activity : doActivities) {
-                            Log.d(CarLog.TAG_PACKAGE,
-                                    "adding " + activity + " from " + info.packageName
-                                            + " to whitelist");
+                            Log.d(CarLog.TAG_PACKAGE, "adding " + activity + " from "
+                                    + info.packageName + " to whitelist");
                         }
                     }
                     activities.addAll(Arrays.asList(doActivities));
@@ -1135,7 +1147,7 @@
                 != PackageManager.SIGNATURE_MATCH) {
             throw new SecurityException(
                     "Caller " + mPackageManager.getNameForUid(Binder.getCallingUid())
-                            + " does not have the right signature");
+                    + " does not have the right signature");
         }
         mCarUxRestrictionsService.setUxRChangeBroadcastEnabled(enable);
     }
@@ -1404,21 +1416,6 @@
     }
 
     /**
-     * Listens to the Boot intent to initiate parsing installed packages.
-     */
-    private class UserSwitchedEventReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (intent == null || intent.getAction() == null) {
-                return;
-            }
-            if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
-                mHandler.requestParsingInstalledPkgs(0);
-            }
-        }
-    }
-
-    /**
      * Listens to the package install/uninstall events to know when to initiate parsing
      * installed packages.
      */
diff --git a/service/src/com/android/car/user/CarUserService.java b/service/src/com/android/car/user/CarUserService.java
index 610a056..5a541e3 100644
--- a/service/src/com/android/car/user/CarUserService.java
+++ b/service/src/com/android/car/user/CarUserService.java
@@ -36,10 +36,12 @@
 import android.location.LocationManager;
 import android.os.Binder;
 import android.os.RemoteException;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.util.Log;
+import android.util.TimingsTraceLog;
 
 import com.android.car.CarServiceBase;
 import com.android.car.R;
@@ -83,7 +85,7 @@
     private @UserIdInt int mLastPassengerId;
     /**
      * Background users that will be restarted in garage mode. This list can include the
-     * current foreground user bit the current foreground user should not be restarted.
+     * current foreground user but the current foreground user should not be restarted.
      */
     @GuardedBy("mLockUser")
     private final ArrayList<Integer> mBackgroundUsersToRestart = new ArrayList<>();
@@ -461,12 +463,23 @@
      * @param unlocked Unlocked (={@code true}) or locked (={@code false}).
      */
     public void setUserLockStatus(@UserIdInt int userId, boolean unlocked) {
+        TimingsTraceLog t = new TimingsTraceLog(TAG_USER,
+                Trace.TRACE_TAG_SYSTEM_SERVER);
+        t.traceBegin("onUserLockChanged-" + userId
+                + (unlocked ? "-unlocked" : "-locked"));
         for (UserCallback callback : mUserCallbacks) {
+            t.traceBegin("onUserLockChanged-"
+                    + callback.getClass().getSimpleName());
             callback.onUserLockChanged(userId, unlocked);
+            t.traceEnd();
         }
+        t.traceEnd();
+
         if (!unlocked) { // nothing else to do when it is locked back.
             return;
         }
+
+        t.traceBegin("setUserLockStatus-UnlockTasks-" + userId);
         ArrayList<Runnable> tasks = null;
         synchronized (mLockUser) {
             if (userId == UserHandle.USER_SYSTEM) {
@@ -502,6 +515,7 @@
                 r.run();
             }
         }
+        t.traceEnd();
     }
 
     /**
@@ -594,6 +608,9 @@
      * @param userId User id of new user.
      */
     public void onSwitchUser(@UserIdInt int userId) {
+        TimingsTraceLog t = new TimingsTraceLog(TAG_USER,
+                Trace.TRACE_TAG_SYSTEM_SERVER);
+        t.traceBegin("onSwitchUser-" + userId);
         if (!isSystemUser(userId)) {
             mCarUserManagerHelper.setLastActiveUser(userId);
         }
@@ -605,8 +622,11 @@
             startFirstPassenger(userId);
         }
         for (UserCallback callback : mUserCallbacks) {
+            t.traceBegin("onSwitchUser-" + callback.getClass().getSimpleName());
             callback.onSwitchUser(userId);
+            t.traceEnd();
         }
+        t.traceEnd();
     }
 
     /**