From 1e887c17e20f505cb521faae626253afee5fa552 Mon Sep 17 00:00:00 2001 From: jackqdyulei Date: Wed, 6 Sep 2017 14:18:29 -0700 Subject: [PATCH 1/9] Make system item has a consistent name. In battery settings, the system item contains many packages. In old behaviour it will use the first package(which has legal name and icon) to represent this item. This behaviour is not consistent. In this cl, we always use package "android" to extract name and icon if it is system item. Bug: 65090883 Test: RunSettingsRoboTests Change-Id: Ibb7f85c06ab1745867f1eaa666cea32c8d3295a6 --- .../settings/fuelgauge/BatteryEntry.java | 21 +++++++++--- .../settings/fuelgauge/BatteryEntryTest.java | 33 ++++++++++++++++++- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/com/android/settings/fuelgauge/BatteryEntry.java b/src/com/android/settings/fuelgauge/BatteryEntry.java index aa7125282f5..7a18cd0fa94 100644 --- a/src/com/android/settings/fuelgauge/BatteryEntry.java +++ b/src/com/android/settings/fuelgauge/BatteryEntry.java @@ -26,6 +26,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.UserInfo; import android.graphics.drawable.Drawable; import android.os.Handler; +import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; @@ -47,6 +48,7 @@ public class BatteryEntry { public static final int MSG_REPORT_FULLY_DRAWN = 2; private static final String TAG = "BatteryEntry"; + private static final String PACKAGE_SYSTEM = "android"; static final HashMap sUidCache = new HashMap(); @@ -268,9 +270,11 @@ public class BatteryEntry { if (sipper.mPackages == null) { sipper.mPackages = pm.getPackagesForUid(uid); } - if (sipper.mPackages != null) { - String[] packageLabels = new String[sipper.mPackages.length]; - System.arraycopy(sipper.mPackages, 0, packageLabels, 0, sipper.mPackages.length); + + final String[] packages = extractPackagesFromSipper(sipper); + if (packages != null) { + String[] packageLabels = new String[packages.length]; + System.arraycopy(packages, 0, packageLabels, 0, packages.length); // Convert package names to user-facing labels where possible IPackageManager ipm = AppGlobals.getPackageManager(); @@ -289,7 +293,7 @@ public class BatteryEntry { packageLabels[i] = label.toString(); } if (ai.icon != 0) { - defaultPackageName = sipper.mPackages[i]; + defaultPackageName = packages[i]; icon = ai.loadIcon(pm); break; } @@ -303,7 +307,7 @@ public class BatteryEntry { name = packageLabels[0]; } else { // Look for an official name for this UID. - for (String pkgName : sipper.mPackages) { + for (String pkgName : packages) { try { final PackageInfo pi = ipm.getPackageInfo(pkgName, 0 /* no flags */, userId); if (pi == null) { @@ -349,4 +353,11 @@ public class BatteryEntry { sHandler.sendMessage(sHandler.obtainMessage(MSG_UPDATE_NAME_ICON, this)); } } + + String[] extractPackagesFromSipper(BatterySipper sipper) { + // Only use system package if uid is system uid, so it could find a consistent name and icon + return sipper.getUid() == Process.SYSTEM_UID + ? new String[]{PACKAGE_SYSTEM} + : sipper.mPackages; + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryEntryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryEntryTest.java index 632d54926a7..a461f466331 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryEntryTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryEntryTest.java @@ -21,6 +21,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Handler; +import android.os.Process; import android.os.UserManager; import com.android.internal.os.BatterySipper; @@ -46,9 +47,12 @@ import static org.mockito.Mockito.when; public class BatteryEntryTest { private static final int APP_UID = 123; + private static final int SYSTEM_UID = Process.SYSTEM_UID; private static final String APP_DEFAULT_PACKAGE_NAME = "com.android.test"; private static final String APP_LABEL = "Test App Name"; private static final String HIGH_DRAIN_PACKAGE = "com.android.test.screen"; + private static final String ANDROID_PACKAGE = "android"; + private static final String[] SYSTEM_PACKAGES = {HIGH_DRAIN_PACKAGE, ANDROID_PACKAGE}; @Rule public MockitoRule mocks = MockitoJUnit.rule(); @@ -84,6 +88,18 @@ public class BatteryEntryTest { return sipper; } + private BatteryEntry createBatteryEntryForSystem() { + return new BatteryEntry(mockContext, mockHandler, mockUserManager, createSipperForSystem()); + } + + private BatterySipper createSipperForSystem() { + BatterySipper sipper = + new BatterySipper(DrainType.APP, new FakeUid(SYSTEM_UID), 0 /* power use */); + sipper.packageWithHighestDrain = HIGH_DRAIN_PACKAGE; + sipper.mPackages = SYSTEM_PACKAGES; + return sipper; + } + @Test public void batteryEntryForApp_shouldSetDefaultPackageNameAndLabel() throws Exception { BatteryEntry entry = createBatteryEntryForApp(); @@ -118,7 +134,22 @@ public class BatteryEntryTest { new String[]{APP_DEFAULT_PACKAGE_NAME, "package2", "package3"}); BatteryEntry entry = createBatteryEntryForApp(); - + assertThat(entry.getLabel()).isEqualTo(HIGH_DRAIN_PACKAGE); } + + @Test + public void extractPackageFromSipper_systemSipper_returnSystemPackage() { + BatteryEntry entry = createBatteryEntryForSystem(); + + assertThat(entry.extractPackagesFromSipper(entry.sipper)).isEqualTo( + new String[]{ANDROID_PACKAGE}); + } + + @Test + public void extractPackageFromSipper_normalSipper_returnDefaultPakcage() { + BatteryEntry entry = createBatteryEntryForApp(); + + assertThat(entry.extractPackagesFromSipper(entry.sipper)).isEqualTo(entry.sipper.mPackages); + } } From 96dc3229018e185111dab6dd5c14a14958f7b539 Mon Sep 17 00:00:00 2001 From: debesay guadad Date: Fri, 26 Feb 2016 14:49:27 +0800 Subject: [PATCH 2/9] Add ims registration status Display "IMS registration state" in Status menu. Introduce carrier config to enable/disable the feature for customization. Since some carriers require, this feature is necessary. Test: manual Checked "IMS registration state" in Status menu Bug: 28806101 Merged-In: I6c452c512f03cf41704b91331e44141ed3050cf9 Change-Id: I6c452c512f03cf41704b91331e44141ed3050cf9 --- res/values/strings.xml | 7 +++++ res/xml/device_info_status.xml | 6 ++++ .../android/settings/deviceinfo/Status.java | 28 +++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/res/values/strings.xml b/res/values/strings.xml index a1f47115956..b2070098517 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -8981,4 +8981,11 @@ " • " + + + "IMS registration state" + + "Registered" + + "Not registered" diff --git a/res/xml/device_info_status.xml b/res/xml/device_info_status.xml index 9a57af987bf..6fe43ec87b0 100644 --- a/res/xml/device_info_status.xml +++ b/res/xml/device_info_status.xml @@ -89,4 +89,10 @@ android:title="@string/status_wimax_mac_address" android:summary="@string/device_info_not_available" android:persistent="false" /> + diff --git a/src/com/android/settings/deviceinfo/Status.java b/src/com/android/settings/deviceinfo/Status.java index baddc6cac2b..faa4134a935 100644 --- a/src/com/android/settings/deviceinfo/Status.java +++ b/src/com/android/settings/deviceinfo/Status.java @@ -28,11 +28,15 @@ import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.os.PersistableBundle; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserManager; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceScreen; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; import android.text.TextUtils; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -56,6 +60,7 @@ public class Status extends SettingsPreferenceFragment { private static final String KEY_WIMAX_MAC_ADDRESS = "wimax_mac_address"; private static final String KEY_SIM_STATUS = "sim_status"; private static final String KEY_IMEI_INFO = "imei_info"; + private static final String KEY_IMS_REGISTRATION_STATE = "ims_reg_state"; // Broadcasts to listen to for connectivity changes. private static final String[] CONNECTIVITY_INTENTS = { @@ -85,6 +90,8 @@ public class Status extends SettingsPreferenceFragment { private Preference mIpAddress; private Preference mWifiMacAddress; private Preference mWimaxMacAddress; + private Preference mImsStatus; + private Handler mHandler; private static class MyHandler extends Handler { @@ -162,6 +169,7 @@ public class Status extends SettingsPreferenceFragment { mWifiMacAddress = findPreference(KEY_WIFI_MAC_ADDRESS); mWimaxMacAddress = findPreference(KEY_WIMAX_MAC_ADDRESS); mIpAddress = findPreference(KEY_IP_ADDRESS); + mImsStatus = findPreference(KEY_IMS_REGISTRATION_STATE); mRes = getResources(); mUnavailable = mRes.getString(R.string.status_unavailable); @@ -269,11 +277,31 @@ public class Status extends SettingsPreferenceFragment { } } + private void setImsRegistrationStatus() { + CarrierConfigManager configManager = (CarrierConfigManager) + getSystemService(Context.CARRIER_CONFIG_SERVICE); + int subId = SubscriptionManager.getDefaultDataSubscriptionId(); + PersistableBundle config = null; + if (configManager != null) { + config = configManager.getConfigForSubId(subId); + } + if (config != null && config.getBoolean( + CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL)) { + TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); + mImsStatus.setSummary((tm != null && tm.isImsRegistered(subId)) ? + R.string.ims_reg_status_registered : R.string.ims_reg_status_not_registered); + } else { + removePreferenceFromScreen(KEY_IMS_REGISTRATION_STATE); + mImsStatus = null; + } + } + void updateConnectivity() { setWimaxStatus(); setWifiStatus(); setBtStatus(); setIpAddressStatus(); + setImsRegistrationStatus(); } void updateTimes() { From d6bddbc4dbcb86394ca3d60b6556196e77424808 Mon Sep 17 00:00:00 2001 From: debesay guadad Date: Fri, 26 Feb 2016 14:49:27 +0800 Subject: [PATCH 3/9] Add ims registration status Display "IMS registration state" in Status menu. Introduce carrier config to enable/disable the feature for customization. Since some carriers require, this feature is necessary. Test: manual Checked "IMS registration state" in Status menu Bug: 28806101 Merged-In: I6c452c512f03cf41704b91331e44141ed3050cf9 Change-Id: I6c452c512f03cf41704b91331e44141ed3050cf9 --- res/values/strings.xml | 6 ++++ res/xml/device_info_status.xml | 6 ++++ .../android/settings/deviceinfo/Status.java | 28 +++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/res/values/strings.xml b/res/values/strings.xml index 5e66cf4dbbe..b77f4b5b501 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -8687,4 +8687,10 @@ " • " + + "IMS registration state" + + "Registered" + + "Not registered" diff --git a/res/xml/device_info_status.xml b/res/xml/device_info_status.xml index 9a57af987bf..6fe43ec87b0 100644 --- a/res/xml/device_info_status.xml +++ b/res/xml/device_info_status.xml @@ -89,4 +89,10 @@ android:title="@string/status_wimax_mac_address" android:summary="@string/device_info_not_available" android:persistent="false" /> + diff --git a/src/com/android/settings/deviceinfo/Status.java b/src/com/android/settings/deviceinfo/Status.java index baddc6cac2b..faa4134a935 100644 --- a/src/com/android/settings/deviceinfo/Status.java +++ b/src/com/android/settings/deviceinfo/Status.java @@ -28,11 +28,15 @@ import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.os.PersistableBundle; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserManager; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceScreen; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; import android.text.TextUtils; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -56,6 +60,7 @@ public class Status extends SettingsPreferenceFragment { private static final String KEY_WIMAX_MAC_ADDRESS = "wimax_mac_address"; private static final String KEY_SIM_STATUS = "sim_status"; private static final String KEY_IMEI_INFO = "imei_info"; + private static final String KEY_IMS_REGISTRATION_STATE = "ims_reg_state"; // Broadcasts to listen to for connectivity changes. private static final String[] CONNECTIVITY_INTENTS = { @@ -85,6 +90,8 @@ public class Status extends SettingsPreferenceFragment { private Preference mIpAddress; private Preference mWifiMacAddress; private Preference mWimaxMacAddress; + private Preference mImsStatus; + private Handler mHandler; private static class MyHandler extends Handler { @@ -162,6 +169,7 @@ public class Status extends SettingsPreferenceFragment { mWifiMacAddress = findPreference(KEY_WIFI_MAC_ADDRESS); mWimaxMacAddress = findPreference(KEY_WIMAX_MAC_ADDRESS); mIpAddress = findPreference(KEY_IP_ADDRESS); + mImsStatus = findPreference(KEY_IMS_REGISTRATION_STATE); mRes = getResources(); mUnavailable = mRes.getString(R.string.status_unavailable); @@ -269,11 +277,31 @@ public class Status extends SettingsPreferenceFragment { } } + private void setImsRegistrationStatus() { + CarrierConfigManager configManager = (CarrierConfigManager) + getSystemService(Context.CARRIER_CONFIG_SERVICE); + int subId = SubscriptionManager.getDefaultDataSubscriptionId(); + PersistableBundle config = null; + if (configManager != null) { + config = configManager.getConfigForSubId(subId); + } + if (config != null && config.getBoolean( + CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL)) { + TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); + mImsStatus.setSummary((tm != null && tm.isImsRegistered(subId)) ? + R.string.ims_reg_status_registered : R.string.ims_reg_status_not_registered); + } else { + removePreferenceFromScreen(KEY_IMS_REGISTRATION_STATE); + mImsStatus = null; + } + } + void updateConnectivity() { setWimaxStatus(); setWifiStatus(); setBtStatus(); setIpAddressStatus(); + setImsRegistrationStatus(); } void updateTimes() { From d78ea6af4aef4253e8af7cdc524fbc2a32b38244 Mon Sep 17 00:00:00 2001 From: Maurice Lam Date: Wed, 6 Sep 2017 20:52:22 -0700 Subject: [PATCH 4/9] Don't default to PIN on non FBE devices So that EncryptionInterstitial is shown as part of the flow Test: cd tests/robotests && mma Bug: 65192141 Change-Id: I13e8b9059aae39cef2a8509f9f0eee1cd0808220 --- .../SetupFingerprintEnrollIntroduction.java | 14 ++- .../password/StorageManagerWrapper.java | 29 +++++ ...etupFingerprintEnrollIntroductionTest.java | 55 ++++++++++ .../shadow/ShadowFingerprintManager.java | 102 ++++++++++++++++++ 4 files changed, 195 insertions(+), 5 deletions(-) create mode 100644 src/com/android/settings/password/StorageManagerWrapper.java create mode 100644 tests/robotests/src/com/android/settings/testutils/shadow/ShadowFingerprintManager.java diff --git a/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java b/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java index 59907cf493b..5656a27d540 100644 --- a/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java +++ b/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroduction.java @@ -32,6 +32,7 @@ import com.android.settings.SetupWizardUtils; import com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment; import com.android.settings.password.SetupChooseLockGeneric; import com.android.settings.password.SetupSkipDialog; +import com.android.settings.password.StorageManagerWrapper; public class SetupFingerprintEnrollIntroduction extends FingerprintEnrollIntroduction { private static final String KEY_LOCK_SCREEN_PRESENT = "wasLockScreenPresent"; @@ -56,11 +57,14 @@ public class SetupFingerprintEnrollIntroduction extends FingerprintEnrollIntrodu @Override protected Intent getChooseLockIntent() { - Intent intent = new Intent(this, SetupChooseLockGeneric.class) - .putExtra( - LockPatternUtils.PASSWORD_TYPE_KEY, - DevicePolicyManager.PASSWORD_QUALITY_NUMERIC); - intent.putExtra(ChooseLockGenericFragment.EXTRA_SHOW_OPTIONS_BUTTON, true); + Intent intent = new Intent(this, SetupChooseLockGeneric.class); + + if (StorageManagerWrapper.isFileEncryptedNativeOrEmulated()) { + intent.putExtra( + LockPatternUtils.PASSWORD_TYPE_KEY, + DevicePolicyManager.PASSWORD_QUALITY_NUMERIC); + intent.putExtra(ChooseLockGenericFragment.EXTRA_SHOW_OPTIONS_BUTTON, true); + } SetupWizardUtils.copySetupExtras(getIntent(), intent); return intent; } diff --git a/src/com/android/settings/password/StorageManagerWrapper.java b/src/com/android/settings/password/StorageManagerWrapper.java new file mode 100644 index 00000000000..5adfaf2e51c --- /dev/null +++ b/src/com/android/settings/password/StorageManagerWrapper.java @@ -0,0 +1,29 @@ +/* + * 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.password; + +import android.os.storage.StorageManager; + +/** + * Wrapper class to allow Robolectric to shadow methods introduced in newer API + */ +public class StorageManagerWrapper { + + public static boolean isFileEncryptedNativeOrEmulated() { + return StorageManager.isFileEncryptedNativeOrEmulated(); + } +} diff --git a/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroductionTest.java b/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroductionTest.java index 801ee5dbfb2..f5859acf52d 100644 --- a/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroductionTest.java +++ b/tests/robotests/src/com/android/settings/fingerprint/SetupFingerprintEnrollIntroductionTest.java @@ -23,28 +23,38 @@ import static org.robolectric.RuntimeEnvironment.application; import android.app.KeyguardManager; import android.content.Intent; +import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.view.View; import android.widget.Button; import com.android.settings.R; import com.android.settings.TestConfig; +import com.android.settings.fingerprint.SetupFingerprintEnrollIntroductionTest + .ShadowStorageManagerWrapper; import com.android.settings.password.SetupChooseLockGeneric.SetupChooseLockGenericFragment; import com.android.settings.password.SetupSkipDialog; +import com.android.settings.password.StorageManagerWrapper; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.ShadowEventLogWriter; +import com.android.settings.testutils.shadow.ShadowFingerprintManager; import com.android.settings.testutils.shadow.ShadowLockPatternUtils; import com.android.settings.testutils.shadow.ShadowUserManager; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; +import org.robolectric.RuntimeEnvironment; import org.robolectric.Shadows; import org.robolectric.annotation.Config; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; import org.robolectric.shadows.ShadowActivity; +import org.robolectric.shadows.ShadowActivity.IntentForResult; import org.robolectric.shadows.ShadowKeyguardManager; import org.robolectric.util.ActivityController; @@ -54,7 +64,9 @@ import org.robolectric.util.ActivityController; sdk = TestConfig.SDK_VERSION, shadows = { ShadowEventLogWriter.class, + ShadowFingerprintManager.class, ShadowLockPatternUtils.class, + ShadowStorageManagerWrapper.class, ShadowUserManager.class }) public class SetupFingerprintEnrollIntroductionTest { @@ -68,12 +80,22 @@ public class SetupFingerprintEnrollIntroductionTest { public void setUp() { MockitoAnnotations.initMocks(this); + RuntimeEnvironment.getRobolectricPackageManager() + .setSystemFeature(PackageManager.FEATURE_FINGERPRINT, true); + ShadowFingerprintManager.addToServiceMap(); + final Intent intent = new Intent(); mController = Robolectric.buildActivity(SetupFingerprintEnrollIntroduction.class, intent); ShadowUserManager.getShadow().setUserInfo(0, mUserInfo); } + @After + public void tearDown() { + ShadowStorageManagerWrapper.reset(); + ShadowFingerprintManager.reset(); + } + @Test public void testKeyguardNotSecure_shouldFinishWithSetupSkipDialogResultSkip() { getShadowKeyguardManager().setIsKeyguardSecure(false); @@ -188,8 +210,41 @@ public class SetupFingerprintEnrollIntroductionTest { assertThat(Shadows.shadowOf(activity).getResultIntent()).isNull(); } + @Test + public void testLockPattern() { + ShadowStorageManagerWrapper.sIsFileEncrypted = false; + + mController.create().postCreate(null).resume(); + + SetupFingerprintEnrollIntroduction activity = mController.get(); + + final Button nextButton = activity.findViewById(R.id.fingerprint_next_button); + nextButton.performClick(); + + ShadowActivity shadowActivity = Shadows.shadowOf(activity); + IntentForResult startedActivity = shadowActivity.getNextStartedActivityForResult(); + assertThat(startedActivity).isNotNull(); + assertThat(startedActivity.intent.hasExtra( + SetupChooseLockGenericFragment.EXTRA_PASSWORD_QUALITY)).isFalse(); + } + private ShadowKeyguardManager getShadowKeyguardManager() { return Shadows.shadowOf(application.getSystemService(KeyguardManager.class)); } + + @Implements(StorageManagerWrapper.class) + public static class ShadowStorageManagerWrapper { + + private static boolean sIsFileEncrypted = true; + + public static void reset() { + sIsFileEncrypted = true; + } + + @Implementation + public static boolean isFileEncryptedNativeOrEmulated() { + return sIsFileEncrypted; + } + } } diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowFingerprintManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowFingerprintManager.java new file mode 100644 index 00000000000..b84cf42ed14 --- /dev/null +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowFingerprintManager.java @@ -0,0 +1,102 @@ +/* + * 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.testutils.shadow; + +import android.content.Context; +import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintManager; +import android.support.annotation.NonNull; + +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.annotation.Resetter; +import org.robolectric.internal.ShadowExtractor; +import org.robolectric.shadows.ShadowContextImpl; +import org.robolectric.util.ReflectionHelpers; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.IntStream; + +@Implements(FingerprintManager.class) +public class ShadowFingerprintManager { + + private static Map getSystemServiceMap() { + return ReflectionHelpers.getStaticField(ShadowContextImpl.class, "SYSTEM_SERVICE_MAP"); + } + + /** + * Call this in @Before of a test to add FingerprintManager to Robolectric's system service + * map. Otherwise getSystemService(FINGERPRINT_SERVICE) will return null. + */ + public static void addToServiceMap() { + getSystemServiceMap().put(Context.FINGERPRINT_SERVICE, FingerprintManager.class.getName()); + } + + @Resetter + public static void reset() { + getSystemServiceMap().remove(Context.FINGERPRINT_SERVICE); + } + + public boolean hardwareDetected = true; + + @NonNull + private List mFingerprints = Collections.emptyList(); + + @Implementation + public boolean isHardwareDetected() { + return hardwareDetected; + } + + @Implementation + public boolean hasEnrolledFingerprints() { + return !mFingerprints.isEmpty(); + } + + @Implementation + public List getEnrolledFingerprints() { + return mFingerprints; + } + + @Implementation + public List getEnrolledFingerprints(int userId) { + return mFingerprints; + } + + public void setEnrolledFingerprints(Fingerprint... fingerprints) { + mFingerprints = Arrays.asList(fingerprints); + } + + public void setDefaultFingerprints(int num) { + setEnrolledFingerprints( + IntStream.range(0, num) + .mapToObj(i -> new Fingerprint( + "Fingerprint " + i, + 0, /* groupId */ + i, /* fingerId */ + 0 /* deviceId */)) + .toArray(Fingerprint[]::new)); + } + + public static ShadowFingerprintManager get() { + return (ShadowFingerprintManager) ShadowExtractor.extract( + RuntimeEnvironment.application.getSystemService(FingerprintManager.class)); + } +} From 4c26da9e13cc8c61db3dce51a9b7e25c2b561e2b Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Fri, 8 Sep 2017 15:29:54 -0700 Subject: [PATCH 5/9] Fork a new developer options page. - Create a new activity/fragment and add it to manifest - Mark old activity/fragment deprecated - Enable/disable the new activity based on FeatureFlag - Initial skeleton code for new fragment Bug: 65522852 Test: make RunSettingsRoboTests -j40 ROBOTEST_FILTER=DevelopmentSettingsDashboardFragmentTest Change-Id: Ib1395693f8b6f61d4726573a9ea841ea53cf207b --- AndroidManifest.xml | 26 +++++ src/com/android/settings/Settings.java | 11 ++ .../android/settings/SettingsActivity.java | 12 ++- .../core/gateway/SettingsGateway.java | 2 + .../dashboard/DashboardFragmentRegistry.java | 11 +- .../development/DevelopmentSettings.java | 2 + .../DevelopmentSettingsDashboardFragment.java | 102 ++++++++++++++++++ ...ther_not_in_search_index_provider_registry | 1 + .../src/android/util/FeatureFlagUtils.java | 46 ++++++++ ...elopmentSettingsDashboardFragmentTest.java | 98 +++++++++++++++++ 10 files changed, 307 insertions(+), 4 deletions(-) create mode 100644 src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java create mode 100644 tests/robotests/src/android/util/FeatureFlagUtils.java create mode 100644 tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 7702257c060..46c6490d880 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1934,6 +1934,32 @@ android:value="true" /> + + + + + + + + + + + + getPreferenceControllers(Context context) { + return buildPreferenceControllers(context); + } + + private static List buildPreferenceControllers(Context context) { + return null; + } + + /** + * For Search. + */ + public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider() { + + @Override + protected boolean isPageSearchEnabled(Context context) { + return DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context); + } + + @Override + public List getXmlResourcesToIndex( + Context context, boolean enabled) { + + final SearchIndexableResource sir = new SearchIndexableResource(context); + sir.xmlResId = R.xml.development_prefs; + return Arrays.asList(sir); + } + + @Override + public List getPreferenceControllers(Context + context) { + return buildPreferenceControllers(context); + } + }; +} diff --git a/tests/robotests/assets/grandfather_not_in_search_index_provider_registry b/tests/robotests/assets/grandfather_not_in_search_index_provider_registry index 948b14ad70c..a2a772ba778 100644 --- a/tests/robotests/assets/grandfather_not_in_search_index_provider_registry +++ b/tests/robotests/assets/grandfather_not_in_search_index_provider_registry @@ -1 +1,2 @@ +com.android.settings.development.DevelopmentSettingsDashboardFragment com.android.settings.display.ScreenZoomPreferenceFragmentForSetupWizard diff --git a/tests/robotests/src/android/util/FeatureFlagUtils.java b/tests/robotests/src/android/util/FeatureFlagUtils.java new file mode 100644 index 00000000000..6bc0557a2b7 --- /dev/null +++ b/tests/robotests/src/android/util/FeatureFlagUtils.java @@ -0,0 +1,46 @@ +/* + * 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 android.util; + +import android.os.SystemProperties; +import android.text.TextUtils; + +/** + * This class is only needed to get around Robolectric issue. + */ +public class FeatureFlagUtils { + public static final String FFLAG_PREFIX = "sys.fflag."; + public static final String FFLAG_OVERRIDE_PREFIX = FFLAG_PREFIX + "override."; + + /** + * Whether or not a flag is enabled. + * + * @param feature the flag name + * @return true if the flag is enabled (either by default in system, or override by user) + */ + public static boolean isEnabled(String feature) { + // Tries to get feature flag from system property. + // Step 1: check if feature flag has any override. Flag name: sys.fflag.override. + String value = SystemProperties.get(FFLAG_OVERRIDE_PREFIX + feature); + if (!TextUtils.isEmpty(value)) { + return Boolean.parseBoolean(value); + } + // Step 2: check if feature flag has any default value. Flag name: sys.fflag. + value = SystemProperties.get(FFLAG_PREFIX + feature); + return Boolean.parseBoolean(value); + } +} diff --git a/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java new file mode 100644 index 00000000000..04ff721167f --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java @@ -0,0 +1,98 @@ +/* + * 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.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.provider.SearchIndexableResource; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.shadow.SettingsShadowResources; +import com.android.settingslib.development.DevelopmentSettingsEnabler; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import java.util.List; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, + shadows = { + SettingsShadowResources.class, + SettingsShadowResources.SettingsShadowTheme.class + }) +public class DevelopmentSettingsDashboardFragmentTest { + + private DevelopmentSettingsDashboardFragment mDashboard; + + @Before + public void setUp() { + mDashboard = new DevelopmentSettingsDashboardFragment(); + } + + @Test + public void shouldNotHaveHelpResource() { + assertThat(mDashboard.getHelpResource()).isEqualTo(0); + } + + @Test + public void shouldLogAsFeatureFlagPage() { + assertThat(mDashboard.getMetricsCategory()) + .isEqualTo(MetricsProto.MetricsEvent.DEVELOPMENT); + } + + @Test + public void searchIndex_shouldIndexFromPrefXml() { + final List index = + DevelopmentSettingsDashboardFragment.SEARCH_INDEX_DATA_PROVIDER + .getXmlResourcesToIndex(RuntimeEnvironment.application, true); + + assertThat(index.size()).isEqualTo(1); + assertThat(index.get(0).xmlResId).isEqualTo(R.xml.development_prefs); + } + + @Test + public void searchIndex_pageDisabled_shouldAddAllKeysToNonIndexable() { + final Context appContext = RuntimeEnvironment.application; + DevelopmentSettingsEnabler.setDevelopmentSettingsEnabled(appContext, false); + + final List nonIndexableKeys = + DevelopmentSettingsDashboardFragment.SEARCH_INDEX_DATA_PROVIDER + .getNonIndexableKeys(appContext); + + assertThat(nonIndexableKeys).contains("development_prefs_screen"); + } + + @Test + public void searchIndex_pageEnabled_shouldNotAddKeysToNonIndexable() { + final Context appContext = RuntimeEnvironment.application; + DevelopmentSettingsEnabler.setDevelopmentSettingsEnabled(appContext, true); + + final List nonIndexableKeys = + DevelopmentSettingsDashboardFragment.SEARCH_INDEX_DATA_PROVIDER + .getNonIndexableKeys(appContext); + + assertThat(nonIndexableKeys).doesNotContain("development_prefs_screen"); + } +} From ad8eba3097106bcfaf5d1ba74e4729034d03b0ed Mon Sep 17 00:00:00 2001 From: Amit Mahajan Date: Thu, 31 Aug 2017 13:59:20 -0700 Subject: [PATCH 6/9] Change to disable mvno data field in ApnEditor if needed. Test: manual Bug: 65243262 Change-Id: I4b3dec6d9dc7fecf0b0a8131dccc349c7daffe48 --- src/com/android/settings/ApnEditor.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/ApnEditor.java b/src/com/android/settings/ApnEditor.java index f67f50309b8..7393091ee87 100644 --- a/src/com/android/settings/ApnEditor.java +++ b/src/com/android/settings/ApnEditor.java @@ -657,7 +657,11 @@ public class ApnEditor extends SettingsPreferenceFragment return null; } else { String[] values = mRes.getStringArray(R.array.mvno_type_entries); - mMvnoMatchData.setEnabled(mvnoIndex != 0); + boolean mvnoMatchDataUneditable = + mReadOnlyApn || (mReadOnlyApnFields != null + && Arrays.asList(mReadOnlyApnFields) + .contains(Telephony.Carriers.MVNO_MATCH_DATA)); + mMvnoMatchData.setEnabled(!mvnoMatchDataUneditable && mvnoIndex != 0); if (newValue != null && newValue.equals(oldValue) == false) { if (values[mvnoIndex].equals("SPN")) { mMvnoMatchData.setText(mTelephonyManager.getSimOperatorName()); From 9be8c3fbfe39a5259c5f8c6adce67fbc5907c802 Mon Sep 17 00:00:00 2001 From: Antony Sargent Date: Thu, 7 Sep 2017 16:19:17 -0700 Subject: [PATCH 7/9] Fix focus behavior of ValidatedEditTextPreference The bug report was that the cursor is set to the beginning of the EditText for wifi hotspot password field, instead of at the end (or having the entire field selected). This fix makes it so that all ValidatedEditTextPreference's will put the cursor at the end of the EditText. Bug: 65413206 Test: make RunSettingsRoboTests Change-Id: I23f3bb1b3f1b49b2f193c0ae2e103eae5c1a1019 --- .../widget/ValidatedEditTextPreference.java | 23 +++++++------ .../ValidatedEditTextPreferenceTest.java | 33 +++++++++++++++++-- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/src/com/android/settings/widget/ValidatedEditTextPreference.java b/src/com/android/settings/widget/ValidatedEditTextPreference.java index 76331d4f97e..58c62ebce84 100644 --- a/src/com/android/settings/widget/ValidatedEditTextPreference.java +++ b/src/com/android/settings/widget/ValidatedEditTextPreference.java @@ -21,8 +21,10 @@ import android.content.Context; import android.support.annotation.VisibleForTesting; import android.text.Editable; import android.text.InputType; +import android.text.TextUtils; import android.text.TextWatcher; import android.util.AttributeSet; +import android.util.Log; import android.view.View; import android.widget.EditText; @@ -61,17 +63,18 @@ public class ValidatedEditTextPreference extends CustomEditTextPreference { @Override protected void onBindDialogView(View view) { super.onBindDialogView(view); - if (mValidator != null) { - final EditText editText = view.findViewById(android.R.id.edit); - if (editText != null) { - editText.removeTextChangedListener(mTextWatcher); - if (mIsPassword) { - editText.setInputType( - InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); - editText.setMaxLines(1); - } - editText.addTextChangedListener(mTextWatcher); + final EditText editText = view.findViewById(android.R.id.edit); + if (editText != null && !TextUtils.isEmpty(editText.getText())) { + editText.setSelection(editText.getText().length()); + } + if (mValidator != null && editText != null) { + editText.removeTextChangedListener(mTextWatcher); + if (mIsPassword) { + editText.setInputType( + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); + editText.setMaxLines(1); } + editText.addTextChangedListener(mTextWatcher); } } diff --git a/tests/robotests/src/com/android/settings/widget/ValidatedEditTextPreferenceTest.java b/tests/robotests/src/com/android/settings/widget/ValidatedEditTextPreferenceTest.java index 88a51474187..e061787483d 100644 --- a/tests/robotests/src/com/android/settings/widget/ValidatedEditTextPreferenceTest.java +++ b/tests/robotests/src/com/android/settings/widget/ValidatedEditTextPreferenceTest.java @@ -35,9 +35,10 @@ import org.robolectric.annotation.Config; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; @RunWith(SettingsRobolectricTestRunner.class) @@ -58,10 +59,36 @@ public class ValidatedEditTextPreferenceTest { } @Test - public void bindDialogView_noTextWatcher_shouldDoNothing() { + public void bindDialogView_nullEditText_shouldNotCrash() { + when(mView.findViewById(android.R.id.edit)).thenReturn(null); + // should not crash trying to get the EditText text + mPreference.onBindDialogView(mView); + } + + @Test + public void bindDialogView_emptyEditText_shouldNotSetSelection() { + final String testText = ""; + final EditText editText = spy(new EditText(RuntimeEnvironment.application)); + editText.setText(testText); + when(mView.findViewById(android.R.id.edit)).thenReturn(editText); + mPreference.onBindDialogView(mView); - verifyZeroInteractions(mView); + // no need to setSelection if text was empty + verify(editText, never()).setSelection(anyInt()); + } + + @Test + public void bindDialogView_nonemptyEditText_shouldSetSelection() { + final String testText = "whatever"; + final EditText editText = spy(new EditText(RuntimeEnvironment.application)); + editText.setText(testText); + when(mView.findViewById(android.R.id.edit)).thenReturn(editText); + + mPreference.onBindDialogView(mView); + + // selection should be set to end of string + verify(editText).setSelection(testText.length()); } @Test From 7efcc35d85dd6d6f1358b41e624527e5ac581ba8 Mon Sep 17 00:00:00 2001 From: Maurice Lam Date: Fri, 8 Sep 2017 11:57:27 -0700 Subject: [PATCH 8/9] Fix sizing of pattern view Align the size of pattern view in ChooseLockPattern, ConfirmLockPattern, and keyguard as much as possible. In rare cases they may still be inconsistent because the screens have different content outside of the pattern view (e.g. header text, navigation buttons etc) Test: Manual Bug: 64339681 Change-Id: Ie07ae211340967a3cdf9c6c28f9818f207dbd2c9 --- res/layout-land/choose_lock_pattern.xml | 8 +++--- .../confirm_lock_pattern_internal.xml | 8 +++--- res/layout/choose_lock_pattern_common.xml | 22 ++++++++++++---- .../confirm_lock_pattern_internal_base.xml | 25 +++++++++++++------ res/values/styles.xml | 10 ++++++++ 5 files changed, 54 insertions(+), 19 deletions(-) diff --git a/res/layout-land/choose_lock_pattern.xml b/res/layout-land/choose_lock_pattern.xml index 0743577afb2..8e78b056078 100644 --- a/res/layout-land/choose_lock_pattern.xml +++ b/res/layout-land/choose_lock_pattern.xml @@ -138,10 +138,12 @@ - + android:layout_weight="1.0" + android:paddingStart="0dp"> - + diff --git a/res/layout-land/confirm_lock_pattern_internal.xml b/res/layout-land/confirm_lock_pattern_internal.xml index 4e58f32a0c4..952ab493d05 100644 --- a/res/layout-land/confirm_lock_pattern_internal.xml +++ b/res/layout-land/confirm_lock_pattern_internal.xml @@ -96,10 +96,12 @@ - + android:layout_weight="1" + android:paddingStart="0dp"> - + diff --git a/res/layout/choose_lock_pattern_common.xml b/res/layout/choose_lock_pattern_common.xml index 3ea960d1a7d..7dca27d2f39 100644 --- a/res/layout/choose_lock_pattern_common.xml +++ b/res/layout/choose_lock_pattern_common.xml @@ -64,19 +64,31 @@ android:gravity="center" android:clipChildren="false" android:clipToPadding="false" - android:orientation="vertical"> + android:orientation="vertical" + android:paddingLeft="0dp" + android:paddingRight="0dp"> - + + + + + diff --git a/res/layout/confirm_lock_pattern_internal_base.xml b/res/layout/confirm_lock_pattern_internal_base.xml index 51096320566..d0ce7697e37 100644 --- a/res/layout/confirm_lock_pattern_internal_base.xml +++ b/res/layout/confirm_lock_pattern_internal_base.xml @@ -61,14 +61,23 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:gravity="center"> + android:gravity="center" + android:paddingLeft="0dp" + android:paddingRight="0dp"> - + android:layout_weight="1"> + + + + diff --git a/res/values/styles.xml b/res/values/styles.xml index fee7c740175..f3e16bffcd6 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -425,6 +425,16 @@ +