packages/apps/Settings
リビジョン | 4caa0889c031a5d0db80e8fce85f495fe4382139 (tree) |
---|---|
日時 | 2017-07-18 14:53:52 |
作者 | Chih-Wei Huang <cwhuang@linu...> |
コミッター | Chih-Wei Huang |
Merge tag 'android-7.1.2_r27' into nougat-x86
Android 7.1.2 release 27
@@ -167,16 +167,6 @@ public class ChooseLockGeneric extends SettingsActivity { | ||
167 | 167 | ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false); |
168 | 168 | mForChangeCredRequiredForBoot = getArguments() != null && getArguments().getBoolean( |
169 | 169 | ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT); |
170 | - if (mIsSetNewPassword) { | |
171 | - // In ACTION_SET_NEW_PARENT_PROFILE_PASSWORD or ACTION_SET_NEW_PASSWORD, the user | |
172 | - // will be asked to confirm the password if one has been set. | |
173 | - // On fingerprint supported device, fingerprint options are represented in the | |
174 | - // options. If the user chooses to skip fingerprint setup, ChooseLockGeneric is | |
175 | - // relaunched to only show options without fingerprint. In this case, we shouldn't | |
176 | - // ask the user to confirm the password again. | |
177 | - mPasswordConfirmed = getActivity().getIntent().getBooleanExtra( | |
178 | - PASSWORD_CONFIRMED, false); | |
179 | - } | |
180 | 170 | |
181 | 171 | if (savedInstanceState != null) { |
182 | 172 | mPasswordConfirmed = savedInstanceState.getBoolean(PASSWORD_CONFIRMED); |
@@ -247,11 +237,12 @@ public class ChooseLockGeneric extends SettingsActivity { | ||
247 | 237 | showFactoryResetProtectionWarningDialog(key); |
248 | 238 | return true; |
249 | 239 | } else if (KEY_SKIP_FINGERPRINT.equals(key)) { |
250 | - Intent chooseLockGenericIntent = new Intent(getActivity(), ChooseLockGeneric.class); | |
240 | + Intent chooseLockGenericIntent = new Intent(getActivity(), | |
241 | + ChooseLockGeneric.InternalActivity.class); | |
251 | 242 | chooseLockGenericIntent.setAction(getIntent().getAction()); |
252 | 243 | // Forward the target user id to ChooseLockGeneric. |
253 | 244 | chooseLockGenericIntent.putExtra(Intent.EXTRA_USER_ID, mUserId); |
254 | - chooseLockGenericIntent.putExtra(PASSWORD_CONFIRMED, mPasswordConfirmed); | |
245 | + chooseLockGenericIntent.putExtra(CONFIRM_CREDENTIALS, !mPasswordConfirmed); | |
255 | 246 | startActivityForResult(chooseLockGenericIntent, SKIP_FINGERPRINT_REQUEST); |
256 | 247 | return true; |
257 | 248 | } else { |
@@ -36,6 +36,7 @@ import android.os.Bundle; | ||
36 | 36 | import android.os.UserHandle; |
37 | 37 | import android.support.v7.preference.Preference; |
38 | 38 | import android.support.v7.preference.Preference.OnPreferenceClickListener; |
39 | +import android.support.v7.preference.PreferenceGroup; | |
39 | 40 | import android.support.v7.preference.PreferenceScreen; |
40 | 41 | import android.util.Log; |
41 | 42 | import android.view.LayoutInflater; |
@@ -83,7 +84,7 @@ public class ManageAccountsSettings extends AccountPreferenceBase | ||
83 | 84 | |
84 | 85 | // If an account type is set, then show only accounts of that type |
85 | 86 | private String mAccountType; |
86 | - // Temporary hack, to deal with backward compatibility | |
87 | + // Temporary hack, to deal with backward compatibility | |
87 | 88 | // mFirstAccount is used for the injected preferences |
88 | 89 | private Account mFirstAccount; |
89 | 90 |
@@ -440,15 +441,18 @@ public class ManageAccountsSettings extends AccountPreferenceBase | ||
440 | 441 | } |
441 | 442 | |
442 | 443 | /** |
443 | - * Filters through the preference list provided by GoogleLoginService. | |
444 | + * Recursively filters through the preference list provided by GoogleLoginService. | |
444 | 445 | * |
445 | 446 | * This method removes all the invalid intent from the list, adds account name as extra into the |
446 | 447 | * intent, and hack the location settings to start it as a fragment. |
447 | 448 | */ |
448 | - private void updatePreferenceIntents(PreferenceScreen prefs) { | |
449 | + private void updatePreferenceIntents(PreferenceGroup prefs) { | |
449 | 450 | final PackageManager pm = getActivity().getPackageManager(); |
450 | 451 | for (int i = 0; i < prefs.getPreferenceCount();) { |
451 | 452 | Preference pref = prefs.getPreference(i); |
453 | + if (pref instanceof PreferenceGroup) { | |
454 | + updatePreferenceIntents((PreferenceGroup) pref); | |
455 | + } | |
452 | 456 | Intent intent = pref.getIntent(); |
453 | 457 | if (intent != null) { |
454 | 458 | // Hack. Launch "Location" as fragment instead of as activity. |
@@ -497,8 +501,8 @@ public class ManageAccountsSettings extends AccountPreferenceBase | ||
497 | 501 | } else { |
498 | 502 | Log.e(TAG, |
499 | 503 | "Refusing to launch authenticator intent because" |
500 | - + "it exploits Settings permissions: " | |
501 | - + prefIntent); | |
504 | + + " it exploits Settings permissions: " | |
505 | + + prefIntent); | |
502 | 506 | } |
503 | 507 | return true; |
504 | 508 | } |
@@ -518,20 +522,26 @@ public class ManageAccountsSettings extends AccountPreferenceBase | ||
518 | 522 | private boolean isSafeIntent(PackageManager pm, Intent intent) { |
519 | 523 | AuthenticatorDescription authDesc = |
520 | 524 | mAuthenticatorHelper.getAccountTypeDescription(mAccountType); |
521 | - ResolveInfo resolveInfo = pm.resolveActivity(intent, 0); | |
525 | + ResolveInfo resolveInfo = | |
526 | + pm.resolveActivityAsUser(intent, 0, mUserHandle.getIdentifier()); | |
522 | 527 | if (resolveInfo == null) { |
523 | 528 | return false; |
524 | 529 | } |
525 | 530 | ActivityInfo resolvedActivityInfo = resolveInfo.activityInfo; |
526 | 531 | ApplicationInfo resolvedAppInfo = resolvedActivityInfo.applicationInfo; |
527 | 532 | try { |
533 | + if (resolvedActivityInfo.exported) { | |
534 | + if (resolvedActivityInfo.permission == null) { | |
535 | + return true; // exported activity without permission. | |
536 | + } else if (pm.checkPermission(resolvedActivityInfo.permission, | |
537 | + authDesc.packageName) == PackageManager.PERMISSION_GRANTED) { | |
538 | + return true; | |
539 | + } | |
540 | + } | |
528 | 541 | ApplicationInfo authenticatorAppInf = pm.getApplicationInfo(authDesc.packageName, 0); |
529 | - return resolvedActivityInfo.exported | |
530 | - || resolvedAppInfo.uid == authenticatorAppInf.uid; | |
542 | + return resolvedAppInfo.uid == authenticatorAppInf.uid; | |
531 | 543 | } catch (NameNotFoundException e) { |
532 | - Log.e(TAG, | |
533 | - "Intent considered unsafe due to exception.", | |
534 | - e); | |
544 | + Log.e(TAG, "Intent considered unsafe due to exception.", e); | |
535 | 545 | return false; |
536 | 546 | } |
537 | 547 | } |
@@ -9,11 +9,13 @@ LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle | ||
9 | 9 | |
10 | 10 | LOCAL_STATIC_JAVA_LIBRARIES := \ |
11 | 11 | android-support-test \ |
12 | + guava \ | |
12 | 13 | mockito-target \ |
13 | 14 | espresso-core \ |
14 | 15 | espresso-contrib-nodep \ |
15 | 16 | espresso-intents-nodep \ |
16 | - ub-uiautomator | |
17 | + ub-uiautomator \ | |
18 | + truth-prebuilt | |
17 | 19 | |
18 | 20 | # Include all test java files. |
19 | 21 | LOCAL_SRC_FILES := $(call all-java-files-under, src) |
@@ -0,0 +1,235 @@ | ||
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 | + | |
17 | +package com.android.settings; | |
18 | + | |
19 | +import android.app.Activity; | |
20 | +import android.app.admin.DevicePolicyManager; | |
21 | +import android.content.Context; | |
22 | +import android.content.Intent; | |
23 | +import android.content.pm.PackageManager; | |
24 | +import android.support.test.filters.MediumTest; | |
25 | +import android.support.test.rule.ActivityTestRule; | |
26 | +import android.support.test.runner.AndroidJUnit4; | |
27 | +import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry; | |
28 | +import android.support.test.runner.lifecycle.Stage; | |
29 | +import android.support.test.uiautomator.UiDevice; | |
30 | +import android.support.test.uiautomator.UiObject; | |
31 | +import android.support.test.uiautomator.UiSelector; | |
32 | + | |
33 | +import android.text.format.DateUtils; | |
34 | +import android.view.KeyEvent; | |
35 | + | |
36 | +import com.android.settings.R; | |
37 | + | |
38 | +import java.util.Collection; | |
39 | + | |
40 | +import org.junit.After; | |
41 | +import org.junit.Assert; | |
42 | +import org.junit.Before; | |
43 | +import org.junit.Rule; | |
44 | +import org.junit.Test; | |
45 | +import org.junit.runner.RunWith; | |
46 | + | |
47 | +import static android.support.test.InstrumentationRegistry.getInstrumentation; | |
48 | +import static com.google.common.truth.Truth.assertThat; | |
49 | +import 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 | |
62 | +public 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 | +} |