Merge "Add usermanager related perf tests - part2"
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTest.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTest.java
index bd0139f..cdbca63 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTest.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTest.java
@@ -18,7 +18,8 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.IActivityManager;
-import android.app.SynchronousUserSwitchObserver;
+import android.app.IStopUserCallback;
+import android.app.UserSwitchObserver;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -43,18 +44,36 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+/**
+ * Perf tests for user life cycle events.
+ *
+ * Running the tests:
+ * make MultiUserPerfTests &&
+ * adb install -r \
+ *     ${ANDROID_PRODUCT_OUT}/data/app/MultiUserPerfTests/MultiUserPerfTests.apk &&
+ * adb shell am instrument -e class android.multiuser.UserLifecycleTest \
+ *     -w com.android.perftests.multiuser/android.support.test.runner.AndroidJUnitRunner
+ */
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class UserLifecycleTest {
     private final int MIN_REPEAT_TIMES = 4;
 
-    private final int TIMEOUT_REMOVE_USER_SEC = 4;
+    private final int TIMEOUT_REMOVE_USER_MS = 4 * 1000; // 4 sec
     private final int CHECK_USER_REMOVED_INTERVAL_MS = 200; // 0.2 sec
 
     private final int TIMEOUT_USER_START_SEC = 4; // 4 sec
 
     private final int TIMEOUT_USER_SWITCH_SEC = 8; // 8 sec
 
+    private final int TIMEOUT_USER_STOP_SEC = 1; // 1 sec
+
+    private final int TIMEOUT_MANAGED_PROFILE_UNLOCK_SEC = 2; // 2 sec
+
+    private final int TIMEOUT_LOCKED_BOOT_COMPLETE_MS = 5 * 1000; // 5 sec
+
+    private final int TIMEOUT_EPHERMAL_USER_STOP_SEC = 6; // 6 sec
+
     private UserManager mUm;
     private ActivityManager mAm;
     private IActivityManager mIam;
@@ -78,7 +97,11 @@
     @After
     public void tearDown() {
         for (int userId : mUsersToRemove) {
-            mUm.removeUser(userId);
+            try {
+                mUm.removeUser(userId);
+            } catch (Exception e) {
+                // Ignore
+            }
         }
     }
 
@@ -88,14 +111,7 @@
             final UserInfo userInfo = mUm.createUser("TestUser", 0);
 
             final CountDownLatch latch = new CountDownLatch(1);
-            InstrumentationRegistry.getContext().registerReceiverAsUser(new BroadcastReceiver() {
-                @Override
-                public void onReceive(Context context, Intent intent) {
-                    if (Intent.ACTION_USER_STARTED.equals(intent.getAction())) {
-                        latch.countDown();
-                    }
-                }
-            }, UserHandle.ALL, new IntentFilter(Intent.ACTION_USER_STARTED), null, null);
+            registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userInfo.id);
             mIam.startUserInBackground(userInfo.id);
             latch.await(TIMEOUT_USER_START_SEC, TimeUnit.SECONDS);
 
@@ -122,37 +138,163 @@
         }
     }
 
