From 02067c2e4ffd20cd9b6f499f797b1668cb587c08 Mon Sep 17 00:00:00 2001 From: Khaled Abdelmohsen Date: Fri, 28 Feb 2020 11:09:57 +0000 Subject: [PATCH] Extract source stamp during app install Bug: 148006415 Test: atest FrameworksServicesTests:AppIntegrityManagerServiceImplTest Change-Id: I3453c94d93e35b01de1a353d592e01db514e8921 --- .../content/integrity/AppInstallMetadata.java | 58 ++++++++-- .../content/integrity/AtomicFormula.java | 7 +- .../AppIntegrityManagerServiceImpl.java | 91 +++++++++++++--- .../SourceStampTestApk.apk | Bin 0 -> 16854 bytes .../AppIntegrityManagerServiceImplTest.java | 102 +++++++++++++----- 5 files changed, 199 insertions(+), 59 deletions(-) create mode 100644 services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/SourceStampTestApk.apk diff --git a/core/java/android/content/integrity/AppInstallMetadata.java b/core/java/android/content/integrity/AppInstallMetadata.java index 4ec94762ac346..4f38fae271f6d 100644 --- a/core/java/android/content/integrity/AppInstallMetadata.java +++ b/core/java/android/content/integrity/AppInstallMetadata.java @@ -42,6 +42,8 @@ public final class AppInstallMetadata { private final List mInstallerCertificates; private final long mVersionCode; private final boolean mIsPreInstalled; + private final boolean mIsStampPresent; + private final boolean mIsStampVerified; private final boolean mIsStampTrusted; // Raw string encoding for the SHA-256 hash of the certificate of the stamp. private final String mStampCertificateHash; @@ -54,6 +56,8 @@ public final class AppInstallMetadata { this.mInstallerCertificates = builder.mInstallerCertificates; this.mVersionCode = builder.mVersionCode; this.mIsPreInstalled = builder.mIsPreInstalled; + this.mIsStampPresent = builder.mIsStampPresent; + this.mIsStampVerified = builder.mIsStampVerified; this.mIsStampTrusted = builder.mIsStampTrusted; this.mStampCertificateHash = builder.mStampCertificateHash; this.mAllowedInstallersAndCertificates = builder.mAllowedInstallersAndCertificates; @@ -89,6 +93,16 @@ public final class AppInstallMetadata { return mIsPreInstalled; } + /** @see AppInstallMetadata.Builder#setIsStampPresent(boolean) */ + public boolean isStampPresent() { + return mIsStampPresent; + } + + /** @see AppInstallMetadata.Builder#setIsStampVerified(boolean) */ + public boolean isStampVerified() { + return mIsStampVerified; + } + /** @see AppInstallMetadata.Builder#setIsStampTrusted(boolean) */ public boolean isStampTrusted() { return mIsStampTrusted; @@ -108,14 +122,16 @@ public final class AppInstallMetadata { public String toString() { return String.format( "AppInstallMetadata { PackageName = %s, AppCerts = %s, InstallerName = %s," - + " InstallerCerts = %s, VersionCode = %d, PreInstalled = %b, " - + "StampTrusted = %b, StampCert = %s }", + + " InstallerCerts = %s, VersionCode = %d, PreInstalled = %b, StampPresent =" + + " %b, StampVerified = %b, StampTrusted = %b, StampCert = %s }", mPackageName, mAppCertificates, mInstallerName == null ? "null" : mInstallerName, mInstallerCertificates == null ? "null" : mInstallerCertificates, mVersionCode, mIsPreInstalled, + mIsStampPresent, + mIsStampVerified, mIsStampTrusted, mStampCertificateHash == null ? "null" : mStampCertificateHash); } @@ -128,6 +144,8 @@ public final class AppInstallMetadata { private List mInstallerCertificates; private long mVersionCode; private boolean mIsPreInstalled; + private boolean mIsStampPresent; + private boolean mIsStampVerified; private boolean mIsStampTrusted; private String mStampCertificateHash; private Map mAllowedInstallersAndCertificates; @@ -221,16 +239,24 @@ public final class AppInstallMetadata { } /** - * Set certificate hash of the stamp embedded in the APK. + * Set whether the stamp embedded in the APK is present or not. * - *

It is represented as the raw string encoding for the SHA-256 hash of the certificate - * of the stamp. - * - * @see AppInstallMetadata#getStampCertificateHash() + * @see AppInstallMetadata#isStampPresent() */ @NonNull - public Builder setStampCertificateHash(@NonNull String stampCertificateHash) { - this.mStampCertificateHash = Objects.requireNonNull(stampCertificateHash); + public Builder setIsStampPresent(boolean isStampPresent) { + this.mIsStampPresent = isStampPresent; + return this; + } + + /** + * Set whether the stamp embedded in the APK is verified or not. + * + * @see AppInstallMetadata#isStampVerified() + */ + @NonNull + public Builder setIsStampVerified(boolean isStampVerified) { + this.mIsStampVerified = isStampVerified; return this; } @@ -245,6 +271,20 @@ public final class AppInstallMetadata { return this; } + /** + * Set certificate hash of the stamp embedded in the APK. + * + *

It is represented as the raw string encoding for the SHA-256 hash of the certificate + * of the stamp. + * + * @see AppInstallMetadata#getStampCertificateHash() + */ + @NonNull + public Builder setStampCertificateHash(@NonNull String stampCertificateHash) { + this.mStampCertificateHash = Objects.requireNonNull(stampCertificateHash); + return this; + } + /** * Build {@link AppInstallMetadata}. * diff --git a/core/java/android/content/integrity/AtomicFormula.java b/core/java/android/content/integrity/AtomicFormula.java index 977a631cecd85..f363a54edc16b 100644 --- a/core/java/android/content/integrity/AtomicFormula.java +++ b/core/java/android/content/integrity/AtomicFormula.java @@ -368,11 +368,10 @@ public abstract class AtomicFormula extends IntegrityFormula { "Key %s cannot be used with StringAtomicFormula", keyToString(key))); mValue = hashValue(key, value); mIsHashedValue = - key == APP_CERTIFICATE + (key == APP_CERTIFICATE || key == INSTALLER_CERTIFICATE - || key == STAMP_CERTIFICATE_HASH - ? true - : !mValue.equals(value); + || key == STAMP_CERTIFICATE_HASH) + || !mValue.equals(value); } StringAtomicFormula(Parcel in) { diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java index 54dd69d706854..f773825a7ff8b 100644 --- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java @@ -52,7 +52,10 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.UserHandle; import android.provider.Settings; +import android.security.FileIntegrityManager; import android.util.Slog; +import android.util.apk.SourceStampVerificationResult; +import android.util.apk.SourceStampVerifier; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; @@ -108,8 +111,10 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { private static final String ALLOWED_INSTALLER_DELIMITER = ","; private static final String INSTALLER_PACKAGE_CERT_DELIMITER = "\\|"; - private static final Set PACKAGE_INSTALLER = new HashSet<>( - Arrays.asList("com.google.android.packageinstaller", "com.android.packageinstaller")); + private static final Set PACKAGE_INSTALLER = + new HashSet<>( + Arrays.asList( + "com.google.android.packageinstaller", "com.android.packageinstaller")); // Access to files inside mRulesDir is protected by mRulesLock; private final Context mContext; @@ -117,6 +122,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { private final PackageManagerInternal mPackageManagerInternal; private final RuleEvaluationEngine mEvaluationEngine; private final IntegrityFileManager mIntegrityFileManager; + private final FileIntegrityManager mFileIntegrityManager; /** Create an instance of {@link AppIntegrityManagerServiceImpl}. */ public static AppIntegrityManagerServiceImpl create(Context context) { @@ -128,6 +134,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { LocalServices.getService(PackageManagerInternal.class), RuleEvaluationEngine.getRuleEvaluationEngine(), IntegrityFileManager.getInstance(), + (FileIntegrityManager) context.getSystemService(Context.FILE_INTEGRITY_SERVICE), handlerThread.getThreadHandler()); } @@ -137,11 +144,13 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { PackageManagerInternal packageManagerInternal, RuleEvaluationEngine evaluationEngine, IntegrityFileManager integrityFileManager, + FileIntegrityManager fileIntegrityManager, Handler handler) { mContext = context; mPackageManagerInternal = packageManagerInternal; mEvaluationEngine = evaluationEngine; mIntegrityFileManager = integrityFileManager; + mFileIntegrityManager = fileIntegrityManager; mHandler = handler; IntentFilter integrityVerificationFilter = new IntentFilter(); @@ -183,8 +192,11 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { success = false; } - FrameworkStatsLog.write(FrameworkStatsLog.INTEGRITY_RULES_PUSHED, success, - ruleProvider, version); + FrameworkStatsLog.write( + FrameworkStatsLog.INTEGRITY_RULES_PUSHED, + success, + ruleProvider, + version); Intent intent = new Intent(); intent.putExtra(EXTRA_STATUS, success ? STATUS_SUCCESS : STATUS_FAILURE); @@ -242,8 +254,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { String installerPackageName = getInstallerPackageName(intent); // Skip integrity verification if the verifier is doing the install. - if (!integrityCheckIncludesRuleProvider() - && isRuleProvider(installerPackageName)) { + if (!integrityCheckIncludesRuleProvider() && isRuleProvider(installerPackageName)) { Slog.i(TAG, "Verifier doing the install. Skipping integrity check."); mPackageManagerInternal.setIntegrityVerificationResult( verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW); @@ -274,15 +285,17 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { builder.setInstallerCertificates(installerCertificates); builder.setIsPreInstalled(isSystemApp(packageName)); builder.setAllowedInstallersAndCert(getAllowedInstallers(packageInfo)); + extractSourceStamp(intent.getData(), builder); AppInstallMetadata appInstallMetadata = builder.build(); Slog.i( TAG, - "To be verified: " + appInstallMetadata + " installers " + getAllowedInstallers( - packageInfo)); - IntegrityCheckResult result = - mEvaluationEngine.evaluate(appInstallMetadata); + "To be verified: " + + appInstallMetadata + + " installers " + + getAllowedInstallers(packageInfo)); + IntegrityCheckResult result = mEvaluationEngine.evaluate(appInstallMetadata); Slog.i( TAG, "Integrity check result: " @@ -323,7 +336,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { * Verify the UID and return the installer package name. * * @return the package name of the installer, or null if it cannot be determined or it is - * installed via adb. + * installed via adb. */ @Nullable private String getInstallerPackageName(Intent intent) { @@ -442,7 +455,8 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { String cert = packageAndCert[1]; packageCertMap.put(packageName, cert); } else if (packageAndCert.length == 1) { - packageCertMap.put(getPackageNameNormalized(packageAndCert[0]), + packageCertMap.put( + getPackageNameNormalized(packageAndCert[0]), INSTALLER_CERTIFICATE_NOT_EVALUATED); } } @@ -452,6 +466,41 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { return packageCertMap; } + /** Extract the source stamp embedded in the APK, if present. */ + private void extractSourceStamp(Uri dataUri, AppInstallMetadata.Builder appInstallMetadata) { + File installationPath = getInstallationPath(dataUri); + if (installationPath == null) { + throw new IllegalArgumentException("Installation path is null, package not found"); + } + SourceStampVerificationResult sourceStampVerificationResult = + SourceStampVerifier.verify(installationPath.getAbsolutePath()); + appInstallMetadata.setIsStampPresent(sourceStampVerificationResult.isPresent()); + appInstallMetadata.setIsStampVerified(sourceStampVerificationResult.isVerified()); + if (sourceStampVerificationResult.isVerified()) { + X509Certificate sourceStampCertificate = + (X509Certificate) sourceStampVerificationResult.getCertificate(); + // Sets source stamp certificate digest. + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] certificateDigest = digest.digest(sourceStampCertificate.getEncoded()); + appInstallMetadata.setStampCertificateHash(getHexDigest(certificateDigest)); + } catch (NoSuchAlgorithmException | CertificateEncodingException e) { + throw new IllegalArgumentException( + "Error computing source stamp certificate digest", e); + } + // Checks if the source stamp certificate is trusted. + try { + appInstallMetadata.setIsStampTrusted( + mFileIntegrityManager.isApkVeritySupported() + && mFileIntegrityManager.isAppSourceCertificateTrusted( + sourceStampCertificate)); + } catch (CertificateEncodingException e) { + throw new IllegalArgumentException( + "Error checking if source stamp certificate is trusted", e); + } + } + } + private static Signature[] getSignatures(@NonNull PackageInfo packageInfo) { SigningInfo signingInfo = packageInfo.signingInfo; @@ -505,8 +554,16 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { ParsedPackage pkg = parser.parsePackage(installationPath, 0, false); int flags = PackageManager.GET_SIGNING_CERTIFICATES | PackageManager.GET_META_DATA; pkg.setSigningDetails(ParsingPackageUtils.collectCertificates(pkg, false)); - return PackageInfoUtils.generate(pkg, null, flags, 0, 0, null, new PackageUserState(), - UserHandle.getCallingUserId(), null); + return PackageInfoUtils.generate( + pkg, + null, + flags, + 0, + 0, + null, + new PackageUserState(), + UserHandle.getCallingUserId(), + null); } catch (Exception e) { Slog.w(TAG, "Exception reading " + dataUri, e); return null; @@ -633,9 +690,9 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { private boolean integrityCheckIncludesRuleProvider() { return Settings.Global.getInt( - mContext.getContentResolver(), - Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, - 0) + mContext.getContentResolver(), + Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, + 0) == 1; } } diff --git a/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/SourceStampTestApk.apk b/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/SourceStampTestApk.apk new file mode 100644 index 0000000000000000000000000000000000000000..8056e0bf6e50d06eee7fa657e08b3a02f6e40e64 GIT binary patch literal 16854 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 literal 0 HcmV?d00001 diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java index d9101bf6a48bb..e2b63e2bb9b7b 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java @@ -28,6 +28,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; @@ -61,6 +62,7 @@ import android.net.Uri; import android.os.Handler; import android.os.Message; import android.provider.Settings; +import android.security.FileIntegrityManager; import androidx.test.InstrumentationRegistry; @@ -96,6 +98,9 @@ public class AppIntegrityManagerServiceImplTest { private static final String TEST_APP_TWO_CERT_PATH = "AppIntegrityManagerServiceImplTest/DummyAppTwoCerts.apk"; + private static final String TEST_APP_SOURCE_STAMP_PATH = + "AppIntegrityManagerServiceImplTest/SourceStampTestApk.apk"; + private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive"; private static final String VERSION = "version"; private static final String TEST_FRAMEWORK_PACKAGE = "com.android.frameworks.servicestests"; @@ -111,6 +116,8 @@ public class AppIntegrityManagerServiceImplTest { // We use SHA256 for package names longer than 32 characters. private static final String INSTALLER_SHA256 = "30F41A7CBF96EE736A54DD6DF759B50ED3CC126ABCEF694E167C324F5976C227"; + private static final String SOURCE_STAMP_CERTIFICATE_HASH = + "681B0E56A796350C08647352A4DB800CC44B2ADC8F4C72FA350BD05D4D50264D"; private static final String DUMMY_APP_TWO_CERTS_CERT_1 = "C0369C2A1096632429DFA8433068AECEAD00BAC337CA92A175036D39CC9AFE94"; @@ -121,27 +128,22 @@ public class AppIntegrityManagerServiceImplTest { private static final String ADB_INSTALLER = "adb"; private static final String PLAY_STORE_CERT = "play_store_cert"; - @org.junit.Rule - public MockitoRule mMockitoRule = MockitoJUnit.rule(); + @org.junit.Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); - @Mock - PackageManagerInternal mPackageManagerInternal; - @Mock - Context mMockContext; - @Mock - Resources mMockResources; - @Mock - RuleEvaluationEngine mRuleEvaluationEngine; - @Mock - IntegrityFileManager mIntegrityFileManager; - @Mock - Handler mHandler; + @Mock PackageManagerInternal mPackageManagerInternal; + @Mock Context mMockContext; + @Mock Resources mMockResources; + @Mock RuleEvaluationEngine mRuleEvaluationEngine; + @Mock IntegrityFileManager mIntegrityFileManager; + @Mock Handler mHandler; + FileIntegrityManager mFileIntegrityManager; private final Context mRealContext = InstrumentationRegistry.getTargetContext(); private PackageManager mSpyPackageManager; private File mTestApk; private File mTestApkTwoCerts; + private File mTestApkSourceStamp; // under test private AppIntegrityManagerServiceImpl mService; @@ -158,19 +160,28 @@ public class AppIntegrityManagerServiceImplTest { Files.copy(inputStream, mTestApkTwoCerts.toPath(), REPLACE_EXISTING); } + mTestApkSourceStamp = File.createTempFile("AppIntegritySourceStamp", ".apk"); + try (InputStream inputStream = mRealContext.getAssets().open(TEST_APP_SOURCE_STAMP_PATH)) { + Files.copy(inputStream, mTestApkSourceStamp.toPath(), REPLACE_EXISTING); + } + + mFileIntegrityManager = + (FileIntegrityManager) + mRealContext.getSystemService(Context.FILE_INTEGRITY_SERVICE); mService = new AppIntegrityManagerServiceImpl( mMockContext, mPackageManagerInternal, mRuleEvaluationEngine, mIntegrityFileManager, + mFileIntegrityManager, mHandler); mSpyPackageManager = spy(mRealContext.getPackageManager()); // setup mocks to prevent NPE when(mMockContext.getPackageManager()).thenReturn(mSpyPackageManager); when(mMockContext.getResources()).thenReturn(mMockResources); - when(mMockResources.getStringArray(anyInt())).thenReturn(new String[]{}); + when(mMockResources.getStringArray(anyInt())).thenReturn(new String[] {}); when(mIntegrityFileManager.initialized()).thenReturn(true); // These are needed to override the Settings.Global.get result. when(mMockContext.getContentResolver()).thenReturn(mRealContext.getContentResolver()); @@ -181,6 +192,7 @@ public class AppIntegrityManagerServiceImplTest { public void tearDown() throws Exception { mTestApk.delete(); mTestApkTwoCerts.delete(); + mTestApkSourceStamp.delete(); } @Test @@ -241,7 +253,8 @@ public class AppIntegrityManagerServiceImplTest { IntentSender mockReceiver = mock(IntentSender.class); List rules = Arrays.asList( - new Rule(IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME), + new Rule( + IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME), Rule.DENY)); mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver); @@ -261,7 +274,8 @@ public class AppIntegrityManagerServiceImplTest { IntentSender mockReceiver = mock(IntentSender.class); List rules = Arrays.asList( - new Rule(IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME), + new Rule( + IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME), Rule.DENY)); mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver); @@ -305,8 +319,7 @@ public class AppIntegrityManagerServiceImplTest { ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(AppInstallMetadata.class); - verify(mRuleEvaluationEngine) - .evaluate(metadataCaptor.capture()); + verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture()); AppInstallMetadata appInstallMetadata = metadataCaptor.getValue(); assertEquals(PACKAGE_NAME, appInstallMetadata.getPackageName()); assertThat(appInstallMetadata.getAppCertificates()).containsExactly(APP_CERT); @@ -341,8 +354,33 @@ public class AppIntegrityManagerServiceImplTest { ArgumentCaptor.forClass(AppInstallMetadata.class); verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture()); AppInstallMetadata appInstallMetadata = metadataCaptor.getValue(); - assertThat(appInstallMetadata.getAppCertificates()).containsExactly( - DUMMY_APP_TWO_CERTS_CERT_1, DUMMY_APP_TWO_CERTS_CERT_2); + assertThat(appInstallMetadata.getAppCertificates()) + .containsExactly(DUMMY_APP_TWO_CERTS_CERT_1, DUMMY_APP_TWO_CERTS_CERT_2); + } + + @Test + public void handleBroadcast_correctArgs_sourceStamp() throws Exception { + whitelistUsAsRuleProvider(); + makeUsSystemApp(); + ArgumentCaptor broadcastReceiverCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + verify(mMockContext) + .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any()); + Intent intent = makeVerificationIntent(); + intent.setDataAndType(Uri.fromFile(mTestApkSourceStamp), PACKAGE_MIME_TYPE); + when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow()); + + broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent); + runJobInHandler(); + + ArgumentCaptor metadataCaptor = + ArgumentCaptor.forClass(AppInstallMetadata.class); + verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture()); + AppInstallMetadata appInstallMetadata = metadataCaptor.getValue(); + assertTrue(appInstallMetadata.isStampPresent()); + assertTrue(appInstallMetadata.isStampVerified()); + assertFalse(appInstallMetadata.isStampTrusted()); + assertEquals(SOURCE_STAMP_CERTIFICATE_HASH, appInstallMetadata.getStampCertificateHash()); } @Test @@ -445,7 +483,7 @@ public class AppIntegrityManagerServiceImplTest { private void whitelistUsAsRuleProvider() { Resources mockResources = mock(Resources.class); when(mockResources.getStringArray(R.array.config_integrityRuleProviderPackages)) - .thenReturn(new String[]{TEST_FRAMEWORK_PACKAGE}); + .thenReturn(new String[] {TEST_FRAMEWORK_PACKAGE}); when(mMockContext.getResources()).thenReturn(mockResources); } @@ -478,8 +516,8 @@ public class AppIntegrityManagerServiceImplTest { PackageInfo packageInfo = mRealContext .getPackageManager() - .getPackageInfo(TEST_FRAMEWORK_PACKAGE, - PackageManager.GET_SIGNING_CERTIFICATES); + .getPackageInfo( + TEST_FRAMEWORK_PACKAGE, PackageManager.GET_SIGNING_CERTIFICATES); doReturn(packageInfo).when(mSpyPackageManager).getPackageInfo(eq(INSTALLER), anyInt()); doReturn(1).when(mSpyPackageManager).getPackageUid(eq(INSTALLER), anyInt()); return makeVerificationIntent(INSTALLER); @@ -501,10 +539,16 @@ public class AppIntegrityManagerServiceImplTest { private void setIntegrityCheckIncludesRuleProvider(boolean shouldInclude) throws Exception { int value = shouldInclude ? 1 : 0; - Settings.Global.putInt(mRealContext.getContentResolver(), - Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, value); - assertThat(Settings.Global.getInt(mRealContext.getContentResolver(), - Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, -1) == 1).isEqualTo( - shouldInclude); + Settings.Global.putInt( + mRealContext.getContentResolver(), + Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, + value); + assertThat( + Settings.Global.getInt( + mRealContext.getContentResolver(), + Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, + -1) + == 1) + .isEqualTo(shouldInclude); } }