diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index 87d8c17e307..f7be1aa47b1 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -354,13 +354,18 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra final NfcVerboseVendorLogPreferenceController nfcVerboseLogController = getDevelopmentOptionsController( NfcVerboseVendorLogPreferenceController.class); + final GraphicsDriverEnableAngleAsSystemDriverController enableAngleController = + getDevelopmentOptionsController( + GraphicsDriverEnableAngleAsSystemDriverController.class); // If hardware offload isn't default value, we must reboot after disable // developer options. Show a dialog for the user to confirm. if ((a2dpController == null || a2dpController.isDefaultValue()) && (leAudioController == null || leAudioController.isDefaultValue()) && (nfcSnoopLogController == null || nfcSnoopLogController.isDefaultValue()) && (nfcVerboseLogController == null - || nfcVerboseLogController.isDefaultValue())) { + || nfcVerboseLogController.isDefaultValue()) + && (enableAngleController == null + || enableAngleController.isDefaultValue())) { disableDeveloperOptions(); } else { DisableDevSettingsDialogFragment.show(this /* host */); diff --git a/src/com/android/settings/development/DisableDevSettingsDialogFragment.java b/src/com/android/settings/development/DisableDevSettingsDialogFragment.java index 5db2ed4281b..224768b0561 100644 --- a/src/com/android/settings/development/DisableDevSettingsDialogFragment.java +++ b/src/com/android/settings/development/DisableDevSettingsDialogFragment.java @@ -45,6 +45,10 @@ public class DisableDevSettingsDialogFragment extends InstrumentedDialogFragment public static void show(DevelopmentSettingsDashboardFragment host) { final DisableDevSettingsDialogFragment dialog = new DisableDevSettingsDialogFragment(); dialog.setTargetFragment(host, 0 /* requestCode */); + // We need to handle data changes and switch state based on which button user clicks, + // therefore we should enforce user to click one of the buttons + // by disallowing dialog dismiss through tapping outside of dialog bounds. + dialog.setCancelable(false); final FragmentManager manager = host.getActivity().getSupportFragmentManager(); dialog.show(manager, TAG); } diff --git a/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverController.java b/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverController.java index 1a065a96aaa..b3af95ec0cc 100644 --- a/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverController.java +++ b/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverController.java @@ -81,6 +81,11 @@ public class GraphicsDriverEnableAngleAsSystemDriverController this(context, fragment, new Injector()); } + private boolean isAngleSupported() { + return TextUtils.equals( + mSystemProperties.get(PROPERTY_RO_GFX_ANGLE_SUPPORTED, ""), "true"); + } + @VisibleForTesting GraphicsDriverEnableAngleAsSystemDriverController( Context context, DevelopmentSettingsDashboardFragment fragment, Injector injector) { @@ -118,38 +123,44 @@ public class GraphicsDriverEnableAngleAsSystemDriverController this); } - @Override - public void updateState(Preference preference) { - // set switch on if "persist.graphics.egl" is "angle" and angle is built in /vendor - // set switch off otherwise. + /** Return the default value of "persist.graphics.egl" */ + public boolean isDefaultValue() { + if (!isAngleSupported()) { + return true; + } + final String currentGlesDriver = mSystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL, ""); - final boolean isAngle = TextUtils.equals(ANGLE_DRIVER_SUFFIX, currentGlesDriver); - final boolean isAngleSupported = - TextUtils.equals( - mSystemProperties.get(PROPERTY_RO_GFX_ANGLE_SUPPORTED, ""), "true"); - ((SwitchPreference) mPreference).setChecked(isAngle && isAngleSupported); - ((SwitchPreference) mPreference).setEnabled(isAngleSupported); + // default value of "persist.graphics.egl" is "" + return TextUtils.isEmpty(currentGlesDriver); } @Override - protected void onDeveloperOptionsSwitchEnabled() { - // only enable the switch if ro.gfx.angle.supported is true - // we use ro.gfx.angle.supported to indicate if ANGLE libs are installed under /vendor - final boolean isAngleSupported = - TextUtils.equals( - mSystemProperties.get(PROPERTY_RO_GFX_ANGLE_SUPPORTED, ""), "true"); - ((SwitchPreference) mPreference).setEnabled(isAngleSupported); + public void updateState(Preference preference) { + super.updateState(preference); + if (isAngleSupported()) { + // set switch on if "persist.graphics.egl" is "angle" and angle is built in /vendor + // set switch off otherwise. + final String currentGlesDriver = + mSystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL, ""); + final boolean isAngle = TextUtils.equals(ANGLE_DRIVER_SUFFIX, currentGlesDriver); + ((SwitchPreference) mPreference).setChecked(isAngle); + } else { + mPreference.setEnabled(false); + ((SwitchPreference) mPreference).setChecked(false); + } } @Override protected void onDeveloperOptionsSwitchDisabled() { - // 1) set the persist.graphics.egl empty string - GraphicsEnvironment.getInstance().toggleAngleAsSystemDriver(false); - // 2) reset the switch - ((SwitchPreference) mPreference).setChecked(false); - // 3) disable switch - ((SwitchPreference) mPreference).setEnabled(false); + // 1) disable the switch + super.onDeveloperOptionsSwitchDisabled(); + if (isAngleSupported()) { + // 2) set the persist.graphics.egl empty string + GraphicsEnvironment.getInstance().toggleAngleAsSystemDriver(false); + // 3) reset the switch + ((SwitchPreference) mPreference).setChecked(false); + } } void toggleSwitchBack() { diff --git a/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.java b/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.java index b585ecec6a4..5786dfc99cd 100644 --- a/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.java +++ b/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.java @@ -17,6 +17,8 @@ package com.android.settings.network.telephony; import android.content.Context; +import android.os.Handler; +import android.os.Looper; import android.os.PersistableBundle; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; @@ -33,9 +35,10 @@ import com.android.internal.telephony.util.ArrayUtils; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; +import com.android.settingslib.utils.ThreadUtils; /** - * Preference controller for "Enhanced 4G LTE" + * Preference controller for "Voice over NR". */ public class NrAdvancedCallingPreferenceController extends TelephonyTogglePreferenceController implements LifecycleObserver, OnStart, OnStop { @@ -50,8 +53,11 @@ public class NrAdvancedCallingPreferenceController extends TelephonyTogglePrefer private boolean mIsVonrVisibleFromCarrierConfig = false; private boolean mIsNrEnableFromCarrierConfig = false; private boolean mHas5gCapability = false; + private boolean mIsVoNrEnabled = false; private Integer mCallState; + private Handler mHandler = new Handler(Looper.getMainLooper()); + public NrAdvancedCallingPreferenceController(Context context, String key) { super(context, key); mTelephonyManager = context.getSystemService(TelephonyManager.class); @@ -94,6 +100,8 @@ public class NrAdvancedCallingPreferenceController extends TelephonyTogglePrefer CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY); mIsNrEnableFromCarrierConfig = !ArrayUtils.isEmpty(nrAvailabilities); + updateVoNrState(); + Log.d(TAG, "mHas5gCapability: " + mHas5gCapability + ",mIsNrEnabledFromCarrierConfig: " + mIsNrEnableFromCarrierConfig + ",mIsVonrEnabledFromCarrierConfig: " + mIsVonrEnabledFromCarrierConfig @@ -162,7 +170,7 @@ public class NrAdvancedCallingPreferenceController extends TelephonyTogglePrefer @Override public boolean isChecked() { - return mTelephonyManager.isVoNrEnabled(); + return mIsVoNrEnabled; } @VisibleForTesting @@ -174,6 +182,19 @@ public class NrAdvancedCallingPreferenceController extends TelephonyTogglePrefer return isCallStateIdle(); } + private void updateVoNrState() { + ThreadUtils.postOnBackgroundThread(() -> { + boolean result = mTelephonyManager.isVoNrEnabled(); + if (result != mIsVoNrEnabled) { + Log.i(TAG, "VoNr state : " + result); + mIsVoNrEnabled = result; + mHandler.post(() -> { + updateState(mPreference); + }); + } + }); + } + private class PhoneCallStateTelephonyCallback extends TelephonyCallback implements TelephonyCallback.CallStateListener { diff --git a/src/com/android/settings/spa/app/specialaccess/PictureInPicture.kt b/src/com/android/settings/spa/app/specialaccess/PictureInPicture.kt index 9fc358b5cf8..5ed361560d7 100644 --- a/src/com/android/settings/spa/app/specialaccess/PictureInPicture.kt +++ b/src/com/android/settings/spa/app/specialaccess/PictureInPicture.kt @@ -23,6 +23,7 @@ import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.content.pm.PackageManager.GET_ACTIVITIES import android.content.pm.PackageManager.PackageInfoFlags +import android.util.Log import androidx.compose.runtime.Composable import androidx.compose.runtime.livedata.observeAsState import com.android.settings.R @@ -56,26 +57,21 @@ class PictureInPictureListModel(private val context: Context) : private val packageManager = context.packageManager override fun transform(userIdFlow: Flow, appListFlow: Flow>) = - userIdFlow.map(::getPictureInPicturePackages).combine(appListFlow) { - pictureInPicturePackages, - appList -> - appList.map { app -> - createPictureInPictureRecord( - app = app, - isSupport = app.packageName in pictureInPicturePackages, - ) + userIdFlow.map(::getPictureInPicturePackages) + .combine(appListFlow) { pictureInPicturePackages, appList -> + appList.map { app -> + createPictureInPictureRecord( + app = app, + isSupport = app.packageName in pictureInPicturePackages, + ) + } } - } - override fun transformItem(app: ApplicationInfo): PictureInPictureRecord { - return createPictureInPictureRecord( - app = app, - isSupport = app.installed && - packageManager - .getPackageInfoAsUser(app.packageName, GET_ACTIVITIES_FLAGS, app.userId) - .supportsPictureInPicture(), - ) - } + override fun transformItem(app: ApplicationInfo) = createPictureInPictureRecord( + app = app, + isSupport = app.installed && + getPackageAndActivityInfo(app)?.supportsPictureInPicture() == true, + ) private fun createPictureInPictureRecord(app: ApplicationInfo, isSupport: Boolean) = PictureInPictureRecord( @@ -103,13 +99,36 @@ class PictureInPictureListModel(private val context: Context) : } private fun getPictureInPicturePackages(userId: Int): Set = - packageManager - .getInstalledPackagesAsUser(GET_ACTIVITIES_FLAGS, userId) + getPackageAndActivityInfoList(userId) .filter { it.supportsPictureInPicture() } .map { it.packageName } .toSet() + private fun getPackageAndActivityInfo(app: ApplicationInfo): PackageInfo? = try { + packageManager.getPackageInfoAsUser(app.packageName, GET_ACTIVITIES_FLAGS, app.userId) + } catch (e: Exception) { + // Query PackageManager.getPackageInfoAsUser() with GET_ACTIVITIES_FLAGS could cause + // exception sometimes. Since we reply on this flag to retrieve the Picture In Picture + // packages, we need to catch the exception to alleviate the impact before PackageManager + // fixing this issue or provide a better api. + Log.e(TAG, "Exception while getPackageInfoAsUser", e) + null + } + + private fun getPackageAndActivityInfoList(userId: Int): List = try { + packageManager.getInstalledPackagesAsUser(GET_ACTIVITIES_FLAGS, userId) + } catch (e: Exception) { + // Query PackageManager.getPackageInfoAsUser() with GET_ACTIVITIES_FLAGS could cause + // exception sometimes. Since we reply on this flag to retrieve the Picture In Picture + // packages, we need to catch the exception to alleviate the impact before PackageManager + // fixing this issue or provide a better api. + Log.e(TAG, "Exception while getInstalledPackagesAsUser", e) + emptyList() + } + companion object { + private const val TAG = "PictureInPictureListModel" + private fun PackageInfo.supportsPictureInPicture() = activities?.any(ActivityInfo::supportsPictureInPicture) ?: false diff --git a/tests/robotests/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerTest.java b/tests/robotests/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerTest.java index de380c40c8e..9210b870d77 100644 --- a/tests/robotests/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerTest.java +++ b/tests/robotests/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerTest.java @@ -141,6 +141,7 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerTest { @Test public void onDeveloperOptionSwitchDisabled_shouldDisableAngleAsSystemDriver() { + ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "true"); mController.onDeveloperOptionsSwitchDisabled(); final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL); assertThat(systemEGLDriver).isEqualTo(""); @@ -148,12 +149,14 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerTest { @Test public void onDeveloperOptionSwitchDisabled_preferenceShouldNotBeChecked() { + ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "true"); mController.onDeveloperOptionsSwitchDisabled(); verify(mPreference).setChecked(false); } @Test public void onDeveloperOptionsSwitchDisabled_preferenceShouldNotBeEnabled() { + ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "true"); mController.onDeveloperOptionsSwitchDisabled(); verify(mPreference).setEnabled(false); } diff --git a/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/PictureInPictureTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/PictureInPictureTest.kt index f90d63947d0..fb0fb698045 100644 --- a/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/PictureInPictureTest.kt +++ b/tests/spa_unit/src/com/android/settings/spa/app/specialaccess/PictureInPictureTest.kt @@ -23,6 +23,7 @@ import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.content.pm.PackageManager.PackageInfoFlags +import android.os.DeadSystemRuntimeException import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settings.R @@ -100,6 +101,23 @@ class PictureInPictureTest { assertThat(record.isSupport).isTrue() } + @Test + fun transform_getInstalledPackagesAsUserThrowsException_treatAsNotSupported() = runTest { + whenever(packageManager.getInstalledPackagesAsUser(any(), anyInt())) + .thenThrow(DeadSystemRuntimeException()) + + val recordListFlow = listModel.transform( + userIdFlow = flowOf(USER_ID), + appListFlow = flowOf(listOf(PICTURE_IN_PICTURE_APP)), + ) + + val recordList = recordListFlow.first() + assertThat(recordList).hasSize(1) + val record = recordList[0] + assertThat(record.app).isSameInstanceAs(PICTURE_IN_PICTURE_APP) + assertThat(record.isSupport).isFalse() + } + @Test fun transformItem() { whenever( @@ -114,6 +132,20 @@ class PictureInPictureTest { assertThat(record.isSupport).isTrue() } + @Test + fun transformItem_getPackageInfoAsUserThrowsException_treatAsNotSupported() { + whenever( + packageManager.getPackageInfoAsUser( + eq(PICTURE_IN_PICTURE_PACKAGE_NAME), any(), eq(USER_ID) + ) + ).thenThrow(DeadSystemRuntimeException()) + + val record = listModel.transformItem(PICTURE_IN_PICTURE_APP) + + assertThat(record.app).isSameInstanceAs(PICTURE_IN_PICTURE_APP) + assertThat(record.isSupport).isFalse() + } + @Test fun filter_isSupport() = runTest { val record = createRecord(isSupport = true) diff --git a/tests/unit/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest.java b/tests/unit/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest.java index c23292ac54a..ae35431fef7 100644 --- a/tests/unit/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest.java +++ b/tests/unit/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest.java @@ -57,7 +57,7 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest { private GraphicsDriverEnableAngleAsSystemDriverController mController; // Signal to wait for SystemProperty values changed - private class PropertyChangeSignal { + private static class PropertyChangeSignal { private CountDownLatch mCountDownLatch; private Runnable mCountDownJob; @@ -217,23 +217,7 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest { } @Test - public void onDeveloperOptionSwitchEnabled_angleSupported_PreferenceShouldEnabled() { - when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any())) - .thenReturn("true"); - mController.onDeveloperOptionsSwitchEnabled(); - assertThat(mPreference.isEnabled()).isTrue(); - } - - @Test - public void onDeveloperOptionSwitchEnabled_angleNotSupported_PrefenceShouldDisabled() { - when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any())) - .thenReturn("false"); - mController.onDeveloperOptionsSwitchEnabled(); - assertThat(mPreference.isEnabled()).isFalse(); - } - - @Test - public void onDeveloperOptionSwitchDisabled_angleIsNotSystemGLESDriver() { + public void onDeveloperOptionSwitchDisabled_angleShouldNotBeSystemGLESDriver() { // Add a callback when SystemProperty changes. // This allows the thread to wait until // GpuService::toggleAngleAsSystemDriver() updates the persist.graphics.egl. @@ -242,6 +226,8 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest { // Test that onDeveloperOptionSwitchDisabled, // persist.graphics.egl updates to "" + when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any())) + .thenReturn("true"); mController.onDeveloperOptionsSwitchDisabled(); propertyChangeSignal1.wait(100); final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL); @@ -253,12 +239,16 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest { @Test public void onDeveloperOptionSwitchDisabled_PreferenceShouldNotBeChecked() { + when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any())) + .thenReturn("true"); mController.onDeveloperOptionsSwitchDisabled(); assertThat(mPreference.isChecked()).isFalse(); } @Test public void onDeveloperOptionSwitchDisabled_PreferenceShouldDisabled() { + when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any())) + .thenReturn("true"); mController.onDeveloperOptionsSwitchDisabled(); assertThat(mPreference.isEnabled()).isFalse(); } diff --git a/tests/unit/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceControllerTest.java index 9d4082ecff6..053fb4d8916 100644 --- a/tests/unit/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceControllerTest.java +++ b/tests/unit/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceControllerTest.java @@ -186,6 +186,7 @@ public class NrAdvancedCallingPreferenceControllerTest { doReturn(true).when(mTelephonyManager).isVoNrEnabled(); mPreference.setChecked(false); + mController.init(SUB_ID); mController.updateState(mPreference); assertThat(mPreference.isChecked()).isTrue();