Merge "Implement the necessary methods for identifying the DENY / FORCE_ALLOW causes for logging purposes."
This commit is contained in:
committed by
Android (Google) Code Review
commit
e9437d2ee3
@@ -204,6 +204,16 @@ public abstract class AtomicFormula extends IntegrityFormula {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAppCertificateFormula() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInstallerFormula() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (mValue == null || mOperator == null) {
|
||||
@@ -374,6 +384,16 @@ public abstract class AtomicFormula extends IntegrityFormula {
|
||||
return getStringMetadataValue(appInstallMetadata, getKey()).equals(mValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAppCertificateFormula() {
|
||||
return getKey() == APP_CERTIFICATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInstallerFormula() {
|
||||
return getKey() == INSTALLER_NAME || getKey() == INSTALLER_CERTIFICATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (mValue == null || mIsHashedValue == null) {
|
||||
@@ -530,6 +550,16 @@ public abstract class AtomicFormula extends IntegrityFormula {
|
||||
return getBooleanMetadataValue(appInstallMetadata, getKey()) == mValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAppCertificateFormula() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInstallerFormula() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (mValue == null) {
|
||||
|
||||
@@ -130,6 +130,16 @@ public final class CompoundFormula extends IntegrityFormula implements Parcelabl
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAppCertificateFormula() {
|
||||
return getFormulas().stream().anyMatch(formula -> formula.isAppCertificateFormula());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInstallerFormula() {
|
||||
return getFormulas().stream().anyMatch(formula -> formula.isInstallerFormula());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
@@ -135,6 +135,21 @@ public abstract class IntegrityFormula {
|
||||
*/
|
||||
public abstract @Tag boolean matches(AppInstallMetadata appInstallMetadata);
|
||||
|
||||
/**
|
||||
* Returns true when the formula (or one of its atomic formulas) has app certificate as key.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public abstract @Tag boolean isAppCertificateFormula();
|
||||
|
||||
/**
|
||||
* Returns true when the formula (or one of its atomic formulas) has installer package name
|
||||
* or installer certificate as key.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public abstract @Tag boolean isInstallerFormula();
|
||||
|
||||
/**
|
||||
* Write an {@link IntegrityFormula} to {@link android.os.Parcel}.
|
||||
*
|
||||
|
||||
@@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.testng.Assert.expectThrows;
|
||||
|
||||
import android.content.integrity.AtomicFormula.BooleanAtomicFormula;
|
||||
import android.content.integrity.AtomicFormula.LongAtomicFormula;
|
||||
import android.content.integrity.AtomicFormula.StringAtomicFormula;
|
||||
import android.os.Parcel;
|
||||
|
||||
@@ -114,8 +115,8 @@ public class AtomicFormulaTest {
|
||||
|
||||
@Test
|
||||
public void testValidAtomicFormula_longValue() {
|
||||
AtomicFormula.LongAtomicFormula longAtomicFormula =
|
||||
new AtomicFormula.LongAtomicFormula(
|
||||
LongAtomicFormula longAtomicFormula =
|
||||
new LongAtomicFormula(
|
||||
AtomicFormula.VERSION_CODE, AtomicFormula.GTE, 1);
|
||||
|
||||
assertThat(longAtomicFormula.getKey()).isEqualTo(AtomicFormula.VERSION_CODE);
|
||||
@@ -151,7 +152,7 @@ public class AtomicFormulaTest {
|
||||
expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
new AtomicFormula.LongAtomicFormula(
|
||||
new LongAtomicFormula(
|
||||
AtomicFormula.PACKAGE_NAME, AtomicFormula.EQ, 1));
|
||||
assertThat(e.getMessage()).matches(
|
||||
"Key PACKAGE_NAME cannot be used with LongAtomicFormula");
|
||||
@@ -182,14 +183,14 @@ public class AtomicFormulaTest {
|
||||
|
||||
@Test
|
||||
public void testParcelUnparcel_int() {
|
||||
AtomicFormula.LongAtomicFormula formula =
|
||||
new AtomicFormula.LongAtomicFormula(
|
||||
LongAtomicFormula formula =
|
||||
new LongAtomicFormula(
|
||||
AtomicFormula.VERSION_CODE, AtomicFormula.GT, 1);
|
||||
Parcel p = Parcel.obtain();
|
||||
formula.writeToParcel(p, 0);
|
||||
p.setDataPosition(0);
|
||||
AtomicFormula.LongAtomicFormula newFormula =
|
||||
AtomicFormula.LongAtomicFormula.CREATOR.createFromParcel(p);
|
||||
LongAtomicFormula newFormula =
|
||||
LongAtomicFormula.CREATOR.createFromParcel(p);
|
||||
|
||||
assertThat(newFormula).isEqualTo(formula);
|
||||
}
|
||||
@@ -211,7 +212,7 @@ public class AtomicFormulaTest {
|
||||
Exception e =
|
||||
expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> new AtomicFormula.LongAtomicFormula(/* key= */ -1,
|
||||
() -> new LongAtomicFormula(/* key= */ -1,
|
||||
AtomicFormula.EQ, /* value= */0));
|
||||
assertThat(e.getMessage()).matches("Unknown key: -1");
|
||||
}
|
||||
@@ -222,7 +223,7 @@ public class AtomicFormulaTest {
|
||||
expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
new AtomicFormula.LongAtomicFormula(
|
||||
new LongAtomicFormula(
|
||||
AtomicFormula.VERSION_CODE, /* operator= */ -1, /* value= */
|
||||
0));
|
||||
assertThat(e.getMessage()).matches("Unknown operator: -1");
|
||||
@@ -252,10 +253,59 @@ public class AtomicFormulaTest {
|
||||
assertThat(stringAtomicFormula.matches(appInstallMetadata)).isFalse();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testIsAppCertificateFormula_string_true() {
|
||||
StringAtomicFormula stringAtomicFormula =
|
||||
new StringAtomicFormula(
|
||||
AtomicFormula.APP_CERTIFICATE, "cert", /* isHashedValue= */false);
|
||||
|
||||
assertThat(stringAtomicFormula.isAppCertificateFormula()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAppCertificateFormula_string_false() {
|
||||
StringAtomicFormula stringAtomicFormula =
|
||||
new StringAtomicFormula(
|
||||
AtomicFormula.PACKAGE_NAME, "com.test.app", /* isHashedValue= */
|
||||
false);
|
||||
|
||||
assertThat(stringAtomicFormula.isAppCertificateFormula()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsInstallerFormula_string_false() {
|
||||
StringAtomicFormula stringAtomicFormula =
|
||||
new StringAtomicFormula(
|
||||
AtomicFormula.APP_CERTIFICATE, "cert", /* isHashedValue= */false);
|
||||
|
||||
assertThat(stringAtomicFormula.isInstallerFormula()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsInstallerFormula_string_installerName_true() {
|
||||
StringAtomicFormula stringAtomicFormula =
|
||||
new StringAtomicFormula(
|
||||
AtomicFormula.INSTALLER_NAME,
|
||||
"com.test.installer",
|
||||
/* isHashedValue= */false);
|
||||
|
||||
assertThat(stringAtomicFormula.isInstallerFormula()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsInstallerFormula_string_installerCertificate_true() {
|
||||
StringAtomicFormula stringAtomicFormula =
|
||||
new StringAtomicFormula(
|
||||
AtomicFormula.INSTALLER_CERTIFICATE, "cert", /* isHashedValue= */false);
|
||||
|
||||
assertThat(stringAtomicFormula.isInstallerFormula()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormulaMatches_long_eq_true() {
|
||||
AtomicFormula.LongAtomicFormula longAtomicFormula =
|
||||
new AtomicFormula.LongAtomicFormula(
|
||||
LongAtomicFormula longAtomicFormula =
|
||||
new LongAtomicFormula(
|
||||
AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 0);
|
||||
AppInstallMetadata appInstallMetadata =
|
||||
getAppInstallMetadataBuilder().setVersionCode(0).build();
|
||||
@@ -265,8 +315,8 @@ public class AtomicFormulaTest {
|
||||
|
||||
@Test
|
||||
public void testFormulaMatches_long_eq_false() {
|
||||
AtomicFormula.LongAtomicFormula longAtomicFormula =
|
||||
new AtomicFormula.LongAtomicFormula(
|
||||
LongAtomicFormula longAtomicFormula =
|
||||
new LongAtomicFormula(
|
||||
AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 0);
|
||||
AppInstallMetadata appInstallMetadata =
|
||||
getAppInstallMetadataBuilder().setVersionCode(1).build();
|
||||
@@ -276,8 +326,8 @@ public class AtomicFormulaTest {
|
||||
|
||||
@Test
|
||||
public void testFormulaMatches_long_gt_true() {
|
||||
AtomicFormula.LongAtomicFormula longAtomicFormula =
|
||||
new AtomicFormula.LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GT,
|
||||
LongAtomicFormula longAtomicFormula =
|
||||
new LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GT,
|
||||
0);
|
||||
AppInstallMetadata appInstallMetadata =
|
||||
getAppInstallMetadataBuilder().setVersionCode(1).build();
|
||||
@@ -287,8 +337,8 @@ public class AtomicFormulaTest {
|
||||
|
||||
@Test
|
||||
public void testFormulaMatches_long_gt_false() {
|
||||
AtomicFormula.LongAtomicFormula longAtomicFormula =
|
||||
new AtomicFormula.LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GT,
|
||||
LongAtomicFormula longAtomicFormula =
|
||||
new LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GT,
|
||||
1);
|
||||
AppInstallMetadata appInstallMetadata =
|
||||
getAppInstallMetadataBuilder().setVersionCode(0).build();
|
||||
@@ -298,8 +348,8 @@ public class AtomicFormulaTest {
|
||||
|
||||
@Test
|
||||
public void testFormulaMatches_long_gte_true() {
|
||||
AtomicFormula.LongAtomicFormula longAtomicFormula =
|
||||
new AtomicFormula.LongAtomicFormula(
|
||||
LongAtomicFormula longAtomicFormula =
|
||||
new LongAtomicFormula(
|
||||
AtomicFormula.VERSION_CODE, AtomicFormula.GTE, 1);
|
||||
|
||||
AppInstallMetadata appInstallMetadata1 =
|
||||
@@ -313,8 +363,8 @@ public class AtomicFormulaTest {
|
||||
|
||||
@Test
|
||||
public void testFormulaMatches_long_gte_false() {
|
||||
AtomicFormula.LongAtomicFormula longAtomicFormula =
|
||||
new AtomicFormula.LongAtomicFormula(
|
||||
LongAtomicFormula longAtomicFormula =
|
||||
new LongAtomicFormula(
|
||||
AtomicFormula.VERSION_CODE, AtomicFormula.GTE, 1);
|
||||
AppInstallMetadata appInstallMetadata =
|
||||
getAppInstallMetadataBuilder().setVersionCode(0).build();
|
||||
@@ -322,6 +372,24 @@ public class AtomicFormulaTest {
|
||||
assertThat(longAtomicFormula.matches(appInstallMetadata)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAppCertificateFormula_long_false() {
|
||||
LongAtomicFormula longAtomicFormula =
|
||||
new AtomicFormula.LongAtomicFormula(
|
||||
AtomicFormula.VERSION_CODE, AtomicFormula.GTE, 1);
|
||||
|
||||
assertThat(longAtomicFormula.isAppCertificateFormula()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsInstallerFormula_long_false() {
|
||||
LongAtomicFormula longAtomicFormula =
|
||||
new LongAtomicFormula(
|
||||
AtomicFormula.VERSION_CODE, AtomicFormula.GTE, 1);
|
||||
|
||||
assertThat(longAtomicFormula.isInstallerFormula()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormulaMatches_bool_true() {
|
||||
BooleanAtomicFormula boolFormula =
|
||||
@@ -342,6 +410,22 @@ public class AtomicFormulaTest {
|
||||
assertThat(boolFormula.matches(appInstallMetadata)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAppCertificateFormula_bool_false() {
|
||||
BooleanAtomicFormula boolFormula =
|
||||
new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true);
|
||||
|
||||
assertThat(boolFormula.isAppCertificateFormula()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsInstallerFormula_bool_false() {
|
||||
BooleanAtomicFormula boolFormula =
|
||||
new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true);
|
||||
|
||||
assertThat(boolFormula.isInstallerFormula()).isFalse();
|
||||
}
|
||||
|
||||
/** Returns a builder with all fields filled with some dummy data. */
|
||||
private AppInstallMetadata.Builder getAppInstallMetadataBuilder() {
|
||||
return new AppInstallMetadata.Builder()
|
||||
|
||||
@@ -225,6 +225,63 @@ public class CompoundFormulaTest {
|
||||
assertThat(compoundFormula.matches(appInstallMetadata)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAppCertificateFormula_false() {
|
||||
CompoundFormula compoundFormula =
|
||||
new CompoundFormula(
|
||||
CompoundFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
|
||||
|
||||
assertThat(compoundFormula.isAppCertificateFormula()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAppCertificateFormula_true() {
|
||||
AtomicFormula appCertFormula =
|
||||
new AtomicFormula.StringAtomicFormula(AtomicFormula.APP_CERTIFICATE,
|
||||
"app.cert", /* isHashed= */false);
|
||||
CompoundFormula compoundFormula =
|
||||
new CompoundFormula(
|
||||
CompoundFormula.AND,
|
||||
Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2, appCertFormula));
|
||||
|
||||
assertThat(compoundFormula.isAppCertificateFormula()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsInstallerFormula_false() {
|
||||
CompoundFormula compoundFormula =
|
||||
new CompoundFormula(
|
||||
CompoundFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
|
||||
|
||||
assertThat(compoundFormula.isInstallerFormula()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsInstallerFormula_installerName_true() {
|
||||
AtomicFormula installerNameFormula =
|
||||
new AtomicFormula.StringAtomicFormula(AtomicFormula.INSTALLER_NAME,
|
||||
"com.test.installer", /* isHashed= */false);
|
||||
CompoundFormula compoundFormula =
|
||||
new CompoundFormula(
|
||||
CompoundFormula.AND,
|
||||
Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2, installerNameFormula));
|
||||
|
||||
assertThat(compoundFormula.isInstallerFormula()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsInstallerFormula_installerCertificate_true() {
|
||||
AtomicFormula installerCertificateFormula =
|
||||
new AtomicFormula.StringAtomicFormula(AtomicFormula.INSTALLER_CERTIFICATE,
|
||||
"cert", /* isHashed= */false);
|
||||
CompoundFormula compoundFormula =
|
||||
new CompoundFormula(
|
||||
CompoundFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2,
|
||||
installerCertificateFormula));
|
||||
|
||||
assertThat(compoundFormula.isInstallerFormula()).isTrue();
|
||||
}
|
||||
|
||||
/** Returns a builder with all fields filled with some dummy data. */
|
||||
private AppInstallMetadata.Builder getAppInstallMetadataBuilder() {
|
||||
return new AppInstallMetadata.Builder()
|
||||
|
||||
@@ -96,20 +96,14 @@ public final class IntegrityCheckResult {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when the {@code Effect.DENY} result is caused by an app certificate mismatch.
|
||||
*/
|
||||
/** Returns true when the {@code mEffect} is caused by an app certificate mismatch. */
|
||||
public boolean isCausedByAppCertRule() {
|
||||
// TODO(b/147095027): implement this.
|
||||
return true;
|
||||
return mRuleList.stream().anyMatch(rule -> rule.getFormula().isAppCertificateFormula());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when the {@code Effect.DENY} result is caused by an installer rule.
|
||||
*/
|
||||
/** Returns true when the {@code mEffect} is caused by an installer rule. */
|
||||
public boolean isCausedByInstallerRule() {
|
||||
// TODO(b/147095027): implement this.
|
||||
return true;
|
||||
return mRuleList.stream().anyMatch(rule -> rule.getFormula().isInstallerFormula());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.server.integrity.model;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.integrity.AtomicFormula;
|
||||
import android.content.integrity.CompoundFormula;
|
||||
import android.content.integrity.Rule;
|
||||
import android.util.StatsLog;
|
||||
|
||||
@@ -26,6 +27,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
@@ -76,4 +78,58 @@ public class IntegrityCheckResultTest {
|
||||
assertThat(denyResult.getLoggingResponse())
|
||||
.isEqualTo(StatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__REJECTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isDenyCausedByAppCertificate() {
|
||||
String packageName = "com.test.deny";
|
||||
String appCert = "app-cert";
|
||||
Rule failedRule =
|
||||
new Rule(
|
||||
new CompoundFormula(
|
||||
CompoundFormula.AND,
|
||||
Arrays.asList(
|
||||
new AtomicFormula.StringAtomicFormula(
|
||||
AtomicFormula.PACKAGE_NAME, packageName),
|
||||
new AtomicFormula.StringAtomicFormula(
|
||||
AtomicFormula.APP_CERTIFICATE, appCert))),
|
||||
Rule.DENY);
|
||||
Rule otherFailedRule =
|
||||
new Rule(
|
||||
new AtomicFormula.LongAtomicFormula(AtomicFormula.VERSION_CODE,
|
||||
AtomicFormula.EQ, 12),
|
||||
Rule.DENY);
|
||||
|
||||
IntegrityCheckResult denyResult =
|
||||
IntegrityCheckResult.deny(Arrays.asList(failedRule, otherFailedRule));
|
||||
|
||||
assertThat(denyResult.isCausedByAppCertRule()).isTrue();
|
||||
assertThat(denyResult.isCausedByInstallerRule()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isDenyCausedByInstaller() {
|
||||
String packageName = "com.test.deny";
|
||||
String appCert = "app-cert";
|
||||
Rule failedRule =
|
||||
new Rule(
|
||||
new CompoundFormula(
|
||||
CompoundFormula.AND,
|
||||
Arrays.asList(
|
||||
new AtomicFormula.StringAtomicFormula(
|
||||
AtomicFormula.PACKAGE_NAME, packageName),
|
||||
new AtomicFormula.StringAtomicFormula(
|
||||
AtomicFormula.INSTALLER_CERTIFICATE, appCert))),
|
||||
Rule.DENY);
|
||||
Rule otherFailedRule =
|
||||
new Rule(
|
||||
new AtomicFormula.LongAtomicFormula(AtomicFormula.VERSION_CODE,
|
||||
AtomicFormula.EQ, 12),
|
||||
Rule.DENY);
|
||||
|
||||
IntegrityCheckResult denyResult =
|
||||
IntegrityCheckResult.deny(Arrays.asList(failedRule, otherFailedRule));
|
||||
|
||||
assertThat(denyResult.isCausedByAppCertRule()).isFalse();
|
||||
assertThat(denyResult.isCausedByInstallerRule()).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -871,6 +871,16 @@ public class RuleBinarySerializerTest {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAppCertificateFormula() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInstallerFormula() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode();
|
||||
|
||||
@@ -298,6 +298,16 @@ public class RuleIndexingDetailsIdentifierTest {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAppCertificateFormula() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInstallerFormula() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode();
|
||||
|
||||
@@ -542,6 +542,16 @@ public class RuleXmlSerializerTest {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAppCertificateFormula() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInstallerFormula() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode();
|
||||
|
||||
Reference in New Issue
Block a user