From 514d74621a3c24221ca43c4df7b88f751ef27eaf Mon Sep 17 00:00:00 2001 From: Edgar Wang Date: Wed, 25 Mar 2020 16:17:40 +0800 Subject: [PATCH 01/10] Correct confirm message in Reset options -> Erase all data(factory rest) - when device doesn't support esim, don't mention SIM in sub-title. Bug: 151899712 Test: manual Change-Id: Id9f7b1e73173a5a623206f785894f5579a0c2151 --- src/com/android/settings/MasterClear.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/com/android/settings/MasterClear.java b/src/com/android/settings/MasterClear.java index 8bb898abca2..57dfd1be809 100644 --- a/src/com/android/settings/MasterClear.java +++ b/src/com/android/settings/MasterClear.java @@ -344,6 +344,8 @@ public class MasterClear extends InstrumentedFragment implements OnGlobalLayoutL noCancelMobilePlan.setVisibility(View.VISIBLE); mEsimStorage.setChecked(true /* checked */); } + } else { + mEsimStorage.setChecked(false /* checked */); } final UserManager um = (UserManager) getActivity().getSystemService(Context.USER_SERVICE); From 37462ff9593be7b1a7e66253be911f6122cfc69b Mon Sep 17 00:00:00 2001 From: Beverly Date: Thu, 26 Mar 2020 16:23:52 -0400 Subject: [PATCH 02/10] Add mocked main looper to test Toasts were updated in frameworks/base and now will try to get the main looper when trying to create an AccesssibilityManager. Add this mock to avoid a null pointer when trying to post a ShadowToast. Fixes: 151844983 Test: atest make RunSettingsRoboTests7 Change-Id: I4cb6fe44506097b52ce1bb0c8ae7a502d0901f64 --- .../notification/zen/ZenModeEventRuleSettingsTest.java | 7 ++++--- .../notification/zen/ZenModeScheduleRuleSettingsTest.java | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/robotests/src/com/android/settings/notification/zen/ZenModeEventRuleSettingsTest.java b/tests/robotests/src/com/android/settings/notification/zen/ZenModeEventRuleSettingsTest.java index 0c49850f5b6..8ebac3097e0 100644 --- a/tests/robotests/src/com/android/settings/notification/zen/ZenModeEventRuleSettingsTest.java +++ b/tests/robotests/src/com/android/settings/notification/zen/ZenModeEventRuleSettingsTest.java @@ -19,6 +19,7 @@ package com.android.settings.notification.zen; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -28,11 +29,11 @@ import android.app.NotificationManager; import android.content.Context; import android.content.Intent; import android.content.res.Resources; +import android.os.Looper; import androidx.fragment.app.FragmentActivity; import com.android.settings.R; -import com.android.settings.notification.zen.ZenModeEventRuleSettings; import org.junit.Before; import org.junit.Test; @@ -40,7 +41,6 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; import org.robolectric.shadows.ShadowApplication; import org.robolectric.shadows.ShadowToast; @@ -68,7 +68,7 @@ public class ZenModeEventRuleSettingsTest { ShadowApplication shadowApplication = ShadowApplication.getInstance(); shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager); - mContext = RuntimeEnvironment.application; + mContext = application; mFragment = spy(new TestFragment()); mFragment.onAttach(application); @@ -81,6 +81,7 @@ public class ZenModeEventRuleSettingsTest { when(mActivity.getTheme()).thenReturn(res.newTheme()); when(mActivity.getIntent()).thenReturn(mIntent); when(mActivity.getResources()).thenReturn(res); + when(mActivity.getMainLooper()).thenReturn(mock(Looper.class)); when(mFragment.getContext()).thenReturn(mContext); } diff --git a/tests/robotests/src/com/android/settings/notification/zen/ZenModeScheduleRuleSettingsTest.java b/tests/robotests/src/com/android/settings/notification/zen/ZenModeScheduleRuleSettingsTest.java index a556dbfaa88..ff96d687902 100644 --- a/tests/robotests/src/com/android/settings/notification/zen/ZenModeScheduleRuleSettingsTest.java +++ b/tests/robotests/src/com/android/settings/notification/zen/ZenModeScheduleRuleSettingsTest.java @@ -19,6 +19,7 @@ package com.android.settings.notification.zen; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -28,11 +29,11 @@ import android.app.NotificationManager; import android.content.Context; import android.content.Intent; import android.content.res.Resources; +import android.os.Looper; import androidx.fragment.app.FragmentActivity; import com.android.settings.R; -import com.android.settings.notification.zen.ZenModeScheduleRuleSettings; import org.junit.Before; import org.junit.Test; @@ -40,7 +41,6 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; import org.robolectric.shadows.ShadowApplication; import org.robolectric.shadows.ShadowToast; @@ -65,7 +65,7 @@ public class ZenModeScheduleRuleSettingsTest { ShadowApplication shadowApplication = ShadowApplication.getInstance(); shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager); - mContext = RuntimeEnvironment.application; + mContext = application; mFragment = spy(new TestFragment()); mFragment.onAttach(application); @@ -78,6 +78,7 @@ public class ZenModeScheduleRuleSettingsTest { when(mActivity.getTheme()).thenReturn(res.newTheme()); when(mActivity.getIntent()).thenReturn(mIntent); when(mActivity.getResources()).thenReturn(res); + when(mActivity.getMainLooper()).thenReturn(mock(Looper.class)); when(mFragment.getContext()).thenReturn(mContext); } From 66090dce59befa1dbf2ba778c8b98d14fc215475 Mon Sep 17 00:00:00 2001 From: Curtis Belmonte Date: Thu, 26 Mar 2020 13:49:27 -0700 Subject: [PATCH 03/10] Set CDC detail string as subtitle, not description With an associated change to the UI of the BiometricPrompt credential view, this commit preserves the current appearance of the CDC auth flow by promoting the "details" string from the description to the subtitle field of the prompt. Test: Manually, using the TestDPC app Bug: 152053691 Change-Id: If1d773f7f9a7b141520eac70a6cd64c09eb27f20 --- .../settings/password/ConfirmDeviceCredentialActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java index 220b64929ad..31427a7ade4 100644 --- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java +++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java @@ -208,7 +208,7 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity { getTitleFromCredentialType(credentialType, isManagedProfile)); } if (mDetails == null) { - bpBundle.putString(BiometricPrompt.KEY_DEVICE_CREDENTIAL_DESCRIPTION, + bpBundle.putString(BiometricPrompt.KEY_DEVICE_CREDENTIAL_SUBTITLE, getDetailsFromCredentialType(credentialType, isManagedProfile)); } From 9f8edf2b671c37f0fcb0bdc91398699df6f988c5 Mon Sep 17 00:00:00 2001 From: "James.cf Lin" Date: Fri, 27 Mar 2020 09:54:08 +0800 Subject: [PATCH 04/10] Fix showing "Invalid Network Mode 26. Ignore" on Preferred network type After setting the network to telephony framework, Setting UI didn't check this network type when show on the UI and show the invalid string. Bug: 152373426 Test: manual Change-Id: Ibf280a9905edcb7bd241a1505e85ac5e83993634 --- .../telephony/EnabledNetworkModePreferenceController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java index 5ae1fdeaf95..948eecaa5c2 100644 --- a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java +++ b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java @@ -466,6 +466,7 @@ public class EnabledNetworkModePreferenceController extends } break; case TelephonyManagerConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO: + case TelephonyManagerConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA: case TelephonyManagerConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA: setSelectedEntry(networkMode); setSummary(mContext.getString(R.string.network_5G) From 2a295fbc12b051827abe39b2dcef096139104696 Mon Sep 17 00:00:00 2001 From: Raff Tsai Date: Fri, 27 Mar 2020 10:15:40 +0800 Subject: [PATCH 05/10] Add TradeFed test config to SettingsPerfTests Bug: 145109184 Test: rebuild Change-Id: Ib21e96c4588583826e363b0a89c23692ac677fe0 --- tests/perftests/AndroidTest.xml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/perftests/AndroidTest.xml diff --git a/tests/perftests/AndroidTest.xml b/tests/perftests/AndroidTest.xml new file mode 100644 index 00000000000..a2af24a1c1f --- /dev/null +++ b/tests/perftests/AndroidTest.xml @@ -0,0 +1,30 @@ + + + + From 6b76eb0ab69293e5dfdb33df74f151eea1ec3031 Mon Sep 17 00:00:00 2001 From: alictsai Date: Thu, 26 Mar 2020 21:13:21 +0800 Subject: [PATCH 06/10] Device details LCR should be show up " ! " with low battery. screenshot: https://screenshot.googleplex.com/f72YS3THutW.png https://screenshot.googleplex.com/v6uw7XsOs9x.png Bug: 151187813 Test: make -j42 RunSettingsRoboTests Change-Id: Iabb4ac54e6f827f54cd7c5512995f185875e4a06 --- res/layout/advanced_bt_entity_sub.xml | 9 ++--- res/values/dimens.xml | 3 ++ ...ancedBluetoothDetailsHeaderController.java | 33 ++++++++++++---- ...dBluetoothDetailsHeaderControllerTest.java | 39 +++++++++++++++++++ 4 files changed, 72 insertions(+), 12 deletions(-) diff --git a/res/layout/advanced_bt_entity_sub.xml b/res/layout/advanced_bt_entity_sub.xml index 17ef865a62f..0c9374fdc7c 100644 --- a/res/layout/advanced_bt_entity_sub.xml +++ b/res/layout/advanced_bt_entity_sub.xml @@ -47,22 +47,21 @@ + android:layout_height="wrap_content"/> + android:layout_marginStart="4dp"/> \ No newline at end of file diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 4ca9798a95d..79071ed3a05 100755 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -408,6 +408,9 @@ 7.8dp 13dp + 15.5dp + 27.5dp + -4dp 8dp diff --git a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java index bee93fb048d..47421923230 100644 --- a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java +++ b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java @@ -31,6 +31,7 @@ import android.provider.DeviceConfig; import android.provider.MediaStore; import android.util.Log; import android.view.View; +import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; @@ -191,11 +192,9 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont context.getResources().getDimensionPixelSize( R.dimen.advanced_bluetooth_battery_meter_height)); drawable.setBatteryLevel(level); - final int attr = level > LOW_BATTERY_LEVEL || charging - ? android.R.attr.colorControlNormal - : android.R.attr.colorError; drawable.setColorFilter(new PorterDuffColorFilter( - com.android.settings.Utils.getColorAttrDefaultColor(context, attr), + com.android.settings.Utils.getColorAttrDefaultColor(context, + android.R.attr.colorControlNormal), PorterDuff.Mode.SRC)); drawable.setCharging(charging); @@ -218,12 +217,10 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont final boolean charging = BluetoothUtils.getBooleanMetaData(bluetoothDevice, chargeMetaKey); if (batteryLevel != BluetoothUtils.META_INT_ERROR) { linearLayout.setVisibility(View.VISIBLE); - final ImageView imageView = linearLayout.findViewById(R.id.bt_battery_icon); - imageView.setImageDrawable(createBtBatteryIcon(mContext, batteryLevel, charging)); - imageView.setVisibility(View.VISIBLE); final TextView textView = linearLayout.findViewById(R.id.bt_battery_summary); textView.setText(com.android.settings.Utils.formatPercentage(batteryLevel)); textView.setVisibility(View.VISIBLE); + showBatteryIcon(linearLayout, batteryLevel, charging); } else { // Hide it if it doesn't have battery information linearLayout.setVisibility(View.GONE); @@ -234,6 +231,28 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont textView.setVisibility(View.VISIBLE); } + private void showBatteryIcon(LinearLayout linearLayout, int level, boolean charging) { + boolean enableLowBattery = level <= LOW_BATTERY_LEVEL && !charging; + final ImageView imageView = linearLayout.findViewById(R.id.bt_battery_icon); + if (enableLowBattery) { + imageView.setImageDrawable(mContext.getDrawable(R.drawable.ic_battery_alert_24dp)); + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( + mContext.getResources().getDimensionPixelSize( + R.dimen.advanced_bluetooth_battery_width), + mContext.getResources().getDimensionPixelSize( + R.dimen.advanced_bluetooth_battery_height)); + layoutParams.rightMargin = mContext.getResources().getDimensionPixelSize( + R.dimen.advanced_bluetooth_battery_right_margin); + imageView.setLayoutParams(layoutParams); + } else { + imageView.setImageDrawable(createBtBatteryIcon(mContext, level, charging)); + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + imageView.setLayoutParams(layoutParams); + } + imageView.setVisibility(View.VISIBLE); + } + private void updateDisconnectLayout() { mLayoutPreference.findViewById(R.id.layout_left).setVisibility(View.GONE); mLayoutPreference.findViewById(R.id.layout_right).setVisibility(View.GONE); diff --git a/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java index 80ab42c58a1..5097938f2ee 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.robolectric.Shadows.shadowOf; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; @@ -58,6 +59,7 @@ public class AdvancedBluetoothDetailsHeaderControllerTest { private static final int BATTERY_LEVEL_MAIN = 30; private static final int BATTERY_LEVEL_LEFT = 25; private static final int BATTERY_LEVEL_RIGHT = 45; + private static final int LOW_BATTERY_LEVEL = 5; private static final String ICON_URI = "content://test.provider/icon.png"; private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C"; @@ -115,6 +117,7 @@ public class AdvancedBluetoothDetailsHeaderControllerTest { when(mBluetoothDevice.getMetadata( BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY)).thenReturn( String.valueOf(BATTERY_LEVEL_MAIN).getBytes()); + when(mCachedDevice.isConnected()).thenReturn(true); mController.refresh(); @@ -143,6 +146,36 @@ public class AdvancedBluetoothDetailsHeaderControllerTest { assertThat(layout.findViewById(R.id.header_icon).getVisibility()).isEqualTo(View.VISIBLE); } + @Test + public void refresh_withLowBatteryAndUncharged_showAlertIcon() { + when(mBluetoothDevice.getMetadata( + BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn( + String.valueOf(LOW_BATTERY_LEVEL).getBytes()); + when(mBluetoothDevice.getMetadata( + BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)).thenReturn( + String.valueOf(LOW_BATTERY_LEVEL).getBytes()); + when(mBluetoothDevice.getMetadata( + BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY)).thenReturn( + String.valueOf(BATTERY_LEVEL_MAIN).getBytes()); + when(mBluetoothDevice.getMetadata( + BluetoothDevice.METADATA_UNTETHERED_LEFT_CHARGING)).thenReturn( + String.valueOf(false).getBytes()); + when(mBluetoothDevice.getMetadata( + BluetoothDevice.METADATA_UNTETHERED_RIGHT_CHARGING)).thenReturn( + String.valueOf(true).getBytes()); + when(mBluetoothDevice.getMetadata( + BluetoothDevice.METADATA_UNTETHERED_CASE_CHARGING)).thenReturn( + String.valueOf(false).getBytes()); + when(mCachedDevice.isConnected()).thenReturn(true); + + mController.refresh(); + + assertBatteryIcon(mLayoutPreference.findViewById(R.id.layout_left), + R.drawable.ic_battery_alert_24dp); + assertBatteryIcon(mLayoutPreference.findViewById(R.id.layout_right), /* resId= */-1); + assertBatteryIcon(mLayoutPreference.findViewById(R.id.layout_middle), /* resId= */ -1); + } + @Test public void getAvailabilityStatus_untetheredHeadsetWithConfigOn_returnAvailable() { DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI, @@ -256,4 +289,10 @@ public class AdvancedBluetoothDetailsHeaderControllerTest { com.android.settings.Utils.formatPercentage(batteryLevel)); } + private void assertBatteryIcon(LinearLayout linearLayout, int resId) { + final ImageView imageView = linearLayout.findViewById(R.id.bt_battery_icon); + assertThat(shadowOf(imageView.getDrawable()).getCreatedFromResId()) + .isEqualTo(resId); + } + } From a009e4465482d2f8b4983dd39abd6bdcdc0ef6b9 Mon Sep 17 00:00:00 2001 From: govenliu Date: Fri, 27 Mar 2020 19:44:14 +0800 Subject: [PATCH 07/10] [Wi-Fi] Fix 3 unit tests fail in WifiMeteredPreferenceController2Test Fix 3 unit tests fail in WifiMeteredPreferenceController2Test. Bug: 151696220 Test: Run make RunSettingsRoboTests ROBOTEST_FILTER=WifiMeteredPreferenceController2Test Change-Id: I1db81c790929387c4fe74f5adb1d323c23b40546 --- .../wifi/details2/WifiMeteredPreferenceController2Test.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/robotests/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2Test.java b/tests/robotests/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2Test.java index 9c31d4481b8..c63fa60437b 100644 --- a/tests/robotests/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2Test.java +++ b/tests/robotests/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2Test.java @@ -31,6 +31,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; @@ -50,6 +51,7 @@ public class WifiMeteredPreferenceController2Test { @Before public void setUp() { + MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; mPreferenceController = spy( From ebf8ffd8fdc114fd9ca84fa02339b5ab4433b733 Mon Sep 17 00:00:00 2001 From: Arc Wang Date: Fri, 27 Mar 2020 16:24:02 +0800 Subject: [PATCH 08/10] [Wi-Fi] Refactor WifiConnectionPreferenceController with WifiTrackerLib Bug: 152582093 Test: make RunSettingsRoboTests ROBOTEST_FILTER=WifiConnectionPreferenceControllerTest Change-Id: I9c0ab0edf5a8e935a2c1afee865bd04304ae6f0f --- .../WifiConnectionPreferenceController.java | 162 ++++++++++-------- ...ifiConnectionPreferenceControllerTest.java | 95 +++++----- 2 files changed, 133 insertions(+), 124 deletions(-) diff --git a/src/com/android/settings/wifi/WifiConnectionPreferenceController.java b/src/com/android/settings/wifi/WifiConnectionPreferenceController.java index 12a6d143656..742edd196dd 100644 --- a/src/com/android/settings/wifi/WifiConnectionPreferenceController.java +++ b/src/com/android/settings/wifi/WifiConnectionPreferenceController.java @@ -17,22 +17,32 @@ package com.android.settings.wifi; import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkScoreManager; +import android.net.wifi.WifiManager; import android.os.Bundle; -import android.util.FeatureFlagUtils; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Process; +import android.os.SimpleClock; +import android.os.SystemClock; +import androidx.annotation.VisibleForTesting; import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.core.SubSettingLauncher; -import com.android.settings.wifi.details.WifiNetworkDetailsFragment; import com.android.settings.wifi.details2.WifiNetworkDetailsFragment2; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; -import com.android.settingslib.wifi.AccessPoint; -import com.android.settingslib.wifi.AccessPointPreference; -import com.android.settingslib.wifi.WifiTracker; -import com.android.settingslib.wifi.WifiTrackerFactory; +import com.android.settingslib.wifi.WifiEntryPreference; +import com.android.wifitrackerlib.WifiEntry; +import com.android.wifitrackerlib.WifiPickerTracker; + +import java.time.Clock; +import java.time.ZoneOffset; // TODO(b/151133650): Replace AbstractPreferenceController with BasePreferenceController. /** @@ -40,21 +50,28 @@ import com.android.settingslib.wifi.WifiTrackerFactory; * controller class when there is a wifi connection present. */ public class WifiConnectionPreferenceController extends AbstractPreferenceController implements - WifiTracker.WifiListener { + WifiPickerTracker.WifiPickerTrackerCallback { private static final String TAG = "WifiConnPrefCtrl"; private static final String KEY = "active_wifi_connection"; + // Max age of tracked WifiEntries. + private static final long MAX_SCAN_AGE_MILLIS = 15_000; + // Interval between initiating WifiPickerTracker scans. + private static final long SCAN_INTERVAL_MILLIS = 10_000; + private UpdateListener mUpdateListener; private Context mPrefContext; private String mPreferenceGroupKey; private PreferenceGroup mPreferenceGroup; - private WifiTracker mWifiTracker; - private AccessPointPreference mPreference; - private AccessPointPreference.UserBadgeCache mBadgeCache; + @VisibleForTesting + public WifiPickerTracker mWifiPickerTracker; + private WifiEntryPreference mPreference; private int order; private int mMetricsCategory; + // Worker thread used for WifiPickerTracker work. + private HandlerThread mWorkerThread; /** * Used to notify a parent controller that this controller has changed in availability, or has @@ -82,16 +99,34 @@ public class WifiConnectionPreferenceController extends AbstractPreferenceContro super(context); mUpdateListener = updateListener; mPreferenceGroupKey = preferenceGroupKey; - mWifiTracker = WifiTrackerFactory.create(context, this, lifecycle, true /* includeSaved */, - true /* includeScans */); this.order = order; mMetricsCategory = metricsCategory; - mBadgeCache = new AccessPointPreference.UserBadgeCache(context.getPackageManager()); + + mWorkerThread = new HandlerThread( + TAG + "{" + Integer.toHexString(System.identityHashCode(this)) + "}", + Process.THREAD_PRIORITY_BACKGROUND); + mWorkerThread.start(); + final Clock elapsedRealtimeClock = new SimpleClock(ZoneOffset.UTC) { + @Override + public long millis() { + return SystemClock.elapsedRealtime(); + } + }; + mWifiPickerTracker = new WifiPickerTracker(lifecycle, context, + context.getSystemService(WifiManager.class), + context.getSystemService(ConnectivityManager.class), + context.getSystemService(NetworkScoreManager.class), + new Handler(Looper.getMainLooper()), + mWorkerThread.getThreadHandler(), + elapsedRealtimeClock, + MAX_SCAN_AGE_MILLIS, + SCAN_INTERVAL_MILLIS, + this); } @Override public boolean isAvailable() { - return mWifiTracker.isConnected() && getCurrentAccessPoint() != null; + return mWifiPickerTracker.getConnectedWifiEntry() != null; } @Override @@ -107,88 +142,69 @@ public class WifiConnectionPreferenceController extends AbstractPreferenceContro update(); } - private AccessPoint getCurrentAccessPoint() { - for (AccessPoint accessPoint : mWifiTracker.getAccessPoints()) { - if (accessPoint.isActive()) { - return accessPoint; - } - } - return null; - } - - private void updatePreference(AccessPoint accessPoint) { + private void updatePreference(WifiEntry wifiEntry) { if (mPreference != null) { mPreferenceGroup.removePreference(mPreference); mPreference = null; } - if (accessPoint == null) { + if (wifiEntry == null || mPrefContext == null) { return; } - if (mPrefContext != null) { - mPreference = new AccessPointPreference(accessPoint, mPrefContext, mBadgeCache, - R.drawable.ic_wifi_signal_0, false /* forSavedNetworks */); - mPreference.setKey(KEY); - mPreference.refresh(); - mPreference.setOrder(order); - if (FeatureFlagUtils.isEnabled(mPrefContext, FeatureFlagUtils.SETTINGS_WIFITRACKER2)) { - mPreference.setOnPreferenceClickListener(pref -> { - Bundle args = new Bundle(); - mPreference.getAccessPoint().saveWifiState(args); - new SubSettingLauncher(mPrefContext) - .setTitleRes(R.string.pref_title_network_details) - .setDestination(WifiNetworkDetailsFragment2.class.getName()) - .setArguments(args) - .setSourceMetricsCategory(mMetricsCategory) - .launch(); - return true; - }); - } else { - mPreference.setOnPreferenceClickListener(pref -> { - Bundle args = new Bundle(); - mPreference.getAccessPoint().saveWifiState(args); - new SubSettingLauncher(mPrefContext) - .setTitleRes(R.string.pref_title_network_details) - .setDestination(WifiNetworkDetailsFragment.class.getName()) - .setArguments(args) - .setSourceMetricsCategory(mMetricsCategory) - .launch(); - return true; - }); - } - mPreferenceGroup.addPreference(mPreference); - } + mPreference = new WifiEntryPreference(mPrefContext, wifiEntry); + mPreference.setKey(KEY); + mPreference.refresh(); + mPreference.setOrder(order); + mPreference.setOnPreferenceClickListener(pref -> { + final Bundle args = new Bundle(); + args.putString(WifiNetworkDetailsFragment2.KEY_CHOSEN_WIFIENTRY_KEY, + wifiEntry.getKey()); + new SubSettingLauncher(mPrefContext) + .setTitleRes(R.string.pref_title_network_details) + .setDestination(WifiNetworkDetailsFragment2.class.getName()) + .setArguments(args) + .setSourceMetricsCategory(mMetricsCategory) + .launch(); + return true; + }); + mPreferenceGroup.addPreference(mPreference); } private void update() { - AccessPoint connectedAccessPoint = null; - if (mWifiTracker.isConnected()) { - connectedAccessPoint = getCurrentAccessPoint(); - } - if (connectedAccessPoint == null) { + final WifiEntry connectedWifiEntry = mWifiPickerTracker.getConnectedWifiEntry(); + if (connectedWifiEntry == null) { updatePreference(null); } else { - if (mPreference == null || !mPreference.getAccessPoint().equals(connectedAccessPoint)) { - updatePreference(connectedAccessPoint); - } else if (mPreference != null) { - mPreference.refresh(); - } + if (mPreference == null || !mPreference.getWifiEntry().equals(connectedWifiEntry)) { + updatePreference(connectedWifiEntry); + } else if (mPreference != null) { + mPreference.refresh(); + } } mUpdateListener.onChildrenUpdated(); } + /** Called when the state of Wifi has changed. */ @Override - public void onWifiStateChanged(int state) { + public void onWifiStateChanged() { + update(); + } + + /** + * Update the results when data changes. + */ + @Override + public void onWifiEntriesChanged() { update(); } @Override - public void onConnectedChanged() { - update(); + public void onNumSavedSubscriptionsChanged() { + // Do nothing. } @Override - public void onAccessPointsChanged() { - update(); + public void onNumSavedNetworksChanged() { + // Do nothing. } } diff --git a/tests/robotests/src/com/android/settings/network/WifiConnectionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/WifiConnectionPreferenceControllerTest.java index 703731858c3..ea957c3e762 100644 --- a/tests/robotests/src/com/android/settings/network/WifiConnectionPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/network/WifiConnectionPreferenceControllerTest.java @@ -29,12 +29,15 @@ import static org.mockito.Mockito.when; import android.content.Context; +import androidx.lifecycle.LifecycleOwner; +import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceScreen; + import com.android.settings.wifi.WifiConnectionPreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; -import com.android.settingslib.wifi.AccessPoint; -import com.android.settingslib.wifi.AccessPointPreference; -import com.android.settingslib.wifi.WifiTracker; -import com.android.settingslib.wifi.WifiTrackerFactory; +import com.android.settingslib.wifi.WifiEntryPreference; +import com.android.wifitrackerlib.WifiEntry; +import com.android.wifitrackerlib.WifiPickerTracker; import org.junit.Before; import org.junit.Test; @@ -45,19 +48,12 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -import java.util.ArrayList; -import java.util.Arrays; - -import androidx.lifecycle.LifecycleOwner; -import androidx.preference.PreferenceCategory; -import androidx.preference.PreferenceScreen; - @RunWith(RobolectricTestRunner.class) public class WifiConnectionPreferenceControllerTest { private static final String KEY = "wifi_connection"; @Mock - WifiTracker mWifiTracker; + WifiPickerTracker mWifiPickerTracker; @Mock PreferenceScreen mScreen; @Mock @@ -74,7 +70,6 @@ public class WifiConnectionPreferenceControllerTest { public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); - WifiTrackerFactory.setTestingWifiTracker(mWifiTracker); mLifecycleOwner = () -> mLifecycle; mLifecycle = new Lifecycle(mLifecycleOwner); when(mScreen.findPreference(eq(KEY))).thenReturn(mPreferenceCategory); @@ -83,49 +78,51 @@ public class WifiConnectionPreferenceControllerTest { mController = new WifiConnectionPreferenceController(mContext, mLifecycle, mUpdateListener, KEY, 0, 0); + mController.mWifiPickerTracker = mWifiPickerTracker; } @Test - public void isAvailable_noWiFiConnection_availableIsFalse() { - when(mWifiTracker.isConnected()).thenReturn(false); + public void isAvailable_noConnectedWifiEntry_availableIsFalse() { + when(mWifiPickerTracker.getConnectedWifiEntry()).thenReturn(null); + assertThat(mController.isAvailable()).isFalse(); } @Test - public void displayPreference_noWiFiConnection_noPreferenceAdded() { - when(mWifiTracker.isConnected()).thenReturn(false); - when(mWifiTracker.getAccessPoints()).thenReturn(new ArrayList<>()); + public void displayPreference_noConnectedWifiEntry_noPreferenceAdded() { + when(mWifiPickerTracker.getConnectedWifiEntry()).thenReturn(null); + mController.displayPreference(mScreen); + verify(mPreferenceCategory, never()).addPreference(any()); } @Test - public void displayPreference_hasWiFiConnection_preferenceAdded() { - when(mWifiTracker.isConnected()).thenReturn(true); - final AccessPoint accessPoint = mock(AccessPoint.class); - when(accessPoint.isActive()).thenReturn(true); - when(mWifiTracker.getAccessPoints()).thenReturn(Arrays.asList(accessPoint)); + public void displayPreference_hasConnectedWifiEntry_preferenceAdded() { + final WifiEntry wifiEntry = mock(WifiEntry.class); + when(mWifiPickerTracker.getConnectedWifiEntry()).thenReturn(wifiEntry); + mController.displayPreference(mScreen); - verify(mPreferenceCategory).addPreference(any(AccessPointPreference.class)); + verify(mPreferenceCategory).addPreference(any(WifiEntryPreference.class)); } @Test public void onConnectedChanged_wifiBecameDisconnected_preferenceRemoved() { - when(mWifiTracker.isConnected()).thenReturn(true); - final AccessPoint accessPoint = mock(AccessPoint.class); + final WifiEntry wifiEntry = mock(WifiEntry.class); + when(mWifiPickerTracker.getConnectedWifiEntry()).thenReturn(wifiEntry); - when(accessPoint.isActive()).thenReturn(true); - when(mWifiTracker.getAccessPoints()).thenReturn(Arrays.asList(accessPoint)); mController.displayPreference(mScreen); - final ArgumentCaptor captor = ArgumentCaptor.forClass( - AccessPointPreference.class); + final ArgumentCaptor captor = ArgumentCaptor.forClass( + WifiEntryPreference.class); verify(mPreferenceCategory).addPreference(captor.capture()); - final AccessPointPreference pref = captor.getValue(); + final WifiEntryPreference pref = captor.getValue(); - when(mWifiTracker.isConnected()).thenReturn(false); - when(mWifiTracker.getAccessPoints()).thenReturn(new ArrayList<>()); + // Become disconnected. + when(mWifiPickerTracker.getConnectedWifiEntry()).thenReturn(null); final int onUpdatedCountBefore = mOnChildUpdatedCount; - mController.onConnectedChanged(); + + mController.onWifiStateChanged(); + verify(mPreferenceCategory).removePreference(pref); assertThat(mOnChildUpdatedCount).isEqualTo(onUpdatedCountBefore + 1); } @@ -133,28 +130,24 @@ public class WifiConnectionPreferenceControllerTest { @Test public void onAccessPointsChanged_wifiBecameConnectedToDifferentAP_preferenceReplaced() { - when(mWifiTracker.isConnected()).thenReturn(true); - final AccessPoint accessPoint1 = mock(AccessPoint.class); - - when(accessPoint1.isActive()).thenReturn(true); - when(mWifiTracker.getAccessPoints()).thenReturn(Arrays.asList(accessPoint1)); + final WifiEntry wifiEntry1 = mock(WifiEntry.class); + when(wifiEntry1.getKey()).thenReturn("KEY_1"); + when(mWifiPickerTracker.getConnectedWifiEntry()).thenReturn(wifiEntry1); mController.displayPreference(mScreen); - final ArgumentCaptor captor = ArgumentCaptor.forClass( - AccessPointPreference.class); + final ArgumentCaptor captor = ArgumentCaptor.forClass( + WifiEntryPreference.class); - - final AccessPoint accessPoint2 = mock(AccessPoint.class); - when(accessPoint1.isActive()).thenReturn(false); - when(accessPoint2.isActive()).thenReturn(true); - when(mWifiTracker.getAccessPoints()).thenReturn(Arrays.asList(accessPoint1, accessPoint2)); + final WifiEntry wifiEntry2 = mock(WifiEntry.class); + when(wifiEntry1.getKey()).thenReturn("KEY_2"); + when(mWifiPickerTracker.getConnectedWifiEntry()).thenReturn(wifiEntry2); final int onUpdatedCountBefore = mOnChildUpdatedCount; - mController.onAccessPointsChanged(); + mController.onWifiEntriesChanged(); verify(mPreferenceCategory, times(2)).addPreference(captor.capture()); - final AccessPointPreference pref1 = captor.getAllValues().get(0); - final AccessPointPreference pref2 = captor.getAllValues().get(1); - assertThat(pref1.getAccessPoint()).isEqualTo(accessPoint1); - assertThat(pref2.getAccessPoint()).isEqualTo(accessPoint2); + final WifiEntryPreference pref1 = captor.getAllValues().get(0); + final WifiEntryPreference pref2 = captor.getAllValues().get(1); + assertThat(pref1.getWifiEntry()).isEqualTo(wifiEntry1); + assertThat(pref2.getWifiEntry()).isEqualTo(wifiEntry2); verify(mPreferenceCategory).removePreference(eq(pref1)); assertThat(mOnChildUpdatedCount).isEqualTo(onUpdatedCountBefore + 1); } From 83d0de8d07c4a3aa6dd56a1975ea0eed1f430c98 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Thu, 26 Mar 2020 14:03:43 -0400 Subject: [PATCH 09/10] Fix broken test Test: this Fixes: 152374833 Change-Id: I9065f6b3700fa288cb3fd65ef2b24c2821325e51 --- .../app/ConversationListPreferenceController.java | 2 ++ .../app/ConversationListPreferenceControllerTest.java | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/notification/app/ConversationListPreferenceController.java b/src/com/android/settings/notification/app/ConversationListPreferenceController.java index 2dc52bf050d..4d424839415 100644 --- a/src/com/android/settings/notification/app/ConversationListPreferenceController.java +++ b/src/com/android/settings/notification/app/ConversationListPreferenceController.java @@ -70,6 +70,8 @@ public abstract class ConversationListPreferenceController extends AbstractPrefe if (containerGroup.getPreferenceCount() == 0) { containerGroup.setVisible(false); + } else { + containerGroup.setVisible(true); } } diff --git a/tests/robotests/src/com/android/settings/notification/app/ConversationListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/ConversationListPreferenceControllerTest.java index 6cc20d78acd..dc82adbd028 100644 --- a/tests/robotests/src/com/android/settings/notification/app/ConversationListPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/app/ConversationListPreferenceControllerTest.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -34,6 +35,8 @@ import android.provider.Settings; import android.service.notification.ConversationChannelWrapper; import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceManager; +import androidx.preference.PreferenceScreen; import com.android.settings.applications.AppInfoBase; import com.android.settings.notification.NotificationBackend; @@ -85,7 +88,10 @@ public class ConversationListPreferenceControllerTest { @Test public void testPopulateList_validConversations() { - PreferenceCategory outerContainer = mock(PreferenceCategory.class); + final PreferenceManager preferenceManager = new PreferenceManager(mContext); + PreferenceScreen ps = preferenceManager.createPreferenceScreen(mContext); + PreferenceCategory outerContainer = spy(new PreferenceCategory(mContext)); + ps.addPreference(outerContainer); ConversationChannelWrapper ccw = new ConversationChannelWrapper(); ccw.setNotificationChannel(mock(NotificationChannel.class)); From 28a034d6edfc1271e21b813574b5a0d9c53be467 Mon Sep 17 00:00:00 2001 From: Kevin Chyn Date: Thu, 26 Mar 2020 17:54:27 -0700 Subject: [PATCH 10/10] Adjust ConfirmDeviceCredentialActivity system bars CDCA is a transparent activity with the sole purpose of requesting authentication. Since authentication is all drawn by SystemUI, we should also stop this activity from drawing the StatusBar. Register to receive biometric system events (early user canceled), so that we can finish() and start the activity transition simultaneously. This fixes some navigation bar jank. Bug: 148273355 Test: Set up a work profile, then install BiometricPromptDemo Disable one-lock and set up a password/biometric for the work profile. Lock/unlock screen, then open the work profile version of the app. No status bar jank seen. Change-Id: I54a352527ed007dcaf1bea14a51711e4022fe028 --- ..._credential_biometric_transition_enter.xml | 24 ------------------- .../settings/password/BiometricFragment.java | 14 +++++++---- .../ConfirmDeviceCredentialActivity.java | 23 +++++++++++------- 3 files changed, 24 insertions(+), 37 deletions(-) delete mode 100644 res/anim/confirm_credential_biometric_transition_enter.xml diff --git a/res/anim/confirm_credential_biometric_transition_enter.xml b/res/anim/confirm_credential_biometric_transition_enter.xml deleted file mode 100644 index 56f35930b8e..00000000000 --- a/res/anim/confirm_credential_biometric_transition_enter.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/com/android/settings/password/BiometricFragment.java b/src/com/android/settings/password/BiometricFragment.java index 7e783227362..fc9e42e00fa 100644 --- a/src/com/android/settings/password/BiometricFragment.java +++ b/src/com/android/settings/password/BiometricFragment.java @@ -77,6 +77,13 @@ public class BiometricFragment extends InstrumentedFragment { mClientCallback.onAuthenticationFailed(); }); } + + @Override + public void onSystemEvent(int event) { + mClientExecutor.execute(() -> { + mClientCallback.onSystemEvent(event); + }); + } }; private final DialogInterface.OnClickListener mNegativeButtonListener = @@ -121,10 +128,6 @@ public class BiometricFragment extends InstrumentedFragment { } } - boolean isAuthenticating() { - return mAuthenticating; - } - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -144,7 +147,8 @@ public class BiometricFragment extends InstrumentedFragment { .setConfirmationRequired(mBundle.getBoolean( BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true)) .setDisallowBiometricsIfPolicyExists(mBundle.getBoolean( - BiometricPrompt.EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS, false)); + BiometricPrompt.EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS, false)) + .setReceiveSystemEvents(true); mBiometricPrompt = builder.build(); mCancellationSignal = new CancellationSignal(); diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java index 220b64929ad..a9a28469c6f 100644 --- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java +++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java @@ -25,6 +25,7 @@ import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; import android.content.Context; import android.content.Intent; +import android.graphics.Color; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricPrompt; @@ -35,6 +36,7 @@ import android.os.Looper; import android.os.UserHandle; import android.os.UserManager; import android.util.Log; +import android.view.WindowManager; import androidx.annotation.NonNull; import androidx.fragment.app.FragmentActivity; @@ -142,6 +144,16 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity { public void onAuthenticationFailed() { mDevicePolicyManager.reportFailedBiometricAttempt(mUserId); } + + @Override + public void onSystemEvent(int event) { + Log.d(TAG, "SystemEvent: " + event); + switch (event) { + case BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL: + finish(); + break; + } + } }; private String getStringForError(int errorCode) { @@ -159,6 +171,9 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + getWindow().setStatusBarColor(Color.TRANSPARENT); + mBiometricManager = getSystemService(BiometricManager.class); mDevicePolicyManager = getSystemService(DevicePolicyManager.class); mUserManager = UserManager.get(this); @@ -379,14 +394,6 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity { finish(); } - @Override - public void finish() { - super.finish(); - // Finish without animation since the activity is just there so we can launch - // BiometricPrompt. - overridePendingTransition(R.anim.confirm_credential_biometric_transition_enter, 0); - } - private boolean isInternalActivity() { return this instanceof ConfirmDeviceCredentialActivity.InternalActivity; }