+    @Test
+    public void stopUserPerf() throws Exception {
+        while (mState.keepRunning()) {
+            mState.pauseTiming();
+            final UserInfo userInfo = mUm.createUser("TestUser", 0);
+            final CountDownLatch latch = new CountDownLatch(1);
+            registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userInfo.id);
+            mIam.startUserInBackground(userInfo.id);
+            latch.await(TIMEOUT_USER_START_SEC, TimeUnit.SECONDS);
+            mState.resumeTiming();
+
+            stopUser(userInfo.id);
+
+            mState.pauseTiming();
+            removeUser(userInfo.id);
+            mState.resumeTiming();
+        }
+    }
+
+    @Test
+    public void lockedBootCompletedPerf() throws Exception {
+        while (mState.keepRunning()) {
+            mState.pauseTiming();
+            final int startUser = mAm.getCurrentUser();
+            final UserInfo userInfo = mUm.createUser("TestUser", 0);
+            final CountDownLatch latch = new CountDownLatch(1);
+            registerUserSwitchObserver(null, latch, userInfo.id);
+            mState.resumeTiming();
+
+            mAm.switchUser(userInfo.id);
+            latch.await(TIMEOUT_LOCKED_BOOT_COMPLETE_MS, TimeUnit.SECONDS);
+
+            mState.pauseTiming();
+            switchUser(startUser);
+            removeUser(userInfo.id);
+            mState.resumeTiming();
+        }
+    }
+
+    @Test
+    public void managedProfileUnlockPerf() throws Exception {
+        while (mState.keepRunning()) {
+            mState.pauseTiming();
+            final UserInfo userInfo = mUm.createProfileForUser("TestUser",
+                    UserInfo.FLAG_MANAGED_PROFILE, mAm.getCurrentUser());
+            final CountDownLatch latch = new CountDownLatch(1);
+            registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch, userInfo.id);
+            mState.resumeTiming();
+
+            mIam.startUserInBackground(userInfo.id);
+            latch.await(TIMEOUT_MANAGED_PROFILE_UNLOCK_SEC, TimeUnit.SECONDS);
+
+            mState.pauseTiming();
+            removeUser(userInfo.id);
+            mState.resumeTiming();
+        }
+    }
+
+    @Test
+    public void ephemeralUserStoppedPerf() throws Exception {
+        while (mState.keepRunning()) {
+            mState.pauseTiming();
+            final int startUser = mAm.getCurrentUser();
+            final UserInfo userInfo = mUm.createUser("TestUser",
+                    UserInfo.FLAG_EPHEMERAL | UserInfo.FLAG_DEMO);
+            switchUser(userInfo.id);
+            final CountDownLatch latch = new CountDownLatch(1);
+            InstrumentationRegistry.getContext().registerReceiver(new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    if (Intent.ACTION_USER_STOPPED.equals(intent.getAction()) && intent.getIntExtra(
+                            Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL) == userInfo.id) {
+                        latch.countDown();
+                    }
+                }
+            }, new IntentFilter(Intent.ACTION_USER_STOPPED));
+            final CountDownLatch switchLatch = new CountDownLatch(1);
+            registerUserSwitchObserver(switchLatch, null, startUser);
+            mState.resumeTiming();
+
+            mAm.switchUser(startUser);
+            latch.await(TIMEOUT_EPHERMAL_USER_STOP_SEC, TimeUnit.SECONDS);
+
+            mState.pauseTiming();
+            switchLatch.await(TIMEOUT_USER_SWITCH_SEC, TimeUnit.SECONDS);
+            removeUser(userInfo.id);
+            mState.resumeTiming();
+        }
+    }
+
     private void switchUser(int userId) throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
-        registerUserSwitchObserver(latch);
+        registerUserSwitchObserver(latch, null, userId);
         mAm.switchUser(userId);
         latch.await(TIMEOUT_USER_SWITCH_SEC, TimeUnit.SECONDS);
     }
 
