1 /*
2  * Copyright 2016, 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.finalization;
18 
19 import static android.app.admin.DeviceAdminReceiver.ACTION_PROFILE_PROVISIONING_COMPLETE;
20 import static android.app.admin.DevicePolicyManager.ACTION_MANAGED_PROFILE_PROVISIONED;
21 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE;
22 
23 import static org.mockito.ArgumentMatchers.eq;
24 import static org.mockito.Matchers.any;
25 import static org.mockito.Mockito.doAnswer;
26 import static org.mockito.Mockito.never;
27 import static org.mockito.Mockito.verify;
28 
29 import android.accounts.Account;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.os.UserHandle;
33 import android.test.AndroidTestCase;
34 import android.test.suitebuilder.annotation.SmallTest;
35 
36 import com.android.managedprovisioning.common.RemoveAccountListener;
37 import com.android.managedprovisioning.common.Utils;
38 
39 import org.mockito.ArgumentCaptor;
40 import org.mockito.Mock;
41 import org.mockito.MockitoAnnotations;
42 import org.mockito.invocation.InvocationOnMock;
43 
44 import java.util.concurrent.Semaphore;
45 import java.util.concurrent.TimeUnit;
46 import java.util.function.Function;
47 
48 /**
49  * Unit tests for {@link DpcReceivedSuccessReceiver}.
50  */
51 public class DpcReceivedSuccessReceiverTest extends AndroidTestCase {
52     private static final int SEND_BROADCAST_TIMEOUT_SECONDS = 1;
53     private static final String TEST_MDM_PACKAGE_NAME = "mdm.package.name";
54     private static final Account TEST_ACCOUNT = new Account("test@account.com", "account.type");
55     private static final Intent TEST_INTENT = new Intent(ACTION_PROFILE_PROVISIONING_COMPLETE);
56     private static final UserHandle MANAGED_PROFILE_USER_HANDLE = UserHandle.of(123);
57 
58     @Mock private Context mContext;
59     @Mock private Utils mUtils;
60 
61     @Override
setUp()62     public void setUp() {
63         // this is necessary for mockito to work
64         System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
65         MockitoAnnotations.initMocks(this);
66     }
67 
68     @SmallTest
testNoAccountMigration()69     public void testNoAccountMigration() {
70         // GIVEN that no account migration occurred during provisioning
71         final DpcReceivedSuccessReceiver receiver = new DpcReceivedSuccessReceiver(
72                 /* migratedAccount */ null, /* keepAccountMigrated */ false,
73                 MANAGED_PROFILE_USER_HANDLE, TEST_MDM_PACKAGE_NAME, mUtils, /* callback */ null,
74                 /* isAdminIntegratedFlow */ false);
75 
76         // WHEN the profile provisioning complete intent was received by the DPC
77         receiver.onReceive(mContext, TEST_INTENT);
78 
79         // THEN an intent should be sent to the primary user
80         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
81         verify(mContext).sendBroadcast(intentCaptor.capture());
82 
83         // THEN the broadcast action is ACTION_MANAGED_PROFILE_PROVISIONED
84         assertEquals(ACTION_MANAGED_PROFILE_PROVISIONED, intentCaptor.getValue().getAction());
85 
86         // THEN the receiver package is the DPC
87         assertEquals(TEST_MDM_PACKAGE_NAME, intentCaptor.getValue().getPackage());
88 
89         // THEN the extra user handle should be of managed profile
90         assertEquals(MANAGED_PROFILE_USER_HANDLE,
91                 intentCaptor.getValue().getExtra(Intent.EXTRA_USER));
92     }
93 
94     @SmallTest
testAccountMigration()95     public void testAccountMigration() throws Exception {
96         // GIVEN that account migration occurred during provisioning
97         final DpcReceivedSuccessReceiver receiver = new DpcReceivedSuccessReceiver(TEST_ACCOUNT,
98                 /* keepAccountMigrated */ false, MANAGED_PROFILE_USER_HANDLE, TEST_MDM_PACKAGE_NAME,
99                 mUtils, /* callback */ null, /* isAdminIntegratedFlow */ false);
100 
101         // WHEN receiver.onReceive is called
102         invokeOnReceiveAndVerifyIntent(receiver, /* postOnReceive */ aVoid -> {
103             // THEN the account should have been removed from the primary user
104             ArgumentCaptor<RemoveAccountListener> captor =
105                     ArgumentCaptor.forClass(RemoveAccountListener.class);
106             verify(mUtils).removeAccountAsync(eq(mContext), eq(TEST_ACCOUNT), captor.capture());
107             captor.getValue().onAccountRemoved();
108             return null;
109         });
110 
111     }
112 
113     @SmallTest
testAccountCopy()114     public void testAccountCopy() throws Exception {
115         // GIVEN that account copy occurred during provisioning
116         final DpcReceivedSuccessReceiver receiver = new DpcReceivedSuccessReceiver(TEST_ACCOUNT,
117                 /* keepAccountMigrated */ true, MANAGED_PROFILE_USER_HANDLE, TEST_MDM_PACKAGE_NAME,
118                 mUtils, /* callback */ null, /* isAdminIntegratedFlow */ false);
119 
120         // WHEN receiver.onReceive is called
121         invokeOnReceiveAndVerifyIntent(receiver, /* postOnReceive */ aVoid -> {
122             // THEN the account is not removed from the primary user
123             verify(mUtils, never()).removeAccountAsync(eq(mContext), eq(TEST_ACCOUNT), any());
124             return null;
125         });
126     }
127 
invokeOnReceiveAndVerifyIntent(final DpcReceivedSuccessReceiver receiver, Function<Void, Void> postOnReceive)128     private void invokeOnReceiveAndVerifyIntent(final DpcReceivedSuccessReceiver receiver,
129             Function<Void, Void> postOnReceive) throws InterruptedException {
130         // prepare a semaphore to handle AsyncTask usage
131         final Semaphore semaphore = new Semaphore(0);
132         doAnswer((InvocationOnMock invocation) -> {
133             semaphore.release(1);
134             return null;
135         }).when(mContext).sendBroadcast(any(Intent.class));
136 
137         // WHEN the profile provisioning complete intent was received by the DPC
138         receiver.onReceive(mContext, TEST_INTENT);
139 
140         postOnReceive.apply(null);
141 
142         assertTrue(semaphore.tryAcquire(SEND_BROADCAST_TIMEOUT_SECONDS, TimeUnit.SECONDS));
143 
144         // THEN an intent should be sent to the primary user
145         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
146         verify(mContext).sendBroadcast(intentCaptor.capture());
147 
148         // THEN the broadcast action is ACTION_MANAGED_PROFILE_PROVISIONED
149         assertEquals(ACTION_MANAGED_PROFILE_PROVISIONED, intentCaptor.getValue().getAction());
150 
151         // THEN the receiver package is the DPC
152         assertEquals(TEST_MDM_PACKAGE_NAME, intentCaptor.getValue().getPackage());
153 
154         // THEN the extra user handle should be of managed profile
155         assertEquals(MANAGED_PROFILE_USER_HANDLE,
156                 intentCaptor.getValue().getExtra(Intent.EXTRA_USER));
157 
158         // THEN the account was added to the broadcast
159         assertEquals(TEST_ACCOUNT, intentCaptor.getValue().getParcelableExtra(
160                 EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE));
161     }
162 }
163