Rewrite DeviceProvisionedController
The old version made IPC calls in the main thread. Instead, fix the following: 1. Calls to settings provider are made in background thread, except maybe on object creation to set initial state or if requesting the state of a user that hasn't been started (unlikely). 2. Cache state of device_provisioned and each user (that has been started) user_setup_complete 3. Do all operations in background (with proper locks) and notify in main thread. 4. Use UserTracker to avoid calls to `ActivityManager`. 5. The tracker is always registered so no changes are missed, and we don't need to retrieve cache every time we would register. The number of calls from the system are pretty low any way. 6. Listeners are not called on addCallback. None of the listeners expected this and at least one assumed that a call meant an actual change in state. Test: atest DeviceProvisionedControllerTest Test: manual, things are not hopelessly broken Fixes: 203138620 Change-Id: I9caccd7593732a07d688d5ca2f8de8cedb8b02ef
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -4299,10 +4299,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));
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* This controller keeps track of the values of device provisioning and user setup complete
|
||||
*/
|
||||
public interface DeviceProvisionedController extends CallbackController<DeviceProvisionedListener> {
|
||||
|
||||
/**
|
||||
* @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() { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<DeviceProvisionedListener> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<DeviceProvisionedController.DeviceProvisionedListener>()
|
||||
|
||||
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<Uri>,
|
||||
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<UserInfo>) {}
|
||||
}
|
||||
|
||||
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<out String>) {
|
||||
pw.println("Device provisioned: ${deviceProvisioned.get()}")
|
||||
synchronized(lock) {
|
||||
pw.println("User setup complete: $userSetupComplete")
|
||||
pw.println("Listeners: $listeners")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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<UserTracker.Callback>
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user