From b65c4c3e254077a11e2b4e84039ee59271c20cee Mon Sep 17 00:00:00 2001 From: tiansiming Date: Wed, 6 Dec 2017 21:29:15 +0800 Subject: [PATCH 01/11] Fix NPE when press ttsEngines settings icon settingsIntent is obtained by method getSettingsIntent in TtsEngines.java, and it has the chance to return null directly in which case will lead to NPE crash when we startActivity in TextToSpeechSettings.java with a null intent. So, a judgement here makes sense. Bug:https://issuetracker.google.com/issues/70266990 Test: 1) Install the apk "partner-BaiduSpeechService.apk" offered in the Bug link above. 2) Go to Settings -> Languages & input -> Advance -> Text-to-speech output 3) Choose "Duer voice engine 3.0" as the preferred engine 4) Press the settings icon in the right Change-Id: I497a745ce62989f3ff1aee661648e90af25fb99e Signed-off-by: tiansiming --- src/com/android/settings/tts/TextToSpeechSettings.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/tts/TextToSpeechSettings.java b/src/com/android/settings/tts/TextToSpeechSettings.java index e8823a6388e..c5669676371 100644 --- a/src/com/android/settings/tts/TextToSpeechSettings.java +++ b/src/com/android/settings/tts/TextToSpeechSettings.java @@ -777,7 +777,11 @@ public class TextToSpeechSettings extends SettingsPreferenceFragment if (KEY_TTS_ENGINE_PREFERENCE.equals(p.getKey())) { EngineInfo info = mEnginesHelper.getEngineInfo(mCurrentEngine); final Intent settingsIntent = mEnginesHelper.getSettingsIntent(info.name); - startActivity(settingsIntent); + if (settingsIntent != null) { + startActivity(settingsIntent); + } else { + Log.e(TAG, "settingsIntent is null"); + } } } From 924e0e215442fa031360ced96789376545be55e2 Mon Sep 17 00:00:00 2001 From: Pavel Grafov Date: Thu, 26 Oct 2017 17:01:53 +0100 Subject: [PATCH 02/11] Respect DISALLOW_UNIFIED_PASSWORD in Settings. Bug: 63909482 Test: make ROBOTEST_FILTER=SecuritySettingsTest RunSettingsRoboTests Test: make cts-verifier Change-Id: Ia89501d5d5339c1340bee36a9bfb8ef72c4ee9b1 --- res/xml/security_settings_unification.xml | 2 +- .../settings/security/SecuritySettings.java | 31 +++++----- .../security/SecuritySettingsTest.java | 57 ++++++++++++++++++- .../testutils/shadow/ShadowUserManager.java | 18 +++++- 4 files changed, 92 insertions(+), 16 deletions(-) diff --git a/res/xml/security_settings_unification.xml b/res/xml/security_settings_unification.xml index 43b8b09be6c..b9c59e599f7 100644 --- a/res/xml/security_settings_unification.xml +++ b/res/xml/security_settings_unification.xml @@ -18,7 +18,7 @@ xmlns:settings="http://schemas.android.com/apk/res-auto" android:title="@string/security_settings_title"> - { + if (compliant) { + parentFragment.launchConfirmDeviceLockForUnification(); + } else { + parentFragment.unifyUncompliantLocks(); } } ) diff --git a/tests/robotests/src/com/android/settings/security/SecuritySettingsTest.java b/tests/robotests/src/com/android/settings/security/SecuritySettingsTest.java index 1ecdd0f5c7c..231cce95a82 100644 --- a/tests/robotests/src/com/android/settings/security/SecuritySettingsTest.java +++ b/tests/robotests/src/com/android/settings/security/SecuritySettingsTest.java @@ -17,9 +17,11 @@ package com.android.settings.security; import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -29,7 +31,9 @@ import android.app.Activity; import android.content.Context; import android.content.pm.PackageManager; import android.hardware.fingerprint.FingerprintManager; +import android.os.UserHandle; import android.os.UserManager; +import android.os.UserManager.EnforcingUser; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceGroup; import android.support.v7.preference.PreferenceScreen; @@ -43,6 +47,9 @@ import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.XmlTestUtils; import com.android.settings.testutils.shadow.ShadowLockPatternUtils; +import com.android.settings.testutils.shadow.ShadowUserManager; +import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; +import com.android.settingslib.RestrictedSwitchPreference; import org.junit.Before; import org.junit.Test; @@ -55,12 +62,15 @@ import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowApplication; import org.robolectric.util.ReflectionHelpers; +import java.util.Arrays; +import java.util.Collections; import java.util.List; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, shadows = { - ShadowLockPatternUtils.class + ShadowLockPatternUtils.class, + ShadowUserManager.class, }) public class SecuritySettingsTest { @@ -187,4 +197,49 @@ public class SecuritySettingsTest { assertThat(keys).containsAllIn(niks); } + + @Test + public void testUnifyLockRestriction() { + // Set up instance under test. + final Context context = spy(RuntimeEnvironment.application); + final SecuritySettings securitySettings = spy(new SecuritySettings()); + when(securitySettings.getContext()).thenReturn(context); + + final int userId = 123; + ReflectionHelpers.setField(securitySettings, "mProfileChallengeUserId", userId); + + final LockPatternUtils utils = mock(LockPatternUtils.class); + when(utils.isSeparateProfileChallengeEnabled(userId)).thenReturn(true); + ReflectionHelpers.setField(securitySettings, "mLockPatternUtils", utils); + + final RestrictedSwitchPreference unifyProfile = mock(RestrictedSwitchPreference.class); + ReflectionHelpers.setField(securitySettings, "mUnifyProfile", unifyProfile); + + // Pretend that no admins enforce the restriction. + ShadowUserManager.getShadow().setUserRestrictionSources( + UserManager.DISALLOW_UNIFIED_PASSWORD, + UserHandle.of(userId), + Collections.emptyList()); + + securitySettings.updateUnificationPreference(); + + verify(unifyProfile).setDisabledByAdmin(null); + + reset(unifyProfile); + + // Pretend that the restriction is enforced by several admins. Having just one would + // require more mocking of implementation details. + final EnforcingUser enforcer1 = new EnforcingUser( + userId, UserManager.RESTRICTION_SOURCE_PROFILE_OWNER); + final EnforcingUser enforcer2 = new EnforcingUser( + UserHandle.USER_SYSTEM, UserManager.RESTRICTION_SOURCE_DEVICE_OWNER); + ShadowUserManager.getShadow().setUserRestrictionSources( + UserManager.DISALLOW_UNIFIED_PASSWORD, + UserHandle.of(userId), + Arrays.asList(enforcer1, enforcer2)); + + securitySettings.updateUnificationPreference(); + + verify(unifyProfile).setDisabledByAdmin(EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN); + } } diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java index f24837dfa72..2df2b043ca3 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.pm.UserInfo; import android.os.UserHandle; import android.os.UserManager; +import android.os.UserManager.EnforcingUser; import android.util.SparseArray; import org.robolectric.RuntimeEnvironment; @@ -31,14 +32,18 @@ import org.robolectric.shadow.api.Shadow; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; @Implements(UserManager.class) public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager { private SparseArray mUserInfos = new SparseArray<>(); private boolean mAdminUser; - private List mRestrictions = new ArrayList<>(); + private final List mRestrictions = new ArrayList<>(); + private final Map> mRestrictionSources = new HashMap<>(); + public void setIsAdminUser(boolean isAdminUser) { mAdminUser = isAdminUser; @@ -91,4 +96,15 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager return (ShadowUserManager) Shadow.extract( RuntimeEnvironment.application.getSystemService(UserManager.class)); } + + @Implementation + public List getUserRestrictionSources( + String restrictionKey, UserHandle userHandle) { + return mRestrictionSources.get(restrictionKey + userHandle.getIdentifier()); + } + + public void setUserRestrictionSources( + String restrictionKey, UserHandle userHandle, List enforcers) { + mRestrictionSources.put(restrictionKey + userHandle.getIdentifier(), enforcers); + } } From 73abbc19c3545c1f48533331b45b39fd6584a269 Mon Sep 17 00:00:00 2001 From: jackqdyulei Date: Wed, 13 Dec 2017 10:18:23 -0800 Subject: [PATCH 03/11] Add PowerUsageSummaryLegacy to Restricted list. Then in guest/secondary user it is also possible to show battery settings. Bug: 70606597 Test: Manual Change-Id: I7993abbf988926d0890229e9c14c779906be6e23 --- src/com/android/settings/core/gateway/SettingsGateway.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java index ecef57e70c3..b2ad2cd89d2 100644 --- a/src/com/android/settings/core/gateway/SettingsGateway.java +++ b/src/com/android/settings/core/gateway/SettingsGateway.java @@ -271,6 +271,7 @@ public class SettingsGateway { Settings.SoundSettingsActivity.class.getName(), Settings.StorageDashboardActivity.class.getName(), Settings.PowerUsageSummaryActivity.class.getName(), + Settings.PowerUsageSummaryLegacyActivity.class.getName(), Settings.UserAndAccountDashboardActivity.class.getName(), Settings.SecuritySettingsActivity.class.getName(), Settings.AccessibilitySettingsActivity.class.getName(), From 11bfef660c8249869441fae5b8a20f6206e55c27 Mon Sep 17 00:00:00 2001 From: Peter Visontay Date: Wed, 13 Dec 2017 17:31:41 +0000 Subject: [PATCH 04/11] Add a unit test for the AppOps permission for displaying UI overlays. The test logic is the same as ExetrnalSourcesSettingsTest but I moved/renamed some methods so both AppOps tests can use them. Test: make -j32 SettingsUnitTests && adb install -r out/target/product/marlin/data/app/SettingsUnitTests/SettingsUnitTests.apk && adb shell am instrument -w -e class com.android.settings.applications.ExternalSourcesSettingsTest com.android.settings.tests.unit/android.support.test.runner.AndroidJUnitRunner Test: same as above, but for DrawOverlaySettingsTest Change-Id: Ifc0f0985a4cb4a82a00839aeb4cd4216c777250e --- tests/unit/AndroidManifest.xml | 1 + .../applications/AppOpsSettingsTest.java | 205 ++++++++++++++++++ .../applications/DrawOverlaySettingsTest.java | 35 +++ .../ExternalSourcesSettingsTest.java | 175 +-------------- 4 files changed, 246 insertions(+), 170 deletions(-) create mode 100644 tests/unit/src/com/android/settings/applications/AppOpsSettingsTest.java create mode 100644 tests/unit/src/com/android/settings/applications/DrawOverlaySettingsTest.java diff --git a/tests/unit/AndroidManifest.xml b/tests/unit/AndroidManifest.xml index 65ed661eff8..b22c01bde6e 100644 --- a/tests/unit/AndroidManifest.xml +++ b/tests/unit/AndroidManifest.xml @@ -23,6 +23,7 @@ + diff --git a/tests/unit/src/com/android/settings/applications/AppOpsSettingsTest.java b/tests/unit/src/com/android/settings/applications/AppOpsSettingsTest.java new file mode 100644 index 00000000000..d89d4a3b059 --- /dev/null +++ b/tests/unit/src/com/android/settings/applications/AppOpsSettingsTest.java @@ -0,0 +1,205 @@ +/* + * 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.applications; + +import android.app.AppOpsManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.net.Uri; +import android.os.UserHandle; +import android.os.UserManager; +import android.support.test.InstrumentationRegistry; +import android.support.test.uiautomator.By; +import android.support.test.uiautomator.BySelector; +import android.support.test.uiautomator.Direction; +import android.support.test.uiautomator.UiDevice; +import android.support.test.uiautomator.UiObject2; +import android.support.test.uiautomator.Until; +import android.support.v7.widget.RecyclerView; +import android.widget.Switch; +import android.widget.TextView; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +import static android.app.AppOpsManager.MODE_ALLOWED; +import static android.app.AppOpsManager.MODE_DEFAULT; +import static android.app.AppOpsManager.MODE_ERRORED; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * An abstract parent for testing settings activities that manage an AppOps permission. + */ +abstract public class AppOpsSettingsTest { + private static final String WM_DISMISS_KEYGUARD_COMMAND = "wm dismiss-keyguard"; + private static final long START_ACTIVITY_TIMEOUT = 5000; + + private Context mContext; + private UiDevice mUiDevice; + private PackageManager mPackageManager; + private AppOpsManager mAppOpsManager; + private List mProfiles; + private String mPackageName; + + // These depend on which app op's settings UI is being tested. + private final String mActivityAction; + private final int mAppOpCode; + + protected AppOpsSettingsTest(String activityAction, int appOpCode) { + mActivityAction = activityAction; + mAppOpCode = appOpCode; + } + + @Before + public void setUp() throws Exception { + mContext = InstrumentationRegistry.getTargetContext(); + mPackageName = InstrumentationRegistry.getContext().getPackageName(); + mPackageManager = mContext.getPackageManager(); + mAppOpsManager = mContext.getSystemService(AppOpsManager.class); + mProfiles = mContext.getSystemService(UserManager.class).getProfiles(UserHandle.myUserId()); + resetAppOpModeForAllProfiles(); + mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + mUiDevice.wakeUp(); + mUiDevice.executeShellCommand(WM_DISMISS_KEYGUARD_COMMAND); + } + + private void resetAppOpModeForAllProfiles() throws Exception { + for (UserInfo user : mProfiles) { + final int uid = mPackageManager.getPackageUidAsUser(mPackageName, user.id); + mAppOpsManager.setMode(mAppOpCode, uid, mPackageName, MODE_DEFAULT); + } + } + + /** + * Creates an intent for showing the permission settings for all apps. + */ + private Intent createManageAllAppsIntent() { + return new Intent(mActivityAction); + } + + /** + * Creates an intent for showing the permission setting for a single app. + */ + private Intent createManageSingleAppIntent(String packageName) { + final Intent intent = createManageAllAppsIntent(); + intent.setData(Uri.parse("package:" + packageName)); + return intent; + } + + private String getApplicationLabel(String packageName) throws Exception { + final ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 0); + return mPackageManager.getApplicationLabel(info).toString(); + } + + private UiObject2 findAndVerifySwitchState(boolean checked) { + final BySelector switchSelector = By.clazz(Switch.class).res("android:id/switch_widget"); + final UiObject2 switchPref = mUiDevice.wait(Until.findObject(switchSelector), + START_ACTIVITY_TIMEOUT); + assertNotNull("Switch not shown", switchPref); + assertTrue("Switch in invalid state", switchPref.isChecked() == checked); + return switchPref; + } + + @Test + public void testAppList() throws Exception { + final String testAppLabel = getApplicationLabel(mPackageName); + + mContext.startActivity(createManageAllAppsIntent()); + final BySelector preferenceListSelector = + By.clazz(RecyclerView.class).res("com.android.settings:id/apps_list"); + final UiObject2 preferenceList = mUiDevice.wait(Until.findObject(preferenceListSelector), + START_ACTIVITY_TIMEOUT); + assertNotNull("App list not shown", preferenceList); + + final BySelector appLabelTextViewSelector = By.clazz(TextView.class) + .res("android:id/title") + .text(testAppLabel); + List listOfMatchingTextViews; + do { + listOfMatchingTextViews = preferenceList.findObjects(appLabelTextViewSelector); + // assuming the number of profiles will be sufficiently small so that all the entries + // for the same package will fit in one screen at some time during the scroll. + } while (listOfMatchingTextViews.size() != mProfiles.size() && + preferenceList.scroll(Direction.DOWN, 0.2f)); + assertEquals("Test app not listed for each profile", mProfiles.size(), + listOfMatchingTextViews.size()); + + for (UiObject2 matchingObject : listOfMatchingTextViews) { + matchingObject.click(); + findAndVerifySwitchState(true); + mUiDevice.pressBack(); + } + } + + private void testAppDetailScreenForAppOp(int appOpMode, int userId) throws Exception { + final String testAppLabel = getApplicationLabel(mPackageName); + final BySelector appDetailTitleSelector = By.clazz(TextView.class) + .res("com.android.settings:id/app_detail_title") + .text(testAppLabel); + + mAppOpsManager.setMode(mAppOpCode, + mPackageManager.getPackageUidAsUser(mPackageName, userId), mPackageName, appOpMode); + mContext.startActivityAsUser(createManageSingleAppIntent(mPackageName), + UserHandle.of(userId)); + mUiDevice.wait(Until.findObject(appDetailTitleSelector), START_ACTIVITY_TIMEOUT); + findAndVerifySwitchState(appOpMode == MODE_ALLOWED || appOpMode == MODE_DEFAULT); + mUiDevice.pressBack(); + } + + @Test + public void testSingleApp() throws Exception { + // App op MODE_DEFAULT is already tested in #testAppList + for (UserInfo user : mProfiles) { + testAppDetailScreenForAppOp(MODE_ALLOWED, user.id); + testAppDetailScreenForAppOp(MODE_ERRORED, user.id); + } + } + + private void testSwitchToggle(int fromAppOp, int toAppOp) throws Exception { + final int packageUid = mPackageManager.getPackageUid(mPackageName, 0); + final boolean initialState = (fromAppOp == MODE_ALLOWED || fromAppOp == MODE_DEFAULT); + + mAppOpsManager.setMode(mAppOpCode, packageUid, mPackageName, fromAppOp); + mContext.startActivity(createManageSingleAppIntent(mPackageName)); + final UiObject2 switchPref = findAndVerifySwitchState(initialState); + switchPref.click(); + Thread.sleep(1000); + assertEquals("Toggling switch did not change app op", toAppOp, + mAppOpsManager.checkOpNoThrow(mAppOpCode, packageUid, + mPackageName)); + mUiDevice.pressBack(); + } + + @Test + public void testIfSwitchTogglesAppOp() throws Exception { + testSwitchToggle(MODE_ALLOWED, MODE_ERRORED); + testSwitchToggle(MODE_ERRORED, MODE_ALLOWED); + } + + @After + public void tearDown() throws Exception { + mUiDevice.pressHome(); + resetAppOpModeForAllProfiles(); + } +} diff --git a/tests/unit/src/com/android/settings/applications/DrawOverlaySettingsTest.java b/tests/unit/src/com/android/settings/applications/DrawOverlaySettingsTest.java new file mode 100644 index 00000000000..24760ae7c2e --- /dev/null +++ b/tests/unit/src/com/android/settings/applications/DrawOverlaySettingsTest.java @@ -0,0 +1,35 @@ +/* + * 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.applications; + +import android.app.AppOpsManager; +import android.provider.Settings; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class DrawOverlaySettingsTest extends AppOpsSettingsTest { + + public DrawOverlaySettingsTest() { + super(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, AppOpsManager.OP_SYSTEM_ALERT_WINDOW); + } + + // Test cases are in the superclass. +} \ No newline at end of file diff --git a/tests/unit/src/com/android/settings/applications/ExternalSourcesSettingsTest.java b/tests/unit/src/com/android/settings/applications/ExternalSourcesSettingsTest.java index 22d4bf6e04e..6ac21afcb8c 100644 --- a/tests/unit/src/com/android/settings/applications/ExternalSourcesSettingsTest.java +++ b/tests/unit/src/com/android/settings/applications/ExternalSourcesSettingsTest.java @@ -16,186 +16,21 @@ package com.android.settings.applications; -import static android.app.AppOpsManager.MODE_ALLOWED; -import static android.app.AppOpsManager.MODE_DEFAULT; -import static android.app.AppOpsManager.MODE_ERRORED; -import static android.app.AppOpsManager.OP_REQUEST_INSTALL_PACKAGES; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - import android.app.AppOpsManager; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.UserInfo; -import android.net.Uri; -import android.os.UserHandle; -import android.os.UserManager; import android.provider.Settings; -import android.support.test.InstrumentationRegistry; import android.support.test.filters.LargeTest; import android.support.test.runner.AndroidJUnit4; -import android.support.test.uiautomator.By; -import android.support.test.uiautomator.BySelector; -import android.support.test.uiautomator.Direction; -import android.support.test.uiautomator.UiDevice; -import android.support.test.uiautomator.UiObject2; -import android.support.test.uiautomator.Until; -import android.support.v7.widget.RecyclerView; -import android.widget.Switch; -import android.widget.TextView; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; import org.junit.runner.RunWith; -import java.util.List; - @RunWith(AndroidJUnit4.class) @LargeTest -public class ExternalSourcesSettingsTest { +public class ExternalSourcesSettingsTest extends AppOpsSettingsTest { - private static final String TAG = ExternalSourcesSettingsTest.class.getSimpleName(); - private static final String WM_DISMISS_KEYGUARD_COMMAND = "wm dismiss-keyguard"; - private static final long START_ACTIVITY_TIMEOUT = 5000; - - private Context mContext; - private UiDevice mUiDevice; - private PackageManager mPackageManager; - private AppOpsManager mAppOpsManager; - private List mProfiles; - private String mPackageName; - - @Before - public void setUp() throws Exception { - mContext = InstrumentationRegistry.getTargetContext(); - mPackageName = InstrumentationRegistry.getContext().getPackageName(); - mPackageManager = mContext.getPackageManager(); - mAppOpsManager = mContext.getSystemService(AppOpsManager.class); - mProfiles = mContext.getSystemService(UserManager.class).getProfiles(UserHandle.myUserId()); - resetAppOpModeForAllProfiles(); - mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); - mUiDevice.wakeUp(); - mUiDevice.executeShellCommand(WM_DISMISS_KEYGUARD_COMMAND); + public ExternalSourcesSettingsTest() { + super(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, + AppOpsManager.OP_REQUEST_INSTALL_PACKAGES); } - private void resetAppOpModeForAllProfiles() throws Exception { - for (UserInfo user : mProfiles) { - final int uid = mPackageManager.getPackageUidAsUser(mPackageName, user.id); - mAppOpsManager.setMode(OP_REQUEST_INSTALL_PACKAGES, uid, mPackageName, MODE_DEFAULT); - } - } - - private Intent createManageExternalSourcesListIntent() { - final Intent manageExternalSourcesIntent = new Intent(); - manageExternalSourcesIntent.setAction(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES); - return manageExternalSourcesIntent; - } - - private Intent createManageExternalSourcesAppIntent(String packageName) { - final Intent intent = createManageExternalSourcesListIntent(); - intent.setData(Uri.parse("package:" + packageName)); - return intent; - } - - private String getApplicationLabel(String packageName) throws Exception { - final ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 0); - return mPackageManager.getApplicationLabel(info).toString(); - } - - private UiObject2 findAndVerifySwitchState(boolean checked) { - final BySelector switchSelector = By.clazz(Switch.class).res("android:id/switch_widget"); - final UiObject2 switchPref = mUiDevice.wait(Until.findObject(switchSelector), - START_ACTIVITY_TIMEOUT); - assertNotNull("Switch not shown", switchPref); - assertTrue("Switch in invalid state", switchPref.isChecked() == checked); - return switchPref; - } - - @Test - public void testManageExternalSourcesList() throws Exception { - final String testAppLabel = getApplicationLabel(mPackageName); - - mContext.startActivity(createManageExternalSourcesListIntent()); - final BySelector preferenceListSelector = - By.clazz(RecyclerView.class).res("com.android.settings:id/apps_list"); - final UiObject2 preferenceList = mUiDevice.wait(Until.findObject(preferenceListSelector), - START_ACTIVITY_TIMEOUT); - assertNotNull("App list not shown", preferenceList); - - final BySelector appLabelTextViewSelector = By.clazz(TextView.class) - .res("android:id/title") - .text(testAppLabel); - List listOfMatchingTextViews; - do { - listOfMatchingTextViews = preferenceList.findObjects(appLabelTextViewSelector); - // assuming the number of profiles will be sufficiently small so that all the entries - // for the same package will fit in one screen at some time during the scroll. - } while (listOfMatchingTextViews.size() != mProfiles.size() && - preferenceList.scroll(Direction.DOWN, 0.2f)); - assertEquals("Test app not listed for each profile", mProfiles.size(), - listOfMatchingTextViews.size()); - - for (UiObject2 matchingObject : listOfMatchingTextViews) { - matchingObject.click(); - findAndVerifySwitchState(true); - mUiDevice.pressBack(); - } - } - - private void testAppDetailScreenForAppOp(int appOpMode, int userId) throws Exception { - final String testAppLabel = getApplicationLabel(mPackageName); - final BySelector appDetailTitleSelector = By.clazz(TextView.class) - .res("com.android.settings:id/app_detail_title") - .text(testAppLabel); - - mAppOpsManager.setMode(OP_REQUEST_INSTALL_PACKAGES, - mPackageManager.getPackageUidAsUser(mPackageName, userId), mPackageName, appOpMode); - mContext.startActivityAsUser(createManageExternalSourcesAppIntent(mPackageName), - UserHandle.of(userId)); - mUiDevice.wait(Until.findObject(appDetailTitleSelector), START_ACTIVITY_TIMEOUT); - findAndVerifySwitchState(appOpMode == MODE_ALLOWED || appOpMode == MODE_DEFAULT); - mUiDevice.pressBack(); - } - - @Test - public void testManageExternalSourcesForApp() throws Exception { - // App op MODE_DEFAULT is already tested in #testManageExternalSourcesList - for (UserInfo user : mProfiles) { - testAppDetailScreenForAppOp(MODE_ALLOWED, user.id); - testAppDetailScreenForAppOp(MODE_ERRORED, user.id); - } - } - - private void testSwitchToggle(int fromAppOp, int toAppOp) throws Exception { - final int packageUid = mPackageManager.getPackageUid(mPackageName, 0); - final boolean initialState = (fromAppOp == MODE_ALLOWED || fromAppOp == MODE_DEFAULT); - - mAppOpsManager.setMode(OP_REQUEST_INSTALL_PACKAGES, packageUid, mPackageName, fromAppOp); - mContext.startActivity(createManageExternalSourcesAppIntent(mPackageName)); - final UiObject2 switchPref = findAndVerifySwitchState(initialState); - switchPref.click(); - Thread.sleep(1000); - assertEquals("Toggling switch did not change app op", toAppOp, - mAppOpsManager.checkOpNoThrow(OP_REQUEST_INSTALL_PACKAGES, packageUid, - mPackageName)); - mUiDevice.pressBack(); - } - - @Test - public void testIfSwitchTogglesAppOp() throws Exception { - testSwitchToggle(MODE_ALLOWED, MODE_ERRORED); - testSwitchToggle(MODE_ERRORED, MODE_ALLOWED); - } - - @After - public void tearDown() throws Exception { - mUiDevice.pressHome(); - resetAppOpModeForAllProfiles(); - } + // Test cases are in the superclass. } From d11d08658ff07d594ff50d038dc1e5865327ae08 Mon Sep 17 00:00:00 2001 From: Ben Lin Date: Wed, 6 Dec 2017 11:31:55 -0800 Subject: [PATCH 05/11] Hide Location Mode from search results if it's not available. Test: make RunSettingsRoboTests ROBOTEST_FILTER=LocationModeTest all passes. Bug: 70283459 Change-Id: Id99d4a8f694b6f223ee8ead9701569840e333a9e --- .../settings/location/LocationMode.java | 5 ++ .../settings/location/LocationModeTest.java | 73 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 tests/robotests/src/com/android/settings/location/LocationModeTest.java diff --git a/src/com/android/settings/location/LocationMode.java b/src/com/android/settings/location/LocationMode.java index 34f082b226a..5931f9e364a 100644 --- a/src/com/android/settings/location/LocationMode.java +++ b/src/com/android/settings/location/LocationMode.java @@ -94,6 +94,11 @@ public class LocationMode extends DashboardFragment { return Arrays.asList(sir); } + @Override + protected boolean isPageSearchEnabled(Context context) { + return context.getResources().getBoolean(R.bool.config_location_mode_available); + } + @Override public List getPreferenceControllers(Context context) { diff --git a/tests/robotests/src/com/android/settings/location/LocationModeTest.java b/tests/robotests/src/com/android/settings/location/LocationModeTest.java new file mode 100644 index 00000000000..0e7a9d72ed0 --- /dev/null +++ b/tests/robotests/src/com/android/settings/location/LocationModeTest.java @@ -0,0 +1,73 @@ +/* + * 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.location; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.spy; + +import android.content.Context; +import android.provider.SearchIndexableResource; + +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.XmlTestUtils; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class LocationModeTest { + + private Context mContext; + private LocationMode mFragment; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + mFragment = new LocationMode(); + } + + @Test + public void testSearchIndexProvider_shouldIndexResource() { + final List indexRes = + mFragment.SEARCH_INDEX_DATA_PROVIDER.getXmlResourcesToIndex(mContext, + true /* enabled */); + + assertThat(indexRes).isNotNull(); + assertThat(indexRes.get(0).xmlResId).isEqualTo(mFragment.getPreferenceScreenResId()); + } + + @Test + @Config(qualifiers = "mcc999") + public void testSearchIndexProvider_ifPageDisabled_shouldNotIndexResource() { + final List niks = LocationMode.SEARCH_INDEX_DATA_PROVIDER + .getNonIndexableKeys(mContext); + final int xmlId = mFragment.getPreferenceScreenResId(); + + final List keys = XmlTestUtils.getKeysFromPreferenceXml(mContext, xmlId); + assertThat(niks).containsAllIn(keys); + } +} From e17c9406f088010a1bf234c8b593fbe774183393 Mon Sep 17 00:00:00 2001 From: gomo Date: Wed, 13 Dec 2017 03:44:09 -0800 Subject: [PATCH 06/11] Add developer option control for GNSS duty cycling. Duty Cycling control is one of the Android-P features. In Android-P the feature will be controlled via a Developer Options button with no public API. This CL adds the button and connect it to be read via the GnssLocationProivder to control the feature. Also Roboelectric test added. Bug: 64009176 Test: Tested on Android-P build and works as expected. Also Robolectric test added. Change-Id: Icc0d259719cf9034caf2c78c790b914ab4292597 --- res/values/strings.xml | 5 + res/xml/development_settings.xml | 5 + .../DevelopmentSettingsDashboardFragment.java | 1 + ...wMeasFullTrackingPreferenceController.java | 87 ++++++++++++ ...sFullTrackingPreferenceControllerTest.java | 124 ++++++++++++++++++ 5 files changed, 222 insertions(+) create mode 100644 src/com/android/settings/development/EnableGnssRawMeasFullTrackingPreferenceController.java create mode 100644 tests/robotests/src/com/android/settings/development/EnableGnssRawMeasFullTrackingPreferenceControllerTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 686cb724122..f5ca7b80e3d 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -9132,4 +9132,9 @@ This feature is not available on this device + + Force Full GnssMeasurement + + Disable GNSS duty-cycling, track all constellations and frequencies. + diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml index d1eb366724d..60efcab79cf 100644 --- a/res/xml/development_settings.xml +++ b/res/xml/development_settings.xml @@ -132,6 +132,11 @@ + + diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index 8f114fc4758..e736798f85c 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -446,6 +446,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra controllers.add(new ResizableActivityPreferenceController(context)); controllers.add(new FreeformWindowsPreferenceController(context)); controllers.add(new ShortcutManagerThrottlingPreferenceController(context)); + controllers.add(new EnableGnssRawMeasFullTrackingPreferenceController(context)); return controllers; } diff --git a/src/com/android/settings/development/EnableGnssRawMeasFullTrackingPreferenceController.java b/src/com/android/settings/development/EnableGnssRawMeasFullTrackingPreferenceController.java new file mode 100644 index 00000000000..09770f6c8e7 --- /dev/null +++ b/src/com/android/settings/development/EnableGnssRawMeasFullTrackingPreferenceController.java @@ -0,0 +1,87 @@ +/* + * 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.development; + +import android.content.Context; +import android.provider.Settings; +import android.support.annotation.VisibleForTesting; +import android.support.v14.preference.SwitchPreference; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.development.DeveloperOptionsPreferenceController; + +public class EnableGnssRawMeasFullTrackingPreferenceController extends + DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener, + PreferenceControllerMixin { + + private static final String ENABLE_GNSS_RAW_MEAS_FULL_TRACKING_KEY = + "enable_gnss_raw_meas_full_tracking"; + + static final int SETTING_VALUE_ON = 1; + static final int SETTING_VALUE_OFF = 0; + + private SwitchPreference mPreference; + + public EnableGnssRawMeasFullTrackingPreferenceController(Context context) { + super(context); + } + + @Override + public String getPreferenceKey() { + return ENABLE_GNSS_RAW_MEAS_FULL_TRACKING_KEY; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + + mPreference = (SwitchPreference) screen.findPreference(getPreferenceKey()); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + final boolean isEnabled = (Boolean) newValue; + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, + isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF); + + return true; + } + + @Override + public void updateState(Preference preference) { + final int enableGnssRawMeasFullTrackingMode = + Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, SETTING_VALUE_OFF); + mPreference.setChecked(enableGnssRawMeasFullTrackingMode != SETTING_VALUE_OFF); + } + + @Override + protected void onDeveloperOptionsSwitchEnabled() { + mPreference.setEnabled(true); + } + + @Override + protected void onDeveloperOptionsSwitchDisabled() { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, SETTING_VALUE_OFF); + mPreference.setEnabled(false); + mPreference.setChecked(false); + } +} diff --git a/tests/robotests/src/com/android/settings/development/EnableGnssRawMeasFullTrackingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/EnableGnssRawMeasFullTrackingPreferenceControllerTest.java new file mode 100644 index 00000000000..95fd1116ef5 --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/EnableGnssRawMeasFullTrackingPreferenceControllerTest.java @@ -0,0 +1,124 @@ +/* + * 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.development; + + +import static com.android.settings.development.EnableGnssRawMeasFullTrackingPreferenceController + .SETTING_VALUE_OFF; +import static com.android.settings.development.EnableGnssRawMeasFullTrackingPreferenceController + .SETTING_VALUE_ON; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.provider.Settings; +import android.support.v14.preference.SwitchPreference; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class EnableGnssRawMeasFullTrackingPreferenceControllerTest { + + @Mock + private SwitchPreference mPreference; + @Mock + private PreferenceScreen mPreferenceScreen; + + private Context mContext; + private EnableGnssRawMeasFullTrackingPreferenceController mController; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application; + mController = new EnableGnssRawMeasFullTrackingPreferenceController(mContext); + when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn( + mPreference); + mController.displayPreference(mPreferenceScreen); + } + + @Test + public void onPreferenceChange_settingEnabled_enableGnssRawMeasFullTrackingShouldBeOn() { + mController.onPreferenceChange(mPreference, true /* new value */); + + final int mode = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, -1 /* default */); + + assertThat(mode).isEqualTo(SETTING_VALUE_ON); + } + + @Test + public void onPreferenceChange_settingDisabled_enableGnssRawMeasFullTrackingShouldBeOff() { + mController.onPreferenceChange(mPreference, false /* new value */); + + final int mode = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, -1 /* default */); + + assertThat(mode).isEqualTo(SETTING_VALUE_OFF); + } + + @Test + public void updateState_settingDisabled_preferenceShouldNotBeChecked() { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, SETTING_VALUE_OFF); + mController.updateState(mPreference); + + verify(mPreference).setChecked(false); + } + + @Test + public void updateState_settingEnabled_preferenceShouldBeChecked() { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, SETTING_VALUE_ON); + mController.updateState(mPreference); + + verify(mPreference).setChecked(true); + } + + @Test + public void onDeveloperOptionsSwitchDisabled_shouldDisablePreference() { + mController.onDeveloperOptionsSwitchDisabled(); + + final int mode = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, -1 /* default */); + + assertThat(mode).isEqualTo(SETTING_VALUE_OFF); + verify(mPreference).setChecked(false); + verify(mPreference).setEnabled(false); + } + + @Test + public void onDeveloperOptionsSwitchEnabled_shouldEnablePreference() { + mController.onDeveloperOptionsSwitchEnabled(); + + verify(mPreference).setEnabled(true); + } +} From a56f6c5bdb8ba6cc984301721357c05b8c208d7f Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Wed, 22 Nov 2017 16:07:48 -0800 Subject: [PATCH 07/11] Switch to modern ArgumentMatchers Test: adb shell am instrument -w -e class com.android.settings.vpn2.PreferenceListTest com.android.settings.tests.unit/android.support.test.runner.AndroidJUnitRunner Change-Id: Id2e4684f6d3048cfb60fee1f747d0796c157a3da --- .../android/settings/vpn2/PreferenceListTest.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/tests/unit/src/com/android/settings/vpn2/PreferenceListTest.java b/tests/unit/src/com/android/settings/vpn2/PreferenceListTest.java index bb12efacf09..2accbf2aa0c 100644 --- a/tests/unit/src/com/android/settings/vpn2/PreferenceListTest.java +++ b/tests/unit/src/com/android/settings/vpn2/PreferenceListTest.java @@ -36,9 +36,9 @@ import java.util.List; import java.util.Map; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.mockito.compat.ArgumentMatcher; public class PreferenceListTest extends AndroidTestCase { private static final String TAG = "PreferenceListTest"; @@ -135,13 +135,10 @@ public class PreferenceListTest extends AndroidTestCase { /* lockdownVpnKey */ null); updater.run(); - final ArgumentMatcher equalsFake = new ArgumentMatcher() { - @Override - public boolean matchesObject(final Object arg) { - if (arg == vpnProfile) return true; - if (arg == null) return false; - return TextUtils.equals(((VpnProfile) arg).key, vpnProfile.key); - } + final ArgumentMatcher equalsFake = arg -> { + if (arg == vpnProfile) return true; + if (arg == null) return false; + return TextUtils.equals(arg.key, vpnProfile.key); }; // The VPN profile should have been used to create a preference and set up at laest once From 40bcefc0bb31b054e40fbdb5d95147aefe0ed1ed Mon Sep 17 00:00:00 2001 From: Beverly Date: Thu, 14 Dec 2017 15:29:28 -0500 Subject: [PATCH 08/11] Clarify secondary dnd media text. Bug: 63077372 Test: manual Change-Id: I38f249851fc5122e89dd3cc33124068598475751 --- res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 35962796d3a..f73d7d983d5 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -7316,8 +7316,8 @@ Media - - Includes system feedback + + Includes system feedback like touch and charging sounds Reminders From 5ef1f502f09ce534eb1d278e05b60dc012016441 Mon Sep 17 00:00:00 2001 From: jackqdyulei Date: Fri, 15 Dec 2017 10:42:15 -0800 Subject: [PATCH 09/11] Turn on the new connected device page by default Bug: 69926683 Test: RunSettingsRoboTests Change-Id: Ice589b825226e92eace5f5fa6b58499a13e36ef8 --- AndroidManifest.xml | 2 +- .../AdvancedConnectedDeviceDashboardFragment.java | 6 ------ .../settings/dashboard/DashboardFragmentRegistry.java | 3 +++ .../AdvancedConnectedDeviceDashboardFragmentTest.java | 5 +++++ 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 0418d51dfc0..4beaf8fc5f7 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -178,7 +178,6 @@ getPreferenceControllers(Context context) { final List controllers = new ArrayList<>(); diff --git a/src/com/android/settings/dashboard/DashboardFragmentRegistry.java b/src/com/android/settings/dashboard/DashboardFragmentRegistry.java index af00dc6088c..e841496d65e 100644 --- a/src/com/android/settings/dashboard/DashboardFragmentRegistry.java +++ b/src/com/android/settings/dashboard/DashboardFragmentRegistry.java @@ -23,6 +23,7 @@ import com.android.settings.accounts.AccountDetailDashboardFragment; import com.android.settings.accounts.UserAndAccountDashboardFragment; import com.android.settings.applications.AppAndNotificationDashboardFragment; import com.android.settings.applications.DefaultAppSettings; +import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment; import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragmentOld; import com.android.settings.development.DevelopmentSettingsDashboardFragment; import com.android.settings.deviceinfo.StorageDashboardFragment; @@ -62,6 +63,8 @@ public class DashboardFragmentRegistry { //TODO(b/69471219): update ConnectedDeviceDashboardFragment once new feature is done. PARENT_TO_CATEGORY_KEY_MAP.put(ConnectedDeviceDashboardFragmentOld.class.getName(), CategoryKey.CATEGORY_DEVICE); + PARENT_TO_CATEGORY_KEY_MAP.put(AdvancedConnectedDeviceDashboardFragment.class.getName(), + CategoryKey.CATEGORY_DEVICE); PARENT_TO_CATEGORY_KEY_MAP.put(AppAndNotificationDashboardFragment.class.getName(), CategoryKey.CATEGORY_APPS); PARENT_TO_CATEGORY_KEY_MAP.put(PowerUsageSummary.class.getName(), diff --git a/tests/robotests/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragmentTest.java index 27675701845..37ccb760b08 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/AdvancedConnectedDeviceDashboardFragmentTest.java @@ -147,6 +147,11 @@ public class AdvancedConnectedDeviceDashboardFragmentTest { assertThat(keys).doesNotContain(mSmsMirroringPreferenceController.getPreferenceKey()); } + @Test + public void testGetCategoryKey_returnCategoryDevice() { + assertThat(mFragment.getCategoryKey()).isEqualTo(CategoryKey.CATEGORY_DEVICE); + } + @Test public void testNonIndexableKeys_existInXmlLayout() { final Context context = RuntimeEnvironment.application; From b705e1a69128860557cdf963e4dec53f159d9a4a Mon Sep 17 00:00:00 2001 From: Janis Danisevskis Date: Wed, 19 Apr 2017 16:23:02 -0700 Subject: [PATCH 10/11] Make Settings aware of legacy user keys with secret key prefix Secret keys are not longer identifiable by their alias prefix. So now we call getKeyCharacteristics and check the algorithm of the key. Bug: 63931634 Test: Manually installed a key and checked that it is still dispayed correctly. Change-Id: I55a4e46434618cb52ceb9456f184e004165872fd --- .../settings/UserCredentialsSettings.java | 78 ++++++++++++------- .../android/settings/UserCredentialsTest.java | 2 +- 2 files changed, 53 insertions(+), 27 deletions(-) diff --git a/src/com/android/settings/UserCredentialsSettings.java b/src/com/android/settings/UserCredentialsSettings.java index 54e12a85faa..7d0fca07f1f 100644 --- a/src/com/android/settings/UserCredentialsSettings.java +++ b/src/com/android/settings/UserCredentialsSettings.java @@ -37,6 +37,8 @@ import android.security.IKeyChainService; import android.security.KeyChain; import android.security.KeyChain.KeyChainConnection; import android.security.KeyStore; +import android.security.keymaster.KeyCharacteristics; +import android.security.keymaster.KeymasterDefs; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.util.SparseArray; @@ -48,19 +50,15 @@ import android.widget.TextView; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.widget.LockPatternUtils; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; -import com.android.settings.SettingsPreferenceFragment; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; - +import java.security.UnrecoverableKeyException; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import java.util.SortedMap; import java.util.TreeMap; -import static android.view.View.GONE; -import static android.view.View.VISIBLE; - public class UserCredentialsSettings extends SettingsPreferenceFragment implements View.OnClickListener { private static final String TAG = "UserCredentialsSettings"; @@ -254,27 +252,57 @@ public class UserCredentialsSettings extends SettingsPreferenceFragment return credentials; } + private boolean isAsymmetric(KeyStore keyStore, String alias, int uid) + throws UnrecoverableKeyException { + KeyCharacteristics keyCharacteristics = new KeyCharacteristics(); + int errorCode = keyStore.getKeyCharacteristics(alias, null, null, uid, + keyCharacteristics); + if (errorCode != KeyStore.NO_ERROR) { + throw (UnrecoverableKeyException) + new UnrecoverableKeyException("Failed to obtain information about key") + .initCause(KeyStore.getKeyStoreException(errorCode)); + } + Integer keymasterAlgorithm = keyCharacteristics.getEnum( + KeymasterDefs.KM_TAG_ALGORITHM); + if (keymasterAlgorithm == null) { + throw new UnrecoverableKeyException("Key algorithm unknown"); + } + return keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_RSA || + keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC; + } + private SortedMap getCredentialsForUid(KeyStore keyStore, int uid) { final SortedMap aliasMap = new TreeMap<>(); for (final Credential.Type type : Credential.Type.values()) { - for (final String alias : keyStore.list(type.prefix, uid)) { - if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) { - // Do not show work profile keys in user credentials - if (alias.startsWith(LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT) || - alias.startsWith(LockPatternUtils.PROFILE_KEY_NAME_DECRYPT)) { + for (final String prefix : type.prefix) { + for (final String alias : keyStore.list(prefix, uid)) { + if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) { + // Do not show work profile keys in user credentials + if (alias.startsWith(LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT) || + alias.startsWith(LockPatternUtils.PROFILE_KEY_NAME_DECRYPT)) { + continue; + } + // Do not show synthetic password keys in user credential + if (alias.startsWith(LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX)) { + continue; + } + } + try { + if (type == Credential.Type.USER_KEY && + !isAsymmetric(keyStore, prefix + alias, uid)) { + continue; + } + } catch (UnrecoverableKeyException e) { + Log.e(TAG, "Unable to determine algorithm of key: " + prefix + alias, e); continue; } - // Do not show synthetic password keys in user credential - if (alias.startsWith(LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX)) { - continue; + Credential c = aliasMap.get(alias); + if (c == null) { + c = new Credential(alias, uid); + aliasMap.put(alias, c); } + c.storedTypes.add(type); } - Credential c = aliasMap.get(alias); - if (c == null) { - c = new Credential(alias, uid); - aliasMap.put(alias, c); - } - c.storedTypes.add(type); } } return aliasMap; @@ -344,7 +372,7 @@ public class UserCredentialsSettings extends SettingsPreferenceFragment */ private static final SparseArray credentialViewTypes = new SparseArray<>(); static { - credentialViewTypes.put(R.id.contents_userkey, Credential.Type.USER_PRIVATE_KEY); + credentialViewTypes.put(R.id.contents_userkey, Credential.Type.USER_KEY); credentialViewTypes.put(R.id.contents_usercrt, Credential.Type.USER_CERTIFICATE); credentialViewTypes.put(R.id.contents_cacrt, Credential.Type.CA_CERTIFICATE); } @@ -380,12 +408,11 @@ public class UserCredentialsSettings extends SettingsPreferenceFragment static enum Type { CA_CERTIFICATE (Credentials.CA_CERTIFICATE), USER_CERTIFICATE (Credentials.USER_CERTIFICATE), - USER_PRIVATE_KEY (Credentials.USER_PRIVATE_KEY), - USER_SECRET_KEY (Credentials.USER_SECRET_KEY); + USER_KEY(Credentials.USER_PRIVATE_KEY, Credentials.USER_SECRET_KEY); - final String prefix; + final String[] prefix; - Type(String prefix) { + Type(String... prefix) { this.prefix = prefix; } } @@ -407,8 +434,7 @@ public class UserCredentialsSettings extends SettingsPreferenceFragment *
    *
  • {@link Credentials.CA_CERTIFICATE}
  • *
  • {@link Credentials.USER_CERTIFICATE}
  • - *
  • {@link Credentials.USER_PRIVATE_KEY}
  • - *
  • {@link Credentials.USER_SECRET_KEY}
  • + *
  • {@link Credentials.USER_KEY}
  • *
