summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk9
-rw-r--r--StubLibraries.bp20
-rw-r--r--packages/Tethering/res/values-mcc310-mnc004/config.xml3
-rw-r--r--packages/Tethering/res/values-mcc311-mnc480/config.xml3
-rw-r--r--packages/Tethering/res/values/config.xml5
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/Tethering.java17
-rw-r--r--packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java102
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt186
-rw-r--r--packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java25
-rw-r--r--services/Android.bp5
-rw-r--r--services/core/java/com/android/server/UserspaceRebootLogger.java29
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java4
-rw-r--r--telephony/java/com/android/internal/telephony/DctConstants.java4
-rw-r--r--tests/AppLaunch/Android.bp4
-rw-r--r--tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java151
-rw-r--r--tests/net/common/java/android/net/DhcpInfoTest.java113
16 files changed, 571 insertions, 109 deletions
diff --git a/Android.mk b/Android.mk
index aea0c951052f..d3627e118ae3 100644
--- a/Android.mk
+++ b/Android.mk
@@ -36,15 +36,6 @@ include $(CLEAR_VARS)
# always included.
INTERNAL_SDK_SOURCE_DIRS := $(addprefix $(LOCAL_PATH)/,$(dirs_to_document))
-$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
-$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE))
-$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE))
-$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE):apistubs/android/public/api/android.txt)
-$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE):apistubs/android/system/api/android.txt)
-$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE):apistubs/android/test/api/android.txt)
-$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_MODULE_LIB_API_FILE):apistubs/android/module-lib/api/android.txt)
-$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_SERVER_API_FILE):apistubs/android/system-server/api/android.txt)
-
# sdk.atree needs to copy the whole dir: $(OUT_DOCS)/offline-sdk to the final zip.
# So keep offline-sdk-timestamp target here, and unzip offline-sdk-docs.zip to
# $(OUT_DOCS)/offline-sdk.
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 304f231cb3af..20c8a83ff83e 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -110,6 +110,11 @@ droidstubs {
baseline_file: "api/lint-baseline.txt",
},
},
+ dist: {
+ targets: ["sdk", "win_sdk"],
+ dir: "apistubs/android/public/api",
+ dest: "android.txt",
+ },
jdiff_enabled: true,
}
@@ -151,6 +156,11 @@ droidstubs {
baseline_file: "api/system-lint-baseline.txt",
},
},
+ dist: {
+ targets: ["sdk", "win_sdk"],
+ dir: "apistubs/android/system/api",
+ dest: "android.txt",
+ },
jdiff_enabled: true,
}
@@ -174,6 +184,11 @@ droidstubs {
baseline_file: "api/test-lint-baseline.txt",
},
},
+ dist: {
+ targets: ["sdk", "win_sdk"],
+ dir: "apistubs/android/test/api",
+ dest: "android.txt",
+ },
}
/////////////////////////////////////////////////////////////////////
@@ -213,6 +228,11 @@ droidstubs {
baseline_file: "api/module-lib-lint-baseline.txt",
},
},
+ dist: {
+ targets: ["sdk", "win_sdk"],
+ dir: "apistubs/android/module-lib/api",
+ dest: "android.txt",
+ },
}
diff --git a/packages/Tethering/res/values-mcc310-mnc004/config.xml b/packages/Tethering/res/values-mcc310-mnc004/config.xml
index 8c627d5df058..5c5be0466a36 100644
--- a/packages/Tethering/res/values-mcc310-mnc004/config.xml
+++ b/packages/Tethering/res/values-mcc310-mnc004/config.xml
@@ -17,4 +17,7 @@
<!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
"0" for disable this feature. -->
<integer name="delay_to_show_no_upstream_after_no_backhaul">5000</integer>
+
+ <!-- Config for showing upstream roaming notification. -->
+ <bool name="config_upstream_roaming_notification">true</bool>
</resources> \ No newline at end of file
diff --git a/packages/Tethering/res/values-mcc311-mnc480/config.xml b/packages/Tethering/res/values-mcc311-mnc480/config.xml
index 8c627d5df058..5c5be0466a36 100644
--- a/packages/Tethering/res/values-mcc311-mnc480/config.xml
+++ b/packages/Tethering/res/values-mcc311-mnc480/config.xml
@@ -17,4 +17,7 @@
<!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
"0" for disable this feature. -->
<integer name="delay_to_show_no_upstream_after_no_backhaul">5000</integer>
+
+ <!-- Config for showing upstream roaming notification. -->
+ <bool name="config_upstream_roaming_notification">true</bool>
</resources> \ No newline at end of file
diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml
index 780a015961b4..aed5ab8df6af 100644
--- a/packages/Tethering/res/values/config.xml
+++ b/packages/Tethering/res/values/config.xml
@@ -206,4 +206,9 @@
<!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
"-1" for disable this feature. -->
<integer name="delay_to_show_no_upstream_after_no_backhaul">-1</integer>
+
+ <!-- Cellular roaming notification is shown when upstream is cellular network and in roaming
+ state. -->
+ <!-- Config for showing upstream roaming notification. -->
+ <bool name="config_upstream_roaming_notification">false</bool>
</resources>
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 14d288699e99..97b19466b38b 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -81,6 +81,7 @@ import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.TetherStatesParcel;
import android.net.TetheredClient;
@@ -1476,7 +1477,7 @@ public class Tethering {
if (mTetherUpstream != newUpstream) {
mTetherUpstream = newUpstream;
mUpstreamNetworkMonitor.setCurrentUpstream(mTetherUpstream);
- reportUpstreamChanged(mTetherUpstream);
+ reportUpstreamChanged(ns);
}
}
@@ -1598,7 +1599,8 @@ public class Tethering {
}
}
- private void handleUpstreamNetworkMonitorCallback(int arg1, Object o) {
+ @VisibleForTesting
+ void handleUpstreamNetworkMonitorCallback(int arg1, Object o) {
if (arg1 == UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES) {
mOffload.sendOffloadExemptPrefixes((Set<IpPrefix>) o);
return;
@@ -1624,6 +1626,9 @@ public class Tethering {
switch (arg1) {
case UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES:
+ if (ns.network.equals(mTetherUpstream)) {
+ mNotificationUpdater.onUpstreamCapabilitiesChanged(ns.networkCapabilities);
+ }
handleNewUpstreamNetworkState(ns);
break;
case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES:
@@ -2009,8 +2014,10 @@ public class Tethering {
});
}
- private void reportUpstreamChanged(Network network) {
+ private void reportUpstreamChanged(UpstreamNetworkState ns) {
final int length = mTetheringEventCallbacks.beginBroadcast();
+ final Network network = (ns != null) ? ns.network : null;
+ final NetworkCapabilities capabilities = (ns != null) ? ns.networkCapabilities : null;
try {
for (int i = 0; i < length; i++) {
try {
@@ -2022,7 +2029,9 @@ public class Tethering {
} finally {
mTetheringEventCallbacks.finishBroadcast();
}
- mNotificationUpdater.onUpstreamNetworkChanged(network);
+ // Need to notify capabilities change after upstream network changed because new network's
+ // capabilities should be checked every time.
+ mNotificationUpdater.onUpstreamCapabilitiesChanged(capabilities);
}
private void reportConfigurationChanged(TetheringConfigurationParcel config) {
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
index de2f90e50e2f..f490cc4719fa 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
@@ -16,6 +16,7 @@
package com.android.networkstack.tethering;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
@@ -30,7 +31,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.net.Network;
+import android.net.NetworkCapabilities;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -50,6 +51,9 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* A class to display tethering-related notifications.
*
@@ -82,6 +86,9 @@ public class TetheringNotificationUpdater {
// Id to update and cancel no upstream notification. Must be unique within the tethering app.
@VisibleForTesting
static final int NO_UPSTREAM_NOTIFICATION_ID = 1002;
+ // Id to update and cancel roaming notification. Must be unique within the tethering app.
+ @VisibleForTesting
+ static final int ROAMING_NOTIFICATION_ID = 1003;
@VisibleForTesting
static final int NO_ICON_ID = 0;
@VisibleForTesting
@@ -95,13 +102,14 @@ public class TetheringNotificationUpdater {
private final Handler mHandler;
// WARNING : the constructor is called on a different thread. Thread safety therefore
- // relies on these values being initialized to 0 or false, and not any other value. If you need
- // to change this, you will need to change the thread where the constructor is invoked,
- // or to introduce synchronization.
+ // relies on these values being initialized to 0, false or null, and not any other value. If you
+ // need to change this, you will need to change the thread where the constructor is invoked, or
+ // to introduce synchronization.
// Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2.
// This value has to be made 1 2 and 4, and OR'd with the others.
private int mDownstreamTypesMask = DOWNSTREAM_NONE;
private boolean mNoUpstream = false;
+ private boolean mRoaming = false;
// WARNING : this value is not able to being initialized to 0 and must have volatile because
// telephony service is not guaranteed that is up before tethering service starts. If telephony
@@ -110,7 +118,13 @@ public class TetheringNotificationUpdater {
// INVALID_SUBSCRIPTION_ID.
private volatile int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- @IntDef({ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID})
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ ENABLE_NOTIFICATION_ID,
+ RESTRICTED_NOTIFICATION_ID,
+ NO_UPSTREAM_NOTIFICATION_ID,
+ ROAMING_NOTIFICATION_ID
+ })
@interface NotificationId {}
private static final class MccMncOverrideInfo {
@@ -160,26 +174,22 @@ public class TetheringNotificationUpdater {
/** Called when downstream has changed */
public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) {
- if (mDownstreamTypesMask == downstreamTypesMask) return;
- mDownstreamTypesMask = downstreamTypesMask;
- updateEnableNotification();
- updateNoUpstreamNotification();
+ updateActiveNotifications(
+ mActiveDataSubId, downstreamTypesMask, mNoUpstream, mRoaming);
}
/** Called when active data subscription id changed */
public void onActiveDataSubscriptionIdChanged(final int subId) {
- if (mActiveDataSubId == subId) return;
- mActiveDataSubId = subId;
- updateEnableNotification();
- updateNoUpstreamNotification();
+ updateActiveNotifications(subId, mDownstreamTypesMask, mNoUpstream, mRoaming);
}
- /** Called when upstream network changed */
- public void onUpstreamNetworkChanged(@Nullable final Network network) {
- final boolean isNoUpstream = (network == null);
- if (mNoUpstream == isNoUpstream) return;
- mNoUpstream = isNoUpstream;
- updateNoUpstreamNotification();
+ /** Called when upstream network capabilities changed */
+ public void onUpstreamCapabilitiesChanged(@Nullable final NetworkCapabilities capabilities) {
+ final boolean isNoUpstream = (capabilities == null);
+ final boolean isRoaming = capabilities != null
+ && !capabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ updateActiveNotifications(
+ mActiveDataSubId, mDownstreamTypesMask, isNoUpstream, isRoaming);
}
@NonNull
@@ -208,6 +218,25 @@ public class TetheringNotificationUpdater {
return res;
}
+ private void updateActiveNotifications(final int subId, final int downstreamTypes,
+ final boolean noUpstream, final boolean isRoaming) {
+ final boolean tetheringActiveChanged =
+ (downstreamTypes == DOWNSTREAM_NONE) != (mDownstreamTypesMask == DOWNSTREAM_NONE);
+ final boolean subIdChanged = subId != mActiveDataSubId;
+ final boolean downstreamChanged = downstreamTypes != mDownstreamTypesMask;
+ final boolean upstreamChanged = noUpstream != mNoUpstream;
+ final boolean roamingChanged = isRoaming != mRoaming;
+ final boolean updateAll = tetheringActiveChanged || subIdChanged;
+ mActiveDataSubId = subId;
+ mDownstreamTypesMask = downstreamTypes;
+ mNoUpstream = noUpstream;
+ mRoaming = isRoaming;
+
+ if (updateAll || downstreamChanged) updateEnableNotification();
+ if (updateAll || upstreamChanged) updateNoUpstreamNotification();
+ if (updateAll || roamingChanged) updateRoamingNotification();
+ }
+
private void updateEnableNotification() {
final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE;
@@ -219,14 +248,20 @@ public class TetheringNotificationUpdater {
private void updateNoUpstreamNotification() {
final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE;
- if (tetheringInactive
- || !mNoUpstream
- || setupNoUpstreamNotification() == NO_NOTIFY) {
+ if (tetheringInactive || !mNoUpstream || setupNoUpstreamNotification() == NO_NOTIFY) {
clearNotification(NO_UPSTREAM_NOTIFICATION_ID);
mHandler.removeMessages(EVENT_SHOW_NO_UPSTREAM);
}
}
+ private void updateRoamingNotification() {
+ final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE;
+
+ if (tetheringInactive || !mRoaming || setupRoamingNotification() == NO_NOTIFY) {
+ clearNotification(ROAMING_NOTIFICATION_ID);
+ }
+ }
+
@VisibleForTesting
void tetheringRestrictionLifted() {
clearNotification(RESTRICTED_NOTIFICATION_ID);
@@ -333,6 +368,29 @@ public class TetheringNotificationUpdater {
return icons;
}
+ private boolean setupRoamingNotification() {
+ final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
+ final boolean upstreamRoamingNotification =
+ res.getBoolean(R.bool.config_upstream_roaming_notification);
+
+ if (!upstreamRoamingNotification) return NO_NOTIFY;
+
+ final String title = res.getString(R.string.upstream_roaming_notification_title);
+ final String message = res.getString(R.string.upstream_roaming_notification_message);
+ if (isEmpty(title) || isEmpty(message)) return NO_NOTIFY;
+
+ final PendingIntent pi = PendingIntent.getActivity(
+ mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
+ 0 /* requestCode */,
+ new Intent(Settings.ACTION_TETHER_SETTINGS),
+ Intent.FLAG_ACTIVITY_NEW_TASK,
+ null /* options */);
+
+ showNotification(R.drawable.stat_sys_tether_general, title, message,
+ ROAMING_NOTIFICATION_ID, pi, new Action[0]);
+ return NOTIFY_DONE;
+ }
+
private boolean setupNoUpstreamNotification() {
final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
final int delayToShowUpstreamNotification =
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
index 294bf1b7e169..04f31a7a2880 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt
@@ -23,10 +23,11 @@ import android.content.res.Resources
import android.net.ConnectivityManager.TETHERING_BLUETOOTH
import android.net.ConnectivityManager.TETHERING_USB
import android.net.ConnectivityManager.TETHERING_WIFI
-import android.net.Network
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
+import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING
import android.os.UserHandle
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.telephony.TelephonyManager
@@ -39,6 +40,7 @@ import com.android.networkstack.tethering.TetheringNotificationUpdater.ENABLE_NO
import com.android.networkstack.tethering.TetheringNotificationUpdater.EVENT_SHOW_NO_UPSTREAM
import com.android.networkstack.tethering.TetheringNotificationUpdater.NO_UPSTREAM_NOTIFICATION_ID
import com.android.networkstack.tethering.TetheringNotificationUpdater.RESTRICTED_NOTIFICATION_ID
+import com.android.networkstack.tethering.TetheringNotificationUpdater.ROAMING_NOTIFICATION_ID
import com.android.networkstack.tethering.TetheringNotificationUpdater.VERIZON_CARRIER_ID
import com.android.testutils.waitForIdle
import org.junit.After
@@ -75,6 +77,8 @@ const val TEST_MESSAGE = "Tap to set up hotspot."
const val TEST_NO_UPSTREAM_TITLE = "Hotspot has no internet access"
const val TEST_NO_UPSTREAM_MESSAGE = "Device cannot connect to internet."
const val TEST_NO_UPSTREAM_BUTTON = "Turn off hotspot"
+const val TEST_ROAMING_TITLE = "Hotspot is on"
+const val TEST_ROAMING_MESSAGE = "Additional charges may apply while roaming."
@RunWith(AndroidJUnit4::class)
@SmallTest
@@ -98,6 +102,11 @@ class TetheringNotificationUpdaterTest {
"WIFI|BT;android.test:drawable/general", "WIFI|USB;android.test:drawable/general",
"USB|BT;android.test:drawable/general", "WIFI|USB|BT;android.test:drawable/general")
+ private val ROAMING_CAPABILITIES = NetworkCapabilities()
+ private val HOME_CAPABILITIES = NetworkCapabilities().addCapability(NET_CAPABILITY_NOT_ROAMING)
+ private val NOTIFICATION_ICON_ID = R.drawable.stat_sys_tether_general
+ private val TIMEOUT_MS = 500L
+
private inner class TestContext(c: Context) : BroadcastInterceptingContext(c) {
override fun createContextAsUser(user: UserHandle, flags: Int) =
if (user == UserHandle.ALL) mockContext else this
@@ -123,6 +132,8 @@ class TetheringNotificationUpdaterTest {
.getStringArray(R.array.tethering_notification_icons)
doReturn(5).`when`(testResources)
.getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul)
+ doReturn(true).`when`(testResources)
+ .getBoolean(R.bool.config_upstream_roaming_notification)
doReturn(TITLE).`when`(defaultResources).getString(R.string.tethering_notification_title)
doReturn(MESSAGE).`when`(defaultResources)
.getString(R.string.tethering_notification_message)
@@ -135,6 +146,10 @@ class TetheringNotificationUpdaterTest {
.getString(R.string.no_upstream_notification_message)
doReturn(TEST_NO_UPSTREAM_BUTTON).`when`(testResources)
.getString(R.string.no_upstream_notification_disable_button)
+ doReturn(TEST_ROAMING_TITLE).`when`(testResources)
+ .getString(R.string.upstream_roaming_notification_title)
+ doReturn(TEST_ROAMING_MESSAGE).`when`(testResources)
+ .getString(R.string.upstream_roaming_notification_message)
doReturn(USB_ICON_ID).`when`(defaultResources)
.getIdentifier(eq("android.test:drawable/usb"), any(), any())
doReturn(BT_ICON_ID).`when`(defaultResources)
@@ -176,41 +191,25 @@ class TetheringNotificationUpdaterTest {
assertEquals(iconId, notification.smallIcon.resId)
assertEquals(title, notification.title())
assertEquals(text, notification.text())
- }
-
- private fun verifyNotificationCancelled(id: Int) =
- verify(notificationManager, times(1)).cancel(any(), eq(id))
- private val tetheringActiveNotifications =
- listOf(NO_UPSTREAM_NOTIFICATION_ID, ENABLE_NOTIFICATION_ID)
-
- private fun verifyCancelAllTetheringActiveNotifications() {
- tetheringActiveNotifications.forEach {
- verifyNotificationCancelled(it)
- }
reset(notificationManager)
}
- private fun verifyOnlyTetheringActiveNotification(
- notifyId: Int,
- iconId: Int,
- title: String,
- text: String
+ private fun verifyNotificationCancelled(
+ notificationIds: List<Int>,
+ resetAfterVerified: Boolean = true
) {
- tetheringActiveNotifications.forEach {
- when (it) {
- notifyId -> verifyNotification(iconId, title, text, notifyId)
- else -> verifyNotificationCancelled(it)
- }
+ notificationIds.forEach {
+ verify(notificationManager, times(1)).cancel(any(), eq(it))
}
- reset(notificationManager)
+ if (resetAfterVerified) reset(notificationManager)
}
@Test
fun testNotificationWithDownstreamChanged() {
// Wifi downstream. No notification.
notificationUpdater.onDownstreamChanged(WIFI_MASK)
- verifyCancelAllTetheringActiveNotifications()
+ verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID))
// Same downstream changed. Nothing happened.
notificationUpdater.onDownstreamChanged(WIFI_MASK)
@@ -218,23 +217,23 @@ class TetheringNotificationUpdaterTest {
// Wifi and usb downstreams. Show enable notification
notificationUpdater.onDownstreamChanged(WIFI_MASK or USB_MASK)
- verifyOnlyTetheringActiveNotification(
- ENABLE_NOTIFICATION_ID, GENERAL_ICON_ID, TITLE, MESSAGE)
+ verifyNotification(GENERAL_ICON_ID, TITLE, MESSAGE, ENABLE_NOTIFICATION_ID)
// Usb downstream. Still show enable notification.
notificationUpdater.onDownstreamChanged(USB_MASK)
- verifyOnlyTetheringActiveNotification(ENABLE_NOTIFICATION_ID, USB_ICON_ID, TITLE, MESSAGE)
+ verifyNotification(USB_ICON_ID, TITLE, MESSAGE, ENABLE_NOTIFICATION_ID)
// No downstream. No notification.
notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
- verifyCancelAllTetheringActiveNotifications()
+ verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID,
+ ROAMING_NOTIFICATION_ID))
}
@Test
fun testNotificationWithActiveDataSubscriptionIdChanged() {
// Usb downstream. Showed enable notification with default resource.
notificationUpdater.onDownstreamChanged(USB_MASK)
- verifyOnlyTetheringActiveNotification(ENABLE_NOTIFICATION_ID, USB_ICON_ID, TITLE, MESSAGE)
+ verifyNotification(USB_ICON_ID, TITLE, MESSAGE, ENABLE_NOTIFICATION_ID)
// Same subId changed. Nothing happened.
notificationUpdater.onActiveDataSubscriptionIdChanged(INVALID_SUBSCRIPTION_ID)
@@ -242,16 +241,17 @@ class TetheringNotificationUpdaterTest {
// Set test sub id. Clear notification with test resource.
notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
- verifyCancelAllTetheringActiveNotifications()
+ verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID,
+ ROAMING_NOTIFICATION_ID))
// Wifi downstream. Show enable notification with test resource.
notificationUpdater.onDownstreamChanged(WIFI_MASK)
- verifyOnlyTetheringActiveNotification(
- ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE)
+ verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID)
// No downstream. No notification.
notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
- verifyCancelAllTetheringActiveNotifications()
+ verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID,
+ ROAMING_NOTIFICATION_ID))
}
private fun assertIconNumbers(number: Int, configs: Array<String?>) {
@@ -305,24 +305,21 @@ class TetheringNotificationUpdaterTest {
// User restrictions on. Show restricted notification.
notificationUpdater.notifyTetheringDisabledByRestriction()
- verifyNotification(R.drawable.stat_sys_tether_general, title, message,
- RESTRICTED_NOTIFICATION_ID)
- reset(notificationManager)
+ verifyNotification(NOTIFICATION_ICON_ID, title, message, RESTRICTED_NOTIFICATION_ID)
// User restrictions off. Clear notification.
notificationUpdater.tetheringRestrictionLifted()
- verifyNotificationCancelled(RESTRICTED_NOTIFICATION_ID)
- reset(notificationManager)
+ verifyNotificationCancelled(listOf(RESTRICTED_NOTIFICATION_ID))
// Set test sub id. No notification.
notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
- verifyCancelAllTetheringActiveNotifications()
+ verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID,
+ ROAMING_NOTIFICATION_ID))
// User restrictions on again. Show restricted notification with test resource.
notificationUpdater.notifyTetheringDisabledByRestriction()
- verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage,
+ verifyNotification(NOTIFICATION_ICON_ID, disallowTitle, disallowMessage,
RESTRICTED_NOTIFICATION_ID)
- reset(notificationManager)
}
val MAX_BACKOFF_MS = 200L
@@ -359,51 +356,53 @@ class TetheringNotificationUpdaterTest {
}
@Test
- fun testNotificationWithUpstreamNetworkChanged() {
+ fun testNotificationWithUpstreamCapabilitiesChanged_NoUpstream() {
// Set test sub id. No notification.
notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
- verifyCancelAllTetheringActiveNotifications()
+ verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID,
+ ROAMING_NOTIFICATION_ID))
// Wifi downstream. Show enable notification with test resource.
notificationUpdater.onDownstreamChanged(WIFI_MASK)
- verifyOnlyTetheringActiveNotification(
- ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE)
+ verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID)
// There is no upstream. Show no upstream notification.
- notificationUpdater.onUpstreamNetworkChanged(null)
- notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L)
- verifyNotification(R.drawable.stat_sys_tether_general, TEST_NO_UPSTREAM_TITLE,
- TEST_NO_UPSTREAM_MESSAGE, NO_UPSTREAM_NOTIFICATION_ID)
- reset(notificationManager)
+ notificationUpdater.onUpstreamCapabilitiesChanged(null)
+ notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
+ verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
+ NO_UPSTREAM_NOTIFICATION_ID)
- // Same upstream network changed. Nothing happened.
- notificationUpdater.onUpstreamNetworkChanged(null)
+ // Same capabilities changed. Nothing happened.
+ notificationUpdater.onUpstreamCapabilitiesChanged(null)
verifyZeroInteractions(notificationManager)
// Upstream come back. Clear no upstream notification.
- notificationUpdater.onUpstreamNetworkChanged(Network(1000))
- verifyNotificationCancelled(NO_UPSTREAM_NOTIFICATION_ID)
- reset(notificationManager)
+ notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES)
+ verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID))
// No upstream again. Show no upstream notification.
- notificationUpdater.onUpstreamNetworkChanged(null)
- notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L)
- verifyNotification(R.drawable.stat_sys_tether_general, TEST_NO_UPSTREAM_TITLE,
- TEST_NO_UPSTREAM_MESSAGE, NO_UPSTREAM_NOTIFICATION_ID)
- reset(notificationManager)
+ notificationUpdater.onUpstreamCapabilitiesChanged(null)
+ notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
+ verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
+ NO_UPSTREAM_NOTIFICATION_ID)
// No downstream. No notification.
notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
- verifyCancelAllTetheringActiveNotifications()
+ verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID,
+ ROAMING_NOTIFICATION_ID))
- // Set R.integer.delay_to_show_no_upstream_after_no_backhaul to 0 and have wifi downstream
- // again. Show enable notification only.
+ // Put up enable notification with wifi downstream and home capabilities.
+ notificationUpdater.onDownstreamChanged(WIFI_MASK)
+ notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES)
+ verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID)
+
+ // Set R.integer.delay_to_show_no_upstream_after_no_backhaul to -1 and change to no upstream
+ // again. Don't put up no upstream notification.
doReturn(-1).`when`(testResources)
.getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul)
- notificationUpdater.onDownstreamChanged(WIFI_MASK)
- notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L)
- verifyOnlyTetheringActiveNotification(
- ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE)
+ notificationUpdater.onUpstreamCapabilitiesChanged(null)
+ notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
+ verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID))
}
@Test
@@ -428,4 +427,57 @@ class TetheringNotificationUpdaterTest {
assertEquals(311, res.configuration.mcc)
assertEquals(480, res.configuration.mnc)
}
+
+ @Test
+ fun testNotificationWithUpstreamCapabilitiesChanged_Roaming() {
+ // Set test sub id. Clear notification.
+ notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
+ verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID,
+ ROAMING_NOTIFICATION_ID))
+
+ // Wifi downstream. Show enable notification with test resource.
+ notificationUpdater.onDownstreamChanged(WIFI_MASK)
+ verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID)
+
+ // Upstream capabilities changed to roaming state. Show roaming notification.
+ notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
+ verifyNotification(NOTIFICATION_ICON_ID, TEST_ROAMING_TITLE, TEST_ROAMING_MESSAGE,
+ ROAMING_NOTIFICATION_ID)
+
+ // Same capabilities change. Nothing happened.
+ notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
+ verifyZeroInteractions(notificationManager)
+
+ // Upstream capabilities changed to home state. Clear roaming notification.
+ notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES)
+ verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID))
+
+ // Upstream capabilities changed to roaming state again. Show roaming notification.
+ notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
+ verifyNotification(NOTIFICATION_ICON_ID, TEST_ROAMING_TITLE, TEST_ROAMING_MESSAGE,
+ ROAMING_NOTIFICATION_ID)
+
+ // No upstream. Clear roaming notification and show no upstream notification.
+ notificationUpdater.onUpstreamCapabilitiesChanged(null)
+ notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
+ verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID), false)
+ verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
+ NO_UPSTREAM_NOTIFICATION_ID)
+
+ // No downstream. No notification.
+ notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
+ verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID,
+ ROAMING_NOTIFICATION_ID))
+
+ // Wifi downstream again. Show enable notification with test resource.
+ notificationUpdater.onDownstreamChanged(WIFI_MASK)
+ verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID)
+
+ // Set R.bool.config_upstream_roaming_notification to false and change upstream
+ // network to roaming state again. No roaming notification.
+ doReturn(false).`when`(testResources)
+ .getBoolean(R.bool.config_upstream_roaming_notification)
+ notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
+ verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
+ }
}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index 15e253af12a9..28bfae0ced4a 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -48,6 +48,7 @@ import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
+import static com.android.networkstack.tethering.UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -1700,7 +1701,29 @@ public class TetheringTest {
stateMachine.chooseUpstreamType(true);
verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(eq(upstreamState.network));
- verify(mNotificationUpdater, times(1)).onUpstreamNetworkChanged(eq(upstreamState.network));
+ verify(mNotificationUpdater, times(1)).onUpstreamCapabilitiesChanged(any());
+ }
+
+ @Test
+ public void testUpstreamCapabilitiesChanged() {
+ final Tethering.TetherMasterSM stateMachine = (Tethering.TetherMasterSM)
+ mTetheringDependencies.mUpstreamNetworkMonitorMasterSM;
+ final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
+ when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState);
+ stateMachine.chooseUpstreamType(true);
+
+ stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState);
+ // Should have two onUpstreamCapabilitiesChanged().
+ // One is called by reportUpstreamChanged(). One is called by EVENT_ON_CAPABILITIES.
+ verify(mNotificationUpdater, times(2)).onUpstreamCapabilitiesChanged(any());
+ reset(mNotificationUpdater);
+
+ // Verify that onUpstreamCapabilitiesChanged won't be called if not current upstream network
+ // capabilities changed.
+ final UpstreamNetworkState upstreamState2 = new UpstreamNetworkState(
+ upstreamState.linkProperties, upstreamState.networkCapabilities, new Network(101));
+ stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState2);
+ verify(mNotificationUpdater, never()).onUpstreamCapabilitiesChanged(any());
}
// TODO: Test that a request for hotspot mode doesn't interfere with an
diff --git a/services/Android.bp b/services/Android.bp
index 8af642cafffd..ab6fb7ff95d2 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -133,6 +133,11 @@ droidstubs {
baseline_file: "api/lint-baseline.txt",
},
},
+ dist: {
+ targets: ["sdk", "win_sdk"],
+ dir: "apistubs/android/system-server/api",
+ dest: "android.txt",
+ },
}
java_library {
diff --git a/services/core/java/com/android/server/UserspaceRebootLogger.java b/services/core/java/com/android/server/UserspaceRebootLogger.java
index 9a9374ce1822..2403b637581f 100644
--- a/services/core/java/com/android/server/UserspaceRebootLogger.java
+++ b/services/core/java/com/android/server/UserspaceRebootLogger.java
@@ -24,6 +24,7 @@ import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPOR
import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__LOCKED;
import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__UNLOCKED;
+import android.os.PowerManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.text.TextUtils;
@@ -53,8 +54,15 @@ public final class UserspaceRebootLogger {
/**
* Modifies internal state to note that {@code UserspaceRebootReported} atom needs to be
* logged on the next successful boot.
+ *
+ * <p>This call should only be made on devices supporting userspace reboot.
*/
public static void noteUserspaceRebootWasRequested() {
+ if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
+ Slog.wtf(TAG, "Userspace reboot is not supported.");
+ return;
+ }
+
SystemProperties.set(USERSPACE_REBOOT_SHOULD_LOG_PROPERTY, "1");
SystemProperties.set(USERSPACE_REBOOT_LAST_STARTED_PROPERTY,
String.valueOf(SystemClock.elapsedRealtime()));
@@ -64,16 +72,30 @@ public final class UserspaceRebootLogger {
* Updates internal state on boot after successful userspace reboot.
*
* <p>Should be called right before framework sets {@code sys.boot_completed} property.
+ *
+ * <p>This call should only be made on devices supporting userspace reboot.
*/
public static void noteUserspaceRebootSuccess() {
+ if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
+ Slog.wtf(TAG, "Userspace reboot is not supported.");
+ return;
+ }
+
SystemProperties.set(USERSPACE_REBOOT_LAST_FINISHED_PROPERTY,
String.valueOf(SystemClock.elapsedRealtime()));
}
/**
* Returns {@code true} if {@code UserspaceRebootReported} atom should be logged.
+ *
+ * <p>This call should only be made on devices supporting userspace reboot.
*/
public static boolean shouldLogUserspaceRebootEvent() {
+ if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
+ Slog.wtf(TAG, "Userspace reboot is not supported.");
+ return false;
+ }
+
return SystemProperties.getBoolean(USERSPACE_REBOOT_SHOULD_LOG_PROPERTY, false);
}
@@ -83,8 +105,15 @@ public final class UserspaceRebootLogger {
* <p>Should be called in the end of {@link
* com.android.server.am.ActivityManagerService#finishBooting()} method, after framework have
* tried to proactivelly unlock storage of the primary user.
+ *
+ * <p>This call should only be made on devices supporting userspace reboot.
*/
public static void logEventAsync(boolean userUnlocked, Executor executor) {
+ if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
+ Slog.wtf(TAG, "Userspace reboot is not supported.");
+ return;
+ }
+
final int outcome = computeOutcome();
final long durationMillis;
if (outcome == USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index a1226115b6e4..d8111ab16e26 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -213,9 +213,11 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
mLocalDeviceAddresses = initLocalDeviceAddresses();
resetSelectRequestBuffer();
launchDeviceDiscovery();
+ if (!mDelayedMessageBuffer.isBuffered(Constants.MESSAGE_ACTIVE_SOURCE)) {
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildRequestActiveSource(mAddress));
+ }
}
-
@ServiceThreadOnly
private List<Integer> initLocalDeviceAddresses() {
assertRunOnServiceThread();
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 3e1d72c7eeb8..2b1d9e58c4d5 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -74,7 +74,6 @@ public class DctConstants {
public static final int BASE = Protocol.BASE_DATA_CONNECTION_TRACKER;
public static final int EVENT_DATA_SETUP_COMPLETE = BASE + 0;
public static final int EVENT_RADIO_AVAILABLE = BASE + 1;
- public static final int EVENT_RECORDS_LOADED = BASE + 2;
public static final int EVENT_TRY_SETUP_DATA = BASE + 3;
public static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = BASE + 6;
public static final int EVENT_VOICE_CALL_STARTED = BASE + 7;
@@ -94,7 +93,6 @@ public class DctConstants {
public static final int EVENT_CLEAN_UP_CONNECTION = BASE + 24;
public static final int EVENT_RESTART_RADIO = BASE + 26;
public static final int EVENT_CLEAN_UP_ALL_CONNECTIONS = BASE + 29;
- public static final int EVENT_ICC_CHANGED = BASE + 33;
public static final int EVENT_DATA_SETUP_COMPLETE_ERROR = BASE + 35;
public static final int CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA = BASE + 36;
public static final int CMD_ENABLE_MOBILE_PROVISIONING = BASE + 37;
@@ -114,7 +112,7 @@ public class DctConstants {
public static final int EVENT_SERVICE_STATE_CHANGED = BASE + 52;
public static final int EVENT_5G_TIMER_HYSTERESIS = BASE + 53;
public static final int EVENT_5G_TIMER_WATCHDOG = BASE + 54;
- public static final int EVENT_UPDATE_CARRIER_CONFIGS = BASE + 55;
+ public static final int EVENT_CARRIER_CONFIG_CHANGED = BASE + 55;
/***** Constants *****/
diff --git a/tests/AppLaunch/Android.bp b/tests/AppLaunch/Android.bp
index f90f26f00e6d..75db55122553 100644
--- a/tests/AppLaunch/Android.bp
+++ b/tests/AppLaunch/Android.bp
@@ -8,6 +8,8 @@ android_test {
"android.test.base",
"android.test.runner",
],
- static_libs: ["androidx.test.rules"],
+ static_libs: [
+ "androidx.test.rules",
+ "ub-uiautomator"],
test_suites: ["device-tests"],
}
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index c64e7d1834c9..0d05044f2c25 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -15,6 +15,8 @@
*/
package com.android.tests.applaunch;
+import static org.junit.Assert.assertNotNull;
+
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.ActivityManager;
@@ -29,7 +31,9 @@ import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
+import android.support.test.uiautomator.UiDevice;
import android.test.InstrumentationTestCase;
import android.test.InstrumentationTestRunner;
import android.util.Log;
@@ -46,6 +50,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
+import java.nio.file.Paths;
import java.time.format.DateTimeFormatter;
import java.time.ZonedDateTime;
import java.time.ZoneOffset;
@@ -67,6 +72,7 @@ import java.util.Set;
* in the following format:
* -e apps <app name>^<result key>|<app name>^<result key>
*/
+@Deprecated
public class AppLaunch extends InstrumentationTestCase {
private static final int JOIN_TIMEOUT = 10000;
@@ -94,6 +100,9 @@ public class AppLaunch extends InstrumentationTestCase {
private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval";
private static final String KEY_COMPILER_FILTERS = "compiler_filters";
private static final String KEY_FORCE_STOP_APP = "force_stop_app";
+ private static final String ENABLE_SCREEN_RECORDING = "enable_screen_recording";
+ private static final int MAX_RECORDING_PARTS = 5;
+ private static final long VIDEO_TAIL_BUFFER = 500;
private static final String SIMPLEPERF_APP_CMD =
"simpleperf --log fatal stat --csv -e cpu-cycles,major-faults --app %s & %s";
@@ -144,14 +153,17 @@ public class AppLaunch extends InstrumentationTestCase {
private Map<String, Intent> mNameToIntent;
private List<LaunchOrder> mLaunchOrderList = new ArrayList<LaunchOrder>();
+ private RecordingThread mCurrentThread;
private Map<String, String> mNameToResultKey;
private Map<String, Map<String, List<AppLaunchResult>>> mNameToLaunchTime;
private IActivityManager mAm;
+ private File launchSubDir = null;
private String mSimplePerfCmd = null;
private String mLaunchOrder = null;
private boolean mDropCache = false;
private int mLaunchIterations = 10;
private boolean mForceStopApp = true;
+ private boolean mEnableRecording = false;
private int mTraceLaunchCount = 0;
private String mTraceDirectoryStr = null;
private Bundle mResult = new Bundle();
@@ -166,6 +178,7 @@ public class AppLaunch extends InstrumentationTestCase {
private boolean mCycleCleanUp = false;
private boolean mTraceAll = false;
private boolean mIterationCycle = false;
+ private UiDevice mDevice;
enum IorapStatus {
UNDEFINED,
@@ -222,7 +235,7 @@ public class AppLaunch extends InstrumentationTestCase {
}
try {
- File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
+ launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
throw new IOException("Unable to create the lauch file sub directory "
@@ -921,9 +934,16 @@ public class AppLaunch extends InstrumentationTestCase {
mLaunchIterations = Integer.parseInt(launchIterations);
}
String forceStopApp = args.getString(KEY_FORCE_STOP_APP);
+
if (forceStopApp != null) {
mForceStopApp = Boolean.parseBoolean(forceStopApp);
}
+
+ String enableRecording = args.getString(ENABLE_SCREEN_RECORDING);
+
+ if (enableRecording != null) {
+ mEnableRecording = Boolean.parseBoolean(enableRecording);
+ }
String appList = args.getString(KEY_APPS);
if (appList == null)
return;
@@ -1036,6 +1056,9 @@ public class AppLaunch extends InstrumentationTestCase {
private AppLaunchResult startApp(String appName, String launchReason)
throws NameNotFoundException, RemoteException {
Log.i(TAG, "Starting " + appName);
+ if(mEnableRecording) {
+ startRecording(appName, launchReason);
+ }
Intent startIntent = mNameToIntent.get(appName);
if (startIntent == null) {
@@ -1051,6 +1074,10 @@ public class AppLaunch extends InstrumentationTestCase {
} catch (InterruptedException e) {
// ignore
}
+
+ if(mEnableRecording) {
+ stopRecording();
+ }
return runnable.getResult();
}
@@ -1358,4 +1385,126 @@ public class AppLaunch extends InstrumentationTestCase {
}
}
+
+ /**
+ * Start the screen recording while launching the app.
+ *
+ * @param appName
+ * @param launchReason
+ */
+ private void startRecording(String appName, String launchReason) {
+ Log.v(TAG, "Started Recording");
+ mCurrentThread = new RecordingThread("test-screen-record",
+ String.format("%s_%s", appName, launchReason));
+ mCurrentThread.start();
+ }
+
+ /**
+ * Stop already started screen recording.
+ */
+ private void stopRecording() {
+ // Skip if not directory.
+ if (launchSubDir == null) {
+ return;
+ }
+
+ // Add some extra time to the video end.
+ SystemClock.sleep(VIDEO_TAIL_BUFFER);
+ // Ctrl + C all screen record processes.
+ mCurrentThread.cancel();
+ // Wait for the thread to completely die.
+ try {
+ mCurrentThread.join();
+ } catch (InterruptedException ex) {
+ Log.e(TAG, "Interrupted when joining the recording thread.", ex);
+ }
+ Log.v(TAG, "Stopped Recording");
+ }
+
+ /** Returns the recording's name for part {@code part} of launch description. */
+ private File getOutputFile(String description, int part) {
+ // Omit the iteration number for the first iteration.
+ final String fileName =
+ String.format(
+ "%s-video%s.mp4", description, part == 1 ? "" : part);
+ return Paths.get(launchSubDir.getAbsolutePath(), description).toFile();
+ }
+
+
+ /**
+ * Encapsulates the start and stop screen recording logic.
+ * Copied from ScreenRecordCollector.
+ */
+ private class RecordingThread extends Thread {
+ private final String mDescription;
+ private final List<File> mRecordings;
+
+ private boolean mContinue;
+
+ public RecordingThread(String name, String description) {
+ super(name);
+
+ mContinue = true;
+ mRecordings = new ArrayList<>();
+
+ assertNotNull("No test description provided for recording.", description);
+ mDescription = description;
+ }
+
+ @Override
+ public void run() {
+ try {
+ // Start at i = 1 to encode parts as X.mp4, X2.mp4, X3.mp4, etc.
+ for (int i = 1; i <= MAX_RECORDING_PARTS && mContinue; i++) {
+ File output = getOutputFile(mDescription, i);
+ Log.d(
+ TAG,
+ String.format("Recording screen to %s", output.getAbsolutePath()));
+ mRecordings.add(output);
+ // Make sure not to block on this background command in the main thread so
+ // that the test continues to run, but block in this thread so it does not
+ // trigger a new screen recording session before the prior one completes.
+ getDevice().executeShellCommand(
+ String.format("screenrecord %s", output.getAbsolutePath()));
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Caught exception while screen recording.");
+ }
+ }
+
+ public void cancel() {
+ mContinue = false;
+
+ // Identify the screenrecord PIDs and send SIGINT 2 (Ctrl + C) to each.
+ try {
+ String[] pids = getDevice().executeShellCommand(
+ "pidof screenrecord").split(" ");
+ for (String pid : pids) {
+ // Avoid empty process ids, because of weird splitting behavior.
+ if (pid.isEmpty()) {
+ continue;
+ }
+
+ getDevice().executeShellCommand(
+ String.format("kill -2 %s", pid));
+ Log.d(
+ TAG,
+ String.format("Sent SIGINT 2 to screenrecord process (%s)", pid));
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to kill screen recording process.");
+ }
+ }
+
+ public List<File> getRecordings() {
+ return mRecordings;
+ }
+ }
+
+ public UiDevice getDevice() {
+ if (mDevice == null) {
+ mDevice = UiDevice.getInstance(getInstrumentation());
+ }
+ return mDevice;
+ }
}
diff --git a/tests/net/common/java/android/net/DhcpInfoTest.java b/tests/net/common/java/android/net/DhcpInfoTest.java
new file mode 100644
index 000000000000..bd5533f33910
--- /dev/null
+++ b/tests/net/common/java/android/net/DhcpInfoTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2009 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.net;
+
+import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTL;
+
+import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals;
+import static com.android.testutils.ParcelUtilsKt.parcelingRoundTrip;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.Nullable;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+
+@RunWith(AndroidJUnit4.class)
+public class DhcpInfoTest {
+ private static final String STR_ADDR1 = "255.255.255.255";
+ private static final String STR_ADDR2 = "127.0.0.1";
+ private static final String STR_ADDR3 = "192.168.1.1";
+ private static final String STR_ADDR4 = "192.168.1.0";
+ private static final int LEASE_TIME = 9999;
+
+ private int ipToInteger(String ipString) throws Exception {
+ return inet4AddressToIntHTL((Inet4Address) InetAddress.getByName(ipString));
+ }
+
+ private DhcpInfo createDhcpInfoObject() throws Exception {
+ final DhcpInfo dhcpInfo = new DhcpInfo();
+ dhcpInfo.ipAddress = ipToInteger(STR_ADDR1);
+ dhcpInfo.gateway = ipToInteger(STR_ADDR2);
+ dhcpInfo.netmask = ipToInteger(STR_ADDR3);
+ dhcpInfo.dns1 = ipToInteger(STR_ADDR4);
+ dhcpInfo.dns2 = ipToInteger(STR_ADDR4);
+ dhcpInfo.serverAddress = ipToInteger(STR_ADDR2);
+ dhcpInfo.leaseDuration = LEASE_TIME;
+ return dhcpInfo;
+ }
+
+ @Test
+ public void testConstructor() {
+ new DhcpInfo();
+ }
+
+ @Test
+ public void testToString() throws Exception {
+ final String expectedDefault = "ipaddr 0.0.0.0 gateway 0.0.0.0 netmask 0.0.0.0 "
+ + "dns1 0.0.0.0 dns2 0.0.0.0 DHCP server 0.0.0.0 lease 0 seconds";
+
+ DhcpInfo dhcpInfo = new DhcpInfo();
+
+ // Test default string.
+ assertEquals(expectedDefault, dhcpInfo.toString());
+
+ dhcpInfo = createDhcpInfoObject();
+
+ final String expected = "ipaddr " + STR_ADDR1 + " gateway " + STR_ADDR2 + " netmask "
+ + STR_ADDR3 + " dns1 " + STR_ADDR4 + " dns2 " + STR_ADDR4 + " DHCP server "
+ + STR_ADDR2 + " lease " + LEASE_TIME + " seconds";
+ // Test with new values
+ assertEquals(expected, dhcpInfo.toString());
+ }
+
+ private boolean dhcpInfoEquals(@Nullable DhcpInfo left, @Nullable DhcpInfo right) {
+ if (left == null && right == null) return true;
+
+ if (left == null || right == null) return false;
+
+ return left.ipAddress == right.ipAddress
+ && left.gateway == right.gateway
+ && left.netmask == right.netmask
+ && left.dns1 == right.dns1
+ && left.dns2 == right.dns2
+ && left.serverAddress == right.serverAddress
+ && left.leaseDuration == right.leaseDuration;
+ }
+
+ @Test
+ public void testParcelDhcpInfo() throws Exception {
+ // Cannot use assertParcelSane() here because this requires .equals() to work as
+ // defined, but DhcpInfo has a different legacy behavior that we cannot change.
+ final DhcpInfo dhcpInfo = createDhcpInfoObject();
+ assertFieldCountEquals(7, DhcpInfo.class);
+
+ final DhcpInfo dhcpInfoRoundTrip = parcelingRoundTrip(dhcpInfo);
+ assertTrue(dhcpInfoEquals(null, null));
+ assertFalse(dhcpInfoEquals(null, dhcpInfoRoundTrip));
+ assertFalse(dhcpInfoEquals(dhcpInfo, null));
+ assertTrue(dhcpInfoEquals(dhcpInfo, dhcpInfoRoundTrip));
+ }
+}