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