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 c8476cb726..71bd6089cc 100644 --- a/tests/unit/Android.bp +++ b/tests/unit/Android.bp @@ -62,6 +62,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); + } }