• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 android.app.admin.DevicePolicyManager.ACTION_ADMIN_POLICY_COMPLIANCE;
20 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
21 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE;
22 
23 import static com.android.managedprovisioning.TestUtils.createTestAdminExtras;
24 
25 import static org.mockito.ArgumentMatchers.anyString;
26 import static org.mockito.Matchers.any;
27 import static org.mockito.Matchers.anyInt;
28 import static org.mockito.Matchers.eq;
29 import static org.mockito.Mockito.never;
30 import static org.mockito.Mockito.verify;
31 import static org.mockito.Mockito.when;
32 
33 import android.app.Activity;
34 import android.content.ComponentName;
35 import android.content.Context;
36 import android.content.Intent;
37 import android.content.ServiceConnection;
38 import android.content.SharedPreferences;
39 import android.os.Bundle;
40 import android.os.PersistableBundle;
41 import android.os.UserHandle;
42 import android.test.AndroidTestCase;
43 import android.test.suitebuilder.annotation.SmallTest;
44 
45 import androidx.test.InstrumentationRegistry;
46 
47 import com.android.managedprovisioning.TestUtils;
48 import com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker;
49 import com.android.managedprovisioning.model.ProvisioningParams;
50 import com.android.managedprovisioning.provisioning.Constants;
51 
52 import org.mockito.ArgumentCaptor;
53 import org.mockito.Mock;
54 import org.mockito.MockitoAnnotations;
55 
56 public class StartDpcInsideSuwServiceConnectionTest extends AndroidTestCase {
57     private static final String TEST_MDM_PACKAGE_NAME = "mdm.package.name";
58     private static final String TEST_MDM_ADMIN_RECEIVER = TEST_MDM_PACKAGE_NAME + ".AdminReceiver";
59     private static final ComponentName TEST_MDM_ADMIN = new ComponentName(TEST_MDM_PACKAGE_NAME,
60             TEST_MDM_ADMIN_RECEIVER);
61     private static final PersistableBundle TEST_MDM_EXTRA_BUNDLE = createTestAdminExtras();
62     private static final int TEST_REQUEST_CODE = 3;
63     private static final String TEST_SUW_PACKAGE_NAME = "suw.package.name";
64     private static final String TEST_SUW_SERVICE_CLASS = TEST_SUW_PACKAGE_NAME + ".TestService";
65     private static final ComponentName TEST_SUW_COMPONENT_NAME = new ComponentName(
66             TEST_SUW_PACKAGE_NAME, TEST_SUW_SERVICE_CLASS);
67 
68     @Mock private Activity mActivity;
69     @Mock private Activity mRestoredActivity;
70     @Mock private Utils mUtils;
71     @Mock private ProvisioningAnalyticsTracker mProvisioningAnalyticsTracker;
72     @Mock private TransitionHelper mTransitionHelper;
73     @Mock private SharedPreferences mSharedPreferences;
74 
75     private StartDpcInsideSuwServiceConnection mStartDpcInsideSuwServiceConnection;
76     private Runnable mDpcIntentSender;
77     private ProvisioningParams mParams;
78     private final Context mTargetContext = InstrumentationRegistry.getTargetContext();
79 
80     @Override
setUp()81     public void setUp() throws Exception {
82         System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
83         MockitoAnnotations.initMocks(this);
84         when(mUtils.canResolveIntentAsUser(any(Context.class), any(Intent.class), anyInt()))
85                 .thenReturn(true);
86 
87         final PolicyComplianceUtils policyComplianceUtils = new PolicyComplianceUtils();
88         mParams = new ProvisioningParams.Builder()
89                 .setDeviceAdminComponentName(TEST_MDM_ADMIN)
90                 .setProvisioningAction(ACTION_PROVISION_MANAGED_DEVICE)
91                 .setAdminExtrasBundle(TEST_MDM_EXTRA_BUNDLE)
92                 .build();
93 
94         when(mActivity.getSharedPreferences(anyString(), anyInt())).thenReturn(mSharedPreferences);
95         when(mActivity.getResources()).thenReturn(mTargetContext.getResources());
96         when(mRestoredActivity.getSharedPreferences(anyString(), anyInt()))
97                 .thenReturn(mSharedPreferences);
98         when(mRestoredActivity.getResources()).thenReturn(mTargetContext.getResources());
99 
100         mStartDpcInsideSuwServiceConnection = new StartDpcInsideSuwServiceConnection();
101         Constants.ENABLE_CUSTOM_TRANSITIONS = true;
102         mDpcIntentSender = () ->
103                 policyComplianceUtils.startPolicyComplianceActivityForResultIfResolved(
104                         mActivity, mParams, TEST_REQUEST_CODE, mUtils,
105                         mProvisioningAnalyticsTracker, mTransitionHelper);
106     }
107 
108     @SmallTest
testBindingSucceeds_serviceConnects()109     public void testBindingSucceeds_serviceConnects() {
110         // GIVEN that we can bind to the SUW NetworkInterceptService
111         when(mActivity.bindService(any(Intent.class), any(ServiceConnection.class), anyInt()))
112                 .thenReturn(true);
113 
114         // WHEN calling triggerDpcStart()
115         mStartDpcInsideSuwServiceConnection.triggerDpcStart(mActivity, mDpcIntentSender);
116 
117         // THEN we bind to the SUW NetworkInterceptService
118         verifyBindServiceCalled(mActivity, mStartDpcInsideSuwServiceConnection);
119 
120         // WHEN connection to the NetworkInterceptService is established
121         mStartDpcInsideSuwServiceConnection.onServiceConnected(TEST_SUW_COMPONENT_NAME, null);
122 
123         // THEN an intent is sent to the DPC
124         verifyDpcLaunched(mActivity);
125 
126         // WHEN calling dpcFinished and unbind
127         mStartDpcInsideSuwServiceConnection.dpcFinished();
128         mStartDpcInsideSuwServiceConnection.unbind(mActivity);
129 
130         // THEN we unbind from the NetworkInterceptService
131         verify(mActivity).unbindService(eq(mStartDpcInsideSuwServiceConnection));
132     }
133 
134     @SmallTest
testBindingSucceeds_instanceStateSavedAndRestoredBeforeServiceConnected()135     public void testBindingSucceeds_instanceStateSavedAndRestoredBeforeServiceConnected() {
136         // GIVEN that we can bind to the SUW NetworkInterceptService
137         when(mActivity.bindService(any(Intent.class), any(ServiceConnection.class), anyInt()))
138                 .thenReturn(true);
139 
140         // WHEN calling triggerDpcStart()
141         mStartDpcInsideSuwServiceConnection.triggerDpcStart(mActivity, mDpcIntentSender);
142 
143         // THEN we bind to the SUW NetworkInterceptService
144         verifyBindServiceCalled(mActivity, mStartDpcInsideSuwServiceConnection);
145 
146         // WHEN saving state and calling unbind before a service connection was established
147         final Bundle savedInstanceState = new Bundle();
148         mStartDpcInsideSuwServiceConnection.saveInstanceState(savedInstanceState);
149         mStartDpcInsideSuwServiceConnection.unbind(mActivity);
150 
151         // THEN we unbind from the NetworkInterceptService
152         verify(mActivity).unbindService(eq(mStartDpcInsideSuwServiceConnection));
153 
154         // GIVEN that a restored activity can also bind to the SUW NetworkInterceptService
155         when(mRestoredActivity.bindService(
156                 any(Intent.class), any(ServiceConnection.class), anyInt())).thenReturn(true);
157 
158         // WHEN we restore the service connection from the saved state
159         final StartDpcInsideSuwServiceConnection restoredServiceConnection =
160                 getRestoredServiceConnection(savedInstanceState);
161 
162         // THEN we bind to the SUW NetworkInterceptService again
163         verifyBindServiceCalled(mRestoredActivity, restoredServiceConnection);
164 
165         // WHEN connection to the NetworkInterceptService is now established
166         restoredServiceConnection.onServiceConnected(TEST_SUW_COMPONENT_NAME, null);
167 
168         // THEN only one intent is sent to the DPC
169         verifyDpcLaunched(mRestoredActivity);
170 
171         // WHEN calling dpcFinished and unbind
172         restoredServiceConnection.dpcFinished();
173         restoredServiceConnection.unbind(mRestoredActivity);
174 
175         // THEN we unbind from the NetworkInterceptService
176         verify(mRestoredActivity).unbindService(eq(restoredServiceConnection));
177     }
178 
179     @SmallTest
testBindingSucceeds_instanceStateSavedAndRestoredAfterServiceConnected()180     public void testBindingSucceeds_instanceStateSavedAndRestoredAfterServiceConnected() {
181         // GIVEN that we can bind to the SUW NetworkInterceptService
182         when(mActivity.bindService(any(Intent.class), any(ServiceConnection.class), anyInt()))
183                 .thenReturn(true);
184 
185         // WHEN calling triggerDpcStart()
186         mStartDpcInsideSuwServiceConnection.triggerDpcStart(mActivity, mDpcIntentSender);
187 
188         // THEN we bind to the SUW NetworkInterceptService
189         verifyBindServiceCalled(mActivity, mStartDpcInsideSuwServiceConnection);
190 
191         // WHEN connection to the NetworkInterceptService is now established
192         mStartDpcInsideSuwServiceConnection.onServiceConnected(TEST_SUW_COMPONENT_NAME, null);
193 
194         // THEN only one intent is sent to the DPC
195         verifyDpcLaunched(mActivity);
196 
197         // WHEN saving state and calling unbind after a service connection was established
198         final Bundle savedInstanceState = new Bundle();
199         mStartDpcInsideSuwServiceConnection.saveInstanceState(savedInstanceState);
200         mStartDpcInsideSuwServiceConnection.unbind(mActivity);
201 
202         // THEN we unbind from the NetworkInterceptService
203         verify(mActivity).unbindService(eq(mStartDpcInsideSuwServiceConnection));
204 
205         // GIVEN that a restored activity can also bind to the SUW NetworkInterceptService
206         when(mRestoredActivity.bindService(
207                 any(Intent.class), any(ServiceConnection.class), anyInt())).thenReturn(true);
208 
209         // WHEN we restore the service connection from the saved state
210         final StartDpcInsideSuwServiceConnection restoredServiceConnection =
211                 getRestoredServiceConnection(savedInstanceState);
212 
213         // THEN we bind to the SUW NetworkInterceptService again
214         verifyBindServiceCalled(mRestoredActivity, restoredServiceConnection);
215 
216         // WHEN connection to the NetworkInterceptService is now established
217         restoredServiceConnection.onServiceConnected(TEST_SUW_COMPONENT_NAME, null);
218 
219         // THEN no new intent is sent to the DPC
220         verify(mRestoredActivity, never()).startActivityForResultAsUser(any(Intent.class), anyInt(),
221                 any(UserHandle.class));
222 
223         // WHEN calling dpcFinished and unbind
224         restoredServiceConnection.dpcFinished();
225         restoredServiceConnection.unbind(mRestoredActivity);
226 
227         // THEN we unbind from the NetworkInterceptService
228         verify(mRestoredActivity).unbindService(eq(restoredServiceConnection));
229     }
230 
231     @SmallTest
testBindingSucceeds_instanceStateSavedAndRestoredAfterDpcFinished()232     public void testBindingSucceeds_instanceStateSavedAndRestoredAfterDpcFinished() {
233         // GIVEN that we can bind to the SUW NetworkInterceptService
234         when(mActivity.bindService(any(Intent.class), any(ServiceConnection.class), anyInt()))
235                 .thenReturn(true);
236 
237         // WHEN calling triggerDpcStart()
238         mStartDpcInsideSuwServiceConnection.triggerDpcStart(mActivity, mDpcIntentSender);
239 
240         // THEN we bind to the SUW NetworkInterceptService
241         verifyBindServiceCalled(mActivity, mStartDpcInsideSuwServiceConnection);
242 
243         // WHEN connection to the NetworkInterceptService is now established
244         mStartDpcInsideSuwServiceConnection.onServiceConnected(TEST_SUW_COMPONENT_NAME, null);
245 
246         // THEN only one intent is sent to the DPC
247         verifyDpcLaunched(mActivity);
248 
249         // WHEN calling dpcFinished and unbind
250         mStartDpcInsideSuwServiceConnection.dpcFinished();
251         mStartDpcInsideSuwServiceConnection.unbind(mActivity);
252 
253         // THEN we unbind from the NetworkInterceptService
254         verify(mActivity).unbindService(eq(mStartDpcInsideSuwServiceConnection));
255 
256         // WHEN calling saveInstanceState() after we've unbound from the service
257         final Bundle savedInstanceState = new Bundle();
258         mStartDpcInsideSuwServiceConnection.saveInstanceState(savedInstanceState);
259 
260         // GIVEN that a restored activity can also bind to the SUW NetworkInterceptService
261         when(mRestoredActivity.bindService(
262                 any(Intent.class), any(ServiceConnection.class), anyInt())).thenReturn(true);
263 
264         // WHEN we restore the service connection from the saved state
265         final StartDpcInsideSuwServiceConnection restoredServiceConnection =
266                 getRestoredServiceConnection(savedInstanceState);
267 
268         // THEN we do not bind to the SUW NetworkInterceptService again
269         verify(mRestoredActivity, never()).bindService(any(Intent.class),
270                 eq(restoredServiceConnection), anyInt());
271 
272         // THEN no new intent is sent to the DPC
273         verify(mRestoredActivity, never()).startActivityForResultAsUser(any(Intent.class), anyInt(),
274                 any(UserHandle.class));
275 
276         // WHEN calling dpcFinished and unbind
277         restoredServiceConnection.dpcFinished();
278         restoredServiceConnection.unbind(mRestoredActivity);
279 
280         // THEN we do not unbind from the NetworkInterceptService
281         verify(mRestoredActivity, never()).unbindService(eq(restoredServiceConnection));
282     }
283 
284     @SmallTest
testBindingSucceeds_serviceConnectsAndDisconnects()285     public void testBindingSucceeds_serviceConnectsAndDisconnects() {
286         // GIVEN that we can bind to the SUW NetworkInterceptService
287         when(mActivity.bindService(any(Intent.class), any(ServiceConnection.class), anyInt()))
288                 .thenReturn(true);
289 
290         // WHEN calling triggerDpcStart()
291         mStartDpcInsideSuwServiceConnection.triggerDpcStart(mActivity, mDpcIntentSender);
292 
293         // THEN we bind to the SUW NetworkInterceptService
294         verifyBindServiceCalled(mActivity, mStartDpcInsideSuwServiceConnection);
295 
296         // WHEN connection to the NetworkInterceptService is established and then lost
297         mStartDpcInsideSuwServiceConnection.onServiceConnected(TEST_SUW_COMPONENT_NAME, null);
298         mStartDpcInsideSuwServiceConnection.onServiceDisconnected(TEST_SUW_COMPONENT_NAME);
299 
300         // THEN only one intent is sent to the DPC
301         verifyDpcLaunched(mActivity);
302 
303         // WHEN calling dpcFinished and unbind
304         mStartDpcInsideSuwServiceConnection.dpcFinished();
305         mStartDpcInsideSuwServiceConnection.unbind(mActivity);
306 
307         // THEN we unbind from the NetworkInterceptService
308         verify(mActivity).unbindService(eq(mStartDpcInsideSuwServiceConnection));
309     }
310 
311     @SmallTest
testBindingSucceeds_serviceConnectsAndDisconnects_instanceStateSavedAndRestored()312     public void testBindingSucceeds_serviceConnectsAndDisconnects_instanceStateSavedAndRestored() {
313         // GIVEN that we can bind to the SUW NetworkInterceptService
314         when(mActivity.bindService(any(Intent.class), any(ServiceConnection.class), anyInt()))
315                 .thenReturn(true);
316 
317         // WHEN calling triggerDpcStart()
318         mStartDpcInsideSuwServiceConnection.triggerDpcStart(mActivity, mDpcIntentSender);
319 
320         // THEN we bind to the SUW NetworkInterceptService
321         verifyBindServiceCalled(mActivity, mStartDpcInsideSuwServiceConnection);
322 
323         // WHEN connection to the NetworkInterceptService is established and then lost
324         mStartDpcInsideSuwServiceConnection.onServiceConnected(TEST_SUW_COMPONENT_NAME, null);
325         mStartDpcInsideSuwServiceConnection.onServiceDisconnected(TEST_SUW_COMPONENT_NAME);
326 
327         // THEN only one intent is sent to the DPC
328         verifyDpcLaunched(mActivity);
329 
330         // WHEN saving state and calling unbind after a service connection was established
331         final Bundle savedInstanceState = new Bundle();
332         mStartDpcInsideSuwServiceConnection.saveInstanceState(savedInstanceState);
333         mStartDpcInsideSuwServiceConnection.unbind(mActivity);
334 
335         // THEN we unbind from the NetworkInterceptService
336         verify(mActivity).unbindService(eq(mStartDpcInsideSuwServiceConnection));
337 
338         // GIVEN that a restored activity can also bind to the SUW NetworkInterceptService
339         when(mRestoredActivity.bindService(
340                 any(Intent.class), any(ServiceConnection.class), anyInt())).thenReturn(true);
341 
342         // WHEN we restore the service connection from the saved state
343         final StartDpcInsideSuwServiceConnection restoredServiceConnection =
344                 getRestoredServiceConnection(savedInstanceState);
345 
346         // THEN we bind to the SUW NetworkInterceptService again
347         verifyBindServiceCalled(mRestoredActivity, restoredServiceConnection);
348 
349         // WHEN connection to the NetworkInterceptService is now established
350         restoredServiceConnection.onServiceConnected(TEST_SUW_COMPONENT_NAME, null);
351 
352         // THEN no new intent is sent to the DPC
353         verify(mRestoredActivity, never()).startActivityForResultAsUser(any(Intent.class), anyInt(),
354                 any(UserHandle.class));
355 
356         // WHEN calling dpcFinished and unbind
357         restoredServiceConnection.dpcFinished();
358         restoredServiceConnection.unbind(mRestoredActivity);
359 
360         // THEN we unbind from the NetworkInterceptService
361         verify(mRestoredActivity).unbindService(eq(restoredServiceConnection));
362     }
363 
364     @SmallTest
testBindingSucceeds_serviceConnectsTwice()365     public void testBindingSucceeds_serviceConnectsTwice() {
366         // GIVEN that we can bind to the SUW NetworkInterceptService
367         when(mActivity.bindService(any(Intent.class), any(ServiceConnection.class), anyInt()))
368                 .thenReturn(true);
369 
370         // WHEN calling triggerDpcStart()
371         mStartDpcInsideSuwServiceConnection.triggerDpcStart(mActivity, mDpcIntentSender);
372 
373         // THEN we bind to the SUW NetworkInterceptService
374         verifyBindServiceCalled(mActivity, mStartDpcInsideSuwServiceConnection);
375 
376         // WHEN connection to the NetworkInterceptService is established, lost, and then
377         // re-established
378         mStartDpcInsideSuwServiceConnection.onServiceConnected(TEST_SUW_COMPONENT_NAME, null);
379         mStartDpcInsideSuwServiceConnection.onServiceDisconnected(TEST_SUW_COMPONENT_NAME);
380         mStartDpcInsideSuwServiceConnection.onServiceConnected(TEST_SUW_COMPONENT_NAME, null);
381 
382         // THEN only one intent is sent to the DPC
383         verifyDpcLaunched(mActivity);
384 
385         // WHEN calling dpcFinished and unbind
386         mStartDpcInsideSuwServiceConnection.dpcFinished();
387         mStartDpcInsideSuwServiceConnection.unbind(mActivity);
388 
389         // THEN we unbind from the NetworkInterceptService
390         verify(mActivity).unbindService(eq(mStartDpcInsideSuwServiceConnection));
391     }
392 
393     @SmallTest
testBindingSucceeds_serviceDies()394     public void testBindingSucceeds_serviceDies() {
395         // GIVEN that we can bind to the SUW NetworkInterceptService
396         when(mActivity.bindService(any(Intent.class), any(ServiceConnection.class), anyInt()))
397                 .thenReturn(true);
398 
399         // WHEN calling triggerDpcStart()
400         mStartDpcInsideSuwServiceConnection.triggerDpcStart(mActivity, mDpcIntentSender);
401 
402         // THEN we bind to the SUW NetworkInterceptService
403         verifyBindServiceCalled(mActivity, mStartDpcInsideSuwServiceConnection);
404 
405         // WHEN connection to the NetworkInterceptService is established, but then dies
406         mStartDpcInsideSuwServiceConnection.onServiceConnected(TEST_SUW_COMPONENT_NAME, null);
407         mStartDpcInsideSuwServiceConnection.onBindingDied(TEST_SUW_COMPONENT_NAME);
408 
409         // THEN only one intent is sent to the DPC
410         verifyDpcLaunched(mActivity);
411 
412         // WHEN calling dpcFinished and unbind
413         mStartDpcInsideSuwServiceConnection.dpcFinished();
414         mStartDpcInsideSuwServiceConnection.unbind(mActivity);
415 
416         // THEN we unbind from the NetworkInterceptService
417         verify(mActivity).unbindService(eq(mStartDpcInsideSuwServiceConnection));
418     }
419 
420     @SmallTest
testBindingSucceeds_nullBinding()421     public void testBindingSucceeds_nullBinding() {
422         // GIVEN that we can bind to the SUW NetworkInterceptService
423         when(mActivity.bindService(any(Intent.class), any(ServiceConnection.class), anyInt()))
424                 .thenReturn(true);
425 
426         // WHEN calling triggerDpcStart()
427         mStartDpcInsideSuwServiceConnection.triggerDpcStart(mActivity, mDpcIntentSender);
428 
429         // THEN we bind to the SUW NetworkInterceptService
430         verifyBindServiceCalled(mActivity, mStartDpcInsideSuwServiceConnection);
431 
432         // WHEN the NetworkInterceptService returns a null binding
433         mStartDpcInsideSuwServiceConnection.onNullBinding(TEST_SUW_COMPONENT_NAME);
434 
435         // THEN an intent is sent to the DPC
436         verifyDpcLaunched(mActivity);
437 
438         // WHEN calling dpcFinished and unbind
439         mStartDpcInsideSuwServiceConnection.dpcFinished();
440         mStartDpcInsideSuwServiceConnection.unbind(mActivity);
441 
442         // THEN we unbind from the NetworkInterceptService
443         verify(mActivity).unbindService(eq(mStartDpcInsideSuwServiceConnection));
444     }
445 
446     @SmallTest
testBindingSucceeds_nullBinding_instanceStateSavedAndRestored()447     public void testBindingSucceeds_nullBinding_instanceStateSavedAndRestored() {
448         // GIVEN that we can bind to the SUW NetworkInterceptService
449         when(mActivity.bindService(any(Intent.class), any(ServiceConnection.class), anyInt()))
450                 .thenReturn(true);
451 
452         // WHEN calling triggerDpcStart()
453         mStartDpcInsideSuwServiceConnection.triggerDpcStart(mActivity, mDpcIntentSender);
454 
455         // THEN we bind to the SUW NetworkInterceptService
456         verifyBindServiceCalled(mActivity, mStartDpcInsideSuwServiceConnection);
457 
458         // WHEN the NetworkInterceptService returns a null binding
459         mStartDpcInsideSuwServiceConnection.onNullBinding(TEST_SUW_COMPONENT_NAME);
460 
461         // THEN an intent is sent to the DPC
462         verifyDpcLaunched(mActivity);
463 
464         // WHEN saving state and calling unbind after the null binding
465         final Bundle savedInstanceState = new Bundle();
466         mStartDpcInsideSuwServiceConnection.saveInstanceState(savedInstanceState);
467         mStartDpcInsideSuwServiceConnection.unbind(mActivity);
468 
469         // THEN we unbind from the NetworkInterceptService
470         verify(mActivity).unbindService(eq(mStartDpcInsideSuwServiceConnection));
471 
472         // GIVEN that a restored activity can bind to the SUW NetworkInterceptService
473         when(mRestoredActivity.bindService(
474                 any(Intent.class), any(ServiceConnection.class), anyInt())).thenReturn(true);
475 
476         // WHEN we restore the service connection from the saved state
477         final StartDpcInsideSuwServiceConnection restoredServiceConnection =
478                 getRestoredServiceConnection(savedInstanceState);
479 
480         // THEN we bind to the SUW NetworkInterceptService again
481         verifyBindServiceCalled(mRestoredActivity, restoredServiceConnection);
482 
483         // WHEN the NetworkInterceptService returns a null binding again
484         restoredServiceConnection.onNullBinding(TEST_SUW_COMPONENT_NAME);
485 
486         // THEN no new intent is sent to the DPC
487         verify(mRestoredActivity, never()).startActivityForResultAsUser(any(Intent.class), anyInt(),
488                 any(UserHandle.class));
489 
490         // WHEN calling dpcFinished and unbind
491         restoredServiceConnection.dpcFinished();
492         restoredServiceConnection.unbind(mRestoredActivity);
493 
494         // THEN we unbind from the NetworkInterceptService
495         verify(mRestoredActivity).unbindService(eq(restoredServiceConnection));
496     }
497 
498     @SmallTest
testBindingFails()499     public void testBindingFails() {
500         // GIVEN that we can't bind to the SUW NetworkInterceptService
501         when(mActivity.bindService(any(Intent.class), any(ServiceConnection.class), anyInt()))
502                 .thenReturn(false);
503 
504         // WHEN calling triggerDpcStart()
505         mStartDpcInsideSuwServiceConnection.triggerDpcStart(mActivity, mDpcIntentSender);
506 
507         // THEN we attempt to bind to the SUW NetworkInterceptService
508         verifyBindServiceCalled(mActivity, mStartDpcInsideSuwServiceConnection);
509 
510         // THEN an intent is sent to the DPC, even though the service binding failed
511         verifyDpcLaunched(mActivity);
512 
513         // WHEN calling dpcFinished and unbind
514         mStartDpcInsideSuwServiceConnection.dpcFinished();
515         mStartDpcInsideSuwServiceConnection.unbind(mActivity);
516 
517         // THEN we unbind from the NetworkInterceptService even if binding failed
518         verify(mActivity).unbindService(eq(mStartDpcInsideSuwServiceConnection));
519     }
520 
521     @SmallTest
testBindingFails_instanceStateSavedAndRestored()522     public void testBindingFails_instanceStateSavedAndRestored() {
523         // GIVEN that we can't bind to the SUW NetworkInterceptService
524         when(mActivity.bindService(any(Intent.class), any(ServiceConnection.class), anyInt()))
525                 .thenReturn(false);
526 
527         // WHEN calling triggerDpcStart()
528         mStartDpcInsideSuwServiceConnection.triggerDpcStart(mActivity, mDpcIntentSender);
529 
530         // THEN we attempt to bind to the SUW NetworkInterceptService
531         verifyBindServiceCalled(mActivity, mStartDpcInsideSuwServiceConnection);
532 
533         // THEN an intent is sent to the DPC, even though the service binding failed
534         verifyDpcLaunched(mActivity);
535 
536         // WHEN saving state and calling unbind after the binding failed
537         final Bundle savedInstanceState = new Bundle();
538         mStartDpcInsideSuwServiceConnection.saveInstanceState(savedInstanceState);
539         mStartDpcInsideSuwServiceConnection.unbind(mActivity);
540 
541         // THEN we unbind from the NetworkInterceptService even if binding failed
542         verify(mActivity).unbindService(eq(mStartDpcInsideSuwServiceConnection));
543 
544         // GIVEN that a restored activity could now bind to the SUW NetworkInterceptService
545         when(mRestoredActivity.bindService(
546                 any(Intent.class), any(ServiceConnection.class), anyInt())).thenReturn(true);
547 
548         // WHEN we restore the service connection from the saved state
549         final StartDpcInsideSuwServiceConnection restoredServiceConnection =
550                 getRestoredServiceConnection(savedInstanceState);
551 
552         // THEN we still do not bind to the SUW NetworkInterceptService
553         verify(mRestoredActivity, never()).bindService(any(Intent.class),
554                 eq(restoredServiceConnection), anyInt());
555 
556         // THEN no new intent is sent to the DPC
557         verify(mRestoredActivity, never()).startActivityForResultAsUser(any(Intent.class), anyInt(),
558                 any(UserHandle.class));
559 
560         // WHEN calling dpcFinished and unbind
561         restoredServiceConnection.dpcFinished();
562         restoredServiceConnection.unbind(mRestoredActivity);
563 
564         // THEN we do not unbind from the NetworkInterceptService
565         verify(mRestoredActivity, never()).unbindService(eq(restoredServiceConnection));
566     }
567 
verifyDpcLaunched(Activity activity)568     private void verifyDpcLaunched(Activity activity) {
569         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
570         verify(mTransitionHelper).startActivityForResultAsUserWithTransition(
571                 eq(activity), intentCaptor.capture(), anyInt(), any(UserHandle.class));
572         final String intentAction = intentCaptor.getValue().getAction();
573         // THEN the intent should be ACTION_PROVISIONING_SUCCESSFUL
574         assertEquals(ACTION_ADMIN_POLICY_COMPLIANCE, intentAction);
575         // THEN the intent should only be sent to the dpc
576         assertEquals(TEST_MDM_PACKAGE_NAME, intentCaptor.getValue().getPackage());
577         // THEN the admin extras bundle should contain mdm extras
578         assertExtras(intentCaptor.getValue());
579         // THEN a metric should be logged
580         verify(mProvisioningAnalyticsTracker).logDpcSetupStarted(eq(activity), eq(intentAction));
581     }
582 
verifyBindServiceCalled(Activity activity, StartDpcInsideSuwServiceConnection serviceConnection)583     private void verifyBindServiceCalled(Activity activity,
584             StartDpcInsideSuwServiceConnection serviceConnection) {
585         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
586         verify(activity).bindService(intentCaptor.capture(), eq(serviceConnection), anyInt());
587         // THEN the intent should be NETWORK_INTERCEPT_SERVICE_ACTION
588         assertEquals(StartDpcInsideSuwServiceConnection.NETWORK_INTERCEPT_SERVICE_ACTION,
589                 intentCaptor.getValue().getAction());
590         // THEN the intent should be sent to Setup Wizard
591         assertEquals(StartDpcInsideSuwServiceConnection.SETUP_WIZARD_PACKAGE_NAME,
592                 intentCaptor.getValue().getPackage());
593     }
594 
assertExtras(Intent intent)595     private void assertExtras(Intent intent) {
596         assertTrue(TestUtils.bundleEquals(TEST_MDM_EXTRA_BUNDLE,
597                 (PersistableBundle) intent.getExtra(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE)));
598     }
599 
getRestoredServiceConnection( Bundle savedInstanceState)600     private StartDpcInsideSuwServiceConnection getRestoredServiceConnection(
601             Bundle savedInstanceState) {
602         final PolicyComplianceUtils policyComplianceUtils = new PolicyComplianceUtils();
603         final Runnable dpcIntentSenderForRestoredActivity = () ->
604                 policyComplianceUtils.startPolicyComplianceActivityForResultIfResolved(
605                         mRestoredActivity, mParams, TEST_REQUEST_CODE,
606                         mUtils, mProvisioningAnalyticsTracker, mTransitionHelper);
607 
608         return new StartDpcInsideSuwServiceConnection(mRestoredActivity, savedInstanceState,
609                 dpcIntentSenderForRestoredActivity);
610     }
611 }
612