-    private void registerUserSwitchObserver(final CountDownLatch latch) throws Exception {
-        ActivityManagerNative.getDefault().registerUserSwitchObserver(
-                new SynchronousUserSwitchObserver() {
-                    @Override
-                    public void onUserSwitching(int newUserId) throws RemoteException {
-                    }
+    private void stopUser(int userId) throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        mIam.stopUser(userId, false /* force */, new IStopUserCallback.Stub() {
+            @Override
+            public void userStopped(int userId) throws RemoteException {
+                latch.countDown();
+            }
 
+            @Override
+            public void userStopAborted(int userId) throws RemoteException {
+            }
+        });
+        latch.await(TIMEOUT_USER_STOP_SEC, TimeUnit.SECONDS);
+    }
+
+    private void registerUserSwitchObserver(final CountDownLatch switchLatch,
+            final CountDownLatch bootCompleteLatch, final int userId) throws Exception {
+        ActivityManagerNative.getDefault().registerUserSwitchObserver(
+                new UserSwitchObserver() {
                     @Override
                     public void onUserSwitchComplete(int newUserId) throws RemoteException {
-                        latch.countDown();
+                        if (switchLatch != null && userId == newUserId) {
+                            switchLatch.countDown();
+                        }
                     }
 
                     @Override
-                    public void onForegroundProfileSwitch(int newProfileId) throws RemoteException {
+                    public void onLockedBootComplete(int newUserId) {
+                        if (bootCompleteLatch != null && userId == newUserId) {
+                            bootCompleteLatch.countDown();
+                        }
                     }
                 }, "UserLifecycleTest");
     }
 
-    private void removeUser(int userId) throws Exception {
-        mUm.removeUser(userId);
-        final long startTime = System.currentTimeMillis();
-        while (mUm.getUserInfo(userId) != null &&
-                System.currentTimeMillis() - startTime < TIMEOUT_REMOVE_USER_SEC) {
-            Thread.sleep(CHECK_USER_REMOVED_INTERVAL_MS);
+    private void registerBroadcastReceiver(final String action, final CountDownLatch latch,
+            final int userId) {
+        InstrumentationRegistry.getContext().registerReceiverAsUser(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (action.equals(intent.getAction()) && intent.getIntExtra(
+                        Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL) == userId) {
+                    latch.countDown();
+                }
+            }
+        }, UserHandle.of(userId), new IntentFilter(action), null, null);
+    }
+
+    private void removeUser(int userId) {
+        try {
+            mUm.removeUser(userId);
+            final long startTime = System.currentTimeMillis();
+            while (mUm.getUserInfo(userId) != null &&
+                    System.currentTimeMillis() - startTime < TIMEOUT_REMOVE_USER_MS) {
+                Thread.sleep(CHECK_USER_REMOVED_INTERVAL_MS);
+            }
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        } catch (Exception e) {
+            // Ignore
         }
         if (mUm.getUserInfo(userId) != null) {
             mUsersToRemove.add(userId);
diff --git a/core/java/android/app/IUserSwitchObserver.aidl b/core/java/android/app/IUserSwitchObserver.aidl
index caee14f..234da8f 100644
--- a/core/java/android/app/IUserSwitchObserver.aidl
+++ b/core/java/android/app/IUserSwitchObserver.aidl
@@ -23,4 +23,5 @@
     void onUserSwitching(int newUserId, IRemoteCallback reply);
     void onUserSwitchComplete(int newUserId);
     void onForegroundProfileSwitch(int newProfileId);
+    void onLockedBootComplete(int newUserId);
 }
diff --git a/core/java/android/app/SynchronousUserSwitchObserver.java b/core/java/android/app/SynchronousUserSwitchObserver.java
index 6d929f9..3a73888 100644
--- a/core/java/android/app/SynchronousUserSwitchObserver.java
+++ b/core/java/android/app/SynchronousUserSwitchObserver.java
@@ -25,7 +25,7 @@
  *
  * @hide
  */
-public abstract class SynchronousUserSwitchObserver extends IUserSwitchObserver.Stub {
+public abstract class SynchronousUserSwitchObserver extends UserSwitchObserver {
     /**
      * Calls {@link #onUserSwitching(int)} and notifies {@code reply} by calling
      * {@link IRemoteCallback#sendResult(Bundle)}.
diff --git a/core/java/android/app/UserSwitchObserver.java b/core/java/android/app/UserSwitchObserver.java
new file mode 100644
index 0000000..c0f7a4c
--- /dev/null
+++ b/core/java/android/app/UserSwitchObserver.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.app;
+
+import android.os.IRemoteCallback;
+import android.os.RemoteException;
+
+/**
+ * @hide
+ */
+public class UserSwitchObserver extends IUserSwitchObserver.Stub {
+    @Override
+    public void onUserSwitching(int newUserId, IRemoteCallback reply) throws RemoteException {
+        if (reply != null) {
+            reply.sendResult(null);
+        }
+    }
+
+    @Override
+    public void onUserSwitchComplete(int newUserId) throws RemoteException {}
+
+    @Override
+    public void onForegroundProfileSwitch(int newProfileId) throws RemoteException {}
+
+    @Override
+    public void onLockedBootComplete(int newUserId) throws RemoteException {}
+}
\ No newline at end of file
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 66e1b27..e7ecd29 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -29,8 +29,8 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.AlarmManager;
-import android.app.IUserSwitchObserver;
 import android.app.PendingIntent;
+import android.app.UserSwitchObserver;
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.TrustManager;
 import android.content.BroadcastReceiver;
@@ -1071,7 +1071,7 @@
         mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener);
         try {
             ActivityManagerNative.getDefault().registerUserSwitchObserver(
-                    new IUserSwitchObserver.Stub() {
+                    new UserSwitchObserver() {
                         @Override
                         public void onUserSwitching(int newUserId, IRemoteCallback reply) {
                             mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHING,
@@ -1082,10 +1082,6 @@
                             mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCH_COMPLETE,
                                     newUserId, 0));
                         }
-                        @Override
-                        public void onForegroundProfileSwitch(int newProfileId) {
-                            // Ignore.
-                        }
                     }, TAG);
         } catch (RemoteException e) {
             e.rethrowAsRuntimeException();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0e957b9..2f88223 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1526,6 +1526,7 @@
     static final int VR_MODE_APPLY_IF_NEEDED_MSG = 69;
     static final int SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG_MSG = 70;
     static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 71;
+    static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 72;
     static final int START_USER_SWITCH_FG_MSG = 712;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
@@ -2274,6 +2275,9 @@
             case REPORT_USER_SWITCH_COMPLETE_MSG: {
                 mUserController.dispatchUserSwitchComplete(msg.arg1);
             } break;
+            case REPORT_LOCKED_BOOT_COMPLETE_MSG: {
+                mUserController.dispatchLockedBootComplete(msg.arg1);
+            } break;
             case SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG: {
                 IUiAutomationConnection connection = (IUiAutomationConnection) msg.obj;
                 try {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 0407361..ba42d3f 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -32,6 +32,7 @@
 import static com.android.server.am.ActivityManagerService.ALLOW_NON_FULL;
 import static com.android.server.am.ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE;
 import static com.android.server.am.ActivityManagerService.MY_PID;
+import static com.android.server.am.ActivityManagerService.REPORT_LOCKED_BOOT_COMPLETE_MSG;
 import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_COMPLETE_MSG;
 import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_MSG;
 import static com.android.server.am.ActivityManagerService.SYSTEM_USER_CURRENT_MSG;
@@ -259,6 +260,8 @@
                 MetricsLogger.histogram(mInjector.getContext(), "framework_locked_boot_completed",
                     uptimeSeconds);
 
+                mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
+                        userId, 0));
                 Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
                 intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
                 intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
@@ -1053,6 +1056,18 @@
         mUserSwitchObservers.finishBroadcast();
     }
 
+    void dispatchLockedBootComplete(int userId) {
+        final int observerCount = mUserSwitchObservers.beginBroadcast();
+        for (int i = 0; i < observerCount; i++) {
+            try {
+                mUserSwitchObservers.getBroadcastItem(i).onLockedBootComplete(userId);
+            } catch (RemoteException e) {
+                // Ignore
+            }
+        }
+        mUserSwitchObservers.finishBroadcast();
+    }
+
     private void stopBackgroundUsersIfEnforced(int oldUserId) {
         // Never stop system user
         if (oldUserId == UserHandle.USER_SYSTEM) {
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 73c8469..5addffb 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -1068,14 +1068,6 @@
                         mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
                                 .sendToTarget();
                     }
-                    @Override
-                    public void onUserSwitchComplete(int newUserId) throws RemoteException {
-                        // Ignore.
-                    }
-                    @Override
-                    public void onForegroundProfileSwitch(int newProfileId) {
-                        // Ignore.
-                    }
                 }, TAG);
         } catch (RemoteException e) {
             Slog.w(TAG, "Failed to listen for user switching event" ,e);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 3b20fb1..5514551 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -27,10 +27,10 @@
 import android.app.ActivityManagerNative;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
-import android.app.IUserSwitchObserver;
 import android.app.IWallpaperManager;
 import android.app.IWallpaperManagerCallback;
 import android.app.PendingIntent;
+import android.app.UserSwitchObserver;
 import android.app.WallpaperInfo;
 import android.app.WallpaperManager;
 import android.app.admin.DevicePolicyManager;
@@ -933,20 +933,11 @@
 
         try {
             ActivityManagerNative.getDefault().registerUserSwitchObserver(
-                    new IUserSwitchObserver.Stub() {
+                    new UserSwitchObserver() {
                         @Override
                         public void onUserSwitching(int newUserId, IRemoteCallback reply) {
                             switchUser(newUserId, reply);
                         }
-
-                        @Override
-                        public void onUserSwitchComplete(int newUserId) throws RemoteException {
-                        }
-
-                        @Override
-                        public void onForegroundProfileSwitch(int newProfileId) {
-                            // Ignore.
-                        }
                     }, TAG);
         } catch (RemoteException e) {
             e.rethrowAsRuntimeException();