From: Chih-Wei Huang Date: Tue, 18 Jul 2017 05:53:52 +0000 (+0800) Subject: Merge tag 'android-7.1.2_r27' into nougat-x86 X-Git-Tag: android-x86-7.1-r1~1 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=4caa0889c031a5d0db80e8fce85f495fe4382139;hp=91c8ff45b0e076cb6591fb2367eb7545fff97a19;p=android-x86%2Fpackages-apps-Settings.git Merge tag 'android-7.1.2_r27' into nougat-x86 Android 7.1.2 release 27 --- diff --git a/src/com/android/settings/ChooseLockGeneric.java b/src/com/android/settings/ChooseLockGeneric.java index e0590415f4..1a70f541b4 100644 --- a/src/com/android/settings/ChooseLockGeneric.java +++ b/src/com/android/settings/ChooseLockGeneric.java @@ -167,16 +167,6 @@ public class ChooseLockGeneric extends SettingsActivity { ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, false); mForChangeCredRequiredForBoot = getArguments() != null && getArguments().getBoolean( ChooseLockSettingsHelper.EXTRA_KEY_FOR_CHANGE_CRED_REQUIRED_FOR_BOOT); - if (mIsSetNewPassword) { - // In ACTION_SET_NEW_PARENT_PROFILE_PASSWORD or ACTION_SET_NEW_PASSWORD, the user - // will be asked to confirm the password if one has been set. - // On fingerprint supported device, fingerprint options are represented in the - // options. If the user chooses to skip fingerprint setup, ChooseLockGeneric is - // relaunched to only show options without fingerprint. In this case, we shouldn't - // ask the user to confirm the password again. - mPasswordConfirmed = getActivity().getIntent().getBooleanExtra( - PASSWORD_CONFIRMED, false); - } if (savedInstanceState != null) { mPasswordConfirmed = savedInstanceState.getBoolean(PASSWORD_CONFIRMED); @@ -247,11 +237,12 @@ public class ChooseLockGeneric extends SettingsActivity { showFactoryResetProtectionWarningDialog(key); return true; } else if (KEY_SKIP_FINGERPRINT.equals(key)) { - Intent chooseLockGenericIntent = new Intent(getActivity(), ChooseLockGeneric.class); + Intent chooseLockGenericIntent = new Intent(getActivity(), + ChooseLockGeneric.InternalActivity.class); chooseLockGenericIntent.setAction(getIntent().getAction()); // Forward the target user id to ChooseLockGeneric. chooseLockGenericIntent.putExtra(Intent.EXTRA_USER_ID, mUserId); - chooseLockGenericIntent.putExtra(PASSWORD_CONFIRMED, mPasswordConfirmed); + chooseLockGenericIntent.putExtra(CONFIRM_CREDENTIALS, !mPasswordConfirmed); startActivityForResult(chooseLockGenericIntent, SKIP_FINGERPRINT_REQUEST); return true; } else { diff --git a/src/com/android/settings/accounts/ManageAccountsSettings.java b/src/com/android/settings/accounts/ManageAccountsSettings.java index 80d6bc1459..c84e0f463e 100644 --- a/src/com/android/settings/accounts/ManageAccountsSettings.java +++ b/src/com/android/settings/accounts/ManageAccountsSettings.java @@ -36,6 +36,7 @@ import android.os.Bundle; import android.os.UserHandle; import android.support.v7.preference.Preference; import android.support.v7.preference.Preference.OnPreferenceClickListener; +import android.support.v7.preference.PreferenceGroup; import android.support.v7.preference.PreferenceScreen; import android.util.Log; import android.view.LayoutInflater; @@ -83,7 +84,7 @@ public class ManageAccountsSettings extends AccountPreferenceBase // If an account type is set, then show only accounts of that type private String mAccountType; - // Temporary hack, to deal with backward compatibility + // Temporary hack, to deal with backward compatibility // mFirstAccount is used for the injected preferences private Account mFirstAccount; @@ -440,15 +441,18 @@ public class ManageAccountsSettings extends AccountPreferenceBase } /** - * Filters through the preference list provided by GoogleLoginService. + * Recursively filters through the preference list provided by GoogleLoginService. * * This method removes all the invalid intent from the list, adds account name as extra into the * intent, and hack the location settings to start it as a fragment. */ - private void updatePreferenceIntents(PreferenceScreen prefs) { + private void updatePreferenceIntents(PreferenceGroup prefs) { final PackageManager pm = getActivity().getPackageManager(); for (int i = 0; i < prefs.getPreferenceCount();) { Preference pref = prefs.getPreference(i); + if (pref instanceof PreferenceGroup) { + updatePreferenceIntents((PreferenceGroup) pref); + } Intent intent = pref.getIntent(); if (intent != null) { // Hack. Launch "Location" as fragment instead of as activity. @@ -497,8 +501,8 @@ public class ManageAccountsSettings extends AccountPreferenceBase } else { Log.e(TAG, "Refusing to launch authenticator intent because" - + "it exploits Settings permissions: " - + prefIntent); + + " it exploits Settings permissions: " + + prefIntent); } return true; } @@ -518,20 +522,26 @@ public class ManageAccountsSettings extends AccountPreferenceBase private boolean isSafeIntent(PackageManager pm, Intent intent) { AuthenticatorDescription authDesc = mAuthenticatorHelper.getAccountTypeDescription(mAccountType); - ResolveInfo resolveInfo = pm.resolveActivity(intent, 0); + ResolveInfo resolveInfo = + pm.resolveActivityAsUser(intent, 0, mUserHandle.getIdentifier()); if (resolveInfo == null) { return false; } ActivityInfo resolvedActivityInfo = resolveInfo.activityInfo; ApplicationInfo resolvedAppInfo = resolvedActivityInfo.applicationInfo; try { + if (resolvedActivityInfo.exported) { + if (resolvedActivityInfo.permission == null) { + return true; // exported activity without permission. + } else if (pm.checkPermission(resolvedActivityInfo.permission, + authDesc.packageName) == PackageManager.PERMISSION_GRANTED) { + return true; + } + } ApplicationInfo authenticatorAppInf = pm.getApplicationInfo(authDesc.packageName, 0); - return resolvedActivityInfo.exported - || resolvedAppInfo.uid == authenticatorAppInf.uid; + return resolvedAppInfo.uid == authenticatorAppInf.uid; } catch (NameNotFoundException e) { - Log.e(TAG, - "Intent considered unsafe due to exception.", - e); + Log.e(TAG, "Intent considered unsafe due to exception.", e); return false; } } diff --git a/tests/app/Android.mk b/tests/app/Android.mk index 979c27d93a..2df9525d2d 100644 --- a/tests/app/Android.mk +++ b/tests/app/Android.mk @@ -9,11 +9,13 @@ LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle LOCAL_STATIC_JAVA_LIBRARIES := \ android-support-test \ + guava \ mockito-target \ espresso-core \ espresso-contrib-nodep \ espresso-intents-nodep \ - ub-uiautomator + ub-uiautomator \ + truth-prebuilt # Include all test java files. LOCAL_SRC_FILES := $(call all-java-files-under, src) diff --git a/tests/app/src/com/android/settings/ChooseLockGenericTest.java b/tests/app/src/com/android/settings/ChooseLockGenericTest.java new file mode 100644 index 0000000000..dee6697b7f --- /dev/null +++ b/tests/app/src/com/android/settings/ChooseLockGenericTest.java @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings; + +import android.app.Activity; +import android.app.admin.DevicePolicyManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.support.test.filters.MediumTest; +import android.support.test.rule.ActivityTestRule; +import android.support.test.runner.AndroidJUnit4; +import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry; +import android.support.test.runner.lifecycle.Stage; +import android.support.test.uiautomator.UiDevice; +import android.support.test.uiautomator.UiObject; +import android.support.test.uiautomator.UiSelector; + +import android.text.format.DateUtils; +import android.view.KeyEvent; + +import com.android.settings.R; + +import java.util.Collection; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static android.support.test.InstrumentationRegistry.getInstrumentation; +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * Tests for {@link ChooseLockGenericTest} + * + * m SettingsTests && + * adb install \ + * -r -g ${ANDROID_PRODUCT_OUT}/data/app/SettingsTests/SettingsTests.apk && + * adb shell am instrument -e class com.android.settings.ChooseLockGenericTest \ + * -w com.android.settings.tests/android.support.test.runner.AndroidJUnitRunner + */ +@RunWith(AndroidJUnit4.class) +@MediumTest +public class ChooseLockGenericTest { + private static final long TIMEOUT = 5 * DateUtils.SECOND_IN_MILLIS; + private static final Intent PHISHING_ATTACK_INTENT = new Intent() + .putExtra("confirm_credentials", false) + .putExtra("password_confirmed", true); + + private UiDevice mDevice; + private Context mTargetContext; + private String mSettingPackage; + private PackageManager mPackageManager; + @Rule + public ActivityTestRule mChooseLockGenericActivityRule = + new ActivityTestRule<>( + ChooseLockGeneric.class, + true /* enable touch at launch */, + false /* don't launch at every test */); + + @Before + public void setUp() throws Exception { + mDevice = UiDevice.getInstance(getInstrumentation()); + mTargetContext = getInstrumentation().getTargetContext(); + mSettingPackage = mTargetContext.getPackageName(); + mPackageManager = mTargetContext.getPackageManager(); + + setPassword(); + } + + @After + public void tearDown() throws Exception { + clearPassword(); + } + + @Test + public void testConfirmLockPasswordShown_deviceWithPassword() throws Exception, Throwable { + // GIVEN a PIN password is set on this device at set up. + // WHEN ChooseLockGeneric is launched with no extras. + mChooseLockGenericActivityRule.launchActivity(null /* No extras */); + // THEN ConfirmLockPassword.InternalActivity is shown. + assertThat(getCurrentActivity()).isInstanceOf(ConfirmLockPassword.InternalActivity.class); + } + + @Test + public void testConfirmLockPasswordShown_deviceWithPassword_phishingAttack() + throws Exception, Throwable { + // GIVEN a PIN password is set on this device at set up. + // WHEN ChooseLockGeneric is launched with extras to by-pass lock password confirmation. + mChooseLockGenericActivityRule.launchActivity(PHISHING_ATTACK_INTENT); + // THEN ConfirmLockPassword.InternalActivity is still shown. + assertThat(getCurrentActivity()).isInstanceOf(ConfirmLockPassword.InternalActivity.class); + } + + private Activity getCurrentActivity() throws Throwable { + getInstrumentation().waitForIdleSync(); + final Activity[] activity = new Activity[1]; + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + Collection activities = ActivityLifecycleMonitorRegistry.getInstance() + .getActivitiesInStage(Stage.RESUMED); + activity[0] = activities.iterator().next(); + } + }); + return activity[0]; + } + + private void launchNewPassword() throws Exception { + Intent newPasswordIntent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD) + .setPackage(mSettingPackage) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + getInstrumentation().getContext().startActivity(newPasswordIntent); + mDevice.waitForIdle(); + } + + /** Sets a PIN password, 12345, for testing. */ + private void setPassword() throws Exception { + launchNewPassword(); + + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { + // Set "lock_none", but it actually means we don't want to enroll a fingerprint. + UiObject view = new UiObject( + new UiSelector().resourceId(mSettingPackage + ":id/lock_none")); + assertTrue("lock_none", view.waitForExists(TIMEOUT)); + view.click(); + mDevice.waitForIdle(); + } + + // Pick PIN from the option list + UiObject view = new UiObject(new UiSelector() + .resourceId(mSettingPackage + ":id/lock_pin")); + assertTrue("lock_pin", view.waitForExists(TIMEOUT)); + view.click(); + mDevice.waitForIdle(); + + // Ignore any interstitial options + view = new UiObject(new UiSelector() + .resourceId(mSettingPackage + ":id/encrypt_dont_require_password")); + if (view.waitForExists(TIMEOUT)) { + view.click(); + mDevice.waitForIdle(); + } + + // Yes, we really want to + view = new UiObject(new UiSelector() + .resourceId(mSettingPackage + ":id/next_button")); + if (view.waitForExists(TIMEOUT)) { + view.click(); + mDevice.waitForIdle(); + } + + // Set our PIN + view = new UiObject(new UiSelector() + .resourceId(mSettingPackage + ":id/password_entry")); + assertTrue("password_entry", view.waitForExists(TIMEOUT)); + + // Enter it twice to confirm + enterTestPin(); + enterTestPin(); + + mDevice.pressBack(); + } + + /** Clears the previous set PIN password. */ + private void clearPassword() throws Exception { + launchNewPassword(); + + // Enter current PIN + UiObject view = new UiObject( + new UiSelector().resourceId(mSettingPackage + ":id/password_entry")); + if (!view.waitForExists(TIMEOUT)) { + // Odd, maybe there is a crash dialog showing; try dismissing it + mDevice.pressBack(); + mDevice.waitForIdle(); + + assertTrue("password_entry", view.waitForExists(TIMEOUT)); + } + + enterTestPin(); + + // Set back to "none" + view = new UiObject(new UiSelector().resourceId(mSettingPackage + ":id/lock_none")); + assertTrue("lock_none", view.waitForExists(TIMEOUT)); + view.click(); + mDevice.waitForIdle(); + + // Yes, we really want "none" if prompted again + view = new UiObject(new UiSelector().resourceId(mSettingPackage + ":id/lock_none")); + if (view.waitForExists(TIMEOUT)) { + view.click(); + mDevice.waitForIdle(); + } + + // Yes, we really want to + view = new UiObject(new UiSelector() + .resourceId("android:id/button1")); + if (view.waitForExists(TIMEOUT)) { + view.click(); + mDevice.waitForIdle(); + } + + mDevice.pressBack(); + } + + private void enterTestPin() throws Exception { + mDevice.waitForIdle(); + mDevice.pressKeyCode(KeyEvent.KEYCODE_1); + mDevice.pressKeyCode(KeyEvent.KEYCODE_2); + mDevice.pressKeyCode(KeyEvent.KEYCODE_3); + mDevice.pressKeyCode(KeyEvent.KEYCODE_4); + mDevice.pressKeyCode(KeyEvent.KEYCODE_5); + mDevice.waitForIdle(); + mDevice.pressEnter(); + mDevice.waitForIdle(); + } +}