Snap for 10341497 from e992927655 to udc-qpr1-release

Change-Id: I7279d7b7f2412a7c1e3a1c425a434b05ae969fec
This commit is contained in:
Android Build Coastguard Worker
2023-06-17 05:23:42 +00:00
17 changed files with 464 additions and 103 deletions

View File

@@ -10542,8 +10542,6 @@
<string name="platform_compat_default_disabled_title">Default disabled changes</string>
<!-- Title for target SDK gated app compat changes category (do not translate 'targetSdkVersion') [CHAR LIMIT=50] -->
<string name="platform_compat_target_sdk_title">Enabled for targetSdkVersion &gt;= <xliff:g id="number" example="29">%d</xliff:g></string>
<!-- Title for the dialog shown when no debuggable apps are available [CHAR LIMIT=30] -->
<string name="platform_compat_dialog_title_no_apps">No apps available</string>
<!-- Explanatory text shown when no debuggable apps are available [CHAR LIMIT=NONE] -->
<string name="platform_compat_dialog_text_no_apps">App compatibility changes can only be modified for debuggable apps. Install a debuggable app and try again.</string>

View File

@@ -258,7 +258,7 @@
android:key="platform_compat_dashboard"
android:title="@string/platform_compat_dashboard_title"
android:summary="@string/platform_compat_dashboard_summary"
android:fragment="com.android.settings.development.compat.PlatformCompatDashboard"
settings:controller="com.android.settings.spa.development.compat.PlatformCompatPreferenceController"
/>
<SwitchPreference

View File

