Merge "Rewrite DeviceProvisionedController" into sc-v2-dev

This commit is contained in:
Fabian Kozynski
2021-10-20 20:24:44 +00:00
committed by Android (Google) Code Review
11 changed files with 533 additions and 170 deletions

View File

@@ -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(

View File

@@ -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());
}
});

View File

@@ -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();

View File

@@ -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));

View File

@@ -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() { }
}
}

View File

@@ -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();
}
}
}

View File

@@ -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")
}
}
}

View File

@@ -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(

View File

@@ -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();

View File

@@ -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();

View File

@@ -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)
}
}