From cda836fff7e8d0abd2122d20b920a7f9ed2cb405 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Tue, 19 Sep 2023 12:28:54 +0800 Subject: [PATCH] Move SystemUpdateManager.retrieveSystemUpdateInfo Into Kotlin Coroutine for true async. Bug: 300851543 Test: manual - on system page Test: unit test Change-Id: Ibec5c9d0934d71ed1a5808cadf3b3542eb3d5fa0 --- .../system/SystemDashboardFragment.java | 17 +- .../settings/system/SystemUpdateManagerExt.kt | 42 +++++ .../SystemUpdatePreferenceController.java | 146 --------------- .../SystemUpdatePreferenceController.kt | 137 ++++++++++++++ tests/robotests/res/values-mcc999/config.xml | 1 - .../SystemUpdatePreferenceControllerTest.java | 171 ------------------ .../system/SystemUpdateManagerExtKtTest.kt | 52 ++++++ .../SystemUpdatePreferenceControllerTest.kt | 168 +++++++++++++++++ 8 files changed, 401 insertions(+), 333 deletions(-) create mode 100644 src/com/android/settings/system/SystemUpdateManagerExt.kt delete mode 100644 src/com/android/settings/system/SystemUpdatePreferenceController.java create mode 100644 src/com/android/settings/system/SystemUpdatePreferenceController.kt delete mode 100644 tests/robotests/src/com/android/settings/system/SystemUpdatePreferenceControllerTest.java create mode 100644 tests/spa_unit/src/com/android/settings/system/SystemUpdateManagerExtKtTest.kt create mode 100644 tests/spa_unit/src/com/android/settings/system/SystemUpdatePreferenceControllerTest.kt diff --git a/src/com/android/settings/system/SystemDashboardFragment.java b/src/com/android/settings/system/SystemDashboardFragment.java index 79c5b9f869d..678b675e6ad 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.dashboard.DashboardFragment; 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 @@ public class SystemDashboardFragment extends DashboardFragment { * For Search. */ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = - new BaseSearchIndexProvider() { - @Override - public List 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 00000000000..8ddf174a5ad --- /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 b2a22ff4a13..00000000000 --- 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 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 00000000000..01df0654220 --- /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 82edea5d4d5..a5767e40907 100644 --- a/tests/robotests/res/values-mcc999/config.xml +++ b/tests/robotests/res/values-mcc999/config.xml @@ -53,7 +53,6 @@ false false false - false false false false 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 61aa294fd9b..00000000000 --- 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 keys = new ArrayList<>(); - mShadowUserManager.setIsAdminUser(true); - - mController.updateNonIndexableKeys(keys); - - assertThat(keys).isEmpty(); - } - - @Test - public void updateNonIndexable_ifNotAvailable_shouldUpdate() { - mShadowUserManager.setIsAdminUser(false); - final List 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 00000000000..0ba91df7dac --- /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() + + 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 00000000000..17cdf0485d0 --- /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() + private val mockSystemUpdateManager = mock() + + 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 { + 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() + + controller.updateNonIndexableKeys(keys) + + assertThat(keys).isEmpty() + } + + @Test + fun updateNonIndexable_ifNotAvailable_shouldUpdate() { + whenever(mockUserManager.isAdminUser).thenReturn(false) + val keys = mutableListOf() + + 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" + } +}