1 /*
2  * Copyright (C) 2021 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.managedprovisioning.common;
18 
19 import static com.android.managedprovisioning.common.RetryLaunchViewModel.LaunchActivityFailureEvent.REASON_EXCEEDED_MAXIMUM_NUMBER_ACTIVITY_LAUNCH_RETRIES;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import android.app.Application;
24 import android.app.admin.DevicePolicyManager;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.os.Handler;
28 import android.os.Looper;
29 
30 import androidx.test.core.app.ApplicationProvider;
31 import androidx.test.filters.SmallTest;
32 import androidx.test.platform.app.InstrumentationRegistry;
33 
34 import com.android.bedstead.nene.utils.Poll;
35 import com.android.managedprovisioning.common.RetryLaunchViewModel.LaunchActivityEvent;
36 import com.android.managedprovisioning.common.RetryLaunchViewModel.LaunchActivityFailureEvent;
37 import com.android.managedprovisioning.common.RetryLaunchViewModel.LaunchActivityWaitingForRetryEvent;
38 
39 import org.junit.Before;
40 import org.junit.Test;
41 import org.junit.runner.RunWith;
42 import org.junit.runners.JUnit4;
43 
44 import java.time.Duration;
45 import java.util.Arrays;
46 import java.util.Queue;
47 import java.util.concurrent.ConcurrentLinkedQueue;
48 import java.util.stream.Collectors;
49 
50 
51 @SmallTest
52 @RunWith(JUnit4.class)
53 public class RetryLaunchViewModelTest {
54     private static final int LAUNCH_ROLE_HOLDER_UPDATER_PERIOD_MILLIS = 100;
55     private static final int NO_EVENT_TIMEOUT_MILLIS = 200;
56     private static final int LAUNCH_ROLE_HOLDER_MAX_RETRIES = 1;
57     private static final int ROLE_HOLDER_UPDATE_MAX_RETRIES = 1;
58     private static final LaunchActivityEvent
59             LAUNCH_ACTIVITY_EVENT = createLaunchRoleHolderUpdaterEvent();
60     private static final LaunchActivityFailureEvent
61             EXCEED_MAX_NUMBER_LAUNCH_RETRIES_EVENT = createExceedMaxNumberLaunchRetriesEvent();
62     private static final LaunchActivityWaitingForRetryEvent WAITING_FOR_RETRY_EVENT =
63             createWaitingForRetryEvent();
64     private static final String TEST_DEVICE_MANAGEMENT_ROLE_HOLDER_UPDATER_PACKAGE_NAME =
65             "com.devicemanagementroleholderupdater.test.package";
66 
67     private final Context mApplicationContext = ApplicationProvider.getApplicationContext();
68     private Handler mHandler;
69     private TestConfig mTestConfig;
70     private boolean mCanLaunchRoleHolderUpdater = true;
71     private RetryLaunchViewModel mViewModel;
72     private Queue<ViewModelEvent> mEvents;
73     private Utils mUtils = new Utils();
74 
75     @Before
setUp()76     public void setUp() {
77         mTestConfig = new TestConfig(
78                 LAUNCH_ROLE_HOLDER_UPDATER_PERIOD_MILLIS,
79                 LAUNCH_ROLE_HOLDER_MAX_RETRIES,
80                 ROLE_HOLDER_UPDATE_MAX_RETRIES);
81         mCanLaunchRoleHolderUpdater = true;
82         InstrumentationRegistry.getInstrumentation().runOnMainSync(
83                 () -> mHandler = new Handler(Looper.myLooper()));
84         mViewModel = createViewModel();
85         mEvents = subscribeToViewModelEvents();
86     }
87 
88     @Test
tryStartRoleHolderUpdater_launchUpdater_works()89     public void tryStartRoleHolderUpdater_launchUpdater_works() {
90         mViewModel.tryStartActivity();
91         blockUntilNextUiThreadCycle();
92 
93         assertThat(mEvents).containsExactly(LAUNCH_ACTIVITY_EVENT);
94     }
95 
96     @Test
tryStartRoleHolderUpdater_rescheduleLaunchUpdater_works()97     public void tryStartRoleHolderUpdater_rescheduleLaunchUpdater_works() {
98         mTestConfig.launchRoleHolderMaxRetries = 2;
99         mCanLaunchRoleHolderUpdater = false;
100 
101         mViewModel.tryStartActivity();
102         mCanLaunchRoleHolderUpdater = true;
103 
104         pollForEvents(
105                 mEvents,
106                 WAITING_FOR_RETRY_EVENT,
107                 LAUNCH_ACTIVITY_EVENT);
108     }
109 
110     @Test
tryStartRoleHolderUpdater_rescheduleLaunchUpdater_exceedsMaxRetryLimit_fails()111     public void tryStartRoleHolderUpdater_rescheduleLaunchUpdater_exceedsMaxRetryLimit_fails() {
112         mTestConfig.roleHolderUpdateMaxRetries = 1;
113         mCanLaunchRoleHolderUpdater = false;
114 
115         mViewModel.tryStartActivity();
116 
117         pollForEvents(
118                 mEvents,
119                 WAITING_FOR_RETRY_EVENT,
120                 EXCEED_MAX_NUMBER_LAUNCH_RETRIES_EVENT);
121     }
122 
123     @Test
stopLaunchRetries_works()124     public void stopLaunchRetries_works() {
125         mTestConfig.roleHolderUpdateMaxRetries = 1;
126         mCanLaunchRoleHolderUpdater = false;
127 
128         mViewModel.tryStartActivity();
129         mViewModel.stopLaunchRetries();
130 
131         pollForEvents(mEvents, WAITING_FOR_RETRY_EVENT);
132     }
133 
134     @Test
markWaitingForActivityResult_works()135     public void markWaitingForActivityResult_works() {
136         mViewModel.markWaitingForActivityResult();
137 
138         assertThat(mViewModel.isWaitingForActivityResult()).isTrue();
139     }
140 
141     @Test
isWaitingForActivityResult_defaultsToFalse()142     public void isWaitingForActivityResult_defaultsToFalse() {
143         assertThat(mViewModel.isWaitingForActivityResult()).isFalse();
144     }
145 
pollForEvents( Queue<ViewModelEvent> actualEvents, ViewModelEvent... expectedEvents)146     private void pollForEvents(
147             Queue<ViewModelEvent> actualEvents,
148             ViewModelEvent... expectedEvents) {
149         for (ViewModelEvent nextExpectedEvent : expectedEvents) {
150             Poll.forValue("CapturedViewModelEvents", () -> actualEvents)
151                     .toMeet(actualEventsQueue -> !actualEventsQueue.isEmpty())
152                     .errorOnFail("Expected CapturedViewModelEvents to contain exactly "
153                             + Arrays.stream(expectedEvents)
154                             .map(Object::toString).collect(Collectors.joining()))
155                     .await();
156             assertThat(actualEvents.remove()).isEqualTo(nextExpectedEvent);
157         }
158         pollForNoEvent(actualEvents);
159     }
160 
pollForNoEvent(Queue<ViewModelEvent> capturedViewModelEvents)161     private void pollForNoEvent(Queue<ViewModelEvent> capturedViewModelEvents) {
162         // TODO(b/208237942): A pattern for testing that something does not happen
163         assertThat(Poll.forValue("CapturedViewModelEvents", () -> capturedViewModelEvents)
164                 .toMeet(viewModelEvents -> !viewModelEvents.isEmpty())
165                 .timeout(Duration.ofMillis(NO_EVENT_TIMEOUT_MILLIS))
166                 .await())
167                 .isEmpty();
168     }
169 
createExceedMaxNumberLaunchRetriesEvent()170     private static LaunchActivityFailureEvent createExceedMaxNumberLaunchRetriesEvent() {
171         return new LaunchActivityFailureEvent(
172                 REASON_EXCEEDED_MAXIMUM_NUMBER_ACTIVITY_LAUNCH_RETRIES);
173     }
174 
createWaitingForRetryEvent()175     private static LaunchActivityWaitingForRetryEvent createWaitingForRetryEvent() {
176         return new LaunchActivityWaitingForRetryEvent();
177     }
178 
createLaunchRoleHolderUpdaterEvent()179     private static LaunchActivityEvent createLaunchRoleHolderUpdaterEvent() {
180         return new LaunchActivityEvent(createUpdateDeviceManagementRoleHolderIntent());
181     }
182 
subscribeToViewModelEvents()183     private Queue<ViewModelEvent> subscribeToViewModelEvents() {
184         Queue<ViewModelEvent> capturedViewModelEvents = new ConcurrentLinkedQueue<>();
185         InstrumentationRegistry.getInstrumentation().runOnMainSync(
186                 () -> mViewModel.observeViewModelEvents()
187                         .observeForever(capturedViewModelEvents::add));
188         return capturedViewModelEvents;
189     }
190 
blockUntilNextUiThreadCycle()191     private void blockUntilNextUiThreadCycle() {
192         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {});
193     }
194 
createViewModel()195     private RetryLaunchViewModel createViewModel() {
196         return new RetryLaunchViewModel(
197                 (Application) mApplicationContext,
198                 createUpdateDeviceManagementRoleHolderIntent(),
199                 mHandler,
200                 (context, intent) -> mCanLaunchRoleHolderUpdater,
201                 mTestConfig);
202     }
203 
createUpdateDeviceManagementRoleHolderIntent()204     private static Intent createUpdateDeviceManagementRoleHolderIntent() {
205         return new Intent(DevicePolicyManager.ACTION_UPDATE_DEVICE_POLICY_MANAGEMENT_ROLE_HOLDER)
206                 .setPackage(TEST_DEVICE_MANAGEMENT_ROLE_HOLDER_UPDATER_PACKAGE_NAME);
207     }
208 
209     private static final class TestConfig implements RetryLaunchViewModel.Config {
210         public int launchRoleHolderUpdaterPeriodMillis;
211         public int launchRoleHolderMaxRetries;
212         public int roleHolderUpdateMaxRetries;
213 
TestConfig( int launchRoleHolderUpdaterPeriodMillis, int launchRoleHolderMaxRetries, int roleHolderUpdateMaxRetries)214         TestConfig(
215                 int launchRoleHolderUpdaterPeriodMillis,
216                 int launchRoleHolderMaxRetries,
217                 int roleHolderUpdateMaxRetries) {
218             this.launchRoleHolderUpdaterPeriodMillis = launchRoleHolderUpdaterPeriodMillis;
219             this.launchRoleHolderMaxRetries = launchRoleHolderMaxRetries;
220             this.roleHolderUpdateMaxRetries = roleHolderUpdateMaxRetries;
221         }
222 
223         @Override
getLaunchActivityRetryMillis()224         public long getLaunchActivityRetryMillis() {
225             return launchRoleHolderUpdaterPeriodMillis;
226         }
227 
228         @Override
getLaunchActivityMaxRetries()229         public int getLaunchActivityMaxRetries() {
230             return launchRoleHolderMaxRetries;
231         }
232 
233     }
234 }
235