@@ -623,9 +623,9 @@ public class FingerprintSettings extends SubSettings {
return; // Activity went away
}
final Preference addPreference = findPreference(KEY_FINGERPRINT_ADD);
mAddFingerprintPreference = findPreference(KEY_FINGERPRINT_ADD);
if (addPreference == null) {
if (mAddFingerprintPreference == null) {
return; // b/275519315 Skip if updateAddPreference() invoke before addPreference()
}

View File

@@ -25,12 +25,4 @@ public interface DevelopmentOptionsActivityRequestCodes {
int REQUEST_CODE_DEBUG_APP = 1;
int REQUEST_MOCK_LOCATION_APP = 2;
int REQUEST_CODE_ANGLE_ALL_USE_ANGLE = 3;
int REQUEST_CODE_ANGLE_DRIVER_PKGS = 4;
int REQUEST_CODE_ANGLE_DRIVER_VALUES = 5;
int REQUEST_COMPAT_CHANGE_APP = 6;
}

View File

@@ -17,21 +17,16 @@
package com.android.settings.development.compat;
import static com.android.internal.compat.OverrideAllowedState.ALLOWED;
import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes.REQUEST_COMPAT_CHANGE_APP;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.settings.SettingsEnums;
import android.compat.Compatibility.ChangeConfig;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.TextUtils;
import android.util.ArraySet;
import androidx.annotation.VisibleForTesting;
@@ -40,35 +35,28 @@ import androidx.preference.Preference.OnPreferenceChangeListener;
import androidx.preference.PreferenceCategory;
import androidx.preference.SwitchPreference;
import com.android.internal.compat.AndroidBuildClassifier;
import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.internal.compat.IPlatformCompat;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.development.AppPicker;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* Dashboard for Platform Compat preferences.
*/
public class PlatformCompatDashboard extends DashboardFragment {
private static final String TAG = "PlatformCompatDashboard";
private static final String COMPAT_APP = "compat_app";
public static final String COMPAT_APP = "compat_app";
private IPlatformCompat mPlatformCompat;
private CompatibilityChangeInfo[] mChanges;
private AndroidBuildClassifier mAndroidBuildClassifier = new AndroidBuildClassifier();
private boolean mShouldStartAppPickerOnResume = true;
@VisibleForTesting
String mSelectedApp;
@@ -108,32 +96,6 @@ public class PlatformCompatDashboard extends DashboardFragment {
} catch (RemoteException e) {
throw new RuntimeException("Could not list changes!", e);
}
if (icicle != null) {
mShouldStartAppPickerOnResume = false;
mSelectedApp = icicle.getString(COMPAT_APP);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_COMPAT_CHANGE_APP) {
mShouldStartAppPickerOnResume = false;
switch (resultCode) {
case Activity.RESULT_OK:
mSelectedApp = data.getAction();
break;
case Activity.RESULT_CANCELED:
if (TextUtils.isEmpty(mSelectedApp)) {
finish();
}
break;
case AppPicker.RESULT_NO_MATCHING_APPS:
mSelectedApp = null;
break;
}
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
@Override
@@ -142,33 +104,18 @@ public class PlatformCompatDashboard extends DashboardFragment {
if (isFinishingOrDestroyed()) {
return;
}
if (!mShouldStartAppPickerOnResume) {
if (TextUtils.isEmpty(mSelectedApp)) {
new AlertDialog.Builder(getContext())
.setTitle(R.string.platform_compat_dialog_title_no_apps)
.setMessage(R.string.platform_compat_dialog_text_no_apps)
.setPositiveButton(R.string.okay, (dialog, which) -> finish())
.setOnDismissListener(dialog -> finish())
.setCancelable(false)
.show();
return;
}
try {
final ApplicationInfo applicationInfo = getApplicationInfo();
addPreferences(applicationInfo);
return;
} catch (PackageManager.NameNotFoundException e) {
mShouldStartAppPickerOnResume = true;
mSelectedApp = null;
}
Bundle arguments = getArguments();
if (arguments == null) {
finish();
return;
}
mSelectedApp = arguments.getString(COMPAT_APP);
try {
final ApplicationInfo applicationInfo = getApplicationInfo();
addPreferences(applicationInfo);
} catch (PackageManager.NameNotFoundException ignored) {
finish();
}
startAppPicker();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(COMPAT_APP, mSelectedApp);
}
private void addPreferences(ApplicationInfo applicationInfo) {
@@ -266,12 +213,6 @@ public class PlatformCompatDashboard extends DashboardFragment {
appPreference.setIcon(icon);
appPreference.setSummary(getString(R.string.platform_compat_selected_app_summary,
mSelectedApp, applicationInfo.targetSdkVersion));
appPreference.setKey(mSelectedApp);
appPreference.setOnPreferenceClickListener(
preference -> {
startAppPicker();
return true;
});
return appPreference;
}
@@ -294,17 +235,6 @@ public class PlatformCompatDashboard extends DashboardFragment {
}
}
private void startAppPicker() {
final Intent intent = new Intent(getContext(), AppPicker.class)
.putExtra(AppPicker.EXTRA_INCLUDE_NOTHING, false);
// If build is neither userdebug nor eng, only include debuggable apps
final boolean debuggableBuild = mAndroidBuildClassifier.isDebuggableBuild();
if (!debuggableBuild) {
intent.putExtra(AppPicker.EXTRA_DEBUGGABLE, true /* value */);
}
startActivityForResult(intent, REQUEST_COMPAT_CHANGE_APP);
}
private class CompatChangePreferenceChangeListener implements OnPreferenceChangeListener {
private final long changeId;

View File

@@ -0,0 +1,81 @@
/*
* 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.slices;
import android.content.ContentResolver;
import android.net.Uri;
import android.provider.SettingsSlicesContract;
/**
* A utility class to check slice Uris for restriction.
*/
public class RestrictedSliceUtils {
/**
* Uri for the notifying open networks Slice.
*/
private static final Uri NOTIFY_OPEN_NETWORKS_SLICE_URI = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
.appendPath("notify_open_networks")
.build();
/**
* Uri for the auto turning on Wi-Fi Slice.
*/
private static final Uri AUTO_TURN_ON_WIFI_SLICE_URI = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
.appendPath("enable_wifi_wakeup")
.build();
/**
* Uri for the usb tethering Slice.
*/
private static final Uri USB_TETHERING_SLICE_URI = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
.appendPath("enable_usb_tethering")
.build();
/**
* Uri for the bluetooth tethering Slice.
*/
private static final Uri BLUETOOTH_TETHERING_SLICE_URI = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
.appendPath("enable_bluetooth_tethering_2")
.build();
/**
* Returns true if the slice Uri restricts access to guest user.
*/
public static boolean isGuestRestricted(Uri sliceUri) {
if (AUTO_TURN_ON_WIFI_SLICE_URI.equals(sliceUri)
|| NOTIFY_OPEN_NETWORKS_SLICE_URI.equals(sliceUri)
|| BLUETOOTH_TETHERING_SLICE_URI.equals(sliceUri)
|| USB_TETHERING_SLICE_URI.equals(sliceUri)
|| CustomSliceRegistry.MOBILE_DATA_SLICE_URI.equals(sliceUri)) {
return true;
}
return false;
}
}

View File

@@ -30,6 +30,7 @@ import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Binder;
import android.os.StrictMode;
import android.os.UserManager;
import android.provider.Settings;
import android.provider.SettingsSlicesContract;
import android.text.TextUtils;
@@ -233,6 +234,14 @@ public class SettingsSliceProvider extends SliceProvider {
getContext().getTheme().rebase();
}
// Checking if some semi-sensitive slices are requested by a guest user. If so, will
// return an empty slice.
final UserManager userManager = getContext().getSystemService(UserManager.class);
if (userManager.isGuestUser() && RestrictedSliceUtils.isGuestRestricted(sliceUri)) {
Log.i(TAG, "Guest user access denied.");
return null;
}
// Before adding a slice to {@link CustomSliceManager}, please get approval
// from the Settings team.
if (CustomSliceRegistry.isValidUri(sliceUri)) {

View File

@@ -36,6 +36,7 @@ import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
import com.android.settings.spa.app.specialaccess.UseFullScreenIntentAppListProvider
import com.android.settings.spa.core.instrumentation.SpaLogProvider
import com.android.settings.spa.development.UsageStatsPageProvider
import com.android.settings.spa.development.compat.PlatformCompatAppListPageProvider
import com.android.settings.spa.home.HomePageProvider
import com.android.settings.spa.network.NetworkAndInternetPageProvider
import com.android.settings.spa.notification.AppListNotificationsPageProvider
@@ -83,6 +84,7 @@ open class SettingsSpaEnvironment(context: Context) : SpaEnvironment(context) {
LanguageAndInputPageProvider,
AppLanguagesPageProvider,
UsageStatsPageProvider,
PlatformCompatAppListPageProvider,
BackgroundInstalledAppsPageProvider,
CloneAppInfoSettingsProvider,
NetworkAndInternetPageProvider,
@@ -95,5 +97,5 @@ open class SettingsSpaEnvironment(context: Context) : SpaEnvironment(context) {
override val logger =
if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_ENABLE_SPA_METRICS))
SpaLogProvider
else object: SpaLogger {}
else object : SpaLogger {}
}

View File

@@ -0,0 +1,38 @@
/*
* 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.spa.development.compat
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import com.android.settings.R
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.rememberContext
import com.android.settingslib.spaprivileged.template.app.AppListPage
object PlatformCompatAppListPageProvider : SettingsPageProvider {
override val name = "PlatformCompatAppList"
@Composable
override fun Page(arguments: Bundle?) {
AppListPage(
title = stringResource(R.string.platform_compat_dashboard_title),
listModel = rememberContext(::PlatformCompatAppListModel),
noItemMessage = stringResource(R.string.platform_compat_dialog_text_no_apps),
)
}
}

View File

@@ -0,0 +1,72 @@
/*
* 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.spa.development.compat
import android.app.settings.SettingsEnums
import android.content.Context
import android.content.pm.ApplicationInfo
import android.os.Build
import androidx.compose.runtime.Composable
import androidx.core.os.bundleOf
import com.android.settings.core.SubSettingLauncher
import com.android.settings.development.compat.PlatformCompatDashboard
import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.framework.util.filterItem
import com.android.settingslib.spa.framework.util.mapItem
import com.android.settingslib.spaprivileged.model.app.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.android.settingslib.spaprivileged.model.app.hasFlag
import com.android.settingslib.spaprivileged.model.app.userHandle
import com.android.settingslib.spaprivileged.template.app.AppListItem
import com.android.settingslib.spaprivileged.template.app.AppListItemModel
import kotlinx.coroutines.flow.Flow
data class PlatformCompatAppRecord(
override val app: ApplicationInfo,
) : AppRecord
class PlatformCompatAppListModel(
private val context: Context,
) : AppListModel<PlatformCompatAppRecord> {
override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) =
appListFlow.mapItem(::PlatformCompatAppRecord)
override fun filter(
userIdFlow: Flow<Int>, option: Int, recordListFlow: Flow<List<PlatformCompatAppRecord>>,
) = recordListFlow.filterItem { record ->
Build.IS_DEBUGGABLE || record.app.hasFlag(ApplicationInfo.FLAG_DEBUGGABLE)
}
@Composable
override fun getSummary(option: Int, record: PlatformCompatAppRecord) =
stateOf(record.app.packageName)
@Composable
override fun AppListItemModel<PlatformCompatAppRecord>.AppItem() {
AppListItem { navigateToAppCompat(app = record.app) }
}
private fun navigateToAppCompat(app: ApplicationInfo) {
SubSettingLauncher(context)
.setDestination(PlatformCompatDashboard::class.qualifiedName)
.setSourceMetricsCategory(SettingsEnums.DEVELOPMENT)
.setArguments(bundleOf(PlatformCompatDashboard.COMPAT_APP to app.packageName))
.setUserHandle(app.userHandle)
.launch()
}
}

View File

@@ -0,0 +1,34 @@
/*
* 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.spa.development.compat
import android.content.Context
import androidx.preference.Preference
import com.android.settings.core.BasePreferenceController
import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
class PlatformCompatPreferenceController(context: Context, preferenceKey: String) :
BasePreferenceController(context, preferenceKey) {
override fun getAvailabilityStatus() = AVAILABLE
override fun handlePreferenceTreeClick(preference: Preference): Boolean {
if (preference.key == mPreferenceKey) {
mContext.startSpaActivity(PlatformCompatAppListPageProvider.name)
return true
}
return false
}
}

View File

@@ -79,6 +79,7 @@ public class UserDetailsSettings extends SettingsPreferenceFragment
/** Whether to enable the app_copying fragment. */
private static final boolean SHOW_APP_COPYING_PREF = false;
private static final int MESSAGE_PADDING = 20;
private UserManager mUserManager;
private UserCapabilities mUserCaps;
@@ -274,6 +275,7 @@ public class UserDetailsSettings extends SettingsPreferenceFragment
context.getDrawable(com.android.settingslib.R.drawable.ic_admin_panel_settings));
dialogHelper.setTitle(R.string.user_revoke_admin_confirm_title);
dialogHelper.setMessage(R.string.user_revoke_admin_confirm_message);
dialogHelper.setMessagePadding(MESSAGE_PADDING);
dialogHelper.setPositiveButton(R.string.remove, view -> {
updateUserAdminStatus(false);
dialogHelper.getDialog().dismiss();
@@ -294,6 +296,7 @@ public class UserDetailsSettings extends SettingsPreferenceFragment
context.getDrawable(com.android.settingslib.R.drawable.ic_admin_panel_settings));
dialogHelper.setTitle(com.android.settingslib.R.string.user_grant_admin_title);
dialogHelper.setMessage(com.android.settingslib.R.string.user_grant_admin_message);
dialogHelper.setMessagePadding(MESSAGE_PADDING);
dialogHelper.setPositiveButton(com.android.settingslib.R.string.user_grant_admin_button,
view -> {
updateUserAdminStatus(true);

View File

@@ -20,19 +20,29 @@ import android.content.Context;
import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiManager;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.SwitchPreference;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.wifi.repository.WifiHotspotRepository;
public class WifiTetherAutoOffPreferenceController extends BasePreferenceController implements
Preference.OnPreferenceChangeListener {
private final WifiManager mWifiManager;
private boolean mSettingsOn;
@VisibleForTesting
boolean mNeedShutdownSecondarySap;
public WifiTetherAutoOffPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
WifiHotspotRepository wifiHotspotRepository = FeatureFactory.getFactory(context)
.getWifiFeatureProvider().getWifiHotspotRepository();
if (wifiHotspotRepository.isSpeedFeatureAvailable() && wifiHotspotRepository.isDualBand()) {
mNeedShutdownSecondarySap = true;
}
mWifiManager = context.getSystemService(WifiManager.class);
}
@@ -51,14 +61,15 @@ public class WifiTetherAutoOffPreferenceController extends BasePreferenceControl
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean settingsOn = (Boolean) newValue;
SoftApConfiguration softApConfiguration = mWifiManager.getSoftApConfiguration();
SoftApConfiguration newSoftApConfiguration =
new SoftApConfiguration.Builder(softApConfiguration)
.setAutoShutdownEnabled(settingsOn)
.build();
boolean settingsOn = (Boolean) newValue;
SoftApConfiguration.Builder configBuilder =
new SoftApConfiguration.Builder(mWifiManager.getSoftApConfiguration());
configBuilder.setAutoShutdownEnabled(settingsOn);
if (mNeedShutdownSecondarySap) {
configBuilder.setBridgedModeOpportunisticShutdownEnabled(settingsOn);
}
mSettingsOn = settingsOn;
return mWifiManager.setSoftApConfiguration(newSoftApConfiguration);
return mWifiManager.setSoftApConfiguration(configBuilder.build());
}
public boolean isEnabled() {

View File

@@ -119,6 +119,7 @@ public class SettingsSliceProviderTest {
private Context mContext;
private SettingsSliceProvider mProvider;
private ShadowPackageManager mPackageManager;
private ShadowUserManager mShadowUserManager;
@Mock
private SliceManager mManager;
@@ -157,6 +158,7 @@ public class SettingsSliceProviderTest {
when(mManager.getPinnedSlices()).thenReturn(Collections.emptyList());
mPackageManager = Shadows.shadowOf(mContext.getPackageManager());
mShadowUserManager = ShadowUserManager.getShadow();
SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
}
@@ -292,6 +294,37 @@ public class SettingsSliceProviderTest {
assertThat(ShadowTheme.isThemeRebased()).isFalse();
}
@Test
public void onBindSlice_guestRestricted_returnsNull() {
final String key = "enable_usb_tethering";
mShadowUserManager.setGuestUser(true);
final Uri testUri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
.appendPath(key)
.build();
final Slice slice = mProvider.onBindSlice(testUri);
assertThat(slice).isNull();
}
@Test
public void onBindSlice_notGuestRestricted_returnsNotNull() {
final String key = "enable_usb_tethering";
final Uri testUri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
.appendPath(key)
.build();
final Slice slice = mProvider.onBindSlice(testUri);
assertThat(slice).isNotNull();
}
@Test
public void getDescendantUris_fullActionUri_returnsSelf() {
final Collection<Uri> descendants = mProvider.onGetSliceDescendants(ACTION_SLICE_URI);

View File

@@ -55,6 +55,7 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager
private int[] profileIdsForUser = new int[0];
private boolean mUserSwitchEnabled;
private Bundle mDefaultGuestUserRestriction = new Bundle();
private boolean mIsGuestUser = false;
private @UserManager.UserSwitchabilityResult int mSwitchabilityStatus =
UserManager.SWITCHABILITY_STATUS_OK;
@@ -270,4 +271,13 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager
mUserProfileInfos.get(i).flags |= UserInfo.FLAG_ADMIN;
}
}
@Implementation
protected boolean isGuestUser() {
return mIsGuestUser;
}
public void setGuestUser(boolean isGuestUser) {
mIsGuestUser = isGuestUser;
}
}

View File

@@ -18,6 +18,7 @@ package com.android.settings.wifi.tether;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -28,6 +29,10 @@ import android.net.wifi.WifiManager;
import androidx.preference.SwitchPreference;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.wifi.factory.WifiFeatureProvider;
import com.android.settings.wifi.repository.WifiHotspotRepository;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -54,6 +59,8 @@ public class WifiTetherAutoOffPreferenceControllerTest {
mContext = spy(RuntimeEnvironment.application);
WifiFeatureProvider provider = FakeFeatureFactory.setupForTest().getWifiFeatureProvider();
when(provider.getWifiHotspotRepository()).thenReturn(mock(WifiHotspotRepository.class));
when(mContext.getSystemService(WifiManager.class)).thenReturn(mWifiManager);
mSoftApConfiguration = new SoftApConfiguration.Builder().build();
when(mWifiManager.getSoftApConfiguration()).thenReturn(mSoftApConfiguration);
@@ -101,6 +108,32 @@ public class WifiTetherAutoOffPreferenceControllerTest {
assertThat(mSwitchPreference.isChecked()).isTrue();
}
@Test
public void onPreferenceChange_needShutdownSecondarySap_setSecondarySap() {
mController.mNeedShutdownSecondarySap = true;
setConfigShutdownSecondarySap(false);
mController.onPreferenceChange(mSwitchPreference, true);
ArgumentCaptor<SoftApConfiguration> config =
ArgumentCaptor.forClass(SoftApConfiguration.class);
verify(mWifiManager).setSoftApConfiguration(config.capture());
assertThat(config.getValue().isBridgedModeOpportunisticShutdownEnabled()).isTrue();
}
@Test
public void onPreferenceChange_noNeedShutdownSecondarySap_doNotSetSecondarySap() {
mController.mNeedShutdownSecondarySap = false;
setConfigShutdownSecondarySap(false);
mController.onPreferenceChange(mSwitchPreference, true);
ArgumentCaptor<SoftApConfiguration> config =
ArgumentCaptor.forClass(SoftApConfiguration.class);
verify(mWifiManager).setSoftApConfiguration(config.capture());
assertThat(config.getValue().isBridgedModeOpportunisticShutdownEnabled()).isFalse();
}
private boolean getAutoOffSetting() {
ArgumentCaptor<SoftApConfiguration> softApConfigCaptor =
ArgumentCaptor.forClass(SoftApConfiguration.class);
@@ -115,4 +148,12 @@ public class WifiTetherAutoOffPreferenceControllerTest {
.build();
when(mWifiManager.getSoftApConfiguration()).thenReturn(mSoftApConfiguration);
}
private void setConfigShutdownSecondarySap(boolean enabled) {
mSoftApConfiguration =
new SoftApConfiguration.Builder(mSoftApConfiguration)
.setBridgedModeOpportunisticShutdownEnabled(enabled)
.build();
when(mWifiManager.getSoftApConfiguration()).thenReturn(mSoftApConfiguration);
}
}

View File

@@ -0,0 +1,107 @@
/*
* 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.spa.development.compat
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.PackageInfoFlags
import androidx.compose.runtime.State
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
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.Mockito.any
import org.mockito.Mockito.anyInt
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 PlatformCompatAppListModelTest {
@get:Rule
val composeTestRule = createComposeRule()
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
@Spy
private val context: Context = ApplicationProvider.getApplicationContext()
@Mock
private lateinit var packageManager: PackageManager
private lateinit var listModel: PlatformCompatAppListModel
@Before
fun setUp() {
whenever(context.packageManager).thenReturn(packageManager)
whenever(packageManager.getInstalledPackagesAsUser(any<PackageInfoFlags>(), anyInt()))
.thenReturn(emptyList())
listModel = PlatformCompatAppListModel(context)
}
@Test
fun transform() = runTest {
val recordListFlow = listModel.transform(
userIdFlow = flowOf(USER_ID),
appListFlow = flowOf(listOf(APP)),
)
val recordList = recordListFlow.first()
assertThat(recordList).hasSize(1)
val record = recordList[0]
assertThat(record.app).isSameInstanceAs(APP)
}
@Test
fun getSummary() = runTest {
val summaryState = getSummaryState(APP)
assertThat(summaryState.value).isEqualTo(PACKAGE_NAME)
}
private fun getSummaryState(app: ApplicationInfo): State<String> {
lateinit var summary: State<String>
composeTestRule.setContent {
summary = listModel.getSummary(
option = 0,
record = PlatformCompatAppRecord(app),
)
}
return summary
}
private companion object {
const val USER_ID = 0
const val PACKAGE_NAME = "package.name"
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
}
}