From 5dc6ca03186cf0fa3492c5d483488ab6725d04e2 Mon Sep 17 00:00:00 2001 From: Remi NGUYEN VAN Date: Fri, 30 Jul 2021 18:14:48 +0900 Subject: [PATCH] Add overlay options for no internet notifications Add an option to display the no internet dialog directly instead of showing a notification when the notification would have been high priority (typically when the network was explicitly selected). This is disabled by default, but allows device manufacturers to use a slightly more disruptive UX to ensure that the user is aware that the network has no connectivity, and can take action. Also add an option to show the same notification as "no internet" instead of the "partial connectivity" notification. This is also disabled by default, but allows device manufacturers to use the "no internet" text if they feel that "partial connectivity" text is hard to understand for the user. Bug: 193847396 Test: atest NetworkNotificationManagerTest Change-Id: Ib5bd74d8cf973bf70d373dd63648c178fae0ebae --- .../res/values/config.xml | 11 ++ .../res/values/overlayable.xml | 2 + .../NetworkNotificationManager.java | 31 ++++- tests/unit/Android.bp | 1 + tests/unit/AndroidManifest.xml | 2 + .../NetworkNotificationManagerTest.java | 112 ++++++++++++++++++ 6 files changed, 158 insertions(+), 1 deletion(-) diff --git a/service/ServiceConnectivityResources/res/values/config.xml b/service/ServiceConnectivityResources/res/values/config.xml index bf32ad5ac5..b22457a9bb 100644 --- a/service/ServiceConnectivityResources/res/values/config.xml +++ b/service/ServiceConnectivityResources/res/values/config.xml @@ -114,4 +114,15 @@ true + + false + + + false + diff --git a/service/ServiceConnectivityResources/res/values/overlayable.xml b/service/ServiceConnectivityResources/res/values/overlayable.xml index 6ac6a0e6a6..5af13d764b 100644 --- a/service/ServiceConnectivityResources/res/values/overlayable.xml +++ b/service/ServiceConnectivityResources/res/values/overlayable.xml @@ -32,6 +32,8 @@ + + diff --git a/service/src/com/android/server/connectivity/NetworkNotificationManager.java b/service/src/com/android/server/connectivity/NetworkNotificationManager.java index ae98d92073..155f6c4395 100644 --- a/service/src/com/android/server/connectivity/NetworkNotificationManager.java +++ b/service/src/com/android/server/connectivity/NetworkNotificationManager.java @@ -198,11 +198,22 @@ public class NetworkNotificationManager { } final Resources r = mResources.get(); + if (highPriority && maybeNotifyViaDialog(r, notifyType, intent)) { + Log.d(TAG, "Notified via dialog for event " + nameOf(eventId)); + return; + } + final CharSequence title; final CharSequence details; Icon icon = Icon.createWithResource( mResources.getResourcesContext(), getIcon(transportType)); - if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) { + final boolean showAsNoInternet = notifyType == NotificationType.PARTIAL_CONNECTIVITY + && r.getBoolean(R.bool.config_partialConnectivityNotifiedAsNoInternet); + if (showAsNoInternet) { + Log.d(TAG, "Showing partial connectivity as NO_INTERNET"); + } + if ((notifyType == NotificationType.NO_INTERNET || showAsNoInternet) + && transportType == TRANSPORT_WIFI) { title = r.getString(R.string.wifi_no_internet, name); details = r.getString(R.string.wifi_no_internet_detailed); } else if (notifyType == NotificationType.PRIVATE_DNS_BROKEN) { @@ -306,6 +317,24 @@ public class NetworkNotificationManager { } } + private boolean maybeNotifyViaDialog(Resources res, NotificationType notifyType, + PendingIntent intent) { + if (notifyType != NotificationType.NO_INTERNET + && notifyType != NotificationType.PARTIAL_CONNECTIVITY) { + return false; + } + if (!res.getBoolean(R.bool.config_notifyNoInternetAsDialogWhenHighPriority)) { + return false; + } + + try { + intent.send(); + } catch (PendingIntent.CanceledException e) { + Log.e(TAG, "Error sending dialog PendingIntent", e); + } + return true; + } + /** * Clear the notification with the given id, only if it matches the given type. */ diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp index a7f57e8c92..96ea761519 100644 --- a/tests/unit/Android.bp +++ b/tests/unit/Android.bp @@ -58,6 +58,7 @@ android_library { jarjar_rules: "jarjar-rules.txt", static_libs: [ "androidx.test.rules", + "androidx.test.uiautomator", "bouncycastle-repackaged-unbundled", "core-tests-support", "FrameworksNetCommonTests", diff --git a/tests/unit/AndroidManifest.xml b/tests/unit/AndroidManifest.xml index 4c60ccf606..887f17158b 100644 --- a/tests/unit/AndroidManifest.xml +++ b/tests/unit/AndroidManifest.xml @@ -53,6 +53,8 @@ + finish()); + setContentView(txt); + } + } + @Mock Context mCtx; @Mock Resources mResources; @Mock DisplayMetrics mDisplayMetrics; @@ -345,4 +379,82 @@ public class NetworkNotificationManagerTest { mManager.clearNotification(id, PARTIAL_CONNECTIVITY); verify(mNotificationManager, never()).cancel(eq(tag), eq(PARTIAL_CONNECTIVITY.eventId)); } + + @Test + public void testNotifyNoInternetAsDialogWhenHighPriority() throws Exception { + doReturn(true).when(mResources).getBoolean( + R.bool.config_notifyNoInternetAsDialogWhenHighPriority); + + mManager.showNotification(TEST_NOTIF_ID, NETWORK_SWITCH, mWifiNai, mCellNai, null, false); + // Non-"no internet" notifications are not affected + verify(mNotificationManager).notify(eq(TEST_NOTIF_TAG), eq(NETWORK_SWITCH.eventId), any()); + + final Instrumentation instr = InstrumentationRegistry.getInstrumentation(); + final Context ctx = instr.getContext(); + final String testAction = "com.android.connectivity.coverage.TEST_DIALOG"; + final Intent intent = new Intent(testAction) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .setClassName(ctx.getPackageName(), TestDialogActivity.class.getName()); + final PendingIntent pendingIntent = PendingIntent.getActivity(ctx, 0 /* requestCode */, + intent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); + + mManager.showNotification(TEST_NOTIF_ID, NO_INTERNET, mWifiNai, null /* switchToNai */, + pendingIntent, true /* highPriority */); + + // Previous notifications are still dismissed + verify(mNotificationManager).cancel(TEST_NOTIF_TAG, NETWORK_SWITCH.eventId); + + // Verify that the activity is shown (the activity shows the action on screen) + final UiObject actionText = UiDevice.getInstance(instr).findObject( + new UiSelector().text(testAction)); + assertTrue("Activity not shown", actionText.waitForExists(TEST_TIMEOUT_MS)); + + // Tapping the text should dismiss the dialog + actionText.click(); + assertTrue("Activity not dismissed", actionText.waitUntilGone(TEST_TIMEOUT_MS)); + + // Verify no NO_INTERNET notification was posted + verify(mNotificationManager, never()).notify(any(), eq(NO_INTERNET.eventId), any()); + } + + private void doNotificationTextTest(NotificationType type, @StringRes int expectedTitleRes, + String expectedTitleArg, @StringRes int expectedContentRes) { + final String expectedTitle = "title " + expectedTitleArg; + final String expectedContent = "expected content"; + doReturn(expectedTitle).when(mResources).getString(expectedTitleRes, expectedTitleArg); + doReturn(expectedContent).when(mResources).getString(expectedContentRes); + + mManager.showNotification(TEST_NOTIF_ID, type, mWifiNai, mCellNai, null, false); + final ArgumentCaptor notifCap = ArgumentCaptor.forClass(Notification.class); + + verify(mNotificationManager).notify(eq(TEST_NOTIF_TAG), eq(type.eventId), + notifCap.capture()); + final Notification notif = notifCap.getValue(); + + assertEquals(expectedTitle, notif.extras.getString(Notification.EXTRA_TITLE)); + assertEquals(expectedContent, notif.extras.getString(Notification.EXTRA_TEXT)); + } + + @Test + public void testNotificationText_NoInternet() { + doNotificationTextTest(NO_INTERNET, + R.string.wifi_no_internet, TEST_EXTRA_INFO, + R.string.wifi_no_internet_detailed); + } + + @Test + public void testNotificationText_Partial() { + doNotificationTextTest(PARTIAL_CONNECTIVITY, + R.string.network_partial_connectivity, TEST_EXTRA_INFO, + R.string.network_partial_connectivity_detailed); + } + + @Test + public void testNotificationText_PartialAsNoInternet() { + doReturn(true).when(mResources).getBoolean( + R.bool.config_partialConnectivityNotifiedAsNoInternet); + doNotificationTextTest(PARTIAL_CONNECTIVITY, + R.string.wifi_no_internet, TEST_EXTRA_INFO, + R.string.wifi_no_internet_detailed); + } }