Move SystemUpdateManager.retrieveSystemUpdateInfo
Into Kotlin Coroutine for true async.
Bug: 300851543
Test: manual - on system page
Test: unit test
Change-Id: Ibec5c9d0934d71ed1a5808cadf3b3542eb3d5fa0
diff --git a/src/com/android/settings/system/SystemDashboardFragment.java b/src/com/android/settings/system/SystemDashboardFragment.java
index 79c5b9f..678b675 100644
--- a/src/com/android/settings/system/SystemDashboardFragment.java
+++ b/src/com/android/settings/system/SystemDashboardFragment.java
@@ -16,9 +16,7 @@
package com.android.settings.system;
import android.app.settings.SettingsEnums;
-import android.content.Context;
import android.os.Bundle;
-import android.provider.SearchIndexableResource;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
@@ -29,9 +27,6 @@
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;
-import java.util.Arrays;
-import java.util.List;
-
@SearchIndexable
public class SystemDashboardFragment extends DashboardFragment {
@@ -85,13 +80,5 @@
* For Search.
*/
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
- new BaseSearchIndexProvider() {
- @Override
- public List<SearchIndexableResource> getXmlResourcesToIndex(
- Context context, boolean enabled) {
- final SearchIndexableResource sir = new SearchIndexableResource(context);
- sir.xmlResId = R.xml.system_dashboard_fragment;
- return Arrays.asList(sir);
- }
- };
-}
\ No newline at end of file
+ new BaseSearchIndexProvider(R.xml.system_dashboard_fragment);
+}
diff --git a/src/com/android/settings/system/SystemUpdateManagerExt.kt b/src/com/android/settings/system/SystemUpdateManagerExt.kt
new file mode 100644
index 0000000..8ddf174
--- /dev/null
+++ b/src/com/android/settings/system/SystemUpdateManagerExt.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 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.settings.system
+
+import android.content.Context
+import android.os.Bundle
+import android.os.SystemUpdateManager
+import android.util.Log
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+private const val TAG = "SystemUpdateManagerExt"
+
+/**
+ * Gets the system update status.
+ *
+ * Note: [SystemUpdateManager.retrieveSystemUpdateInfo] must be called on worker thread to avoid
+ * StrictMode violation.
+ */
+suspend fun Context.getSystemUpdateInfo(): Bundle? = withContext(Dispatchers.Default) {
+ val updateManager = getSystemService(SystemUpdateManager::class.java)!!
+ try {
+ updateManager.retrieveSystemUpdateInfo()
+ } catch (e: Exception) {
+ Log.w(TAG, "Error getting system update info.")
+ null
+ }
+}
diff --git a/src/com/android/settings/system/SystemUpdatePreferenceController.java b/src/com/android/settings/system/SystemUpdatePreferenceController.java
deleted file mode 100644
index b2a22ff..0000000
--- a/src/com/android/settings/system/SystemUpdatePreferenceController.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.system;
-
-import static android.content.Context.CARRIER_CONFIG_SERVICE;
-import static android.content.Context.SYSTEM_UPDATE_SERVICE;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.PersistableBundle;
-import android.os.SystemUpdateManager;
-import android.os.UserManager;
-import android.telephony.CarrierConfigManager;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.R;
-import com.android.settings.Utils;
-import com.android.settings.core.BasePreferenceController;
-
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.FutureTask;
-
-public class SystemUpdatePreferenceController extends BasePreferenceController {
-
- private static final String TAG = "SysUpdatePrefContr";
-
- private static final String KEY_SYSTEM_UPDATE_SETTINGS = "system_update_settings";
-
- private final UserManager mUm;
- private final SystemUpdateManager mUpdateManager;
-
- public SystemUpdatePreferenceController(Context context) {
- super(context, KEY_SYSTEM_UPDATE_SETTINGS);
- mUm = UserManager.get(context);
- mUpdateManager = (SystemUpdateManager) context.getSystemService(SYSTEM_UPDATE_SERVICE);
- }
-
- @Override
- public int getAvailabilityStatus() {
- return mContext.getResources().getBoolean(R.bool.config_show_system_update_settings)
- && mUm.isAdminUser()
- ? AVAILABLE
- : UNSUPPORTED_ON_DEVICE;
- }
-
- @Override
- public void displayPreference(PreferenceScreen screen) {
- super.displayPreference(screen);
- if (isAvailable()) {
- Utils.updatePreferenceToSpecificActivityOrRemove(mContext, screen,
- getPreferenceKey(),
- Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
- }
- }
-
- @Override
- public boolean handlePreferenceTreeClick(Preference preference) {
- if (TextUtils.equals(getPreferenceKey(), preference.getKey())) {
- CarrierConfigManager configManager =
- (CarrierConfigManager) mContext.getSystemService(CARRIER_CONFIG_SERVICE);
- PersistableBundle b = configManager.getConfig();
- if (b != null && b.getBoolean(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_BOOL)) {
- ciActionOnSysUpdate(b);
- }
- }
- // always return false here because this handler does not want to block other handlers.
- return false;
- }
-
- @Override
- public CharSequence getSummary() {
- CharSequence summary = mContext.getString(R.string.android_version_summary,
- Build.VERSION.RELEASE_OR_PREVIEW_DISPLAY);
- final FutureTask<Bundle> bundleFutureTask = new FutureTask<>(
- // Put the API call in a future to avoid StrictMode violation.
- () -> mUpdateManager.retrieveSystemUpdateInfo());
- final Bundle updateInfo;
- try {
- bundleFutureTask.run();
- updateInfo = bundleFutureTask.get();
- } catch (InterruptedException | ExecutionException e) {
- Log.w(TAG, "Error getting system update info.");
- return summary;
- }
- switch (updateInfo.getInt(SystemUpdateManager.KEY_STATUS)) {
- case SystemUpdateManager.STATUS_WAITING_DOWNLOAD:
- case SystemUpdateManager.STATUS_IN_PROGRESS:
- case SystemUpdateManager.STATUS_WAITING_INSTALL:
- case SystemUpdateManager.STATUS_WAITING_REBOOT:
- summary = mContext.getText(R.string.android_version_pending_update_summary);
- break;
- case SystemUpdateManager.STATUS_UNKNOWN:
- Log.d(TAG, "Update statue unknown");
- // fall through to next branch
- case SystemUpdateManager.STATUS_IDLE:
- final String version = updateInfo.getString(SystemUpdateManager.KEY_TITLE);
- if (!TextUtils.isEmpty(version)) {
- summary = mContext.getString(R.string.android_version_summary, version);
- }
- break;
- }
- return summary;
- }
-
- /**
- * Trigger client initiated action (send intent) on system update
- */
- private void ciActionOnSysUpdate(PersistableBundle b) {
- String intentStr = b.getString(CarrierConfigManager.
- KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING);
- if (!TextUtils.isEmpty(intentStr)) {
- String extra = b.getString(CarrierConfigManager.
- KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING);
- String extraVal = b.getString(CarrierConfigManager.
- KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING);
-
- Intent intent = new Intent(intentStr);
- if (!TextUtils.isEmpty(extra)) {
- intent.putExtra(extra, extraVal);
- }
- Log.d(TAG, "ciActionOnSysUpdate: broadcasting intent " + intentStr +
- " with extra " + extra + ", " + extraVal);
- intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- mContext.getApplicationContext().sendBroadcast(intent);
- }
- }
-}
diff --git a/src/com/android/settings/system/SystemUpdatePreferenceController.kt b/src/com/android/settings/system/SystemUpdatePreferenceController.kt
new file mode 100644
index 0000000..01df065
--- /dev/null
+++ b/src/com/android/settings/system/SystemUpdatePreferenceController.kt
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2023 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.settings.system
+
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import android.os.PersistableBundle
+import android.os.SystemUpdateManager
+import android.os.UserManager
+import android.telephony.CarrierConfigManager
+import android.util.Log
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+import com.android.settings.R
+import com.android.settings.Utils
+import com.android.settings.core.BasePreferenceController
+import com.android.settingslib.spaprivileged.framework.common.userManager
+import kotlinx.coroutines.launch
+
+open class SystemUpdatePreferenceController(context: Context, preferenceKey: String) :
+ BasePreferenceController(context, preferenceKey) {
+ private val userManager: UserManager = context.userManager
+ private lateinit var preference: Preference
+
+ override fun getAvailabilityStatus() =
+ if (mContext.resources.getBoolean(R.bool.config_show_system_update_settings) &&
+ userManager.isAdminUser
+ ) AVAILABLE else UNSUPPORTED_ON_DEVICE
+
+ override fun displayPreference(screen: PreferenceScreen) {
+ super.displayPreference(screen)
+ preference = screen.findPreference(preferenceKey)!!
+ if (isAvailable) {
+ Utils.updatePreferenceToSpecificActivityOrRemove(
+ mContext,
+ screen,
+ preferenceKey,
+ Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY,
+ )
+ }
+ }
+
+ override fun handlePreferenceTreeClick(preference: Preference): Boolean {
+ if (preferenceKey == preference.key) {
+ val configManager = mContext.getSystemService(CarrierConfigManager::class.java)!!
+ configManager.getConfig(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_BOOL)?.let {
+ if (it.getBoolean(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_BOOL)) {
+ ciActionOnSysUpdate(it)
+ }
+ }
+ }
+ // always return false here because this handler does not want to block other handlers.
+ return false
+ }
+
+ override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
+ viewLifecycleOwner.lifecycleScope.launch {
+ viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ preference.summary = calculateSummary()
+ }
+ }
+ }
+
+ private suspend fun calculateSummary(): String {
+ val updateInfo = mContext.getSystemUpdateInfo() ?: return getReleaseVersionSummary()
+
+ val status = updateInfo.getInt(SystemUpdateManager.KEY_STATUS)
+ if (status == SystemUpdateManager.STATUS_UNKNOWN) {
+ Log.d(TAG, "Update statue unknown")
+ }
+ when (status) {
+ SystemUpdateManager.STATUS_WAITING_DOWNLOAD,
+ SystemUpdateManager.STATUS_IN_PROGRESS,
+ SystemUpdateManager.STATUS_WAITING_INSTALL,
+ SystemUpdateManager.STATUS_WAITING_REBOOT -> {
+ return mContext.getString(R.string.android_version_pending_update_summary)
+ }
+
+ SystemUpdateManager.STATUS_IDLE,
+ SystemUpdateManager.STATUS_UNKNOWN -> {
+ val version = updateInfo.getString(SystemUpdateManager.KEY_TITLE)
+ if (!version.isNullOrEmpty()) {
+ return mContext.getString(R.string.android_version_summary, version)
+ }
+ }
+ }
+ return getReleaseVersionSummary()
+ }
+
+ private fun getReleaseVersionSummary(): String = mContext.getString(
+ R.string.android_version_summary,
+ Build.VERSION.RELEASE_OR_PREVIEW_DISPLAY,
+ )
+
+ /**
+ * Trigger client initiated action (send intent) on system update
+ */
+ private fun ciActionOnSysUpdate(b: PersistableBundle) {
+ val intentStr = b.getString(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING)
+ if (intentStr.isNullOrEmpty()) return
+ val extra = b.getString(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING)
+ val extraVal =
+ b.getString(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING)
+ Log.d(
+ TAG,
+ "ciActionOnSysUpdate: broadcasting intent $intentStr with extra $extra, $extraVal"
+ )
+ val intent = Intent(intentStr).apply {
+ if (!extra.isNullOrEmpty()) putExtra(extra, extraVal)
+ addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND)
+ }
+ mContext.applicationContext.sendBroadcast(intent)
+ }
+
+ companion object {
+ private const val TAG = "SysUpdatePrefContr"
+ }
+}
diff --git a/tests/robotests/res/values-mcc999/config.xml b/tests/robotests/res/values-mcc999/config.xml
index 82edea5..a5767e4 100644
--- a/tests/robotests/res/values-mcc999/config.xml
+++ b/tests/robotests/res/values-mcc999/config.xml
@@ -53,7 +53,6 @@
<bool name="config_show_pointer_speed">false</bool>
<bool name="config_show_vibrate_input_devices">false</bool>
<bool name="config_show_reset_dashboard">false</bool>
- <bool name="config_show_system_update_settings">false</bool>
<bool name="config_show_device_model">false</bool>
<bool name="config_show_top_level_accessibility">false</bool>
<bool name="config_show_top_level_battery">false</bool>
diff --git a/tests/robotests/src/com/android/settings/system/SystemUpdatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/system/SystemUpdatePreferenceControllerTest.java
deleted file mode 100644
index 61aa294..0000000
--- a/tests/robotests/src/com/android/settings/system/SystemUpdatePreferenceControllerTest.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.system;
-
-import static android.os.SystemUpdateManager.KEY_STATUS;
-import static android.os.SystemUpdateManager.KEY_TITLE;
-import static android.os.SystemUpdateManager.STATUS_IDLE;
-import static android.os.SystemUpdateManager.STATUS_UNKNOWN;
-import static android.os.SystemUpdateManager.STATUS_WAITING_DOWNLOAD;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.SystemUpdateManager;
-
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.R;
-import com.android.settings.testutils.shadow.ShadowUserManager;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowApplication;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = ShadowUserManager.class)
-public class SystemUpdatePreferenceControllerTest {
-
- @Mock
- private PreferenceScreen mScreen;
- @Mock
- private SystemUpdateManager mSystemUpdateManager;
-
- private Context mContext;
- private ShadowUserManager mShadowUserManager;
- private SystemUpdatePreferenceController mController;
- private Preference mPreference;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
- mShadowUserManager = ShadowUserManager.getShadow();
-
- ShadowApplication.getInstance().setSystemService(Context.SYSTEM_UPDATE_SERVICE,
- mSystemUpdateManager);
- mController = new SystemUpdatePreferenceController(mContext);
- mPreference = new Preference(RuntimeEnvironment.application);
- mPreference.setKey(mController.getPreferenceKey());
- when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
- }
-
- @After
- public void cleanUp() {
- mShadowUserManager.setIsAdminUser(false);
- }
-
- @Test
- public void updateNonIndexable_ifAvailable_shouldNotUpdate() {
- final List<String> keys = new ArrayList<>();
- mShadowUserManager.setIsAdminUser(true);
-
- mController.updateNonIndexableKeys(keys);
-
- assertThat(keys).isEmpty();
- }
-
- @Test
- public void updateNonIndexable_ifNotAvailable_shouldUpdate() {
- mShadowUserManager.setIsAdminUser(false);
- final List<String> keys = new ArrayList<>();
-
- mController.updateNonIndexableKeys(keys);
-
- assertThat(keys).hasSize(1);
- }
-
- @Test
- public void displayPrefs_ifVisible_butNotAdminUser_shouldNotDisplay() {
- mShadowUserManager.setIsAdminUser(false);
- mController.displayPreference(mScreen);
-
- assertThat(mPreference.isVisible()).isFalse();
- }
-
- @Test
- @Config(qualifiers = "mcc999")
- public void displayPrefs_ifAdminUser_butNotVisible_shouldNotDisplay() {
- mShadowUserManager.setIsAdminUser(true);
- mController.displayPreference(mScreen);
-
- assertThat(mPreference.isVisible()).isFalse();
- }
-
- @Test
- public void displayPrefs_ifAvailable_shouldDisplay() {
- mShadowUserManager.setIsAdminUser(true);
-
- mController.displayPreference(mScreen);
-
- assertThat(mPreference.isVisible()).isTrue();
- }
-
- @Test
- public void updateState_systemUpdateStatusUnknown_shouldSetToAndroidVersion() {
- final Bundle bundle = new Bundle();
- bundle.putInt(KEY_STATUS, STATUS_UNKNOWN);
- when(mSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.getSummary())
- .isEqualTo(mContext.getString(R.string.android_version_summary,
- Build.VERSION.RELEASE_OR_PREVIEW_DISPLAY));
- }
-
- @Test
- public void updateState_systemUpdateStatusIdle_shouldSetToAndroidVersion() {
- final String testReleaseName = "ANDROID TEST VERSION";
-
- final Bundle bundle = new Bundle();
- bundle.putInt(KEY_STATUS, STATUS_IDLE);
- bundle.putString(KEY_TITLE, testReleaseName);
- when(mSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.getSummary())
- .isEqualTo(mContext.getString(R.string.android_version_summary, testReleaseName));
- }
-
- @Test
- public void updateState_systemUpdateInProgress_shouldSetToUpdatePending() {
- final Bundle bundle = new Bundle();
- bundle.putInt(KEY_STATUS, STATUS_WAITING_DOWNLOAD);
- when(mSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.getSummary())
- .isEqualTo(mContext.getString(R.string.android_version_pending_update_summary));
- }
-}
diff --git a/tests/spa_unit/src/com/android/settings/system/SystemUpdateManagerExtKtTest.kt b/tests/spa_unit/src/com/android/settings/system/SystemUpdateManagerExtKtTest.kt
new file mode 100644
index 0000000..0ba91df
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/system/SystemUpdateManagerExtKtTest.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 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.settings.system
+
+import android.content.Context
+import android.os.Bundle
+import android.os.SystemUpdateManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class SystemUpdateManagerExtKtTest {
+
+ private val mockSystemUpdateManager = mock<SystemUpdateManager>()
+
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ on { getSystemService(SystemUpdateManager::class.java) } doReturn mockSystemUpdateManager
+ }
+
+ @Test
+ fun getSystemUpdateInfo() = runTest {
+ val bundle = Bundle()
+ whenever(mockSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle)
+
+ val info = context.getSystemUpdateInfo()
+
+ assertThat(info).isSameInstanceAs(bundle)
+ }
+}
diff --git a/tests/spa_unit/src/com/android/settings/system/SystemUpdatePreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/system/SystemUpdatePreferenceControllerTest.kt
new file mode 100644
index 0000000..17cdf04
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/system/SystemUpdatePreferenceControllerTest.kt
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2023 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.settings.system
+
+import android.content.Context
+import android.os.Build
+import android.os.Bundle
+import android.os.SystemClock
+import android.os.SystemUpdateManager
+import android.os.UserManager
+import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.android.settingslib.spaprivileged.framework.common.userManager
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class SystemUpdatePreferenceControllerTest {
+ private val mockUserManager = mock<UserManager>()
+ private val mockSystemUpdateManager = mock<SystemUpdateManager>()
+
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ on { userManager } doReturn mockUserManager
+ on { getSystemService(SystemUpdateManager::class.java) } doReturn mockSystemUpdateManager
+ }
+
+ private val resources = spy(context.resources) {
+ on { getBoolean(R.bool.config_show_system_update_settings) } doReturn true
+ }
+
+ private val preference = Preference(context).apply { key = KEY }
+ private val preferenceScreen = mock<PreferenceScreen> {
+ onGeneric { findPreference(KEY) } doReturn preference
+ }
+ private val controller = SystemUpdatePreferenceController(context, KEY)
+
+ @Before
+ fun setUp() {
+ whenever(context.resources).thenReturn(resources)
+ }
+
+ @Test
+ fun updateNonIndexable_ifAvailable_shouldNotUpdate() {
+ whenever(mockUserManager.isAdminUser).thenReturn(true)
+ val keys = mutableListOf<String>()
+
+ controller.updateNonIndexableKeys(keys)
+
+ assertThat(keys).isEmpty()
+ }
+
+ @Test
+ fun updateNonIndexable_ifNotAvailable_shouldUpdate() {
+ whenever(mockUserManager.isAdminUser).thenReturn(false)
+ val keys = mutableListOf<String>()
+
+ controller.updateNonIndexableKeys(keys)
+
+ assertThat(keys).containsExactly(KEY)
+ }
+
+ @Test
+ fun displayPrefs_ifVisible_butNotAdminUser_shouldNotDisplay() {
+ whenever(mockUserManager.isAdminUser).thenReturn(false)
+
+ controller.displayPreference(preferenceScreen)
+
+ assertThat(preference.isVisible).isFalse()
+ }
+
+ @Test
+ fun displayPrefs_ifAdminUser_butNotVisible_shouldNotDisplay() {
+ whenever(mockUserManager.isAdminUser).thenReturn(true)
+ whenever(resources.getBoolean(R.bool.config_show_system_update_settings)).thenReturn(false)
+
+ controller.displayPreference(preferenceScreen)
+
+ assertThat(preference.isVisible).isFalse()
+ }
+
+ @Test
+ fun displayPrefs_ifAvailable_shouldDisplay() {
+ whenever(mockUserManager.isAdminUser).thenReturn(true)
+
+ controller.displayPreference(preferenceScreen)
+
+ assertThat(preference.isVisible).isTrue()
+ }
+
+ @Test
+ fun updateState_systemUpdateStatusUnknown_shouldSetToAndroidVersion() {
+ val bundle = Bundle().apply {
+ putInt(SystemUpdateManager.KEY_STATUS, SystemUpdateManager.STATUS_UNKNOWN)
+ }
+ whenever(mockSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle)
+ controller.displayPreference(preferenceScreen)
+
+ controller.onViewCreated(TestLifecycleOwner())
+ SystemClock.sleep(100)
+
+ assertThat(preference.summary).isEqualTo(
+ context.getString(
+ R.string.android_version_summary,
+ Build.VERSION.RELEASE_OR_PREVIEW_DISPLAY,
+ )
+ )
+ }
+
+ @Test
+ fun updateState_systemUpdateStatusIdle_shouldSetToAndroidVersion() {
+ val testReleaseName = "ANDROID TEST VERSION"
+ val bundle = Bundle().apply {
+ putInt(SystemUpdateManager.KEY_STATUS, SystemUpdateManager.STATUS_IDLE)
+ putString(SystemUpdateManager.KEY_TITLE, testReleaseName)
+ }
+ whenever(mockSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle)
+ controller.displayPreference(preferenceScreen)
+
+ controller.onViewCreated(TestLifecycleOwner())
+ SystemClock.sleep(100)
+
+ assertThat(preference.summary)
+ .isEqualTo(context.getString(R.string.android_version_summary, testReleaseName))
+ }
+
+ @Test
+ fun updateState_systemUpdateInProgress_shouldSetToUpdatePending() {
+ val bundle = Bundle().apply {
+ putInt(SystemUpdateManager.KEY_STATUS, SystemUpdateManager.STATUS_WAITING_DOWNLOAD)
+ }
+ whenever(mockSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle)
+ controller.displayPreference(preferenceScreen)
+
+ controller.onViewCreated(TestLifecycleOwner())
+ SystemClock.sleep(100)
+
+ assertThat(preference.summary)
+ .isEqualTo(context.getString(R.string.android_version_pending_update_summary))
+ }
+
+ private companion object {
+ const val KEY = "test_key"
+ }
+}