diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml index fd222bcf0f5..d030c36678c 100644 --- a/res/xml/development_settings.xml +++ b/res/xml/development_settings.xml @@ -199,10 +199,11 @@ android:title="@string/enable_terminal_title" android:summary="@string/enable_terminal_summary" /> - + android:summary="@string/enable_linux_terminal_summary" + android:fragment="com.android.settings.development.linuxterminal.LinuxTerminalDashboardFragment" /> + + + + + + diff --git a/src/com/android/settings/dashboard/profileselector/ProfileFragmentBridge.java b/src/com/android/settings/dashboard/profileselector/ProfileFragmentBridge.java index 1e5145acb6d..de6e158db2c 100644 --- a/src/com/android/settings/dashboard/profileselector/ProfileFragmentBridge.java +++ b/src/com/android/settings/dashboard/profileselector/ProfileFragmentBridge.java @@ -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()); } } diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectFragment.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectFragment.java index 494ef95f99b..270ab9c231e 100644 --- a/src/com/android/settings/dashboard/profileselector/ProfileSelectFragment.java +++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectFragment.java @@ -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; diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectLinuxTerminalFragment.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectLinuxTerminalFragment.java new file mode 100644 index 00000000000..c10a3e2e387 --- /dev/null +++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectLinuxTerminalFragment.java @@ -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); + } +} diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index 8f2a13cfcf8..922c897cab8 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -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; diff --git a/src/com/android/settings/development/LinuxTerminalPreferenceController.java b/src/com/android/settings/development/LinuxTerminalPreferenceController.java deleted file mode 100644 index 3e419e408fc..00000000000 --- a/src/com/android/settings/development/LinuxTerminalPreferenceController.java +++ /dev/null @@ -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; - } - } -} diff --git a/src/com/android/settings/development/linuxterminal/EnableLinuxTerminalPreferenceController.java b/src/com/android/settings/development/linuxterminal/EnableLinuxTerminalPreferenceController.java new file mode 100644 index 00000000000..5989aebb078 --- /dev/null +++ b/src/com/android/settings/development/linuxterminal/EnableLinuxTerminalPreferenceController.java @@ -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; + } + } +} diff --git a/src/com/android/settings/development/linuxterminal/LinuxTerminalDashboardFragment.java b/src/com/android/settings/development/linuxterminal/LinuxTerminalDashboardFragment.java new file mode 100644 index 00000000000..0eeeeddf790 --- /dev/null +++ b/src/com/android/settings/development/linuxterminal/LinuxTerminalDashboardFragment.java @@ -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 createPreferenceControllers( + @NonNull Context context) { + List 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); + } + }; +} diff --git a/src/com/android/settings/development/linuxterminal/LinuxTerminalPreferenceController.java b/src/com/android/settings/development/linuxterminal/LinuxTerminalPreferenceController.java new file mode 100644 index 00000000000..b3a0f801f61 --- /dev/null +++ b/src/com/android/settings/development/linuxterminal/LinuxTerminalPreferenceController.java @@ -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; + } + } +} diff --git a/tests/robotests/src/com/android/settings/development/LinuxTerminalPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/LinuxTerminalPreferenceControllerTest.java deleted file mode 100644 index 96b6d6aa3d5..00000000000 --- a/tests/robotests/src/com/android/settings/development/LinuxTerminalPreferenceControllerTest.java +++ /dev/null @@ -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); - } -} diff --git a/tests/robotests/src/com/android/settings/development/linuxterminal/EnableLinuxTerminalPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/linuxterminal/EnableLinuxTerminalPreferenceControllerTest.java new file mode 100644 index 00000000000..80d5ca5034d --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/linuxterminal/EnableLinuxTerminalPreferenceControllerTest.java @@ -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 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)); + } +} diff --git a/tests/robotests/src/com/android/settings/development/linuxterminal/LinuxTerminalPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/linuxterminal/LinuxTerminalPreferenceControllerTest.java new file mode 100644 index 00000000000..17c34356297 --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/linuxterminal/LinuxTerminalPreferenceControllerTest.java @@ -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(); + } +}