1/*
2 * Copyright (C) 2017 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
17package com.android.settings;
18
19import android.app.Activity;
20import android.app.admin.DevicePolicyManager;
21import android.content.Context;
22import android.content.Intent;
23import android.content.pm.PackageManager;
24import android.support.test.filters.MediumTest;
25import android.support.test.rule.ActivityTestRule;
26import android.support.test.runner.AndroidJUnit4;
27import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
28import android.support.test.runner.lifecycle.Stage;
29import android.support.test.uiautomator.UiDevice;
30import android.support.test.uiautomator.UiObject;
31import android.support.test.uiautomator.UiSelector;
32
33import android.text.format.DateUtils;
34import android.view.KeyEvent;
35
36import com.android.settings.R;
37
38import java.util.Collection;
39
40import org.junit.After;
41import org.junit.Assert;
42import org.junit.Before;
43import org.junit.Rule;
44import org.junit.Test;
45import org.junit.runner.RunWith;
46
47import static android.support.test.InstrumentationRegistry.getInstrumentation;
48import static com.google.common.truth.Truth.assertThat;
49import static org.junit.Assert.assertTrue;
50
51/**
52 * Tests for {@link ChooseLockGenericTest}
53 *
54 * m SettingsTests &&
55 * adb install \
56 * -r -g  ${ANDROID_PRODUCT_OUT}/data/app/SettingsTests/SettingsTests.apk &&
57 * adb shell am instrument -e class com.android.settings.ChooseLockGenericTest \
58 * -w com.android.settings.tests/android.support.test.runner.AndroidJUnitRunner
59 */
60@RunWith(AndroidJUnit4.class)
61@MediumTest
62public class ChooseLockGenericTest {
63    private static final long TIMEOUT = 5 * DateUtils.SECOND_IN_MILLIS;
64    private static final Intent PHISHING_ATTACK_INTENT = new Intent()
65            .putExtra("confirm_credentials", false)
66            .putExtra("password_confirmed", true);
67
68    private UiDevice mDevice;
69    private Context mTargetContext;
70    private String mSettingPackage;
71    private PackageManager mPackageManager;
72    @Rule
73    public ActivityTestRule<ChooseLockGeneric> mChooseLockGenericActivityRule =
74            new ActivityTestRule<>(
75                    ChooseLockGeneric.class,
76                    true /* enable touch at launch */,
77                    false /* don't launch at every test */);
78
79    @Before
80    public void setUp() throws Exception {
81        mDevice = UiDevice.getInstance(getInstrumentation());
82        mTargetContext = getInstrumentation().getTargetContext();
83        mSettingPackage = mTargetContext.getPackageName();
84        mPackageManager = mTargetContext.getPackageManager();
85
86        setPassword();
87    }
88
89    @After
90    public void tearDown() throws Exception {
91        clearPassword();
92    }
93
94    @Test
95    public void testConfirmLockPasswordShown_deviceWithPassword() throws Exception, Throwable {
96        // GIVEN a PIN password is set on this device at set up.
97        // WHEN ChooseLockGeneric is launched with no extras.
98        mChooseLockGenericActivityRule.launchActivity(null /* No extras */);
99        // THEN ConfirmLockPassword.InternalActivity is shown.
100        assertThat(getCurrentActivity()).isInstanceOf(ConfirmLockPassword.InternalActivity.class);
101    }
102
103    @Test
104    public void testConfirmLockPasswordShown_deviceWithPassword_phishingAttack()
105            throws Exception, Throwable {
106        // GIVEN a PIN password is set on this device at set up.
107        // WHEN ChooseLockGeneric is launched with extras to by-pass lock password confirmation.
108        mChooseLockGenericActivityRule.launchActivity(PHISHING_ATTACK_INTENT);
109        // THEN ConfirmLockPassword.InternalActivity is still shown.
110        assertThat(getCurrentActivity()).isInstanceOf(ConfirmLockPassword.InternalActivity.class);
111    }
112
113    private Activity getCurrentActivity() throws Throwable {
114        getInstrumentation().waitForIdleSync();
115        final Activity[] activity = new Activity[1];
116        getInstrumentation().runOnMainSync(new Runnable() {
117            @Override
118            public void run() {
119                Collection<Activity> activities = ActivityLifecycleMonitorRegistry.getInstance()
120                        .getActivitiesInStage(Stage.RESUMED);
121                activity[0] = activities.iterator().next();
122            }
123        });
124        return activity[0];
125    }
126
127    private void launchNewPassword() throws Exception {
128        Intent newPasswordIntent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD)
129                .setPackage(mSettingPackage)
130                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
131        getInstrumentation().getContext().startActivity(newPasswordIntent);
132        mDevice.waitForIdle();
133    }
134
135    /** Sets a PIN password, 12345, for testing. */
136    private void setPassword() throws Exception {
137        launchNewPassword();
138
139        if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
140            // Set "lock_none", but it actually means we don't want to enroll a fingerprint.
141            UiObject view = new UiObject(
142                    new UiSelector().resourceId(mSettingPackage + ":id/lock_none"));
143            assertTrue("lock_none", view.waitForExists(TIMEOUT));
144            view.click();
145            mDevice.waitForIdle();
146        }
147
148        // Pick PIN from the option list
149        UiObject view = new UiObject(new UiSelector()
150                .resourceId(mSettingPackage + ":id/lock_pin"));
151        assertTrue("lock_pin", view.waitForExists(TIMEOUT));
152        view.click();
153        mDevice.waitForIdle();
154
155        // Ignore any interstitial options
156        view = new UiObject(new UiSelector()
157                .resourceId(mSettingPackage + ":id/encrypt_dont_require_password"));
158        if (view.waitForExists(TIMEOUT)) {
159            view.click();
160            mDevice.waitForIdle();
161        }
162
163        // Yes, we really want to
164        view = new UiObject(new UiSelector()
165                .resourceId(mSettingPackage + ":id/next_button"));
166        if (view.waitForExists(TIMEOUT)) {
167            view.click();
168            mDevice.waitForIdle();
169        }
170
171        // Set our PIN
172        view = new UiObject(new UiSelector()
173                .resourceId(mSettingPackage + ":id/password_entry"));
174        assertTrue("password_entry", view.waitForExists(TIMEOUT));
175
176        // Enter it twice to confirm
177        enterTestPin();
178        enterTestPin();
179
180        mDevice.pressBack();
181    }
182
183    /** Clears the previous set PIN password. */
184    private void clearPassword() throws Exception {
185        launchNewPassword();
186
187        // Enter current PIN
188        UiObject view = new UiObject(
189                new UiSelector().resourceId(mSettingPackage + ":id/password_entry"));
190        if (!view.waitForExists(TIMEOUT)) {
191            // Odd, maybe there is a crash dialog showing; try dismissing it
192            mDevice.pressBack();
193            mDevice.waitForIdle();
194
195            assertTrue("password_entry", view.waitForExists(TIMEOUT));
196        }
197
198        enterTestPin();
199
200        // Set back to "none"
201        view = new UiObject(new UiSelector().resourceId(mSettingPackage + ":id/lock_none"));
202        assertTrue("lock_none", view.waitForExists(TIMEOUT));
203        view.click();
204        mDevice.waitForIdle();
205
206        // Yes, we really want "none" if prompted again
207        view = new UiObject(new UiSelector().resourceId(mSettingPackage + ":id/lock_none"));
208        if (view.waitForExists(TIMEOUT)) {
209            view.click();
210            mDevice.waitForIdle();
211        }
212
213        // Yes, we really want to
214        view = new UiObject(new UiSelector()
215                .resourceId("android:id/button1"));
216        if (view.waitForExists(TIMEOUT)) {
217            view.click();
218            mDevice.waitForIdle();
219        }
220
221        mDevice.pressBack();
222    }
223
224    private void enterTestPin() throws Exception {
225        mDevice.waitForIdle();
226        mDevice.pressKeyCode(KeyEvent.KEYCODE_1);
227        mDevice.pressKeyCode(KeyEvent.KEYCODE_2);
228        mDevice.pressKeyCode(KeyEvent.KEYCODE_3);
229        mDevice.pressKeyCode(KeyEvent.KEYCODE_4);
230        mDevice.pressKeyCode(KeyEvent.KEYCODE_5);
231        mDevice.waitForIdle();
232        mDevice.pressEnter();
233        mDevice.waitForIdle();
234    }
235}
236