1 /*
2  * Copyright (C) 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 package com.android.managedprovisioning.e2eui;
17 
18 import static androidx.test.espresso.Espresso.onView;
19 import static androidx.test.espresso.action.ViewActions.click;
20 import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
21 import static androidx.test.espresso.matcher.ViewMatchers.withText;
22 
23 import static org.hamcrest.Matchers.allOf;
24 import static org.hamcrest.Matchers.containsString;
25 
26 import android.content.Intent;
27 import android.content.pm.UserInfo;
28 import android.os.SystemClock;
29 import android.os.UserManager;
30 import android.test.AndroidTestCase;
31 import android.util.Log;
32 import android.view.View;
33 
34 import androidx.test.espresso.ViewInteraction;
35 import androidx.test.espresso.base.DefaultFailureHandler;
36 import androidx.test.filters.LargeTest;
37 import androidx.test.rule.ActivityTestRule;
38 
39 import com.android.managedprovisioning.R;
40 import com.android.managedprovisioning.TestInstrumentationRunner;
41 import com.android.managedprovisioning.common.BlockingBroadcastReceiver;
42 import com.android.managedprovisioning.preprovisioning.PreProvisioningActivity;
43 
44 import com.google.android.setupcompat.template.FooterButton;
45 
46 import org.hamcrest.Matcher;
47 
48 import java.util.List;
49 
50 @LargeTest
51 public class ManagedProfileTest extends AndroidTestCase {
52     private static final String TAG = "ManagedProfileTest";
53 
54     private static final long PROVISIONING_RESULT_TIMEOUT_SECONDS = 120L;
55     private static final long FIVE_SECONDS_MILLIS = 5000;
56 
57     public ActivityTestRule mActivityRule;
58     private ProvisioningResultListener mResultListener;
59 
60     @Override
setUp()61     protected void setUp() throws Exception {
62         super.setUp();
63         mActivityRule = new ActivityTestRule<>(
64                 PreProvisioningActivity.class,
65                 true /* initialTouchMode */,
66                 false);  // launchActivity. False to set intent per method
67         mResultListener = new ProvisioningResultListener(getContext());
68         TestInstrumentationRunner.registerReplacedActivity(PreProvisioningActivity.class,
69                 (cl, className, intent) -> new TestPreProvisioningActivity(mResultListener));
70     }
71 
72     @Override
tearDown()73     protected void tearDown() throws Exception {
74         super.tearDown();
75         TestInstrumentationRunner.unregisterReplacedActivity(PreProvisioningActivity.class);
76         mResultListener.unregister();
77 
78         // Remove any managed profiles in case that
79         removeAllManagedProfiles();
80     }
81 
removeAllManagedProfiles()82     private void removeAllManagedProfiles() {
83         UserManager um = getContext().getSystemService(UserManager.class);
84         List<UserInfo> users = um.getUsers();
85         for (UserInfo user : users) {
86             if (user.isManagedProfile()) {
87                 int userId = user.getUserHandle().getIdentifier();
88                 removeProfileAndWait(userId);
89             }
90         }
91     }
92 
93     /** Remove a profile and wait until it has been removed before continuing. */
removeProfileAndWait(int userId)94     private void removeProfileAndWait(int userId) {
95         Log.e(TAG, "remove managed profile user: " + userId);
96         UserManager userManager = getContext().getSystemService(UserManager.class);
97 
98         // Intent.ACTION_MANAGED_PROFILE_REMOVED gets sent too early, so we need to wait for
99         // Intent.ACTION_USER_REMOVED
100         BlockingBroadcastReceiver receiver =
101                 new BlockingBroadcastReceiver(mContext, Intent.ACTION_USER_REMOVED);
102         try {
103             receiver.register();
104             userManager.removeUserEvenWhenDisallowed(userId);
105 
106             long timeoutMillis = PROVISIONING_RESULT_TIMEOUT_SECONDS * 1000;
107             Intent confirmation = receiver.awaitForBroadcast(timeoutMillis);
108 
109             if (confirmation == null) {
110                 // The user was not removed
111                 fail("Waiting for profile to be removed, but was not removed.");
112             }
113         } finally {
114             receiver.unregisterQuietly();
115         }
116     }
117 
118     // TODO(b/151421429): investigate why this test fails
119 //    public void testManagedProfile() throws Exception {
120 //        mActivityRule.launchActivity(ManagedProfileAdminReceiver.INTENT_PROVISION_MANAGED_PROFILE);
121 //
122 //        // Try pressing "Accept & continue" for 1 minute (12 * 5 seconds)
123 //        new EspressoClickRetryActions(/* retries= */ 12, /* delayMillis= */ FIVE_SECONDS_MILLIS) {
124 //            @Override
125 //            public ViewInteraction newViewInteraction1() {
126 //                return onView(allOf(withClassName(containsString("FooterActionButton")),
127 //                        withText(R.string.accept_and_continue)));
128 //            }
129 //        }.run();
130 //
131 //        mResultListener.register();
132 //
133 //        // Try pressing "Next" for 10 minutes (120 * 5 seconds). This must be long enough for
134 //        // DPC download/installation to happen, and education screens to complete.
135 //        new EspressoClickRetryActions(/* retries= */ 120, /* delayMillis= */ FIVE_SECONDS_MILLIS) {
136 //            @Override
137 //            public ViewInteraction newViewInteraction1() {
138 //                return onView(allOf(withClassName(containsString("FooterActionButton")),
139 //                        withText(R.string.next)));
140 //            }
141 //        }.run();
142 //
143 //        // Try pressing "Next" for 10 minutes (5 * 5 seconds). This must be long enough to show the
144 //        // cross profile consent screen.
145 //        new EspressoClickRetryActions(/* retries= */ 5, /* delayMillis= */ FIVE_SECONDS_MILLIS) {
146 //            @Override
147 //            public ViewInteraction newViewInteraction1() {
148 //                return onView(allOf(withClassName(containsString("FooterActionButton")),
149 //                        withText(R.string.next)));
150 //            }
151 //        }.run();
152 //
153 //        if (mResultListener.await(PROVISIONING_RESULT_TIMEOUT_SECONDS)) {
154 //            assertTrue(mResultListener.getResult());
155 //        } else {
156 //            fail("timeout: " + PROVISIONING_RESULT_TIMEOUT_SECONDS + " seconds");
157 //        }
158 //    }
159 
160     private abstract class EspressoClickRetryActions {
161         private final int mRetries;
162         private final long mDelayMillis;
163         private int i = 0;
164 
EspressoClickRetryActions(int retries, long delayMillis)165         EspressoClickRetryActions(int retries, long delayMillis) {
166             mRetries = retries;
167             mDelayMillis = delayMillis;
168         }
169 
newViewInteraction1()170         public abstract ViewInteraction newViewInteraction1();
171 
run()172         public void run() {
173             i++;
174             newViewInteraction1()
175                     .withFailureHandler(this::handleFailure)
176                     .perform(click());
177             Log.i(TAG, "newViewInteraction1 succeeds.");
178         }
179 
handleFailure(Throwable e, Matcher<View> matcher)180         private void handleFailure(Throwable e, Matcher<View> matcher) {
181             Log.i(TAG, "espresso handleFailure count: " + i, e);
182             if (i < mRetries) {
183                 SystemClock.sleep(mDelayMillis);
184                 run();
185             } else {
186                 new DefaultFailureHandler(getContext()).handle(e, matcher);
187             }
188         }
189     }
190 }
191 
192