Snap for 10204122 from b04b977cac to udc-qpr1-release

Change-Id: I68548908df256cf68efc2277d1849b9e60d0b9e1
This commit is contained in:
Android Build Coastguard Worker
2023-05-26 01:33:15 +00:00
12 changed files with 328 additions and 96 deletions

View File

@@ -1031,13 +1031,21 @@
<!-- Dialog message for dialog which shows when user touches the icon on the screen, instead of the sensor at the back [CHAR LIMIT=NONE] -->
<string name="security_settings_fingerprint_enroll_touch_dialog_message">Touch the sensor on the back of your phone. Use your index finger.</string>
<!-- Dialog message for dialog which shows when finger cannot be processed in enrollment. [CHAR LIMIT=45] -->
<string name="security_settings_fingerprint_enroll_error_unable_to_process_dialog_title">Enrollment was not completed</string>
<string name="security_settings_fingerprint_enroll_error_unable_to_process_dialog_title">Can\u2019t complete fingerprint setup</string>
<!-- Dialog message for dialog which shows when finger cannot be enrolled because the HAL is unable to process it (SUW). -->
<string name="security_settings_fingerprint_enroll_error_unable_to_process_message_setup">You can try again now or set up your fingerprint later in Settings.</string>
<!-- Dialog message for dialog which shows when finger cannot be enrolled because the HAL is unable to process it. -->
<string name="security_settings_fingerprint_enroll_error_unable_to_process_message">You can try again now or set up your fingerprint later.</string>
<!-- Dialog message for dialog which shows when finger cannot be enrolled. [CHAR LIMIT=45] -->
<string name="security_settings_fingerprint_enroll_error_dialog_title">Fingerprint setup timed out</string>
<!-- Dialog message for dialog which shows when finger cannot be enrolled due to being idle too long (SUW). -->
<string name="security_settings_fingerprint_enroll_error_timeout_dialog_message_setup">You can set up your fingerprint later in Settings.</string>
<!-- Dialog message for dialog which shows when finger cannot be enrolled due to an internal error or fingerprint can't be read (SUW). -->
<string name="security_settings_fingerprint_enroll_error_generic_dialog_message_setup">Something went wrong. You can set up your fingerprint later in Settings.</string>
<!-- Dialog message for dialog which shows when finger cannot be enrolled due to being idle too long. -->
<string name="security_settings_fingerprint_enroll_error_timeout_dialog_message">You can set up your fingerprint later in Settings.</string>
<string name="security_settings_fingerprint_enroll_error_timeout_dialog_message">You can set up your fingerprint later.</string>
<!-- Dialog message for dialog which shows when finger cannot be enrolled due to an internal error or fingerprint can't be read. -->
<string name="security_settings_fingerprint_enroll_error_generic_dialog_message">Fingerprint enrollment didn\'t work. Try again or use a different finger.</string>
<string name="security_settings_fingerprint_enroll_error_generic_dialog_message">Something went wrong. You can set up your fingerprint later.</string>
<!-- Button text shown at the end of enrollment that allows the user to add another fingerprint -->
<string name="fingerprint_enroll_button_add">Add another</string>
<!-- Button text shown at the end of enrollment that allows the user to move to the next step -->
@@ -5509,6 +5517,8 @@
<string name="battery_usage_less_than_percent">&lt; <xliff:g id="percentage">%1$s</xliff:g></string>
<!-- Process Stats strings -->
<skip />
<!-- Description of battery information footer text. [CHAR LIMIT=NONE] -->
<string name="battery_cycle_count_footer">Due to quality inspections before shipping, the cycle count may not be zero on first use</string>
<!-- [CHAR LIMIT=NONE] Activity title for Process Stats summary -->
<string name="process_stats_summary_title">Process Stats</string>

View File

@@ -42,4 +42,9 @@
settings:controller="com.android.settings.deviceinfo.batteryinfo.BatteryCycleCountPreferenceController"
settings:enableCopying="true"/>
<com.android.settingslib.widget.FooterPreference
android:key="battery_info_footer"
android:title="@string/battery_cycle_count_footer"
android:selectable="false"
settings:searchable="false" />
</PreferenceScreen>

View File

