Make Linux terminal option profile aware

Bug: 374034911
Test: atest, plus following manual test \
  - Test tabbed UI with/without work profile \
  - Test that disabled by work profile launches alert dialog \
  - Test whether toggling an app only toggle the app for the user.
Flag: Build.RELEASE_AVF_SUPPORT_CUSTOM_VM_WITH_PARAVIRTUALIZED_DEVICES
Change-Id: I4bf0a2d521cf3e632f6c0320e0b5cc0154d5b68f
This commit is contained in:
Jaewan Kim
2024-11-04 15:25:53 +09:00
parent 15f88ddc70
commit ed3abffcfc
13 changed files with 684 additions and 274 deletions

View File

@@ -199,10 +199,11 @@
android:title="@string/enable_terminal_title"
android:summary="@string/enable_terminal_summary" />
<SwitchPreferenceCompat
android:key="enable_linux_terminal"
<Preference
android:key="linux_terminal"
android:title="@string/enable_linux_terminal_title"
android:summary="@string/enable_linux_terminal_summary" />
android:summary="@string/enable_linux_terminal_summary"
android:fragment="com.android.settings.development.linuxterminal.LinuxTerminalDashboardFragment" />
<SwitchPreferenceCompat
android:key="bugreport_in_power"

View File

@@ -0,0 +1,27 @@
<!--
Copyright 2024 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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/enable_linux_terminal_title">
<com.android.settings.widget.SettingsMainSwitchPreference
android:key="enable_linux_terminal"
android:title="@string/enable_linux_terminal_summary"
settings:controller="com.android.settings.development.linuxterminal.EnableLinuxTerminalPreferenceController" />
</PreferenceScreen>

View File

@@ -20,6 +20,7 @@ import android.util.ArrayMap;
import com.android.settings.accounts.AccountDashboardFragment;
import com.android.settings.applications.manageapplications.ManageApplications;
import com.android.settings.development.linuxterminal.LinuxTerminalDashboardFragment;
import com.android.settings.deviceinfo.StorageDashboardFragment;
import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
import com.android.settings.inputmethod.NewKeyboardLayoutEnabledLocalesFragment;
@@ -52,5 +53,8 @@ public class ProfileFragmentBridge {
ProfileSelectKeyboardFragment.class.getName());
FRAGMENT_MAP.put(NewKeyboardLayoutEnabledLocalesFragment.class.getName(),
ProfileSelectPhysicalKeyboardFragment.class.getName());
FRAGMENT_MAP.put(
LinuxTerminalDashboardFragment.class.getName(),
ProfileSelectLinuxTerminalFragment.class.getName());
}
}

View File

