From 229b8096438d25bbd56fe98c12d117c00f366196 Mon Sep 17 00:00:00 2001 From: Benjamin Franz Date: Tue, 9 Feb 2021 10:30:38 +0000 Subject: [PATCH] Create a new permission to override Overridable change ids With this change we allow system packages with the new permission to override ChangeIds specifically annotated as Overridable to set overrides even on non-debuggable builds. Bug: 174043039 Bug: 175874108 Test: atest FrameworksServicesTests:CompatConfigTest Test: atest FrameworksServicesTests:PlatformCompatTest Change-Id: Ib8d5d83b5fd62acb5808d10f5c413616f29ee65c Merged-In: Ib8d5d83b5fd62acb5808d10f5c413616f29ee65c --- core/api/system-current.txt | 16 ++++++ .../android/app/compat/CompatChanges.java | 9 ++-- .../android/app/compat/PackageOverride.java | 49 +++++++------------ .../compat/CompatibilityOverrideConfig.java | 5 +- .../internal/compat/IPlatformCompat.aidl | 14 ++++-- core/res/AndroidManifest.xml | 7 +++ .../android/server/compat/CompatChange.java | 14 +++++- .../android/server/compat/CompatConfig.java | 17 ++++++- .../server/compat/OverrideValidatorImpl.java | 32 +++++++++++- .../android/server/compat/PlatformCompat.java | 28 +++++++++-- .../server/compat/CompatConfigBuilder.java | 2 +- .../server/compat/CompatConfigTest.java | 45 +++++++++++++++-- .../server/compat/PlatformCompatTest.java | 2 +- 13 files changed, 185 insertions(+), 55 deletions(-) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index da56f45e9d6e1..aaba9e1bf0c8b 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -160,6 +160,7 @@ package android { field public static final String OBSERVE_NETWORK_POLICY = "android.permission.OBSERVE_NETWORK_POLICY"; field public static final String OBSERVE_ROLE_HOLDERS = "android.permission.OBSERVE_ROLE_HOLDERS"; field public static final String OPEN_ACCESSIBILITY_DETAILS_SETTINGS = "android.permission.OPEN_ACCESSIBILITY_DETAILS_SETTINGS"; + field public static final String OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD = "android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"; field public static final String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG"; field public static final String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT"; field public static final String PACKET_KEEPALIVE_OFFLOAD = "android.permission.PACKET_KEEPALIVE_OFFLOAD"; @@ -1124,6 +1125,21 @@ package android.app.compat { method public static boolean isChangeEnabled(long); method @RequiresPermission(allOf={"android.permission.READ_COMPAT_CHANGE_CONFIG", "android.permission.LOG_COMPAT_CHANGE"}) public static boolean isChangeEnabled(long, @NonNull String, @NonNull android.os.UserHandle); method @RequiresPermission(allOf={"android.permission.READ_COMPAT_CHANGE_CONFIG", "android.permission.LOG_COMPAT_CHANGE"}) public static boolean isChangeEnabled(long, int); + method @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) public static void setPackageOverride(@NonNull String, @NonNull java.util.Map); + } + + public final class PackageOverride { + method public long getMaxVersionCode(); + method public long getMinVersionCode(); + method public boolean isEnabled(); + } + + public static final class PackageOverride.Builder { + ctor public PackageOverride.Builder(); + method @NonNull public android.app.compat.PackageOverride build(); + method @NonNull public android.app.compat.PackageOverride.Builder setEnabled(boolean); + method @NonNull public android.app.compat.PackageOverride.Builder setMaxVersionCode(long); + method @NonNull public android.app.compat.PackageOverride.Builder setMinVersionCode(long); } } diff --git a/core/java/android/app/compat/CompatChanges.java b/core/java/android/app/compat/CompatChanges.java index ab38832458d6d..74e1ece3fa893 100644 --- a/core/java/android/app/compat/CompatChanges.java +++ b/core/java/android/app/compat/CompatChanges.java @@ -104,16 +104,15 @@ public final class CompatChanges { * * @param packageName The package name of the app in question. * @param overrides A map from changeId to the override applied for this change id. - * @hide */ - @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG) - public static void setPackageOverride(String packageName, - Map overrides) { + @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) + public static void setPackageOverride(@NonNull String packageName, + @NonNull Map overrides) { IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface( ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(overrides); try { - platformCompat.setOverridesFromInstaller(config, packageName); + platformCompat.setOverridesOnReleaseBuilds(config, packageName); } catch (RemoteException e) { e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/compat/PackageOverride.java b/core/java/android/app/compat/PackageOverride.java index 9f97cd41128a3..59b355523a309 100644 --- a/core/java/android/app/compat/PackageOverride.java +++ b/core/java/android/app/compat/PackageOverride.java @@ -17,8 +17,9 @@ package android.app.compat; import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Parcel; -import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -32,15 +33,16 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ -public class PackageOverride implements Parcelable { +@SystemApi +public final class PackageOverride { + /** @hide */ @IntDef({ VALUE_UNDEFINED, VALUE_ENABLED, VALUE_DISABLED }) @Retention(RetentionPolicy.SOURCE) - /** @hide */ public @interface EvaluatedOverride { } @@ -75,10 +77,6 @@ public class PackageOverride implements Parcelable { this.mEnabled = enabled; } - private PackageOverride(Parcel in) { - this(in.readLong(), in.readLong(), in.readBoolean()); - } - /** * Evaluate the override for the given {@code versionCode}. If no override is defined for * the specified version code, {@link #VALUE_UNDEFINED} is returned. @@ -114,24 +112,22 @@ public class PackageOverride implements Parcelable { } /** Returns the enabled value for the override. */ - public boolean getEnabled() { + public boolean isEnabled() { return mEnabled; } /** @hide */ - @Override - public int describeContents() { - return 0; - } - - /** @hide */ - @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(Parcel dest) { dest.writeLong(mMinVersionCode); dest.writeLong(mMaxVersionCode); dest.writeBoolean(mEnabled); } + /** @hide */ + public static PackageOverride createFromParcel(Parcel in) { + return new PackageOverride(in.readLong(), in.readLong(), in.readBoolean()); + } + /** @hide */ @Override public String toString() { @@ -141,25 +137,10 @@ public class PackageOverride implements Parcelable { return String.format("[%d,%d,%b]", mMinVersionCode, mMaxVersionCode, mEnabled); } - /** @hide */ - public static final Creator CREATOR = - new Creator() { - - @Override - public PackageOverride createFromParcel(Parcel in) { - return new PackageOverride(in); - } - - @Override - public PackageOverride[] newArray(int size) { - return new PackageOverride[size]; - } - }; - /** * Builder to construct a PackageOverride. */ - public static class Builder { + public static final class Builder { private long mMinVersionCode = Long.MIN_VALUE; private long mMaxVersionCode = Long.MAX_VALUE; private boolean mEnabled; @@ -169,6 +150,7 @@ public class PackageOverride implements Parcelable { * * default value: {@code Long.MIN_VALUE}. */ + @NonNull public Builder setMinVersionCode(long minVersionCode) { mMinVersionCode = minVersionCode; return this; @@ -179,6 +161,7 @@ public class PackageOverride implements Parcelable { * * default value: {@code Long.MAX_VALUE}. */ + @NonNull public Builder setMaxVersionCode(long maxVersionCode) { mMaxVersionCode = maxVersionCode; return this; @@ -189,6 +172,7 @@ public class PackageOverride implements Parcelable { * * default value: {@code false}. */ + @NonNull public Builder setEnabled(boolean enabled) { mEnabled = enabled; return this; @@ -200,6 +184,7 @@ public class PackageOverride implements Parcelable { * @throws IllegalArgumentException if {@code minVersionCode} is larger than * {@code maxVersionCode}. */ + @NonNull public PackageOverride build() { if (mMinVersionCode > mMaxVersionCode) { throw new IllegalArgumentException("minVersionCode must not be larger than " diff --git a/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java b/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java index 1c222a73eabc9..9a02b7b7aae99 100644 --- a/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java +++ b/core/java/com/android/internal/compat/CompatibilityOverrideConfig.java @@ -40,8 +40,7 @@ public final class CompatibilityOverrideConfig implements Parcelable { overrides = new HashMap<>(); for (int i = 0; i < keyCount; i++) { long key = in.readLong(); - PackageOverride override = in.readParcelable(PackageOverride.class.getClassLoader()); - overrides.put(key, override); + overrides.put(key, PackageOverride.createFromParcel(in)); } } @@ -55,7 +54,7 @@ public final class CompatibilityOverrideConfig implements Parcelable { dest.writeInt(overrides.size()); for (Long key : overrides.keySet()) { dest.writeLong(key); - dest.writeParcelable(overrides.get(key), 0); + overrides.get(key).writeToParcel(dest); } } diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl index 60213e4411c39..78d1d22c401e1 100644 --- a/core/java/com/android/internal/compat/IPlatformCompat.aidl +++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl @@ -151,15 +151,23 @@ interface IPlatformCompat { void setOverrides(in CompatibilityChangeConfig overrides, in String packageName); /** - * Adds overrides to compatibility changes. + * Adds overrides to compatibility changes on release builds. * - *

Kills the app to allow the changes to take effect. + *

The caller to this API needs to hold + * {@code android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD} and all change ids + * in {@code overrides} need to annotated with {@link android.compat.annotation.Overridable}. + * + * A release build in this definition means that {@link android.os.Build#IS_DEBUGGABLE} needs to + * be {@code false}. + * + *

Note that this does not kill the app, and therefore overrides read from the app process + * will not be updated. Overrides read from the system process do take effect. * * @param overrides parcelable containing the compat change overrides to be applied * @param packageName the package name of the app whose changes will be overridden * @throws SecurityException if overriding changes is not permitted */ - void setOverridesFromInstaller(in CompatibilityOverrideConfig overrides, in String packageName); + void setOverridesOnReleaseBuilds(in CompatibilityOverrideConfig overrides, in String packageName); /** * Adds overrides to compatibility changes. diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 2f352e955d29c..4f69c1e7494a5 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5123,9 +5123,16 @@ + + test APK targetSdkVersion. static final long CTS_SYSTEM_API_CHANGEID = 149391281; // This is a bug id. + /** + * An overridable change ID to be used only in the CTS test for this SystemApi + */ + @ChangeId + @Disabled + @Overridable + static final long CTS_SYSTEM_API_OVERRIDABLE_CHANGEID = 174043039; // This is a bug id. + + /** * Callback listener for when compat changes are updated for a package. * See {@link #registerListener(ChangeListener)} for more details. @@ -211,6 +222,7 @@ public final class CompatChange extends CompatibilityChangeInfo { boolean hasPackageOverride(String pname) { return mRawOverrides.containsKey(pname); } + /** * Remove any package override for the given package name, restoring the default behaviour. * @@ -355,7 +367,7 @@ public final class CompatChange extends CompatibilityChangeInfo { override.setPackageName(entry.getKey()); override.setMinVersionCode(entry.getValue().getMinVersionCode()); override.setMaxVersionCode(entry.getValue().getMaxVersionCode()); - override.setEnabled(entry.getValue().getEnabled()); + override.setEnabled(entry.getValue().isEnabled()); rawList.add(override); } changeOverrides.setRaw(rawOverrides); diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index ef86f42d6c3c2..55e2696300930 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -303,6 +303,16 @@ final class CompatConfig { } } + /** + * Returns whether the change is overridable. + */ + boolean isOverridable(long changeId) { + synchronized (mChanges) { + CompatChange c = mChanges.get(changeId); + return c != null && c.getOverridable(); + } + } + /** * Removes an override previously added via {@link #addOverride(long, String, boolean)}. * @@ -343,7 +353,7 @@ final class CompatConfig { /** * Removes all overrides previously added via {@link #addOverride(long, String, boolean)} or - * {@link #addOverrides(CompatibilityChangeConfig, String)} for a certain package. + * {@link #addOverrides(CompatibilityOverrideConfig, String)} for a certain package. * *

This restores the default behaviour for the given app. * @@ -632,8 +642,11 @@ final class CompatConfig { } boolean shouldInvalidateCache = false; for (CompatChange c: changes) { + if (!c.hasPackageOverride(packageName)) { + continue; + } OverrideAllowedState allowedState = - mOverrideValidator.getOverrideAllowedState(c.getId(), packageName); + mOverrideValidator.getOverrideAllowedStateForRecheck(c.getId(), packageName); shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, mContext); } if (shouldInvalidateCache) { diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java index aa66a1a8b01fd..b5006913af35d 100644 --- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java +++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java @@ -16,6 +16,9 @@ package com.android.server.compat; +import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + import static com.android.internal.compat.OverrideAllowedState.ALLOWED; import static com.android.internal.compat.OverrideAllowedState.DEFERRED_VERIFICATION; import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK; @@ -24,6 +27,7 @@ import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_S import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE; import static com.android.internal.compat.OverrideAllowedState.PLATFORM_TOO_OLD; +import android.annotation.NonNull; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -69,8 +73,25 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub { mForceNonDebuggableFinalBuild = false; } + /** + * Check the allowed state for the given changeId and packageName on a recheck. + * + *

Recheck happens when the given app is getting updated. In this case we cannot do a + * permission check on the caller, so we're using the fact that the override was present as + * proof that the original caller was allowed to set this override. + */ + OverrideAllowedState getOverrideAllowedStateForRecheck(long changeId, + @NonNull String packageName) { + return getOverrideAllowedStateInternal(changeId, packageName, true); + } + @Override public OverrideAllowedState getOverrideAllowedState(long changeId, String packageName) { + return getOverrideAllowedStateInternal(changeId, packageName, false); + } + + private OverrideAllowedState getOverrideAllowedStateInternal(long changeId, String packageName, + boolean isRecheck) { if (mCompatConfig.isLoggingOnly(changeId)) { return new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1); } @@ -99,6 +120,16 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub { } catch (NameNotFoundException e) { return new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1); } + // If the change is annotated as @Overridable, apps with the specific permission can + // set the override even on production builds. When rechecking the override, e.g. during an + // app update we can bypass this check, as it wouldn't have been here in the first place. + if (mCompatConfig.isOverridable(changeId) + && (isRecheck + || mContext.checkCallingOrSelfPermission( + OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) + == PERMISSION_GRANTED)) { + return new OverrideAllowedState(ALLOWED, -1, -1); + } int appTargetSdk = applicationInfo.targetSdkVersion; // Only allow overriding debuggable apps. if ((applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) { @@ -130,5 +161,4 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub { void forceNonDebuggableFinalForTest(boolean value) { mForceNonDebuggableFinalBuild = value; } - } diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index 40e386359a40f..20469a21db0c1 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -18,6 +18,7 @@ package com.android.server.compat; import static android.Manifest.permission.LOG_COMPAT_CHANGE; import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG; +import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD; import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Process.SYSTEM_UID; @@ -182,11 +183,12 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override - public void setOverridesFromInstaller(CompatibilityOverrideConfig overrides, + public void setOverridesOnReleaseBuilds(CompatibilityOverrideConfig overrides, String packageName) { - checkCompatChangeOverridePermission(); + // TODO(b/183630314): Unify the permission enforcement with the other setOverrides* methods. + checkCompatChangeOverrideOverridablePermission(); + checkAllCompatOverridesAreOverridable(overrides); mCompatConfig.addOverrides(overrides, packageName); - killPackage(packageName); } @Override @@ -383,6 +385,26 @@ public class PlatformCompat extends IPlatformCompat.Stub { } } + private void checkCompatChangeOverrideOverridablePermission() { + // Don't check for permissions within the system process + if (Binder.getCallingUid() == SYSTEM_UID) { + return; + } + if (mContext.checkCallingOrSelfPermission(OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) + != PERMISSION_GRANTED) { + throw new SecurityException("Cannot override compat change"); + } + } + + private void checkAllCompatOverridesAreOverridable(CompatibilityOverrideConfig overrides) { + for (Long changeId : overrides.overrides.keySet()) { + if (!mCompatConfig.isOverridable(changeId)) { + throw new SecurityException("Only change ids marked as Overridable can be " + + "overridden."); + } + } + } + private void checkCompatChangeReadAndLogPermission() { checkCompatChangeReadPermission(); checkCompatChangeLogPermission(); diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java index fcd6b842426a7..7bdc87ef6bd76 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java @@ -116,7 +116,7 @@ class CompatConfigBuilder { } CompatConfigBuilder addOverridableChangeWithId(long id) { - mChanges.add(new CompatChange(id, "", -1, -1, false, true, "", true)); + mChanges.add(new CompatChange(id, "", -1, -1, true, false, "", true)); return this; } diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java index bd774056aef80..a866363f46e65 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java @@ -259,6 +259,36 @@ public class CompatConfigTest { assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse(); } + @Test + public void testInstallerCanSetOverrides() throws Exception { + final long changeId = 1234L; + final int installerUid = 23; + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addOverridableChangeWithId(1234L) + .build(); + ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() + .withPackageName("com.some.package") + .build(); + PackageManager packageManager = mock(PackageManager.class); + when(mContext.getPackageManager()).thenReturn(packageManager); + when(packageManager.getApplicationInfo(eq("com.some.package"), anyInt())) + .thenReturn(applicationInfo); + + // Force the validator to prevent overriding the change by using a user build. + when(mBuildClassifier.isDebuggableBuild()).thenReturn(false); + when(mBuildClassifier.isFinalBuild()).thenReturn(true); + + CompatibilityOverrideConfig config = new CompatibilityOverrideConfig( + Collections.singletonMap(1234L, + new PackageOverride.Builder() + .setMaxVersionCode(99L) + .setEnabled(true) + .build())); + + compatConfig.addOverrides(config, "com.some.package"); + assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue(); + } + @Test public void testApplyDeferredOverridesAfterInstallingApp() throws Exception { ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() @@ -639,9 +669,18 @@ public class CompatConfigTest { .build()); when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt())) .thenThrow(new NameNotFoundException()); - - compatConfig.addOverride(1L, "foo.bar", true); - compatConfig.addOverride(2L, "bar.baz", false); + compatConfig.addOverrides( + new CompatibilityOverrideConfig( + Collections.singletonMap( + 1L, + new PackageOverride.Builder().setEnabled(true).build())), + "foo.bar"); + compatConfig.addOverrides( + new CompatibilityOverrideConfig( + Collections.singletonMap( + 2L, + new PackageOverride.Builder().setEnabled(false).build())), + "bar.baz"); assertThat(readFile(overridesFile)).isEqualTo("\n" + "\n" diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java index 3fc6e99183826..a2664e5f3b0a1 100644 --- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java @@ -113,7 +113,7 @@ public class PlatformCompatTest { new CompatibilityChangeInfo( 6L, "", Build.VERSION_CODES.R, -1, false, false, "", false), new CompatibilityChangeInfo(7L, "", -1, -1, false, true, "", false), - new CompatibilityChangeInfo(8L, "", -1, -1, false, true, "", true)); + new CompatibilityChangeInfo(8L, "", -1, -1, true, false, "", true)); } @Test