Home | History | Annotate | Download | only in timezone
      1 /*
      2  * Copyright (C) 2017 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.timezone;
     18 
     19 import com.android.timezone.distro.DistroVersion;
     20 import com.android.timezone.distro.StagedDistroOperation;
     21 import com.android.timezone.distro.TimeZoneDistro;
     22 import com.android.timezone.distro.installer.TimeZoneDistroInstaller;
     23 
     24 import org.junit.Before;
     25 import org.junit.Test;
     26 
     27 import android.app.timezone.Callback;
     28 import android.app.timezone.DistroRulesVersion;
     29 import android.app.timezone.ICallback;
     30 import android.app.timezone.RulesManager;
     31 import android.app.timezone.RulesState;
     32 import android.os.ParcelFileDescriptor;
     33 
     34 import java.io.File;
     35 import java.io.FileDescriptor;
     36 import java.io.FileOutputStream;
     37 import java.io.IOException;
     38 import java.io.PrintWriter;
     39 import java.util.concurrent.Executor;
     40 import javax.annotation.Nullable;
     41 
     42 import libcore.io.IoUtils;
     43 
     44 import static com.android.server.timezone.RulesManagerService.REQUIRED_UPDATER_PERMISSION;
     45 import static org.junit.Assert.assertEquals;
     46 import static org.junit.Assert.assertFalse;
     47 import static org.junit.Assert.assertNotNull;
     48 import static org.junit.Assert.assertNull;
     49 import static org.junit.Assert.assertTrue;
     50 import static org.junit.Assert.fail;
     51 import static org.mockito.ArgumentMatchers.any;
     52 import static org.mockito.Mockito.doNothing;
     53 import static org.mockito.Mockito.doReturn;
     54 import static org.mockito.Mockito.doThrow;
     55 import static org.mockito.Mockito.mock;
     56 import static org.mockito.Mockito.reset;
     57 import static org.mockito.Mockito.verify;
     58 import static org.mockito.Mockito.verifyNoMoreInteractions;
     59 import static org.mockito.Mockito.verifyZeroInteractions;
     60 import static org.mockito.Mockito.when;
     61 
     62 /**
     63  * White box interaction / unit testing of the {@link RulesManagerService}.
     64  */
     65 public class RulesManagerServiceTest {
     66 
     67     private RulesManagerService mRulesManagerService;
     68 
     69     private FakeExecutor mFakeExecutor;
     70     private PermissionHelper mMockPermissionHelper;
     71     private PackageTracker mMockPackageTracker;
     72     private TimeZoneDistroInstaller mMockTimeZoneDistroInstaller;
     73 
     74     @Before
     75     public void setUp() {
     76         mFakeExecutor = new FakeExecutor();
     77 
     78         mMockPackageTracker = mock(PackageTracker.class);
     79         mMockPermissionHelper = mock(PermissionHelper.class);
     80         mMockTimeZoneDistroInstaller = mock(TimeZoneDistroInstaller.class);
     81 
     82         mRulesManagerService = new RulesManagerService(
     83                 mMockPermissionHelper,
     84                 mFakeExecutor,
     85                 mMockPackageTracker,
     86                 mMockTimeZoneDistroInstaller);
     87     }
     88 
     89     @Test(expected = SecurityException.class)
     90     public void getRulesState_noCallerPermission() throws Exception {
     91         configureCallerDoesNotHavePermission();
     92         mRulesManagerService.getRulesState();
     93     }
     94 
     95     @Test(expected = SecurityException.class)
     96     public void requestInstall_noCallerPermission() throws Exception {
     97         configureCallerDoesNotHavePermission();
     98         mRulesManagerService.requestInstall(null, null, null);
     99     }
    100 
    101     @Test(expected = SecurityException.class)
    102     public void requestUninstall_noCallerPermission() throws Exception {
    103         configureCallerDoesNotHavePermission();
    104         mRulesManagerService.requestUninstall(null, null);
    105     }
    106 
    107     @Test(expected = SecurityException.class)
    108     public void requestNothing_noCallerPermission() throws Exception {
    109         configureCallerDoesNotHavePermission();
    110         mRulesManagerService.requestNothing(null, true);
    111     }
    112 
    113     @Test
    114     public void getRulesState_systemRulesError() throws Exception {
    115         configureDeviceCannotReadSystemRulesVersion();
    116 
    117         assertNull(mRulesManagerService.getRulesState());
    118     }
    119 
    120     @Test
    121     public void getRulesState_stagedInstall() throws Exception {
    122         configureCallerHasPermission();
    123 
    124         configureDeviceSystemRulesVersion("2016a");
    125 
    126         DistroVersion stagedDistroVersion = new DistroVersion(
    127                 DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
    128                 DistroVersion.CURRENT_FORMAT_MINOR_VERSION - 1,
    129                 "2016c",
    130                 3);
    131         configureStagedInstall(stagedDistroVersion);
    132 
    133         DistroVersion installedDistroVersion = new DistroVersion(
    134                 DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
    135                 DistroVersion.CURRENT_FORMAT_MINOR_VERSION - 1,
    136                 "2016b",
    137                 4);
    138         configureInstalledDistroVersion(installedDistroVersion);
    139 
    140         DistroRulesVersion stagedDistroRulesVersion = new DistroRulesVersion(
    141                 stagedDistroVersion.rulesVersion, stagedDistroVersion.revision);
    142         DistroRulesVersion installedDistroRulesVersion = new DistroRulesVersion(
    143                 installedDistroVersion.rulesVersion, installedDistroVersion.revision);
    144         RulesState expectedRuleState = new RulesState(
    145                 "2016a", RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
    146                 false /* operationInProgress */,
    147                 RulesState.STAGED_OPERATION_INSTALL, stagedDistroRulesVersion,
    148                 RulesState.DISTRO_STATUS_INSTALLED, installedDistroRulesVersion);
    149         assertEquals(expectedRuleState, mRulesManagerService.getRulesState());
    150     }
    151 
    152     @Test
    153     public void getRulesState_nothingStaged() throws Exception {
    154         configureCallerHasPermission();
    155 
    156         configureDeviceSystemRulesVersion("2016a");
    157 
    158         configureNoStagedOperation();
    159 
    160         DistroVersion installedDistroVersion = new DistroVersion(
    161                 DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
    162                 DistroVersion.CURRENT_FORMAT_MINOR_VERSION - 1,
    163                 "2016b",
    164                 4);
    165         configureInstalledDistroVersion(installedDistroVersion);
    166 
    167         DistroRulesVersion installedDistroRulesVersion = new DistroRulesVersion(
    168                 installedDistroVersion.rulesVersion, installedDistroVersion.revision);
    169         RulesState expectedRuleState = new RulesState(
    170                 "2016a", RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
    171                 false /* operationInProgress */,
    172                 RulesState.STAGED_OPERATION_NONE, null /* stagedDistroRulesVersion */,
    173                 RulesState.DISTRO_STATUS_INSTALLED, installedDistroRulesVersion);
    174         assertEquals(expectedRuleState, mRulesManagerService.getRulesState());
    175     }
    176 
    177     @Test
    178     public void getRulesState_uninstallStaged() throws Exception {
    179         configureCallerHasPermission();
    180 
    181         configureDeviceSystemRulesVersion("2016a");
    182 
    183         configureStagedUninstall();
    184 
    185         DistroVersion installedDistroVersion = new DistroVersion(
    186                 DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
    187                 DistroVersion.CURRENT_FORMAT_MINOR_VERSION - 1,
    188                 "2016b",
    189                 4);
    190         configureInstalledDistroVersion(installedDistroVersion);
    191 
    192         DistroRulesVersion installedDistroRulesVersion = new DistroRulesVersion(
    193                 installedDistroVersion.rulesVersion, installedDistroVersion.revision);
    194         RulesState expectedRuleState = new RulesState(
    195                 "2016a", RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
    196                 false /* operationInProgress */,
    197                 RulesState.STAGED_OPERATION_UNINSTALL, null /* stagedDistroRulesVersion */,
    198                 RulesState.DISTRO_STATUS_INSTALLED, installedDistroRulesVersion);
    199         assertEquals(expectedRuleState, mRulesManagerService.getRulesState());
    200     }
    201 
    202     @Test
    203     public void getRulesState_installedRulesError() throws Exception {
    204         configureCallerHasPermission();
    205 
    206         String systemRulesVersion = "2016a";
    207         configureDeviceSystemRulesVersion(systemRulesVersion);
    208 
    209         configureStagedUninstall();
    210         configureDeviceCannotReadInstalledDistroVersion();
    211 
    212         RulesState expectedRuleState = new RulesState(
    213                 "2016a", RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
    214                 false /* operationInProgress */,
    215                 RulesState.STAGED_OPERATION_UNINSTALL, null /* stagedDistroRulesVersion */,
    216                 RulesState.DISTRO_STATUS_UNKNOWN, null /* installedDistroRulesVersion */);
    217         assertEquals(expectedRuleState, mRulesManagerService.getRulesState());
    218     }
    219 
    220     @Test
    221     public void getRulesState_stagedRulesError() throws Exception {
    222         configureCallerHasPermission();
    223 
    224         String systemRulesVersion = "2016a";
    225         configureDeviceSystemRulesVersion(systemRulesVersion);
    226 
    227         configureDeviceCannotReadStagedDistroOperation();
    228 
    229         DistroVersion installedDistroVersion = new DistroVersion(
    230                 DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
    231                 DistroVersion.CURRENT_FORMAT_MINOR_VERSION - 1,
    232                 "2016b",
    233                 4);
    234         configureInstalledDistroVersion(installedDistroVersion);
    235 
    236         DistroRulesVersion installedDistroRulesVersion = new DistroRulesVersion(
    237                 installedDistroVersion.rulesVersion, installedDistroVersion.revision);
    238         RulesState expectedRuleState = new RulesState(
    239                 "2016a", RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
    240                 false /* operationInProgress */,
    241                 RulesState.STAGED_OPERATION_UNKNOWN, null /* stagedDistroRulesVersion */,
    242                 RulesState.DISTRO_STATUS_INSTALLED, installedDistroRulesVersion);
    243         assertEquals(expectedRuleState, mRulesManagerService.getRulesState());
    244     }
    245 
    246     @Test
    247     public void getRulesState_noInstalledRules() throws Exception {
    248         configureCallerHasPermission();
    249 
    250         String systemRulesVersion = "2016a";
    251         configureDeviceSystemRulesVersion(systemRulesVersion);
    252         configureNoStagedOperation();
    253         configureInstalledDistroVersion(null);
    254 
    255         RulesState expectedRuleState = new RulesState(
    256                 systemRulesVersion, RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
    257                 false /* operationInProgress */,
    258                 RulesState.STAGED_OPERATION_NONE, null /* stagedDistroRulesVersion */,
    259                 RulesState.DISTRO_STATUS_NONE, null /* installedDistroRulesVersion */);
    260         assertEquals(expectedRuleState, mRulesManagerService.getRulesState());
    261     }
    262 
    263     @Test
    264     public void getRulesState_operationInProgress() throws Exception {
    265         configureCallerHasPermission();
    266 
    267         String systemRulesVersion = "2016a";
    268         String installedRulesVersion = "2016b";
    269         int revision = 3;
    270 
    271         configureDeviceSystemRulesVersion(systemRulesVersion);
    272 
    273         DistroVersion installedDistroVersion = new DistroVersion(
    274                 DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
    275                 DistroVersion.CURRENT_FORMAT_MINOR_VERSION - 1,
    276                 installedRulesVersion,
    277                 revision);
    278         configureInstalledDistroVersion(installedDistroVersion);
    279 
    280         ParcelFileDescriptor parcelFileDescriptor =
    281                 createParcelFileDescriptor(createArbitraryBytes(1000));
    282 
    283         // Start an async operation so there is one in progress. The mFakeExecutor won't actually
    284         // execute it.
    285         byte[] tokenBytes = createArbitraryTokenBytes();
    286         ICallback callback = new StubbedCallback();
    287 
    288         mRulesManagerService.requestInstall(parcelFileDescriptor, tokenBytes, callback);
    289 
    290         // Request the rules state while the async operation is "happening".
    291         RulesState actualRulesState = mRulesManagerService.getRulesState();
    292         RulesState expectedRuleState = new RulesState(
    293                 systemRulesVersion, RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
    294                 true /* operationInProgress */,
    295                 RulesState.STAGED_OPERATION_UNKNOWN, null /* stagedDistroRulesVersion */,
    296                 RulesState.DISTRO_STATUS_UNKNOWN, null /* installedDistroRulesVersion */);
    297         assertEquals(expectedRuleState, actualRulesState);
    298     }
    299 
    300     @Test
    301     public void requestInstall_operationInProgress() throws Exception {
    302         configureCallerHasPermission();
    303 
    304         ParcelFileDescriptor parcelFileDescriptor1 =
    305                 createParcelFileDescriptor(createArbitraryBytes(1000));
    306 
    307         byte[] tokenBytes = createArbitraryTokenBytes();
    308         ICallback callback = new StubbedCallback();
    309 
    310         // First request should succeed.
    311         assertEquals(RulesManager.SUCCESS,
    312                 mRulesManagerService.requestInstall(parcelFileDescriptor1, tokenBytes, callback));
    313 
    314         // Something async should be enqueued. Clear it but do not execute it so we can detect the
    315         // second request does nothing.
    316         mFakeExecutor.getAndResetLastCommand();
    317 
    318         // Second request should fail.
    319         ParcelFileDescriptor parcelFileDescriptor2 =
    320                 createParcelFileDescriptor(createArbitraryBytes(1000));
    321         assertEquals(RulesManager.ERROR_OPERATION_IN_PROGRESS,
    322                 mRulesManagerService.requestInstall(parcelFileDescriptor2, tokenBytes, callback));
    323 
    324         assertClosed(parcelFileDescriptor2);
    325 
    326         // Assert nothing async was enqueued.
    327         mFakeExecutor.assertNothingQueued();
    328         verifyNoInstallerCallsMade();
    329         verifyNoPackageTrackerCallsMade();
    330     }
    331 
    332     @Test
    333     public void requestInstall_badToken() throws Exception {
    334         configureCallerHasPermission();
    335 
    336         ParcelFileDescriptor parcelFileDescriptor =
    337                 createParcelFileDescriptor(createArbitraryBytes(1000));
    338 
    339         byte[] badTokenBytes = new byte[2];
    340         ICallback callback = new StubbedCallback();
    341 
    342         try {
    343             mRulesManagerService.requestInstall(parcelFileDescriptor, badTokenBytes, callback);
    344             fail();
    345         } catch (IllegalArgumentException expected) {
    346         }
    347 
    348         assertClosed(parcelFileDescriptor);
    349 
    350         // Assert nothing async was enqueued.
    351         mFakeExecutor.assertNothingQueued();
    352         verifyNoInstallerCallsMade();
    353         verifyNoPackageTrackerCallsMade();
    354     }
    355 
    356     @Test
    357     public void requestInstall_nullParcelFileDescriptor() throws Exception {
    358         configureCallerHasPermission();
    359 
    360         ParcelFileDescriptor parcelFileDescriptor = null;
    361         byte[] tokenBytes = createArbitraryTokenBytes();
    362         ICallback callback = new StubbedCallback();
    363 
    364         try {
    365             mRulesManagerService.requestInstall(parcelFileDescriptor, tokenBytes, callback);
    366             fail();
    367         } catch (NullPointerException expected) {}
    368 
    369         // Assert nothing async was enqueued.
    370         mFakeExecutor.assertNothingQueued();
    371         verifyNoInstallerCallsMade();
    372         verifyNoPackageTrackerCallsMade();
    373     }
    374 
    375     @Test
    376     public void requestInstall_nullCallback() throws Exception {
    377         configureCallerHasPermission();
    378 
    379         ParcelFileDescriptor parcelFileDescriptor =
    380                 createParcelFileDescriptor(createArbitraryBytes(1000));
    381         byte[] tokenBytes = createArbitraryTokenBytes();
    382         ICallback callback = null;
    383 
    384         try {
    385             mRulesManagerService.requestInstall(parcelFileDescriptor, tokenBytes, callback);
    386             fail();
    387         } catch (NullPointerException expected) {}
    388 
    389         assertClosed(parcelFileDescriptor);
    390 
    391         // Assert nothing async was enqueued.
    392         mFakeExecutor.assertNothingQueued();
    393         verifyNoInstallerCallsMade();
    394         verifyNoPackageTrackerCallsMade();
    395     }
    396 
    397     @Test
    398     public void requestInstall_asyncSuccess() throws Exception {
    399         configureCallerHasPermission();
    400 
    401         ParcelFileDescriptor parcelFileDescriptor =
    402                 createParcelFileDescriptor(createArbitraryBytes(1000));
    403 
    404         CheckToken token = createArbitraryToken();
    405         byte[] tokenBytes = token.toByteArray();
    406 
    407         TestCallback callback = new TestCallback();
    408 
    409         // Request the install.
    410         assertEquals(RulesManager.SUCCESS,
    411                 mRulesManagerService.requestInstall(parcelFileDescriptor, tokenBytes, callback));
    412 
    413         // Assert nothing has happened yet.
    414         callback.assertNoResultReceived();
    415         verifyNoInstallerCallsMade();
    416         verifyNoPackageTrackerCallsMade();
    417 
    418         // Set up the installer.
    419         configureStageInstallExpectation(TimeZoneDistroInstaller.INSTALL_SUCCESS);
    420 
    421         // Simulate the async execution.
    422         mFakeExecutor.simulateAsyncExecutionOfLastCommand();
    423 
    424         assertClosed(parcelFileDescriptor);
    425 
    426         // Verify the expected calls were made to other components.
    427         verifyStageInstallCalled();
    428         verifyPackageTrackerCalled(token, true /* success */);
    429 
    430         // Check the callback was called.
    431         callback.assertResultReceived(Callback.SUCCESS);
    432     }
    433 
    434     @Test
    435     public void requestInstall_nullTokenBytes() throws Exception {
    436         configureCallerHasPermission();
    437 
    438         ParcelFileDescriptor parcelFileDescriptor =
    439                 createParcelFileDescriptor(createArbitraryBytes(1000));
    440 
    441         TestCallback callback = new TestCallback();
    442 
    443         // Request the install.
    444         assertEquals(RulesManager.SUCCESS,
    445                 mRulesManagerService.requestInstall(
    446                         parcelFileDescriptor, null /* tokenBytes */, callback));
    447 
    448         // Assert nothing has happened yet.
    449         verifyNoInstallerCallsMade();
    450         callback.assertNoResultReceived();
    451 
    452         // Set up the installer.
    453         configureStageInstallExpectation(TimeZoneDistroInstaller.INSTALL_SUCCESS);
    454 
    455         // Simulate the async execution.
    456         mFakeExecutor.simulateAsyncExecutionOfLastCommand();
    457 
    458         assertClosed(parcelFileDescriptor);
    459 
    460         // Verify the expected calls were made to other components.
    461         verifyStageInstallCalled();
    462         verifyPackageTrackerCalled(null /* expectedToken */, true /* success */);
    463 
    464         // Check the callback was received.
    465         callback.assertResultReceived(Callback.SUCCESS);
    466     }
    467 
    468     @Test
    469     public void requestInstall_asyncInstallFail() throws Exception {
    470         configureCallerHasPermission();
    471 
    472         ParcelFileDescriptor parcelFileDescriptor =
    473                 createParcelFileDescriptor(createArbitraryBytes(1000));
    474 
    475         CheckToken token = createArbitraryToken();
    476         byte[] tokenBytes = token.toByteArray();
    477 
    478         TestCallback callback = new TestCallback();
    479 
    480         // Request the install.
    481         assertEquals(RulesManager.SUCCESS,
    482                 mRulesManagerService.requestInstall(parcelFileDescriptor, tokenBytes, callback));
    483 
    484         // Assert nothing has happened yet.
    485         verifyNoInstallerCallsMade();
    486         callback.assertNoResultReceived();
    487 
    488         // Set up the installer.
    489         configureStageInstallExpectation(TimeZoneDistroInstaller.INSTALL_FAIL_VALIDATION_ERROR);
    490 
    491         // Simulate the async execution.
    492         mFakeExecutor.simulateAsyncExecutionOfLastCommand();
    493 
    494         assertClosed(parcelFileDescriptor);
    495 
    496         // Verify the expected calls were made to other components.
    497         verifyStageInstallCalled();
    498 
    499         // Validation failure is treated like a successful check: repeating it won't improve things.
    500         boolean expectedSuccess = true;
    501         verifyPackageTrackerCalled(token, expectedSuccess);
    502 
    503         // Check the callback was received.
    504         callback.assertResultReceived(Callback.ERROR_INSTALL_VALIDATION_ERROR);
    505     }
    506 
    507     @Test
    508     public void requestUninstall_operationInProgress() throws Exception {
    509         configureCallerHasPermission();
    510 
    511         byte[] tokenBytes = createArbitraryTokenBytes();
    512         ICallback callback = new StubbedCallback();
    513 
    514         // First request should succeed.
    515         assertEquals(RulesManager.SUCCESS,
    516                 mRulesManagerService.requestUninstall(tokenBytes, callback));
    517 
    518         // Something async should be enqueued. Clear it but do not execute it so we can detect the
    519         // second request does nothing.
    520         mFakeExecutor.getAndResetLastCommand();
    521 
    522         // Second request should fail.
    523         assertEquals(RulesManager.ERROR_OPERATION_IN_PROGRESS,
    524                 mRulesManagerService.requestUninstall(tokenBytes, callback));
    525 
    526         // Assert nothing async was enqueued.
    527         mFakeExecutor.assertNothingQueued();
    528         verifyNoInstallerCallsMade();
    529         verifyNoPackageTrackerCallsMade();
    530     }
    531 
    532     @Test
    533     public void requestUninstall_badToken() throws Exception {
    534         configureCallerHasPermission();
    535 
    536         byte[] badTokenBytes = new byte[2];
    537         ICallback callback = new StubbedCallback();
    538 
    539         try {
    540             mRulesManagerService.requestUninstall(badTokenBytes, callback);
    541             fail();
    542         } catch (IllegalArgumentException expected) {
    543         }
    544 
    545         // Assert nothing async was enqueued.
    546         mFakeExecutor.assertNothingQueued();
    547         verifyNoInstallerCallsMade();
    548         verifyNoPackageTrackerCallsMade();
    549     }
    550 
    551     @Test
    552     public void requestUninstall_nullCallback() throws Exception {
    553         configureCallerHasPermission();
    554 
    555         byte[] tokenBytes = createArbitraryTokenBytes();
    556         ICallback callback = null;
    557 
    558         try {
    559             mRulesManagerService.requestUninstall(tokenBytes, callback);
    560             fail();
    561         } catch (NullPointerException expected) {}
    562 
    563         // Assert nothing async was enqueued.
    564         mFakeExecutor.assertNothingQueued();
    565         verifyNoInstallerCallsMade();
    566         verifyNoPackageTrackerCallsMade();
    567     }
    568 
    569     @Test
    570     public void requestUninstall_asyncSuccess() throws Exception {
    571         configureCallerHasPermission();
    572 
    573         CheckToken token = createArbitraryToken();
    574         byte[] tokenBytes = token.toByteArray();
    575 
    576         TestCallback callback = new TestCallback();
    577 
    578         // Request the uninstall.
    579         assertEquals(RulesManager.SUCCESS,
    580                 mRulesManagerService.requestUninstall(tokenBytes, callback));
    581 
    582         // Assert nothing has happened yet.
    583         callback.assertNoResultReceived();
    584         verifyNoInstallerCallsMade();
    585         verifyNoPackageTrackerCallsMade();
    586 
    587         // Set up the installer.
    588         configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_SUCCESS);
    589 
    590         // Simulate the async execution.
    591         mFakeExecutor.simulateAsyncExecutionOfLastCommand();
    592 
    593         // Verify the expected calls were made to other components.
    594         verifyStageUninstallCalled();
    595         verifyPackageTrackerCalled(token, true /* success */);
    596 
    597         // Check the callback was called.
    598         callback.assertResultReceived(Callback.SUCCESS);
    599     }
    600 
    601     @Test
    602     public void requestUninstall_asyncNothingInstalled() throws Exception {
    603         configureCallerHasPermission();
    604 
    605         CheckToken token = createArbitraryToken();
    606         byte[] tokenBytes = token.toByteArray();
    607 
    608         TestCallback callback = new TestCallback();
    609 
    610         // Request the uninstall.
    611         assertEquals(RulesManager.SUCCESS,
    612                 mRulesManagerService.requestUninstall(tokenBytes, callback));
    613 
    614         // Assert nothing has happened yet.
    615         callback.assertNoResultReceived();
    616         verifyNoInstallerCallsMade();
    617         verifyNoPackageTrackerCallsMade();
    618 
    619         // Set up the installer.
    620         configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED);
    621 
    622         // Simulate the async execution.
    623         mFakeExecutor.simulateAsyncExecutionOfLastCommand();
    624 
    625         // Verify the expected calls were made to other components.
    626         verifyStageUninstallCalled();
    627         verifyPackageTrackerCalled(token, true /* success */);
    628 
    629         // Check the callback was called.
    630         callback.assertResultReceived(Callback.SUCCESS);
    631     }
    632 
    633     @Test
    634     public void requestUninstall_nullTokenBytes() throws Exception {
    635         configureCallerHasPermission();
    636 
    637         TestCallback callback = new TestCallback();
    638 
    639         // Request the uninstall.
    640         assertEquals(RulesManager.SUCCESS,
    641                 mRulesManagerService.requestUninstall(null /* tokenBytes */, callback));
    642 
    643         // Assert nothing has happened yet.
    644         verifyNoInstallerCallsMade();
    645         callback.assertNoResultReceived();
    646 
    647         // Set up the installer.
    648         configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_SUCCESS);
    649 
    650         // Simulate the async execution.
    651         mFakeExecutor.simulateAsyncExecutionOfLastCommand();
    652 
    653         // Verify the expected calls were made to other components.
    654         verifyStageUninstallCalled();
    655         verifyPackageTrackerCalled(null /* expectedToken */, true /* success */);
    656 
    657         // Check the callback was received.
    658         callback.assertResultReceived(Callback.SUCCESS);
    659     }
    660 
    661     @Test
    662     public void requestUninstall_asyncUninstallFail() throws Exception {
    663         configureCallerHasPermission();
    664 
    665         CheckToken token = createArbitraryToken();
    666         byte[] tokenBytes = token.toByteArray();
    667 
    668         TestCallback callback = new TestCallback();
    669 
    670         // Request the uninstall.
    671         assertEquals(RulesManager.SUCCESS,
    672                 mRulesManagerService.requestUninstall(tokenBytes, callback));
    673 
    674         // Assert nothing has happened yet.
    675         verifyNoInstallerCallsMade();
    676         callback.assertNoResultReceived();
    677 
    678         // Set up the installer.
    679         configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_FAIL);
    680 
    681         // Simulate the async execution.
    682         mFakeExecutor.simulateAsyncExecutionOfLastCommand();
    683 
    684         // Verify the expected calls were made to other components.
    685         verifyStageUninstallCalled();
    686         verifyPackageTrackerCalled(token, false /* success */);
    687 
    688         // Check the callback was received.
    689         callback.assertResultReceived(Callback.ERROR_UNKNOWN_FAILURE);
    690     }
    691 
    692     @Test
    693     public void requestNothing_operationInProgressOk() throws Exception {
    694         configureCallerHasPermission();
    695 
    696         // Set up a parallel operation.
    697         assertEquals(RulesManager.SUCCESS,
    698                 mRulesManagerService.requestUninstall(null, new StubbedCallback()));
    699         // Something async should be enqueued. Clear it but do not execute it to simulate it still
    700         // being in progress.
    701         mFakeExecutor.getAndResetLastCommand();
    702 
    703         CheckToken token = createArbitraryToken();
    704         byte[] tokenBytes = token.toByteArray();
    705 
    706         // Make the call.
    707         mRulesManagerService.requestNothing(tokenBytes, true /* success */);
    708 
    709         // Assert nothing async was enqueued.
    710         mFakeExecutor.assertNothingQueued();
    711 
    712         // Verify the expected calls were made to other components.
    713         verifyPackageTrackerCalled(token, true /* success */);
    714         verifyNoInstallerCallsMade();
    715     }
    716 
    717     @Test
    718     public void requestNothing_badToken() throws Exception {
    719         configureCallerHasPermission();
    720 
    721         byte[] badTokenBytes = new byte[2];
    722 
    723         try {
    724             mRulesManagerService.requestNothing(badTokenBytes, true /* success */);
    725             fail();
    726         } catch (IllegalArgumentException expected) {
    727         }
    728 
    729         // Assert nothing async was enqueued.
    730         mFakeExecutor.assertNothingQueued();
    731 
    732         // Assert no other calls were made.
    733         verifyNoInstallerCallsMade();
    734         verifyNoPackageTrackerCallsMade();
    735     }
    736 
    737     @Test
    738     public void requestNothing() throws Exception {
    739         configureCallerHasPermission();
    740 
    741         CheckToken token = createArbitraryToken();
    742         byte[] tokenBytes = token.toByteArray();
    743 
    744         // Make the call.
    745         mRulesManagerService.requestNothing(tokenBytes, false /* success */);
    746 
    747         // Assert everything required was done.
    748         verifyNoInstallerCallsMade();
    749         verifyPackageTrackerCalled(token, false /* success */);
    750     }
    751 
    752     @Test
    753     public void requestNothing_nullTokenBytes() throws Exception {
    754         configureCallerHasPermission();
    755 
    756         // Make the call.
    757         mRulesManagerService.requestNothing(null /* tokenBytes */, true /* success */);
    758 
    759         // Assert everything required was done.
    760         verifyNoInstallerCallsMade();
    761         verifyPackageTrackerCalled(null /* token */, true /* success */);
    762     }
    763 
    764     @Test
    765     public void dump_noPermission() throws Exception {
    766         when(mMockPermissionHelper.checkDumpPermission(any(String.class), any(PrintWriter.class)))
    767                 .thenReturn(false);
    768 
    769         doDumpCallAndCapture(mRulesManagerService, null);
    770         verifyZeroInteractions(mMockPackageTracker, mMockTimeZoneDistroInstaller);
    771     }
    772 
    773     @Test
    774     public void dump_emptyArgs() throws Exception {
    775         doSuccessfulDumpCall(mRulesManagerService, new String[0]);
    776 
    777         // Verify the package tracker was consulted.
    778         verify(mMockPackageTracker).dump(any(PrintWriter.class));
    779     }
    780 
    781     @Test
    782     public void dump_nullArgs() throws Exception {
    783         doSuccessfulDumpCall(mRulesManagerService, null);
    784         // Verify the package tracker was consulted.
    785         verify(mMockPackageTracker).dump(any(PrintWriter.class));
    786     }
    787 
    788     @Test
    789     public void dump_unknownArgs() throws Exception {
    790         String dumpedTextUnknownArgs = doSuccessfulDumpCall(
    791                 mRulesManagerService, new String[] { "foo", "bar"});
    792 
    793         // Verify the package tracker was consulted.
    794         verify(mMockPackageTracker).dump(any(PrintWriter.class));
    795 
    796         String dumpedTextZeroArgs = doSuccessfulDumpCall(mRulesManagerService, null);
    797         assertEquals(dumpedTextZeroArgs, dumpedTextUnknownArgs);
    798     }
    799 
    800     @Test
    801     public void dump_formatState() throws Exception {
    802         // Just expect these to not throw exceptions, not return nothing, and not interact with the
    803         // package tracker.
    804         doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("p"));
    805         doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("s"));
    806         doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("c"));
    807         doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("i"));
    808         doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("o"));
    809         doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("t"));
    810         doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("a"));
    811         doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("z" /* Unknown */));
    812         doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("piscotz"));
    813 
    814         verifyZeroInteractions(mMockPackageTracker);
    815     }
    816 
    817     private static String[] dumpFormatArgs(String argsString) {
    818         return new String[] { "-format_state", argsString};
    819     }
    820 
    821     private String doSuccessfulDumpCall(RulesManagerService rulesManagerService, String[] args)
    822             throws Exception {
    823         when(mMockPermissionHelper.checkDumpPermission(any(String.class), any(PrintWriter.class)))
    824                 .thenReturn(true);
    825 
    826         // Set up the mocks to return (arbitrary) information about the current device state.
    827         when(mMockTimeZoneDistroInstaller.getSystemRulesVersion()).thenReturn("2017a");
    828         when(mMockTimeZoneDistroInstaller.getInstalledDistroVersion()).thenReturn(
    829                 new DistroVersion(2, 3, "2017b", 4));
    830         when(mMockTimeZoneDistroInstaller.getStagedDistroOperation()).thenReturn(
    831                 StagedDistroOperation.install(new DistroVersion(5, 6, "2017c", 7)));
    832 
    833         // Do the dump call.
    834         String dumpedOutput = doDumpCallAndCapture(rulesManagerService, args);
    835 
    836         assertFalse(dumpedOutput.isEmpty());
    837 
    838         return dumpedOutput;
    839     }
    840 
    841     private static String doDumpCallAndCapture(
    842             RulesManagerService rulesManagerService, String[] args) throws IOException {
    843         File file = File.createTempFile("dump", null);
    844         try {
    845             try (FileOutputStream fos = new FileOutputStream(file)) {
    846                 FileDescriptor fd = fos.getFD();
    847                 rulesManagerService.dump(fd, args);
    848             }
    849             return IoUtils.readFileAsString(file.getAbsolutePath());
    850         } finally {
    851             file.delete();
    852         }
    853     }
    854 
    855     private void verifyNoPackageTrackerCallsMade() {
    856         verifyNoMoreInteractions(mMockPackageTracker);
    857         reset(mMockPackageTracker);
    858     }
    859 
    860     private void verifyPackageTrackerCalled(
    861             CheckToken expectedCheckToken, boolean expectedSuccess) {
    862         verify(mMockPackageTracker).recordCheckResult(expectedCheckToken, expectedSuccess);
    863         reset(mMockPackageTracker);
    864     }
    865 
    866     private void configureCallerHasPermission() throws Exception {
    867         doNothing()
    868                 .when(mMockPermissionHelper)
    869                 .enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
    870     }
    871 
    872     private void configureCallerDoesNotHavePermission() {
    873         doThrow(new SecurityException("Simulated permission failure"))
    874                 .when(mMockPermissionHelper)
    875                 .enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
    876     }
    877 
    878     private void configureStageInstallExpectation(int resultCode)
    879             throws Exception {
    880         when(mMockTimeZoneDistroInstaller.stageInstallWithErrorCode(any(TimeZoneDistro.class)))
    881                 .thenReturn(resultCode);
    882     }
    883 
    884     private void configureStageUninstallExpectation(int resultCode) throws Exception {
    885         doReturn(resultCode).when(mMockTimeZoneDistroInstaller).stageUninstall();
    886     }
    887 
    888     private void verifyStageInstallCalled() throws Exception {
    889         verify(mMockTimeZoneDistroInstaller).stageInstallWithErrorCode(any(TimeZoneDistro.class));
    890         verifyNoMoreInteractions(mMockTimeZoneDistroInstaller);
    891         reset(mMockTimeZoneDistroInstaller);
    892     }
    893 
    894     private void verifyStageUninstallCalled() throws Exception {
    895         verify(mMockTimeZoneDistroInstaller).stageUninstall();
    896         verifyNoMoreInteractions(mMockTimeZoneDistroInstaller);
    897         reset(mMockTimeZoneDistroInstaller);
    898     }
    899 
    900     private void verifyNoInstallerCallsMade() {
    901         verifyNoMoreInteractions(mMockTimeZoneDistroInstaller);
    902         reset(mMockTimeZoneDistroInstaller);
    903     }
    904 
    905     private static byte[] createArbitraryBytes(int length) {
    906         byte[] bytes = new byte[length];
    907         for (int i = 0; i < length; i++) {
    908             bytes[i] = (byte) i;
    909         }
    910         return bytes;
    911     }
    912 
    913     private byte[] createArbitraryTokenBytes() {
    914         return createArbitraryToken().toByteArray();
    915     }
    916 
    917     private CheckToken createArbitraryToken() {
    918         return new CheckToken(1, new PackageVersions(1, 1));
    919     }
    920 
    921     private void configureDeviceSystemRulesVersion(String systemRulesVersion) throws Exception {
    922         when(mMockTimeZoneDistroInstaller.getSystemRulesVersion()).thenReturn(systemRulesVersion);
    923     }
    924 
    925     private void configureInstalledDistroVersion(@Nullable DistroVersion installedDistroVersion)
    926             throws Exception {
    927         when(mMockTimeZoneDistroInstaller.getInstalledDistroVersion())
    928                 .thenReturn(installedDistroVersion);
    929     }
    930 
    931     private void configureStagedInstall(DistroVersion stagedDistroVersion) throws Exception {
    932         when(mMockTimeZoneDistroInstaller.getStagedDistroOperation())
    933                 .thenReturn(StagedDistroOperation.install(stagedDistroVersion));
    934     }
    935 
    936     private void configureStagedUninstall() throws Exception {
    937         when(mMockTimeZoneDistroInstaller.getStagedDistroOperation())
    938                 .thenReturn(StagedDistroOperation.uninstall());
    939     }
    940 
    941     private void configureNoStagedOperation() throws Exception {
    942         when(mMockTimeZoneDistroInstaller.getStagedDistroOperation()).thenReturn(null);
    943     }
    944 
    945     private void configureDeviceCannotReadStagedDistroOperation() throws Exception {
    946         when(mMockTimeZoneDistroInstaller.getStagedDistroOperation())
    947                 .thenThrow(new IOException("Simulated failure"));
    948     }
    949 
    950     private void configureDeviceCannotReadSystemRulesVersion() throws Exception {
    951         when(mMockTimeZoneDistroInstaller.getSystemRulesVersion())
    952                 .thenThrow(new IOException("Simulated failure"));
    953     }
    954 
    955     private void configureDeviceCannotReadInstalledDistroVersion() throws Exception {
    956         when(mMockTimeZoneDistroInstaller.getInstalledDistroVersion())
    957                 .thenThrow(new IOException("Simulated failure"));
    958     }
    959 
    960     private static void assertClosed(ParcelFileDescriptor parcelFileDescriptor) {
    961         assertFalse(parcelFileDescriptor.getFileDescriptor().valid());
    962     }
    963 
    964     private static class FakeExecutor implements Executor {
    965 
    966         private Runnable mLastCommand;
    967 
    968         @Override
    969         public void execute(Runnable command) {
    970             assertNull(mLastCommand);
    971             assertNotNull(command);
    972             mLastCommand = command;
    973         }
    974 
    975         public Runnable getAndResetLastCommand() {
    976             assertNotNull(mLastCommand);
    977             Runnable toReturn = mLastCommand;
    978             mLastCommand = null;
    979             return toReturn;
    980         }
    981 
    982         public void simulateAsyncExecutionOfLastCommand() {
    983             Runnable toRun = getAndResetLastCommand();
    984             toRun.run();
    985         }
    986 
    987         public void assertNothingQueued() {
    988             assertNull(mLastCommand);
    989         }
    990     }
    991 
    992     private static class TestCallback extends ICallback.Stub {
    993 
    994         private boolean mOnFinishedCalled;
    995         private int mLastError;
    996 
    997         @Override
    998         public void onFinished(int error) {
    999             assertFalse(mOnFinishedCalled);
   1000             mOnFinishedCalled = true;
   1001             mLastError = error;
   1002         }
   1003 
   1004         public void assertResultReceived(int expectedResult) {
   1005             assertTrue(mOnFinishedCalled);
   1006             assertEquals(expectedResult, mLastError);
   1007         }
   1008 
   1009         public void assertNoResultReceived() {
   1010             assertFalse(mOnFinishedCalled);
   1011         }
   1012     }
   1013 
   1014     private static class StubbedCallback extends ICallback.Stub {
   1015         @Override
   1016         public void onFinished(int error) {
   1017             fail("Unexpected call");
   1018         }
   1019     }
   1020 
   1021     private static ParcelFileDescriptor createParcelFileDescriptor(byte[] bytes)
   1022             throws IOException {
   1023         File file = File.createTempFile("pfd", null);
   1024         try (FileOutputStream fos = new FileOutputStream(file)) {
   1025             fos.write(bytes);
   1026         }
   1027         ParcelFileDescriptor pfd =
   1028                 ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
   1029         // This should now be safe to delete. The ParcelFileDescriptor has an open fd.
   1030         file.delete();
   1031         return pfd;
   1032     }
   1033 }
   1034