1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.integrity;
18 
19 import static android.content.integrity.AppIntegrityManager.EXTRA_STATUS;
20 import static android.content.integrity.AppIntegrityManager.STATUS_FAILURE;
21 import static android.content.integrity.AppIntegrityManager.STATUS_SUCCESS;
22 import static android.content.integrity.InstallerAllowedByManifestFormula.INSTALLER_CERTIFICATE_NOT_EVALUATED;
23 import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
24 import static android.content.pm.PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE;
25 import static android.content.pm.PackageManager.EXTRA_VERIFICATION_INSTALLER_UID;
26 
27 import static com.google.common.truth.Truth.assertThat;
28 
29 import static org.junit.Assert.assertEquals;
30 import static org.junit.Assert.assertFalse;
31 import static org.junit.Assert.assertNull;
32 import static org.junit.Assert.assertTrue;
33 import static org.mockito.ArgumentMatchers.any;
34 import static org.mockito.ArgumentMatchers.anyInt;
35 import static org.mockito.ArgumentMatchers.anyLong;
36 import static org.mockito.ArgumentMatchers.eq;
37 import static org.mockito.Mockito.atLeastOnce;
38 import static org.mockito.Mockito.doReturn;
39 import static org.mockito.Mockito.doThrow;
40 import static org.mockito.Mockito.mock;
41 import static org.mockito.Mockito.spy;
42 import static org.mockito.Mockito.verify;
43 import static org.mockito.Mockito.when;
44 
45 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
46 
47 import android.content.BroadcastReceiver;
48 import android.content.Context;
49 import android.content.Intent;
50 import android.content.IntentFilter;
51 import android.content.IntentSender;
52 import android.content.integrity.AppInstallMetadata;
53 import android.content.integrity.AtomicFormula;
54 import android.content.integrity.IntegrityFormula;
55 import android.content.integrity.Rule;
56 import android.content.pm.ApplicationInfo;
57 import android.content.pm.PackageInfo;
58 import android.content.pm.PackageManager;
59 import android.content.pm.PackageManagerInternal;
60 import android.content.pm.ParceledListSlice;
61 import android.content.res.Resources;
62 import android.net.Uri;
63 import android.os.Handler;
64 import android.os.Message;
65 import android.provider.Settings;
66 
67 import androidx.test.InstrumentationRegistry;
68 
69 import com.android.internal.R;
70 import com.android.server.compat.PlatformCompat;
71 import com.android.server.integrity.engine.RuleEvaluationEngine;
72 import com.android.server.integrity.model.IntegrityCheckResult;
73 import com.android.server.pm.parsing.PackageParser2;
74 import com.android.server.pm.parsing.TestPackageParser2;
75 import com.android.server.testutils.TestUtils;
76 
77 import org.junit.After;
78 import org.junit.Before;
79 import org.junit.Test;
80 import org.junit.runner.RunWith;
81 import org.junit.runners.JUnit4;
82 import org.mockito.ArgumentCaptor;
83 import org.mockito.Mock;
84 import org.mockito.junit.MockitoJUnit;
85 import org.mockito.junit.MockitoRule;
86 
87 import java.io.File;
88 import java.io.IOException;
89 import java.io.InputStream;
90 import java.nio.file.Files;
91 import java.util.Arrays;
92 import java.util.List;
93 import java.util.Map;
94 import java.util.function.Supplier;
95 
96 /** Unit test for {@link com.android.server.integrity.AppIntegrityManagerServiceImpl} */
97 @RunWith(JUnit4.class)
98 public class AppIntegrityManagerServiceImplTest {
99     private static final String TEST_APP_PATH =
100             "AppIntegrityManagerServiceImplTest/AppIntegrityManagerServiceTestApp.apk";
101 
102     private static final String TEST_APP_TWO_CERT_PATH =
103             "AppIntegrityManagerServiceImplTest/DummyAppTwoCerts.apk";
104 
105     private static final String TEST_APP_SOURCE_STAMP_PATH =
106             "AppIntegrityManagerServiceImplTest/SourceStampTestApk.apk";
107 
108     private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
109     private static final String VERSION = "version";
110     private static final String TEST_FRAMEWORK_PACKAGE = "com.android.frameworks.servicestests";
111 
112     private static final String PACKAGE_NAME = "com.test.app";
113 
114     private static final long VERSION_CODE = 100;
115     private static final String INSTALLER = "com.long.random.test.installer.name";
116 
117     // These are obtained by running the test and checking logcat.
118     private static final String APP_CERT =
119             "F14CFECF5070874C05D3D2FA98E046BE20BDE02A0DC74BAF6B59C6A0E4C06850";
120     // We use SHA256 for package names longer than 32 characters.
121     private static final String INSTALLER_SHA256 =
122             "30F41A7CBF96EE736A54DD6DF759B50ED3CC126ABCEF694E167C324F5976C227";
123     private static final String SOURCE_STAMP_CERTIFICATE_HASH =
124             "C6E737809CEF2B08CC6694892215F82A5E8FBC3C2A0F6212770310B90622D2D9";
125 
126     private static final String DUMMY_APP_TWO_CERTS_CERT_1 =
127             "C0369C2A1096632429DFA8433068AECEAD00BAC337CA92A175036D39CC9AFE94";
128     private static final String DUMMY_APP_TWO_CERTS_CERT_2 =
129             "94366E0A80F3A3F0D8171A15760B88E228CD6E1101F0414C98878724FBE70147";
130 
131     private static final String PLAY_STORE_PKG = "com.android.vending";
132     private static final String ADB_INSTALLER = "adb";
133     private static final String PLAY_STORE_CERT = "play_store_cert";
134 
135     @org.junit.Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
136 
137     @Mock PackageManagerInternal mPackageManagerInternal;
138     @Mock PlatformCompat mPlatformCompat;
139     @Mock Context mMockContext;
140     @Mock Resources mMockResources;
141     @Mock RuleEvaluationEngine mRuleEvaluationEngine;
142     @Mock IntegrityFileManager mIntegrityFileManager;
143     @Mock Handler mHandler;
144 
145     private Supplier<PackageParser2> mParserSupplier = TestPackageParser2::new;
146 
147     private final Context mRealContext = InstrumentationRegistry.getTargetContext();
148 
149     private PackageManager mSpyPackageManager;
150     private File mTestApk;
151     private File mTestApkTwoCerts;
152     private File mTestApkSourceStamp;
153 
154     // under test
155     private AppIntegrityManagerServiceImpl mService;
156 
157     @Before
setup()158     public void setup() throws Exception {
159         mTestApk = File.createTempFile("AppIntegrity", ".apk");
160         try (InputStream inputStream = mRealContext.getAssets().open(TEST_APP_PATH)) {
161             Files.copy(inputStream, mTestApk.toPath(), REPLACE_EXISTING);
162         }
163 
164         mTestApkTwoCerts = File.createTempFile("AppIntegrityTwoCerts", ".apk");
165         try (InputStream inputStream = mRealContext.getAssets().open(TEST_APP_TWO_CERT_PATH)) {
166             Files.copy(inputStream, mTestApkTwoCerts.toPath(), REPLACE_EXISTING);
167         }
168 
169         mTestApkSourceStamp = File.createTempFile("AppIntegritySourceStamp", ".apk");
170         try (InputStream inputStream = mRealContext.getAssets().open(TEST_APP_SOURCE_STAMP_PATH)) {
171             Files.copy(inputStream, mTestApkSourceStamp.toPath(), REPLACE_EXISTING);
172         }
173 
174         mService =
175                 new AppIntegrityManagerServiceImpl(
176                         mMockContext,
177                         mPackageManagerInternal,
178                         mParserSupplier,
179                         mRuleEvaluationEngine,
180                         mIntegrityFileManager,
181                         mHandler);
182 
183         mSpyPackageManager = spy(mRealContext.getPackageManager());
184         // setup mocks to prevent NPE
185         when(mMockContext.getPackageManager()).thenReturn(mSpyPackageManager);
186         when(mMockContext.getResources()).thenReturn(mMockResources);
187         when(mMockResources.getStringArray(anyInt())).thenReturn(new String[] {});
188         when(mIntegrityFileManager.initialized()).thenReturn(true);
189         // These are needed to override the Settings.Global.get result.
190         when(mMockContext.getContentResolver()).thenReturn(mRealContext.getContentResolver());
191         setIntegrityCheckIncludesRuleProvider(true);
192     }
193 
194     @After
tearDown()195     public void tearDown() throws Exception {
196         mTestApk.delete();
197         mTestApkTwoCerts.delete();
198         mTestApkSourceStamp.delete();
199     }
200 
201     @Test
updateRuleSet_notAuthorized()202     public void updateRuleSet_notAuthorized() throws Exception {
203         makeUsSystemApp();
204         Rule rule =
205                 new Rule(
206                         new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
207                         Rule.DENY);
208         TestUtils.assertExpectException(
209                 SecurityException.class,
210                 "Only system packages specified in config_integrityRuleProviderPackages are"
211                         + " allowed to call this method.",
212                 () ->
213                         mService.updateRuleSet(
214                                 VERSION,
215                                 new ParceledListSlice<>(Arrays.asList(rule)),
216                                 /* statusReceiver= */ null));
217     }
218 
219     @Test
updateRuleSet_notSystemApp()220     public void updateRuleSet_notSystemApp() throws Exception {
221         whitelistUsAsRuleProvider();
222         makeUsSystemApp(false);
223         Rule rule =
224                 new Rule(
225                         new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
226                         Rule.DENY);
227         TestUtils.assertExpectException(
228                 SecurityException.class,
229                 "Only system packages specified in config_integrityRuleProviderPackages are"
230                         + " allowed to call this method.",
231                 () ->
232                         mService.updateRuleSet(
233                                 VERSION,
234                                 new ParceledListSlice<>(Arrays.asList(rule)),
235                                 /* statusReceiver= */ null));
236     }
237 
238     @Test
updateRuleSet_authorized()239     public void updateRuleSet_authorized() throws Exception {
240         whitelistUsAsRuleProvider();
241         makeUsSystemApp();
242         Rule rule =
243                 new Rule(
244                         new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
245                         Rule.DENY);
246 
247         // no SecurityException
248         mService.updateRuleSet(
249                 VERSION, new ParceledListSlice<>(Arrays.asList(rule)), mock(IntentSender.class));
250     }
251 
252     @Test
updateRuleSet_correctMethodCall()253     public void updateRuleSet_correctMethodCall() throws Exception {
254         whitelistUsAsRuleProvider();
255         makeUsSystemApp();
256         IntentSender mockReceiver = mock(IntentSender.class);
257         List<Rule> rules =
258                 Arrays.asList(
259                         new Rule(
260                                 IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME),
261                                 Rule.DENY));
262 
263         mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver);
264         runJobInHandler();
265 
266         verify(mIntegrityFileManager).writeRules(VERSION, TEST_FRAMEWORK_PACKAGE, rules);
267         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
268         verify(mockReceiver).sendIntent(any(), anyInt(), intentCaptor.capture(), any(), any());
269         assertEquals(STATUS_SUCCESS, intentCaptor.getValue().getIntExtra(EXTRA_STATUS, -1));
270     }
271 
272     @Test
updateRuleSet_fail()273     public void updateRuleSet_fail() throws Exception {
274         whitelistUsAsRuleProvider();
275         makeUsSystemApp();
276         doThrow(new IOException()).when(mIntegrityFileManager).writeRules(any(), any(), any());
277         IntentSender mockReceiver = mock(IntentSender.class);
278         List<Rule> rules =
279                 Arrays.asList(
280                         new Rule(
281                                 IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME),
282                                 Rule.DENY));
283 
284         mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver);
285         runJobInHandler();
286 
287         verify(mIntegrityFileManager).writeRules(VERSION, TEST_FRAMEWORK_PACKAGE, rules);
288         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
289         verify(mockReceiver).sendIntent(any(), anyInt(), intentCaptor.capture(), any(), any());
290         assertEquals(STATUS_FAILURE, intentCaptor.getValue().getIntExtra(EXTRA_STATUS, -1));
291     }
292 
293     @Test
broadcastReceiverRegistration()294     public void broadcastReceiverRegistration() throws Exception {
295         whitelistUsAsRuleProvider();
296         makeUsSystemApp();
297         ArgumentCaptor<IntentFilter> intentFilterCaptor =
298                 ArgumentCaptor.forClass(IntentFilter.class);
299 
300         verify(mMockContext).registerReceiver(any(), intentFilterCaptor.capture(), any(), any());
301         assertEquals(1, intentFilterCaptor.getValue().countActions());
302         assertEquals(
303                 Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION,
304                 intentFilterCaptor.getValue().getAction(0));
305         assertEquals(1, intentFilterCaptor.getValue().countDataTypes());
306         assertEquals(PACKAGE_MIME_TYPE, intentFilterCaptor.getValue().getDataType(0));
307     }
308 
309     @Test
handleBroadcast_correctArgs()310     public void handleBroadcast_correctArgs() throws Exception {
311         whitelistUsAsRuleProvider();
312         makeUsSystemApp();
313         ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
314                 ArgumentCaptor.forClass(BroadcastReceiver.class);
315         verify(mMockContext)
316                 .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
317         Intent intent = makeVerificationIntent();
318         when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
319 
320         broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
321         runJobInHandler();
322 
323         ArgumentCaptor<AppInstallMetadata> metadataCaptor =
324                 ArgumentCaptor.forClass(AppInstallMetadata.class);
325         verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture());
326         AppInstallMetadata appInstallMetadata = metadataCaptor.getValue();
327         assertEquals(PACKAGE_NAME, appInstallMetadata.getPackageName());
328         assertThat(appInstallMetadata.getAppCertificates()).containsExactly(APP_CERT);
329         assertEquals(INSTALLER_SHA256, appInstallMetadata.getInstallerName());
330         // we cannot check installer cert because it seems to be device specific.
331         assertEquals(VERSION_CODE, appInstallMetadata.getVersionCode());
332         assertFalse(appInstallMetadata.isPreInstalled());
333         // Asserting source stamp not present.
334         assertFalse(appInstallMetadata.isStampPresent());
335         assertFalse(appInstallMetadata.isStampVerified());
336         assertFalse(appInstallMetadata.isStampTrusted());
337         assertNull(appInstallMetadata.getStampCertificateHash());
338         // These are hardcoded in the test apk android manifest
339         Map<String, String> allowedInstallers =
340                 appInstallMetadata.getAllowedInstallersAndCertificates();
341         assertEquals(2, allowedInstallers.size());
342         assertEquals(PLAY_STORE_CERT, allowedInstallers.get(PLAY_STORE_PKG));
343         assertEquals(INSTALLER_CERTIFICATE_NOT_EVALUATED, allowedInstallers.get(ADB_INSTALLER));
344     }
345 
346     @Test
handleBroadcast_correctArgs_multipleCerts()347     public void handleBroadcast_correctArgs_multipleCerts() throws Exception {
348         whitelistUsAsRuleProvider();
349         makeUsSystemApp();
350         ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
351                 ArgumentCaptor.forClass(BroadcastReceiver.class);
352         verify(mMockContext)
353                 .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
354         Intent intent = makeVerificationIntent();
355         intent.setDataAndType(Uri.fromFile(mTestApkTwoCerts), PACKAGE_MIME_TYPE);
356         when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
357 
358         broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
359         runJobInHandler();
360 
361         ArgumentCaptor<AppInstallMetadata> metadataCaptor =
362                 ArgumentCaptor.forClass(AppInstallMetadata.class);
363         verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture());
364         AppInstallMetadata appInstallMetadata = metadataCaptor.getValue();
365         assertThat(appInstallMetadata.getAppCertificates())
366                 .containsExactly(DUMMY_APP_TWO_CERTS_CERT_1, DUMMY_APP_TWO_CERTS_CERT_2);
367     }
368 
369     @Test
handleBroadcast_correctArgs_sourceStamp()370     public void handleBroadcast_correctArgs_sourceStamp() throws Exception {
371         whitelistUsAsRuleProvider();
372         makeUsSystemApp();
373         ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
374                 ArgumentCaptor.forClass(BroadcastReceiver.class);
375         verify(mMockContext)
376                 .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
377         Intent intent = makeVerificationIntent();
378         intent.setDataAndType(Uri.fromFile(mTestApkSourceStamp), PACKAGE_MIME_TYPE);
379         when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
380 
381         broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
382         runJobInHandler();
383 
384         ArgumentCaptor<AppInstallMetadata> metadataCaptor =
385                 ArgumentCaptor.forClass(AppInstallMetadata.class);
386         verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture());
387         AppInstallMetadata appInstallMetadata = metadataCaptor.getValue();
388         assertTrue(appInstallMetadata.isStampPresent());
389         assertTrue(appInstallMetadata.isStampVerified());
390         assertTrue(appInstallMetadata.isStampTrusted());
391         assertEquals(SOURCE_STAMP_CERTIFICATE_HASH, appInstallMetadata.getStampCertificateHash());
392     }
393 
394     @Test
handleBroadcast_allow()395     public void handleBroadcast_allow() throws Exception {
396         whitelistUsAsRuleProvider();
397         makeUsSystemApp();
398         ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
399                 ArgumentCaptor.forClass(BroadcastReceiver.class);
400         verify(mMockContext)
401                 .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
402         Intent intent = makeVerificationIntent();
403         when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
404 
405         broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
406         runJobInHandler();
407 
408         verify(mPackageManagerInternal)
409                 .setIntegrityVerificationResult(
410                         1, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
411     }
412 
413     @Test
handleBroadcast_reject()414     public void handleBroadcast_reject() throws Exception {
415         whitelistUsAsRuleProvider();
416         makeUsSystemApp();
417         ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
418                 ArgumentCaptor.forClass(BroadcastReceiver.class);
419         verify(mMockContext)
420                 .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
421         when(mRuleEvaluationEngine.evaluate(any()))
422                 .thenReturn(
423                         IntegrityCheckResult.deny(
424                                 Arrays.asList(
425                                         new Rule(
426                                                 new AtomicFormula.BooleanAtomicFormula(
427                                                         AtomicFormula.PRE_INSTALLED, false),
428                                                 Rule.DENY))));
429         Intent intent = makeVerificationIntent();
430 
431         broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
432         runJobInHandler();
433 
434         verify(mPackageManagerInternal)
435                 .setIntegrityVerificationResult(
436                         1, PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT);
437     }
438 
439     @Test
handleBroadcast_notInitialized()440     public void handleBroadcast_notInitialized() throws Exception {
441         whitelistUsAsRuleProvider();
442         makeUsSystemApp();
443         when(mIntegrityFileManager.initialized()).thenReturn(false);
444         ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
445                 ArgumentCaptor.forClass(BroadcastReceiver.class);
446         verify(mMockContext)
447                 .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
448         Intent intent = makeVerificationIntent();
449         when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
450 
451         broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
452         runJobInHandler();
453 
454         // The evaluation will still run since we still evaluate manifest based rules.
455         verify(mPackageManagerInternal)
456                 .setIntegrityVerificationResult(
457                         1, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
458     }
459 
460     @Test
verifierAsInstaller_skipIntegrityVerification()461     public void verifierAsInstaller_skipIntegrityVerification() throws Exception {
462         whitelistUsAsRuleProvider();
463         makeUsSystemApp();
464         setIntegrityCheckIncludesRuleProvider(false);
465         ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
466                 ArgumentCaptor.forClass(BroadcastReceiver.class);
467         verify(mMockContext, atLeastOnce())
468                 .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
469         Intent intent = makeVerificationIntent(TEST_FRAMEWORK_PACKAGE);
470         when(mRuleEvaluationEngine.evaluate(any()))
471                 .thenReturn(IntegrityCheckResult.deny(/* rule= */ null));
472 
473         broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
474         runJobInHandler();
475 
476         verify(mPackageManagerInternal)
477                 .setIntegrityVerificationResult(
478                         1, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
479     }
480 
481     @Test
getCurrentRules()482     public void getCurrentRules() throws Exception {
483         whitelistUsAsRuleProvider();
484         makeUsSystemApp();
485         Rule rule = new Rule(IntegrityFormula.Application.packageNameEquals("package"), Rule.DENY);
486         when(mIntegrityFileManager.readRules(any())).thenReturn(Arrays.asList(rule));
487 
488         assertThat(mService.getCurrentRules().getList()).containsExactly(rule);
489     }
490 
491     @Test
getWhitelistedRuleProviders_returnsEmptyForNonSystemApps()492     public void getWhitelistedRuleProviders_returnsEmptyForNonSystemApps() throws Exception {
493         whitelistUsAsRuleProvider();
494         makeUsSystemApp(false);
495 
496         assertThat(mService.getWhitelistedRuleProviders()).isEmpty();
497     }
498 
499     @Test
getWhitelistedRuleProviders()500     public void getWhitelistedRuleProviders() throws Exception {
501         whitelistUsAsRuleProvider();
502         makeUsSystemApp();
503 
504         assertThat(mService.getWhitelistedRuleProviders()).containsExactly(TEST_FRAMEWORK_PACKAGE);
505     }
506 
whitelistUsAsRuleProvider()507     private void whitelistUsAsRuleProvider() {
508         Resources mockResources = mock(Resources.class);
509         when(mockResources.getStringArray(R.array.config_integrityRuleProviderPackages))
510                 .thenReturn(new String[] {TEST_FRAMEWORK_PACKAGE});
511         when(mMockContext.getResources()).thenReturn(mockResources);
512     }
513 
runJobInHandler()514     private void runJobInHandler() {
515         ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
516         // sendMessageAtTime is the first non-final method in the call chain when "post" is invoked.
517         verify(mHandler).sendMessageAtTime(messageCaptor.capture(), anyLong());
518         messageCaptor.getValue().getCallback().run();
519     }
520 
makeUsSystemApp()521     private void makeUsSystemApp() throws Exception {
522         makeUsSystemApp(true);
523     }
524 
makeUsSystemApp(boolean isSystemApp)525     private void makeUsSystemApp(boolean isSystemApp) throws Exception {
526         PackageInfo packageInfo =
527                 mRealContext.getPackageManager().getPackageInfo(TEST_FRAMEWORK_PACKAGE, 0);
528         if (isSystemApp) {
529             packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
530         } else {
531             packageInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
532         }
533         doReturn(packageInfo)
534                 .when(mSpyPackageManager)
535                 .getPackageInfo(eq(TEST_FRAMEWORK_PACKAGE), anyInt());
536         when(mMockContext.getPackageManager()).thenReturn(mSpyPackageManager);
537     }
538 
makeVerificationIntent()539     private Intent makeVerificationIntent() throws Exception {
540         PackageInfo packageInfo =
541                 mRealContext
542                         .getPackageManager()
543                         .getPackageInfo(
544                                 TEST_FRAMEWORK_PACKAGE, PackageManager.GET_SIGNING_CERTIFICATES);
545         doReturn(packageInfo).when(mSpyPackageManager).getPackageInfo(eq(INSTALLER), anyInt());
546         doReturn(1).when(mSpyPackageManager).getPackageUid(eq(INSTALLER), anyInt());
547         doReturn(new String[]{INSTALLER}).when(mSpyPackageManager).getPackagesForUid(anyInt());
548         return makeVerificationIntent(INSTALLER);
549     }
550 
makeVerificationIntent(String installer)551     private Intent makeVerificationIntent(String installer) throws Exception {
552         Intent intent = new Intent();
553         intent.setDataAndType(Uri.fromFile(mTestApk), PACKAGE_MIME_TYPE);
554         intent.setAction(Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION);
555         intent.putExtra(EXTRA_VERIFICATION_ID, 1);
556         intent.putExtra(Intent.EXTRA_PACKAGE_NAME, PACKAGE_NAME);
557         intent.putExtra(EXTRA_VERIFICATION_INSTALLER_PACKAGE, installer);
558         intent.putExtra(
559                 EXTRA_VERIFICATION_INSTALLER_UID,
560                 mMockContext.getPackageManager().getPackageUid(installer, /* flags= */ 0));
561         intent.putExtra(Intent.EXTRA_LONG_VERSION_CODE, VERSION_CODE);
562         return intent;
563     }
564 
setIntegrityCheckIncludesRuleProvider(boolean shouldInclude)565     private void setIntegrityCheckIncludesRuleProvider(boolean shouldInclude) throws Exception {
566         int value = shouldInclude ? 1 : 0;
567         Settings.Global.putInt(
568                 mRealContext.getContentResolver(),
569                 Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
570                 value);
571         assertThat(
572                         Settings.Global.getInt(
573                                         mRealContext.getContentResolver(),
574                                         Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
575                                         -1)
576                                 == 1)
577                 .isEqualTo(shouldInclude);
578     }
579 }
580