diff --git a/src/com/android/settings/development/compat/PlatformCompatDashboard.java b/src/com/android/settings/development/compat/PlatformCompatDashboard.java index dab45f2d123..53c6e9ff83a 100644 --- a/src/com/android/settings/development/compat/PlatformCompatDashboard.java +++ b/src/com/android/settings/development/compat/PlatformCompatDashboard.java @@ -16,7 +16,9 @@ package com.android.settings.development.compat; +import static com.android.settings.development.AppPicker.EXTRA_DEBUGGABLE; import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes.REQUEST_COMPAT_CHANGE_APP; +import static com.android.internal.compat.OverrideAllowedState.ALLOWED; import android.app.Activity; import android.app.settings.SettingsEnums; @@ -25,7 +27,9 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Bundle; import android.os.RemoteException; import android.os.ServiceManager; @@ -37,9 +41,12 @@ 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.internal.compat.IOverrideValidator; +import com.android.internal.compat.OverrideAllowedState; import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.development.AppPicker; @@ -61,6 +68,8 @@ public class PlatformCompatDashboard extends DashboardFragment { private CompatibilityChangeInfo[] mChanges; + private AndroidBuildClassifier mAndroidBuildClassifier = new AndroidBuildClassifier(); + @VisibleForTesting String mSelectedApp; @@ -114,17 +123,21 @@ public class PlatformCompatDashboard extends DashboardFragment { if (requestCode == REQUEST_COMPAT_CHANGE_APP) { if (resultCode == Activity.RESULT_OK) { mSelectedApp = data.getAction(); - addPreferences(); + try { + final ApplicationInfo applicationInfo = getApplicationInfo(); + addPreferences(applicationInfo); + } catch (PackageManager.NameNotFoundException e) { + startAppPicker(); + } } return; } super.onActivityResult(requestCode, resultCode, data); } - private void addPreferences() { + private void addPreferences(ApplicationInfo applicationInfo) { getPreferenceScreen().removeAll(); - getPreferenceScreen().addPreference( - createAppPreference(getApplicationInfo().loadIcon(getPackageManager()))); + getPreferenceScreen().addPreference(createAppPreference(applicationInfo)); // Differentiate compatibility changes into default enabled, default disabled and enabled // after target sdk. final CompatibilityChangeConfig configMappings = getAppChangeMappings(); @@ -161,7 +174,7 @@ public class PlatformCompatDashboard extends DashboardFragment { try { final ApplicationInfo applicationInfo = getApplicationInfo(); return getPlatformCompat().getAppConfig(applicationInfo); - } catch (RemoteException e) { + } catch (RemoteException | PackageManager.NameNotFoundException e) { throw new RuntimeException("Could not get app config!", e); } } @@ -180,7 +193,15 @@ public class PlatformCompatDashboard extends DashboardFragment { change.getName() != null ? change.getName() : "Change_" + change.getId(); item.setSummary(changeName); item.setKey(changeName); - item.setEnabled(true); + boolean shouldEnable = true; + try { + shouldEnable = getPlatformCompat().getOverrideValidator() + .getOverrideAllowedState(change.getId(), mSelectedApp) + .state == ALLOWED; + } catch (RemoteException e) { + throw new RuntimeException("Could not check if change can be overridden for app.", e); + } + item.setEnabled(shouldEnable); item.setChecked(currentValue); item.setOnPreferenceChangeListener( new CompatChangePreferenceChangeListener(change.getId())); @@ -192,12 +213,8 @@ public class PlatformCompatDashboard extends DashboardFragment { * * @return an {@link ApplicationInfo} instance. */ - ApplicationInfo getApplicationInfo() { - try { - return getPackageManager().getApplicationInfo(mSelectedApp, 0); - } catch (PackageManager.NameNotFoundException e) { - throw new RuntimeException("Could not get ApplicationInfo for selected app!", e); - } + ApplicationInfo getApplicationInfo() throws PackageManager.NameNotFoundException { + return getPackageManager().getApplicationInfo(mSelectedApp, 0); } /** @@ -206,9 +223,10 @@ public class PlatformCompatDashboard extends DashboardFragment { *

The {@link Preference} contains the icon, package name and target SDK for the selected * app. Selecting this preference will also re-trigger the app selection dialog.

*/ - Preference createAppPreference(Drawable icon) { - final ApplicationInfo applicationInfo = getApplicationInfo(); - final Preference appPreference = new Preference(getPreferenceScreen().getContext()); + Preference createAppPreference(ApplicationInfo applicationInfo) { + final Context context = getPreferenceScreen().getContext(); + final Drawable icon = applicationInfo.loadIcon(context.getPackageManager()); + final Preference appPreference = new Preference(context); appPreference.setIcon(icon); appPreference.setSummary(mSelectedApp + " SDK " @@ -243,6 +261,11 @@ public class PlatformCompatDashboard extends DashboardFragment { private void startAppPicker() { final Intent intent = new Intent(getContext(), AppPicker.class); + // 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); } diff --git a/tests/robotests/src/com/android/settings/development/compat/PlatformCompatDashboardTest.java b/tests/robotests/src/com/android/settings/development/compat/PlatformCompatDashboardTest.java index 693ed45b56a..d0cb97a8ac8 100644 --- a/tests/robotests/src/com/android/settings/development/compat/PlatformCompatDashboardTest.java +++ b/tests/robotests/src/com/android/settings/development/compat/PlatformCompatDashboardTest.java @@ -16,16 +16,23 @@ package com.android.settings.development.compat; +import static com.android.internal.compat.OverrideAllowedState.ALLOWED; +import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.compat.Compatibility.ChangeConfig; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.graphics.drawable.Drawable; import android.os.RemoteException; @@ -38,7 +45,9 @@ import androidx.preference.SwitchPreference; import com.android.internal.compat.CompatibilityChangeConfig; import com.android.internal.compat.CompatibilityChangeInfo; +import com.android.internal.compat.IOverrideValidator; import com.android.internal.compat.IPlatformCompat; +import com.android.internal.compat.OverrideAllowedState; import com.android.settings.R; import org.junit.Before; @@ -66,6 +75,10 @@ public class PlatformCompatDashboardTest { private ApplicationInfo mApplicationInfo; @Mock private PreferenceManager mPreferenceManager; + @Mock + private IOverrideValidator mOverrideValidator; + @Mock + private PackageManager mPackageManager; private Context mContext; private CompatibilityChangeInfo[] mChanges; @@ -81,7 +94,11 @@ public class PlatformCompatDashboardTest { mChanges[3] = new CompatibilityChangeInfo(4L, "Enabled_After_SDK_1_2", 1, false, ""); mChanges[4] = new CompatibilityChangeInfo(5L, "Enabled_After_SDK_2", 2, false, ""); when(mPlatformCompat.listAllChanges()).thenReturn(mChanges); - mContext = RuntimeEnvironment.application; + when(mPlatformCompat.getOverrideValidator()).thenReturn(mOverrideValidator); + // By default, allow any change + when(mOverrideValidator.getOverrideAllowedState(anyLong(),anyString())) + .thenReturn(new OverrideAllowedState(ALLOWED, -1, -1)); + mContext = spy(RuntimeEnvironment.application); mPreferenceManager = new PreferenceManager(mContext); mPreferenceScreen = mPreferenceManager.createPreferenceScreen(mContext); mApplicationInfo.packageName = APP_NAME; @@ -91,6 +108,7 @@ public class PlatformCompatDashboardTest { doReturn(mPlatformCompat).when(mDashboard).getPlatformCompat(); doReturn(mPreferenceScreen).when(mDashboard).getPreferenceScreen(); doReturn(mPreferenceManager).when(mDashboard).getPreferenceManager(); + doReturn(mPackageManager).when(mContext).getPackageManager(); } @Test @@ -107,8 +125,10 @@ public class PlatformCompatDashboardTest { @Test public void createAppPreference_targetSdkEquals1_summaryReturnsAppNameAndTargetSdk() { mApplicationInfo.targetSdkVersion = 1; + Drawable icon = mock(Drawable.class); + when(mApplicationInfo.loadIcon(any(PackageManager.class))).thenReturn(icon); - Preference appPreference = mDashboard.createAppPreference(any(Drawable.class)); + Preference appPreference = mDashboard.createAppPreference(mApplicationInfo); assertThat(appPreference.getSummary()).isEqualTo(APP_NAME + " SDK 1"); } @@ -128,6 +148,7 @@ public class PlatformCompatDashboardTest { assertThat(enabledPreference.getSummary()).isEqualTo(mChanges[0].getName()); assertThat(enabledPreference instanceof SwitchPreference).isTrue(); assertThat(enabledSwitchPreference.isChecked()).isTrue(); + assertThat(enabledSwitchPreference.isEnabled()).isTrue(); } @Test @@ -139,10 +160,32 @@ public class PlatformCompatDashboardTest { Preference disabledPreference = mDashboard.createPreferenceForChange(mContext, disabledChange, config); - + assertThat(disabledPreference.getSummary()).isEqualTo(mChanges[1].getName()); SwitchPreference disabledSwitchPreference = (SwitchPreference) disabledPreference; assertThat(disabledSwitchPreference.isChecked()).isFalse(); + assertThat(disabledSwitchPreference.isEnabled()).isTrue(); + } + + @Test + public void createPreferenceForChange_cannotOverride_createDisabledEntry() + throws RemoteException { + CompatibilityChangeInfo enabledChange = mChanges[0]; + CompatibilityChangeConfig config = new CompatibilityChangeConfig( + new ChangeConfig(new HashSet(Arrays.asList(enabledChange.getId())), + new HashSet())); + when(mOverrideValidator.getOverrideAllowedState(anyLong(),anyString())) + .thenReturn(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1)); + + Preference preference = mDashboard.createPreferenceForChange(mContext, enabledChange, + config); + + SwitchPreference switchPreference = (SwitchPreference) preference; + + assertThat(preference.getSummary()).isEqualTo(mChanges[0].getName()); + assertThat(preference instanceof SwitchPreference).isTrue(); + assertThat(switchPreference.isChecked()).isTrue(); + assertThat(switchPreference.isEnabled()).isFalse(); } @Test