diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 0923caaf0d761..50d2dd16b407f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -187,9 +187,13 @@ public abstract class SystemUIDefaultModule {
return new Recents(context, recentsImplementation, commandQueue);
}
- @Binds
- abstract DeviceProvisionedController bindDeviceProvisionedController(
- DeviceProvisionedControllerImpl deviceProvisionedController);
+ @SysUISingleton
+ @Provides
+ static DeviceProvisionedController bindDeviceProvisionedController(
+ DeviceProvisionedControllerImpl deviceProvisionedController) {
+ deviceProvisionedController.init();
+ return deviceProvisionedController;
+ }
@Binds
abstract KeyguardViewController bindKeyguardViewController(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index d9b0ee7e99212..f72178f0c8b05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -332,8 +332,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
deviceProvisionedController.addCallback(new DeviceProvisionedListener() {
@Override
public void onUserSetupChanged() {
- setUserSetupComplete(deviceProvisionedController.isUserSetup(
- deviceProvisionedController.getCurrentUser()));
+ setUserSetupComplete(deviceProvisionedController.isCurrentUserSetup());
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 7b110a0ec01ff..d19ed28bd823d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -604,8 +604,7 @@ public class PhoneStatusBarPolicy
@Override
public void onUserSetupChanged() {
- boolean userSetup = mProvisionedController.isUserSetup(
- mProvisionedController.getCurrentUser());
+ boolean userSetup = mProvisionedController.isCurrentUserSetup();
if (mCurrentUserSetup == userSetup) return;
mCurrentUserSetup = userSetup;
updateAlarm();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index caa0dbe1a2c9a..2e5a9076aa8bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -4340,10 +4340,9 @@ public class StatusBar extends SystemUI implements
private final DeviceProvisionedListener mUserSetupObserver = new DeviceProvisionedListener() {
@Override
public void onUserSetupChanged() {
- final boolean userSetup = mDeviceProvisionedController.isUserSetup(
- mDeviceProvisionedController.getCurrentUser());
- Log.d(TAG, "mUserSetupObserver - DeviceProvisionedListener called for user "
- + mDeviceProvisionedController.getCurrentUser());
+ final boolean userSetup = mDeviceProvisionedController.isCurrentUserSetup();
+ Log.d(TAG, "mUserSetupObserver - DeviceProvisionedListener called for "
+ + "current user");
if (MULTIUSER_DEBUG) {
Log.d(TAG, String.format("User setup changed: userSetup=%s mUserSetup=%s",
userSetup, mUserSetup));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
index 7b4c35a8d25ab..3944c8c77f499 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
@@ -14,23 +14,60 @@
package com.android.systemui.statusbar.policy;
+import android.provider.Settings;
+
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
+/**
+ * Controller to cache in process the state of the device provisioning.
+ *
+ * This controller keeps track of the values of device provisioning and user setup complete
+ */
public interface DeviceProvisionedController extends CallbackController {
+ /**
+ * @return whether the device is provisioned
+ * @see Settings.Global#DEVICE_PROVISIONED
+ */
boolean isDeviceProvisioned();
- boolean isUserSetup(int currentUser);
+
+ /**
+ * @deprecated use {@link com.android.systemui.settings.UserTracker}
+ */
+ @Deprecated
int getCurrentUser();
- default boolean isCurrentUserSetup() {
- return isUserSetup(getCurrentUser());
- }
+ /**
+ * @param user the user to query
+ * @return whether that user has completed the user setup
+ * @see Settings.Secure#USER_SETUP_COMPLETE
+ */
+ boolean isUserSetup(int user);
+ /**
+ * @see DeviceProvisionedController#isUserSetup
+ */
+ boolean isCurrentUserSetup();
+
+ /**
+ * Interface to provide calls when the values tracked change
+ */
interface DeviceProvisionedListener {
+ /**
+ * Call when the device changes from not provisioned to provisioned
+ */
default void onDeviceProvisionedChanged() { }
+
+ /**
+ * Call on user switched
+ */
default void onUserSwitched() {
onUserSetupChanged();
}
+
+ /**
+ * Call when some user changes from not provisioned to provisioned
+ */
default void onUserSetupChanged() { }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
deleted file mode 100644
index 485b1b109eb4b..0000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2017 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 com.android.systemui.statusbar.policy;
-
-import android.app.ActivityManager;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.provider.Settings.Global;
-import android.provider.Settings.Secure;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.settings.CurrentUserTracker;
-import com.android.systemui.util.settings.GlobalSettings;
-import com.android.systemui.util.settings.SecureSettings;
-
-import java.util.ArrayList;
-
-import javax.inject.Inject;
-
-/**
- */
-@SysUISingleton
-public class DeviceProvisionedControllerImpl extends CurrentUserTracker implements
- DeviceProvisionedController {
-
- protected static final String TAG = DeviceProvisionedControllerImpl.class.getSimpleName();
- protected final ArrayList mListeners = new ArrayList<>();
- private final GlobalSettings mGlobalSettings;
- private final SecureSettings mSecureSettings;
- private final Uri mDeviceProvisionedUri;
- private final Uri mUserSetupUri;
- protected final ContentObserver mSettingsObserver;
-
- /**
- */
- @Inject
- public DeviceProvisionedControllerImpl(@Main Handler mainHandler,
- BroadcastDispatcher broadcastDispatcher, GlobalSettings globalSettings,
- SecureSettings secureSettings) {
- super(broadcastDispatcher);
- mGlobalSettings = globalSettings;
- mSecureSettings = secureSettings;
- mDeviceProvisionedUri = mGlobalSettings.getUriFor(Global.DEVICE_PROVISIONED);
- mUserSetupUri = mSecureSettings.getUriFor(Secure.USER_SETUP_COMPLETE);
- mSettingsObserver = new ContentObserver(mainHandler) {
- @Override
- public void onChange(boolean selfChange, Uri uri, int flags) {
- Log.d(TAG, "Setting change: " + uri);
- if (mUserSetupUri.equals(uri)) {
- notifySetupChanged();
- } else {
- notifyProvisionedChanged();
- }
- }
- };
- }
-
- @Override
- public boolean isDeviceProvisioned() {
- return mGlobalSettings.getInt(Global.DEVICE_PROVISIONED, 0) != 0;
- }
-
- @Override
- public boolean isUserSetup(int currentUser) {
- return mSecureSettings.getIntForUser(Secure.USER_SETUP_COMPLETE, 0, currentUser) != 0;
- }
-
- @Override
- public int getCurrentUser() {
- return ActivityManager.getCurrentUser();
- }
-
- @Override
- public void addCallback(@NonNull DeviceProvisionedListener listener) {
- mListeners.add(listener);
- if (mListeners.size() == 1) {
- startListening(getCurrentUser());
- }
- listener.onUserSetupChanged();
- listener.onDeviceProvisionedChanged();
- }
-
- @Override
- public void removeCallback(@NonNull DeviceProvisionedListener listener) {
- mListeners.remove(listener);
- if (mListeners.size() == 0) {
- stopListening();
- }
- }
-
- protected void startListening(int user) {
- mGlobalSettings.registerContentObserverForUser(mDeviceProvisionedUri, true,
- mSettingsObserver, 0);
- mSecureSettings.registerContentObserverForUser(mUserSetupUri, true,
- mSettingsObserver, user);
- startTracking();
- }
-
- protected void stopListening() {
- stopTracking();
- mGlobalSettings.unregisterContentObserver(mSettingsObserver);
- }
-
- @Override
- public void onUserSwitched(int newUserId) {
- mGlobalSettings.unregisterContentObserver(mSettingsObserver);
- mGlobalSettings.registerContentObserverForUser(mDeviceProvisionedUri, true,
- mSettingsObserver, 0);
- mSecureSettings.registerContentObserverForUser(mUserSetupUri, true,
- mSettingsObserver, newUserId);
- notifyUserChanged();
- }
-
- private void notifyUserChanged() {
- for (int i = mListeners.size() - 1; i >= 0; --i) {
- mListeners.get(i).onUserSwitched();
- }
- }
-
- private void notifySetupChanged() {
- for (int i = mListeners.size() - 1; i >= 0; --i) {
- mListeners.get(i).onUserSetupChanged();
- }
- }
-
- private void notifyProvisionedChanged() {
- for (int i = mListeners.size() - 1; i >= 0; --i) {
- mListeners.get(i).onDeviceProvisionedChanged();
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
new file mode 100644
index 0000000000000..acc12141796e0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2021 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 com.android.systemui.statusbar.policy
+
+import android.content.Context
+import android.content.pm.UserInfo
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import android.os.HandlerExecutor
+import android.os.UserHandle
+import android.provider.Settings
+import android.util.ArraySet
+import android.util.SparseBooleanArray
+import androidx.annotation.GuardedBy
+import androidx.annotation.WorkerThread
+import com.android.systemui.Dumpable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.settings.SecureSettings
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import java.util.concurrent.Executor
+import java.util.concurrent.atomic.AtomicBoolean
+import javax.inject.Inject
+
+@SysUISingleton
+open class DeviceProvisionedControllerImpl @Inject constructor(
+ private val secureSettings: SecureSettings,
+ private val globalSettings: GlobalSettings,
+ private val userTracker: UserTracker,
+ private val dumpManager: DumpManager,
+ @Background private val backgroundHandler: Handler,
+ @Main private val mainExecutor: Executor
+) : DeviceProvisionedController,
+ DeviceProvisionedController.DeviceProvisionedListener,
+ Dumpable {
+
+ companion object {
+ private const val ALL_USERS = -1
+ private const val NO_USERS = -2
+ protected const val TAG = "DeviceProvisionedControllerImpl"
+ }
+
+ private val deviceProvisionedUri = globalSettings.getUriFor(Settings.Global.DEVICE_PROVISIONED)
+ private val userSetupUri = secureSettings.getUriFor(Settings.Secure.USER_SETUP_COMPLETE)
+
+ private val deviceProvisioned = AtomicBoolean(false)
+ @GuardedBy("lock")
+ private val userSetupComplete = SparseBooleanArray()
+ @GuardedBy("lock")
+ private val listeners = ArraySet()
+
+ private val lock = Any()
+
+ private val backgroundExecutor = HandlerExecutor(backgroundHandler)
+
+ private val initted = AtomicBoolean(false)
+
+ private val _currentUser: Int
+ get() = userTracker.userId
+
+ override fun getCurrentUser(): Int {
+ return _currentUser
+ }
+
+ private val observer = object : ContentObserver(backgroundHandler) {
+ override fun onChange(
+ selfChange: Boolean,
+ uris: MutableCollection,
+ flags: Int,
+ userId: Int
+ ) {
+ val updateDeviceProvisioned = deviceProvisionedUri in uris
+ val updateUser = if (userSetupUri in uris) userId else NO_USERS
+ updateValues(updateDeviceProvisioned, updateUser)
+ if (updateDeviceProvisioned) {
+ onDeviceProvisionedChanged()
+ }
+ if (updateUser != NO_USERS) {
+ onUserSetupChanged()
+ }
+ }
+ }
+
+ private val userChangedCallback = object : UserTracker.Callback {
+ @WorkerThread
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ updateValues(updateDeviceProvisioned = false, updateUser = newUser)
+ onUserSwitched()
+ }
+
+ override fun onProfilesChanged(profiles: List) {}
+ }
+
+ init {
+ userSetupComplete.put(currentUser, false)
+ }
+
+ /**
+ * Call to initialize values and register observers
+ */
+ open fun init() {
+ if (!initted.compareAndSet(false, true)) {
+ return
+ }
+ dumpManager.registerDumpable(this)
+ updateValues()
+ userTracker.addCallback(userChangedCallback, backgroundExecutor)
+ globalSettings.registerContentObserver(deviceProvisionedUri, observer)
+ secureSettings.registerContentObserverForUser(userSetupUri, observer, UserHandle.USER_ALL)
+ }
+
+ @WorkerThread
+ private fun updateValues(updateDeviceProvisioned: Boolean = true, updateUser: Int = ALL_USERS) {
+ if (updateDeviceProvisioned) {
+ deviceProvisioned
+ .set(globalSettings.getInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0)
+ }
+ synchronized(lock) {
+ if (updateUser == ALL_USERS) {
+ val N = userSetupComplete.size()
+ for (i in 0 until N) {
+ val user = userSetupComplete.keyAt(i)
+ val value = secureSettings
+ .getIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, user) != 0
+ userSetupComplete.put(user, value)
+ }
+ } else if (updateUser != NO_USERS) {
+ val value = secureSettings
+ .getIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, updateUser) != 0
+ userSetupComplete.put(updateUser, value)
+ }
+ }
+ }
+
+ /**
+ * Adds a listener.
+ *
+ * The listener will not be called when this happens.
+ */
+ override fun addCallback(listener: DeviceProvisionedController.DeviceProvisionedListener) {
+ synchronized(lock) {
+ listeners.add(listener)
+ }
+ }
+
+ override fun removeCallback(listener: DeviceProvisionedController.DeviceProvisionedListener) {
+ synchronized(lock) {
+ listeners.remove(listener)
+ }
+ }
+
+ override fun isDeviceProvisioned(): Boolean {
+ return deviceProvisioned.get()
+ }
+
+ override fun isUserSetup(user: Int): Boolean {
+ val index = synchronized(lock) {
+ userSetupComplete.indexOfKey(user)
+ }
+ return if (index < 0) {
+ val value = secureSettings
+ .getIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, user) != 0
+ synchronized(lock) {
+ userSetupComplete.put(user, value)
+ }
+ value
+ } else {
+ synchronized(lock) {
+ userSetupComplete.get(user, false)
+ }
+ }
+ }
+
+ override fun isCurrentUserSetup(): Boolean {
+ return isUserSetup(currentUser)
+ }
+
+ override fun onDeviceProvisionedChanged() {
+ dispatchChange(
+ DeviceProvisionedController.DeviceProvisionedListener::onDeviceProvisionedChanged
+ )
+ }
+
+ override fun onUserSetupChanged() {
+ dispatchChange(DeviceProvisionedController.DeviceProvisionedListener::onUserSetupChanged)
+ }
+
+ override fun onUserSwitched() {
+ dispatchChange(DeviceProvisionedController.DeviceProvisionedListener::onUserSwitched)
+ }
+
+ protected fun dispatchChange(
+ callback: DeviceProvisionedController.DeviceProvisionedListener.() -> Unit
+ ) {
+ val listenersCopy = synchronized(lock) {
+ ArrayList(listeners)
+ }
+ mainExecutor.execute {
+ listenersCopy.forEach(callback)
+ }
+ }
+
+ override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array) {
+ pw.println("Device provisioned: ${deviceProvisioned.get()}")
+ synchronized(lock) {
+ pw.println("User setup complete: $userSetupComplete")
+ pw.println("Listeners: $listeners")
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index 8912448232719..923aff1e6b3d4 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -180,9 +180,13 @@ public abstract class TvSystemUIModule {
return new Recents(context, recentsImplementation, commandQueue);
}
- @Binds
- abstract DeviceProvisionedController bindDeviceProvisionedController(
- DeviceProvisionedControllerImpl deviceProvisionedController);
+ @SysUISingleton
+ @Provides
+ static DeviceProvisionedController providesDeviceProvisionedController(
+ DeviceProvisionedControllerImpl deviceProvisionedController) {
+ deviceProvisionedController.init();
+ return deviceProvisionedController;
+ }
@Binds
abstract KeyguardViewController bindKeyguardViewController(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
index 67cab7413f1f6..b23d07a314b43 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
@@ -216,7 +216,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
mCallbackHandler = mock(CallbackHandler.class);
mMockProvisionController = mock(DeviceProvisionedController.class);
- when(mMockProvisionController.isUserSetup(anyInt())).thenReturn(true);
+ when(mMockProvisionController.isCurrentUserSetup()).thenReturn(true);
doAnswer(invocation -> {
mUserCallback = (DeviceProvisionedListener) invocation.getArguments()[0];
mUserCallback.onUserSetupChanged();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
index 00dedd9610747..12f8282c7aa0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
@@ -21,7 +21,6 @@ import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -211,7 +210,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0);
setConnectivityViaCallbackInNetworkController(
NetworkCapabilities.TRANSPORT_CELLULAR, false, false, null);
- when(mMockProvisionController.isUserSetup(anyInt())).thenReturn(false);
+ when(mMockProvisionController.isCurrentUserSetup()).thenReturn(false);
mUserCallback.onUserSetupChanged();
TestableLooper.get(this).processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
new file mode 100644
index 0000000000000..5129f85841654
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2021 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 com.android.systemui.statusbar.policy
+
+import android.os.Handler
+import android.provider.Settings
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class DeviceProvisionedControllerImplTest : SysuiTestCase() {
+
+ companion object {
+ private const val START_USER = 0
+ }
+
+ private lateinit var controller: DeviceProvisionedControllerImpl
+
+ @Mock
+ private lateinit var userTracker: UserTracker
+ @Mock
+ private lateinit var dumpManager: DumpManager
+ @Mock
+ private lateinit var listener: DeviceProvisionedController.DeviceProvisionedListener
+ @Captor
+ private lateinit var userTrackerCallbackCaptor: ArgumentCaptor
+
+ private lateinit var mainExecutor: FakeExecutor
+ private lateinit var testableLooper: TestableLooper
+ private lateinit var settings: FakeSettings
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
+ mainExecutor = FakeExecutor(FakeSystemClock())
+ settings = FakeSettings()
+ `when`(userTracker.userId).thenReturn(START_USER)
+
+ controller = DeviceProvisionedControllerImpl(
+ settings,
+ settings,
+ userTracker,
+ dumpManager,
+ Handler(testableLooper.looper),
+ mainExecutor
+ )
+ }
+
+ @Test
+ fun testNotProvisionedByDefault() {
+ init()
+ assertThat(controller.isDeviceProvisioned).isFalse()
+ }
+
+ @Test
+ fun testNotUserSetupByDefault() {
+ init()
+ assertThat(controller.isUserSetup(START_USER)).isFalse()
+ }
+
+ @Test
+ fun testProvisionedWhenCreated() {
+ settings.putInt(Settings.Global.DEVICE_PROVISIONED, 1)
+ init()
+
+ assertThat(controller.isDeviceProvisioned).isTrue()
+ }
+
+ @Test
+ fun testUserSetupWhenCreated() {
+ settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
+ init()
+
+ assertThat(controller.isUserSetup(START_USER))
+ }
+
+ @Test
+ fun testDeviceProvisionedChange() {
+ init()
+
+ settings.putInt(Settings.Global.DEVICE_PROVISIONED, 1)
+ testableLooper.processAllMessages() // background observer
+
+ assertThat(controller.isDeviceProvisioned).isTrue()
+ }
+
+ @Test
+ fun testUserSetupChange() {
+ init()
+
+ settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
+ testableLooper.processAllMessages() // background observer
+
+ assertThat(controller.isUserSetup(START_USER)).isTrue()
+ }
+
+ @Test
+ fun testUserSetupChange_otherUser() {
+ init()
+ val otherUser = 10
+
+ settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, otherUser)
+ testableLooper.processAllMessages() // background observer
+
+ assertThat(controller.isUserSetup(START_USER)).isFalse()
+ assertThat(controller.isUserSetup(otherUser)).isTrue()
+ }
+
+ @Test
+ fun testCurrentUserSetup() {
+ val otherUser = 10
+ settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, otherUser)
+ init()
+
+ assertThat(controller.isCurrentUserSetup).isFalse()
+ switchUser(otherUser)
+ testableLooper.processAllMessages()
+
+ assertThat(controller.isCurrentUserSetup).isTrue()
+ }
+
+ @Test
+ fun testListenerNotCalledOnAdd() {
+ init()
+ controller.addCallback(listener)
+
+ mainExecutor.runAllReady()
+
+ verify(listener, never()).onDeviceProvisionedChanged()
+ verify(listener, never()).onUserSetupChanged()
+ verify(listener, never()).onUserSwitched()
+ }
+
+ @Test
+ fun testListenerCalledOnUserSwitched() {
+ init()
+ controller.addCallback(listener)
+
+ switchUser(10)
+
+ testableLooper.processAllMessages()
+ mainExecutor.runAllReady()
+
+ verify(listener).onUserSwitched()
+ verify(listener, never()).onUserSetupChanged()
+ verify(listener, never()).onDeviceProvisionedChanged()
+ }
+
+ @Test
+ fun testListenerCalledOnUserSetupChanged() {
+ init()
+ controller.addCallback(listener)
+
+ settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
+ testableLooper.processAllMessages()
+ mainExecutor.runAllReady()
+
+ verify(listener, never()).onUserSwitched()
+ verify(listener).onUserSetupChanged()
+ verify(listener, never()).onDeviceProvisionedChanged()
+ }
+
+ @Test
+ fun testListenerCalledOnDeviceProvisionedChanged() {
+ init()
+ controller.addCallback(listener)
+
+ settings.putInt(Settings.Global.DEVICE_PROVISIONED, 1)
+ testableLooper.processAllMessages()
+ mainExecutor.runAllReady()
+
+ verify(listener, never()).onUserSwitched()
+ verify(listener, never()).onUserSetupChanged()
+ verify(listener).onDeviceProvisionedChanged()
+ }
+
+ @Test
+ fun testRemoveListener() {
+ init()
+ controller.addCallback(listener)
+ controller.removeCallback(listener)
+
+ switchUser(10)
+ settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
+ settings.putInt(Settings.Global.DEVICE_PROVISIONED, 1)
+
+ testableLooper.processAllMessages()
+ mainExecutor.runAllReady()
+
+ verify(listener, never()).onDeviceProvisionedChanged()
+ verify(listener, never()).onUserSetupChanged()
+ verify(listener, never()).onUserSwitched()
+ }
+
+ private fun init() {
+ controller.init()
+ verify(userTracker).addCallback(capture(userTrackerCallbackCaptor), any())
+ }
+
+ private fun switchUser(toUser: Int) {
+ `when`(userTracker.userId).thenReturn(toUser)
+ userTrackerCallbackCaptor.value.onUserChanged(toUser, mContext)
+ }
+}
\ No newline at end of file