• Home
  • History
  • Annotate
  • 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  
44  import androidx.test.InstrumentationRegistry;
45  import androidx.test.filters.SmallTest;
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