*/ final EnumSet storedTypes = EnumSet.noneOf(Type.class); diff --git a/tests/unit/src/com/android/settings/UserCredentialsTest.java b/tests/unit/src/com/android/settings/UserCredentialsTest.java index 41ef4dec2f5..8a72797e2cb 100644 --- a/tests/unit/src/com/android/settings/UserCredentialsTest.java +++ b/tests/unit/src/com/android/settings/UserCredentialsTest.java @@ -40,7 +40,7 @@ public class UserCredentialsTest extends InstrumentationTestCase { Credential c = new Credential(alias, Process.SYSTEM_UID); c.storedTypes.add(Credential.Type.CA_CERTIFICATE); - c.storedTypes.add(Credential.Type.USER_SECRET_KEY); + c.storedTypes.add(Credential.Type.USER_KEY); Parcel p = Parcel.obtain(); c.writeToParcel(p, /* flags */ 0); From e0693ced0d5241cbc564f8a20f930518cd66293b Mon Sep 17 00:00:00 2001 From: Phil Weaver Date: Fri, 15 Dec 2017 16:13:41 -0800 Subject: [PATCH 11/11] Add setting to disable animations Fixes: 12910879 Test: make RunSettingsRoboTests Also went to Settings -> Accessibility, turned off animations, and observed that animations stopped. Turned it back on, and the came back. Also fiddled with the granular settings in developer options, which behaved as expected. Change-Id: I189fbc4152d3cdb5ee45562c33f94c739f569c32 --- res/values/strings.xml | 2 + res/xml/accessibility_settings.xml | 4 ++ .../accessibility/AccessibilitySettings.java | 39 +++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/res/values/strings.xml b/res/values/strings.xml index 694a1b052dc..4a8e7e3ea0a 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4353,6 +4353,8 @@ Power button ends call Large mouse pointer + + Remove animations Mono audio diff --git a/res/xml/accessibility_settings.xml b/res/xml/accessibility_settings.xml index d7d2e02e4e1..b5da848eb30 100644 --- a/res/xml/accessibility_settings.xml +++ b/res/xml/accessibility_settings.xml @@ -62,6 +62,10 @@ + + mLongPressTimeoutValueToTitleMap = new HashMap<>(); private final Handler mHandler = new Handler(); @@ -194,6 +203,7 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements private SwitchPreference mTogglePowerButtonEndsCallPreference; private SwitchPreference mToggleLockScreenRotationPreference; private SwitchPreference mToggleLargePointerIconPreference; + private SwitchPreference mToggleDisableAnimationsPreference; private SwitchPreference mToggleMasterMonoPreference; private ListPreference mSelectLongPressTimeoutPreference; private Preference mNoServicesMessagePreference; @@ -317,6 +327,9 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements } else if (mToggleLargePointerIconPreference == preference) { handleToggleLargePointerIconPreferenceClick(); return true; + } else if (mToggleDisableAnimationsPreference == preference) { + handleToggleDisableAnimations(); + return true; } else if (mToggleMasterMonoPreference == preference) { handleToggleMasterMonoPreferenceClick(); return true; @@ -349,6 +362,14 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements mToggleLargePointerIconPreference.isChecked() ? 1 : 0); } + private void handleToggleDisableAnimations() { + String newAnimationValue = mToggleDisableAnimationsPreference.isChecked() + ? ANIMATION_OFF_VALUE : ANIMATION_ON_VALUE; + for (String animationPreference : TOGGLE_ANIMATION_TARGETS) { + Settings.Global.putString(getContentResolver(), animationPreference, newAnimationValue); + } + } + private void handleToggleMasterMonoPreferenceClick() { Settings.System.putIntForUser(getContentResolver(), Settings.System.MASTER_MONO, mToggleMasterMonoPreference.isChecked() ? 1 : 0, UserHandle.USER_CURRENT); @@ -389,6 +410,9 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements mToggleLargePointerIconPreference = (SwitchPreference) findPreference(TOGGLE_LARGE_POINTER_ICON); + mToggleDisableAnimationsPreference = + (SwitchPreference) findPreference(TOGGLE_DISABLE_ANIMATIONS); + // Master Mono mToggleMasterMonoPreference = (SwitchPreference) findPreference(TOGGLE_MASTER_MONO); @@ -620,6 +644,8 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements mToggleLargePointerIconPreference.setChecked(Settings.Secure.getInt(getContentResolver(), Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON, 0) != 0); + updateDisableAnimationsToggle(); + // Master mono updateMasterMono(); @@ -702,6 +728,19 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements } } + private void updateDisableAnimationsToggle() { + boolean allAnimationsDisabled = true; + for (String animationSetting : TOGGLE_ANIMATION_TARGETS) { + if (!TextUtils.equals( + Settings.Global.getString(getContentResolver(), animationSetting), + ANIMATION_OFF_VALUE)) { + allAnimationsDisabled = false; + break; + } + } + mToggleDisableAnimationsPreference.setChecked(allAnimationsDisabled); + } + private void updateMasterMono() { final boolean masterMono = Settings.System.getIntForUser( getContentResolver(), Settings.System.MASTER_MONO,