@@ -331,23 +331,29 @@ public abstract class ProfileSelectFragment extends DashboardFragment {
for (UserInfo userInfo : userInfos) {
if (userInfo.isMain()) {
fragments.add(createAndGetFragment(
ProfileType.PERSONAL,
bundle != null ? bundle : new Bundle(),
personalFragmentConstructor));
fragments.add(
createAndGetFragment(
ProfileType.PERSONAL,
userInfo.id,
bundle != null ? bundle : new Bundle(),
personalFragmentConstructor));
} else if (userInfo.isManagedProfile()) {
fragments.add(createAndGetFragment(
ProfileType.WORK,
bundle != null ? bundle.deepCopy() : new Bundle(),
workFragmentConstructor));
fragments.add(
createAndGetFragment(
ProfileType.WORK,
userInfo.id,
bundle != null ? bundle.deepCopy() : new Bundle(),
workFragmentConstructor));
} else if (Flags.allowPrivateProfile()
&& android.multiuser.Flags.enablePrivateSpaceFeatures()
&& userInfo.isPrivateProfile()) {
if (!privateSpaceInfoProvider.isPrivateSpaceLocked(context)) {
fragments.add(createAndGetFragment(
ProfileType.PRIVATE,
bundle != null ? bundle.deepCopy() : new Bundle(),
privateFragmentConstructor));
fragments.add(
createAndGetFragment(
ProfileType.PRIVATE,
userInfo.id,
bundle != null ? bundle.deepCopy() : new Bundle(),
privateFragmentConstructor));
}
} else {
Log.d(TAG, "Not showing tab for unsupported user " + userInfo);
@@ -364,8 +370,12 @@ public abstract class ProfileSelectFragment extends DashboardFragment {
}
private static Fragment createAndGetFragment(
@ProfileType int profileType, Bundle bundle, FragmentConstructor fragmentConstructor) {
@ProfileType int profileType,
int userId,
Bundle bundle,
FragmentConstructor fragmentConstructor) {
bundle.putInt(EXTRA_PROFILE, profileType);
bundle.putInt(EXTRA_USER_ID, userId);
final Fragment fragment = fragmentConstructor.constructAndGetFragment();
fragment.setArguments(bundle);
return fragment;

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2024 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.dashboard.profileselector;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import com.android.settings.development.DeveloperOptionAwareMixin;
import com.android.settings.development.linuxterminal.LinuxTerminalDashboardFragment;
/** Linux terminal preferences at developers option for personal/managed profile. */
public class ProfileSelectLinuxTerminalFragment extends ProfileSelectFragment
implements DeveloperOptionAwareMixin {
private static final String TAG = "ProfileSelLinuxTerminalFrag";
@Override
protected String getLogTag() {
return TAG;
}
@Override
@NonNull
public Fragment[] getFragments() {
return getFragments(
getContext(),
getArguments(),
LinuxTerminalDashboardFragment::new,
LinuxTerminalDashboardFragment::new,
LinuxTerminalDashboardFragment::new);
}
}

View File

@@ -72,6 +72,7 @@ import com.android.settings.development.bluetooth.BluetoothQualityDialogPreferen
import com.android.settings.development.bluetooth.BluetoothSampleRateDialogPreferenceController;
import com.android.settings.development.bluetooth.BluetoothStackLogPreferenceController;
import com.android.settings.development.graphicsdriver.GraphicsDriverEnableAngleAsSystemDriverController;
import com.android.settings.development.linuxterminal.LinuxTerminalPreferenceController;
import com.android.settings.development.qstile.DevelopmentTiles;
import com.android.settings.development.storage.SharedDataPreferenceController;
import com.android.settings.overlay.FeatureFactory;

View File

@@ -1,127 +0,0 @@
/*
* Copyright 2024 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.development;
import android.content.Context;
import android.content.pm.PackageManager;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
public class LinuxTerminalPreferenceController extends DeveloperOptionsPreferenceController
implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
private static final String TAG = "LinuxTerminalPrefCtrl";
private static final String ENABLE_TERMINAL_KEY = "enable_linux_terminal";
@NonNull
private final PackageManager mPackageManager;
@Nullable
private final String mTerminalPackageName;
public LinuxTerminalPreferenceController(@NonNull Context context) {
super(context);
mPackageManager = mContext.getPackageManager();
String packageName = mContext.getString(R.string.config_linux_terminal_app_package_name);
mTerminalPackageName =
isPackageInstalled(mPackageManager, packageName) ? packageName : null;
Log.d(TAG, "Terminal app package name=" + packageName + ", isAvailable=" + isAvailable());
}
// Avoid lazy initialization because this may be called before displayPreference().
@Override
public boolean isAvailable() {
// Returns true only if the terminal app is installed which only happens when the build flag
// RELEASE_AVF_SUPPORT_CUSTOM_VM_WITH_PARAVIRTUALIZED_DEVICES is true.
// TODO(b/343795511): Add explicitly check for the flag when it's accessible from Java code.
return getTerminalPackageName() != null;
}
@Override
@NonNull
public String getPreferenceKey() {
return ENABLE_TERMINAL_KEY;
}
@Override
public void displayPreference(@NonNull PreferenceScreen screen) {
super.displayPreference(screen);
mPreference.setEnabled(isAvailable());
}
@Override
public boolean onPreferenceChange(
@NonNull Preference preference, @NonNull Object newValue) {
String packageName = getTerminalPackageName();
if (packageName == null) {
return false;
}
boolean terminalEnabled = (Boolean) newValue;
int state = terminalEnabled
? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
: PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
mPackageManager.setApplicationEnabledSetting(packageName, state, /* flags=*/ 0);
((TwoStatePreference) mPreference).setChecked(terminalEnabled);
return true;
}
@Override
public void updateState(@NonNull Preference preference) {
String packageName = getTerminalPackageName();
if (packageName == null) {
return;
}
boolean isTerminalEnabled = mPackageManager.getApplicationEnabledSetting(packageName)
== PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
((TwoStatePreference) mPreference).setChecked(isTerminalEnabled);
}
// Can be mocked for testing
@VisibleForTesting
@Nullable
String getTerminalPackageName() {
return mTerminalPackageName;
}
private static boolean isPackageInstalled(PackageManager manager, String packageName) {
if (TextUtils.isEmpty(packageName)) {
return false;
}
try {
return manager.getPackageInfo(
packageName,
PackageManager.MATCH_ALL | PackageManager.MATCH_DISABLED_COMPONENTS) != null;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
}

View File

@@ -0,0 +1,143 @@
/*
* Copyright 2024 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.development.linuxterminal;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.widget.CompoundButton;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.widget.SettingsMainSwitchPreference;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
/** Preference controller for enable/disable toggle of the linux terminal */
public class EnableLinuxTerminalPreferenceController extends BasePreferenceController
implements CompoundButton.OnCheckedChangeListener, PreferenceControllerMixin {
@VisibleForTesting
static final int TERMINAL_PACKAGE_NAME_RESID = R.string.config_linux_terminal_app_package_name;
private static final String TAG = "LinuxTerminalPrefCtrl";
private static final String ENABLE_TERMINAL_KEY = "enable_linux_terminal";
@NonNull private final PackageManager mPackageManager;
private final boolean mIsPrimaryUser;
@Nullable private final String mTerminalPackageName;
@Nullable private SettingsMainSwitchPreference mPreference;
public EnableLinuxTerminalPreferenceController(
@NonNull Context context, @NonNull Context userAwareContext, int userId) {
this(context, userAwareContext, userId == UserHandle.myUserId());
}
@VisibleForTesting
EnableLinuxTerminalPreferenceController(
@NonNull Context context, @NonNull Context userAwareContext, boolean isPrimaryUser) {
super(context, ENABLE_TERMINAL_KEY);
mPackageManager = userAwareContext.getPackageManager();
mIsPrimaryUser = isPrimaryUser;
String packageName =
userAwareContext.getString(R.string.config_linux_terminal_app_package_name);
mTerminalPackageName =
isPackageInstalled(mPackageManager, packageName) ? packageName : null;
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public void displayPreference(@NonNull PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
if (mPreference != null) {
mPreference.addOnSwitchChangeListener(this);
}
}
@Override
public void onCheckedChanged(@NonNull CompoundButton buttonView, boolean isChecked) {
if (mTerminalPackageName == null) {
return;
}
int state =
isChecked
? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
: PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
mPackageManager.setApplicationEnabledSetting(mTerminalPackageName, state, /* flags= */ 0);
}
@Override
@SuppressWarnings("NullAway") // setDisabledByAdmin(EnforcedAdmin) doesn't have @Nullable
public void updateState(@NonNull Preference preference) {
if (mPreference != preference) {
return;
}
boolean isInstalled = (mTerminalPackageName != null);
if (isInstalled) {
mPreference.setDisabledByAdmin(/* admin= */ null);
mPreference.setEnabled(/* enabled= */ true);
boolean terminalEnabled =
mPackageManager.getApplicationEnabledSetting(mTerminalPackageName)
== PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
mPreference.setChecked(terminalEnabled);
} else {
if (mIsPrimaryUser) {
Log.e(TAG, "Terminal app doesn't exist for primary user but UI was shown");
mPreference.setDisabledByAdmin(/* admin= */ null);
mPreference.setEnabled(/* enabled= */ false);
} else {
// If admin hasn't enabled the system app, mark it as disabled by admin.
mPreference.setDisabledByAdmin(new EnforcedAdmin());
// Make it enabled, so clicking it would show error dialog.
mPreference.setEnabled(/* enabled= */ true);
}
mPreference.setChecked(/* checked= */ false);
}
}
private static boolean isPackageInstalled(PackageManager manager, String packageName) {
if (TextUtils.isEmpty(packageName)) {
return false;
}
try {
return manager.getPackageInfo(
packageName,
PackageManager.MATCH_ALL | PackageManager.MATCH_DISABLED_COMPONENTS)
!= null;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright 2024 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.development.linuxterminal;
import static android.content.Intent.EXTRA_USER_ID;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.UserHandle;
import androidx.annotation.NonNull;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.development.DevelopmentSettingsEnabler;
import com.android.settingslib.search.SearchIndexable;
import java.util.ArrayList;
import java.util.List;
/** Fragment shown for 'Linux terminal development' preference in developer option. */
@SearchIndexable
public class LinuxTerminalDashboardFragment extends DashboardFragment {
private static final String TAG = "LinuxTerminalFrag";
private Context mUserAwareContext;
private int mUserId;
@Override
public int getMetricsCategory() {
return SettingsEnums.LINUX_TERMINAL_DASHBOARD;
}
@NonNull
@Override
public String getLogTag() {
return TAG;
}
@Override
public int getPreferenceScreenResId() {
return R.xml.linux_terminal_settings;
}
@Override
public void onAttach(@NonNull Context context) {
// Initialize mUserId and mUserAwareContext before super.onAttach(),
// so createPreferenceControllers() can be called with proper values from super.onAttach().
int currentUserId = UserHandle.myUserId();
mUserId = getArguments().getInt(EXTRA_USER_ID, currentUserId);
mUserAwareContext =
(currentUserId == mUserId)
? context
: context.createContextAsUser(UserHandle.of(mUserId), /* flags= */ 0);
// Note: This calls createPreferenceControllers() inside.
super.onAttach(context);
}
@Override
@NonNull
public List<AbstractPreferenceController> createPreferenceControllers(
@NonNull Context context) {
List<AbstractPreferenceController> list = new ArrayList<>();
list.add(new EnableLinuxTerminalPreferenceController(context, mUserAwareContext, mUserId));
return list;
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.linux_terminal_settings) {
@Override
protected boolean isPageSearchEnabled(Context context) {
return DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context);
}
};
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2024 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.development.linuxterminal;
import android.content.Context;
import android.content.pm.PackageManager;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
/** Preference controller for Linux terminal option in developers option */
public class LinuxTerminalPreferenceController extends DeveloperOptionsPreferenceController
implements PreferenceControllerMixin {
@VisibleForTesting
static final int TERMINAL_PACKAGE_NAME_RESID = R.string.config_linux_terminal_app_package_name;
private static final String LINUX_TERMINAL_KEY = "linux_terminal";
@Nullable private final String mTerminalPackageName;
public LinuxTerminalPreferenceController(@NonNull Context context) {
super(context);
String packageName = context.getString(TERMINAL_PACKAGE_NAME_RESID);
mTerminalPackageName =
isPackageInstalled(context.getPackageManager(), packageName) ? packageName : null;
}
// Avoid lazy initialization because this may be called before displayPreference().
@Override
public boolean isAvailable() {
// Returns true only if the terminal app is installed which only happens when the build flag
// RELEASE_AVF_SUPPORT_CUSTOM_VM_WITH_PARAVIRTUALIZED_DEVICES is true.
// TODO(b/343795511): Add explicitly check for the flag when it's accessible from Java code.
return mTerminalPackageName != null;
}
@Override
@NonNull
public String getPreferenceKey() {
return LINUX_TERMINAL_KEY;
}
private static boolean isPackageInstalled(PackageManager manager, String packageName) {
if (TextUtils.isEmpty(packageName)) {
return false;
}
try {
return manager.getPackageInfo(
packageName,
PackageManager.MATCH_ALL | PackageManager.MATCH_DISABLED_COMPONENTS)
!= null;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
}

View File

@@ -1,131 +0,0 @@
/*
* Copyright 2024 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.development;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
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;
@RunWith(RobolectricTestRunner.class)
public class LinuxTerminalPreferenceControllerTest {
@Mock
private Context mContext;
@Mock
private SwitchPreference mPreference;
@Mock
private PreferenceScreen mPreferenceScreen;
@Mock
private PackageManager mPackageManager;
@Mock
private ApplicationInfo mApplicationInfo;
private String mTerminalPackageName = "com.android.virtualization.terminal";
private LinuxTerminalPreferenceController mController;
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
doReturn(mPackageManager).when(mContext).getPackageManager();
doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfo(
eq(mTerminalPackageName), any());
mController = spy(new LinuxTerminalPreferenceController(mContext));
doReturn(true).when(mController).isAvailable();
doReturn(mTerminalPackageName).when(mController).getTerminalPackageName();
when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
.thenReturn(mPreference);
mController.displayPreference(mPreferenceScreen);
}
@Test
public void isAvailable_whenPackageNameIsNull_returnsFalse() throws Exception {
mController = spy(new LinuxTerminalPreferenceController(mContext));
doReturn(null).when(mController).getTerminalPackageName();
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void isAvailable_whenAppDoesNotExist_returnsFalse() throws Exception {
doThrow(new NameNotFoundException()).when(mPackageManager).getApplicationInfo(
eq(mTerminalPackageName), any());
mController = spy(new LinuxTerminalPreferenceController(mContext));
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void onPreferenceChanged_turnOnTerminal() {
mController.onPreferenceChange(null, true);
verify(mPackageManager).setApplicationEnabledSetting(
mTerminalPackageName,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
/* flags= */ 0);
}
@Test
public void onPreferenceChanged_turnOffTerminal() {
mController.onPreferenceChange(null, false);
verify(mPackageManager).setApplicationEnabledSetting(
mTerminalPackageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
/* flags= */ 0);
}
@Test
public void updateState_preferenceShouldBeChecked() {
when(mPackageManager.getApplicationEnabledSetting(mTerminalPackageName))
.thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
mController.updateState(mPreference);
verify(mPreference).setChecked(true);
}
@Test
public void updateState_preferenceShouldNotBeChecked() {
when(mPackageManager.getApplicationEnabledSetting(mTerminalPackageName))
.thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
mController.updateState(mPreference);
verify(mPreference).setChecked(false);
}
}

View File

@@ -0,0 +1,178 @@
/*
* Copyright 2024 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.development.linuxterminal;
import static com.android.settings.development.linuxterminal.EnableLinuxTerminalPreferenceController.TERMINAL_PACKAGE_NAME_RESID;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import androidx.preference.PreferenceScreen;
import com.android.settings.widget.SettingsMainSwitchPreference;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.ParameterizedRobolectricTestRunner;
import java.util.Arrays;
import java.util.List;
/** Tests {@link EnableLinuxTerminalPreferenceController} */
@RunWith(ParameterizedRobolectricTestRunner.class)
public class EnableLinuxTerminalPreferenceControllerTest {
/** Defines parameters for parameterized test */
@ParameterizedRobolectricTestRunner.Parameters(
name = "isPrimaryUser={0}, installed={1}, enabled={2}")
public static List<Object[]> params() {
return Arrays.asList(
new Object[] {true, true, false},
new Object[] {true, true, true},
new Object[] {false, false, false},
new Object[] {false, true, false},
new Object[] {false, true, true});
}
@ParameterizedRobolectricTestRunner.Parameter(0)
public boolean mIsPrimaryUser;
@ParameterizedRobolectricTestRunner.Parameter(1)
public boolean mInstalled;
@ParameterizedRobolectricTestRunner.Parameter(2)
public boolean mEnabled;
@Mock private Context mContext;
@Mock private Context mUserContext;
@Mock private SettingsMainSwitchPreference mPreference;
@Mock private PreferenceScreen mPreferenceScreen;
@Mock private PackageManager mPackageManager;
@Mock private PackageInfo mPackageInfo;
private String mTerminalPackageName = "com.android.virtualization.terminal";
private EnableLinuxTerminalPreferenceController mController;
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
doReturn(mTerminalPackageName)
.when(mUserContext)
.getString(eq(TERMINAL_PACKAGE_NAME_RESID));
doReturn(mPackageManager).when(mUserContext).getPackageManager();
doReturn(mInstalled ? mPackageInfo : null)
.when(mPackageManager)
.getPackageInfo(eq(mTerminalPackageName), anyInt());
doReturn(
mEnabled
? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
: PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
.when(mPackageManager)
.getApplicationEnabledSetting(eq(mTerminalPackageName));
mController =
new EnableLinuxTerminalPreferenceController(mContext, mUserContext, mIsPrimaryUser);
doReturn(mPreference)
.when(mPreferenceScreen)
.findPreference(eq(mController.getPreferenceKey()));
mController.displayPreference(mPreferenceScreen);
mController.updateState(mPreference);
}
@Test
public void isAvailable_returnTrue() {
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void onCheckedChanged_whenChecked_turnOnTerminal() {
assumeTrue(mInstalled);
mController.onCheckedChanged(/* buttonView= */ null, /* isChecked= */ true);
verify(mPackageManager)
.setApplicationEnabledSetting(
mTerminalPackageName,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
/* flags= */ 0);
}
@Test
public void onCheckedChanged_whenUnchecked_turnOffTerminal() {
assumeTrue(mInstalled);
mController.onCheckedChanged(/* buttonView= */ null, /* isChecked= */ false);
verify(mPackageManager)
.setApplicationEnabledSetting(
mTerminalPackageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
/* flags= */ 0);
}
@Test
public void updateState_enabled() {
verify(mPreference).setEnabled(/* enabled= */ true);
}
@Test
public void updateState_whenEnabled_checked() {
assumeTrue(mEnabled);
verify(mPreference).setChecked(/* checked= */ true);
}
@Test
public void updateState_whenDisabled_unchecked() {
assumeFalse(mEnabled);
verify(mPreference).setChecked(/* checked= */ false);
}
@Test
public void updateState_withProfileWhenAllowed_enabledByAdmin() {
assumeFalse(mIsPrimaryUser);
assumeTrue(mInstalled);
verify(mPreference).setDisabledByAdmin(eq(null));
}
@Test
public void updateState_withProfileWhenNotAllowed_disabledByAdmin() {
assumeFalse(mIsPrimaryUser);
assumeFalse(mInstalled);
verify(mPreference).setDisabledByAdmin(any(EnforcedAdmin.class));
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright 2024 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.development.linuxterminal;
import static com.android.settings.development.linuxterminal.LinuxTerminalPreferenceController.TERMINAL_PACKAGE_NAME_RESID;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.eq;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
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;
/** Tests {@link LinuxTerminalPreferenceController} */
@RunWith(RobolectricTestRunner.class)
public class LinuxTerminalPreferenceControllerTest {
@Mock private Context mContext;
@Mock private PackageManager mPackageManager;
@Mock private PackageInfo mPackageInfo;
private String mTerminalPackageName = "com.android.virtualization.terminal";
private LinuxTerminalPreferenceController mController;
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
doReturn(mTerminalPackageName).when(mContext).getString(TERMINAL_PACKAGE_NAME_RESID);
doReturn(mPackageManager).when(mContext).getPackageManager();
doReturn(mPackageInfo)
.when(mPackageManager)
.getPackageInfo(eq(mTerminalPackageName), anyInt());
}
@Test
public void isAvailable_whenPackageExists_returnsTrue() throws NameNotFoundException {
mController = new LinuxTerminalPreferenceController(mContext);
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void isAvailable_whenPackageNameIsNull_returnsFalse() {
doReturn(null).when(mContext).getString(TERMINAL_PACKAGE_NAME_RESID);
mController = new LinuxTerminalPreferenceController(mContext);
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void isAvailable_whenAppDoesNotExist_returnsFalse() throws Exception {
doThrow(new NameNotFoundException())
.when(mPackageManager)
.getPackageInfo(eq(mTerminalPackageName), anyInt());
mController = new LinuxTerminalPreferenceController(mContext);
assertThat(mController.isAvailable()).isFalse();
}
}