@@ -511,7 +511,8 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
// showErrorDialog() will cause onWindowFocusChanged(false), set mIsCanceled to false
// before showErrorDialog() to prevent that another error dialog is triggered again.
mIsCanceled = true;
FingerprintErrorDialog.showErrorDialog(this, errorMsgId);
FingerprintErrorDialog.showErrorDialog(this, errorMsgId,
this instanceof SetupFingerprintEnrollEnrolling);
cancelEnrollment();
stopIconAnimation();
stopListenOrientationEvent();

View File

@@ -329,7 +329,8 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements
if (mNextClicked && errMsgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
proceedToEnrolling(false /* cancelEnrollment */);
} else {
FingerprintErrorDialog.showErrorDialog(this, errMsgId);
FingerprintErrorDialog.showErrorDialog(this, errMsgId,
this instanceof SetupFingerprintEnrollFindSensor);
}
}

View File

@@ -18,11 +18,14 @@ package com.android.settings.biometrics.fingerprint;
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED;
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_TIMEOUT;
import static com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.KEY_STATE_CANCELED;
import android.app.Activity;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.Intent;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
@@ -48,6 +51,8 @@ public class FingerprintErrorDialog extends InstrumentedDialogFragment {
final CharSequence errorTitle = getArguments().getCharSequence(KEY_ERROR_TITLE);
final int errMsgId = getArguments().getInt(KEY_ERROR_ID);
final boolean wasTimeout = errMsgId == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT;
final boolean showTryAgain = errMsgId
== BiometricFingerprintConstants.FINGERPRINT_ERROR_UNABLE_TO_PROCESS;
builder.setTitle(errorTitle)
.setMessage(errorString)
@@ -64,12 +69,33 @@ public class FingerprintErrorDialog extends InstrumentedDialogFragment {
}
activity.finish();
});
if (showTryAgain) {
builder.setPositiveButton(
R.string.security_settings_fingerprint_enroll_dialog_try_again,
(dialog, which) -> {
dialog.dismiss();
final Activity activity = getActivity();
final Intent intent = activity.getIntent();
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
intent.putExtra(KEY_STATE_CANCELED, false);
activity.startActivity(intent);
activity.finish();
})
.setNegativeButton(R.string.security_settings_fingerprint_enroll_dialog_ok,
(dialog, which) -> {
dialog.dismiss();
final Activity activity = getActivity();
activity.setResult(RESULT_FINISHED);
activity.finish();
});
}
final AlertDialog dialog = builder.create();
dialog.setCanceledOnTouchOutside(false);
return dialog;
}
public static void showErrorDialog(BiometricEnrollBase host, int errMsgId) {
public static void showErrorDialog(BiometricEnrollBase host, int errMsgId, boolean isSetup) {
if (host.isFinishing()) {
return;
}
@@ -77,12 +103,39 @@ public class FingerprintErrorDialog extends InstrumentedDialogFragment {
if (fragmentManager.isDestroyed() || fragmentManager.isStateSaved()) {
return;
}
CharSequence errMsg = host.getText(getErrorMessage(errMsgId));
CharSequence errMsg;
if (isSetup) {
errMsg = host.getText(getSetupErrorMessage(errMsgId));
} else {
errMsg = host.getText(getErrorMessage(errMsgId));
}
final CharSequence errTitle = host.getText(getErrorTitle(errMsgId));
final FingerprintErrorDialog dialog = newInstance(errMsg, errTitle, errMsgId);
dialog.show(fragmentManager, FingerprintErrorDialog.class.getName());
}
/**
* Gets dialog message as error id inside {@link FingerprintManager}
*/
public static int getSetupErrorMessage(int errMsgId) {
switch (errMsgId) {
case FingerprintManager.FINGERPRINT_ERROR_TIMEOUT:
// This message happens when the underlying crypto layer decides to revoke
// the enrollment auth token.
return R.string
.security_settings_fingerprint_enroll_error_timeout_dialog_message_setup;
case FingerprintManager.FINGERPRINT_ERROR_BAD_CALIBRATION:
return R.string.security_settings_fingerprint_bad_calibration;
case FingerprintManager.FINGERPRINT_ERROR_UNABLE_TO_PROCESS:
return R.string
.security_settings_fingerprint_enroll_error_unable_to_process_message_setup;
default:
// There's nothing specific to tell the user about. Ask them to try again.
return R.string
.security_settings_fingerprint_enroll_error_generic_dialog_message_setup;
}
}
/**
* Gets dialog message as error id inside {@link FingerprintManager}
*/
@@ -94,6 +147,9 @@ public class FingerprintErrorDialog extends InstrumentedDialogFragment {
return R.string.security_settings_fingerprint_enroll_error_timeout_dialog_message;
case FingerprintManager.FINGERPRINT_ERROR_BAD_CALIBRATION:
return R.string.security_settings_fingerprint_bad_calibration;
case FingerprintManager.FINGERPRINT_ERROR_UNABLE_TO_PROCESS:
return R.string
.security_settings_fingerprint_enroll_error_unable_to_process_message;
default:
// There's nothing specific to tell the user about. Ask them to try again.
return R.string.security_settings_fingerprint_enroll_error_generic_dialog_message;
@@ -105,11 +161,11 @@ public class FingerprintErrorDialog extends InstrumentedDialogFragment {
*/
public static int getErrorTitle(int errMsgId) {
switch (errMsgId) {
case FingerprintManager.FINGERPRINT_ERROR_UNABLE_TO_PROCESS:
case FingerprintManager.FINGERPRINT_ERROR_TIMEOUT:
return R.string.security_settings_fingerprint_enroll_error_dialog_title;
default:
return R.string
.security_settings_fingerprint_enroll_error_unable_to_process_dialog_title;
default:
return R.string.security_settings_fingerprint_enroll_error_dialog_title;
}
}

View File

@@ -196,8 +196,10 @@ public class DataSaverBackend {
public interface Listener {
void onDataSaverChanged(boolean isDataSaving);
void onAllowlistStatusChanged(int uid, boolean isAllowlisted);
/** This is called when allow list status is changed. */
default void onAllowlistStatusChanged(int uid, boolean isAllowlisted) {}
void onDenylistStatusChanged(int uid, boolean isDenylisted);
/** This is called when deny list status is changed. */
default void onDenylistStatusChanged(int uid, boolean isDenylisted) {}
}
}

View File

@@ -15,33 +15,36 @@
*/
package com.android.settings.datausage
import android.app.Application
import android.app.settings.SettingsEnums
import android.content.Context
import android.net.NetworkPolicyManager
import android.os.Bundle
import android.os.UserHandle
import android.telephony.SubscriptionManager
import android.widget.Switch
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import com.android.settings.R
import com.android.settings.SettingsActivity
import com.android.settings.SettingsPreferenceFragment
import com.android.settings.applications.AppStateBaseBridge
import com.android.settings.datausage.AppStateDataUsageBridge.DataUsageState
import com.android.settings.search.BaseSearchIndexProvider
import com.android.settings.widget.SettingsMainSwitchBar
import com.android.settingslib.applications.ApplicationsState
import com.android.settingslib.search.SearchIndexable
import com.android.settingslib.spa.framework.util.formatString
import com.android.settingslib.spaprivileged.model.app.AppListRepository
import com.android.settingslib.spaprivileged.model.app.AppListRepositoryImpl
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@SearchIndexable
class DataSaverSummary : SettingsPreferenceFragment() {
private lateinit var switchBar: SettingsMainSwitchBar
private lateinit var dataSaverBackend: DataSaverBackend
private lateinit var unrestrictedAccess: Preference
private var dataUsageBridge: AppStateDataUsageBridge? = null
private var session: ApplicationsState.Session? = null
// Flag used to avoid infinite loop due if user switch it on/off too quick.
private var switching = false
@@ -72,27 +75,15 @@ class DataSaverSummary : SettingsPreferenceFragment() {
override fun onResume() {
super.onResume()
dataSaverBackend.refreshAllowlist()
dataSaverBackend.refreshDenylist()
dataSaverBackend.addListener(dataSaverBackendListener)
dataUsageBridge?.resume(/* forceLoadAllApps= */ true)
?: viewLifecycleOwner.lifecycleScope.launch {
val applicationsState = ApplicationsState.getInstance(
requireContext().applicationContext as Application
)
dataUsageBridge = AppStateDataUsageBridge(
applicationsState, dataUsageBridgeCallbacks, dataSaverBackend
)
session =
applicationsState.newSession(applicationsStateCallbacks, settingsLifecycle)
dataUsageBridge?.resume(/* forceLoadAllApps= */ true)
}
viewLifecycleOwner.lifecycleScope.launch {
unrestrictedAccess.summary = getUnrestrictedSummary(requireContext())
}
}
override fun onPause() {
super.onPause()
dataSaverBackend.remListener(dataSaverBackendListener)
dataUsageBridge?.pause()
}
private fun onSwitchChanged(isChecked: Boolean) {
@@ -115,52 +106,36 @@ class DataSaverSummary : SettingsPreferenceFragment() {
switching = false
}
}
override fun onAllowlistStatusChanged(uid: Int, isAllowlisted: Boolean) {}
override fun onDenylistStatusChanged(uid: Int, isDenylisted: Boolean) {}
}
private val dataUsageBridgeCallbacks = AppStateBaseBridge.Callback {
updateUnrestrictedAccessSummary()
}
private val applicationsStateCallbacks = object : ApplicationsState.Callbacks {
override fun onRunningStateChanged(running: Boolean) {}
override fun onPackageListChanged() {}
override fun onRebuildComplete(apps: ArrayList<ApplicationsState.AppEntry>?) {}
override fun onPackageIconChanged() {}
override fun onPackageSizeChanged(packageName: String?) {}
override fun onAllSizesComputed() {
updateUnrestrictedAccessSummary()
}
override fun onLauncherInfoChanged() {
updateUnrestrictedAccessSummary()
}
override fun onLoadEntriesCompleted() {}
}
private fun updateUnrestrictedAccessSummary() {
if (!isAdded || isFinishingOrDestroyed) return
val allApps = session?.allApps ?: return
val count = allApps.count {
ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(it) &&
(it.extraInfo as? DataUsageState)?.isDataSaverAllowlisted == true
}
unrestrictedAccess.summary =
resources.formatString(R.string.data_saver_unrestricted_summary, "count" to count)
}
companion object {
private const val KEY_UNRESTRICTED_ACCESS = "unrestricted_access"
@VisibleForTesting
suspend fun getUnrestrictedSummary(
context: Context,
appListRepository: AppListRepository =
AppListRepositoryImpl(context.applicationContext),
) = context.formatString(
R.string.data_saver_unrestricted_summary,
"count" to getAllowCount(context.applicationContext, appListRepository),
)
private suspend fun getAllowCount(context: Context, appListRepository: AppListRepository) =
withContext(Dispatchers.IO) {
coroutineScope {
val appsDeferred = async {
appListRepository.loadAndFilterApps(
userId = UserHandle.myUserId(),
isSystemApp = false,
)
}
val uidsAllowed = NetworkPolicyManager.from(context)
.getUidsWithPolicy(NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND)
appsDeferred.await().count { app -> app.uid in uidsAllowed }
}
}
private fun Context.isDataSaverVisible(): Boolean =
resources.getBoolean(R.bool.config_show_data_saver)

View File

@@ -17,6 +17,8 @@
package com.android.settings.network.telephony;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
@@ -33,9 +35,10 @@ import com.android.internal.telephony.util.ArrayUtils;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import com.android.settingslib.utils.ThreadUtils;
/**
* Preference controller for "Enhanced 4G LTE"
* Preference controller for "Voice over NR".
*/
public class NrAdvancedCallingPreferenceController extends TelephonyTogglePreferenceController
implements LifecycleObserver, OnStart, OnStop {
@@ -50,8 +53,11 @@ public class NrAdvancedCallingPreferenceController extends TelephonyTogglePrefer
private boolean mIsVonrVisibleFromCarrierConfig = false;
private boolean mIsNrEnableFromCarrierConfig = false;
private boolean mHas5gCapability = false;
private boolean mIsVoNrEnabled = false;
private Integer mCallState;
private Handler mHandler = new Handler(Looper.getMainLooper());
public NrAdvancedCallingPreferenceController(Context context, String key) {
super(context, key);
mTelephonyManager = context.getSystemService(TelephonyManager.class);
@@ -94,6 +100,8 @@ public class NrAdvancedCallingPreferenceController extends TelephonyTogglePrefer
CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
mIsNrEnableFromCarrierConfig = !ArrayUtils.isEmpty(nrAvailabilities);
updateVoNrState();
Log.d(TAG, "mHas5gCapability: " + mHas5gCapability
+ ",mIsNrEnabledFromCarrierConfig: " + mIsNrEnableFromCarrierConfig
+ ",mIsVonrEnabledFromCarrierConfig: " + mIsVonrEnabledFromCarrierConfig
@@ -162,7 +170,7 @@ public class NrAdvancedCallingPreferenceController extends TelephonyTogglePrefer
@Override
public boolean isChecked() {
return mTelephonyManager.isVoNrEnabled();
return mIsVoNrEnabled;
}
@VisibleForTesting
@@ -174,6 +182,19 @@ public class NrAdvancedCallingPreferenceController extends TelephonyTogglePrefer
return isCallStateIdle();
}
private void updateVoNrState() {
ThreadUtils.postOnBackgroundThread(() -> {
boolean result = mTelephonyManager.isVoNrEnabled();
if (result != mIsVoNrEnabled) {
Log.i(TAG, "VoNr state : " + result);
mIsVoNrEnabled = result;
mHandler.post(() -> {
updateState(mPreference);
});
}
});
}
private class PhoneCallStateTelephonyCallback extends TelephonyCallback implements
TelephonyCallback.CallStateListener {

View File

@@ -23,6 +23,7 @@ import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager.GET_ACTIVITIES
import android.content.pm.PackageManager.PackageInfoFlags
import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import com.android.settings.R
@@ -56,26 +57,21 @@ class PictureInPictureListModel(private val context: Context) :
private val packageManager = context.packageManager
override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) =
userIdFlow.map(::getPictureInPicturePackages).combine(appListFlow) {
pictureInPicturePackages,
appList ->
appList.map { app ->
createPictureInPictureRecord(
app = app,
isSupport = app.packageName in pictureInPicturePackages,
)
userIdFlow.map(::getPictureInPicturePackages)
.combine(appListFlow) { pictureInPicturePackages, appList ->
appList.map { app ->
createPictureInPictureRecord(
app = app,
isSupport = app.packageName in pictureInPicturePackages,
)
}
}
}
override fun transformItem(app: ApplicationInfo): PictureInPictureRecord {
return createPictureInPictureRecord(
app = app,
isSupport = app.installed &&
packageManager
.getPackageInfoAsUser(app.packageName, GET_ACTIVITIES_FLAGS, app.userId)
.supportsPictureInPicture(),
)
}
override fun transformItem(app: ApplicationInfo) = createPictureInPictureRecord(
app = app,
isSupport = app.installed &&
getPackageAndActivityInfo(app)?.supportsPictureInPicture() == true,
)
private fun createPictureInPictureRecord(app: ApplicationInfo, isSupport: Boolean) =
PictureInPictureRecord(
@@ -103,13 +99,36 @@ class PictureInPictureListModel(private val context: Context) :
}
private fun getPictureInPicturePackages(userId: Int): Set<String> =
packageManager
.getInstalledPackagesAsUser(GET_ACTIVITIES_FLAGS, userId)
getPackageAndActivityInfoList(userId)
.filter { it.supportsPictureInPicture() }
.map { it.packageName }
.toSet()
private fun getPackageAndActivityInfo(app: ApplicationInfo): PackageInfo? = try {
packageManager.getPackageInfoAsUser(app.packageName, GET_ACTIVITIES_FLAGS, app.userId)
} catch (e: Exception) {
// Query PackageManager.getPackageInfoAsUser() with GET_ACTIVITIES_FLAGS could cause
// exception sometimes. Since we reply on this flag to retrieve the Picture In Picture
// packages, we need to catch the exception to alleviate the impact before PackageManager
// fixing this issue or provide a better api.
Log.e(TAG, "Exception while getPackageInfoAsUser", e)
null
}
private fun getPackageAndActivityInfoList(userId: Int): List<PackageInfo> = try {
packageManager.getInstalledPackagesAsUser(GET_ACTIVITIES_FLAGS, userId)
} catch (e: Exception) {
// Query PackageManager.getPackageInfoAsUser() with GET_ACTIVITIES_FLAGS could cause
// exception sometimes. Since we reply on this flag to retrieve the Picture In Picture
// packages, we need to catch the exception to alleviate the impact before PackageManager
// fixing this issue or provide a better api.
Log.e(TAG, "Exception while getInstalledPackagesAsUser", e)
emptyList()
}
companion object {
private const val TAG = "PictureInPictureListModel"
private fun PackageInfo.supportsPictureInPicture() =
activities?.any(ActivityInfo::supportsPictureInPicture) ?: false

View File

@@ -0,0 +1,109 @@
/*
* 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.datausage
import android.content.Context
import android.content.pm.ApplicationInfo
import android.net.NetworkPolicyManager
import android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.datausage.DataSaverSummary.Companion.getUnrestrictedSummary
import com.android.settingslib.spaprivileged.model.app.AppListRepository
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.Mockito.`when` as whenever
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class DataSaverSummaryTest {
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var networkPolicyManager: NetworkPolicyManager
@Before
fun setUp() {
whenever(context.applicationContext).thenReturn(context)
whenever(NetworkPolicyManager.from(context)).thenReturn(networkPolicyManager)
}
@Test
fun getUnrestrictedSummary_whenTwoAppsAllowed() = runTest {
whenever(
networkPolicyManager.getUidsWithPolicy(POLICY_ALLOW_METERED_BACKGROUND)
).thenReturn(intArrayOf(APP1.uid, APP2.uid))
val summary =
getUnrestrictedSummary(context = context, appListRepository = FakeAppListRepository)
assertThat(summary)
.isEqualTo("2 apps allowed to use unrestricted data when Data Saver is on")
}
@Test
fun getUnrestrictedSummary_whenNoAppsAllowed() = runTest {
whenever(
networkPolicyManager.getUidsWithPolicy(POLICY_ALLOW_METERED_BACKGROUND)
).thenReturn(intArrayOf())
val summary =
getUnrestrictedSummary(context = context, appListRepository = FakeAppListRepository)
assertThat(summary)
.isEqualTo("0 apps allowed to use unrestricted data when Data Saver is on")
}
private companion object {
val APP1 = ApplicationInfo().apply { uid = 10001 }
val APP2 = ApplicationInfo().apply { uid = 10002 }
val APP3 = ApplicationInfo().apply { uid = 10003 }
object FakeAppListRepository : AppListRepository {
override suspend fun loadApps(
userId: Int,
loadInstantApps: Boolean,
matchAnyUserForAdmin: Boolean,
) = emptyList<ApplicationInfo>()
override fun showSystemPredicate(
userIdFlow: Flow<Int>,
showSystemFlow: Flow<Boolean>,
): Flow<(app: ApplicationInfo) -> Boolean> = flowOf { false }
override fun getSystemPackageNamesBlocking(userId: Int): Set<String> = emptySet()
override suspend fun loadAndFilterApps(userId: Int, isSystemApp: Boolean) =
listOf(APP1, APP2, APP3)
}
}
}

View File

@@ -23,6 +23,7 @@ import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.PackageInfoFlags
import android.os.DeadSystemRuntimeException
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
@@ -100,6 +101,23 @@ class PictureInPictureTest {
assertThat(record.isSupport).isTrue()
}
@Test
fun transform_getInstalledPackagesAsUserThrowsException_treatAsNotSupported() = runTest {
whenever(packageManager.getInstalledPackagesAsUser(any<PackageInfoFlags>(), anyInt()))
.thenThrow(DeadSystemRuntimeException())
val recordListFlow = listModel.transform(
userIdFlow = flowOf(USER_ID),
appListFlow = flowOf(listOf(PICTURE_IN_PICTURE_APP)),
)
val recordList = recordListFlow.first()
assertThat(recordList).hasSize(1)
val record = recordList[0]
assertThat(record.app).isSameInstanceAs(PICTURE_IN_PICTURE_APP)
assertThat(record.isSupport).isFalse()
}
@Test
fun transformItem() {
whenever(
@@ -114,6 +132,20 @@ class PictureInPictureTest {
assertThat(record.isSupport).isTrue()
}
@Test
fun transformItem_getPackageInfoAsUserThrowsException_treatAsNotSupported() {
whenever(
packageManager.getPackageInfoAsUser(
eq(PICTURE_IN_PICTURE_PACKAGE_NAME), any<PackageInfoFlags>(), eq(USER_ID)
)
).thenThrow(DeadSystemRuntimeException())
val record = listModel.transformItem(PICTURE_IN_PICTURE_APP)
assertThat(record.app).isSameInstanceAs(PICTURE_IN_PICTURE_APP)
assertThat(record.isSupport).isFalse()
}
@Test
fun filter_isSupport() = runTest {
val record = createRecord(isSupport = true)

View File

@@ -186,6 +186,7 @@ public class NrAdvancedCallingPreferenceControllerTest {
doReturn(true).when(mTelephonyManager).isVoNrEnabled();
mPreference.setChecked(false);
mController.init(SUB_ID);
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isTrue();